]> code.delx.au - gnu-emacs/blobdiff - lisp/forms.el
(find-file-noselect): If after-find-file switches buffers,
[gnu-emacs] / lisp / forms.el
index c8276c7f68c2e492244648000b60e435b7773bee..aa20ecd561ff38e1ec9284f50b9ceea7fd0c65da 100644 (file)
@@ -1,8 +1,8 @@
-;;; forms.el -- Forms mode: edit a file as a form to fill in.
-;;; Copyright (C) 1991, 1994 Free Software Foundation, Inc.
+;;; forms.el --- Forms mode: edit a file as a form to fill in
 
-;; Author: Johan Vromans <jv@nl.net>
-;; Version: $Revision: 2.13 $
+;; Copyright (C) 1991, 1994, 1995 Free Software Foundation, Inc.
+
+;; Author: Johan Vromans <jvromans@squirrel.nl>
 
 ;; This file is part of GNU Emacs.
 
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
-;;; Visit a file using a form.
-;;;
-;;; === Naming conventions
-;;;
-;;; The names of all variables and functions start with 'forms-'.
-;;; Names which start with 'forms--' are intended for internal use, and
-;;; should *NOT* be used from the outside.
-;;;
-;;; All variables are buffer-local, to enable multiple forms visits 
-;;; simultaneously.
-;;; Variable `forms--mode-setup' is local to *ALL* buffers, for it 
-;;; controls if forms-mode has been enabled in a buffer.
-;;;
-;;; === How it works ===
-;;;
-;;; Forms mode means visiting a data file which is supposed to consist
-;;; of records each containing a number of fields.  The records are
-;;; separated by a newline, the fields are separated by a user-defined
-;;; field separater (default: TAB).
-;;; When shown, a record is transferred to an Emacs buffer and
-;;; presented using a user-defined form.  One record is shown at a
-;;; time.
-;;;
-;;; Forms mode is a composite mode.  It involves two files, and two
-;;; buffers.
-;;; The first file, called the control file, defines the name of the
-;;; data file and the forms format.  This file buffer will be used to
-;;; present the forms.
-;;; The second file holds the actual data.  The buffer of this file
-;;; will be buried, for it is never accessed directly.
-;;;
-;;; Forms mode is invoked using M-x forms-find-file control-file .
-;;; Alternativily `forms-find-file-other-window' can be used.
-;;;
-;;; You may also visit the control file, and switch to forms mode by hand
-;;; with M-x forms-mode .
-;;;
-;;; Automatic mode switching is supported if you specify 
-;;; "-*- forms -*-" in the first line of the control file.
-;;; 
-;;; The control file is visited, evaluated using `eval-current-buffer',
-;;; and should set at least the following variables:
-;;;
-;;;    forms-file                              [string]
-;;;                    The name of the data file.
-;;;
-;;;    forms-number-of-fields                  [integer]
-;;;                    The number of fields in each record.
-;;;
-;;;    forms-format-list                       [list]
-;;;                    Formatting instructions.
-;;;
-;;; `forms-format-list' should be a list, each element containing
-;;;
-;;;   - a string, e.g. "hello".  The string is inserted in the forms
-;;;    "as is".
-;;;   
-;;;   - an integer, denoting a field number.
-;;;    The contents of this field are inserted at this point.
-;;;     Fields are numbered starting with number one.
-;;;   
-;;;   - a function call, e.g. (insert "text").
-;;;    This function call is dynamically evaluated and should return a
-;;;     string.  It should *NOT* have side-effects on the forms being
-;;;     constructed.  The current fields are available to the function
-;;;     in the variable `forms-fields', they should *NOT* be modified.
-;;;   
-;;;   - a lisp symbol, that must evaluate to one of the above.
-;;;
-;;; Optional variables which may be set in the control file:
-;;;
-;;;    forms-field-sep                         [string, default TAB]
-;;;                    The field separator used to separate the
-;;;                    fields in the data file.  It may be a string.
-;;;
-;;;    forms-read-only                         [bool, default nil]
-;;;                    Non-nil means that the data file is visited
-;;;                    read-only (view mode) as opposed to edit mode.
-;;;                    If no write access to the data file is
-;;;                    possible, view mode is enforced. 
-;;;
-;;;    forms-multi-line                        [string, default "^K"]
-;;;                    If non-null the records of the data file may
-;;;                    contain fields that can span multiple lines in
-;;;                    the form.
-;;;                    This variable denotes the separator character
-;;;                    to be used for this purpose.  Upon display, all
-;;;                    occurrencies of this character are translated
-;;;                    to newlines.  Upon storage they are translated
-;;;                    back to the separator character.
-;;;
-;;;    forms-forms-scroll                      [bool, default nil]
-;;;                    Non-nil means: rebind locally the commands that
-;;;                    perform `scroll-up' or `scroll-down' to use
-;;;                    `forms-next-field' resp. `forms-prev-field'.
-;;;
-;;;    forms-forms-jump                        [bool, default nil]
-;;;                    Non-nil means: rebind locally the commands that
-;;;                    perform `beginning-of-buffer' or `end-of-buffer'
-;;;                    to perform `forms-first-field' resp. `forms-last-field'.
-;;;
-;;;    forms-read-file-filter                  [symbol, default nil]
-;;;                    If not nil: this should be the name of a 
-;;;                    function that is called after the forms data file
-;;;                    has been read.  It can be used to transform
-;;;                    the contents of the file into a format more suitable
-;;;                    for forms-mode processing.
-;;;
-;;;    forms-write-file-filter                 [symbol, default nil]
-;;;                    If not nil: this should be the name of a 
-;;;                    function that is called before the forms data file
-;;;                    is written (saved) to disk.  It can be used to undo
-;;;                    the effects of `forms-read-file-filter', if any.
-;;;
-;;;    forms-new-record-filter                 [symbol, default nil]
-;;;                    If not nil: this should be the name of a 
-;;;                    function that is called when a new
-;;;                    record is created.  It can be used to fill in
-;;;                    the new record with default fields, for example.
-;;;
-;;;    forms-modified-record-filter            [symbol, default nil]
-;;;                    If not nil: this should be the name of a 
-;;;                    function that is called when a record has
-;;;                    been modified.  It is called after the fields
-;;;                    are parsed.  It can be used to register
-;;;                    modification dates, for example.
-;;;
-;;;    forms-use-text-properties               [bool, see text for default]
-;;;                    This variable controls if forms mode should use
-;;;                    text properties to protect the form text from being
-;;;                    modified (using text-property `read-only').
-;;;                    Also, the read-write fields are shown using a
-;;;                    distinct face, if possible.
-;;;                    This variable defaults to t if running Emacs 19
-;;;                    with text properties.
-;;;                    The default face to show read-write fields is
-;;;                    copied from face `region'.
-;;;
-;;;    forms-ro-face                           [symbol, default 'default]
-;;;                    This is the face that is used to show
-;;;                    read-only text on the screen.If used, this
-;;;                    variable should be set to a symbol that is a
-;;;                    valid face.
-;;;                    E.g.
-;;;                      (make-face 'my-face)
-;;;                      (setq forms-ro-face 'my-face)
-;;;
-;;;    forms-rw-face                           [symbol, default 'region]
-;;;                    This is the face that is used to show
-;;;                    read-write text on the screen.
-;;;
-;;; After evaluating the control file, its buffer is cleared and used
-;;; for further processing.
-;;; The data file (as designated by `forms-file') is visited in a buffer
-;;; `forms--file-buffer' which will not normally be shown.
-;;; Great malfunctioning may be expected if this file/buffer is modified
-;;; outside of this package while it is being visited!
-;;;
-;;; Normal operation is to transfer one line (record) from the data file,
-;;; split it into fields (into `forms--the-record-list'), and display it
-;;; using the specs in `forms-format-list'.
-;;; A format routine `forms--format' is built upon startup to format 
-;;; the records according to `forms-format-list'.
-;;;
-;;; When a form is changed the record is updated as soon as this form
-;;; is left.  The contents of the form are parsed using information
-;;; obtained from `forms-format-list', and the fields which are
-;;; deduced from the form are modified.  Fields not shown on the forms
-;;; retain their origional values.  The newly formed record then
-;;; replaces the contents of the old record in `forms--file-buffer'.
-;;; A parse routine `forms--parser' is built upon startup to parse
-;;; the records.
-;;;
-;;; Two exit functions exist: `forms-exit' and `forms-exit-no-save'.
-;;; `forms-exit' saves the data to the file, if modified.
-;;; `forms-exit-no-save` does not.  However, if `forms-exit-no-save'
-;;; is executed and the file buffer has been modified, Emacs will ask
-;;; questions anyway.
-;;;
-;;; Other functions provided by forms mode are:
-;;;
-;;;    paging (forward, backward) by record
-;;;    jumping (first, last, random number)
-;;;    searching
-;;;    creating and deleting records
-;;;    reverting the form (NOT the file buffer)
-;;;    switching edit <-> view mode v.v.
-;;;    jumping from field to field
-;;;
-;;; As an documented side-effect: jumping to the last record in the
-;;; file (using forms-last-record) will adjust forms--total-records if
-;;; needed.
-;;;
-;;; The forms buffer can be in on eof two modes: edit mode or view
-;;; mode.  View mode is a read-only mode, you cannot modify the
-;;; contents of the buffer.
-;;;
-;;; Edit mode commands:
-;;; 
-;;; TAB                 forms-next-field
-;;; \C-c TAB    forms-next-field
-;;; \C-c <      forms-first-record
-;;; \C-c >      forms-last-record
-;;; \C-c ?      describe-mode
-;;; \C-c \C-k   forms-delete-record
-;;; \C-c \C-q   forms-toggle-read-only
-;;; \C-c \C-o   forms-insert-record
-;;; \C-c \C-l   forms-jump-record
-;;; \C-c \C-n   forms-next-record
-;;; \C-c \C-p   forms-prev-record
-;;; \C-c \C-r   forms-search-backward
-;;; \C-c \C-s   forms-search-forward
-;;; \C-c \C-x   forms-exit
-;;; 
-;;; Read-only mode commands:
-;;; 
-;;; SPC         forms-next-record
-;;; DEL         forms-prev-record
-;;; ?   describe-mode
-;;; \C-q forms-toggle-read-only
-;;; l   forms-jump-record
-;;; n   forms-next-record
-;;; p   forms-prev-record
-;;; r   forms-search-backward
-;;; s   forms-search-forward
-;;; x   forms-exit
-;;; 
-;;; Of course, it is also possible to use the \C-c prefix to obtain the
-;;; same command keys as in edit mode.
-;;; 
-;;; The following bindings are available, independent of the mode: 
-;;; 
-;;; [next]       forms-next-record
-;;; [prior]      forms-prev-record
-;;; [begin]      forms-first-record
-;;; [end]        forms-last-record
-;;; [S-TAB]      forms-prev-field
-;;; [backtab] forms-prev-field
-;;;
-;;; For convenience, TAB is always bound to `forms-next-field', so you
-;;; don't need the C-c prefix for this command.
-;;;
-;;; As mentioned above (see `forms-forms-scroll' and `forms-forms-jump')
-;;; the bindings of standard functions `scroll-up', `scroll-down',
-;;; `beginning-of-buffer' and `end-of-buffer' can be locally replaced with
-;;; forms mode functions next/prev record and first/last
-;;; record.
-;;;
-;;; `local-write-file hook' is defined to save the actual data file
-;;; instead of the buffer data, `revert-file-hook' is defined to
-;;; revert a forms to original.
+;; Visit a file using a form.
+;;
+;; === Naming conventions
+;;
+;; The names of all variables and functions start with 'forms-'.
+;; Names which start with 'forms--' are intended for internal use, and
+;; should *NOT* be used from the outside.
+;;
+;; All variables are buffer-local, to enable multiple forms visits 
+;; simultaneously.
+;; Variable `forms--mode-setup' is local to *ALL* buffers, for it 
+;; controls if forms-mode has been enabled in a buffer.
+;;
+;; === How it works ===
+;;
+;; Forms mode means visiting a data file which is supposed to consist
+;; of records each containing a number of fields.  The records are
+;; separated by a newline, the fields are separated by a user-defined
+;; field separator (default: TAB).
+;; When shown, a record is transferred to an Emacs buffer and
+;; presented using a user-defined form.  One record is shown at a
+;; time.
+;;
+;; Forms mode is a composite mode.  It involves two files, and two
+;; buffers.
+;; The first file, called the control file, defines the name of the
+;; data file and the forms format.  This file buffer will be used to
+;; present the forms.
+;; The second file holds the actual data.  The buffer of this file
+;; will be buried, for it is never accessed directly.
+;;
+;; Forms mode is invoked using M-x forms-find-file control-file .
+;; Alternatively `forms-find-file-other-window' can be used.
+;;
+;; You may also visit the control file, and switch to forms mode by hand
+;; with M-x forms-mode .
+;;
+;; Automatic mode switching is supported if you specify 
+;; "-*- forms -*-" in the first line of the control file.
+;; 
+;; The control file is visited, evaluated using `eval-current-buffer',
+;; and should set at least the following variables:
+;;
+;;     forms-file                              [string]
+;;                     The name of the data file.
+;;
+;;     forms-number-of-fields                  [integer]
+;;                     The number of fields in each record.
+;;
+;;     forms-format-list                       [list]
+;;                     Formatting instructions.
+;;
+;; `forms-format-list' should be a list, each element containing
+;;
+;;   - a string, e.g. "hello".  The string is inserted in the forms
+;;     "as is".
+;;   
+;;   - an integer, denoting a field number.
+;;     The contents of this field are inserted at this point.
+;;     Fields are numbered starting with number one.
+;;   
+;;   - a function call, e.g. (insert "text").
+;;     This function call is dynamically evaluated and should return a
+;;     string.  It should *NOT* have side-effects on the forms being
+;;     constructed.  The current fields are available to the function
+;;     in the variable `forms-fields', they should *NOT* be modified.
+;;   
+;;   - a lisp symbol, that must evaluate to one of the above.
+;;
+;; Optional variables which may be set in the control file:
+;;
+;;     forms-field-sep                         [string, default TAB]
+;;                     The field separator used to separate the
+;;                     fields in the data file.  It may be a string.
+;;
+;;     forms-read-only                         [bool, default nil]
+;;                     Non-nil means that the data file is visited
+;;                     read-only (view mode) as opposed to edit mode.
+;;                     If no write access to the data file is
+;;                     possible, view mode is enforced. 
+;;
+;;     forms-check-number-of-fields            [bool, default t]
+;;                   If non-nil, a warning will be issued whenever
+;;                   a record is found that does not have the number
+;;                   of fields specified by `forms-number-of-fields'.
+;;
+;;     forms-multi-line                        [string, default "^K"]
+;;                     If non-null the records of the data file may
+;;                     contain fields that can span multiple lines in
+;;                     the form.
+;;                     This variable denotes the separator character
+;;                     to be used for this purpose.  Upon display, all
+;;                     occurrences of this character are translated
+;;                     to newlines.  Upon storage they are translated
+;;                     back to the separator character.
+;;
+;;     forms-forms-scroll                      [bool, default nil]
+;;                     Non-nil means: rebind locally the commands that
+;;                     perform `scroll-up' or `scroll-down' to use
+;;                     `forms-next-field' resp. `forms-prev-field'.
+;;
+;;     forms-forms-jump                        [bool, default nil]
+;;                     Non-nil means: rebind locally the commands that
+;;                     perform `beginning-of-buffer' or `end-of-buffer'
+;;                     to perform `forms-first-field' resp. `forms-last-field'.
+;;
+;;     forms-read-file-filter                  [symbol, default nil]
+;;                     If not nil: this should be the name of a 
+;;                     function that is called after the forms data file
+;;                     has been read.  It can be used to transform
+;;                     the contents of the file into a format more suitable
+;;                     for forms-mode processing.
+;;
+;;     forms-write-file-filter                 [symbol, default nil]
+;;                     If not nil: this should be the name of a 
+;;                     function that is called before the forms data file
+;;                     is written (saved) to disk.  It can be used to undo
+;;                     the effects of `forms-read-file-filter', if any.
+;;
+;;     forms-new-record-filter                 [symbol, default nil]
+;;                     If not nil: this should be the name of a 
+;;                     function that is called when a new
+;;                     record is created.  It can be used to fill in
+;;                     the new record with default fields, for example.
+;;
+;;     forms-modified-record-filter            [symbol, default nil]
+;;                     If not nil: this should be the name of a 
+;;                     function that is called when a record has
+;;                     been modified.  It is called after the fields
+;;                     are parsed.  It can be used to register
+;;                     modification dates, for example.
+;;
+;;     forms-use-text-properties               [bool, see text for default]
+;;                     This variable controls if forms mode should use
+;;                     text properties to protect the form text from being
+;;                     modified (using text-property `read-only').
+;;                     Also, the read-write fields are shown using a
+;;                     distinct face, if possible.
+;;                     As of emacs 19.29, the `intangible' text property
+;;                     is used to prevent moving into read-only fields.
+;;                     This variable defaults to t if running Emacs 19
+;;                     with text properties.
+;;                     The default face to show read-write fields is
+;;                     copied from face `region'.
+;;
+;;     forms-ro-face                           [symbol, default 'default]
+;;                     This is the face that is used to show
+;;                     read-only text on the screen.If used, this
+;;                     variable should be set to a symbol that is a
+;;                     valid face.
+;;                     E.g.
+;;                       (make-face 'my-face)
+;;                       (setq forms-ro-face 'my-face)
+;;
+;;     forms-rw-face                           [symbol, default 'region]
+;;                     This is the face that is used to show
+;;                     read-write text on the screen.
+;;
+;; After evaluating the control file, its buffer is cleared and used
+;; for further processing.
+;; The data file (as designated by `forms-file') is visited in a buffer
+;; `forms--file-buffer' which will not normally be shown.
+;; Great malfunctioning may be expected if this file/buffer is modified
+;; outside of this package while it is being visited!
+;;
+;; Normal operation is to transfer one line (record) from the data file,
+;; split it into fields (into `forms--the-record-list'), and display it
+;; using the specs in `forms-format-list'.
+;; A format routine `forms--format' is built upon startup to format 
+;; the records according to `forms-format-list'.
+;;
+;; When a form is changed the record is updated as soon as this form
+;; is left.  The contents of the form are parsed using information
+;; obtained from `forms-format-list', and the fields which are
+;; deduced from the form are modified.  Fields not shown on the forms
+;; retain their original values.  The newly formed record then
+;; replaces the contents of the old record in `forms--file-buffer'.
+;; A parse routine `forms--parser' is built upon startup to parse
+;; the records.
+;;
+;; Two exit functions exist: `forms-exit' and `forms-exit-no-save'.
+;; `forms-exit' saves the data to the file, if modified.
+;; `forms-exit-no-save` does not.  However, if `forms-exit-no-save'
+;; is executed and the file buffer has been modified, Emacs will ask
+;; questions anyway.
+;;
+;; Other functions provided by forms mode are:
+;;
+;;     paging (forward, backward) by record
+;;     jumping (first, last, random number)
+;;     searching
+;;     creating and deleting records
+;;     reverting the form (NOT the file buffer)
+;;     switching edit <-> view mode v.v.
+;;     jumping from field to field
+;;
+;; As an documented side-effect: jumping to the last record in the
+;; file (using forms-last-record) will adjust forms--total-records if
+;; needed.
+;;
+;; The forms buffer can be in on eof two modes: edit mode or view
+;; mode.  View mode is a read-only mode, you cannot modify the
+;; contents of the buffer.
+;;
+;; Edit mode commands:
+;; 
+;; TAB          forms-next-field
+;; \C-c TAB     forms-next-field
+;; \C-c <       forms-first-record
+;; \C-c >       forms-last-record
+;; \C-c ?       describe-mode
+;; \C-c \C-k    forms-delete-record
+;; \C-c \C-q    forms-toggle-read-only
+;; \C-c \C-o    forms-insert-record
+;; \C-c \C-l    forms-jump-record
+;; \C-c \C-n    forms-next-record
+;; \C-c \C-p    forms-prev-record
+;; \C-c \C-r    forms-search-backward
+;; \C-c \C-s    forms-search-forward
+;; \C-c \C-x    forms-exit
+;; 
+;; Read-only mode commands:
+;; 
+;; SPC          forms-next-record
+;; DEL  forms-prev-record
+;; ?    describe-mode
+;; \C-q forms-toggle-read-only
+;; l    forms-jump-record
+;; n    forms-next-record
+;; p    forms-prev-record
+;; r    forms-search-backward
+;; s    forms-search-forward
+;; x    forms-exit
+;; 
+;; Of course, it is also possible to use the \C-c prefix to obtain the
+;; same command keys as in edit mode.
+;; 
+;; The following bindings are available, independent of the mode: 
+;; 
+;; [next]        forms-next-record
+;; [prior]       forms-prev-record
+;; [begin]       forms-first-record
+;; [end]         forms-last-record
+;; [S-TAB]       forms-prev-field
+;; [backtab] forms-prev-field
+;;
+;; For convenience, TAB is always bound to `forms-next-field', so you
+;; don't need the C-c prefix for this command.
+;;
+;; As mentioned above (see `forms-forms-scroll' and `forms-forms-jump')
+;; the bindings of standard functions `scroll-up', `scroll-down',
+;; `beginning-of-buffer' and `end-of-buffer' can be locally replaced with
+;; forms mode functions next/prev record and first/last
+;; record.
+;;
+;; `local-write-file hook' is defined to save the actual data file
+;; instead of the buffer data, `revert-file-hook' is defined to
+;; revert a forms to original.
 \f
 ;;; Code:
 
 (provide 'forms)                       ;;; official
 (provide 'forms-mode)                  ;;; for compatibility
 
-(defconst forms-version (substring "$Revision: 2.13 $" 11 -2)
+(defconst forms-version (substring "$Revision: 2.27 $" 11 -2)
   "The version number of forms-mode (as string).  The complete RCS id is:
 
-  $Id: forms.el,v 2.13 1994/10/15 10:59:17 jv Exp $")
+  $Id: forms.el,v 2.27 1996/01/25 06:16:34 kwzh Exp kwzh $")
 
 (defvar forms-mode-hooks nil
   "Hook functions to be run upon entering Forms mode.")
 \f
 ;;; Optional variables with default values.
 
+(defvar forms-check-number-of-fields t
+  "*If non-nil, warn about records with wrong number of fields.")
+
 (defvar forms-field-sep "\t"
   "Field separator character (default TAB).")
 
@@ -618,7 +629,7 @@ Commands:                        Equivalent keys in read-only mode:
          (save-excursion
            (set-buffer forms--file-buffer)
            (make-variable-buffer-local 'local-write-file-hooks)
-           (setq local-write-file-hooks write-file-filter)))))
+           (setq local-write-file-hooks (list write-file-filter))))))
 
   ;; count the number of records, and set see if it may be modified
   (let (ro)
@@ -658,8 +669,8 @@ Commands:                        Equivalent keys in read-only mode:
        (insert 
         "GNU Emacs Forms Mode version " forms-version "\n\n"
         (if (file-exists-p forms-file)
-            (concat "No records available in file \"" forms-file "\".\n\n")
-          (format "Creating new file \"%s\"\nwith %d field%s per record.\n\n"
+            (concat "No records available in file `" forms-file "'\n\n")
+          (format "Creating new file `%s'\nwith %d field%s per record\n\n"
                   forms-file forms-number-of-fields
                   (if (= 1 forms-number-of-fields) "" "s")))
         "Use " (substitute-command-keys "\\[forms-insert-record]")
@@ -691,7 +702,7 @@ Commands:                        Equivalent keys in read-only mode:
   ;; of the fields on the display. This array is used by 
   ;; `forms--parser-using-text-properties' to extract the fields data
   ;; from the form on the screen.
-  ;; Upon completion, `forms-format-list' is garanteed correct, so
+  ;; Upon completion, `forms-format-list' is guaranteed correct, so
   ;; `forms--make-format' and `forms--make-parser' do not need to perform
   ;; any checks.
 
@@ -763,8 +774,8 @@ Commands:                        Equivalent keys in read-only mode:
          ;; Validate.
          (or (fboundp (car-safe el))
              (error (concat "Forms format error: "
-                            "not a function "
-                            (prin1-to-string (car-safe el)))))
+                            "not a function %S")
+                    (car-safe el)))
 
          ;; Shift.
          (if prev-item
@@ -775,8 +786,8 @@ Commands:                        Equivalent keys in read-only mode:
         ;; else
         (t
          (error (concat "Forms format error: "
-                        "invalid element "
-                        (prin1-to-string el)))))
+                        "invalid element %S")
+                el)))
 
        ;; Advance to next element of the list.
        (setq the-list rem)))
@@ -884,7 +895,7 @@ Commands:                        Equivalent keys in read-only mode:
                (,@ (if (numberp (car forms-format-list))
                        nil
                      '((add-text-properties (point-min) (1+ (point-min))
-                                            '(front-sticky (read-only))))))
+                                            '(front-sticky (read-only intangible))))))
                ;; Prevent insertion after the last text.
                (remove-text-properties (1- (point)) (point)
                                        '(rear-nonsticky)))
@@ -968,8 +979,10 @@ Commands:                        Equivalent keys in read-only mode:
           (point))
         (list 'face forms--ro-face     ; read-only appearance
               'read-only (,@ (list (1+ forms--marker)))
+              'intangible t
               'insert-in-front-hooks '(forms--iif-hook)
-              'rear-nonsticky '(face read-only insert-in-front-hooks))))))
+              'rear-nonsticky '(face read-only insert-in-front-hooks
+                                intangible))))))
     
    ((numberp el)
     (` ((let ((here (point)))
@@ -995,8 +1008,10 @@ Commands:                        Equivalent keys in read-only mode:
           (point))
         (list 'face forms--ro-face
               'read-only (,@ (list (1+ forms--marker)))
+              'intangible t
               'insert-in-front-hooks '(forms--iif-hook)
-              'rear-nonsticky '(read-only face insert-in-front-hooks))))))
+              'rear-nonsticky '(read-only face insert-in-front-hooks
+                                intangible))))))
 
    ;; end of cond
    ))
@@ -1079,9 +1094,9 @@ Commands:                        Equivalent keys in read-only mode:
        (if (setq there 
                  (next-single-property-change here 'read-only))
            (aset forms--recordv (aref forms--elements i)
-                 (buffer-substring here there))
+                 (buffer-substring-no-properties here there))
          (aset forms--recordv (aref forms--elements i)
-               (buffer-substring here (point-max)))))
+               (buffer-substring-no-properties here (point-max)))))
       (setq i (1+ i)))))
 
 (defun forms--make-parser-elt (el)
@@ -1103,7 +1118,7 @@ Commands:                        Equivalent keys in read-only mode:
   ;;     (setq here (point))
   ;;     (if (not (search-forward "\nmore text: " nil t nil))
   ;;       (error "Parse error: cannot find \"\\nmore text: \""))
-  ;;     (aset forms--recordv 5 (buffer-substring here (- (point) 12)))
+  ;;     (aset forms--recordv 5 (buffer-substring-no-properties here (- (point) 12)))
   ;;
   ;;    ;;  (tocol 40)
   ;;   (let ((forms--dyntext (car-safe forms--dynamic-text)))
@@ -1113,7 +1128,7 @@ Commands:                        Equivalent keys in read-only mode:
   ;;     (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
   ;;     ... 
   ;;     ;; final flush (due to terminator sentinel, see below)
-  ;;   (aset forms--recordv 7 (buffer-substring (point) (point-max)))
+  ;;   (aset forms--recordv 7 (buffer-substring-no-properties (point) (point-max)))
 
   (cond
    ((stringp el)
@@ -1123,7 +1138,7 @@ Commands:                        Equivalent keys in read-only mode:
                (if (not (search-forward (, el) nil t nil))
                    (error "Parse error: cannot find \"%s\"" (, el)))
                (aset forms--recordv (, (1- forms--field))
-                     (buffer-substring here
+                     (buffer-substring-no-properties here
                                        (- (point) (, (length el)))))))
          (` ((if (not (looking-at (, (regexp-quote el))))
                  (error "Parse error: not looking at \"%s\"" (, el)))
@@ -1139,7 +1154,7 @@ Commands:                        Equivalent keys in read-only mode:
    ((null el)
     (if forms--field
        (` ((aset forms--recordv (, (1- forms--field))
-                 (buffer-substring (point) (point-max)))))))
+                 (buffer-substring-no-properties (point) (point-max)))))))
    ((listp el)
     (prog1
        (if forms--field
@@ -1148,7 +1163,7 @@ Commands:                        Equivalent keys in read-only mode:
                  (if (not (search-forward forms--dyntext nil t nil))
                      (error "Parse error: cannot find \"%s\"" forms--dyntext))
                  (aset forms--recordv (, (1- forms--field))
-                       (buffer-substring here
+                       (buffer-substring-no-properties here
                                          (- (point) (length forms--dyntext)))))))
          (` ((let ((forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
                (if (not (looking-at (regexp-quote forms--dyntext)))
@@ -1229,7 +1244,6 @@ Commands:                        Equivalent keys in read-only mode:
   (define-key forms-mode-map "\C-x" 'forms-exit)
   (define-key forms-mode-map "<" 'forms-first-record)
   (define-key forms-mode-map ">" 'forms-last-record)
-  (define-key forms-mode-map "?" 'describe-mode)
   (define-key forms-mode-map "\C-?" 'forms-prev-record)
 
   ;; `forms-mode-ro-map' replaces the local map when in read-only mode.
@@ -1269,19 +1283,19 @@ Commands:                        Equivalent keys in read-only mode:
   (define-key map [menu-bar forms menu-forms-sep1]
     '("----"))
   (define-key map [menu-bar forms menu-forms-save]
-    '("Save data" . forms-save-buffer))
+    '("Save Data" . forms-save-buffer))
   (define-key map [menu-bar forms menu-forms-print]
-    '("Print data" . forms-print))
+    '("Print Data" . forms-print))
   (define-key map [menu-bar forms menu-forms-describe]
-    '("Describe mode" . describe-mode))
+    '("Describe Mode" . describe-mode))
   (define-key map [menu-bar forms menu-forms-toggle-ro]
     '("Toggle View/Edit" . forms-toggle-read-only))
   (define-key map [menu-bar forms menu-forms-jump-record]
     '("Jump" . forms-jump-record))
   (define-key map [menu-bar forms menu-forms-search-backward]
-    '("Search backward" . forms-search-backward))
+    '("Search Backward" . forms-search-backward))
   (define-key map [menu-bar forms menu-forms-search-forward]
-    '("Search forward" . forms-search-forward))
+    '("Search Forward" . forms-search-forward))
   (define-key map [menu-bar forms menu-forms-delete-record]
     '("Delete" . forms-delete-record))
   (define-key map [menu-bar forms menu-forms-insert-record]
@@ -1289,19 +1303,19 @@ Commands:                        Equivalent keys in read-only mode:
   (define-key map [menu-bar forms menu-forms-sep2]
     '("----"))
   (define-key map [menu-bar forms menu-forms-last-record]
-    '("Last record" . forms-last-record))
+    '("Last Record" . forms-last-record))
   (define-key map [menu-bar forms menu-forms-first-record]
-    '("First record" . forms-first-record))
+    '("First Record" . forms-first-record))
   (define-key map [menu-bar forms menu-forms-prev-record]
-    '("Previous record" . forms-prev-record))
+    '("Previous Record" . forms-prev-record))
   (define-key map [menu-bar forms menu-forms-next-record]
-    '("Next record" . forms-next-record))
+    '("Next Record" . forms-next-record))
   (define-key map [menu-bar forms menu-forms-sep3]
     '("----"))
   (define-key map [menu-bar forms menu-forms-prev-field]
-    '("Previous field" . forms-prev-field))
+    '("Previous Field" . forms-prev-field))
   (define-key map [menu-bar forms menu-forms-next-field]
-    '("Next field" . forms-next-field))
+    '("Next Field" . forms-next-field))
   (put 'forms-insert-record 'menu-enable '(not forms-read-only))
   (put 'forms-delete-record 'menu-enable '(not forms-read-only))
 )
@@ -1315,19 +1329,19 @@ Commands:                        Equivalent keys in read-only mode:
   (define-key map [menu-bar forms menu-forms-edit-sep1]
     '("----"))
   (define-key map [menu-bar forms menu-forms-edit-save]
-    '("Save data" . forms-save-buffer))
+    '("Save Data" . forms-save-buffer))
   (define-key map [menu-bar forms menu-forms-edit-print]
-    '("Print data" . forms-print))
+    '("Print Data" . forms-print))
   (define-key map [menu-bar forms menu-forms-edit-describe]
-    '("Describe mode" . describe-mode))
+    '("Describe Mode" . describe-mode))
   (define-key map [menu-bar forms menu-forms-edit-toggle-ro]
     '("Toggle View/Edit" . forms-toggle-read-only))
   (define-key map [menu-bar forms menu-forms-edit-jump-record]
     '("Jump" . forms-jump-record))
   (define-key map [menu-bar forms menu-forms-edit-search-backward]
-    '("Search backward" . forms-search-backward))
+    '("Search Backward" . forms-search-backward))
   (define-key map [menu-bar forms menu-forms-edit-search-forward]
-    '("Search forward" . forms-search-forward))
+    '("Search Forward" . forms-search-forward))
   (define-key map [menu-bar forms menu-forms-edit-delete-record]
     '("Delete" . forms-delete-record))
   (define-key map [menu-bar forms menu-forms-edit-insert-record]
@@ -1335,19 +1349,19 @@ Commands:                        Equivalent keys in read-only mode:
   (define-key map [menu-bar forms menu-forms-edit-sep2]
     '("----"))
   (define-key map [menu-bar forms menu-forms-edit-last-record]
-    '("Last record" . forms-last-record))
+    '("Last Record" . forms-last-record))
   (define-key map [menu-bar forms menu-forms-edit-first-record]
-    '("First record" . forms-first-record))
+    '("First Record" . forms-first-record))
   (define-key map [menu-bar forms menu-forms-edit-prev-record]
-    '("Previous record" . forms-prev-record))
+    '("Previous Record" . forms-prev-record))
   (define-key map [menu-bar forms menu-forms-edit-next-record]
-    '("Next record" . forms-next-record))
+    '("Next Record" . forms-next-record))
   (define-key map [menu-bar forms menu-forms-edit-sep3]
     '("----"))
   (define-key map [menu-bar forms menu-forms-edit-prev-field]
-    '("Previous field" . forms-prev-field))
+    '("Previous Field" . forms-prev-field))
   (define-key map [menu-bar forms menu-forms-edit-next-field]
-    '("Next field" . forms-next-field))
+    '("Next Field" . forms-next-field))
   (put 'forms-insert-record 'menu-enable '(not forms-read-only))
   (put 'forms-delete-record 'menu-enable '(not forms-read-only))
 )
@@ -1401,7 +1415,7 @@ Commands:                        Equivalent keys in read-only mode:
 
 (defun forms--help ()
   "Initial help for Forms mode."
-  (message (substitute-command-keys (concat
+  (message "%s" (substitute-command-keys (concat
   "\\[forms-next-record]:next"
   "   \\[forms-prev-record]:prev"
   "   \\[forms-first-record]:first"
@@ -1449,7 +1463,7 @@ Commands:                        Equivalent keys in read-only mode:
   (let ((here (point)))
     (prog2
      (end-of-line)
-     (buffer-substring here (point))
+     (buffer-substring-no-properties here (point))
      (goto-char here))))
 
 (defun forms--show-record (the-record)
@@ -1480,9 +1494,11 @@ Commands:                        Equivalent keys in read-only mode:
   ;; Verify the number of fields, extend forms--the-record-list if needed.
   (if (= (length forms--the-record-list) forms-number-of-fields)
       nil
-    (beep)
-    (message "Warning: this record has %d fields instead of %d"
-            (length forms--the-record-list) forms-number-of-fields)
+    (if (null forms-check-number-of-fields)
+       nil
+      (beep)
+      (message "Warning: this record has %d fields instead of %d"
+              (length forms--the-record-list) forms-number-of-fields))
     (if (< (length forms--the-record-list) forms-number-of-fields)
        (setq forms--the-record-list 
              (append forms--the-record-list
@@ -1691,7 +1707,7 @@ As a side effect: re-calculates the number of records in the data file."
 (defun forms-toggle-read-only (arg)
   "Toggles read-only mode of a forms mode buffer.
 With an argument, enables read-only mode if the argument is positive.
-Otherwise enables edit mode if the visited file is writeable."
+Otherwise enables edit mode if the visited file is writable."
 
   (interactive "P")
 
@@ -1708,7 +1724,7 @@ Otherwise enables edit mode if the visited file is writeable."
              buffer-read-only)
            (progn
              (setq forms-read-only t)
-             (message "No write access to \"%s\"" forms-file)
+             (message "No write access to `%s'" forms-file)
              (beep))
          (setq forms-read-only nil))
        (if (equal ro forms-read-only)
@@ -1817,7 +1833,7 @@ it is called to fill (some of) the fields with default values."
          (if (null (re-search-forward regexp nil t))
              (progn
                (goto-char here)
-               (message (concat "\"" regexp "\" not found."))
+               (message "\"%s\" not found" regexp)
                nil)
            (setq the-record (forms--get-record))
            (setq the-line (1+ (count-lines (point-min) (point))))))
@@ -1849,7 +1865,7 @@ it is called to fill (some of) the fields with default values."
          (if (null (re-search-backward regexp nil t))
              (progn
                (goto-char here)
-               (message (concat "\"" regexp "\" not found."))
+               (message "\"%s\" not found" regexp)
                nil)
            (setq the-record (forms--get-record))
            (setq the-line (1+ (count-lines (point-min) (point))))))
@@ -1891,7 +1907,8 @@ Calls `forms-write-file-filter' before writing out the data."
   (let ((i 0)
        (here (point))
        there
-       (cnt 0))
+       (cnt 0)
+       (inhibit-point-motion-hooks t))
 
     (if (zerop arg)
        (setq cnt 1)
@@ -1917,7 +1934,8 @@ Calls `forms-write-file-filter' before writing out the data."
   (let ((i (length forms--markers))
        (here (point))
        there
-       (cnt 0))
+       (cnt 0)
+       (inhibit-point-motion-hooks t))
 
     (if (zerop arg)
        (setq cnt 1)
@@ -1967,7 +1985,7 @@ Calls `forms-write-file-filter' before writing out the data."
 (defun forms-enumerate (the-fields)
   "Take a quoted list of symbols, and set their values to sequential numbers.
 The first symbol gets number 1, the second 2 and so on.
-It returns the higest number.
+It returns the highest number.
 
 Usage: (setq forms-number-of-fields
              (forms-enumerate