home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 033 / atcp40de.zip / FORMS.EL < prev    next >
Text File  |  1994-10-07  |  42KB  |  1,374 lines

  1. ;;; forms.el -- Forms Mode - A GNU Emacs Major Mode
  2. ;;; SCCS Status     : @(#)@ forms    1.2.9
  3. ;;; Author          : Johan Vromans
  4. ;;; Created On      : 1989
  5. ;;; Last Modified By: Johan Vromans
  6. ;;; Last Modified On: Tue Jan 14 15:33:22 1992
  7. ;;; Update Count    : 22
  8. ;;; Status          : OK
  9.  
  10. ;;; This file is part of GNU Emacs.
  11. ;;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;;; but WITHOUT ANY WARRANTY.  No author or distributor
  13. ;;; accepts responsibility to anyone for the consequences of using it
  14. ;;; or for whether it serves any particular purpose or works at all,
  15. ;;; unless he says so in writing.  Refer to the GNU Emacs General Public
  16. ;;; License for full details.
  17.  
  18. ;;; Everyone is granted permission to copy, modify and redistribute
  19. ;;; GNU Emacs, but only under the conditions described in the
  20. ;;; GNU Emacs General Public License.   A copy of this license is
  21. ;;; supposed to have been given to you along with GNU Emacs so you
  22. ;;; can know your rights and responsibilities. 
  23. ;;; If you don't have this copy, write to the Free Software
  24. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  25. ;;;
  26.  
  27. ;; LCD Archive Entry:
  28. ;; forms|Johan Vromans|jv@mh.nl
  29. ;; |Major mode for working with plain-text databases in forms-oriented manner
  30. ;; |92/01/14|1.2.9|~/modes/forms.tar.Z|
  31.  
  32.  
  33. ;;; HISTORY 
  34. ;;; 14-Jan-1992        Johan Vromans    
  35. ;;;    Add LCD entry.
  36. ;;; 1-Jul-1991        Johan Vromans    
  37. ;;;    Normalized error messages.
  38. ;;; 30-Jun-1991        Johan Vromans    
  39. ;;;    Add support for forms-modified-record-filter.
  40. ;;;    Allow the filter functions to be the name of a function.
  41. ;;;    Fix: parse--format used forms--dynamic-text destructively.
  42. ;;;    Internally optimized the forms-format-list.
  43. ;;;    Added support for debugging.
  44. ;;;    Stripped duplicate documentation.
  45. ;;;   
  46. ;;; 29-Jun-1991        Johan Vromans    
  47. ;;;    Add support for functions and lisp symbols in forms-format-list.
  48. ;;;    Add function forms-enumerate.
  49.  
  50. (provide 'forms)            ;;; official
  51. (provide 'forms-mode)            ;;; for compatibility
  52.  
  53. ;;; Visit a file using a form.
  54. ;;;
  55. ;;; === Naming conventions
  56. ;;;
  57. ;;; The names of all variables and functions start with 'form-'.
  58. ;;; Names which start with 'form--' are intended for internal use, and
  59. ;;; should *NOT* be used from the outside.
  60. ;;;
  61. ;;; All variables are buffer-local, to enable multiple forms visits 
  62. ;;; simultaneously.
  63. ;;; Variable 'forms--mode-setup' is local to *ALL* buffers, for it 
  64. ;;; controls if forms-mode has been enabled in a buffer.
  65. ;;;
  66. ;;; === How it works ===
  67. ;;;
  68. ;;; Forms mode means visiting a data file which is supposed to consist
  69. ;;; of records each containing a number of fields. The records are
  70. ;;; separated by a newline, the fields are separated by a user-defined
  71. ;;; field separater (default: TAB).
  72. ;;; When shown, a record is transferred to an emacs buffer and
  73. ;;; presented using a user-defined form. One record is shown at a
  74. ;;; time.
  75. ;;;
  76. ;;; Forms mode is a composite mode. It involves two files, and two
  77. ;;; buffers.
  78. ;;; The first file, called the control file, defines the name of the
  79. ;;; data file and the forms format. This file buffer will be used to
  80. ;;; present the forms.
  81. ;;; The second file holds the actual data. The buffer of this file
  82. ;;; will be buried, for it is never accessed directly.
  83. ;;;
  84. ;;; Forms mode is invoked using "forms-find-file control-file".
  85. ;;; Alternativily forms-find-file-other-window can be used.
  86. ;;;
  87. ;;; You may also visit the control file, and switch to forms mode by hand
  88. ;;; with M-x forms-mode .
  89. ;;;
  90. ;;; Automatic mode switching is supported, so you may use "find-file"
  91. ;;; if you specify "-*- forms -*-" in the first line of the control file.
  92. ;;; 
  93. ;;; The control file is visited, evaluated using
  94. ;;; eval-current-buffer, and should set at least the following
  95. ;;; variables:
  96. ;;;
  97. ;;;    forms-file            [string] the name of the data file.
  98. ;;;
  99. ;;;    forms-number-of-fields        [integer]
  100. ;;;            The number of fields in each record.
  101. ;;;
  102. ;;;    forms-format-list           [list]   formatting instructions.
  103. ;;;
  104. ;;; The forms-format-list should be a list, each element containing
  105. ;;;
  106. ;;;  - a string, e.g. "hello" (which is inserted \"as is\"),
  107. ;;;
  108. ;;;  - an integer, denoting a field number. The contents of the field
  109. ;;;    are inserted at this point.
  110. ;;;    The first field has number one.
  111. ;;;
  112. ;;;  - a function call, e.g. (insert "text"). This function call is 
  113. ;;;    dynamically evaluated and should return a string. It should *NOT*
  114. ;;;    have side-effects on the forms being constructed.
  115. ;;;    The current fields are available to the function in the variable
  116. ;;;    forms-fields, they should *NOT* be modified.
  117. ;;;
  118. ;;;  - a lisp symbol, that must evaluate to one of the above.
  119. ;;;
  120. ;;; Optional variables which may be set in the control file:
  121. ;;;
  122. ;;;    forms-field-sep                [string, default TAB]
  123. ;;;            The field separator used to separate the
  124. ;;;            fields in the data file. It may be a string.
  125. ;;;
  126. ;;;    forms-read-only                [bool, default nil]
  127. ;;;            't' means that the data file is visited read-only.
  128. ;;;            If no write access to the data file is
  129. ;;;            possible, read-only mode is enforced. 
  130. ;;;
  131. ;;;    forms-multi-line            [string, default "^K"]
  132. ;;;            If non-null the records of the data file may
  133. ;;;            contain fields which span multiple lines in
  134. ;;;            the form.
  135. ;;;            This variable denoted the separator character
  136. ;;;            to be used for this purpose. Upon display, all
  137. ;;;            occurrencies of this character are translated
  138. ;;;            to newlines. Upon storage they are translated
  139. ;;;            back to the separator.
  140. ;;;
  141. ;;;    forms-forms-scroll            [bool, default t]
  142. ;;;            If non-nil: redefine scroll-up/down to perform
  143. ;;;            forms-next/prev-field if in forms mode.
  144. ;;;
  145. ;;;    forms-forms-jump            [bool, default t]
  146. ;;;            If non-nil: redefine beginning/end-of-buffer
  147. ;;;            to performs forms-first/last-field if in
  148. ;;;            forms mode.
  149. ;;;
  150. ;;;    forms-new-record-filter            [symbol, no default]
  151. ;;;            If defined: this should be the name of a 
  152. ;;;            function that is called when a new
  153. ;;;            record is created. It can be used to fill in
  154. ;;;            the new record with default fields, for example.
  155. ;;;            Instead of the name of the function, it may
  156. ;;;            be the function itself.
  157. ;;;
  158. ;;;    forms-modified-record-filter        [symbol, no default]
  159. ;;;            If defined: this should be the name of a 
  160. ;;;            function that is called when a record has
  161. ;;;            been modified. It is called after the fields
  162. ;;;            are parsed. It can be used to register
  163. ;;;            modification dates, for example.
  164. ;;;            Instead of the name of the function, it may
  165. ;;;            be the function itself.
  166. ;;;
  167. ;;; After evaluating the control file, its buffer is cleared and used
  168. ;;; for further processing.
  169. ;;; The data file (as designated by "forms-file") is visited in a buffer
  170. ;;; (forms--file-buffer) which will not normally be shown.
  171. ;;; Great malfunctioning may be expected if this file/buffer is modified
  172. ;;; outside of this package while it's being visited!
  173. ;;;
  174. ;;; A record from the data file is transferred from the data file,
  175. ;;; split into fields (into forms--the-record-list), and displayed using
  176. ;;; the specs in forms-format-list.
  177. ;;; A format routine 'forms--format' is built upon startup to format 
  178. ;;; the records.
  179. ;;;
  180. ;;; When a form is changed the record is updated as soon as this form
  181. ;;; is left. The contents of the form are parsed using forms-format-list,
  182. ;;; and the fields which are deduced from the form are modified. So,
  183. ;;; fields not shown on the forms retain their origional values.
  184. ;;; The newly formed record and replaces the contents of the
  185. ;;; old record in forms--file-buffer.
  186. ;;; A parse routine 'forms--parser' is built upon startup to parse
  187. ;;; the records.
  188. ;;;
  189. ;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save
  190. ;;; (which doesn't). However, if forms-exit-no-save is executed and the file
  191. ;;; buffer has been modified, emacs will ask questions.
  192. ;;;
  193. ;;; Other functions are:
  194. ;;;
  195. ;;;    paging (forward, backward) by record
  196. ;;;    jumping (first, last, random number)
  197. ;;;    searching
  198. ;;;    creating and deleting records
  199. ;;;    reverting the form (NOT the file buffer)
  200. ;;;    switching edit <-> view mode v.v.
  201. ;;;    jumping from field to field
  202. ;;;
  203. ;;; As an documented side-effect: jumping to the last record in the
  204. ;;; file (using forms-last-record) will adjust forms--total-records if
  205. ;;; needed.
  206. ;;;
  207. ;;; Commands and keymaps:
  208. ;;;
  209. ;;; A local keymap 'forms-mode-map' is used in the forms buffer.
  210. ;;; As conventional, this map can be accessed with C-c prefix.
  211. ;;; In read-only mode, the C-c prefix must be omitted.
  212. ;;;
  213. ;;; Default bindings:
  214. ;;;
  215. ;;;    \C-c    forms-mode-map
  216. ;;;    TAB    forms-next-field
  217. ;;;    SPC     forms-next-record
  218. ;;;    <    forms-first-record
  219. ;;;    >    forms-last-record
  220. ;;;    ?    describe-mode
  221. ;;;    d    forms-delete-record
  222. ;;;    e    forms-edit-mode
  223. ;;;    i    forms-insert-record
  224. ;;;    j    forms-jump-record
  225. ;;;    n    forms-next-record
  226. ;;;    p    forms-prev-record
  227. ;;;    q    forms-exit
  228. ;;;    s    forms-search
  229. ;;;    v    forms-view-mode
  230. ;;;    x    forms-exit-no-save
  231. ;;;    DEL    forms-prev-record
  232. ;;;
  233. ;;; Standard functions scroll-up, scroll-down, beginning-of-buffer and
  234. ;;; end-of-buffer are wrapped with re-definitions, which map them to
  235. ;;; next/prev record and first/last record.
  236. ;;; Buffer-local variables forms-forms-scroll and forms-forms-jump
  237. ;;; may be used to control these redefinitions.
  238. ;;;
  239. ;;; Function save-buffer is also wrapped to perform a sensible action.
  240. ;;; A revert-file-hook is defined to revert a forms to original.
  241. ;;;
  242. ;;; For convenience, TAB is always bound to forms-next-field, so you
  243. ;;; don't need the C-c prefix for this command.
  244. ;;;
  245. ;;; Global variables and constants
  246.  
  247. (defconst forms-version "1.2.9"
  248.   "Version of forms-mode implementation")
  249.  
  250. (defvar forms-forms-scrolls t
  251.   "If non-null: redefine scroll-up/down to be used with forms-mode.")
  252.  
  253. (defvar forms-forms-jumps t
  254.   "If non-null: redefine beginning/end-of-buffer to be used with forms-mode.")
  255.  
  256. (defvar forms-mode-hooks nil
  257.   "Hook functions to be run upon entering forms mode.")
  258. ;;;
  259. ;;; Mandatory variables - must be set by evaluating the control file
  260.  
  261. (defvar forms-file nil
  262.   "Name of the file holding the data.")
  263.  
  264. (defvar forms-format-list nil
  265.   "List of formatting specifications.")
  266.  
  267. (defvar forms-number-of-fields nil
  268.   "Number of fields per record.")
  269.  
  270. ;;;
  271. ;;; Optional variables with default values
  272.  
  273. (defvar forms-field-sep "\t"
  274.   "Field separator character (default TAB)")
  275.  
  276. (defvar forms-read-only nil
  277.   "Read-only mode (defaults to the write access on the data file).")
  278.  
  279. (defvar forms-multi-line "\C-k"
  280.   "Character to separate multi-line fields (default ^K)")
  281.  
  282. (defvar forms-forms-scroll t
  283.   "Redefine scroll-up/down to perform forms-next/prev-record when in
  284.  forms mode.")
  285.  
  286. (defvar forms-forms-jump t
  287.   "Redefine beginning/end-of-buffer to perform forms-first/last-record
  288.  when in forms mode.")
  289.  
  290. ;;;
  291. ;;; Internal variables.
  292.  
  293. (defvar forms--file-buffer nil
  294.   "Buffer which holds the file data")
  295.  
  296. (defvar forms--total-records 0
  297.   "Total number of records in the data file.")
  298.  
  299. (defvar forms--current-record 0
  300.   "Number of the record currently on the screen.")
  301.  
  302. (defvar forms-mode-map nil        ; yes - this one is global
  303.    "Keymap for form buffer.")
  304.  
  305. (defvar forms--markers nil
  306.   "Field markers in the screen.")
  307.  
  308. (defvar forms--number-of-markers 0
  309.   "Number of fields on screen.")
  310.  
  311. (defvar forms--the-record-list nil 
  312.    "List of strings of the current record, as parsed from the file.")
  313.  
  314. (defvar forms--search-regexp nil
  315.   "Last regexp used by forms-search.")
  316.  
  317. (defvar forms--format nil
  318.   "Formatting routine.")
  319.  
  320. (defvar forms--parser nil
  321.   "Forms parser routine.")
  322.  
  323. (defvar forms--mode-setup nil
  324.   "Internal - keeps track of forms-mode being set-up.")
  325. (make-variable-buffer-local 'forms--mode-setup)
  326.  
  327. (defvar forms--new-record-filter nil
  328.   "Internal - set if a new record filter has been defined.")
  329.  
  330. (defvar forms--modified-record-filter nil
  331.   "Internal - set if a modified record filter has been defined.")
  332.  
  333. (defvar forms--dynamic-text nil
  334.   "Internal - holds dynamic text to insert between fields.")
  335.  
  336. (defvar forms-fields nil
  337.   "List with fields of the current forms. First field has number 1.")
  338.  
  339. ;;;
  340. ;;; forms-mode
  341. ;;;
  342. ;;; This is not a simple major mode, as usual. Therefore, forms-mode
  343. ;;; takes an optional argument 'primary' which is used for the initial
  344. ;;; set-up. Normal use would leave 'primary' to nil.
  345. ;;;
  346. ;;; A global buffer-local variable 'forms--mode-setup' has the same effect
  347. ;;; but makes it possible to auto-invoke forms-mode using find-file.
  348. ;;;
  349. ;;; Note: although it seems logical to have (make-local-variable) executed
  350. ;;; where the variable is first needed, I deliberately placed all calls
  351. ;;; in the forms-mode function.
  352.  
  353. (defvar forms-new-record-filter)
  354. (defvar forms-modified-record-filter)
  355.  
  356. (defun forms-mode (&optional primary)
  357.   "Major mode to visit files in a field-structured manner using a form.
  358.  
  359.  Commands (prefix with C-c if not in read-only mode):
  360.  \\{forms-mode-map}"
  361.  
  362.   (interactive)                ; no - 'primary' is not prefix arg
  363.  
  364.   ;; Primary set-up: evaluate buffer and check if the mandatory
  365.   ;; variables have been set.
  366.   (if (or primary (not forms--mode-setup))
  367.       (progn
  368.     (kill-all-local-variables)
  369.  
  370.     ;; make mandatory variables
  371.     (make-local-variable 'forms-file)
  372.     (make-local-variable 'forms-number-of-fields)
  373.     (make-local-variable 'forms-format-list)
  374.  
  375.     ;; make optional variables
  376.     (make-local-variable 'forms-field-sep)
  377.         (make-local-variable 'forms-read-only)
  378.         (make-local-variable 'forms-multi-line)
  379.     (make-local-variable 'forms-forms-scroll)
  380.     (make-local-variable 'forms-forms-jump)
  381.     (fmakunbound 'forms-new-record-filter)
  382.  
  383.     ;; eval the buffer, should set variables
  384.     (eval-current-buffer)
  385.  
  386.     ;; check if the mandatory variables make sense.
  387.     (or forms-file
  388.         (error "'forms-file' has not been set"))
  389.     (or forms-number-of-fields
  390.         (error "'forms-number-of-fields' has not been set"))
  391.     (or (> forms-number-of-fields 0)
  392.         (error "'forms-number-of-fields' must be > 0")
  393.     (or (stringp forms-field-sep))
  394.         (error "'forms-field-sep' is not a string"))
  395.     (if forms-multi-line
  396.         (if (and (stringp forms-multi-line)
  397.              (eq (length forms-multi-line) 1))
  398.         (if (string= forms-multi-line forms-field-sep)
  399.             (error "'forms-multi-line' is equal to 'forms-field-sep'"))
  400.           (error "'forms-multi-line' must be nil or a one-character string")))
  401.         
  402.     ;; validate and process forms-format-list
  403.     (make-local-variable 'forms--number-of-markers)
  404.     (make-local-variable 'forms--markers)
  405.     (forms--process-format-list)
  406.  
  407.     ;; build the formatter and parser
  408.     (make-local-variable 'forms--format)
  409.     (forms--make-format)
  410.     (make-local-variable 'forms--parser)
  411.     (forms--make-parser)
  412.  
  413.     ;; check if record filters are defined
  414.     (make-local-variable 'forms--new-record-filter)
  415.     (setq forms--new-record-filter 
  416.           (cond
  417.            ((fboundp 'forms-new-record-filter)
  418.         (symbol-function 'forms-new-record-filter))
  419.            ((and (boundp 'forms-new-record-filter)
  420.              (fboundp forms-new-record-filter))
  421.         forms-new-record-filter)))
  422.     (fmakunbound 'forms-new-record-filter)
  423.     (make-local-variable 'forms--modified-record-filter)
  424.     (setq forms--modified-record-filter 
  425.           (cond
  426.            ((fboundp 'forms-modified-record-filter)
  427.         (symbol-function 'forms-modified-record-filter))
  428.            ((and (boundp 'forms-modified-record-filter)
  429.              (fboundp forms-modified-record-filter))
  430.         forms-modified-record-filter)))
  431.     (fmakunbound 'forms-modified-record-filter)
  432.  
  433.     ;; dynamic text support
  434.     (make-local-variable 'forms--dynamic-text)
  435.     (make-local-variable 'forms-fields)
  436.  
  437.     ;; prepare this buffer for further processing
  438.     (setq buffer-read-only nil)
  439.  
  440.     ;; prevent accidental overwrite of the control file and autosave
  441.     (setq buffer-file-name nil)
  442.     (auto-save-mode nil)
  443.  
  444.     ;; and clean it
  445.     (erase-buffer)))
  446.  
  447.   ;; make local variables
  448.   (make-local-variable 'forms--file-buffer)
  449.   (make-local-variable 'forms--total-records)
  450.   (make-local-variable 'forms--current-record)
  451.   (make-local-variable 'forms--the-record-list)
  452.   (make-local-variable 'forms--search-rexexp)
  453.  
  454.   ;; A bug in the current Emacs release prevents a keymap
  455.   ;; which is buffer-local from being used by 'describe-mode'.
  456.   ;; Hence we'll leave it global.
  457.   ;;(make-local-variable 'forms-mode-map)
  458.   (if forms-mode-map            ; already defined
  459.       nil
  460.     (setq forms-mode-map (make-keymap))
  461.     (forms--mode-commands forms-mode-map)
  462.     (forms--change-commands))
  463.  
  464.   ;; find the data file
  465.   (setq forms--file-buffer (find-file-noselect forms-file))
  466.  
  467.   ;; count the number of records, and set see if it may be modified
  468.   (let (ro)
  469.     (setq forms--total-records
  470.       (save-excursion
  471.         (set-buffer forms--file-buffer)
  472.         (bury-buffer (current-buffer))
  473.         (setq ro buffer-read-only)
  474.         (count-lines (point-min) (point-max))))
  475.     (if ro
  476.     (setq forms-read-only t)))
  477.  
  478.   ;; set the major mode indicator
  479.   (setq major-mode 'forms-mode)
  480.   (setq mode-name "Forms")
  481.   (make-local-variable 'minor-mode-alist) ; needed?
  482.   (forms--set-minor-mode)
  483.   (forms--set-keymaps)
  484.  
  485.   (set-buffer-modified-p nil)
  486.  
  487.   ;; We have our own revert function - use it
  488.   (make-local-variable 'revert-buffer-function)
  489.   (setq revert-buffer-function 'forms-revert-buffer)
  490.  
  491.   ;; setup the first (or current) record to show
  492.   (if (< forms--current-record 1)
  493.       (setq forms--current-record 1))
  494.   (forms-jump-record forms--current-record)
  495.  
  496.   ;; user customising
  497.   (run-hooks 'forms-mode-hooks)
  498.  
  499.   ;; be helpful
  500.   (forms--help)
  501.  
  502.   ;; initialization done
  503.   (setq forms--mode-setup t))
  504.  
  505. ;;;
  506. ;;; forms-process-format-list
  507. ;;;
  508. ;;; Validates forms-format-list.
  509. ;;;
  510. ;;; Sets forms--number-of-markers and forms--markers.
  511.  
  512. (defun forms--process-format-list ()
  513.   "Validate forms-format-list and set some global variables."
  514.  
  515.   (forms--debug "forms-forms-list before 1st pass:\n"
  516.         'forms-format-list)
  517.  
  518.   ;; it must be non-nil
  519.   (or forms-format-list
  520.       (error "'forms-format-list' has not been set"))
  521.   ;; it must be a list ...
  522.   (or (listp forms-format-list)
  523.       (error "'forms-format-list' is not a list"))
  524.  
  525.   (setq forms--number-of-markers 0)
  526.  
  527.   (let ((the-list forms-format-list)    ; the list of format elements
  528.     (this-item 0)            ; element in list
  529.     (field-num 0))            ; highest field number 
  530.  
  531.     (setq forms-format-list nil)    ; gonna rebuild
  532.  
  533.     (while the-list
  534.  
  535.       (let ((el (car-safe the-list))
  536.         (rem (cdr-safe the-list)))
  537.  
  538.     ;; if it is a symbol, eval it first
  539.     (if (and (symbolp el)
  540.          (boundp el))
  541.         (setq el (eval el)))
  542.  
  543.     (cond
  544.  
  545.      ;; try string ...
  546.      ((stringp el))            ; string is OK
  547.       
  548.      ;; try numeric ...
  549.      ((numberp el) 
  550.  
  551.       (if (or (<= el 0)
  552.           (> el forms-number-of-fields))
  553.           (error
  554.            "Forms error: field number %d out of range 1..%d"
  555.            el forms-number-of-fields))
  556.  
  557.       (setq forms--number-of-markers (1+ forms--number-of-markers))
  558.       (if (> el field-num)
  559.           (setq field-num el)))
  560.  
  561.      ;; try function
  562.      ((listp el)
  563.       (or (fboundp (car-safe el))
  564.           (error 
  565.            "Forms error: not a function: %s"
  566.            (prin1-to-string (car-safe el)))))
  567.  
  568.      ;; else
  569.      (t
  570.       (error "Invalid element in 'forms-format-list': %s"
  571.          (prin1-to-string el))))
  572.  
  573.     ;; advance to next element of the list
  574.     (setq the-list rem)
  575.     (setq forms-format-list
  576.           (append forms-format-list (list el) nil)))))
  577.  
  578.   (forms--debug "forms-forms-list after 1st pass:\n"
  579.         'forms-format-list)
  580.  
  581.   ;; concat adjacent strings
  582.   (setq forms-format-list (forms--concat-adjacent forms-format-list))
  583.  
  584.   (forms--debug "forms-forms-list after 2nd pass:\n"
  585.         'forms-format-list
  586.         'forms--number-of-markers)
  587.  
  588.   (setq forms--markers (make-vector forms--number-of-markers nil)))
  589.  
  590.  
  591. ;;;
  592. ;;; Build the format routine from forms-format-list.
  593. ;;;
  594. ;;; The format routine (forms--format) will look like
  595. ;;; 
  596. ;;; (lambda (arg)
  597. ;;;   (setq forms--dynamic-text nil)
  598. ;;;   ;;  "text: "
  599. ;;;   (insert "text: ")
  600. ;;;   ;;  6
  601. ;;;   (aset forms--markers 0 (point-marker))
  602. ;;;   (insert (elt arg 5))
  603. ;;;   ;;  "\nmore text: "
  604. ;;;   (insert "\nmore text: ")
  605. ;;;   ;;  (tocol 40)
  606. ;;;   (let ((the-dyntext (tocol 40)))
  607. ;;;     (insert the-dyntext)
  608. ;;;     (setq forms--dynamic-text (append forms--dynamic-text
  609. ;;;                      (list the-dyntext))))
  610. ;;;   ;;  9
  611. ;;;   (aset forms--markers 1 (point-marker))
  612. ;;;   (insert (elt arg 8))
  613. ;;;
  614. ;;;   ... )
  615. ;;; 
  616.  
  617. (defun forms--make-format ()
  618.   "Generate format function for forms"
  619.   (setq forms--format (forms--format-maker forms-format-list))
  620.   (forms--debug 'forms--format))
  621.  
  622. (defun forms--format-maker (the-format-list)
  623.   "Returns the parser function for forms"
  624.   (let ((the-marker 0))
  625.     (` (lambda (arg)
  626.      (setq forms--dynamic-text nil)
  627.      (,@ (apply 'append
  628.             (mapcar 'forms--make-format-elt the-format-list)))))))
  629.  
  630. (defun forms--make-format-elt (el)
  631.   (cond ((stringp el)
  632.      (` ((insert (, el)))))
  633.     ((numberp el)
  634.      (prog1
  635.          (` ((aset forms--markers (, the-marker) (point-marker))
  636.          (insert (elt arg (, (1- el))))))
  637.        (setq the-marker (1+ the-marker))))
  638.     ((listp el)
  639.      (prog1
  640.          (` ((let ((the-dyntext (, el)))
  641.            (insert the-dyntext)
  642.            (setq forms--dynamic-text (append forms--dynamic-text
  643.                              (list the-dyntext)))))
  644.         )))
  645.     ))
  646.  
  647.  
  648. (defun forms--concat-adjacent (the-list)
  649.   "Concatenate adjacent strings in the-list and return the resulting list"
  650.   (if (consp the-list)
  651.       (let ((the-rest (forms--concat-adjacent (cdr the-list))))
  652.     (if (and (stringp (car the-list)) (stringp (car the-rest)))
  653.         (cons (concat (car the-list) (car the-rest))
  654.           (cdr the-rest))
  655.         (cons (car the-list) the-rest)))
  656.       the-list))
  657. ;;;
  658. ;;; forms--make-parser.
  659. ;;;
  660. ;;; Generate parse routine from forms-format-list.
  661. ;;;
  662. ;;; The parse routine (forms--parser) will look like (give or take
  663. ;;; a few " " .
  664. ;;; 
  665. ;;; (lambda nil
  666. ;;;   (let (here)
  667. ;;;     (goto-char (point-min))
  668. ;;; 
  669. ;;;    ;;  "text: "
  670. ;;;     (if (not (looking-at "text: "))
  671. ;;;         (error "Parse error: cannot find \"text: \""))
  672. ;;;     (forward-char 6)    ; past "text: "
  673. ;;; 
  674. ;;;     ;;  6
  675. ;;;    ;;  "\nmore text: "
  676. ;;;     (setq here (point))
  677. ;;;     (if (not (search-forward "\nmore text: " nil t nil))
  678. ;;;         (error "Parse error: cannot find \"\\nmore text: \""))
  679. ;;;     (aset the-recordv 5 (buffer-substring here (- (point) 12)))
  680. ;;;
  681. ;;;    ;;  (tocol 40)
  682. ;;;    (let ((the-dyntext (car-safe forms--dynamic-text)))
  683. ;;;      (if (not (looking-at (regexp-quote the-dyntext)))
  684. ;;;          (error "Parse error: not looking at \"%s\"" the-dyntext))
  685. ;;;      (forward-char (length the-dyntext))
  686. ;;;      (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
  687. ;;;     ... 
  688. ;;;     ;; final flush (due to terminator sentinel, see below)
  689. ;;;    (aset the-recordv 7 (buffer-substring (point) (point-max)))
  690. ;;; 
  691.  
  692. (defun forms--make-parser ()
  693.   "Generate parser function for forms"
  694.   (setq forms--parser (forms--parser-maker forms-format-list))
  695.   (forms--debug 'forms--parser))
  696.  
  697. (defun forms--parser-maker (the-format-list)
  698.   "Returns the parser function for forms"
  699.   (let ((the-field nil)
  700.     (seen-text nil)
  701.     the--format-list)
  702.     ;; add a terminator sentinel
  703.     (setq the--format-list (append the-format-list (list nil)))
  704.     (` (lambda nil
  705.      (let (here)
  706.        (goto-char (point-min))
  707.      (,@ (apply 'append
  708.             (mapcar 'forms--make-parser-elt the--format-list))))))))
  709.  
  710. (defun forms--make-parser-elt (el)
  711.   (cond
  712.    ((stringp el)
  713.     (prog1
  714.     (if the-field
  715.         (` ((setq here (point))
  716.         (if (not (search-forward (, el) nil t nil))
  717.             (error "Parse error: cannot find \"%s\"" (, el)))
  718.         (aset the-recordv (, (1- the-field))
  719.               (buffer-substring here
  720.                     (- (point) (, (length el)))))))
  721.       (` ((if (not (looking-at (, (regexp-quote el))))
  722.           (error "Parse error: not looking at \"%s\"" (, el)))
  723.           (forward-char (, (length el))))))
  724.       (setq seen-text t)
  725.       (setq the-field nil)))
  726.    ((numberp el)
  727.     (if the-field
  728.     (error "Cannot parse adjacent fields %d and %d"
  729.            the-field el)
  730.       (setq the-field el)
  731.       nil))
  732.    ((null el)
  733.     (if the-field
  734.     (` ((aset the-recordv (, (1- the-field))
  735.           (buffer-substring (point) (point-max)))))))
  736.    ((listp el)
  737.     (prog1
  738.     (if the-field
  739.         (` ((let ((here (point))
  740.               (the-dyntext (car-safe forms--dynamic-text)))
  741.           (if (not (search-forward the-dyntext nil t nil))
  742.               (error "Parse error: cannot find \"%s\"" the-dyntext))
  743.           (aset the-recordv (, (1- the-field))
  744.             (buffer-substring here
  745.                       (- (point) (length the-dyntext))))
  746.           (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))))
  747.       (` ((let ((the-dyntext (car-safe forms--dynamic-text)))
  748.         (if (not (looking-at (regexp-quote the-dyntext)))
  749.             (error "Parse error: not looking at \"%s\"" the-dyntext))
  750.         (forward-char (length the-dyntext))
  751.         (setq forms--dynamic-text (cdr-safe forms--dynamic-text))))))
  752.       (setq seen-text t)
  753.       (setq the-field nil)))
  754.    ))
  755. ;;;
  756.  
  757. (defun forms--set-minor-mode ()
  758.   (setq minor-mode-alist
  759.     (if forms-read-only
  760.         " View"
  761.       nil)))
  762.  
  763. (defun forms--set-keymaps ()
  764.   "Set the keymaps used in this mode."
  765.  
  766.   (if forms-read-only
  767.       (use-local-map forms-mode-map)
  768.     (use-local-map (make-sparse-keymap))
  769.     (define-key (current-local-map) "\C-c" forms-mode-map)
  770.     (define-key (current-local-map) "\t"   'forms-next-field)))
  771.  
  772. (defun forms--mode-commands (map)
  773.   "Fill map with all commands."
  774.   (define-key map "\t" 'forms-next-field)
  775.   (define-key map " " 'forms-next-record)
  776.   (define-key map "d" 'forms-delete-record)
  777.   (define-key map "e" 'forms-edit-mode)
  778.   (define-key map "i" 'forms-insert-record)
  779.   (define-key map "j" 'forms-jump-record)
  780.   (define-key map "n" 'forms-next-record)
  781.   (define-key map "p" 'forms-prev-record)
  782.   (define-key map "q" 'forms-exit)
  783.   (define-key map "s" 'forms-search)
  784.   (define-key map "v" 'forms-view-mode)
  785.   (define-key map "x" 'forms-exit-no-save)
  786.   (define-key map "<" 'forms-first-record)
  787.   (define-key map ">" 'forms-last-record)
  788.   (define-key map "?" 'describe-mode)
  789.   (define-key map "\177" 'forms-prev-record)
  790.  ;  (define-key map "\C-c" map)
  791.   (define-key map "\e" 'ESC-prefix)
  792.   (define-key map "\C-x" ctl-x-map)
  793.   (define-key map "\C-u" 'universal-argument)
  794.   (define-key map "\C-h" help-map)
  795.   )
  796. ;;;
  797. ;;; Changed functions
  798. ;;;
  799. ;;; Emacs (as of 18.55) lacks the functionality of buffer-local
  800. ;;; funtions. Therefore we save the original meaning of some handy
  801. ;;; functions, and replace them with a wrapper.
  802.  
  803. (defun forms--change-commands ()
  804.   "Localize some commands."
  805.   ;;
  806.   ;; scroll-down -> forms-prev-record
  807.   ;;
  808.   (if (fboundp 'forms--scroll-down)
  809.       nil
  810.     (fset 'forms--scroll-down (symbol-function 'scroll-down))
  811.     (fset 'scroll-down
  812.       (function
  813.        (lambda (&optional arg) 
  814.          (interactive "P")
  815.          (if (and forms--mode-setup
  816.               forms-forms-scroll)
  817.          (forms-prev-record arg)
  818.            (forms--scroll-down arg))))))
  819.   ;;
  820.   ;; scroll-up -> forms-next-record
  821.   ;;
  822.   (if (fboundp 'forms--scroll-up)
  823.       nil
  824.     (fset 'forms--scroll-up   (symbol-function 'scroll-up))
  825.     (fset 'scroll-up
  826.       (function
  827.        (lambda (&optional arg) 
  828.          (interactive "P")
  829.          (if (and forms--mode-setup
  830.               forms-forms-scroll)
  831.          (forms-next-record arg)
  832.            (forms--scroll-up arg))))))
  833.   ;;
  834.   ;; beginning-of-buffer -> forms-first-record
  835.   ;;
  836.   (if (fboundp 'forms--beginning-of-buffer)
  837.       nil
  838.     (fset 'forms--beginning-of-buffer (symbol-function 'beginning-of-buffer))
  839.     (fset 'beginning-of-buffer
  840.       (function
  841.        (lambda ()
  842.          (interactive)
  843.          (if (and forms--mode-setup
  844.               forms-forms-jump)
  845.          (forms-first-record)
  846.            (forms--beginning-of-buffer))))))
  847.   ;;
  848.   ;; end-of-buffer -> forms-end-record
  849.   ;;
  850.   (if (fboundp 'forms--end-of-buffer)
  851.       nil
  852.     (fset 'forms--end-of-buffer (symbol-function 'end-of-buffer))
  853.     (fset 'end-of-buffer
  854.       (function
  855.        (lambda ()
  856.          (interactive)
  857.          (if (and forms--mode-setup
  858.               forms-forms-jump)
  859.          (forms-last-record)
  860.            (forms--end-of-buffer))))))
  861.   ;;
  862.   ;; save-buffer -> forms--save-buffer
  863.   ;;
  864.   (if (fboundp 'forms--save-buffer)
  865.       nil
  866.     (fset 'forms--save-buffer (symbol-function 'save-buffer))
  867.     (fset 'save-buffer
  868.       (function
  869.        (lambda (&optional arg)
  870.          (interactive "p")
  871.          (if forms--mode-setup
  872.          (progn
  873.            (forms--checkmod)
  874.            (save-excursion
  875.              (set-buffer forms--file-buffer)
  876.              (forms--save-buffer arg)))
  877.            (forms--save-buffer arg))))))
  878.   ;;
  879.   )
  880.  
  881. (defun forms--help ()
  882.   "Initial help."
  883.   ;; We should use
  884.   ;;(message (substitute-command-keys (concat
  885.   ;;"\\[forms-next-record]:next"
  886.   ;;"   \\[forms-prev-record]:prev"
  887.   ;;"   \\[forms-first-record]:first"
  888.   ;;"   \\[forms-last-record]:last"
  889.   ;;"   \\[describe-mode]:help"
  890.   ;;"   \\[forms-exit]:exit")))
  891.   ;; but it's too slow ....
  892.   (if forms-read-only
  893.       (message "SPC:next   DEL:prev   <:first   >:last   ?:help   q:exit")
  894.     (message "C-c n:next   C-c p:prev   C-c <:first   C-c >:last   C-c ?:help   C-c q:exit")))
  895.  
  896. (defun forms--trans (subj arg rep)
  897.   "Translate in SUBJ all chars ARG into char REP. ARG and REP should
  898.  be single-char strings."
  899.   (let ((i 0)
  900.     (x (length subj))
  901.     (re (regexp-quote arg))
  902.     (k (string-to-char rep)))
  903.     (while (setq i (string-match re subj i))
  904.       (aset subj i k)
  905.       (setq i (1+ i)))))
  906.  
  907. (defun forms--exit (query &optional save)
  908.   (let ((buf (buffer-name forms--file-buffer)))
  909.     (forms--checkmod)
  910.     (if (and save
  911.          (buffer-modified-p forms--file-buffer))
  912.     (save-excursion
  913.       (set-buffer forms--file-buffer)
  914.       (save-buffer)))
  915.     (save-excursion
  916.       (set-buffer forms--file-buffer)
  917.       (delete-auto-save-file-if-necessary)
  918.       (kill-buffer (current-buffer)))
  919.     (if (get-buffer buf)    ; not killed???
  920.       (if save
  921.       (progn
  922.         (beep)
  923.         (message "Problem saving buffers?")))
  924.       (delete-auto-save-file-if-necessary)
  925.       (kill-buffer (current-buffer)))))
  926.  
  927. (defun forms--get-record ()
  928.   "Fetch the current record from the file buffer."
  929.   ;;
  930.   ;; This function is executed in the context of the forms--file-buffer.
  931.   ;;
  932.   (or (bolp)
  933.       (beginning-of-line nil))
  934.   (let ((here (point)))
  935.     (prog2
  936.      (end-of-line)
  937.      (buffer-substring here (point))
  938.      (goto-char here))))
  939.  
  940. (defun forms--show-record (the-record)
  941.   "Format THE-RECORD according to forms-format-list,
  942.  and display it in the current buffer."
  943.  
  944.   ;; split the-record
  945.   (let (the-result
  946.     (start-pos 0)
  947.     found-pos
  948.     (field-sep-length (length forms-field-sep)))
  949.     (if forms-multi-line
  950.     (forms--trans the-record forms-multi-line "\n"))
  951.     ;; add an extra separator (makes splitting easy)
  952.     (setq the-record (concat the-record forms-field-sep))
  953.     (while (setq found-pos (string-match forms-field-sep the-record start-pos))
  954.       (let ((ent (substring the-record start-pos found-pos)))
  955.     (setq the-result
  956.           (append the-result (list ent)))
  957.     (setq start-pos (+ field-sep-length found-pos))))
  958.     (setq forms--the-record-list the-result))
  959.  
  960.   (setq buffer-read-only nil)
  961.   (erase-buffer)
  962.  
  963.   ;; verify the number of fields, extend forms--the-record-list if needed
  964.   (if (= (length forms--the-record-list) forms-number-of-fields)
  965.       nil
  966.     (beep)
  967.     (message "Record has %d fields instead of %d."
  968.          (length forms--the-record-list) forms-number-of-fields)
  969.     (if (< (length forms--the-record-list) forms-number-of-fields)
  970.     (setq forms--the-record-list 
  971.           (append forms--the-record-list
  972.               (make-list 
  973.                (- forms-number-of-fields 
  974.               (length forms--the-record-list))
  975.                "")))))
  976.  
  977.   ;; call the formatter function
  978.   (setq forms-fields (append (list nil) forms--the-record-list nil))
  979.   (funcall forms--format forms--the-record-list)
  980.  
  981.   ;; prepare
  982.   (goto-char (point-min))
  983.   (set-buffer-modified-p nil)
  984.   (setq buffer-read-only forms-read-only)
  985.   (setq mode-line-process
  986.     (concat " " forms--current-record "/" forms--total-records)))
  987.  
  988. (defun forms--parse-form ()
  989.   "Parse contents of form into list of strings."
  990.   ;; The contents of the form are parsed, and a new list of strings
  991.   ;; is constructed.
  992.   ;; A vector with the strings from the original record is 
  993.   ;; constructed, which is updated with the new contents. Therefore
  994.   ;; fields which were not in the form are not modified.
  995.   ;; Finally, the vector is transformed into a list for further processing.
  996.  
  997.   (let (the-recordv)
  998.  
  999.     ;; build the vector
  1000.     (setq the-recordv (vconcat forms--the-record-list))
  1001.  
  1002.     ;; parse the form and update the vector
  1003.     (let ((forms--dynamic-text forms--dynamic-text))
  1004.       (funcall forms--parser))
  1005.  
  1006.     (if forms--modified-record-filter
  1007.     ;; As a service to the user, we add a zeroth element so she
  1008.     ;; can use the same indices as in the forms definition.
  1009.     (let ((the-fields (vconcat [nil] the-recordv)))
  1010.       (setq the-fields (funcall forms--modified-record-filter the-fields))
  1011.       (cdr (append the-fields nil)))
  1012.  
  1013.       ;; transform to a list and return
  1014.       (append the-recordv nil))))
  1015.  
  1016. (defun forms--update ()
  1017.   "Update current record with contents of form. As a side effect: sets
  1018. forms--the-record-list ."
  1019.   (if forms-read-only
  1020.       (progn
  1021.     (message "Read-only buffer!")
  1022.     (beep))
  1023.  
  1024.     (let (the-record)
  1025.       ;; build new record
  1026.       (setq forms--the-record-list (forms--parse-form))
  1027.       (setq the-record
  1028.         (mapconcat 'identity forms--the-record-list forms-field-sep))
  1029.  
  1030.       ;; handle multi-line fields, if allowed
  1031.       (if forms-multi-line
  1032.       (forms--trans the-record "\n" forms-multi-line))
  1033.  
  1034.       ;; a final sanity check before updating
  1035.       (if (string-match "\n" the-record)
  1036.       (progn
  1037.         (message "Multi-line fields in this record - update refused!")
  1038.         (beep))
  1039.  
  1040.     (save-excursion
  1041.       (set-buffer forms--file-buffer)
  1042.       ;; Insert something before kill-line is called. See kill-line
  1043.       ;; doc. Bugfix provided by Ignatios Souvatzis.
  1044.       (insert "*")
  1045.       (beginning-of-line)
  1046.       (kill-line nil)
  1047.       (insert the-record)
  1048.       (beginning-of-line))))))
  1049.  
  1050. (defun forms--checkmod ()
  1051.   "Check if this form has been modified, and call forms--update if so."
  1052.   (if (buffer-modified-p nil)
  1053.       (let ((here (point)))
  1054.     (forms--update)
  1055.     (set-buffer-modified-p nil)
  1056.     (goto-char here))))
  1057.  
  1058. ;;;
  1059. ;;; Start and exit
  1060. (defun forms-find-file (fn)
  1061.   "Visit file FN in forms mode"
  1062.   (interactive "fForms file: ")
  1063.   (find-file-read-only fn)
  1064.   (or forms--mode-setup (forms-mode t)))
  1065.  
  1066. (defun forms-find-file-other-window (fn)
  1067.   "Visit file FN in form mode in other window"
  1068.   (interactive "fFbrowse file in other window: ")
  1069.   (find-file-other-window fn)
  1070.   (eval-current-buffer)
  1071.   (or forms--mode-setup (forms-mode t)))
  1072.  
  1073. (defun forms-exit (query)
  1074.   "Normal exit. Modified buffers are saved."
  1075.   (interactive "P")
  1076.   (forms--exit query t))
  1077.  
  1078. (defun forms-exit-no-save (query)
  1079.   "Exit without saving buffers."
  1080.   (interactive "P")
  1081.   (forms--exit query nil))
  1082.  
  1083. ;;;
  1084. ;;; Navigating commands
  1085.  
  1086. (defun forms-next-record (arg)
  1087.   "Advance to the ARGth following record."
  1088.   (interactive "P")
  1089.   (forms-jump-record (+ forms--current-record (prefix-numeric-value arg)) t))
  1090.  
  1091. (defun forms-prev-record (arg)
  1092.   "Advance to the ARGth previous record."
  1093.   (interactive "P")
  1094.   (forms-jump-record (- forms--current-record (prefix-numeric-value arg)) t))
  1095.  
  1096. (defun forms-jump-record (arg &optional relative)
  1097.   "Jump to a random record."
  1098.   (interactive "NRecord number: ")
  1099.  
  1100.   ;; verify that the record number is within range
  1101.   (if (or (> arg forms--total-records)
  1102.       (<= arg 0))
  1103.     (progn
  1104.       (beep)
  1105.       ;; don't give the message if just paging
  1106.       (if (not relative)
  1107.       (message "Record number %d out of range 1..%d"
  1108.            arg forms--total-records))
  1109.       )
  1110.  
  1111.     ;; flush
  1112.     (forms--checkmod)
  1113.  
  1114.     ;; calculate displacement
  1115.     (let ((disp (- arg forms--current-record))
  1116.       (cur forms--current-record))
  1117.  
  1118.       ;; forms--show-record needs it now
  1119.       (setq forms--current-record arg)
  1120.  
  1121.       ;; get the record and show it
  1122.       (forms--show-record
  1123.        (save-excursion
  1124.      (set-buffer forms--file-buffer)
  1125.      (beginning-of-line)
  1126.  
  1127.      ;; move, and adjust the amount if needed (shouldn't happen)
  1128.      (if relative
  1129.          (if (zerop disp)
  1130.          nil
  1131.            (setq cur (+ cur disp (- (forward-line disp)))))
  1132.        (setq cur (+ cur disp (- (goto-line arg)))))
  1133.  
  1134.      (forms--get-record)))
  1135.  
  1136.       ;; this shouldn't happen
  1137.       (if (/= forms--current-record cur)
  1138.       (progn
  1139.         (setq forms--current-record cur)
  1140.         (beep)
  1141.         (message "Stuck at record %d." cur))))))
  1142.  
  1143. (defun forms-first-record ()
  1144.   "Jump to first record."
  1145.   (interactive)
  1146.   (forms-jump-record 1))
  1147.  
  1148. (defun forms-last-record ()
  1149.   "Jump to last record. As a side effect: re-calculates the number
  1150.  of records in the data file."
  1151.   (interactive)
  1152.   (let
  1153.       ((numrec 
  1154.     (save-excursion
  1155.       (set-buffer forms--file-buffer)
  1156.       (count-lines (point-min) (point-max)))))
  1157.     (if (= numrec forms--total-records)
  1158.     nil
  1159.       (beep)
  1160.       (setq forms--total-records numrec)
  1161.       (message "Number of records reset to %d." forms--total-records)))
  1162.   (forms-jump-record forms--total-records))
  1163.  
  1164. ;;;
  1165. ;;; Other commands
  1166. (defun forms-view-mode ()
  1167.   "Visit buffer read-only."
  1168.   (interactive)
  1169.   (if forms-read-only
  1170.       nil
  1171.     (forms--checkmod)            ; sync
  1172.     (setq forms-read-only t)
  1173.     (forms-mode)))
  1174.  
  1175. (defun forms-edit-mode ()
  1176.   "Make form suitable for editing, if possible."
  1177.   (interactive)
  1178.   (let ((ro forms-read-only))
  1179.     (if (save-excursion
  1180.       (set-buffer forms--file-buffer)
  1181.       buffer-read-only)
  1182.     (progn
  1183.       (setq forms-read-only t)
  1184.       (message "No write access to \"%s\"" forms-file)
  1185.       (beep))
  1186.       (setq forms-read-only nil))
  1187.     (if (equal ro forms-read-only)
  1188.     nil
  1189.       (forms-mode))))
  1190.  
  1191. ;; Sample:
  1192. ;; (defun my-new-record-filter (the-fields)
  1193. ;;   ;; numbers are relative to 1
  1194. ;;   (aset the-fields 4 (current-time-string))
  1195. ;;   (aset the-fields 6 (user-login-name))
  1196. ;;   the-list)
  1197. ;; (setq forms-new-record-filter 'my-new-record-filter)
  1198.  
  1199. (defun forms-insert-record (arg)
  1200.   "Create a new record before the current one. With ARG: store the
  1201.  record after the current one.
  1202.  If a function forms-new-record-filter is defined, or forms-new-record-filter
  1203.  contains the name of a function, it is called to
  1204.  fill (some of) the fields with default values."
  1205.  ; The above doc is not true, but for documentary purposes only
  1206.  
  1207.   (interactive "P")
  1208.  
  1209.   (let ((ln (if arg (1+ forms--current-record) forms--current-record))
  1210.         the-list the-record)
  1211.  
  1212.     (forms--checkmod)
  1213.     (if forms--new-record-filter
  1214.     ;; As a service to the user, we add a zeroth element so she
  1215.     ;; can use the same indices as in the forms definition.
  1216.     (let ((the-fields (make-vector (1+ forms-number-of-fields) "")))
  1217.       (setq the-fields (funcall forms--new-record-filter the-fields))
  1218.       (setq the-list (cdr (append the-fields nil))))
  1219.       (setq the-list (make-list forms-number-of-fields "")))
  1220.  
  1221.     (setq the-record
  1222.       (mapconcat
  1223.       'identity
  1224.       the-list
  1225.       forms-field-sep))
  1226.  
  1227.     (save-excursion
  1228.       (set-buffer forms--file-buffer)
  1229.       (goto-line ln)
  1230.       (open-line 1)
  1231.       (insert the-record)
  1232.       (beginning-of-line))
  1233.     
  1234.     (setq forms--current-record ln))
  1235.  
  1236.   (setq forms--total-records (1+ forms--total-records))
  1237.   (forms-jump-record forms--current-record))
  1238.  
  1239. (defun forms-delete-record (arg)
  1240.   "Deletes a record. With ARG: don't ask."
  1241.   (interactive "P")
  1242.   (forms--checkmod)
  1243.   (if (or arg
  1244.       (y-or-n-p "Really delete this record? "))
  1245.       (let ((ln forms--current-record))
  1246.     (save-excursion
  1247.       (set-buffer forms--file-buffer)
  1248.       (goto-line ln)
  1249.       (kill-line 1))
  1250.     (setq forms--total-records (1- forms--total-records))
  1251.     (if (> forms--current-record forms--total-records)
  1252.         (setq forms--current-record forms--total-records))
  1253.     (forms-jump-record forms--current-record)))
  1254.   (message ""))
  1255.  
  1256. (defun forms-search (regexp)
  1257.   "Search REGEXP in file buffer."
  1258.   (interactive 
  1259.    (list (read-string (concat "Search for" 
  1260.                   (if forms--search-regexp
  1261.                    (concat " ("
  1262.                        forms--search-regexp
  1263.                        ")"))
  1264.                   ": "))))
  1265.   (if (equal "" regexp)
  1266.       (setq regexp forms--search-regexp))
  1267.   (forms--checkmod)
  1268.  
  1269.   (let (the-line the-record here
  1270.          (fld-sep forms-field-sep))
  1271.     (if (save-excursion
  1272.       (set-buffer forms--file-buffer)
  1273.       (setq here (point))
  1274.       (end-of-line)
  1275.       (if (null (re-search-forward regexp nil t))
  1276.           (progn
  1277.         (goto-char here)
  1278.         (message (concat "\"" regexp "\" not found."))
  1279.         nil)
  1280.         (setq the-record (forms--get-record))
  1281.         (setq the-line (1+ (count-lines (point-min) (point))))))
  1282.     (progn
  1283.       (setq forms--current-record the-line)
  1284.       (forms--show-record the-record)
  1285.       (re-search-forward regexp nil t))))
  1286.   (setq forms--search-regexp regexp))
  1287.  
  1288. (defun forms-revert-buffer (&optional arg noconfirm)
  1289.   "Reverts current form to un-modified."
  1290.   (interactive "P")
  1291.   (if (or noconfirm
  1292.       (yes-or-no-p "Revert form to unmodified? "))
  1293.       (progn
  1294.     (set-buffer-modified-p nil)
  1295.     (forms-jump-record forms--current-record))))
  1296.  
  1297. (defun forms-next-field (arg)
  1298.   "Jump to ARG-th next field."
  1299.   (interactive "p")
  1300.  
  1301.   (let ((i 0)
  1302.     (here (point))
  1303.     there
  1304.     (cnt 0))
  1305.  
  1306.     (if (zerop arg)
  1307.     (setq cnt 1)
  1308.       (setq cnt (+ cnt arg)))
  1309.  
  1310.     (if (catch 'done
  1311.       (while (< i forms--number-of-markers)
  1312.         (if (or (null (setq there (aref forms--markers i)))
  1313.             (<= there here))
  1314.         nil
  1315.           (if (<= (setq cnt (1- cnt)) 0)
  1316.           (progn
  1317.             (goto-char there)
  1318.             (throw 'done t))))
  1319.         (setq i (1+ i))))
  1320.     nil
  1321.       (goto-char (aref forms--markers 0)))))
  1322.  
  1323. ;;;
  1324. ;;; Special service
  1325. ;;;
  1326. (defun forms-enumerate (the-fields)
  1327.   "Take a quoted list of symbols, and set their values to the numbers
  1328. 1, 2 and so on. Returns the higest number.
  1329.  
  1330. Usage: (setq forms-number-of-fields
  1331.              (forms-enumerate
  1332.               '(field1 field2 field2 ...)))"
  1333.  
  1334.   (let ((the-index 0))
  1335.     (while the-fields
  1336.       (setq the-index (1+ the-index))
  1337.       (let ((el (car-safe the-fields)))
  1338.     (setq the-fields (cdr-safe the-fields))
  1339.     (set el the-index)))
  1340.     the-index))
  1341.  
  1342. ;;;
  1343. ;;; Debugging
  1344. ;;;
  1345. (defvar forms--debug nil
  1346.   "*Enables forms-mode debugging if not nil.")
  1347.  
  1348. (defun forms--debug (&rest args)
  1349.   "Internal - debugging routine"
  1350.   (if forms--debug
  1351.       (let ((ret nil))
  1352.     (while args
  1353.       (let ((el (car-safe args)))
  1354.         (setq args (cdr-safe args))
  1355.         (if (stringp el)
  1356.         (setq ret (concat ret el))
  1357.           (setq ret (concat ret (prin1-to-string el) " = "))
  1358.           (if (boundp el)
  1359.           (let ((vel (eval el)))
  1360.             (setq ret (concat ret (prin1-to-string vel) "\n")))
  1361.         (setq ret (concat ret "<unbound>" "\n")))
  1362.           (if (fboundp el)
  1363.           (setq ret (concat ret (prin1-to-string (symbol-function el)) 
  1364.                     "\n"))))))
  1365.     (save-excursion
  1366.       (set-buffer (get-buffer-create "*forms-mode debug*"))
  1367.       (goto-char (point-max))
  1368.       (insert ret)))))
  1369.  
  1370. ;;; Local Variables:
  1371. ;;; eval: (headers)
  1372. ;;; eval: (setq comment-start ";;; ")
  1373. ;;; End:
  1374.