Initial Commit
[packages] / xemacs-packages / forms / forms.el
1 ;;; forms.el --- Forms mode: edit a file as a form to fill in
2
3 ;; Copyright (C) 1991, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
4
5 ;; Author: Johan Vromans <jvromans@squirrel.nl>
6 ;; hacked on by jwz for XEmacs
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Synched up with: forms.el version 2.37 from the GNU Emacs tree.
26
27 ;;; Commentary:
28
29 ;; Visit a file using a form.
30 ;;
31 ;; === Naming conventions
32 ;;
33 ;; The names of all variables and functions start with 'forms-'.
34 ;; Names which start with 'forms--' are intended for internal use, and
35 ;; should *NOT* be used from the outside.
36 ;;
37 ;; All variables are buffer-local, to enable multiple forms visits 
38 ;; simultaneously.
39 ;; Variable `forms--mode-setup' is local to *ALL* buffers, for it 
40 ;; controls if forms-mode has been enabled in a buffer.
41 ;;
42 ;; === How it works ===
43 ;;
44 ;; Forms mode means visiting a data file which is supposed to consist
45 ;; of records each containing a number of fields.  The records are
46 ;; separated by a newline, the fields are separated by a user-defined
47 ;; field separator (default: TAB).
48 ;; When shown, a record is transferred to an Emacs buffer and
49 ;; presented using a user-defined form.  One record is shown at a
50 ;; time.
51 ;;
52 ;; Forms mode is a composite mode.  It involves two files, and two
53 ;; buffers.
54 ;; The first file, called the control file, defines the name of the
55 ;; data file and the forms format.  This file buffer will be used to
56 ;; present the forms.
57 ;; The second file holds the actual data.  The buffer of this file
58 ;; will be buried, for it is never accessed directly.
59 ;;
60 ;; Forms mode is invoked using M-x forms-find-file control-file .
61 ;; Alternatively `forms-find-file-other-window' can be used.
62 ;;
63 ;; You may also visit the control file, and switch to forms mode by hand
64 ;; with M-x forms-mode .
65 ;;
66 ;; Automatic mode switching is supported if you specify 
67 ;; "-*- forms -*-" in the first line of the control file.
68 ;; 
69 ;; The control file is visited, evaluated using `eval-current-buffer',
70 ;; and should set at least the following variables:
71 ;;
72 ;;      forms-file                              [string]
73 ;;                      The name of the data file.
74 ;;
75 ;;      forms-number-of-fields                  [integer]
76 ;;                      The number of fields in each record.
77 ;;
78 ;;      forms-format-list                       [list]
79 ;;                      Formatting instructions.
80 ;;
81 ;; `forms-format-list' should be a list, each element containing
82 ;;
83 ;;   - a string, e.g. "hello".  The string is inserted in the forms
84 ;;      "as is".
85 ;;   
86 ;;   - an integer, denoting a field number.
87 ;;      The contents of this field are inserted at this point.
88 ;;     Fields are numbered starting with number one.
89 ;;   
90 ;;   - a function call, e.g. (insert "text").
91 ;;      This function call is dynamically evaluated and should return a
92 ;;     string.  It should *NOT* have side-effects on the forms being
93 ;;     constructed.  The current fields are available to the function
94 ;;     in the variable `forms-fields', they should *NOT* be modified.
95 ;;   
96 ;;   - a lisp symbol, that must evaluate to one of the above.
97 ;;
98 ;; Optional variables which may be set in the control file:
99 ;;
100 ;;      forms-field-sep                         [string, default TAB]
101 ;;                      The field separator used to separate the
102 ;;                      fields in the data file.  It may be a string.
103 ;;
104 ;;      forms-read-only                         [bool, default nil]
105 ;;                      Non-nil means that the data file is visited
106 ;;                      read-only (view mode) as opposed to edit mode.
107 ;;                      If no write access to the data file is
108 ;;                      possible, view mode is enforced. 
109 ;;
110 ;;      forms-check-number-of-fields            [bool, default t]
111 ;;                      If non-nil, a warning will be issued whenever
112 ;;                      a record is found that does not have the number
113 ;;                      of fields specified by `forms-number-of-fields'.
114 ;;
115 ;;      forms-multi-line                        [string, default "^K"]
116 ;;                      If non-null the records of the data file may
117 ;;                      contain fields that can span multiple lines in
118 ;;                      the form.
119 ;;                      This variable denotes the separator character
120 ;;                      to be used for this purpose.  Upon display, all
121 ;;                      occurrences of this character are translated
122 ;;                      to newlines.  Upon storage they are translated
123 ;;                      back to the separator character.
124 ;;
125 ;;      forms-forms-scroll                      [bool, default nil]
126 ;;                      Non-nil means: rebind locally the commands that
127 ;;                      perform `scroll-up' or `scroll-down' to use
128 ;;                      `forms-next-field' resp. `forms-prev-field'.
129 ;;
130 ;;      forms-forms-jump                        [bool, default nil]
131 ;;                      Non-nil means: rebind locally the commands that
132 ;;                      perform `beginning-of-buffer' or `end-of-buffer'
133 ;;                      to perform `forms-first-field' and `forms-last-field'.
134 ;;
135 ;;      forms-insert-after                      [bool, default nil]
136 ;;                      Non-nil means: inserts of new records go after
137 ;;                      current record, also initial position is at last
138 ;;                      record.
139 ;;
140 ;;      forms-read-file-filter                  [symbol, default nil]
141 ;;                      If not nil: this should be the name of a 
142 ;;                      function that is called after the forms data file
143 ;;                      has been read.  It can be used to transform
144 ;;                      the contents of the file into a format more suitable
145 ;;                      for forms-mode processing.
146 ;;
147 ;;      forms-write-file-filter                 [symbol, default nil]
148 ;;                      If not nil: this should be the name of a 
149 ;;                      function that is called before the forms data file
150 ;;                      is written (saved) to disk.  It can be used to undo
151 ;;                      the effects of `forms-read-file-filter', if any.
152 ;;
153 ;;      forms-new-record-filter                 [symbol, default nil]
154 ;;                      If not nil: this should be the name of a 
155 ;;                      function that is called when a new
156 ;;                      record is created.  It can be used to fill in
157 ;;                      the new record with default fields, for example.
158 ;;
159 ;;      forms-modified-record-filter            [symbol, default nil]
160 ;;                      If not nil: this should be the name of a 
161 ;;                      function that is called when a record has
162 ;;                      been modified.  It is called after the fields
163 ;;                      are parsed.  It can be used to register
164 ;;                      modification dates, for example.
165 ;;
166 ;;      forms-use-extents                       [bool, see text for default]
167 ;;      forms-use-text-properties               [bool, see text for default]
168 ;;                      These variables control if forms mode should use
169 ;;                      text properties to protect the form text from being
170 ;;                      modified (using text-property `read-only').
171 ;;                      Also, the read-write fields are shown using a
172 ;;                      distinct face, if possible.
173 ;;                      As of emacs 19.29, the `intangible' text property
174 ;;                      is used to prevent moving into read-only fields.
175 ;;                      This variable defaults to t if running Emacs 19
176 ;;                      with text properties.
177 ;;                      The default face to show read-write fields is
178 ;;                      copied from face `region'.
179 ;;
180 ;;      forms-ro-face                           [symbol, default 'default]
181 ;;                      This is the face that is used to show
182 ;;                      read-only text on the screen.If used, this
183 ;;                      variable should be set to a symbol that is a
184 ;;                      valid face.
185 ;;                      E.g.
186 ;;                        (make-face 'my-face)
187 ;;                        (setq forms-ro-face 'my-face)
188 ;;
189 ;;      forms-rw-face                           [symbol, default 'region]
190 ;;                      This is the face that is used to show
191 ;;                      read-write text on the screen.
192 ;;
193 ;; After evaluating the control file, its buffer is cleared and used
194 ;; for further processing.
195 ;; The data file (as designated by `forms-file') is visited in a buffer
196 ;; `forms--file-buffer' which will not normally be shown.
197 ;; Great malfunctioning may be expected if this file/buffer is modified
198 ;; outside of this package while it is being visited!
199 ;;
200 ;; Normal operation is to transfer one line (record) from the data file,
201 ;; split it into fields (into `forms--the-record-list'), and display it
202 ;; using the specs in `forms-format-list'.
203 ;; A format routine `forms--format' is built upon startup to format 
204 ;; the records according to `forms-format-list'.
205 ;;
206 ;; When a form is changed the record is updated as soon as this form
207 ;; is left.  The contents of the form are parsed using information
208 ;; obtained from `forms-format-list', and the fields which are
209 ;; deduced from the form are modified.  Fields not shown on the forms
210 ;; retain their original values.  The newly formed record then
211 ;; replaces the contents of the old record in `forms--file-buffer'.
212 ;; A parse routine `forms--parser' is built upon startup to parse
213 ;; the records.
214 ;;
215 ;; Two exit functions exist: `forms-exit' and `forms-exit-no-save'.
216 ;; `forms-exit' saves the data to the file, if modified.
217 ;; `forms-exit-no-save` does not.  However, if `forms-exit-no-save'
218 ;; is executed and the file buffer has been modified, Emacs will ask
219 ;; questions anyway.
220 ;;
221 ;; Other functions provided by forms mode are:
222 ;;
223 ;;      paging (forward, backward) by record
224 ;;      jumping (first, last, random number)
225 ;;      searching
226 ;;      creating and deleting records
227 ;;      reverting the form (NOT the file buffer)
228 ;;      switching edit <-> view mode v.v.
229 ;;      jumping from field to field
230 ;;
231 ;; As an documented side-effect: jumping to the last record in the
232 ;; file (using forms-last-record) will adjust forms--total-records if
233 ;; needed.
234 ;;
235 ;; The forms buffer can be in on eof two modes: edit mode or view
236 ;; mode.  View mode is a read-only mode, you cannot modify the
237 ;; contents of the buffer.
238 ;;
239 ;; Edit mode commands:
240 ;; 
241 ;; TAB           forms-next-field
242 ;; \C-c TAB      forms-next-field
243 ;; \C-c <        forms-first-record
244 ;; \C-c >        forms-last-record
245 ;; \C-c ?        describe-mode
246 ;; \C-c \C-k     forms-delete-record
247 ;; \C-c \C-q     forms-toggle-read-only
248 ;; \C-c \C-o     forms-insert-record
249 ;; \C-c \C-l     forms-jump-record
250 ;; \C-c \C-n     forms-next-record
251 ;; \C-c \C-p     forms-prev-record
252 ;; \C-c \C-r     forms-search-backward
253 ;; \C-c \C-s     forms-search-forward
254 ;; \C-c \C-x     forms-exit
255 ;; 
256 ;; Read-only mode commands:
257 ;; 
258 ;; SPC   forms-next-record
259 ;; DEL   forms-prev-record
260 ;; ?     describe-mode
261 ;; \C-q forms-toggle-read-only
262 ;; l     forms-jump-record
263 ;; n     forms-next-record
264 ;; p     forms-prev-record
265 ;; r     forms-search-backward
266 ;; s     forms-search-forward
267 ;; x     forms-exit
268 ;; 
269 ;; Of course, it is also possible to use the \C-c prefix to obtain the
270 ;; same command keys as in edit mode.
271 ;; 
272 ;; The following bindings are available, independent of the mode: 
273 ;; 
274 ;; [next]         forms-next-record
275 ;; [prior]        forms-prev-record
276 ;; [begin]        forms-first-record
277 ;; [end]          forms-last-record
278 ;; [S-TAB]        forms-prev-field
279 ;; [backtab] forms-prev-field
280 ;;
281 ;; For convenience, TAB is always bound to `forms-next-field', so you
282 ;; don't need the C-c prefix for this command.
283 ;;
284 ;; As mentioned above (see `forms-forms-scroll' and `forms-forms-jump')
285 ;; the bindings of standard functions `scroll-up', `scroll-down',
286 ;; `beginning-of-buffer' and `end-of-buffer' can be locally replaced with
287 ;; forms mode functions next/prev record and first/last
288 ;; record.
289 ;;
290 ;; `local-write-file-hook' is defined to save the actual data file
291 ;; instead of the buffer data, `revert-file-hook' is defined to
292 ;; revert a forms to original.
293 \f
294 ;;; Code:
295
296 (defgroup forms nil
297   "Edit a file as a form to fill in."
298   :group 'data)
299
300 ;;; Global variables and constants:
301
302 (provide 'forms)                        ;;; official
303 (provide 'forms-mode)                   ;;; for compatibility
304
305 (defconst forms-version (substring "$Revision: 1.3 $" 11 -2)
306   "The version number of forms-mode (as string).  The complete RCS id is:
307
308   $Id: forms.el,v 1.3 1999-12-04 11:31:33 andreasj Exp $")
309
310 (defcustom forms-mode-hooks nil
311   "Hook functions to be run upon entering Forms mode."
312   :group 'forms
313   :type 'function)
314 \f
315 ;;; Mandatory variables - must be set by evaluating the control file.
316
317 (defvar forms-file nil
318   "Name of the file holding the data.")
319
320 (defvar forms-format-list nil
321   "List of formatting specifications.")
322
323 (defvar forms-number-of-fields nil
324   "Number of fields per record.")
325 \f
326 ;;; Optional variables with default values.
327
328 (defcustom forms-check-number-of-fields t
329   "*If non-nil, warn about records with wrong number of fields."
330   :group 'forms
331   :type 'boolean)
332
333 (defvar forms-field-sep "\t"
334   "Field separator character (default TAB).")
335
336 (defvar forms-read-only nil
337   "Non-nil means: visit the file in view (read-only) mode.
338 This is set automatically if the file permissions don't let you write it.")
339
340 (defvar forms-multi-line "\C-k"
341   "If not nil: use this character to separate multi-line fields
342 (default C-k).")
343
344 (defcustom forms-forms-scroll nil
345   "*Non-nil means replace scroll-up/down commands in Forms mode.
346 The replacement commands performs forms-next/prev-record."
347   :group 'forms
348   :type 'boolean)
349
350 (defcustom forms-forms-jump nil
351   "*Non-nil means redefine beginning/end-of-buffer in Forms mode.
352 The replacement commands performs forms-first/last-record."
353   :group 'forms
354   :type 'boolean)
355
356 (defvar forms-read-file-filter nil
357   "The name of a function that is called after reading the data file.
358 This can be used to change the contents of the file to something more
359 suitable for forms processing.")
360
361 (defvar forms-write-file-filter nil
362   "The name of a function that is called before writing the data file.
363 This can be used to undo the effects of `form-read-file-hook'.")
364
365 (defvar forms-new-record-filter nil
366   "The name of a function that is called when a new record is created.")
367
368 (defvar forms-modified-record-filter nil
369   "The name of a function that is called when a record has been modified.")
370
371 (defvar forms-fields nil
372   "List with fields of the current forms.  First field has number 1.
373 This variable is for use by the filter routines only. 
374 The contents may NOT be modified.")
375
376 (defcustom forms-use-colors t
377   "*Non-nil means Emacs should attempt to use text-properties (GNU
378  Emacs) or extents (XEmacs) if they are available."
379   :group 'forms
380   :type 'boolean)
381
382 (defcustom forms-use-extents (and forms-use-colors
383                                   (fboundp 'set-extent-property)); XEmacs 19.9+
384   "*Non-nil means: use XEmacs/Lucid Emacs extents.
385 Defaults to t if this Emacs is capable of handling extents."
386   :group 'forms
387   :type 'boolean)
388
389 (defcustom forms-use-text-properties (and forms-use-colors
390                                           (not forms-use-extents); XEmacs 19.9+
391                                           (fboundp 'set-text-properties))
392   "*Non-nil means: use text properties.
393 Defaults to t if this Emacs is capable of handling text properties."
394   :group 'forms
395   :type 'boolean)
396
397 (defcustom forms-insert-after nil
398   "*Non-nil means: inserts of new records go after current record.
399 Also, initial position is at last record."
400   :group 'forms
401   :type 'boolean)
402
403 (defface forms-label-face '((((class color))
404                              (:foreground "Red"))
405                             (t (:underline t)))
406   "Face used for marked labels."
407   :group 'forms)
408
409 (defface forms-field-face '((((class color))
410                              (:foreground "Black"))
411                             (t (:underline t)))
412   "Face used for marked fields."
413   :group 'forms)
414
415 (defcustom forms-ro-face (if (string-match "XEmacs" emacs-version)
416                              'forms-label-face
417                            'default)
418   "The face (a symbol) that is used to display read-only text on the screen."
419   :group 'forms
420   :type 'face)
421
422 (defcustom forms-rw-face (if (string-match "XEmacs" emacs-version)
423                              'forms-field-face
424                            'region)
425   "The face (a symbol) that is used to display read-write text on the screen."
426   :group 'forms
427   :type 'face)
428 \f
429 ;;; Internal variables.
430
431 (defvar forms--xemacs-p (string-match "XEmacs" emacs-version))
432
433 (defvar forms--file-buffer nil
434   "Buffer which holds the file data")
435
436 (defvar forms--total-records 0
437   "Total number of records in the data file.")
438
439 (defvar forms--current-record 0
440   "Number of the record currently on the screen.")
441
442 (defvar forms-mode-map nil
443    "Keymap for form buffer.")
444 (defvar forms-mode-ro-map nil
445    "Keymap for form buffer in view mode.")
446 (defvar forms-mode-edit-map nil
447    "Keymap for form buffer in edit mode.")
448
449 (defvar forms--markers nil
450   "Field markers in the screen.")
451
452 (defvar forms--dyntexts nil
453   "Dynamic texts (resulting from function calls) on the screen.")
454
455 (defvar forms--the-record-list nil 
456    "List of strings of the current record, as parsed from the file.")
457
458 (defvar forms--search-regexp nil
459   "Last regexp used by forms-search functions.")
460
461 (defvar forms--format nil
462   "Formatting routine.")
463
464 (defvar forms--parser nil
465   "Forms parser routine.")
466
467 (defvar forms--mode-setup nil
468   "To keep track of forms-mode being set-up.")
469 (make-variable-buffer-local 'forms--mode-setup)
470
471 (defvar forms--dynamic-text nil
472   "Array that holds dynamic texts to insert between fields.")
473
474 (defvar forms--elements nil
475   "Array with the order in which the fields are displayed.")
476
477 (defvar forms--ro-face nil
478   "Face used to represent read-only data on the screen.")
479
480 (defvar forms--rw-face nil
481   "Face used to represent read-write data on the screen.")
482 \f
483 ;;;###autoload 
484 (defun forms-mode (&optional primary)
485   "Major mode to visit files in a field-structured manner using a form.
486
487 Commands:                        Equivalent keys in read-only mode:
488  TAB            forms-next-field          TAB
489  C-c TAB        forms-next-field          
490  C-c <          forms-first-record         <
491  C-c >          forms-last-record          >
492  C-c ?          describe-mode              ?
493  C-c C-k        forms-delete-record
494  C-c C-q        forms-toggle-read-only     q
495  C-c C-o        forms-insert-record
496  C-c C-l        forms-jump-record          l
497  C-c C-n        forms-next-record          n
498  C-c C-p        forms-prev-record          p
499  C-c C-r        forms-search-reverse       r
500  C-c C-s        forms-search-forward       s
501  C-c C-x        forms-exit                 x
502 "
503   (interactive)
504
505   ;; This is not a simple major mode, as usual.  Therefore, forms-mode
506   ;; takes an optional argument `primary' which is used for the
507   ;; initial set-up.  Normal use would leave `primary' to nil.
508   ;; A global buffer-local variable `forms--mode-setup' has the same
509   ;; effect but makes it possible to auto-invoke forms-mode using
510   ;; `find-file'.
511   ;; Note: although it seems logical to have `make-local-variable'
512   ;; executed where the variable is first needed, I have deliberately
513   ;; placed all calls in this function.
514
515   ;; Primary set-up: evaluate buffer and check if the mandatory
516   ;; variables have been set.
517   (if (or primary (not forms--mode-setup))
518       (progn
519         ;;(message "forms: setting up...")
520         (kill-all-local-variables)
521
522         ;; Make mandatory variables.
523         (make-local-variable 'forms-file)
524         (make-local-variable 'forms-number-of-fields)
525         (make-local-variable 'forms-format-list)
526
527         ;; Make optional variables.
528         (make-local-variable 'forms-field-sep)
529         (make-local-variable 'forms-read-only)
530         (make-local-variable 'forms-multi-line)
531         (make-local-variable 'forms-forms-scroll)
532         (make-local-variable 'forms-forms-jump)
533         (make-local-variable 'forms-insert-after)
534         (make-local-variable 'forms-use-extents)
535         (make-local-variable 'forms-use-text-properties)
536
537         ;; Filter functions.
538         (make-local-variable 'forms-read-file-filter)
539         (make-local-variable 'forms-write-file-filter)
540         (make-local-variable 'forms-new-record-filter)
541         (make-local-variable 'forms-modified-record-filter)
542
543         ;; Make sure no filters exist.
544         (setq forms-read-file-filter nil)
545         (setq forms-write-file-filter nil)
546         (setq forms-new-record-filter nil)
547         (setq forms-modified-record-filter nil)
548
549         (if forms--xemacs-p ; XEmacs
550             (progn
551               ;; forms-field-face defaults to bold.
552               ;; forms-label-face defaults to no attributes
553               ;;   (inherit from default.)
554               (make-face 'forms-field-face)
555               (make-face 'forms-label-face)
556               (if (face-differs-from-default-p 'forms-field-face)
557                   nil
558                 (copy-face 'bold 'forms-field-face)
559                 ;;(set-face-underline-p 'forms-field-face t)
560                 )))
561
562         ;; If running Emacs 19 under X, setup faces to show read-only and 
563         ;; read-write fields.
564         (if (fboundp 'make-face)
565             (progn
566               (make-local-variable 'forms-ro-face)
567               (make-local-variable 'forms-rw-face)))
568
569         ;; eval the buffer, should set variables
570         ;;(message "forms: processing control file...")
571         ;; If enable-local-eval is not set to t the user is asked first.
572         (if (or (eq enable-local-eval t)
573                 (yes-or-no-p 
574                  (concat "Evaluate lisp code in buffer "
575                          (buffer-name) " to display forms ")))
576             (eval-current-buffer)
577           (error "`enable-local-eval' inhibits buffer evaluation"))
578
579         ;; Check if the mandatory variables make sense.
580         (or forms-file
581             (error (concat "Forms control file error: " 
582                            "`forms-file' has not been set")))
583
584         ;; Check forms-field-sep first, since it can be needed to
585         ;; construct a default format list.
586         (or (stringp forms-field-sep)
587             (error (concat "Forms control file error: "
588                            "`forms-field-sep' is not a string")))
589
590         (if forms-number-of-fields
591             (or (and (numberp forms-number-of-fields)
592                      (> forms-number-of-fields 0))
593                 (error (concat "Forms control file error: "
594                                "`forms-number-of-fields' must be a number > 0")))
595           (or (null forms-format-list)
596               (error (concat "Forms control file error: "
597                              "`forms-number-of-fields' has not been set"))))
598
599         (or forms-format-list
600             (forms--intuit-from-file))
601
602         (if forms-multi-line
603             (if (and (stringp forms-multi-line)
604                      (eq (length forms-multi-line) 1))
605                 (if (string= forms-multi-line forms-field-sep)
606                     (error (concat "Forms control file error: " 
607                                    "`forms-multi-line' is equal to 'forms-field-sep'")))
608               (error (concat "Forms control file error: "
609                              "`forms-multi-line' must be nil or a one-character string"))))
610             
611         ;; Validate and process forms-format-list.
612         ;;(message "forms: pre-processing format list...")
613         (make-local-variable 'forms--elements)
614         (forms--process-format-list)
615
616         ;; Build the formatter and parser.
617         ;;(message "forms: building formatter...")
618         (make-local-variable 'forms--format)
619         (make-local-variable 'forms--markers)
620         (make-local-variable 'forms--dyntexts)
621         ;;(message "forms: building parser...")
622         (forms--make-format)
623         (make-local-variable 'forms--parser)
624         (forms--make-parser)
625         ;;(message "forms: building parser... done.")
626
627         ;; Check if record filters are defined.
628         (if (and forms-new-record-filter
629                  (not (fboundp forms-new-record-filter)))
630             (error (concat "Forms control file error: "
631                            "`forms-new-record-filter' is not a function")))
632
633         (if (and forms-modified-record-filter
634                  (not (fboundp forms-modified-record-filter)))
635             (error (concat "Forms control file error: "
636                            "`forms-modified-record-filter' is not a function")))
637
638         ;; The filters acces the contents of the forms using `forms-fields'.
639         (make-local-variable 'forms-fields)
640
641         ;; Dynamic text support.
642         (make-local-variable 'forms--dynamic-text)
643
644         ;; Prevent accidental overwrite of the control file and auto-save.
645         (set-visited-file-name nil)
646
647         ;; Prepare this buffer for further processing.
648         (setq buffer-read-only nil)
649         (erase-buffer)
650
651         ;;(message "forms: setting up... done.")
652         ))
653
654   ;; initialization done
655   (setq forms--mode-setup t)
656
657   ;; Copy desired faces to the actual variables used by the forms formatter.
658   (if (fboundp 'make-face)
659       (progn
660         (make-local-variable 'forms--ro-face)
661         (make-local-variable 'forms--rw-face)
662         (if forms-read-only
663             (progn
664               (setq forms--ro-face forms-ro-face)
665               (setq forms--rw-face forms-ro-face))
666           (setq forms--ro-face forms-ro-face)
667           (setq forms--rw-face forms-rw-face))))
668
669   ;; Make more local variables.
670   (make-local-variable 'forms--file-buffer)
671   (make-local-variable 'forms--total-records)
672   (make-local-variable 'forms--current-record)
673   (make-local-variable 'forms--the-record-list)
674   (make-local-variable 'forms--search-regexp)
675
676   ; The keymaps are global, so multiple forms mode buffers can share them.
677   ;(make-local-variable 'forms-mode-map)
678   ;(make-local-variable 'forms-mode-ro-map)
679   ;(make-local-variable 'forms-mode-edit-map)
680   (if forms-mode-map                    ; already defined
681       nil
682     ;;(message "forms: building keymap...")
683     (forms--mode-commands)
684     ;;(message "forms: building keymap... done.")
685     )
686
687   ;; set the major mode indicator
688   (setq major-mode 'forms-mode)
689   (setq mode-name "Forms")
690
691   ;; find the data file
692   (setq forms--file-buffer (find-file-noselect forms-file))
693
694   ;; Pre-transform.
695   (let ((read-file-filter forms-read-file-filter)
696         (write-file-filter forms-write-file-filter))
697     (if read-file-filter
698         (save-excursion
699           (set-buffer forms--file-buffer)
700           (let ((inhibit-read-only t)
701                 (file-modified (buffer-modified-p)))
702             (run-hooks 'read-file-filter)
703             (if (not file-modified) (set-buffer-modified-p nil)))
704           (if write-file-filter
705               (progn
706                 (make-variable-buffer-local 'local-write-file-hooks)
707                 (add-hook 'local-write-file-hooks write-file-filter))))
708       (if write-file-filter
709           (save-excursion
710             (set-buffer forms--file-buffer)
711             (make-variable-buffer-local 'local-write-file-hooks)
712             (add-hook 'local-write-file-hooks write-file-filter)))))
713
714   ;; count the number of records, and set see if it may be modified
715   (let (ro)
716     (setq forms--total-records
717           (save-excursion
718             (prog1
719                 (progn
720                   ;;(message "forms: counting records...")
721                   (set-buffer forms--file-buffer)
722                   (bury-buffer (current-buffer))
723                   (setq ro buffer-read-only)
724                   (count-lines (point-min) (point-max)))
725               ;;(message "forms: counting records... done.")
726               )))
727     (if ro
728         (setq forms-read-only t)))
729
730   ;;(message "forms: proceeding setup...")
731
732   ;; Since we aren't really implementing a minor mode, we hack the modeline
733   ;; directly to get the text " View " into forms-read-only form buffers.  For
734   ;; that reason, this variable must be buffer only.
735   (make-local-variable 'minor-mode-alist)
736   (setq minor-mode-alist (list (list 'forms-read-only " View")))
737
738   ;;(message "forms: proceeding setup (keymaps)...")
739   (forms--set-keymaps)
740   ;;(message "forms: proceeding setup (commands)...")
741   (forms--change-commands)
742
743   ;;(message "forms: proceeding setup (buffer)...")
744   (set-buffer-modified-p nil)
745
746   (if (= forms--total-records 0)
747       ;;(message "forms: proceeding setup (new file)...")
748       (progn
749         (insert 
750          "GNU Emacs Forms Mode version " forms-version "\n\n"
751          (if (file-exists-p forms-file)
752              (concat "No records available in file `" forms-file "'\n\n")
753            (format "Creating new file `%s'\nwith %d field%s per record\n\n"
754                    forms-file forms-number-of-fields
755                    (if (= 1 forms-number-of-fields) "" "s")))
756          "Use " (substitute-command-keys "\\[forms-insert-record]")
757          " to create new records.\n")
758         (setq forms--current-record 1)
759         (setq buffer-read-only t)
760         (set-buffer-modified-p nil))
761
762     ;; setup the first (or current) record to show
763     (if (< forms--current-record 1)
764         (setq forms--current-record 1))
765     (forms-jump-record forms--current-record)
766     )
767
768   (if forms-insert-after
769       (forms-last-record)
770     (forms-first-record))
771
772   ;; user customising
773   ;;(message "forms: proceeding setup (user hooks)...")
774   (run-hooks 'forms-mode-hooks)
775   ;;(message "forms: setting up... done.")
776
777   ;; be helpful
778   (forms--help)
779 )
780 \f
781 (defun forms--process-format-list ()
782   ;; Validate `forms-format-list' and set some global variables.
783   ;; Symbols in the list are evaluated, and consecutive strings are
784   ;; concatenated.
785   ;; Array `forms--elements' is constructed that contains the order
786   ;; of the fields on the display. This array is used by 
787   ;; `forms--parser-using-text-properties' to extract the fields data
788   ;; from the form on the screen.
789   ;; Upon completion, `forms-format-list' is guaranteed correct, so
790   ;; `forms--make-format' and `forms--make-parser' do not need to perform
791   ;; any checks.
792
793   ;; Verify that `forms-format-list' is not nil.
794   (or forms-format-list
795       (error (concat "Forms control file error: "
796                      "`forms-format-list' has not been set")))
797   ;; It must be a list.
798   (or (listp forms-format-list)
799       (error (concat "Forms control file error: "
800                      "`forms-format-list' is not a list")))
801
802   ;; Assume every field is painted once.
803   ;; `forms--elements' will grow if needed.
804   (setq forms--elements (make-vector forms-number-of-fields nil))
805
806   (let ((the-list forms-format-list)    ; the list of format elements
807         (this-item 0)                   ; element in list
808         (prev-item nil)
809         (field-num 0))                  ; highest field number 
810
811     (setq forms-format-list nil)        ; gonna rebuild
812
813     (while the-list
814
815       (let ((el (car-safe the-list))
816             (rem (cdr-safe the-list)))
817
818         ;; If it is a symbol, eval it first.
819         (if (and (symbolp el)
820                  (boundp el))
821             (setq el (eval el)))
822
823         (cond
824
825          ;; Try string ...
826          ((stringp el)
827           (if (stringp prev-item)       ; try to concatenate strings
828               (setq prev-item (concat prev-item el))
829             (if prev-item
830                 (setq forms-format-list
831                       (append forms-format-list (list prev-item) nil)))
832             (setq prev-item el)))
833
834          ;; Try numeric ...
835          ((numberp el) 
836
837           ;; Validate range.
838           (if (or (<= el 0)
839                   (> el forms-number-of-fields))
840               (error (concat "Forms format error: "
841                              "field number %d out of range 1..%d")
842                      el forms-number-of-fields))
843
844           ;; Store forms order.
845           (if (>= field-num (length forms--elements))
846               (setq forms--elements (vconcat forms--elements (1- el)))
847             (aset forms--elements field-num (1- el)))
848           (setq field-num (1+ field-num))
849
850           (if prev-item
851               (setq forms-format-list
852                     (append forms-format-list (list prev-item) nil)))
853           (setq prev-item el))
854
855          ;; Try function ...
856          ((listp el)
857
858           ;; Validate.
859           (or (fboundp (car-safe el))
860               (error (concat "Forms format error: "
861                              "%S is not a function")
862                      (car-safe el)))
863
864           ;; Shift.
865           (if prev-item
866               (setq forms-format-list
867                     (append forms-format-list (list prev-item) nil)))
868           (setq prev-item el))
869
870          ;; else
871          (t
872           (error (concat "Forms format error: "
873                          "invalid element %S")
874                  el)))
875
876         ;; Advance to next element of the list.
877         (setq the-list rem)))
878
879     ;; Append last item.
880     (if prev-item
881         (progn
882           (setq forms-format-list
883                 (append forms-format-list (list prev-item) nil))
884           ;; Append a newline if the last item is a field.
885           ;; This prevents parsing problems.
886           ;; Also it makes it possible to insert an empty last field.
887           (if (numberp prev-item)
888               (setq forms-format-list
889                     (append forms-format-list (list "\n") nil))))))
890
891   (forms--debug 'forms-format-list
892                 'forms--elements))
893 \f
894 ;; Special treatment for read-only segments.
895 ;;
896 ;; If text is inserted between two read-only segments, there seems to
897 ;; be no way to give the newly inserted text the RW face.
898 ;; To solve this, read-only segments get the `insert-in-front-hooks'
899 ;; property set with a function that temporarily switches the
900 ;; properties of the first character of the segment to the RW face, so
901 ;; the new text gets the right face. The `post-command-hook' is
902 ;; used to restore the original properties.
903
904 (defvar forms--iif-start nil
905   "Record start of modification command.")
906 (defvar forms--iif-properties nil
907   "Original properties of the character being overridden.")
908
909 (defun forms--iif-hook (begin end)
910   "`insert-in-front-hooks' function for read-only segments."
911
912   ;; Note start location.  By making it a marker that points one 
913   ;; character beyond the actual location, it is guaranteed to move 
914   ;; correctly if text is inserted.
915   (or forms--iif-start
916       (setq forms--iif-start (copy-marker (1+ (point)))))
917
918   ;; Check if there is special treatment required.
919   (if (or (<= forms--iif-start 2)
920           (get-text-property (- forms--iif-start 2)
921                              'read-only))
922       (progn
923         ;; Fetch current properties.
924         (setq forms--iif-properties 
925               (text-properties-at (1- forms--iif-start)))
926
927         ;; Replace them.
928         (let ((inhibit-read-only t))
929           (set-text-properties 
930            (1- forms--iif-start) forms--iif-start
931            (list 'face forms--rw-face 'front-sticky '(face))))
932
933         ;; Enable `post-command-hook' to restore the properties.
934         (add-hook post-command-hook 'forms--iif-post-command-hook))
935
936     ;; No action needed.  Clear marker.
937     (setq forms--iif-start nil)))
938
939 (defun forms--iif-post-command-hook ()
940   "`post-command-hook' function for read-only segments."
941
942   ;; Disable `post-command-hook'.
943   (remove-hook 'post-command-hook 'forms--iif-post-command-hook)
944
945   ;; Restore properties.
946   (if forms--iif-start
947       (let ((inhibit-read-only t))
948         (set-text-properties 
949          (1- forms--iif-start) forms--iif-start
950          forms--iif-properties)))
951
952   ;; Cleanup.
953   (setq forms--iif-start nil))
954 \f
955 (defvar forms--marker)
956 (defvar forms--dyntext)
957
958 (defun forms--make-format ()
959   "Generate `forms--format' using the information in `forms-format-list'."
960
961   ;; The real work is done using a mapcar of `forms--make-format-elt' on
962   ;; `forms-format-list'.
963   ;; This function sets up the necessary environment, and decides
964   ;; which function to mapcar.
965
966   (let ((forms--marker 0)
967         (forms--dyntext 0))
968     (setq 
969      forms--format
970      (if forms-use-text-properties 
971          (` (lambda (arg)
972               (let ((inhibit-read-only t))
973                 (,@ (apply 'append
974                            (mapcar 'forms--make-format-elt-using-text-properties
975                                    forms-format-list)))
976                 ;; Prevent insertion before the first text.
977                 (,@ (if (numberp (car forms-format-list))
978                         nil
979                       '((add-text-properties (point-min) (1+ (point-min))
980                                              '(front-sticky (read-only intangible))))))
981                 ;; Prevent insertion after the last text.
982                 (remove-text-properties (1- (point)) (point)
983                                         '(rear-nonsticky)))
984               (setq forms--iif-start nil)))
985        (if forms-use-extents            ; XEmacs version
986            (` (lambda (arg)
987                 (,@ (apply 'append
988                            (mapcar
989                             'forms--make-format-elt-using-extents
990                             forms-format-list)))
991
992                 ;; After creating all the extents, set their endpoint behavior.
993                 ;; We can't do this when creating the extents, because
994                 ;; otherwise the text we insert for the labels would be
995                 ;; interpreted as user input, and would alter the endpoints
996                 ;; of the previous extents we created (the text-entry fields
997                 ;; would be extended by the following static-text areas.)
998                 (map-extents
999                  (function
1000                   (lambda (extent ignore)
1001                     (cond
1002                      ((not (extent-property extent 'forms))
1003                       ;; it's not one of ours; leave it alone.
1004                       nil)
1005                      ((not (extent-property extent 'read-only))
1006                       ;; text-entry fields should be [closed,closed] so that
1007                       ;; characters at either boundary go into them.
1008                       (set-extent-property extent 'end-open nil))
1009                      ;; Read-only fields should be (open,open) so that a
1010                      ;; read-only error isn't signalled when characters are
1011                      ;; inserted adjascent to them.  However, the very first
1012                      ;; label should be [closed,open) so that one can't
1013                      ;; insert text at point-min before the first label,
1014                      ;; and the very last should be (open,closed] for the
1015                      ;; same reason.
1016                      ((= (point-min) (extent-start-position extent))
1017                       (set-extent-property extent 'start-open nil)
1018                       (set-extent-property extent 'end-open t))
1019                      ((= (point-max) (extent-end-position extent))
1020                       (set-extent-property extent 'start-open t)
1021                       (set-extent-property extent 'end-open nil))
1022                      (t
1023                       (set-extent-property extent 'start-open t)
1024                       (set-extent-property extent 'end-open t)))
1025                     ;; return nil to continue mapping.
1026                     nil))
1027                  (current-buffer) (point-min) (point-max))
1028                 ))
1029          (` (lambda (arg)
1030               (,@ (apply 'append
1031                          (mapcar 'forms--make-format-elt
1032                                  forms-format-list))))))
1033        ))
1034
1035     ;; We have tallied the number of markers and dynamic texts,
1036     ;; so we can allocate the arrays now.
1037     (setq forms--markers (make-vector forms--marker nil))
1038     (setq forms--dyntexts (make-vector forms--dyntext nil)))
1039   (forms--debug 'forms--format))
1040
1041 (defun forms--make-format-elt-using-text-properties (el)
1042   "Helper routine to generate format function."
1043
1044   ;; The format routine `forms--format' will look like
1045   ;;
1046   ;; ;; preamble
1047   ;; (lambda (arg)
1048   ;;   (let ((inhibit-read-only t))
1049   ;;
1050   ;;     ;; A string, e.g. "text: ".
1051   ;;     (set-text-properties 
1052   ;;      (point)
1053   ;;      (progn (insert "text: ") (point)) 
1054   ;;      (list 'face forms--ro-face
1055   ;;            'read-only 1
1056   ;;            'insert-in-front-hooks 'forms--iif-hook
1057   ;;            'rear-nonsticky '(read-only face insert-in-front-hooks)))
1058   ;;
1059   ;;     ;; A field, e.g. 6.
1060   ;;     (let ((here (point)))
1061   ;;       (aset forms--markers 0 (point-marker))
1062   ;;       (insert (elt arg 5))
1063   ;;       (or (= (point) here)
1064   ;;      (set-text-properties 
1065   ;;       here (point)
1066   ;;       (list 'face forms--rw-face
1067   ;;             'front-sticky '(face))))
1068   ;;
1069   ;;     ;; Another string, e.g. "\nmore text: ".
1070   ;;     (set-text-properties
1071   ;;      (point)
1072   ;;      (progn (insert "\nmore text: ") (point))
1073   ;;      (list 'face forms--ro-face
1074   ;;            'read-only 2
1075   ;;            'insert-in-front-hooks 'forms--iif-hook
1076   ;;            'rear-nonsticky '(read-only face insert-in-front-hooks)))
1077   ;;
1078   ;;     ;; A function, e.g. (tocol 40).
1079   ;;     (set-text-properties
1080   ;;      (point)
1081   ;;      (progn
1082   ;;        (insert (aset forms--dyntexts 0 (tocol 40)))
1083   ;;        (point))
1084   ;;      (list 'face forms--ro-face
1085   ;;            'read-only 2
1086   ;;            'insert-in-front-hooks 'forms--iif-hook
1087   ;;            'rear-nonsticky '(read-only face insert-in-front-hooks)))
1088   ;;
1089   ;;     ;; Prevent insertion before the first text.
1090   ;;     (add-text-properties (point-min) (1+ (point-min))
1091   ;;                          '(front-sticky (read-only))))))
1092   ;;     ;; Prevent insertion after the last text.
1093   ;;     (remove-text-properties (1- (point)) (point)
1094   ;;                             '(rear-nonsticky)))
1095   ;;
1096   ;;     ;; wrap up
1097   ;;     (setq forms--iif-start nil)
1098   ;;     ))
1099
1100   (cond
1101    ((stringp el)
1102     
1103     (` ((set-text-properties 
1104          (point)                        ; start at point
1105          (progn                         ; until after insertion
1106            (insert (, el))
1107            (point))
1108          (list 'face forms--ro-face     ; read-only appearance
1109                'read-only (,@ (list (1+ forms--marker)))
1110                'intangible t
1111                'insert-in-front-hooks '(forms--iif-hook)
1112                'rear-nonsticky '(face read-only insert-in-front-hooks
1113                                  intangible))))))
1114     
1115    ((numberp el)
1116     (` ((let ((here (point)))
1117           (aset forms--markers 
1118                 (, (prog1 forms--marker
1119                      (setq forms--marker (1+ forms--marker))))
1120                 (point-marker))
1121           (insert (elt arg (, (1- el))))
1122           (or (= (point) here)
1123               (set-text-properties 
1124                here (point)
1125                (list 'face forms--rw-face
1126                      'front-sticky '(face))))))))
1127
1128    ((listp el)
1129     (` ((set-text-properties
1130          (point)
1131          (progn
1132            (insert (aset forms--dyntexts 
1133                          (, (prog1 forms--dyntext
1134                               (setq forms--dyntext (1+ forms--dyntext))))
1135                          (, el)))
1136            (point))
1137          (list 'face forms--ro-face
1138                'read-only (,@ (list (1+ forms--marker)))
1139                'intangible t
1140                'insert-in-front-hooks '(forms--iif-hook)
1141                'rear-nonsticky '(read-only face insert-in-front-hooks
1142                                  intangible))))))
1143
1144    ;; end of cond
1145    ))
1146
1147 ;; XEmacs
1148 (defun forms--make-format-elt-using-extents (el)
1149   "Helper routine to generate format function."
1150
1151   ;; The format routine `forms--format' will look like
1152   ;;
1153   ;; ;; preamble
1154   ;;  (lambda (arg)
1155   ;;  
1156   ;;    ;; A string, e.g. "text: ".
1157   ;;    (let ((extent (make-extent
1158   ;;                   (point)
1159   ;;                   (progn (insert "text: ") (point)))))
1160   ;;      (set-extent-face extent forms--ro-face)
1161   ;;      (set-extent-property extent 'read-only t)
1162   ;;      (set-extent-property extent 'forms t)
1163   ;;      )
1164   ;;
1165   ;;    ;; A field, e.g. 6.
1166   ;;    (let ((here (point)))
1167   ;;      (aset forms--markers 0 (point-marker))
1168   ;;      (insert (elt arg 5))
1169   ;;      (if (= (point) here)
1170   ;;          nil
1171   ;;        (let ((extent (make-extent here (point))))
1172   ;;          (set-extent-face extent forms--rw-face)
1173   ;;          (set-extent-property extent 'forms t)
1174   ;;          )))
1175   ;;  
1176   ;;    ;; A function, e.g. (tocol 40).
1177   ;;    (let ((extent (make-extent
1178   ;;                   (point)
1179   ;;                   (progn
1180   ;;                     (insert (aset forms--dyntexts 0 (tocol 40)))
1181   ;;                     (point)))))
1182   ;;      (set-extent-face extent forms--ro-face)
1183   ;;      (set-extent-property extent 'read-only t)
1184   ;;      (set-extent-property extent 'forms t)
1185   ;;      )
1186   ;;    
1187   ;;    ;; wrap up
1188   ;;    (setq forms--iif-start nil)
1189   ;;    )
1190
1191   (cond
1192    ((stringp el)
1193     
1194     (` ((let ((extent (make-extent
1195                        (point)                  ; start at point
1196                        (progn                   ; until after insertion
1197                          (insert (, el))
1198                          (point)))))
1199           (set-extent-face extent forms--ro-face)
1200           (set-extent-property extent 'forms t)
1201           (set-extent-property extent 'read-only t)
1202           ))))
1203
1204    ((numberp el)
1205     (` ((let ((here (point)))
1206           (aset forms--markers 
1207                 (, (prog1 forms--marker
1208                      (setq forms--marker (1+ forms--marker))))
1209                 (point-marker))
1210           (insert (elt arg (, (1- el))))
1211           (if (= (point) here)
1212               nil
1213             (let ((extent (make-extent here (point))))
1214               (set-extent-face extent forms--rw-face)
1215               (set-extent-property extent 'forms t)
1216               ))))))
1217    
1218    ((listp el)
1219     (` ((let ((extent
1220                (make-extent
1221                 (point)
1222                 (progn
1223                   (insert (aset forms--dyntexts 
1224                                 (, (prog1 forms--dyntext
1225                                      (setq forms--dyntext
1226                                            (1+ forms--dyntext))))
1227                                 (, el)))
1228                   (point)))))
1229           (set-extent-face extent forms--ro-face)
1230           (set-extent-property extent 'forms t)
1231           (set-extent-property extent 'read-only t)
1232           ))))
1233    
1234    ;; end of cond
1235    ))
1236
1237 (defun forms--make-format-elt (el)
1238   "Helper routine to generate format function."
1239
1240   ;; If we're not using text properties, the format routine
1241   ;; `forms--format' will look like
1242   ;;
1243   ;; (lambda (arg)
1244   ;;   ;; a string, e.g. "text: "
1245   ;;   (insert "text: ")
1246   ;;   ;; a field, e.g. 6
1247   ;;   (aset forms--markers 0 (point-marker))
1248   ;;   (insert (elt arg 5))
1249   ;;   ;; another string, e.g. "\nmore text: "
1250   ;;   (insert "\nmore text: ")
1251   ;;   ;; a function, e.g. (tocol 40)
1252   ;;   (insert (aset forms--dyntexts 0 (tocol 40)))
1253   ;;   ... )
1254
1255   (cond 
1256    ((stringp el)
1257     (` ((insert (, el)))))
1258    ((numberp el)
1259     (prog1
1260         (` ((aset forms--markers (, forms--marker) (point-marker))
1261             (insert (elt arg (, (1- el))))))
1262       (setq forms--marker (1+ forms--marker))))
1263    ((listp el)
1264     (prog1
1265         (` ((insert (aset forms--dyntexts (, forms--dyntext) (, el)))))
1266       (setq forms--dyntext (1+ forms--dyntext))))))
1267 \f
1268 (defvar forms--field)
1269 (defvar forms--recordv)
1270 (defvar forms--seen-text)
1271
1272 (defun forms--make-parser ()
1273   "Generate `forms--parser' from the information in `forms-format-list'."
1274
1275   ;; If we can use text properties, we simply set it to
1276   ;; `forms--parser-using-text-properties'.
1277   ;; Otherwise, the function is constructed using a mapcar of
1278   ;; `forms--make-parser-elt on `forms-format-list'.
1279
1280   (setq
1281    forms--parser
1282    (if forms-use-text-properties
1283        (function forms--parser-using-text-properties)
1284      (let ((forms--field nil)
1285            (forms--seen-text nil)
1286            (forms--dyntext 0))
1287
1288        ;; Note: we add a nil element to the list passed to `mapcar',
1289        ;; see `forms--make-parser-elt' for details.
1290        (` (lambda nil
1291             (let (here)
1292               (goto-char (point-min))
1293               (,@ (apply 'append
1294                          (mapcar 
1295                           'forms--make-parser-elt 
1296                           (append forms-format-list (list nil)))))))))))
1297
1298   (forms--debug 'forms--parser))
1299
1300 (defun forms--parser-using-text-properties ()
1301   "Extract field info from forms when using text properties."
1302
1303   ;; Using text properties, we can simply jump to the markers, and
1304   ;; extract the information up to the following read-only segment.
1305
1306   (let ((i 0)
1307         here there)
1308     (while (< i (length forms--markers))
1309       (goto-char (setq here (aref forms--markers i)))
1310       (if (get-text-property here 'read-only)
1311           (aset forms--recordv (aref forms--elements i) nil)
1312         (if (setq there 
1313                   (next-single-property-change here 'read-only))
1314             (aset forms--recordv (aref forms--elements i)
1315                   (buffer-substring-no-properties here there))
1316           (aset forms--recordv (aref forms--elements i)
1317                 (buffer-substring-no-properties here (point-max)))))
1318       (setq i (1+ i)))))
1319
1320 (defun forms--make-parser-elt (el)
1321   "Helper routine to generate forms parser function."
1322
1323   ;; The parse routine will look like:
1324   ;;
1325   ;; (lambda nil
1326   ;;   (let (here)
1327   ;;     (goto-char (point-min))
1328   ;; 
1329   ;;     ;;  "text: "
1330   ;;     (if (not (looking-at "text: "))
1331   ;;        (error "Parse error: cannot find \"text: \""))
1332   ;;     (forward-char 6)       ; past "text: "
1333   ;; 
1334   ;;     ;;  6
1335   ;;     ;;  "\nmore text: "
1336   ;;     (setq here (point))
1337   ;;     (if (not (search-forward "\nmore text: " nil t nil))
1338   ;;        (error "Parse error: cannot find \"\\nmore text: \""))
1339   ;;     (aset forms--recordv 5 (buffer-substring-no-properties here (- (point) 12)))
1340   ;;
1341   ;;     ;;  (tocol 40)
1342   ;;    (let ((forms--dyntext (car-safe forms--dynamic-text)))
1343   ;;      (if (not (looking-at (regexp-quote forms--dyntext)))
1344   ;;          (error "Parse error: not looking at \"%s\"" forms--dyntext))
1345   ;;      (forward-char (length forms--dyntext))
1346   ;;      (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
1347   ;;     ... 
1348   ;;     ;; final flush (due to terminator sentinel, see below)
1349   ;;    (aset forms--recordv 7 (buffer-substring-no-properties (point) (point-max)))
1350
1351   (cond
1352    ((stringp el)
1353     (prog1
1354         (if forms--field
1355             (` ((setq here (point))
1356                 (if (not (search-forward (, el) nil t nil))
1357                     (error "Parse error: cannot find `%s'" (, el)))
1358                 (aset forms--recordv (, (1- forms--field))
1359                       (buffer-substring-no-properties here
1360                                         (- (point) (, (length el)))))))
1361           (` ((if (not (looking-at (, (regexp-quote el))))
1362                   (error "Parse error: not looking at `%s'" (, el)))
1363               (forward-char (, (length el))))))
1364       (setq forms--seen-text t)
1365       (setq forms--field nil)))
1366    ((numberp el)
1367     (if forms--field
1368         (error "Cannot parse adjacent fields %d and %d"
1369                forms--field el)
1370       (setq forms--field el)
1371       nil))
1372    ((null el)
1373     (if forms--field
1374         (` ((aset forms--recordv (, (1- forms--field))
1375                   (buffer-substring-no-properties (point) (point-max)))))))
1376    ((listp el)
1377     (prog1
1378         (if forms--field
1379             (` ((let ((here (point))
1380                       (forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
1381                   (if (not (search-forward forms--dyntext nil t nil))
1382                       (error "Parse error: cannot find `%s'" forms--dyntext))
1383                   (aset forms--recordv (, (1- forms--field))
1384                         (buffer-substring-no-properties here
1385                                           (- (point) (length forms--dyntext)))))))
1386           (` ((let ((forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
1387                 (if (not (looking-at (regexp-quote forms--dyntext)))
1388                     (error "Parse error: not looking at `%s'" forms--dyntext))
1389                 (forward-char (length forms--dyntext))))))
1390       (setq forms--dyntext (1+ forms--dyntext))
1391       (setq forms--seen-text t)
1392       (setq forms--field nil)))
1393    ))
1394 \f
1395 (defun forms--intuit-from-file ()
1396   "Get number of fields and a default form using the data file."
1397
1398   ;; If `forms-number-of-fields' is not set, get it from the data file.
1399   (if (null forms-number-of-fields)
1400
1401       ;; Need a file to do this.
1402       (if (not (file-exists-p forms-file))
1403           (error "Need existing file or explicit 'forms-number-of-records'.")
1404
1405         ;; Visit the file and extract the first record.
1406         (setq forms--file-buffer (find-file-noselect forms-file))
1407         (let ((read-file-filter forms-read-file-filter)
1408               (the-record))
1409           (setq the-record
1410                 (save-excursion
1411                   (set-buffer forms--file-buffer)
1412                   (let ((inhibit-read-only t))
1413                     (run-hooks 'read-file-filter))
1414                   (goto-char (point-min))
1415                   (forms--get-record)))
1416
1417           ;; This may be overkill, but try to avoid interference with 
1418           ;; the normal processing.
1419           (kill-buffer forms--file-buffer)
1420
1421           ;; Count the number of fields in `the-record'.
1422           (let (the-result
1423                 (start-pos 0)
1424                 found-pos
1425                 (field-sep-length (length forms-field-sep)))
1426             (setq forms-number-of-fields 1)
1427             (while (setq found-pos
1428                          (string-match forms-field-sep the-record start-pos))
1429               (progn
1430                 (setq forms-number-of-fields (1+ forms-number-of-fields))
1431                 (setq start-pos (+ field-sep-length found-pos))))))))
1432
1433   ;; Construct default format list.
1434   (setq forms-format-list (list "Forms file \"" forms-file "\".\n\n"))
1435   (let ((i 0))
1436     (while (<= (setq i (1+ i)) forms-number-of-fields)
1437       (setq forms-format-list
1438             (append forms-format-list
1439                     (list (format "%4d: " i) i "\n"))))))
1440 \f
1441 (defun forms--set-keymaps ()
1442   "Set the keymaps used in this mode."
1443
1444   (use-local-map (if forms-read-only 
1445                      forms-mode-ro-map 
1446                    forms-mode-edit-map)))
1447
1448 (defun forms--mode-commands ()
1449   "Fill the Forms mode keymaps."
1450
1451   ;; `forms-mode-map' is always accessible via \C-c prefix.
1452   (setq forms-mode-map (make-keymap))
1453   (define-key forms-mode-map "\t" 'forms-next-field)
1454   (define-key forms-mode-map "\C-k" 'forms-delete-record)
1455   (define-key forms-mode-map "\C-q" 'forms-toggle-read-only)
1456   (define-key forms-mode-map "\C-o" 'forms-insert-record)
1457   (define-key forms-mode-map "\C-l" 'forms-jump-record)
1458   (define-key forms-mode-map "\C-n" 'forms-next-record)
1459   (define-key forms-mode-map "\C-p" 'forms-prev-record)
1460   (define-key forms-mode-map "\C-r" 'forms-search-backward)
1461   (define-key forms-mode-map "\C-s" 'forms-search-forward)
1462   (define-key forms-mode-map "\C-x" 'forms-exit)
1463   (define-key forms-mode-map "<" 'forms-first-record)
1464   (define-key forms-mode-map ">" 'forms-last-record)
1465   (define-key forms-mode-map "?" 'describe-mode) ; XEmacs
1466   (define-key forms-mode-map "\C-?" 'forms-prev-record)
1467
1468   ;; `forms-mode-ro-map' replaces the local map when in read-only mode.
1469   (setq forms-mode-ro-map (make-keymap))
1470   (suppress-keymap forms-mode-ro-map)
1471   (define-key forms-mode-ro-map "\C-c" forms-mode-map)
1472   (define-key forms-mode-ro-map "\t" 'forms-next-field)
1473   (define-key forms-mode-ro-map "q" 'forms-toggle-read-only)
1474   (define-key forms-mode-ro-map "l" 'forms-jump-record)
1475   (define-key forms-mode-ro-map "n" 'forms-next-record)
1476   (define-key forms-mode-ro-map "p" 'forms-prev-record)
1477   (define-key forms-mode-ro-map "r" 'forms-search-backward)
1478   (define-key forms-mode-ro-map "s" 'forms-search-forward)
1479   (define-key forms-mode-ro-map "x" 'forms-exit)
1480   (define-key forms-mode-ro-map "<" 'forms-first-record)
1481   (define-key forms-mode-ro-map ">" 'forms-last-record)
1482   (define-key forms-mode-ro-map "?" 'describe-mode)
1483   (define-key forms-mode-ro-map " " 'forms-next-record)
1484   (forms--mode-commands1 forms-mode-ro-map)
1485   (forms--mode-menu-ro forms-mode-ro-map)
1486
1487   ;; This is the normal, local map.
1488   (setq forms-mode-edit-map (make-keymap))
1489   (define-key forms-mode-edit-map "\t"   'forms-next-field)
1490   (define-key forms-mode-edit-map "\C-c" forms-mode-map)
1491   (forms--mode-commands1 forms-mode-edit-map)
1492   (forms--mode-menu-edit forms-mode-edit-map)
1493   )
1494
1495 (defun forms--mode-menu-ro (map)
1496 ;;; Menu initialisation
1497 ;  (define-key map [menu-bar] (make-sparse-keymap))
1498   (define-key map [menu-bar forms]
1499     (cons "Forms" (make-sparse-keymap "Forms")))
1500   (define-key map [menu-bar forms menu-forms-exit]
1501     '("Exit Forms Mode" . forms-exit))
1502   (define-key map [menu-bar forms menu-forms-sep1]
1503     '("----"))
1504   (define-key map [menu-bar forms menu-forms-save]
1505     '("Save Data" . forms-save-buffer))
1506   (define-key map [menu-bar forms menu-forms-print]
1507     '("Print Data" . forms-print))
1508   (define-key map [menu-bar forms menu-forms-describe]
1509     '("Describe Mode" . describe-mode))
1510   (define-key map [menu-bar forms menu-forms-toggle-ro]
1511     '("Toggle View/Edit" . forms-toggle-read-only))
1512   (define-key map [menu-bar forms menu-forms-jump-record]
1513     '("Jump" . forms-jump-record))
1514   (define-key map [menu-bar forms menu-forms-search-backward]
1515     '("Search Backward" . forms-search-backward))
1516   (define-key map [menu-bar forms menu-forms-search-forward]
1517     '("Search Forward" . forms-search-forward))
1518   (define-key map [menu-bar forms menu-forms-delete-record]
1519     '("Delete" . forms-delete-record))
1520   (define-key map [menu-bar forms menu-forms-insert-record]
1521     '("Insert" . forms-insert-record))
1522   (define-key map [menu-bar forms menu-forms-sep2]
1523     '("----"))
1524   (define-key map [menu-bar forms menu-forms-last-record]
1525     '("Last Record" . forms-last-record))
1526   (define-key map [menu-bar forms menu-forms-first-record]
1527     '("First Record" . forms-first-record))
1528   (define-key map [menu-bar forms menu-forms-prev-record]
1529     '("Previous Record" . forms-prev-record))
1530   (define-key map [menu-bar forms menu-forms-next-record]
1531     '("Next Record" . forms-next-record))
1532   (define-key map [menu-bar forms menu-forms-sep3]
1533     '("----"))
1534   (define-key map [menu-bar forms menu-forms-prev-field]
1535     '("Previous Field" . forms-prev-field))
1536   (define-key map [menu-bar forms menu-forms-next-field]
1537     '("Next Field" . forms-next-field))
1538   (put 'forms-insert-record 'menu-enable '(not forms-read-only))
1539   (put 'forms-delete-record 'menu-enable '(not forms-read-only))
1540 )
1541 (defun forms--mode-menu-edit (map)
1542 ;;; Menu initialisation
1543 ;  (define-key map [menu-bar] (make-sparse-keymap))
1544   (define-key map [menu-bar forms]
1545     (cons "Forms" (make-sparse-keymap "Forms")))
1546   (define-key map [menu-bar forms menu-forms-edit--exit]
1547     '("Exit" . forms-exit))
1548   (define-key map [menu-bar forms menu-forms-edit-sep1]
1549     '("----"))
1550   (define-key map [menu-bar forms menu-forms-edit-save]
1551     '("Save Data" . forms-save-buffer))
1552   (define-key map [menu-bar forms menu-forms-edit-print]
1553     '("Print Data" . forms-print))
1554   (define-key map [menu-bar forms menu-forms-edit-describe]
1555     '("Describe Mode" . describe-mode))
1556   (define-key map [menu-bar forms menu-forms-edit-toggle-ro]
1557     '("Toggle View/Edit" . forms-toggle-read-only))
1558   (define-key map [menu-bar forms menu-forms-edit-jump-record]
1559     '("Jump" . forms-jump-record))
1560   (define-key map [menu-bar forms menu-forms-edit-search-backward]
1561     '("Search Backward" . forms-search-backward))
1562   (define-key map [menu-bar forms menu-forms-edit-search-forward]
1563     '("Search Forward" . forms-search-forward))
1564   (define-key map [menu-bar forms menu-forms-edit-delete-record]
1565     '("Delete" . forms-delete-record))
1566   (define-key map [menu-bar forms menu-forms-edit-insert-record]
1567     '("Insert" . forms-insert-record))
1568   (define-key map [menu-bar forms menu-forms-edit-sep2]
1569     '("----"))
1570   (define-key map [menu-bar forms menu-forms-edit-last-record]
1571     '("Last Record" . forms-last-record))
1572   (define-key map [menu-bar forms menu-forms-edit-first-record]
1573     '("First Record" . forms-first-record))
1574   (define-key map [menu-bar forms menu-forms-edit-prev-record]
1575     '("Previous Record" . forms-prev-record))
1576   (define-key map [menu-bar forms menu-forms-edit-next-record]
1577     '("Next Record" . forms-next-record))
1578   (define-key map [menu-bar forms menu-forms-edit-sep3]
1579     '("----"))
1580   (define-key map [menu-bar forms menu-forms-edit-prev-field]
1581     '("Previous Field" . forms-prev-field))
1582   (define-key map [menu-bar forms menu-forms-edit-next-field]
1583     '("Next Field" . forms-next-field))
1584   (put 'forms-insert-record 'menu-enable '(not forms-read-only))
1585   (put 'forms-delete-record 'menu-enable '(not forms-read-only))
1586 )
1587
1588 (defun forms--mode-commands1 (map) 
1589   "Helper routine to define keys."
1590   (if forms--xemacs-p                   ; XEmacs
1591       (progn
1592         (define-key map [tab] 'forms-next-field)
1593         (define-key map [(shift tab)] 'forms-prev-field))
1594     (define-key map [TAB] 'forms-next-field)
1595     (define-key map [S-tab] 'forms-prev-field))
1596   (define-key map [next] 'forms-next-record)
1597   (define-key map [prior] 'forms-prev-record)
1598   (define-key map [begin] 'forms-first-record)
1599   (define-key map [last] 'fo