home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / infoserv / gopher / Unix / emacs-client / forms / forms.el.Z / forms.el
Encoding:
Text File  |  1993-10-21  |  41.0 KB  |  1,366 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. (defun forms-mode (&optional primary)
  354.   "Major mode to visit files in a field-structured manner using a form.
  355.  
  356.  Commands (prefix with C-c if not in read-only mode):
  357.  \\{forms-mode-map}"
  358.  
  359.   (interactive)                ; no - 'primary' is not prefix arg
  360.  
  361.   ;; Primary set-up: evaluate buffer and check if the mandatory
  362.   ;; variables have been set.
  363.   (if (or primary (not forms--mode-setup))
  364.       (progn
  365.     (kill-all-local-variables)
  366.  
  367.     ;; make mandatory variables
  368.     (make-local-variable 'forms-file)
  369.     (make-local-variable 'forms-number-of-fields)
  370.     (make-local-variable 'forms-format-list)
  371.  
  372.     ;; make optional variables
  373.     (make-local-variable 'forms-field-sep)
  374.         (make-local-variable 'forms-read-only)
  375.         (make-local-variable 'forms-multi-line)
  376.     (make-local-variable 'forms-forms-scroll)
  377.     (make-local-variable 'forms-forms-jump)
  378.     (fmakunbound 'forms-new-record-filter)
  379.  
  380.     ;; eval the buffer, should set variables
  381.     (eval-current-buffer)
  382.  
  383.     ;; check if the mandatory variables make sense.
  384.     (or forms-file
  385.         (error "'forms-file' has not been set"))
  386.     (or forms-number-of-fields
  387.         (error "'forms-number-of-fields' has not been set"))
  388.     (or (> forms-number-of-fields 0)
  389.         (error "'forms-number-of-fields' must be > 0")
  390.     (or (stringp forms-field-sep))
  391.         (error "'forms-field-sep' is not a string"))
  392.     (if forms-multi-line
  393.         (if (and (stringp forms-multi-line)
  394.              (eq (length forms-multi-line) 1))
  395.         (if (string= forms-multi-line forms-field-sep)
  396.             (error "'forms-multi-line' is equal to 'forms-field-sep'"))
  397.           (error "'forms-multi-line' must be nil or a one-character string")))
  398.         
  399.     ;; validate and process forms-format-list
  400.     (make-local-variable 'forms--number-of-markers)
  401.     (make-local-variable 'forms--markers)
  402.     (forms--process-format-list)
  403.  
  404.     ;; build the formatter and parser
  405.     (make-local-variable 'forms--format)
  406.     (forms--make-format)
  407.     (make-local-variable 'forms--parser)
  408.     (forms--make-parser)
  409.  
  410.     ;; check if record filters are defined
  411.     (make-local-variable 'forms--new-record-filter)
  412.     (setq forms--new-record-filter 
  413.           (cond
  414.            ((fboundp 'forms-new-record-filter)
  415.         (symbol-function 'forms-new-record-filter))
  416.            ((and (boundp 'forms-new-record-filter)
  417.              (fboundp forms-new-record-filter))
  418.         forms-new-record-filter)))
  419.     (fmakunbound 'forms-new-record-filter)
  420.     (make-local-variable 'forms--modified-record-filter)
  421.     (setq forms--modified-record-filter 
  422.           (cond
  423.            ((fboundp 'forms-modified-record-filter)
  424.         (symbol-function 'forms-modified-record-filter))
  425.            ((and (boundp 'forms-modified-record-filter)
  426.              (fboundp forms-modified-record-filter))
  427.         forms-modified-record-filter)))
  428.     (fmakunbound 'forms-modified-record-filter)
  429.  
  430.     ;; dynamic text support
  431.     (make-local-variable 'forms--dynamic-text)
  432.     (make-local-variable 'forms-fields)
  433.  
  434.     ;; prepare this buffer for further processing
  435.     (setq buffer-read-only nil)
  436.  
  437.     ;; prevent accidental overwrite of the control file and autosave
  438.     (setq buffer-file-name nil)
  439.     (auto-save-mode nil)
  440.  
  441.     ;; and clean it
  442.     (erase-buffer)))
  443.  
  444.   ;; make local variables
  445.   (make-local-variable 'forms--file-buffer)
  446.   (make-local-variable 'forms--total-records)
  447.   (make-local-variable 'forms--current-record)
  448.   (make-local-variable 'forms--the-record-list)
  449.   (make-local-variable 'forms--search-rexexp)
  450.  
  451.   ;; A bug in the current Emacs release prevents a keymap
  452.   ;; which is buffer-local from being used by 'describe-mode'.
  453.   ;; Hence we'll leave it global.
  454.   ;;(make-local-variable 'forms-mode-map)
  455.   (if forms-mode-map            ; already defined
  456.       nil
  457.     (setq forms-mode-map (make-keymap))
  458.     (forms--mode-commands forms-mode-map)
  459.     (forms--change-commands))
  460.  
  461.   ;; find the data file
  462.   (setq forms--file-buffer (find-file-noselect forms-file))
  463.  
  464.   ;; count the number of records, and set see if it may be modified
  465.   (let (ro)
  466.     (setq forms--total-records
  467.       (save-excursion
  468.         (set-buffer forms--file-buffer)
  469.         (bury-buffer (current-buffer))
  470.         (setq ro buffer-read-only)
  471.         (count-lines (point-min) (point-max))))
  472.     (if ro
  473.     (setq forms-read-only t)))
  474.  
  475.   ;; set the major mode indicator
  476.   (setq major-mode 'forms-mode)
  477.   (setq mode-name "Forms")
  478.   (make-local-variable 'minor-mode-alist) ; needed?
  479.   (forms--set-minor-mode)
  480.   (forms--set-keymaps)
  481.  
  482.   (set-buffer-modified-p nil)
  483.  
  484.   ;; We have our own revert function - use it
  485.   (make-local-variable 'revert-buffer-function)
  486.   (setq revert-buffer-function 'forms-revert-buffer)
  487.  
  488.   ;; setup the first (or current) record to show
  489.   (if (< forms--current-record 1)
  490.       (setq forms--current-record 1))
  491.   (forms-jump-record forms--current-record)
  492.  
  493.   ;; user customising
  494.   (run-hooks 'forms-mode-hooks)
  495.  
  496.   ;; be helpful
  497.   (forms--help)
  498.  
  499.   ;; initialization done
  500.   (setq forms--mode-setup t))
  501.  
  502. ;;;
  503. ;;; forms-process-format-list
  504. ;;;
  505. ;;; Validates forms-format-list.
  506. ;;;
  507. ;;; Sets forms--number-of-markers and forms--markers.
  508.  
  509. (defun forms--process-format-list ()
  510.   "Validate forms-format-list and set some global variables."
  511.  
  512.   (forms--debug "forms-forms-list before 1st pass:\n"
  513.         'forms-format-list)
  514.  
  515.   ;; it must be non-nil
  516.   (or forms-format-list
  517.       (error "'forms-format-list' has not been set"))
  518.   ;; it must be a list ...
  519.   (or (listp forms-format-list)
  520.       (error "'forms-format-list' is not a list"))
  521.  
  522.   (setq forms--number-of-markers 0)
  523.  
  524.   (let ((the-list forms-format-list)    ; the list of format elements
  525.     (this-item 0)            ; element in list
  526.     (field-num 0))            ; highest field number 
  527.  
  528.     (setq forms-format-list nil)    ; gonna rebuild
  529.  
  530.     (while the-list
  531.  
  532.       (let ((el (car-safe the-list))
  533.         (rem (cdr-safe the-list)))
  534.  
  535.     ;; if it is a symbol, eval it first
  536.     (if (and (symbolp el)
  537.          (boundp el))
  538.         (setq el (eval el)))
  539.  
  540.     (cond
  541.  
  542.      ;; try string ...
  543.      ((stringp el))            ; string is OK
  544.       
  545.      ;; try numeric ...
  546.      ((numberp el) 
  547.  
  548.       (if (or (<= el 0)
  549.           (> el forms-number-of-fields))
  550.           (error
  551.            "Forms error: field number %d out of range 1..%d"
  552.            el forms-number-of-fields))
  553.  
  554.       (setq forms--number-of-markers (1+ forms--number-of-markers))
  555.       (if (> el field-num)
  556.           (setq field-num el)))
  557.  
  558.      ;; try function
  559.      ((listp el)
  560.       (or (fboundp (car-safe el))
  561.           (error 
  562.            "Forms error: not a function: %s"
  563.            (prin1-to-string (car-safe el)))))
  564.  
  565.      ;; else
  566.      (t
  567.       (error "Invalid element in 'forms-format-list': %s"
  568.          (prin1-to-string el))))
  569.  
  570.     ;; advance to next element of the list
  571.     (setq the-list rem)
  572.     (setq forms-format-list
  573.           (append forms-format-list (list el) nil)))))
  574.  
  575.   (forms--debug "forms-forms-list after 1st pass:\n"
  576.         'forms-format-list)
  577.  
  578.   ;; concat adjacent strings
  579.   (setq forms-format-list (forms--concat-adjacent forms-format-list))
  580.  
  581.   (forms--debug "forms-forms-list after 2nd pass:\n"
  582.         'forms-format-list
  583.         'forms--number-of-markers)
  584.  
  585.   (setq forms--markers (make-vector forms--number-of-markers nil)))
  586.  
  587.  
  588. ;;;
  589. ;;; Build the format routine from forms-format-list.
  590. ;;;
  591. ;;; The format routine (forms--format) will look like
  592. ;;; 
  593. ;;; (lambda (arg)
  594. ;;;   (setq forms--dynamic-text nil)
  595. ;;;   ;;  "text: "
  596. ;;;   (insert "text: ")
  597. ;;;   ;;  6
  598. ;;;   (aset forms--markers 0 (point-marker))
  599. ;;;   (insert (elt arg 5))
  600. ;;;   ;;  "\nmore text: "
  601. ;;;   (insert "\nmore text: ")
  602. ;;;   ;;  (tocol 40)
  603. ;;;   (let ((the-dyntext (tocol 40)))
  604. ;;;     (insert the-dyntext)
  605. ;;;     (setq forms--dynamic-text (append forms--dynamic-text
  606. ;;;                      (list the-dyntext))))
  607. ;;;   ;;  9
  608. ;;;   (aset forms--markers 1 (point-marker))
  609. ;;;   (insert (elt arg 8))
  610. ;;;
  611. ;;;   ... )
  612. ;;; 
  613.  
  614. (defun forms--make-format ()
  615.   "Generate format function for forms"
  616.   (setq forms--format (forms--format-maker forms-format-list))
  617.   (forms--debug 'forms--format))
  618.  
  619. (defun forms--format-maker (the-format-list)
  620.   "Returns the parser function for forms"
  621.   (let ((the-marker 0))
  622.     (` (lambda (arg)
  623.      (setq forms--dynamic-text nil)
  624.      (,@ (apply 'append
  625.             (mapcar 'forms--make-format-elt the-format-list)))))))
  626.  
  627. (defun forms--make-format-elt (el)
  628.   (cond ((stringp el)
  629.      (` ((insert (, el)))))
  630.     ((numberp el)
  631.      (prog1
  632.          (` ((aset forms--markers (, the-marker) (point-marker))
  633.          (insert (elt arg (, (1- el))))))
  634.        (setq the-marker (1+ the-marker))))
  635.     ((listp el)
  636.      (prog1
  637.          (` ((let ((the-dyntext (, el)))
  638.            (insert the-dyntext)
  639.            (setq forms--dynamic-text (append forms--dynamic-text
  640.                              (list the-dyntext)))))
  641.         )))
  642.     ))
  643.  
  644.  
  645. (defun forms--concat-adjacent (the-list)
  646.   "Concatenate adjacent strings in the-list and return the resulting list"
  647.   (if (consp the-list)
  648.       (let ((the-rest (forms--concat-adjacent (cdr the-list))))
  649.     (if (and (stringp (car the-list)) (stringp (car the-rest)))
  650.         (cons (concat (car the-list) (car the-rest))
  651.           (cdr the-rest))
  652.         (cons (car the-list) the-rest)))
  653.       the-list))
  654. ;;;
  655. ;;; forms--make-parser.
  656. ;;;
  657. ;;; Generate parse routine from forms-format-list.
  658. ;;;
  659. ;;; The parse routine (forms--parser) will look like (give or take
  660. ;;; a few " " .
  661. ;;; 
  662. ;;; (lambda nil
  663. ;;;   (let (here)
  664. ;;;     (goto-char (point-min))
  665. ;;; 
  666. ;;;    ;;  "text: "
  667. ;;;     (if (not (looking-at "text: "))
  668. ;;;         (error "Parse error: cannot find \"text: \""))
  669. ;;;     (forward-char 6)    ; past "text: "
  670. ;;; 
  671. ;;;     ;;  6
  672. ;;;    ;;  "\nmore text: "
  673. ;;;     (setq here (point))
  674. ;;;     (if (not (search-forward "\nmore text: " nil t nil))
  675. ;;;         (error "Parse error: cannot find \"\\nmore text: \""))
  676. ;;;     (aset the-recordv 5 (buffer-substring here (- (point) 12)))
  677. ;;;
  678. ;;;    ;;  (tocol 40)
  679. ;;;    (let ((the-dyntext (car-safe forms--dynamic-text)))
  680. ;;;      (if (not (looking-at (regexp-quote the-dyntext)))
  681. ;;;          (error "Parse error: not looking at \"%s\"" the-dyntext))
  682. ;;;      (forward-char (length the-dyntext))
  683. ;;;      (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
  684. ;;;     ... 
  685. ;;;     ;; final flush (due to terminator sentinel, see below)
  686. ;;;    (aset the-recordv 7 (buffer-substring (point) (point-max)))
  687. ;;; 
  688.  
  689. (defun forms--make-parser ()
  690.   "Generate parser function for forms"
  691.   (setq forms--parser (forms--parser-maker forms-format-list))
  692.   (forms--debug 'forms--parser))
  693.  
  694. (defun forms--parser-maker (the-format-list)
  695.   "Returns the parser function for forms"
  696.   (let ((the-field nil)
  697.     (seen-text nil)
  698.     the--format-list)
  699.     ;; add a terminator sentinel
  700.     (setq the--format-list (append the-format-list (list nil)))
  701.     (` (lambda nil
  702.      (let (here)
  703.        (goto-char (point-min))
  704.      (,@ (apply 'append
  705.             (mapcar 'forms--make-parser-elt the--format-list))))))))
  706.  
  707. (defun forms--make-parser-elt (el)
  708.   (cond
  709.    ((stringp el)
  710.     (prog1
  711.     (if the-field
  712.         (` ((setq here (point))
  713.         (if (not (search-forward (, el) nil t nil))
  714.             (error "Parse error: cannot find \"%s\"" (, el)))
  715.         (aset the-recordv (, (1- the-field))
  716.               (buffer-substring here
  717.                     (- (point) (, (length el)))))))
  718.       (` ((if (not (looking-at (, (regexp-quote el))))
  719.           (error "Parse error: not looking at \"%s\"" (, el)))
  720.           (forward-char (, (length el))))))
  721.       (setq seen-text t)
  722.       (setq the-field nil)))
  723.    ((numberp el)
  724.     (if the-field
  725.     (error "Cannot parse adjacent fields %d and %d"
  726.            the-field el)
  727.       (setq the-field el)
  728.       nil))
  729.    ((null el)
  730.     (if the-field
  731.     (` ((aset the-recordv (, (1- the-field))
  732.           (buffer-substring (point) (point-max)))))))
  733.    ((listp el)
  734.     (prog1
  735.     (if the-field
  736.         (` ((let ((here (point))
  737.               (the-dyntext (car-safe forms--dynamic-text)))
  738.           (if (not (search-forward the-dyntext nil t nil))
  739.               (error "Parse error: cannot find \"%s\"" the-dyntext))
  740.           (aset the-recordv (, (1- the-field))
  741.             (buffer-substring here
  742.                       (- (point) (length the-dyntext))))
  743.           (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))))
  744.       (` ((let ((the-dyntext (car-safe forms--dynamic-text)))
  745.         (if (not (looking-at (regexp-quote the-dyntext)))
  746.             (error "Parse error: not looking at \"%s\"" the-dyntext))
  747.         (forward-char (length the-dyntext))
  748.         (setq forms--dynamic-text (cdr-safe forms--dynamic-text))))))
  749.       (setq seen-text t)
  750.       (setq the-field nil)))
  751.    ))
  752. ;;;
  753.  
  754. (defun forms--set-minor-mode ()
  755.   (setq minor-mode-alist
  756.     (if forms-read-only
  757.         " View"
  758.       nil)))
  759.  
  760. (defun forms--set-keymaps ()
  761.   "Set the keymaps used in this mode."
  762.  
  763.   (if forms-read-only
  764.       (use-local-map forms-mode-map)
  765.     (use-local-map (make-sparse-keymap))
  766.     (define-key (current-local-map) "\C-c" forms-mode-map)
  767.     (define-key (current-local-map) "\t"   'forms-next-field)))
  768.  
  769. (defun forms--mode-commands (map)
  770.   "Fill map with all commands."
  771.   (define-key map "\t" 'forms-next-field)
  772.   (define-key map " " 'forms-next-record)
  773.   (define-key map "d" 'forms-delete-record)
  774.   (define-key map "e" 'forms-edit-mode)
  775.   (define-key map "i" 'forms-insert-record)
  776.   (define-key map "j" 'forms-jump-record)
  777.   (define-key map "n" 'forms-next-record)
  778.   (define-key map "p" 'forms-prev-record)
  779.   (define-key map "q" 'forms-exit)
  780.   (define-key map "s" 'forms-search)
  781.   (define-key map "v" 'forms-view-mode)
  782.   (define-key map "x" 'forms-exit-no-save)
  783.   (define-key map "<" 'forms-first-record)
  784.   (define-key map ">" 'forms-last-record)
  785.   (define-key map "?" 'describe-mode)
  786.   (define-key map "\177" 'forms-prev-record)
  787.  ;  (define-key map "\C-c" map)
  788.   (define-key map "\e" 'ESC-prefix)
  789.   (define-key map "\C-x" ctl-x-map)
  790.   (define-key map "\C-u" 'universal-argument)
  791.   (define-key map "\C-h" help-map)
  792.   )
  793. ;;;
  794. ;;; Changed functions
  795. ;;;
  796. ;;; Emacs (as of 18.55) lacks the functionality of buffer-local
  797. ;;; funtions. Therefore we save the original meaning of some handy
  798. ;;; functions, and replace them with a wrapper.
  799.  
  800. (defun forms--change-commands ()
  801.   "Localize some commands."
  802.   ;;
  803.   ;; scroll-down -> forms-prev-record
  804.   ;;
  805.   (if (fboundp 'forms--scroll-down)
  806.       nil
  807.     (fset 'forms--scroll-down (symbol-function 'scroll-down))
  808.     (fset 'scroll-down
  809.       '(lambda (&optional arg) 
  810.          (interactive "P")
  811.          (if (and forms--mode-setup
  812.               forms-forms-scroll)
  813.          (forms-prev-record arg)
  814.            (forms--scroll-down arg)))))
  815.   ;;
  816.   ;; scroll-up -> forms-next-record
  817.   ;;
  818.   (if (fboundp 'forms--scroll-up)
  819.       nil
  820.     (fset 'forms--scroll-up   (symbol-function 'scroll-up))
  821.     (fset 'scroll-up
  822.       '(lambda (&optional arg) 
  823.          (interactive "P")
  824.          (if (and forms--mode-setup
  825.               forms-forms-scroll)
  826.          (forms-next-record arg)
  827.            (forms--scroll-up arg)))))
  828.   ;;
  829.   ;; beginning-of-buffer -> forms-first-record
  830.   ;;
  831.   (if (fboundp 'forms--beginning-of-buffer)
  832.       nil
  833.     (fset 'forms--beginning-of-buffer (symbol-function 'beginning-of-buffer))
  834.     (fset 'beginning-of-buffer
  835.       '(lambda ()
  836.          (interactive)
  837.          (if (and forms--mode-setup
  838.               forms-forms-jump)
  839.          (forms-first-record)
  840.            (forms--beginning-of-buffer)))))
  841.   ;;
  842.   ;; end-of-buffer -> forms-end-record
  843.   ;;
  844.   (if (fboundp 'forms--end-of-buffer)
  845.       nil
  846.     (fset 'forms--end-of-buffer (symbol-function 'end-of-buffer))
  847.     (fset 'end-of-buffer
  848.       '(lambda ()
  849.          (interactive)
  850.          (if (and forms--mode-setup
  851.               forms-forms-jump)
  852.          (forms-last-record)
  853.            (forms--end-of-buffer)))))
  854.   ;;
  855.   ;; save-buffer -> forms--save-buffer
  856.   ;;
  857.   (if (fboundp 'forms--save-buffer)
  858.       nil
  859.     (fset 'forms--save-buffer (symbol-function 'save-buffer))
  860.     (fset 'save-buffer
  861.       '(lambda (&optional arg)
  862.          (interactive "p")
  863.          (if forms--mode-setup
  864.          (progn
  865.            (forms--checkmod)
  866.            (save-excursion
  867.              (set-buffer forms--file-buffer)
  868.              (forms--save-buffer arg)))
  869.            (forms--save-buffer arg)))))
  870.   ;;
  871.   )
  872.  
  873. (defun forms--help ()
  874.   "Initial help."
  875.   ;; We should use
  876.   ;;(message (substitute-command-keys (concat
  877.   ;;"\\[forms-next-record]:next"
  878.   ;;"   \\[forms-prev-record]:prev"
  879.   ;;"   \\[forms-first-record]:first"
  880.   ;;"   \\[forms-last-record]:last"
  881.   ;;"   \\[describe-mode]:help"
  882.   ;;"   \\[forms-exit]:exit")))
  883.   ;; but it's too slow ....
  884.   (if forms-read-only
  885.       (message "SPC:next   DEL:prev   <:first   >:last   ?:help   q:exit")
  886.     (message "C-c n:next   C-c p:prev   C-c <:first   C-c >:last   C-c ?:help   C-c q:exit")))
  887.  
  888. (defun forms--trans (subj arg rep)
  889.   "Translate in SUBJ all chars ARG into char REP. ARG and REP should
  890.  be single-char strings."
  891.   (let ((i 0)
  892.     (x (length subj))
  893.     (re (regexp-quote arg))
  894.     (k (string-to-char rep)))
  895.     (while (setq i (string-match re subj i))
  896.       (aset subj i k)
  897.       (setq i (1+ i)))))
  898.  
  899. (defun forms--exit (query &optional save)
  900.   (let ((buf (buffer-name forms--file-buffer)))
  901.     (forms--checkmod)
  902.     (if (and save
  903.          (buffer-modified-p forms--file-buffer))
  904.     (save-excursion
  905.       (set-buffer forms--file-buffer)
  906.       (save-buffer)))
  907.     (save-excursion
  908.       (set-buffer forms--file-buffer)
  909.       (delete-auto-save-file-if-necessary)
  910.       (kill-buffer (current-buffer)))
  911.     (if (get-buffer buf)    ; not killed???
  912.       (if save
  913.       (progn
  914.         (beep)
  915.         (message "Problem saving buffers?")))
  916.       (delete-auto-save-file-if-necessary)
  917.       (kill-buffer (current-buffer)))))
  918.  
  919. (defun forms--get-record ()
  920.   "Fetch the current record from the file buffer."
  921.   ;;
  922.   ;; This function is executed in the context of the forms--file-buffer.
  923.   ;;
  924.   (or (bolp)
  925.       (beginning-of-line nil))
  926.   (let ((here (point)))
  927.     (prog2
  928.      (end-of-line)
  929.      (buffer-substring here (point))
  930.      (goto-char here))))
  931.  
  932. (defun forms--show-record (the-record)
  933.   "Format THE-RECORD according to forms-format-list,
  934.  and display it in the current buffer."
  935.  
  936.   ;; split the-record
  937.   (let (the-result
  938.     (start-pos 0)
  939.     found-pos
  940.     (field-sep-length (length forms-field-sep)))
  941.     (if forms-multi-line
  942.     (forms--trans the-record forms-multi-line "\n"))
  943.     ;; add an extra separator (makes splitting easy)
  944.     (setq the-record (concat the-record forms-field-sep))
  945.     (while (setq found-pos (string-match forms-field-sep the-record start-pos))
  946.       (let ((ent (substring the-record start-pos found-pos)))
  947.     (setq the-result
  948.           (append the-result (list ent)))
  949.     (setq start-pos (+ field-sep-length found-pos))))
  950.     (setq forms--the-record-list the-result))
  951.  
  952.   (setq buffer-read-only nil)
  953.   (erase-buffer)
  954.  
  955.   ;; verify the number of fields, extend forms--the-record-list if needed
  956.   (if (= (length forms--the-record-list) forms-number-of-fields)
  957.       nil
  958.     (beep)
  959.     (message "Record has %d fields instead of %d."
  960.          (length forms--the-record-list) forms-number-of-fields)
  961.     (if (< (length forms--the-record-list) forms-number-of-fields)
  962.     (setq forms--the-record-list 
  963.           (append forms--the-record-list
  964.               (make-list 
  965.                (- forms-number-of-fields 
  966.               (length forms--the-record-list))
  967.                "")))))
  968.  
  969.   ;; call the formatter function
  970.   (setq forms-fields (append (list nil) forms--the-record-list nil))
  971.   (funcall forms--format forms--the-record-list)
  972.  
  973.   ;; prepare
  974.   (goto-char (point-min))
  975.   (set-buffer-modified-p nil)
  976.   (setq buffer-read-only forms-read-only)
  977.   (setq mode-line-process
  978.     (concat " " forms--current-record "/" forms--total-records)))
  979.  
  980. (defun forms--parse-form ()
  981.   "Parse contents of form into list of strings."
  982.   ;; The contents of the form are parsed, and a new list of strings
  983.   ;; is constructed.
  984.   ;; A vector with the strings from the original record is 
  985.   ;; constructed, which is updated with the new contents. Therefore
  986.   ;; fields which were not in the form are not modified.
  987.   ;; Finally, the vector is transformed into a list for further processing.
  988.  
  989.   (let (the-recordv)
  990.  
  991.     ;; build the vector
  992.     (setq the-recordv (vconcat forms--the-record-list))
  993.  
  994.     ;; parse the form and update the vector
  995.     (let ((forms--dynamic-text forms--dynamic-text))
  996.       (funcall forms--parser))
  997.  
  998.     (if forms--modified-record-filter
  999.     ;; As a service to the user, we add a zeroth element so she
  1000.     ;; can use the same indices as in the forms definition.
  1001.     (let ((the-fields (vconcat [nil] the-recordv)))
  1002.       (setq the-fields (funcall forms--modified-record-filter the-fields))
  1003.       (cdr (append the-fields nil)))
  1004.  
  1005.       ;; transform to a list and return
  1006.       (append the-recordv nil))))
  1007.  
  1008. (defun forms--update ()
  1009.   "Update current record with contents of form. As a side effect: sets
  1010. forms--the-record-list ."
  1011.   (if forms-read-only
  1012.       (progn
  1013.     (message "Read-only buffer!")
  1014.     (beep))
  1015.  
  1016.     (let (the-record)
  1017.       ;; build new record
  1018.       (setq forms--the-record-list (forms--parse-form))
  1019.       (setq the-record
  1020.         (mapconcat 'identity forms--the-record-list forms-field-sep))
  1021.  
  1022.       ;; handle multi-line fields, if allowed
  1023.       (if forms-multi-line
  1024.       (forms--trans the-record "\n" forms-multi-line))
  1025.  
  1026.       ;; a final sanity check before updating
  1027.       (if (string-match "\n" the-record)
  1028.       (progn
  1029.         (message "Multi-line fields in this record - update refused!")
  1030.         (beep))
  1031.  
  1032.     (save-excursion
  1033.       (set-buffer forms--file-buffer)
  1034.       ;; Insert something before kill-line is called. See kill-line
  1035.       ;; doc. Bugfix provided by Ignatios Souvatzis.
  1036.       (insert "*")
  1037.       (beginning-of-line)
  1038.       (kill-line nil)
  1039.       (insert the-record)
  1040.       (beginning-of-line))))))
  1041.  
  1042. (defun forms--checkmod ()
  1043.   "Check if this form has been modified, and call forms--update if so."
  1044.   (if (buffer-modified-p nil)
  1045.       (let ((here (point)))
  1046.     (forms--update)
  1047.     (set-buffer-modified-p nil)
  1048.     (goto-char here))))
  1049.  
  1050. ;;;
  1051. ;;; Start and exit
  1052. (defun forms-find-file (fn)
  1053.   "Visit file FN in forms mode"
  1054.   (interactive "fForms file: ")
  1055.   (find-file-read-only fn)
  1056.   (or forms--mode-setup (forms-mode t)))
  1057.  
  1058. (defun forms-find-file-other-window (fn)
  1059.   "Visit file FN in form mode in other window"
  1060.   (interactive "fFbrowse file in other window: ")
  1061.   (find-file-other-window fn)
  1062.   (eval-current-buffer)
  1063.   (or forms--mode-setup (forms-mode t)))
  1064.  
  1065. (defun forms-exit (query)
  1066.   "Normal exit. Modified buffers are saved."
  1067.   (interactive "P")
  1068.   (forms--exit query t))
  1069.  
  1070. (defun forms-exit-no-save (query)
  1071.   "Exit without saving buffers."
  1072.   (interactive "P")
  1073.   (forms--exit query nil))
  1074.  
  1075. ;;;
  1076. ;;; Navigating commands
  1077.  
  1078. (defun forms-next-record (arg)
  1079.   "Advance to the ARGth following record."
  1080.   (interactive "P")
  1081.   (forms-jump-record (+ forms--current-record (prefix-numeric-value arg)) t))
  1082.  
  1083. (defun forms-prev-record (arg)
  1084.   "Advance to the ARGth previous record."
  1085.   (interactive "P")
  1086.   (forms-jump-record (- forms--current-record (prefix-numeric-value arg)) t))
  1087.  
  1088. (defun forms-jump-record (arg &optional relative)
  1089.   "Jump to a random record."
  1090.   (interactive "NRecord number: ")
  1091.  
  1092.   ;; verify that the record number is within range
  1093.   (if (or (> arg forms--total-records)
  1094.       (<= arg 0))
  1095.     (progn
  1096.       (beep)
  1097.       ;; don't give the message if just paging
  1098.       (if (not relative)
  1099.       (message "Record number %d out of range 1..%d"
  1100.            arg forms--total-records))
  1101.       )
  1102.  
  1103.     ;; flush
  1104.     (forms--checkmod)
  1105.  
  1106.     ;; calculate displacement
  1107.     (let ((disp (- arg forms--current-record))
  1108.       (cur forms--current-record))
  1109.  
  1110.       ;; forms--show-record needs it now
  1111.       (setq forms--current-record arg)
  1112.  
  1113.       ;; get the record and show it
  1114.       (forms--show-record
  1115.        (save-excursion
  1116.      (set-buffer forms--file-buffer)
  1117.      (beginning-of-line)
  1118.  
  1119.      ;; move, and adjust the amount if needed (shouldn't happen)
  1120.      (if relative
  1121.          (if (zerop disp)
  1122.          nil
  1123.            (setq cur (+ cur disp (- (forward-line disp)))))
  1124.        (setq cur (+ cur disp (- (goto-line arg)))))
  1125.  
  1126.      (forms--get-record)))
  1127.  
  1128.       ;; this shouldn't happen
  1129.       (if (/= forms--current-record cur)
  1130.       (progn
  1131.         (setq forms--current-record cur)
  1132.         (beep)
  1133.         (message "Stuck at record %d." cur))))))
  1134.  
  1135. (defun forms-first-record ()
  1136.   "Jump to first record."
  1137.   (interactive)
  1138.   (forms-jump-record 1))
  1139.  
  1140. (defun forms-last-record ()
  1141.   "Jump to last record. As a side effect: re-calculates the number
  1142.  of records in the data file."
  1143.   (interactive)
  1144.   (let
  1145.       ((numrec 
  1146.     (save-excursion
  1147.       (set-buffer forms--file-buffer)
  1148.       (count-lines (point-min) (point-max)))))
  1149.     (if (= numrec forms--total-records)
  1150.     nil
  1151.       (beep)
  1152.       (setq forms--total-records numrec)
  1153.       (message "Number of records reset to %d." forms--total-records)))
  1154.   (forms-jump-record forms--total-records))
  1155.  
  1156. ;;;
  1157. ;;; Other commands
  1158. (defun forms-view-mode ()
  1159.   "Visit buffer read-only."
  1160.   (interactive)
  1161.   (if forms-read-only
  1162.       nil
  1163.     (forms--checkmod)            ; sync
  1164.     (setq forms-read-only t)
  1165.     (forms-mode)))
  1166.  
  1167. (defun forms-edit-mode ()
  1168.   "Make form suitable for editing, if possible."
  1169.   (interactive)
  1170.   (let ((ro forms-read-only))
  1171.     (if (save-excursion
  1172.       (set-buffer forms--file-buffer)
  1173.       buffer-read-only)
  1174.     (progn
  1175.       (setq forms-read-only t)
  1176.       (message "No write access to \"%s\"" forms-file)
  1177.       (beep))
  1178.       (setq forms-read-only nil))
  1179.     (if (equal ro forms-read-only)
  1180.     nil
  1181.       (forms-mode))))
  1182.  
  1183. ;; Sample:
  1184. ;; (defun my-new-record-filter (the-fields)
  1185. ;;   ;; numbers are relative to 1
  1186. ;;   (aset the-fields 4 (current-time-string))
  1187. ;;   (aset the-fields 6 (user-login-name))
  1188. ;;   the-list)
  1189. ;; (setq forms-new-record-filter 'my-new-record-filter)
  1190.  
  1191. (defun forms-insert-record (arg)
  1192.   "Create a new record before the current one. With ARG: store the
  1193.  record after the current one.
  1194.  If a function forms-new-record-filter is defined, or forms-new-record-filter
  1195.  contains the name of a function, it is called to
  1196.  fill (some of) the fields with default values."
  1197.  ; The above doc is not true, but for documentary purposes only
  1198.  
  1199.   (interactive "P")
  1200.  
  1201.   (let ((ln (if arg (1+ forms--current-record) forms--current-record))
  1202.         the-list the-record)
  1203.  
  1204.     (forms--checkmod)
  1205.     (if forms--new-record-filter
  1206.     ;; As a service to the user, we add a zeroth element so she
  1207.     ;; can use the same indices as in the forms definition.
  1208.     (let ((the-fields (make-vector (1+ forms-number-of-fields) "")))
  1209.       (setq the-fields (funcall forms--new-record-filter the-fields))
  1210.       (setq the-list (cdr (append the-fields nil))))
  1211.       (setq the-list (make-list forms-number-of-fields "")))
  1212.  
  1213.     (setq the-record
  1214.       (mapconcat
  1215.       'identity
  1216.       the-list
  1217.       forms-field-sep))
  1218.  
  1219.     (save-excursion
  1220.       (set-buffer forms--file-buffer)
  1221.       (goto-line ln)
  1222.       (open-line 1)
  1223.       (insert the-record)
  1224.       (beginning-of-line))
  1225.     
  1226.     (setq forms--current-record ln))
  1227.  
  1228.   (setq forms--total-records (1+ forms--total-records))
  1229.   (forms-jump-record forms--current-record))
  1230.  
  1231. (defun forms-delete-record (arg)
  1232.   "Deletes a record. With ARG: don't ask."
  1233.   (interactive "P")
  1234.   (forms--checkmod)
  1235.   (if (or arg
  1236.       (y-or-n-p "Really delete this record? "))
  1237.       (let ((ln forms--current-record))
  1238.     (save-excursion
  1239.       (set-buffer forms--file-buffer)
  1240.       (goto-line ln)
  1241.       (kill-line 1))
  1242.     (setq forms--total-records (1- forms--total-records))
  1243.     (if (> forms--current-record forms--total-records)
  1244.         (setq forms--current-record forms--total-records))
  1245.     (forms-jump-record forms--current-record)))
  1246.   (message ""))
  1247.  
  1248. (defun forms-search (regexp)
  1249.   "Search REGEXP in file buffer."
  1250.   (interactive 
  1251.    (list (read-string (concat "Search for" 
  1252.                   (if forms--search-regexp
  1253.                    (concat " ("
  1254.                        forms--search-regexp
  1255.                        ")"))
  1256.                   ": "))))
  1257.   (if (equal "" regexp)
  1258.       (setq regexp forms--search-regexp))
  1259.   (forms--checkmod)
  1260.  
  1261.   (let (the-line the-record here
  1262.          (fld-sep forms-field-sep))
  1263.     (if (save-excursion
  1264.       (set-buffer forms--file-buffer)
  1265.       (setq here (point))
  1266.       (end-of-line)
  1267.       (if (null (re-search-forward regexp nil t))
  1268.           (progn
  1269.         (goto-char here)
  1270.         (message (concat "\"" regexp "\" not found."))
  1271.         nil)
  1272.         (setq the-record (forms--get-record))
  1273.         (setq the-line (1+ (count-lines (point-min) (point))))))
  1274.     (progn
  1275.       (setq forms--current-record the-line)
  1276.       (forms--show-record the-record)
  1277.       (re-search-forward regexp nil t))))
  1278.   (setq forms--search-regexp regexp))
  1279.  
  1280. (defun forms-revert-buffer (&optional arg noconfirm)
  1281.   "Reverts current form to un-modified."
  1282.   (interactive "P")
  1283.   (if (or noconfirm
  1284.       (yes-or-no-p "Revert form to unmodified? "))
  1285.       (progn
  1286.     (set-buffer-modified-p nil)
  1287.     (forms-jump-record forms--current-record))))
  1288.  
  1289. (defun forms-next-field (arg)
  1290.   "Jump to ARG-th next field."
  1291.   (interactive "p")
  1292.  
  1293.   (let ((i 0)
  1294.     (here (point))
  1295.     there
  1296.     (cnt 0))
  1297.  
  1298.     (if (zerop arg)
  1299.     (setq cnt 1)
  1300.       (setq cnt (+ cnt arg)))
  1301.  
  1302.     (if (catch 'done
  1303.       (while (< i forms--number-of-markers)
  1304.         (if (or (null (setq there (aref forms--markers i)))
  1305.             (<= there here))
  1306.         nil
  1307.           (if (<= (setq cnt (1- cnt)) 0)
  1308.           (progn
  1309.             (goto-char there)
  1310.             (throw 'done t))))
  1311.         (setq i (1+ i))))
  1312.     nil
  1313.       (goto-char (aref forms--markers 0)))))
  1314.  
  1315. ;;;
  1316. ;;; Special service
  1317. ;;;
  1318. (defun forms-enumerate (the-fields)
  1319.   "Take a quoted list of symbols, and set their values to the numbers
  1320. 1, 2 and so on. Returns the higest number.
  1321.  
  1322. Usage: (setq forms-number-of-fields
  1323.              (forms-enumerate
  1324.               '(field1 field2 field2 ...)))"
  1325.  
  1326.   (let ((the-index 0))
  1327.     (while the-fields
  1328.       (setq the-index (1+ the-index))
  1329.       (let ((el (car-safe the-fields)))
  1330.     (setq the-fields (cdr-safe the-fields))
  1331.     (set el the-index)))
  1332.     the-index))
  1333.  
  1334. ;;;
  1335. ;;; Debugging
  1336. ;;;
  1337. (defvar forms--debug nil
  1338.   "*Enables forms-mode debugging if not nil.")
  1339.  
  1340. (defun forms--debug (&rest args)
  1341.   "Internal - debugging routine"
  1342.   (if forms--debug
  1343.       (let ((ret nil))
  1344.     (while args
  1345.       (let ((el (car-safe args)))
  1346.         (setq args (cdr-safe args))
  1347.         (if (stringp el)
  1348.         (setq ret (concat ret el))
  1349.           (setq ret (concat ret (prin1-to-string el) " = "))
  1350.           (if (boundp el)
  1351.           (let ((vel (eval el)))
  1352.             (setq ret (concat ret (prin1-to-string vel) "\n")))
  1353.         (setq ret (concat ret "<unbound>" "\n")))
  1354.           (if (fboundp el)
  1355.           (setq ret (concat ret (prin1-to-string (symbol-function el)) 
  1356.                     "\n"))))))
  1357.     (save-excursion
  1358.       (set-buffer (get-buffer-create "*forms-mode debug*"))
  1359.       (goto-char (point-max))
  1360.       (insert ret)))))
  1361.  
  1362. ;;; Local Variables:
  1363. ;;; eval: (headers)
  1364. ;;; eval: (setq comment-start ";;; ")
  1365. ;;; End:
  1366.