home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / modes / multi-forms-mode / forms.el < prev    next >
Encoding:
Text File  |  1992-06-25  |  122.9 KB  |  3,351 lines

  1. ;;; multi-forms.el - Forms Mode with multiple records/page - A GNU Emacs Major Mode
  2. ;;; SCCS Status     : multi forms-mode    1.4.0
  3. ;;; Author          : Frank Ritter (based on Johan Vromans forms-mode)
  4. ;;; Created On      : 1989
  5. ;;; Last Modified By: Frank Ritter
  6. ;;; Last Modified On: Thu Jun 25 15:45:54 1992
  7. ;;; Update Count    : 571
  8. ;;; Status          : OK
  9.  
  10. ;; LCD Archive Entry:
  11. ;; multiple forms-mode|Frank Ritter|ritter@cs.cmu.edu
  12. ;; |Mode to edit file of records, displaying multiple records per buffer
  13. ;; |92-6-25|1.4.0|~/?/multi-forms-mode.tar.Z
  14.  
  15.  
  16. ;;; Table of contents
  17. ;;;    i.    Disclaimer
  18. ;;;    ii.    Overview of how multi forms-mode works
  19. ;;;    iii.    HISTORY
  20. ;;;     iv.    Global variables and constants
  21. ;;;     v.    Mandatory variables - must be set in/by the control file
  22. ;;;     vi.    Internal variables
  23. ;;;    vii.    Requires and loads
  24. ;;;    viii.    Known bugs
  25. ;;;    ix.    Installation notes
  26. ;;;
  27. ;;;     I.    forms-mode and startup code
  28. ;;;     II.    other mode helpers
  29. ;;;    III.    forms--make-record-filters
  30. ;;;    IV.    Set up and check local variables
  31. ;;;     V.    Set up the format (printing out) stuff
  32. ;;;     VI.    Set up the parsing (rereading records) routines
  33. ;;;    VII.    Set up the keymaps
  34. ;;;     VIII.    Changed movement functions
  35. ;;;    IX.    Changed saving functions
  36. ;;;    X.    Translation functions between Excel and Forms
  37. ;;;    XI.    forms-self-insert & character insertion setup functions
  38. ;;;    XII.    Report functions
  39. ;;;    XIII.    Changes to "normal" commands
  40. ;;;    XIV.    Changes to picture-mode commands
  41. ;;;    XV.    Utility functions
  42. ;;;    XVI.    Debugging functions
  43. ;;;
  44. ;;; Original forms-mode version by Johan Vromans (jv@mh.nl).
  45. ;;; Revision and extensions by Frank Ritter (ritter@cs.cmu.edu).
  46. ;;; Copyright 1992 Frank Ritter (extensions past Johan's 1991 version).
  47. ;;; Multi-forms mode code is not necc. compatible with Johan's, do not load
  48. ;;; them into the same emacs.  Some forms-mode data files should be readable,
  49. ;;; however, under multi-forms. 
  50. ;;;
  51.  
  52.  
  53. ;;;
  54. ;;;    i.    Disclaimer
  55. ;;;
  56. ;;; This file is part of GNU Emacs.
  57. ;;; GNU Emacs is distributed in the hope that it will be useful,
  58. ;;; but WITHOUT ANY WARRANTY.  No author or distributor
  59. ;;; accepts responsibility to anyone for the consequences of using it
  60. ;;; or for whether it serves any particular purpose or works at all,
  61. ;;; unless he says so in writing.  Refer to the GNU Emacs General Public
  62. ;;; License for full details.
  63.  
  64. ;;; Everyone is granted permission to copy, modify and redistribute
  65. ;;; GNU Emacs, but only under the conditions described in the
  66. ;;; GNU Emacs General Public License.   A copy of this license is
  67. ;;; supposed to have been given to you along with GNU Emacs so you
  68. ;;; can know your rights and responsibilities. 
  69. ;;; If you don't have this copy, write to the Free Software
  70. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  71. ;;;
  72.  
  73.  
  74. ;;;
  75. ;;;    ii.    Overview of how multi forms-mode works
  76. ;;; 
  77. ;;;
  78. ;;; === Naming conventions
  79. ;;;
  80. ;;; The names of all variables and functions start with 'form-'.
  81. ;;; Names that start with 'form--' are intended for internal mode use, and
  82. ;;; should *NOT* be touched by users.
  83. ;;;
  84. ;;; Most variables are buffer-local, to enable visiting several
  85. ;;; files simultaneously.
  86. ;;; Variable 'forms--mode-setup' is local to *ALL* buffers, for it 
  87. ;;; controls if forms-mode has been enabled in a buffer.
  88. ;;;
  89. ;;; === How it works ===
  90. ;;;
  91. ;;; Multi forms-mode is like Johan Vromans forms-mode, in that 
  92. ;;; it helps you create a record based way to view a file contents.
  93. ;;; The original Forms-mode lets you create a small file that
  94. ;;; contains information on how to display each record,
  95. ;;; including labels, and field sizes, and a pointer to the file
  96. ;;; that contained the actual records.  One could then step
  97. ;;; through the records, with one record displayed at a time.
  98. ;;; Multi-forms take this concept two steps further.  One step
  99. ;;; was to display more than one record per buffer.  Multi
  100. ;;; forms-mode displays all of them at once.  How many you see
  101. ;;; is limited by the window and font size.  The other step was
  102. ;;; to keep the formating commands and the data in the same
  103. ;;; file.  In each case, The records are separated by a newline,
  104. ;;; the fields are separated by a user-defined field separater
  105. ;;; (default: TAB).  When shown, the records are either
  106. ;;; transferred one at a time, or en mass to an emacs buffer and
  107. ;;; are presented using a user-defined form.
  108. ;;;
  109. ;;; Forms mode is a composite mode. It involves two files (and two
  110. ;;; buffers), or a single file (and two buffers).  In the two file version,
  111. ;;; the first file, called the control file, defines the name of the
  112. ;;; data file and the forms format.  This file buffer will be used to
  113. ;;; present the forms.  The second file holds the actual data.  The buffer
  114. ;;; of this file will be buried, for it is never accessed directly.
  115. ;;;
  116. ;;; In the single file version, the control data and then the data are in 
  117. ;;; the same file, separated by the forms-header-stop string 
  118. ;;; (default: ";**** end of forms header ****")
  119. ;;; A leading comment header is also allowed from the control data to the 
  120. ;;; forms-comment-header-stop separation string 
  121. ;;; (default: ";**** end of forms comment header ****")
  122. ;;; Forms mode is invoked for the two-file version by 
  123. ;;; using "forms-find-file control-file".  Alternativily 
  124. ;;; forms-find-file-other-window can be used.
  125. ;;; If forms-query-mode is t (the default), the user will be queried before 
  126. ;;; a file is put into forms-mode.  This allows you to edit a file in 
  127. ;;; single-file forms format.
  128. ;;;
  129. ;;; You may also visit the control file, and switch to forms mode by hand
  130. ;;; with M-x forms-mode.
  131. ;;;
  132. ;;; Automatic mode switching is supported, so you may use "find-file" with 
  133. ;;; single-file formated files if you specify "-*- forms -*-" in the first 
  134. ;;; line of the control/data file.  
  135. ;;;
  136. ;;; This file (forms.el) must also be loaded with a (load "forms") or 
  137. ;;; an (autoload 'forms-mode "forms-mode").
  138. ;;; 
  139. ;;; The control file (or single file) is visited, and its commands are 
  140. ;;; evaluated.  They should set at least the following variables:
  141. ;;;
  142. ;;;    forms-file            [string] the name of the data file.
  143. ;;;                                 (omit in single file version.)
  144. ;;;
  145. ;;;    forms-format-list           [list]   formatting instructions.
  146. ;;;
  147. ;;;    forms-number-of-fields        [integer]
  148. ;;;            The number of fields in each record.
  149. ;;;                                (now not explicitly needed)
  150. ;;;
  151. ;;; The forms-format-list should be a list, each element containing 
  152. ;;; information on how to display an item, which may or may not be a field
  153. ;;; from the data.  There are 8 fields, all required, in each element.
  154. ;;; All formats are relative, size fields only count if field is a number 
  155. ;;; (i.e, refers to the data)
  156. ;;;
  157. ;;;  - name (symbol) used in menus (or to be used in menus), to name this
  158. ;;;    element
  159. ;;;  - visiblep (t/nil), will this element be displayed? (currently ignored)
  160. ;;;
  161. ;;;  - label (string/nil) to print in front of field element
  162. ;;;  - field element.  It can be:
  163. ;;;    - a string, e.g. "hello" (which is inserted \"as is\"),
  164. ;;;    - an integer, denoting a field number. The contents of the field
  165. ;;;    are inserted at this point.  The first field is number one.
  166. ;;;    - an s-expression, e.g. (insert "text"). This s-expression is
  167. ;;;    dynamically evaluated and should return a string. It should *NOT*
  168. ;;;    have side-effects on the forms being constructed.
  169. ;;;    The fields of the current record are available to the function in
  170. ;;;    the variable forms-fields, they should *NOT* be modified.
  171. ;;;    - a lisp symbol, that must evaluate to one of the above.
  172. ;;;
  173. ;;;  - default value (s-expression).  Not currently used.
  174. ;;;
  175. ;;;  - newline-p (t/nil/N) number of newlines to put in front of label. t=1.
  176. ;;;
  177. ;;;  - field-size (number of chars or t).  Amount of maximum field element to
  178. ;;;    display.  If t, display to end of line.  Error (perhaps unsignaled)
  179. ;;;    to have to elements on same line with field-size t.
  180. ;;;  
  181. ;;;  - line-size (t/nil/N) number of lines field element is allowed to be.
  182. ;;;    t=1, nil=0.
  183. ;;;  Here's an example element: (timestamp t "TIME: " 1  "1"  nil  8  nil)
  184.  
  185. ;;; Optional variables that may be set in the control file:
  186. ;;;
  187. ;;;    forms-field-sep                [string, default TAB]
  188. ;;;            The field separator used to separate the
  189. ;;;            fields in the data file. It may be a string.
  190. ;;;
  191. ;;;    forms-read-only                [bool, default nil]
  192. ;;;            't' means that the data file is visited read-only.
  193. ;;;            If no write access to the data file is
  194. ;;;            possible, read-only mode is enforced. 
  195. ;;;
  196. ;;;    forms-multi-line            [string, default "^K"]
  197. ;;;            If non-null the records of the data file may
  198. ;;;            contain fields that span multiple lines in
  199. ;;;            the form.
  200. ;;;            This variable denoted the separator character
  201. ;;;            to be used for this purpose. Upon display, all
  202. ;;;            occurrencies of this character are translated
  203. ;;;            to newlines. Upon storage they are translated
  204. ;;;            back to the separator.
  205. ;;;
  206. ;;;    forms-forms-scroll            [bool, default t]
  207. ;;;            If non-nil: redefine scroll-up/down to perform
  208. ;;;            forms-next/prev-field if in forms mode.
  209. ;;;
  210. ;;;    forms-forms-jump            [bool, default t]
  211. ;;;            If non-nil: redefine beginning/end-of-buffer
  212. ;;;            to performs forms-first/last-field if in
  213. ;;;            forms mode.
  214. ;;;
  215. ;;;    forms-new-record-filter            [symbol, no default]
  216. ;;;            If defined: this should be the name of a 
  217. ;;;            function that is called when a new
  218. ;;;            record is created. It can be used to fill in
  219. ;;;            the new record with default fields, for example.
  220. ;;;            Instead of the name of the function, it may
  221. ;;;            be the function itself.
  222. ;;;
  223. ;;;    forms-modified-record-filter        [symbol, no default]
  224. ;;;            If defined: this should be the name of a 
  225. ;;;            function that is called when a record has
  226. ;;;            been modified. It is called after the fields
  227. ;;;            are parsed. It can be used to register
  228. ;;;            modification dates, for example.
  229. ;;;            Instead of the name of the function, it may
  230. ;;;            be the function itself.
  231. ;;;
  232. ;;; 
  233. ;;; After evaluating the control file, its buffer is cleared and used for
  234. ;;; further processing.  The data file (as designated by "forms-file") is
  235. ;;; visited in a buffer (forms--file-buffer) that will not normally be
  236. ;;; shown.  Great malfunctioning may be expected if the data file/buffer
  237. ;;; is modified outside of this package while it's open under forms!
  238. ;;; 
  239. ;;;
  240. ;;; A record from the data file is transferred from the data file,
  241. ;;; split into fields (into forms--the-record-list), and displayed using
  242. ;;; the specs in forms-format-list.
  243. ;;; A format routine 'forms--format' is built upon startup to format 
  244. ;;; the records.
  245. ;;;
  246. ;;; When a form is changed the record is updated as soon as the form
  247. ;;; is left.  The contents of the form are parsed using forms-format-list,
  248. ;;; and the fields that are deduced from the form are modified
  249. ;;; (fields not shown on the forms retain their origional values).
  250. ;;; The newly formed record and replaces the contents of the
  251. ;;; old record in forms--file-buffer.
  252. ;;; A parse routine 'forms--parser' is built upon startup to parse
  253. ;;; the records.
  254. ;;;
  255. ;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save
  256. ;;; (which doesn't).  However, if forms-exit-no-save is executed and the file
  257. ;;; buffer has been modified, emacs will ask the appropriate and regular 
  258. ;;; questions about saving the buffer.
  259. ;;;
  260. ;;; Other functions are:
  261. ;;;
  262. ;;;    paging (forward, backward) by record
  263. ;;;    jumping (first, last, any number)
  264. ;;;    searching
  265. ;;;    creating and deleting records
  266. ;;;    reverting a single form (NOT the entire file buffer)
  267. ;;;    switching edit <-> view mode v.v.
  268. ;;;    jumping from field to field
  269. ;;;     a menu of commands
  270. ;;;     print out a listing of the formated elements
  271. ;;;     edit associated comments and the command header on the fly
  272. ;;;     tally field counts (i.e., count the amounts of each type of field type 
  273. ;;;       for a given or queried field)
  274. ;;;
  275. ;;; As an documented side-effect: jumping to the last record in the
  276. ;;; file (using forms-last-record) will adjust forms--total-records if
  277. ;;; needed.
  278. ;;;
  279. ;;; Commands and keymaps:
  280. ;;;
  281. ;;; A local keymap 'forms-mode-map' is used in the forms buffer.
  282. ;;; As conventional, this map can be accessed with C-c prefix.
  283. ;;; In read-only mode, the C-c prefix is be omitted.
  284. ;;;
  285. ;;; Default bindings:
  286. ;;;
  287. ;;;    \C-c    forms-mode-map
  288. ;;;    TAB    forms-next-field
  289. ;;;    SPC     forms-next-record
  290. ;;;    <    forms-first-record
  291. ;;;    >    forms-last-record
  292. ;;;    ?    describe-mode
  293. ;;;    b    forms-prev-record
  294. ;;;    d    forms-delete-record
  295. ;;;    e    forms-edit-mode
  296. ;;;    i    forms-insert-record
  297. ;;;    j    forms-jump-record
  298. ;;;     m       forms-run-menu
  299. ;;;    n    forms-next-record
  300. ;;;    p    forms-prev-record
  301. ;;;    q    forms-exit
  302. ;;;    s    forms-search
  303. ;;;    v    forms-view-mode
  304. ;;;    x    forms-exit-no-save
  305. ;;;    DEL    forms-prev-record
  306. ;;;
  307. ;;; Buffer-local variables forms-forms-scroll and forms-forms-jump
  308. ;;; may be used to control how far forms-scroll-down/up scroll, to the bottom
  309. ;;; of a record or to the bottom of the whole list.
  310. ;;;
  311. ;;; A revert-file-hook is defined to revert a forms to original file contents.
  312. ;;;
  313. ;;; For convenience, TAB (and M-TAB) is bound to forms-next-field, so you
  314. ;;; don't need the C-c prefix for this command.
  315. ;;; See the example test.forms for an example of the single file format, and
  316. ;;; demo1.forms for the double file format.
  317.  
  318. ;;; Example files should be included with this source code
  319. ;;; file.  if it is not, one may be obtained by emailing the
  320. ;;; author, ritter@cs.cmu.edu
  321.  
  322.  
  323. ;;;
  324. ;;;    iii.    HISTORY
  325. ;;;
  326.  
  327. ;;; 25-Jun-92 - finally exported
  328. ;;; Feb 92?  Johan releases a later forms-mode. 
  329. ;;;     (multi-forms is not based on this version)
  330. ;;; Jan 92 -FER packaged for export; single file version
  331. ;;;   with protected keyword labels
  332. ;;; Dec-Aug 91 -FER  added support for single file version
  333. ;;;                  cleaned up keymaps
  334. ;;; 19-Jul-91 - FER  changed to (provide 'forms)
  335. ;;; 1-Jul-1991        Johan Vromans    
  336. ;;;    Normalized error messages.
  337. ;;; 30-Jun-1991        Johan Vromans    
  338. ;;;    Add support for forms-modified-record-filter.
  339. ;;;    Allow the filter functions to be the name of a function.
  340. ;;;    Fix: parse--format used forms--dynamic-text destructively.
  341. ;;;    Internally optimized the forms-format-list.
  342. ;;;    Added support for debugging.
  343. ;;;    Stripped duplicate documentation.
  344. ;;; 29-Jun-1991        Johan Vromans    
  345. ;;;    Add support for functions and lisp symbols in forms-format-list.
  346. ;;;    Add function forms-enumerate.
  347.  
  348. (provide 'forms)
  349.  
  350. ;;; you must set this up upon installation:
  351. (defvar forms-load-path
  352.   "/afs/cs/project/soar/member/ritter/spa/spa-mode/new/forms/new"
  353.   "*Directory where forms.el (and its associated files) live.")
  354.  
  355.  
  356. ;;;
  357. ;;;     iv.    Global variables and constants
  358. ;;;
  359. ;;; As should be expected, user visible and setable variables have a leading
  360. ;;; * on their doc string.
  361.  
  362. (defconst forms-version "1.3.0"
  363.   "*Version of forms-mode implementation.")
  364.  
  365. (defvar forms-forms-scrolls t
  366.   "*If non-null: redefine scroll-up/down to be used with forms-mode.")
  367.  
  368. (defvar forms-forms-jumps t
  369.   "*If non-null: redefine beginning/end-of-buffer commands to be used with 
  370. forms-mode.")
  371.  
  372. (defvar forms-mode-hooks nil
  373.   "*Hook functions to be run upon entering forms mode.")
  374.  
  375. (defvar forms-query-on-entry-p t
  376.   "*Ask for confirmation each time forms-mode is called.  Particularly 
  377. important when setting up single file versions.")
  378.  
  379. (defvar forms-go-to-beginning-on-jump nil
  380.   "*If non-null (default is nil), when jumping to a new record, move
  381. point to be at beginning of record.")
  382.  
  383. (defvar forms-mode-load-hook nil
  384.   "*Hook variable run after forms-mode is loaded.")
  385.  
  386. (defvar forms-bad-count-fields nil
  387.   "*If forms-count-fields, or forms-show-record fails, the arguments are 
  388. placed here to help the user debug.")
  389. ;; not made local, so always inspectable
  390.  
  391.  
  392. ;;;
  393. ;;;     v.    Mandatory variables - must be set in/by the control file
  394. ;;;
  395.  
  396. (defvar forms-auto-report-hook nil
  397.   "*Hook to put commands on to generate auto-report.")
  398.  
  399. (defvar forms-file nil
  400.   "*Name of the file holding the data.")
  401.  
  402. (defvar forms-format-list nil
  403.   "*List of formatting specifications.")
  404.  
  405.  
  406. ;;;
  407. ;;;     vi.    Optional variables with default values
  408. ;;;
  409.  
  410. (defvar forms-read-only nil
  411.   "Read-only mode (defaults to the write access on the data file).")
  412.  
  413. (defvar forms-multi-line "\C-k"
  414.   "Character to separate multi-line fields (default ^K).")
  415.  
  416. (defvar forms-forms-scroll t
  417.   "Redefine scroll-up/down to perform forms-next/prev-record when in
  418.  forms mode.")
  419.  
  420. (defvar forms-forms-jump t
  421.   "Redefine beginning/end-of-buffer to perform forms-first/last-record
  422.  when in forms mode.")
  423.  
  424. (defvar forms-header-stop ";**** end of forms header ****
  425. "
  426.   "*Indicates the end of the header; must begin with a semi-colon, can't be 
  427. set by command, must be done in user's .emacs file or in the source code.")
  428.  
  429. (defvar forms-comment-header-stop ";**** end of forms comment header ****"
  430.   "*Indicates the end of the comment header; should begin with a semi-colon,
  431. can't be set by command, must be done in user's .emacs file or in the 
  432. source code.")
  433.  
  434. (defvar forms-field-sep "\t"
  435.   "*Field separator character (default TAB).")
  436.  
  437. (defvar forms-record-separator "----------------\n")
  438.  
  439. (defvar forms-mode-map nil
  440.    "Global keymap for forms buffer in read/write mode.")
  441.  
  442. (defvar forms-mode-commands-map nil
  443.    "Global keymap with commands for forms buffer in read only mode.")
  444.  
  445. (defvar forms-mode-hidden-buffer-map nil
  446.    "Keymap for the hidden guy.")
  447.  
  448. (defvar forms--field-sep-char "\t"
  449.   "Field separator turned into a real char (character).")
  450.  
  451.  
  452. ;;;
  453. ;;;     vi.    Internal variables
  454. ;;;
  455. ;;;  Internal variables and functions start with "--".
  456.  
  457. (defvar forms--blank-bag '(?\ ?\t ?:))
  458.  
  459. (defvar forms-number-of-fields nil
  460.   "Number of fields per record.")
  461.  
  462. (defvar forms-quick-help-writable nil "Initial help for writable files.")
  463.  
  464. (defvar forms-quick-help-read-only nil "Initial help for read only files.")
  465.  
  466. (defvar forms--commands-header-final-pos nil
  467.   "Marker indicating end of header position.")
  468.  
  469. (defvar forms--comments-header-final-pos nil
  470.   "Marker indicating end of comment header position.")
  471.  
  472. (defvar forms--comments-header-initial-pos nil
  473.   "Marker indicating end of comment header position.")
  474.  
  475. (defvar forms--comments-header ""
  476.   "Where the comment header is kept upon occasion.")
  477.  
  478. (defvar forms--commands-header ""
  479.   "Where the command header is kept upon occasion.")
  480.  
  481. (defvar forms--all-in-one nil
  482.   "Set to t when the file has its header and data in the same file.")
  483.  
  484. (defvar forms--display-buffer nil
  485.   "Buffer that file is displayed in.")
  486.  
  487. (defvar forms--file-buffer nil
  488.   "Buffer that holds the file data.")
  489.  
  490. (defvar forms--comments-buffer nil
  491.   "Buffer that holds the file comments data.")
  492.  
  493. (defvar forms--commands-buffer nil
  494.   "Buffer that holds the file header commands data.")
  495.  
  496. (defvar forms--total-records 0
  497.   "Total number of records in the data file.")
  498.  
  499. (defvar forms--current-record 0
  500.   "Number of the record currently on the screen.")
  501.  
  502. (defvar forms--markers nil
  503.   "Field markers in the screen.")
  504.  
  505. ;(defvar forms--number-of-markers 0
  506. ;  "Number of fields on screen.")
  507.  
  508. (defvar forms--the-record-list nil 
  509.    "List of strings of the current record, as parsed from the file.")
  510.  
  511. (defvar forms--search-regexp nil
  512.   "Last regexp used by forms-search.")
  513.  
  514. (defvar forms--format nil
  515.   "Formatting routine.")
  516.  
  517. (defvar forms--parser nil
  518.   "Forms parser routine.")
  519.  
  520. (defvar forms--mode-setup nil
  521.   "Internal - keeps track of forms-mode being set-up.")
  522. (make-variable-buffer-local 'forms--mode-setup)
  523.  
  524. (defvar forms--new-record-filter nil
  525.   "Internal - set if a forms-new-record-filter has been defined.")
  526.  
  527. (defvar forms--modified-record-filter nil
  528.   "Internal - set if a modified record filter has been defined.")
  529.  
  530. (defvar forms--dynamic-text nil
  531.   "Internal - holds the dynamic text inserted between fields, ie, text
  532. that might change or that gets computed each time.  Set in
  533. make-format-elt, used in make-parse.")
  534.  
  535. (defvar forms-fields nil
  536.   "List with fields of the current forms. First field has number 1.")
  537.  
  538. (defvar forms--column 1
  539.   "Current column when set up by forms--find-row-and-column.")
  540. (defvar forms--row 1
  541.   "Current row when set up by forms--find-row-and-column.")
  542.  
  543. (defvar forms--char-insertable-function nil)
  544.  
  545. (defvar forms--advanced-column-keywords
  546.       '("Verbalization: " "Soar-action: " "Comments: .  "))
  547.  
  548. (defvar forms--maxlength-advanced-column-keywords
  549.       (apply 'max (mapcar 'length forms--advanced-column-keywords)))
  550.  
  551. (defvar forms--first-advanced-row 0 
  552.   "The first row in the current form with variable line length fields.")
  553. (defvar forms--first-advanced-column 0
  554.   "The first column of the first row with variable line length fields.")
  555.  
  556. ;;; modification to global variables
  557.  
  558.  
  559. ;;(setq old-sentence-end  "[.?!][]\"')}]*\\($\\|    \\|  \\)[     
  560. ;;]*")
  561. (setq old-sentence-end sentence-end)
  562. (setq sentence-end
  563.       (format "\\(%s\\)\\|%s" old-sentence-end forms-record-separator))
  564.  
  565.  
  566. ;;;
  567. ;;;    vii.    Requires and loads
  568. ;;;
  569.  
  570. (setq load-path
  571.       (cons (expand-file-name forms-load-path)
  572.       (cons (expand-file-name (concat forms-load-path "/utilities"))
  573.             load-path)))
  574.  
  575. (require 'simple-menu)
  576. (require 'forms-simple-menus)
  577. (require 'my-picture)
  578. (require 'cl)
  579. (require 'soar-misc)
  580. (require 'insert-date)
  581. (require 'byte-compile "bytecomp")
  582. (require 'ritter-math)
  583.  
  584.  
  585. ;;;
  586. ;;;    viii.    Known bugs
  587. ;;;
  588. ;;; Fixes can be posted back to Frank.  No killer bugs known (although double-
  589. ;;; file version may no longer work).
  590. ;;;
  591. ;;; Bugs (N:M)  N is first estimate of time to do in min, 
  592. ;;;     
  593. ;;;     1 - must do   2 - should do   
  594. ;;;     3 - do if in area, would be a feature  4 - imaginable feature
  595.  
  596. ;;; 1 (10) some way to move current record to top of window
  597. ;;; 2 (25) dabbrev-expand needs to be nice to fixed width fields
  598. ;;; 2 (50) support tallys with tests
  599. ;;; 2 (40) procedures that count, add to comments header somehow
  600. ;;; 2 (15) query if want tally report in generated listing report
  601. ;;; 2 (60) add automaticly rechecked tallies to the report
  602. ;;; 2 (20) add the comments to the report, with a query first
  603. ;;; 2 (30) should use default values
  604. ;;; 2 (20) forms-open-line should allow an open-line at the very end/very
  605. ;;;   beginning of a record 
  606. ;;; 2 (100) an autosave function should be created.  I have notes on this, and 
  607. ;;;         now do just this thing for another system like this.... FER
  608. ;;; 3 (30) should check if forms-format-list has duplicate labels in 
  609. ;;;   forms--process-format-list
  610. ;;; 3 (30) set up tally report to give %'s
  611. ;;; 3 (15) ^k in open advanced fields (ie comments) inserts extra spaces
  612. ;;; 3 opening read-only files is screwed up.
  613. ;;; 3 (10) just-one-space must also be fixed to work in fixed with areas
  614. ;;; 3 if field label is just spaces, put name in as option
  615. ;;; 3 think about how add/remove-fields works with parsing and formating
  616. ;;;   commands
  617. ;;; 
  618. ;;; 4 hot text for menu selections for given fields
  619. ;;; 4 hidden fields appear to get munged
  620. ;;; 4 No truncate of fields
  621. ;;; 4 write a function to clear a column
  622.  
  623.  
  624. ;;;
  625. ;;;    ix.    Installation notes
  626. ;;;
  627.  
  628. ;;; How I export it:
  629. ;;;         tar crvlf multi-forms-mode.tar forms.el my-picture.el \
  630. ;;;              forms-simple-menus.el test.forms \
  631. ;;;              README demo1.forms \
  632. ;;;              ../original/forms.ti \
  633. ;;;              utilities/ritter-math.el utilities/simple-menu.el \
  634. ;;;              utilities/soar-misc.el utilities/insert-date.el
  635. ;;;   compress multi-forms-mode.tar
  636. ;;;
  637. ;;; How you can unpack it:  uncompress forms-mode.tar.Z
  638. ;;;                  tar xf forms-mode.tar
  639. ;;;                  set forms-load-path directory so it can load
  640. ;;;                       (see below)
  641. ;;;
  642. ;;; You should bytecompile all the files.
  643. ;;;
  644. ;;; demo1.forms shows an example file that uses a separate data file.
  645. ;;; test.forms shows an example file that includes its own records.
  646. ;;;
  647. ;;; One of the best ways to get going is to examine these files, and
  648. ;;; modify them to suit your needs.
  649. ;;;
  650. ;;; This file (forms.el) must also be loaded with a (load "forms") or 
  651. ;;; an (autoload 'forms-mode "forms-mode").
  652.  
  653.  
  654. ;;;
  655. ;;;     I.    forms-mode
  656. ;;;
  657. ;;; This is not a typical or simple major mode.
  658. ;;; forms-mode takes an optional argument 'primary', which is used for 
  659. ;;; the initial set-up.  It forces the buffer into forms-mode, or resets 
  660. ;;; the mode is the buffer is already in forms-mode.  Normal use and 
  661. ;;; users will not see or need PRIMARY and should leave it nil.
  662. ;;;
  663. ;;; A global buffer-local variable 'forms--mode-setup' has the same effect
  664. ;;; but makes it possible to auto-invoke forms-mode using find-file.
  665. ;;;
  666. ;;; Note: although it seems logical to have (make-local-variable) executed
  667. ;;; where the variable is first needed, I (JV) deliberately placed all calls
  668. ;;; in the forms-mode function.
  669.  
  670. (defun forms-mode (&optional primary no-query)
  671.   "Major mode to visit files in a field-structured manner using a form.
  672. If PRIMARY is t, then entry is treated as first call and all initializations 
  673. are run.  If NO-QUERY is t, then don't ask the user.
  674.  Key map used when writeable:
  675.  \\{forms-mode-map}
  676.  Commands prefixed with C-c when writeable mode,
  677.      and available directly in read-only mode):
  678.  \\{forms-mode-commands-map}"
  679.   ;; we also reset the point, which is ok on entry, but not good on repeated
  680.   ;; calls.
  681.  
  682.   (interactive)                ; no - 'primary' is not prefix arg
  683.   (if (or (not forms-query-on-entry-p)
  684.           no-query
  685.           (y-or-n-p (format "Put %s into forms mode? " (buffer-name))))
  686.       (forms--mode-helper primary)))
  687.  
  688. (defun forms--mode-helper (&optional primary)
  689.  ;; Primary set-up: evaluate buffer and check if the mandatory
  690.  ;; variables have been set.
  691.  (message "Starting forms-mode up...")
  692.  (if (or primary (not forms--mode-setup))
  693.      (progn
  694.        (forms--make-local-variables)
  695.        (goto-char (point-min)) ; in case it was opened first
  696.        (if (not (= 1 (forms--set-commands-header-final-pos)))
  697.            (setq forms--all-in-one t))
  698.        ;; eval the buffer, should set variables
  699.        ;; this way it is backwardly compatible ;-fer
  700.        (setq forms--display-buffer (current-buffer))
  701.        (if forms--all-in-one
  702.            (forms--setup-all-in-one)
  703.            (progn (eval-current-buffer)
  704.                   ;; find the data file
  705.                   (setq forms--file-buffer (find-file-noselect forms-file)) ) )
  706.  
  707.     ;; prepare the display buffer for further processing
  708.           ;; and clean it
  709.         (setq buffer-read-only nil)
  710.         (erase-buffer)
  711.  
  712.         (forms--initialize-functions)
  713.     ;; prevent accidental overwrite of the control file and auto-save
  714.     (setq buffer-file-name nil)
  715.          ))
  716.   (message "Starting forms-mode up....")
  717.   (if forms-read-only
  718.       (use-local-map forms-mode-commands-map)
  719.       (use-local-map forms-mode-map))
  720.   (forms--set-minor-mode)
  721.  
  722.   ;; count the number of records, and set see if it may be modified
  723.   (setq forms--total-records
  724.         (save-excursion
  725.         (set-buffer forms--file-buffer)
  726.             (set-buffer-modified-p nil)
  727.         (bury-buffer (current-buffer))
  728.             (if buffer-read-only
  729.                 (setq forms-read-only t))
  730.         (count-lines (point-min) (point-max))))
  731.  
  732.   ;; set the major mode indicator
  733.   (setq major-mode 'forms-mode)
  734.   (setq mode-name "Forms")
  735.   (message "Starting forms-mode up.....")
  736.   ;; setup the first (or current) record to show, but only on entry
  737.   (if (not forms--mode-setup)
  738.       (progn
  739.          (forms--draw-all-records forms--display-buffer)
  740.          (goto-char (point-min))
  741.          (set-buffer-modified-p nil)
  742.          (forms--set-mode-line)
  743.          (auto-fill-mode -1))) ;make sure auto-fill is off
  744.  
  745.   ;; run user customising &  be helpful
  746.   (run-hooks 'forms-mode-hooks)
  747.   (forms-quick-help)
  748.   (setq forms--mode-setup t))
  749. ;; end forms--mode-helper
  750.  
  751. (defun forms--initialize-functions ()
  752.   ;; check if the mandatory variables make sense.
  753.   (forms--check-local-variables)
  754.   ;; validate and process forms-format-list
  755.   (forms--process-format-list)
  756.   ;; build the formatter and parser
  757.   (forms--make-char-insertable-function)
  758.   (forms--make-format)
  759.   (forms--make-parser)
  760.   (forms--make-forms-query-list)
  761.  
  762.   ;; check if record filters are defined
  763.   (forms--make-record-filters))
  764.  
  765. (defun forms--setup-all-in-one ()
  766.  (save-excursion
  767.    ;; set up the display buffer
  768.    (setq forms-read-only (if buffer-read-only t nil))
  769.    (setq forms--commands-header 
  770.          (buffer-substring 1 forms--commands-header-final-pos))
  771.    ;; set up the file-buffer
  772.    (setq forms--file-buffer-name (concat "*forms-" (buffer-name) "*"))
  773.    (setq forms--file-buffer (get-buffer-create forms--file-buffer-name))
  774.    (forms--call-commands)
  775.    (forms--make-file-buffer)
  776.    (erase-buffer)))
  777.  
  778. (defun forms--make-file-buffer ()
  779.   "Make the file-buffer (back buffer) for the current forms file."
  780.   (save-excursion
  781.   (let ((temp-forms-body (buffer-substring (point-min) (point-max)))
  782.         (original-file-name (buffer-file-name (current-buffer))) )
  783.     (setq forms-file original-file-name)
  784.     (set-buffer forms--file-buffer)
  785.     (make-local-variable 'forms--commands-header-final-pos)
  786.     (make-local-variable 'forms--comments-header-final-pos)
  787.     (make-local-variable 'forms--comments-header-initial-pos)
  788.  
  789.     (setq forms--commands-header-final-pos (make-marker))
  790.     (setq forms--comments-header-final-pos (make-marker))
  791.     (setq forms--comments-header-initial-pos (make-marker))
  792.     (use-local-map forms-mode-hidden-buffer-map)
  793.     (setq buffer-auto-save-file-name (concat original-file-name "~"))
  794.     (erase-buffer)
  795.     (insert-before-markers temp-forms-body)
  796.     (goto-char (point-min))
  797.     (forms--set-commands-header-final-pos)
  798.     (forms--call-commands)
  799.     (forms--parse-comments)
  800.     (narrow-to-region (+ 1 forms--comments-header-final-pos
  801.                          (length forms-comment-header-stop))
  802.                       (point-max))
  803.     (goto-char (point-min)))))
  804.  
  805. (defun forms--call-commands ()
  806.   "Calls the commands in buffer, and sets up the comment string."
  807.   ;; called on each of the main buffer and the back buffer
  808.   (narrow-to-region 1 forms--commands-header-final-pos) ; 1 is point-min
  809.   (eval-current-buffer)
  810.   (widen))
  811.  
  812. (defun forms--set-commands-header-final-pos ()
  813.  ;; sets the forms--commands-header-final-pos as a marker
  814.  (set-marker forms--commands-header-final-pos  ;this is bolp of command
  815.     (save-excursion (search-forward forms-header-stop nil t)
  816.                     (- (point) (length forms-header-stop)))))
  817.  
  818. (defun forms--parse-comments ()
  819.   (set-marker forms--comments-header-initial-pos
  820.         (+ forms--commands-header-final-pos (length forms-header-stop)))
  821.   (set-marker forms--comments-header-final-pos
  822.         (save-excursion (search-forward forms-comment-header-stop nil t)
  823.                         (beginning-of-line)
  824.                         (point) ))
  825.   (setq forms--comments-header
  826.         (buffer-substring forms--comments-header-initial-pos
  827.                           forms--comments-header-final-pos)))
  828.  
  829. (defun forms--make-forms-query-list ()
  830.   (setq forms--format-query-list nil)
  831.   (mapcar '(lambda (x)
  832.              (let ((field (format-item-field x))
  833.                    (new-label (string-trim forms--blank-bag
  834.                                            (format-item-label x))))
  835.                (if (numberp field)
  836.                    (push (cons new-label field)
  837.                          forms--format-query-list))))
  838.           forms-format-list))
  839.  
  840. (defun forms--auto-save-file-name ()
  841.   (concat default-directory (buffer-name)))
  842.  
  843.  
  844. ;;;
  845. ;;;     II.    other mode helpers
  846. ;;;
  847. ;;; One could just use the %% as an indicator.  Rarely do you really look at
  848. ;;; files you can't touch, and in those cases, you'll find out quickly enough.
  849.  
  850. (defun forms--set-minor-mode ()
  851.   (setq minor-mode-alist (if forms-read-only " View" nil)))
  852.  
  853. (defun forms-view-mode ()
  854.   "Visit buffer read-only."
  855.   (interactive)
  856.   (if forms-read-only
  857.       nil
  858.     (setq forms-read-only t)
  859.     (forms-mode nil t)
  860.     (forms-quick-help))  )
  861.  
  862. (defun forms-edit-mode ()
  863.   "Make form suitable for editing, if possible."
  864.   (interactive)
  865.   (let ((read-only forms-read-only) )
  866.     (if (save-excursion (set-buffer forms--file-buffer)
  867.                         buffer-read-only)
  868.     (progn (setq forms-read-only t)
  869.                (message "No write access to \"%s\"" forms-file)
  870.                (beep))
  871.       (setq forms-read-only nil))
  872.     (if (equal read-only forms-read-only)
  873.     nil
  874.       (forms-mode nil t))))
  875.  
  876. (defun forms-revert-record (&optional arg noconfirm)
  877.  "Reverts current form to un-modified."
  878.  (interactive "P")
  879.  (if (or noconfirm
  880.          (yes-or-no-p 
  881.               "Revert current record (not whole buffer) to unmodified? "))
  882.      (progn (set-buffer-modified-p nil)
  883.             (forms-jump-record forms--current-record))))
  884.  
  885. (defvar forms--comments-recursive-edit nil) ;make local later
  886.  
  887. (defun forms-edit-comments-header ()
  888.   "Edit and view the comments of the buffer."
  889.   (interactive)
  890.   (forms--set-mode-line)
  891.   (if forms--comments-recursive-edit
  892.       (switch-to-buffer-other-window forms--comments-recursive-edit)
  893.   (let ((comment-header (save-excursion
  894.                           (set-buffer forms--file-buffer)
  895.                           forms--comments-header))
  896.         (old-buffer (current-buffer)) 
  897.         new-buffer)
  898.     (save-excursion
  899.     (save-window-excursion        ; Prepare buffer
  900.       (setq new-buffer
  901.         (forms--setup-buffer-create 'forms--comments-buffer
  902.               "*forms-comments-" comment-header "COMMENTS" (buffer-name)))
  903.       (message (substitute-command-keys
  904.         "Type replacement and exit with \\[exit-recursive-edit]"))
  905.       (save-excursion (set-buffer old-buffer)
  906.                       (setq forms--comments-recursive-edit new-buffer))
  907.       (recursive-edit)
  908.       (set-buffer-modified-p nil)
  909.       (save-excursion (set-buffer old-buffer) 
  910.                       (setq forms--comments-recursive-edit nil))
  911.       ;; Wait for return from recursive edit
  912.       (setq forms--comments-header (buffer-substring (point-min) (point-max)))
  913.       (delete-windows-on forms--comments-buffer)))
  914.     (bury-buffer forms--comments-buffer)
  915.     ;; stuff the new comment into the file
  916.     (save-excursion
  917.       (set-buffer forms--file-buffer)
  918.       (widen)
  919.       ;; may have to do some fancy dancing to keep markers separated & correct
  920.       (delete-region forms--comments-header-initial-pos
  921.                      forms--comments-header-final-pos)
  922.       (goto-char forms--comments-header-initial-pos)
  923.       (insert forms--comments-header)
  924.       (set-marker forms--comments-header-final-pos
  925.             (+ forms--comments-header-initial-pos
  926.                (length forms--comments-header)))
  927.       (narrow-to-region
  928.         (+ 1 forms--comments-header-final-pos
  929.            (length forms-comment-header-stop))
  930.         (point-max)))
  931.     (forms--set-mode-line)  )))
  932.  
  933. ;; select and set up a buffer to edit
  934. (defun forms--setup-buffer-create
  935.  (buffer-symbol-name extension-string contents window-label old-buffer)
  936.  (let* ((new-buffer-name (concat extension-string (buffer-name) "*"))
  937.         (new-buffer (get-buffer new-buffer-name)) )
  938.  (if (bufferp new-buffer)
  939.      (progn   )
  940.    (setq new-buffer
  941.          (set buffer-symbol-name (get-buffer-create new-buffer-name)))
  942.    (set-buffer new-buffer)
  943.    (use-local-map forms-mode-hidden-buffer-map)
  944.    (erase-buffer)
  945.    (insert-before-markers contents)
  946.    (set-buffer-modified-p nil)
  947.    (goto-char (point-min))
  948.    (setq mode-line-format
  949.          (forms--make-edit-subtext-mode-line-format window-label old-buffer)))
  950.  (if (not (get-buffer-window new-buffer))
  951.      (progn (switch-to-buffer-other-window new-buffer)
  952.             (shrink-window -3))
  953.    (switch-to-buffer new-buffer))
  954.  new-buffer))
  955.  
  956.  
  957. (defvar forms--commands-recursive-edit nil) ;make local later
  958.  
  959. ;;; main buffer is write protected during commands edit
  960. (defun forms-edit-commands-header ()
  961.   "Edit and view the commands-header of the buffer."
  962.   (interactive)
  963.   (forms--set-mode-line)
  964.   (if forms--commands-recursive-edit
  965.       (switch-to-buffer-other-window forms--commands-recursive-edit)
  966.   (forms--update-all)  ; save all stuff to back buffer here....
  967.   (let ((old-buffer (current-buffer))
  968.         must-rebuild new-buffer header new-header-stuff
  969.         (old-forms-format-list forms-format-list)
  970.         (old-buffer-read-only buffer-read-only))
  971.     (if (not old-buffer-read-only) (toggle-read-only)) 
  972.     (save-excursion
  973.     (save-window-excursion        ; Prepare buffer
  974.       (setq new-buffer
  975.         (forms--setup-buffer-create 'forms--commands-buffer
  976.            "*forms-commands-" forms--commands-header "COMMANDS" (buffer-name)))
  977.       (message (substitute-command-keys
  978.         "Type replacement and exit with \\[exit-recursive-edit]"))
  979.       (save-excursion (set-buffer old-buffer)
  980.                       (setq forms--commands-recursive-edit new-buffer))
  981.       (recursive-edit)
  982.       ;; Wait for return from recursive edit
  983.       (if (buffer-modified-p)
  984.           (progn
  985.             (setq new-header-stuff t)
  986.             (set-buffer-modified-p nil)
  987.             (setq header (buffer-substring (point-min) (point-max)))
  988.             ;; do a test eval & let it bomb here if wouldn't work
  989.            (eval-current-buffer)  
  990.            (setq must-rebuild 
  991.                  (not (equal old-forms-format-list forms-format-list))))
  992.          (setq must-rebuild nil))
  993.       (save-excursion (set-buffer old-buffer) 
  994.                       (setq forms--commands-recursive-edit nil))
  995.       (delete-windows-on forms--commands-buffer)
  996.       (bury-buffer forms--commands-buffer)))
  997.     (if (not old-buffer-read-only) (toggle-read-only))
  998.     (if new-header-stuff
  999.         (progn
  1000.            (forms--save-and-eval-new-header)
  1001.            (if must-rebuild
  1002.               (forms--rebuild-display-buffer)
  1003.               (message "No major changes, you get off easy.")))
  1004.       (message "No changes at all, you get very easy.")))))
  1005.  
  1006.  
  1007. (defun forms--save-and-eval-new-header ()
  1008.   ;; starting in display-buffer
  1009.   (save-excursion
  1010.     (set-buffer forms--file-buffer)
  1011.     (widen)
  1012.     (delete-region 1 forms--commands-header-final-pos)
  1013.     (goto-char 1) ;1 is (point-min)
  1014.     (setq forms--commands-header header)
  1015.     (insert-before-markers forms--commands-header)
  1016.     ;; this should be set automagically now
  1017.     ;;(setq forms--commands-header-final-pos (length forms--commands-header))
  1018.     (forms--call-commands)    ;(forms--parse-comments)
  1019.     (narrow-to-region
  1020.       (+ 1 forms--comments-header-final-pos (length forms-comment-header-stop))
  1021.       (point-max)) )
  1022.   (forms--set-mode-line)
  1023.   (save-excursion
  1024.     (goto-char 1)
  1025.     (setq forms--commands-header header)
  1026.     (insert-before-markers forms--commands-header)
  1027.     ;; (setq forms--commands-header-final-pos (length forms--commands-header))
  1028.     (forms--call-commands)
  1029.     (delete-region 1 forms--commands-header-final-pos)) ;used to be 1+
  1030.     (set-buffer-modified-p nil)  )
  1031.  
  1032. (defun forms--rebuild-display-buffer ()
  1033.   ;; vars are bound by calling function, used for clarity for now...
  1034.   (let ((start-rec forms--current-record)
  1035.         (start-row forms--row))
  1036.     (message "Significant changes, must rebuild display with new commands...")
  1037.     (bury-buffer forms--commands-buffer)
  1038.     (erase-buffer)
  1039.     (forms--initialize-functions)    
  1040.     (forms--draw-all-records forms--display-buffer)
  1041.     (forms-jump-record start-rec)
  1042.     (forward-line (- 1 start-row))
  1043.     ;do choice-parse?
  1044.     (forms--initialize-functions)
  1045.     (message "Rebuilding file with new commands...Finished.")
  1046.     (set-buffer-modified-p nil)
  1047.     (forms--set-mode-line)))
  1048.  
  1049. (defun forms-view-auto-report-header ()
  1050.   "View the auto-report-header of the buffer."
  1051.  (interactive)
  1052.  (let (start save-buffer
  1053.        (old-current-record forms--current-record)
  1054.        (header forms--commands-header)
  1055.        (display-buffer
  1056.         (get-buffer-create (concat "*forms-auto-report-" (buffer-name) "*"))) )
  1057.     (setq start (point))        ; Save current location
  1058.     (save-window-excursion        ; Prepare buffer
  1059.       (setq save-buffer (buffer-name))
  1060.       (switch-to-buffer-other-window display-buffer)
  1061.       (erase-buffer)
  1062.       (run-hooks 'forms-auto-report-hook)
  1063.       (goto-char (point-min))
  1064.       (set-buffer-modified-p nil)
  1065.     ;; stuff the new report into the file?
  1066.   )))
  1067.  
  1068.   ;; note good cog eng here, we make the command to get out always visible
  1069.   ;; also simplified the mode line considerably
  1070.  
  1071. ;(forms--edit-subtext-mode-line-format 'commands 'buffer-name)
  1072. (defun forms--make-edit-subtext-mode-line-format (type buffer-name)
  1073.  "Set up the mode-line for a header section of buffer."
  1074.   (list
  1075.      mode-line-modified
  1076.     (format "{%s} of %s" type buffer-name)
  1077.     global-mode-string
  1078.     (substitute-command-keys
  1079.         " %[(\\[exit-recursive-edit] to exit, \\[describe-mode] for help)%]")
  1080.     "---"
  1081.    '(-3 . "%p")  "-%-"))
  1082.  
  1083.  
  1084. ;;;
  1085. ;;;    III.    forms--make-record-filters
  1086. ;;;
  1087.  
  1088. (defun forms--make-record-filters ()
  1089.   "Check if record filters are defined and define them iff necc."
  1090.   (setq forms--new-record-filter 
  1091.         (cond ((fboundp 'forms-new-record-filter)
  1092.                (symbol-function 'forms-new-record-filter))
  1093.               ((and (boundp 'forms-new-record-filter)
  1094.                     (fboundp forms-new-record-filter))
  1095.                forms-new-record-filter)))
  1096.   (fmakunbound 'forms-new-record-filter)
  1097.   (make-local-variable 'forms--modified-record-filter)
  1098.   (setq forms--modified-record-filter 
  1099.         (cond ((fboundp 'forms-modified-record-filter)
  1100.                (symbol-function 'forms-modified-record-filter))
  1101.               ((and (boundp 'forms-modified-record-filter)
  1102.                     (fboundp forms-modified-record-filter))
  1103.                forms-modified-record-filter)))
  1104.   (fmakunbound 'forms-modified-record-filter)  )
  1105.  
  1106.  
  1107. ;;;
  1108. ;;;    IV.    Set up and check local variables
  1109. ;;;
  1110.  
  1111. (defun forms--check-local-variables ()
  1112.   "Check if the mandatory variables make sense."
  1113.   (or forms-file (error "'forms-file' has not been set"))
  1114.   (or (stringp forms-field-sep)
  1115.       (error "'forms-field-sep' is not a string"))
  1116.   (setq forms--field-sep-char (string-to-char forms-field-sep))
  1117.   (or forms-number-of-fields
  1118.       (setq forms-number-of-fields
  1119.             (save-excursion (set-buffer forms--file-buffer)
  1120.                  (forms--set-number-of-fields)))
  1121.       (error "'forms-number-of-fields' has not been set"))
  1122.   (or (> forms-number-of-fields 0)
  1123.       (error "'forms-number-of-fields' must be > 0"))
  1124.   (if forms-multi-line
  1125.       (if (and (stringp forms-multi-line)
  1126.                (eq (length forms-multi-line) 1))
  1127.           (if (string= forms-multi-line forms-field-sep)
  1128.               (error "'forms-multi-line' is equal to 'forms-field-sep'"))
  1129.           (error "'forms-multi-line' must be nil or a one-character string")))
  1130.   )    
  1131.  
  1132.  
  1133. (defun forms--make-local-variables ()
  1134.   "Make all the local variables that forms-mode needs."
  1135.   (kill-all-local-variables)
  1136.   ;; make mandatory variables
  1137.   (make-local-variable 'forms-file)
  1138.   (make-local-variable 'forms-number-of-fields)
  1139.   (setq forms-number-of-fields nil)
  1140.   (make-local-variable 'forms-format-list)
  1141.   (make-local-variable 'forms--all-in-one)
  1142.   (make-local-variable 'forms-auto-report-hook)
  1143.  
  1144.   ;; make optional variables
  1145.   (make-local-variable 'forms-field-sep)
  1146.   (make-local-variable 'forms--field-sep-char)
  1147.   (make-local-variable 'forms-read-only)
  1148.   (make-local-variable 'forms-multi-line)
  1149.   (make-local-variable 'forms-forms-scroll)
  1150.   (make-local-variable 'forms-forms-jump)
  1151.   ;; set up the markers
  1152.   (make-local-variable 'forms--commands-header-final-pos)
  1153.   (make-local-variable 'forms--comments-header-final-pos)
  1154.   (make-local-variable 'forms--comments-header-initial-pos)
  1155.  
  1156.   (setq forms--commands-header-final-pos (make-marker))
  1157.   (setq forms--comments-header-final-pos (make-marker))
  1158.   (setq forms--comments-header-initial-pos (make-marker))
  1159.  
  1160.   (make-local-variable 'forms--commands-header)
  1161.   (make-local-variable 'forms--comments-header)
  1162.   (make-local-variable 'forms--commands-recursive-edit)
  1163.   (make-local-variable 'forms--comments-recursive-edit)
  1164.                         
  1165.   ;; record-filter stuff
  1166.   (make-local-variable 'forms--new-record-filter)
  1167.   (fmakunbound 'forms-new-record-filter)
  1168.  
  1169.   ;; make local variables; second batch from forms-mode
  1170.   ;; these are the working stack
  1171.   (make-local-variable 'forms--file-buffer)
  1172.   (make-local-variable 'forms--comments-buffer)
  1173.   (make-local-variable 'forms--commands-buffer)
  1174.   (make-local-variable 'forms--total-records)
  1175.   (make-local-variable 'forms--current-record)
  1176.   (make-local-variable 'forms--the-record-list)
  1177.   (make-local-variable 'forms--search-rexexp)
  1178.  
  1179.   ;; dynamic text support
  1180.   (make-local-variable 'forms--dynamic-text)
  1181.   (make-local-variable 'forms-fields)
  1182.  
  1183.   ;; We have our own (partial) revert function - use it
  1184.   (make-local-variable 'revert-buffer-function)
  1185.   (setq revert-buffer-function 'forms-revert-record)
  1186.  
  1187.   ;; variables for format processing
  1188. ;  (make-local-variable 'forms--number-of-markers)
  1189.   (make-local-variable 'forms--markers)
  1190.  
  1191.   (make-local-variable 'forms--format)
  1192.   (make-local-variable 'forms--parser)
  1193.  
  1194.   ;; new stuff, mostly for new insertion functions
  1195.   (make-local-variable 'forms--display-buffer)
  1196.   (make-local-variable 'forms--column)
  1197.   (make-local-variable 'forms--row)
  1198.   (make-local-variable 'forms--char-insertable-function)
  1199.   (make-local-variable 'forms--advanced-column-keywords)
  1200.   (make-local-variable 'forms--first-advanced-row)
  1201.   (make-local-variable 'forms--first-advanced-column)
  1202.   (make-local-variable 'forms--maxlength-advanced-column-keywords)
  1203. )
  1204.  
  1205.  
  1206. ;;;
  1207. ;;;     V.    Set up the format (printing out ) stuff
  1208. ;;;
  1209. ;;; Validates forms-format-list.
  1210. ;;;
  1211. ;;; Sets forms--number-of-markers and forms--markers.
  1212.  
  1213. (defun forms--process-format-list ()
  1214.   "Validate forms-format-list and set some global variables."
  1215.   (forms--debug "forms-forms-list before 1st pass:\n" 'forms-format-list)
  1216.   ;; it must be non-nil
  1217.   (or forms-format-list (error "'forms-format-list' has not been set"))
  1218.   ;; it must be a list ...
  1219.   (or (listp forms-format-list) (error "'forms-format-list' is not a list"))
  1220.   ;; it must be made out of lists
  1221.   (if forms--all-in-one
  1222.       (mapcar '(lambda (x) 
  1223.                  (if (not (listp x))
  1224.                      (error "forms-format-list sublist >>%s<< is not a list." 
  1225.                              x)))
  1226.                forms-format-list))
  1227.   ;; check for duplicate labels here:
  1228.  
  1229.   ;(setq forms--number-of-markers 0)
  1230.  
  1231.   (let ((the-list forms-format-list)    ; the list of format elements
  1232.     (this-item 0)            ; element in list
  1233.     (field-num 0))            ; highest field number 
  1234.  
  1235.    (setq forms-format-list nil)    ; gonna rebuild
  1236.  
  1237.    (while the-list
  1238.      (let* ((el-list (car-safe the-list))
  1239.             (el (format-item-field el-list))
  1240.             (rem (cdr-safe the-list)))
  1241.  
  1242.       ;; if it is a symbol, eval it first
  1243.       (if (and (symbolp el) (boundp el))
  1244.           (setq el (eval el)))
  1245.       (cond
  1246.     ((stringp el))            ; string is OK
  1247.     ((numberp el)
  1248.      (if (or (<= el 0)
  1249.              (> el forms-number-of-fields))
  1250.          (error "Forms error: field number %d out of range 1..%d"
  1251.                 el forms-number-of-fields))
  1252.      ;(setq forms--number-of-markers (1+ forms--number-of-markers))
  1253.      (if (> el field-num)
  1254.          (setq field-num el)))
  1255.     ((and (listp el)
  1256.               (not el))  ;it's nil
  1257.          (setq el ""))
  1258.     ((listp el)     ; try s-exp
  1259.      (or (fboundp (car-safe el))
  1260.          (error "Forms error: not a function: %s"
  1261.                 (prin1-to-string (car-safe el)))))
  1262.     (t
  1263.       (error "Invalid element in 'forms-format-list': %s"
  1264.          (prin1-to-string el))))
  1265.  
  1266.       ;; advance to next element of the list
  1267.       (forms--replace-format-item 3 el-list el) ;this changes el
  1268.       (setq the-list rem)
  1269.       (setq forms-format-list
  1270.         (append forms-format-list (list el-list))))))
  1271.  
  1272.   (forms--debug "forms-forms-list after 1st pass:\n" 'forms-format-list)
  1273.  
  1274.   ;; concat adjacent strings
  1275.   (setq forms-format-list (forms--concat-adjacent forms-format-list))
  1276.  
  1277.   (forms--debug "forms-forms-list after 2nd pass:\n")
  1278.         ;'forms-format-list 'forms--number-of-markers
  1279.  
  1280.   ;(setq forms--markers (make-vector forms--number-of-markers nil))
  1281.   )
  1282.   ;; end - forms--process-format-list
  1283.  
  1284. ;;;
  1285. ;;; Build the format routine from forms-format-list.
  1286. ;;;
  1287. ;;; The format routine (forms--format) will look like
  1288. ;;; 
  1289. ;;; (lambda (arg)
  1290. ;;;   (setq forms--dynamic-text nil)
  1291. ;;;   ;;  "text: "
  1292. ;;;   (insert "text: ")
  1293. ;;;   ;;  6
  1294. ;;;   (aset forms--markers 0 (point-marker))
  1295. ;;;   (insert (elt arg 5))
  1296. ;;;   ;;  "\nmore text: "
  1297. ;;;   (insert "\nmore text: ")
  1298. ;;;   ;;  (tocol 40)
  1299. ;;;   (let ((the-dyntext (tocol 40)))
  1300. ;;;     (insert the-dyntext)
  1301. ;;;     (setq forms--dynamic-text (append forms--dynamic-text
  1302. ;;;                      (list the-dyntext))))
  1303. ;;;   ;;  9
  1304. ;;;   (aset forms--markers 1 (point-marker))
  1305. ;;;   (insert (elt arg 8))
  1306. ;;;
  1307. ;;;   ... )
  1308. ;;; 
  1309.  
  1310. (defun forms--make-format ()
  1311.   "Generate format function for forms."
  1312.   (setq ff  (setq forms--format (forms--format-maker forms-format-list)))
  1313.   (setq forms--format (byte-compile-lambda forms--format))
  1314.   (forms--debug 'forms--format))
  1315.  
  1316. (defun forms--format-maker (the-format-list)
  1317.   "Returns the formating function for forms."
  1318.   (let ((the-marker 0))
  1319.     (` (lambda (arg)
  1320.      (setq forms--dynamic-text nil)
  1321.          (forms--insert-line-number forms--current-record)
  1322.      (,@ (apply 'append
  1323.             (mapcar 'forms--make-format-elt the-format-list)))))))
  1324.  
  1325. (defun forms--insert-line-number (i)
  1326.   (insert (format "%d " i))
  1327.   (insert forms-record-separator))
  1328.  
  1329.  
  1330. ;; arg gets bound in these guys' run time environment
  1331. (defun forms--make-format-elt (item-list)
  1332.  (let ((el (format-item-field item-list))
  1333.        (label (format-item-label item-list))
  1334.        (name (format-item-name item-list))
  1335.        (field-size (format-item-field-size item-list))
  1336.        (newline-p (format-item-newline-p item-list))
  1337.        (line-size (format-item-line-size item-list))   )
  1338.   (cond
  1339.    ((stringp el)
  1340.     (` ( (if (forms--item-list-visible-p '(, name))
  1341.              (progn
  1342.               (insert-n-times "\n" (, newline-p))
  1343.               (insert (, label))
  1344.               (if nil ' string)
  1345.               (forms--insert (, el) (, field-size) (, line-size))))
  1346.             )))
  1347.    ((numberp el)
  1348.     (prog1
  1349.       (` ( (if (forms--item-list-visible-p '(, name))
  1350.                (progn
  1351.                 (insert-n-times "\n" (, newline-p))
  1352.                 (insert (, label))
  1353.                 (if forms--markers
  1354.                     (aset forms--markers (, the-marker) (point-marker)))
  1355.                 (forms--insert (elt arg (, (1- el)))
  1356.                                (, field-size) (, line-size))))))
  1357.       (setq the-marker (1+ the-marker))))
  1358.    ((listp el)
  1359.     (let ((the-dyntext (eval el)))
  1360.       (` ((if (forms--item-list-visible-p '(, name))
  1361.               (progn
  1362.                 (insert-n-times "\n" (, newline-p))
  1363.                 (insert (, label))
  1364.                 (forms--insert (, the-dyntext) (, field-size) (, line-size))
  1365.                 (setq forms--dynamic-text (append forms--dynamic-text
  1366.                                                   (list (, the-dyntext)))) )))
  1367.       )))  )))
  1368.  
  1369. (defun forms--insert (el field-size line-size)
  1370.   "Insert record ELement based on FIELD-SIZE and LINE-SIZE."
  1371.   (let ((e-length (length el))
  1372.         (eline-size el)
  1373.         (start (point))
  1374.         eline-length)
  1375.     (if (and (numberp field-size) (> e-length field-size))
  1376.         (setq el (substring el 0 field-size)))
  1377.     (insert el)
  1378.     (if (and (numberp field-size) (< e-length field-size))
  1379.         (insert-n-times " " (- field-size e-length)))
  1380.     (setq eline-length (count-lines start (point)))
  1381.     (if (and (numberp line-size) (< eline-length line-size))
  1382.         (insert-n-times "\n" (- line-size eline-length)))  ))
  1383.  
  1384. (defun forms--concat-adjacent (the-list)
  1385.   "Concatenate adjacent strings in the-list and return the resulting list."
  1386.   (if (consp the-list)
  1387.       (let ((the-rest (forms--concat-adjacent (cdr the-list))))
  1388.     (if (and (stringp (car the-list)) (stringp (car the-rest)))
  1389.         (cons (concat (car the-list) (car the-rest))
  1390.           (cdr the-rest))
  1391.         (cons (car the-list) the-rest)))
  1392.       the-list))
  1393.  
  1394.  
  1395. ;;;
  1396. ;;;     VI.    Set up the parsing (rereading records) routines
  1397. ;;;
  1398. ;;; Generate parse routine from forms-format-list.  They parse for 
  1399. ;;; form correctness, and also put the findings into a vector called
  1400. ;;; the-recordv.
  1401. ;;;
  1402. ;;; The parse routine (forms--parser) will look like (give or take
  1403. ;;; a few " "'s. [this is an unrevised comment -FER]
  1404. ;;; 
  1405. ;;; (lambda nil
  1406. ;;;   (let (here)
  1407. ;;;     (goto-char (point-min)))
  1408. ;;; 
  1409. ;;;    ;;  "text: "
  1410. ;;;     (if (not (looking-at "text: "))
  1411. ;;;         (error "Parse error: cannot find \"text: \""))
  1412. ;;;     (forward-char 6)    ; past "text: "
  1413. ;;;     ;;  6
  1414. ;;;    ;;  "\nmore text: "
  1415. ;;;     (setq here (point))
  1416. ;;;     (if (not (search-forward "\nmore text: " nil t nil))
  1417. ;;;         (error "Parse error: cannot find \"\\nmore text: \""))
  1418. ;;;     (aset the-recordv 5 (buffer-substring here (- (point) 12)))
  1419. ;;;
  1420. ;;;    ;;  (tocol 40)
  1421. ;;;    (let ((the-dyntext (car-safe forms--dynamic-text)))
  1422. ;;;      (if (not (looking-at (regexp-quote the-dyntext)))
  1423. ;;;          (error "Parse error: not looking at \"%s\"" the-dyntext))
  1424. ;;;      (forward-char (length the-dyntext))
  1425. ;;;      (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
  1426. ;;;     ... 
  1427. ;;;     ;; final flush (due to terminator sentinel, see below)
  1428. ;;;    (aset the-recordv 7 (buffer-substring (point) (point-max)))))
  1429. ;;; 
  1430.  
  1431. (defun forms--make-parser ()
  1432.   "Generate parser function for forms."
  1433.   (setq forms--parser (forms--parser-maker forms-format-list))
  1434.   ;; boy, this sure doesn't look like it saves us much...
  1435.   (setq forms--parser (byte-compile-lambda forms--parser))
  1436.   (forms--debug 'forms--parser))
  1437.  
  1438. (defun forms--parser-maker (the-format-list)
  1439.   "Returns the parser function for forms."
  1440.   (let ((the-field nil)  ; temp variable storing the last field parsed
  1441.     (seen-text nil)
  1442.     the--format-list
  1443.         (last-field-size nil))
  1444.     ;; last-field-size is set to the previous clause's field size, used to catch
  1445.     ;; errors.
  1446.     ;; add a terminator sentinel
  1447.     ;(setq the--format-list (append the-format-list (list nil)))
  1448.     ;; not longer needed?
  1449.     (setq the--format-list the-format-list)
  1450.     (` (lambda nil
  1451.      (let (here max-record-point)
  1452.            (setq max-record-point
  1453.                  (or (save-excursion
  1454.                        (if (search-forward forms-record-separator nil t)
  1455.                            (progn (forward-line -1) (forward-char -1)
  1456.                                   (point))))
  1457.                      (point-max)))
  1458.            (search-backward forms-record-separator nil t)
  1459.            (search-forward forms-record-separator nil t)
  1460.      (,@ (apply 'append
  1461.             (mapcar 'forms--make-parser-elt the--format-list))))))))
  1462.  
  1463.  
  1464. (defun forms--make-parser-elt (item-list)
  1465.  (let ((el (format-item-field item-list))
  1466.        (name (format-item-name item-list))
  1467.        (label (format-item-label item-list))
  1468.        (field-size (format-item-field-size item-list))
  1469.        (newline-p (format-item-newline-p item-list))
  1470.        (line-size (format-item-line-size item-list))        )
  1471.  (if (and (not newline-p) (eq last-field-size t))
  1472.      (error
  1473.    "Must have 't' new-line after fullsize field [clause=%s  l=%s]."
  1474.              name label))
  1475.  (setq last-field-size field-size)
  1476.  (cond
  1477.   ((stringp el)
  1478.    (` ((if (forms--item-list-visible-p '(, name))
  1479.            (progn
  1480.        (if (, newline-p) (progn (forward-line) (beginning-of-line)))
  1481.        (if (and (, label)
  1482.                 (not (search-forward (, label) max-record-point t nil)))
  1483.            (error "Parse error: cannot find \"%s\" [in #%s point=%s]" 
  1484.                   (+ 1 i) (, label) (point) ))
  1485.        (if (and (not (string= (, el) ""))
  1486.                 (not (search-forward (, el) max-record-point t nil)))
  1487.            (error "Parse error: cannot find \"%s\" [in #%s point=%s]" 
  1488.                   (, el) (+ 1 i) (point))))
  1489.        ) )))
  1490.   ((numberp el)
  1491.    (` ((if (forms--item-list-visible-p '(, name))
  1492.            (progn
  1493.        (if (, newline-p) (progn (forward-line) (beginning-of-line)))
  1494.        (if (and (, label)
  1495.                 (not (forms--search-forward (, label) max-record-point t nil)))
  1496.        (error "Parse error: cannot find \"%s\" [in #%s point=%s]" 
  1497.                   (, label) (+ 1 i) (point)))
  1498.        (setq here (point))
  1499.        (forms--elt-parse (, field-size) (, line-size) '(, name))
  1500.        (aset the-recordv (, (1- el))
  1501.              (buffer-substring here (point)))))
  1502.        ) ))
  1503.   ((null el)
  1504.    (` ((if (forms--item-list-visible-p '(, name))
  1505.            (progn
  1506.        (if (, newline-p) (progn (forward-line) (beginning-of-line)))
  1507.        (if (and (, label)
  1508.                 (not (search-forward (, label) max-record-point t nil)))
  1509.        (error "Parse error: cannot find \"%s\" [in #%s point=%s]" 
  1510.                   (, label) (+ 1 i) (point)))))
  1511.       ) ) )
  1512.   ((listp el)
  1513.    (` ((if (forms--item-list-visible-p '(, name))
  1514.            (progn
  1515.        (if (, newline-p) (progn (forward-line) (beginning-of-line)))
  1516.        (if (and (, label)
  1517.                 (not (search-forward (, label) max-record-point t nil)))
  1518.            (error "Parse error: cannot find \"%s\" [in #%s point=%s]" 
  1519.                   (, label) (+ 1 i) (point)))
  1520.        (setq here (point))
  1521.        (let ((the-dyntext (car-safe forms--dynamic-text)))
  1522.          (if (not (looking-at (regexp-quote the-dyntext)))
  1523.              (error "Parse error: not looking at \"%s\" [in #%s point=%s]" 
  1524.                     the-dyntext (+ 1 i) (point)))
  1525.          (forward-char (length the-dyntext))
  1526.          (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))))
  1527.       ) ))
  1528.   )))
  1529.  
  1530. (defun forms--elt-parse (field-size line-size name)
  1531.  "Set the point at the end of the forms size."
  1532.  ;; field-size can be # or t (end of line)
  1533.  ;; line-size can be #, nil or t (till next known label), or next record sep
  1534.  (let (search-item)
  1535.  (if (not line-size)
  1536.      (if (numberp field-size)
  1537.          (forms--elt-parse-forward-char field-size)
  1538.        (end-of-line))
  1539.    ;; else if line-size
  1540.    (if (numberp line-size)
  1541.        (progn (forward-line line-size)
  1542.               (end-of-line))
  1543.      (if (setq search-item (format-item-label (forms--next-visible-item name)))
  1544.          (if (search-forward search-item max-record-point 'to-end)
  1545.              (progn (beginning-of-line)
  1546.                     (backward-char 1)))
  1547.        (setq search-item forms-record-separator)
  1548.        (if (search-forward search-item (point-max) 'to-end)
  1549.            (progn (forward-line -1)
  1550.                   (forward-char -1))))))))
  1551.  
  1552. (defun forms--elt-parse-forward-char (field-size)
  1553.   "Forward field-size chars or to end of line, whichever comes first."
  1554.   (let ( (line-end (save-excursion (end-of-line) (point))) )
  1555.   (forward-char field-size)
  1556.   (if (> (point) line-end)
  1557.       (goto-char line-end))
  1558. ))
  1559.  
  1560. (defun forms--parse-form ()
  1561.   "Parse contents of form into list of strings."
  1562.   ;; The contents of the form are parsed, and a new list of strings
  1563.   ;; is constructed.
  1564.   ;; A vector with the strings from the original record is 
  1565.   ;; constructed, which is updated with the new contents. Therefore
  1566.   ;; fields that were not in the form are not modified.
  1567.   ;; Finally, the vector is transformed into a list for further processing.
  1568.  
  1569.   (let (the-recordv)
  1570.     ;; build the vector
  1571.     (setq the-recordv (vconcat forms--the-record-list))
  1572.     ;; parse the form and update the vector
  1573.     (let ((forms--dynamic-text forms--dynamic-text))
  1574.       (funcall forms--parser))
  1575.     (if forms--modified-record-filter
  1576.     ;; As a service to the user, we add a zeroth element so she
  1577.     ;; can use the same indices as in the forms definition.
  1578.     (let ((the-fields (vconcat [nil] the-recordv)))
  1579.       (setq the-fields (funcall forms--modified-record-filter the-fields))
  1580.       (cdr (append the-fields nil)))
  1581.       ;; transform to a list and return
  1582.       (append the-recordv nil))))
  1583.  
  1584.  
  1585. ;;;
  1586. ;;;    VII.    Set up the keymaps
  1587. ;;;
  1588. ;;; Set the keymaps used in this mode.
  1589.  
  1590. (defvar forms-prefix "\C-c" "Key that forms-mode commands hang off of.")
  1591.  
  1592. (defun forms--defkey (keymap key command)
  1593.   "Define KEYMAP forms-prefix+KEY as command."
  1594.   ;; if form-prefix has been bound to nil, then just set the key as is...
  1595.   (let ((prefix-map (if forms-prefix
  1596.                         (lookup-key keymap forms-prefix)
  1597.                       keymap)) )
  1598.     (if (not (keymapp prefix-map))
  1599.     (setq prefix-map
  1600.           (define-key keymap forms-prefix (make-sparse-keymap))))
  1601.     (define-key prefix-map key command)))
  1602.  
  1603. (defun forms--mode-commands (map)
  1604.   "Fill MAP with all forms commands."
  1605.   ;; this is used by both keymaps; commands are prefixed with forms-prefix
  1606.   ;; (often C-c)
  1607.   (forms--defkey map "2"    'forms-duplicate-record)
  1608.   (forms--defkey map "\t"   'forms-next-field)
  1609.   (forms--defkey map " "    'forms-next-record)
  1610.   (forms--defkey map "b"    'forms-prev-record)
  1611.   (forms--defkey map "\C-c" 'forms-edit-comments-header)
  1612.   (forms--defkey map "d"    'forms-delete-record)
  1613.   (forms--defkey map "e"    'forms-edit-mode)
  1614.   (forms--defkey map "\C-h" 'forms-edit-commands-header)
  1615.   (forms--defkey map "i"    'forms-insert-record)
  1616.   (forms--defkey map "j"    'forms-jump-record)
  1617.   (forms--defkey map "m"    'forms-run-menu)
  1618.   (forms--defkey map "\C-m" 'forms-run-menu)
  1619.   (forms--defkey map "n"    'forms-next-record)
  1620.   (forms--defkey map "p"    'forms-prev-record)
  1621.   (forms--defkey map "q"    'forms-exit)
  1622.   (forms--defkey map "s"    'forms-search)
  1623.   (forms--defkey map "v"    'forms-view-mode)
  1624.   (forms--defkey map "x"    'forms-exit-no-save)
  1625.   (forms--defkey map "<"    'forms-end-of-buffer)
  1626.   (forms--defkey map ">"    'forms-beginning-of-buffer)
  1627.   (forms--defkey map "?"    'describe-mode)
  1628.   ;; del seems like a bad thing to encourage people to hit
  1629.   ;(forms--defkey map "\177" 'forms-prev-record)  ; \177 is ^?
  1630.   )
  1631.  
  1632. (defun forms--new-command-mappings (map)
  1633.   ;; these guys are always on the top map, and represent overides of global
  1634.   ;; bindings
  1635.   (define-key map "\M-\C-a" 'forms-beginning-of-form)
  1636.   (define-key map "\M-\C-e" 'forms-end-of-form)
  1637.   (define-key map "\M-v" 'forms-scroll-down)
  1638.   (define-key map "\C-v" 'forms-scroll-up)
  1639.   (define-key map "\C-_" 'forms-undo)
  1640.   (define-key map "\M-<" 'forms-first-record)
  1641.   (define-key map "\M->" 'forms-last-record)
  1642.   (define-key map "\C-xk" 'forms-exit)  ;replaces kill buffer
  1643.   (define-key map "\C-x\C-s" 'forms-save-buffer)
  1644.   (define-key map "\C-x\C-w" 'forms-write-buffer)
  1645. )
  1646.  
  1647. (if forms-mode-commands-map ;this is the read-only guy
  1648.     nil
  1649.     (let ((forms-prefix nil))
  1650.      (setq forms-mode-commands-map (make-sparse-keymap))
  1651.      (forms--mode-commands forms-mode-commands-map)
  1652.      (forms--new-command-mappings forms-mode-commands-map))
  1653.    ;; now set the C-c characters
  1654.    (forms--mode-commands forms-mode-commands-map))
  1655.  
  1656. (if forms-mode-hidden-buffer-map  ; this is the guy on the hidden buffer
  1657.     nil
  1658.     (setq forms-mode-hidden-buffer-map  (make-sparse-keymap))
  1659.     (define-key forms-mode-hidden-buffer-map "\C-xk" 'no-op)
  1660. )
  1661.  
  1662. ;; this is the read/write map
  1663. (if forms-mode-map
  1664.     nil
  1665.   (setq forms-mode-map (make-keymap))
  1666.   (forms--mode-commands forms-mode-map)
  1667.   (forms--new-command-mappings forms-mode-map)
  1668.   (let ((i ?\ ))
  1669.     (while (< i ?\177)
  1670.       (aset forms-mode-map i 'forms-self-insert)
  1671.       (setq i (1+ i))))
  1672.   (define-key forms-mode-map "\C-b" 'backward-char)
  1673.   (define-key forms-mode-map "\C-d" 'forms-clear-column)
  1674.   (define-key forms-mode-map "\177" 'forms-backward-clear-column) ;DEL
  1675.   (define-key forms-mode-map "\C-j" 'picture-duplicate-line)
  1676.   (define-key forms-mode-map "\C-k" 'forms-kill-line)   ;ok-fer
  1677.   (define-key forms-mode-map "\C-m" 'forms-newline)     ;ok-fer
  1678.   (define-key forms-mode-map "\C-o" 'forms-open-line)   ;ok-fer
  1679.   (define-key forms-mode-map "\C-q" 'forms-quoted-insert) ;ok-fer
  1680.   (define-key forms-mode-map "\C-w" 'forms-kill-region) ;ok-fer
  1681.   (define-key forms-mode-map "\C-y" 'forms-yank)        ;ok-fer
  1682.   (define-key forms-mode-map "\ed"  'forms-kill-word)   ;ok-fer
  1683.   (define-key forms-mode-map "\t"   'forms-next-field)  ;ok-fer
  1684.   (define-key forms-mode-map "\e\t" 'forms-prev-field)  ;ok-fer
  1685.   (define-key forms-mode-map "\e\177"  'forms-backward-kill-word)  ;ok-fer
  1686.   (define-key forms-mode-map "\ey" 'forms-yank-pop)
  1687.   ;; these are probably bad keybindings
  1688.   (define-key forms-mode-map "\C-c\C-k" 'picture-clear-rectangle)
  1689.   (define-key forms-mode-map "\C-c\C-w" 'picture-clear-rectangle-to-register)
  1690.   (define-key forms-mode-map "\C-c\C-y" 'picture-yank-rectangle)
  1691.   (define-key forms-mode-map "\C-c\C-x" 'picture-yank-rectangle-from-register)
  1692.   (define-key forms-mode-map "\C-c\C-f" 'picture-motion)
  1693.   (define-key forms-mode-map "\C-c\C-b" 'picture-motion-reverse)
  1694.   ;(define-key forms-mode-map "\C-c<" 'picture-movement-left)
  1695.   ;(define-key forms-mode-map "\C-c>" 'picture-movement-right)
  1696.   (define-key forms-mode-map "\C-c^" 'picture-movement-up)
  1697.   (define-key forms-mode-map "\C-c." 'picture-movement-down)
  1698.   (define-key forms-mode-map "\C-c`" 'picture-movement-nw)
  1699.   (define-key forms-mode-map "\C-c'" 'picture-movement-ne)
  1700.   (define-key forms-mode-map "\C-c/" 'picture-movement-sw)
  1701.   (define-key forms-mode-map "\C-c\\" 'picture-movement-se)
  1702. )
  1703.  
  1704.  
  1705. (let ((old-map (current-local-map)))
  1706.   (if forms-quick-help-writable
  1707.       nil
  1708.     (use-local-map forms-mode-map)
  1709.     (setq forms-quick-help-writable
  1710.           (substitute-command-keys (concat
  1711.             "\\[forms-run-menu]:menu"
  1712.             "  \\[describe-mode]:help"
  1713.             "  \\[forms-next-record]:next"
  1714.             " \\[forms-prev-record]:prev"
  1715.             " \\[forms-first-record]:1st"
  1716.             " \\[forms-last-record]:last"
  1717.             " \\[forms-exit]:exit")))
  1718.     (use-local-map forms-mode-commands-map)
  1719.     (setq forms-quick-help-read-only
  1720.           (substitute-command-keys (concat
  1721.             "\\[forms-run-menu]:menu"
  1722.             "  \\[describe-mode]:help"
  1723.             "  \\[forms-next-record]:next"
  1724.             " \\[forms-prev-record]:prev"
  1725.             " \\[forms-first-record]:1st"
  1726.             " \\[forms-last-record]:last"
  1727.             " \\[forms-exit]:exit"))))
  1728.    (use-local-map old-map))
  1729.  
  1730. ;; A bug in the current Emacs release prevents a keymap
  1731. ;; that is buffer-local from being used by 'describe-mode'.
  1732. ;; Hence we'll leave it global.
  1733. ;; -fer I think I see, some forms may want different keybindings.
  1734. ;; you can set this up on the way in, putting the changes in each file.
  1735. ;; with static keymaps, you can do this with a hook function
  1736.  
  1737.  
  1738. ;;;
  1739. ;;;     VIII.    Changed movement functions
  1740. ;;;
  1741. ;;; Emacs (as of 18.55) lacks the functionality of buffer-local
  1742. ;;; funtions.  We no longer save the original meaning of some handy
  1743. ;;; functions, and replace them with a wrapper.
  1744. ;;; This is best done with a keymap.
  1745.  
  1746. (defun forms-backward-kill-word (&optional arg)
  1747.   "Kill characters backward until encountering the end of a word.
  1748. With argument, do this that ARG times.  Preserves forms column fill when
  1749. appropriate."
  1750.   (interactive "p")
  1751.   (forms-kill-word (- arg)))
  1752.  
  1753. (defun forms-kill-word (arg)
  1754.   "Kill characters forward until encountering the end of a word.
  1755. With argument, tries to do this ARG times.  Preserves forms column fill when
  1756. appropriate."
  1757.   (interactive "P")
  1758.   (setq arg (prefix-numeric-value arg))
  1759.   (forms--find-row-and-column)
  1760.   (let* ((arg-sign (if (> arg 0) 1 -1))
  1761.          (here (point))
  1762.          (there (save-excursion (forward-word arg-sign) (point)))
  1763.          (max-there (forms--char-writable-region here there)) )
  1764.     (if (numberp max-there)
  1765.         (progn (kill-region here max-there)
  1766.            (if (and forms--first-advanced-row
  1767.                     (< forms--row forms--first-advanced-row))
  1768.                (insert-n-times " " (- max-there here))))
  1769.         (forms--next-writable arg-sign)
  1770.         (if (minusp arg-sign) (forward-char 1))
  1771.         (error ""))
  1772.     (goto-char there)
  1773.     (setq arg (+ arg (- arg-sign)))
  1774.     ;; recurse on rest
  1775.     (if (not (= arg 0))
  1776.         (forms-kill-word arg))  ))
  1777.  
  1778. (defun forms-scroll-down (&optional arg) 
  1779.   (interactive "P")
  1780.   (if (and forms--mode-setup
  1781.            forms-forms-scroll)
  1782.       (forms-prev-record arg)
  1783.    (forms--scroll-down arg)))
  1784.  
  1785. (defun forms-scroll-up (&optional arg) 
  1786.   (interactive "P")
  1787.   (if (and forms--mode-setup
  1788.            forms-forms-scroll)
  1789.      (forms-next-record arg)
  1790.      (forms--scroll-up arg)))
  1791.  
  1792. (defun forms-beginning-of-buffer ()
  1793.   (interactive)
  1794.   (if (and forms--mode-setup
  1795.            forms-forms-jump)
  1796.       (forms-first-record)
  1797.       (forms--beginning-of-buffer)))
  1798.  
  1799. (defun forms-end-of-buffer ()
  1800.   (interactive)
  1801.   (if (and forms--mode-setup
  1802.            forms-forms-jump)
  1803.       (forms-last-record)
  1804.       (forms--end-of-buffer)))
  1805.  
  1806. (defun forms-next-record (arg)
  1807.   "Advance to the ARGth following record."
  1808.   (interactive "P")
  1809.   (forms--find-current-record-number)
  1810.   (if (not (forms-jump-record (+ forms--current-record
  1811.                                  (prefix-numeric-value arg)) t t))
  1812.       (if (or forms-read-only
  1813.               (not (y-or-n-p "Insert a new record at end? ")))
  1814.           (error "At end of records.")
  1815.         (forms-insert-record t))))
  1816.  
  1817. (defun forms-prev-record (arg)
  1818.   "Advance to the ARGth previous record."
  1819.   (interactive "P")
  1820.   (forms--find-current-record-number)
  1821.   (if (not (forms-jump-record (- forms--current-record
  1822.                                  (prefix-numeric-value arg)) t t))
  1823.       (if (or forms-read-only
  1824.               (not (y-or-n-p "Insert a new record at beginning? ")))
  1825.           (error "At beginning of records.")
  1826.         (forms-insert-record nil))))
  1827.  
  1828.  
  1829. (defun forms-jump-record (arg &optional relative non-interactive)
  1830.   "Jump to a given record."
  1831.   (interactive "NRecord number: ")
  1832.   ;; verify that the record number is within range
  1833.   ;; for old-version, see forms.02.el
  1834.   (if (or (> arg forms--total-records) (<= arg 0))
  1835.       (if non-interactive
  1836.           nil ;return nil
  1837.          (progn (beep)
  1838.              (message "Record number %d out of range 1..%d"
  1839.                       arg forms--total-records)))
  1840.     ;; this could be made smarter and faster...
  1841.     (goto-char (point-min))
  1842.     (if (search-forward forms-record-separator nil nil arg)
  1843.         (progn (recenter 1)
  1844.                (forms--set-mode-line)
  1845.                t))))
  1846.  
  1847. (defun forms-beginning-of-form (arg)
  1848.   "Moves point to beginning of current form."
  1849.   (interactive "p")
  1850.  (let ((there (point)))
  1851.   (forward-line 1)
  1852.   (search-backward forms-record-separator nil nil arg)
  1853.   (beginning-of-line)
  1854.   (if (and (= there (point))
  1855.            (not (bobp)))
  1856.      (progn (forward-line -1) (forms-beginning-of-form 1)))
  1857.  ))
  1858.  
  1859. (defun forms-end-of-form (arg)
  1860.   "Moves point to end of current form.  "
  1861.   (interactive "p")
  1862.  (let ((there (point)))
  1863.   (end-of-line)
  1864.   (if (search-forward forms-record-separator nil t arg)
  1865.       (progn (forward-line -2)
  1866.          (end-of-line)
  1867.          (if (and (= there (point)) (not (eobp)))
  1868.              (progn (forward-line 1) (forms-end-of-form 1))))
  1869.       (end-of-buffer)
  1870.       ;; records always have a trailing CR
  1871.       (forward-char -1))  ))
  1872.  
  1873.  
  1874. (defun forms-first-record ()
  1875.   "Jump to first record."
  1876.   (interactive)
  1877.   (forms-jump-record 1))
  1878.  
  1879. (defun forms-last-record ()
  1880.   "Jump to last record. As a side effect: re-calculates the number
  1881.  of records in the data file."
  1882.   (interactive)
  1883.   (let ((numrec (save-excursion
  1884.                   (set-buffer forms--file-buffer)
  1885.                   (count-lines (point-min) (point-max)))))
  1886.     (if (= numrec forms--total-records)
  1887.     nil
  1888.       (beep)
  1889.       (setq forms--total-records numrec)
  1890.       (message "Number of records reset to %d." forms--total-records)))
  1891.   (forms-jump-record forms--total-records))
  1892.  
  1893.  
  1894. (defun forms-prev-field (arg)
  1895.   "Jump to ARG-th previous field."
  1896.   (interactive "p")
  1897.   (forms-next-field (- arg)))
  1898.  
  1899. ;(defun forms-next-field (arg)
  1900. ;  "Jump to ARG-th next field."
  1901. ;  (interactive "p")
  1902. ;  (let ((i 0)   (cnt 0)
  1903. ;        (here   (point))
  1904. ;        there)
  1905. ;    (if (zerop arg)
  1906. ;        (setq cnt 1)
  1907. ;      (setq cnt (+ cnt arg)))
  1908. ;    (if (catch 'done
  1909. ;          (while (< i forms--number-of-markers)
  1910. ;            (if (or (null (setq there (aref forms--markers i)))
  1911. ;                    (<= there here))
  1912. ;                nil
  1913. ;              (if (<= (setq cnt (1- cnt)) 0)
  1914. ;                  (progn
  1915. ;                    (goto-char there)
  1916. ;                    (throw 'done t))))
  1917. ;            (setq i (1+ i))))
  1918. ;        nil
  1919. ;      (goto-char (aref forms--markers 0)))))
  1920.  
  1921. (defun forms-next-field (arg)
  1922.   "Jump to ARG-th next field."
  1923.   (interactive "p")
  1924.   (forms--find-row-and-column)
  1925.   (let ((signp (if (< 0 arg) 1 -1))
  1926.         (bwritable (if (bobp) nil
  1927.                        (save-excursion (forward-char -1)
  1928.                                    (funcall forms--char-writable-function)))))
  1929.     ;(message "going %s with sign %s bw %s" arg signp bwritable)
  1930.     (cond ((plusp signp)
  1931.            (forms--next-unwritable signp)
  1932.            (forms--next-writable signp))
  1933.           ((and bwritable (minusp signp))
  1934.            (forward-char -1)
  1935.            (forms--next-unwritable signp)
  1936.            (forward-char 1))
  1937.           ((minusp signp)
  1938.            (forms--next-unwritable signp)
  1939.            (forms--next-writable signp)
  1940.            (forms--next-unwritable signp)
  1941.            (forward-char 1)))
  1942.     (cond ((or (= arg 1) (= arg -1)))
  1943.           (t (forms-next-field (- arg signp))))    ))
  1944.  
  1945. (defun forms--next-unwritable (arg)
  1946.   (while (funcall forms--char-writable-function)
  1947.     (forward-char arg)))
  1948.  
  1949. (defun forms--next-writable (arg)
  1950.   (while (not (funcall forms--char-writable-function))
  1951.     (forward-char arg)))
  1952.  
  1953. (defun forms-open-line (arg)
  1954.  "Insert an empty line after the point if you are in the variable
  1955. length field region of a record."
  1956.  (interactive "p")
  1957.  (forms--find-row-and-column)
  1958.  (if (or (and (funcall forms--char-writable-function)     ;standard test
  1959.               forms--first-advanced-row
  1960.               (>= forms--row forms--first-advanced-row))
  1961.          (and (= 0 (current-column))                      ;bolp in safe region
  1962.               forms--first-advanced-row
  1963.               (> forms--row forms--first-advanced-row)))
  1964.      (open-line arg)
  1965.    (error "Can't insert a newline in the middle of labels.")))
  1966.  
  1967. (defun forms-quoted-insert ()
  1968.   "Insert a quoted char only after querying the user, insertion may 
  1969. mess up the display so that it is unparsable."
  1970.   (interactive)
  1971.   (if (y-or-n-p "Are you sure you want to insert a raw character? ")
  1972.       (progn (message "Character to insert: ")
  1973.       (call-interactively 'quoted-insert))))
  1974.  
  1975.  
  1976. ;;;
  1977. ;;;    IX.    Changed saving functions
  1978. ;;;
  1979.  
  1980. (defun forms-revert-buffer (&optional ask-auto-save ask-confirm)
  1981.   (interactive "p")
  1982.   (let ((revert-buffer-function nil)
  1983.         (old-current-record forms--current-record)
  1984.         (buffer-file-name forms-file))
  1985.     (revert-buffer ask-auto-save ask-confirm)
  1986.     (set-buffer-modified-p nil)
  1987.     (forms-jump-record old-current-record))
  1988.   (forms--set-mode-line))
  1989.  
  1990. (defun forms-save-buffer (&optional arg)
  1991.   (interactive "p")
  1992.   (forms--update-all)
  1993.   (save-excursion
  1994.     (if forms--all-in-one
  1995.         (progn 
  1996.         (let ((file-name forms-file) )
  1997.           (set-buffer forms--file-buffer)
  1998.           (write-file file-name)
  1999.           (setq buffer-file-name nil)
  2000.           (setq buffer-auto-save-file-name
  2001.                 (concat file-name "~"))  ))
  2002.       (progn
  2003.           (set-buffer forms--file-buffer)
  2004.           (save-buffer arg)))  )
  2005.   (set-buffer-modified-p nil)  
  2006.   (forms--set-mode-line))
  2007.  
  2008. (defun forms-write-buffer (&optional arg)
  2009.  "Write out a forms buffer, prompting for FILENAME."
  2010.   (interactive "F")
  2011.   (save-excursion
  2012.     (if forms--all-in-one
  2013.         (progn
  2014.           (set-buffer forms--file-buffer)
  2015.           (let ((old-name (buffer-name)))
  2016.            ;; this has to be a write to a file
  2017.            (write-file arg)
  2018.            (rename-buffer old-name)
  2019.            (setq buffer-auto-save-file-name
  2020.                  (concat forms-file "~"))
  2021.            (setq buffer-file-name nil) ))
  2022.       (progn (set-buffer forms--file-buffer)
  2023.              (write-file arg))  ))
  2024.   (setq forms-file arg)
  2025.   (rename-buffer (file-name-nondirectory forms-file))
  2026.   (forms--set-mode-line)    )
  2027.  
  2028. (defun forms--exit (query &optional save)
  2029.  "Exit forms-mode and delete a buffer.  If QUERY is t,
  2030. ask user to save iff buffer is modified.  Iff save is t, also ask."
  2031.  (catch 'forms--exit
  2032.  (let ((backup-buf (buffer-name forms--file-buffer))
  2033.        (b-name (buffer-name (current-buffer))) )
  2034.    (if (and query  (not (buffer-modified-p forms--file-buffer))
  2035.             (not (y-or-n-p
  2036.                     (format "Do you really want to kill forms-mode file %s? "
  2037.                             b-name))))
  2038.        (error "Ok, going back to forms-mode."))
  2039.    (if (and query (or (buffer-modified-p forms--file-buffer)
  2040.                       (buffer-modified-p nil)))
  2041.        (if (not (yes-or-no-p
  2042.                  (format "Forms-mode buffer %s modified; kill anyway? "                          b-name)))
  2043.            (throw 'forms--exit nil)))
  2044.    (forms--kill-buffer forms--file-buffer)
  2045.    (forms--kill-buffer forms--commands-buffer)
  2046.    (forms--kill-buffer forms--comments-buffer)
  2047.    (forms--kill-buffer (current-buffer))
  2048.    (message " "))))
  2049.  
  2050. (defun forms--kill-buffer (abuffer)
  2051.   (if (bufferp abuffer)
  2052.       (save-excursion (set-buffer abuffer)
  2053.                       (delete-auto-save-file-if-necessary)
  2054.                       (kill-buffer abuffer))))
  2055.  
  2056. (defun forms-find-file (fn)
  2057.   "Visit file FN in forms mode."
  2058.   (interactive "fForms file: ")
  2059.   (find-file-read-only fn)
  2060.   (or forms--mode-setup (forms-mode t t)))
  2061.  
  2062. (defun forms-find-file-other-window (fn)
  2063.   "Visit file FN in form mode in other window"
  2064.   (interactive "fFbrowse file in other window: ")
  2065.   (find-file-other-window fn)
  2066.   (eval-current-buffer)
  2067.   (or forms--mode-setup (forms-mode t t)))
  2068.  
  2069. (defun forms-exit (no-query)
  2070.   "Normal exit. Modified buffers are automatically saved if NO-QUERY is t.
  2071. Otherwise user is asked."
  2072.   (interactive "P")
  2073.   (forms--exit (not no-query) t))
  2074.  
  2075. (defun forms-exit-no-save (no-query)
  2076.   "Exit without saving buffers.  Modified buffers are not saved if NO-QUERY
  2077. is t. Otherwise user is asked."
  2078.   (interactive "P")
  2079.   (forms--exit (not no-query) nil))
  2080.  
  2081.  
  2082. ;;;
  2083. ;;;    X.    Translation functions between Excel and Forms
  2084. ;;;
  2085.  
  2086. (defun forms-excel-to-forms-format (old-char new-char)
  2087.  "Changes the current buffer from an Excel file saved as text only,
  2088. to a file ready to be read into forms-mode."
  2089.  (interactive "cOLD forms-field-sep character (taken raw): 
  2090. cNEW forms-field-sep character (taken raw): ")
  2091.  (let ((old-mode-line-format mode-line-format)  comment-end  comment-start)
  2092.  (unwind-protect
  2093.   (progn (goto-char (point-min))
  2094.     (insert ";; -*- mode: forms -*-\n\n")
  2095.     (setq forms-field-sep (char-to-string new-char))
  2096.     (insert (format "(setq forms-field-sep \"%s\")\n\n"
  2097.                     (char-to-string new-char)))
  2098.     (insert-file (concat forms-load-path "/forms--example-header"))
  2099.     (exchange-point-and-mark)
  2100.     (insert forms-header-stop)
  2101.     (setq comment-start (point))
  2102.     (message (setq mode-line-format (substitute-command-keys
  2103. "Move point to very first char after comments, then type \\[exit-recursive-edit]")))
  2104.     (recursive-edit)      
  2105.     (setq comment-end (point))
  2106.     (subst-char-in-region comment-start comment-end old-char ?  t)
  2107.     (insert forms-comment-header-stop)
  2108.     (insert "\n")
  2109.     (message "Replacing tabs with %s" new-char)
  2110.     (subst-char-in-region comment-end (point-max) old-char new-char t)
  2111.     (message "Finished with translation to Soar/PA mode format."))
  2112.   (setq mode-line-format old-mode-line-format))  ))
  2113.  
  2114. (defun forms-record-lengths ()
  2115.   "Count the record length of the currently exposed region. (It assumes 
  2116. you have narrowed to only the record region, or are working with a raw file."
  2117.   (interactive)
  2118.   (setq forms-frl-results nil)
  2119.   (forms-do-to-all-raw-records "Counting fields"
  2120.       (push (forms-count-fields) forms-frl-results))
  2121.   (setq forms-frl-results (nreverse forms-frl-results))
  2122.   (if (= (apply 'min forms-frl-results)
  2123.          (apply 'max forms-frl-results))
  2124.       (message "All records %s long." (first forms-frl-results))
  2125.     (message 
  2126.        "Not all records %s long.  From %s to %s.  Check forms-frl-results."
  2127.         (first forms-frl-results) (apply 'min forms-frl-results) 
  2128.         (apply 'max forms-frl-results))))
  2129.  
  2130. (defun forms-trim-records (n)
  2131.   "Trim a line in forms back buffer format to be N fields long."
  2132.   (interactive "P")
  2133.   (if (not n)
  2134.       (setq n
  2135.             (read-from-minibuffer "Trim to field number: "
  2136.                 (format "%s" forms-number-of-fields) nil t)))
  2137.   (setq forms-number-of-fields n)
  2138.   (save-excursion
  2139.   (let ((eol (save-excursion (end-of-line) (point))) )
  2140.     (beginning-of-line)
  2141.     (if (not (search-forward forms-field-sep eol t n))
  2142.         (progn (forward-line)
  2143.                (message "No trimming happened on this well shaped line."))
  2144.     (forward-char (- (length forms-field-sep)))
  2145.     (delete-region (point) eol))))
  2146.   (forward-line))
  2147.  
  2148.  
  2149.  
  2150. ;;;
  2151. ;;;    XI.    forms-self-insert & character insertion setup functions
  2152. ;;;
  2153.  
  2154. (defun forms-newline (arg)
  2155.   "Insert ARG newlines if you are in the variable length records and
  2156. point is not over a label, else move down a line."
  2157.   (interactive "p")
  2158.   (forms--find-row-and-column)
  2159.   (if (and forms--first-advanced-row
  2160.            (< forms--row forms--first-advanced-row))
  2161.       (forward-line arg)
  2162.     (if (funcall forms--char-writable-function)
  2163.         (newline arg)
  2164.       (forward-line arg))  ))
  2165.  
  2166. (defun forms-self-insert (arg)
  2167.   "Insert this character in place of character previously at the cursor, if
  2168. it's ok.  The cursor then moves in the direction you previously specified
  2169. with the commands picture-movement-right, picture-movement-up, etc.
  2170. Do \\[command-apropos]  picture-movement  to see those commands."
  2171.   (interactive "p")
  2172.   (if (funcall forms--char-writable-function)
  2173.       (while (> arg 0)
  2174.         (forms--char-insert last-input-char)
  2175.         (setq arg (- arg 1)) )
  2176.     (progn (forward-char) (beep))
  2177.            ;(error "In a label or protected field.")
  2178.   ))
  2179.  
  2180. (defun forms--char-insert (input-char)
  2181.   (if (char-equal last-input-char forms--field-sep-char)
  2182.       (error "You should not insert the forms-field-sep character: %s."
  2183.              forms-field-sep))
  2184.   (if (or (and forms--first-advanced-row 
  2185.                (> forms--row forms--first-advanced-row))
  2186.           (and forms--first-advanced-row
  2187.                (= forms--row forms--first-advanced-row)
  2188.                (>= forms--column forms--first-advanced-column)))
  2189.       (insert input-char)
  2190.     (move-to-column-force (1+ (current-column)))
  2191.     (delete-char -1)
  2192.     (insert input-char)
  2193.     (forward-char -1)
  2194.     (picture-move)))
  2195.  
  2196. (defun forms-kill-line (arg)
  2197.   "Clear out rest of line if characters are writable;
  2198. if not ok, take what you can get.  If at end of line, remove the newline.
  2199. Cleared-out text goes into the kill ring, as do newlines that are advanced 
  2200. over.  With argument, clear out (and save in kill ring) that many lines."
  2201.   (interactive "P")
  2202.   (let (max-kill)
  2203.   (save-excursion
  2204.   (if arg
  2205.       (progn (setq arg (prefix-numeric-value arg))
  2206.              (if (setq max-kill
  2207.                        (forms--char-writable-region (point)
  2208.                            (save-excursion (forward-line arg) (point))))
  2209.                  (kill-region (point) max-kill)))
  2210.     ;; else no arg
  2211.     (if (looking-at "[ \t]*$")  ;blank line
  2212.         (if (setq max-kill
  2213.                   (forms--char-writable-region (point)
  2214.                       (save-excursion (forward-line 1) (point))))
  2215.             (progn ; check for label on following line
  2216.               (if (not (forms--char-writable-region max-kill (1+ max-kill)))
  2217.                   (setq max-kill (- max-kill 1)))
  2218.               (kill-region (point) max-kill))
  2219.           (error "Can't delete labels."))
  2220.       ;; else not at end of line
  2221.       (if (setq max-kill (forms--char-writable-region (point)
  2222.                               (save-excursion (end-of-line) (point))))
  2223.           (progn (kill-region (point) max-kill)
  2224.               (if (not (= max-kill (- (point)
  2225.                                       (save-excursion (end-of-line) (point)))))
  2226.                  (insert-n-times " " (- max-kill (point)))))
  2227.         (error "Can't delete labels.")))
  2228.     t))))
  2229.  
  2230. (defun forms--char-writable-region (start end)
  2231.   ;; return max point, of chars that are writable
  2232.   ;; this is directed unlike most region commands
  2233.   (save-excursion
  2234.   (let ((increment (if (> start end) -1 1)))
  2235.     (if (> start end) (setq start (- start 1)))
  2236.     (goto-char start)
  2237.     (while (and (not (= (point) end))
  2238.                 (funcall forms--char-writable-function))
  2239.       (forward-char increment))
  2240.     (if (= (point) start)
  2241.         nil
  2242.       (if (not (funcall forms--char-writable-function))
  2243.           (if (looking-at "^")  ;new lines are verboten if not writable
  2244.               (- (point) increment)
  2245.             (point))
  2246.         (point))      ))))
  2247.  
  2248. (defun forms-backward-clear-column (arg)
  2249.  "Clear out ARG columns before point, moving back over them."
  2250.  ;; evolved from clear-column
  2251.  (interactive "p")
  2252.  (let* ((increment (if (> arg 0) -1 1))
  2253.         (cc (current-column))
  2254.         (target-col (+ cc (- arg))))
  2255.  (cond ((and (= cc 0) (not (funcall forms--char-writable-function)))
  2256.         (if (save-excursion (forward-char -1)
  2257.                (and (= 0 (current-column))
  2258.                     (funcall forms--char-writable-function)))
  2259.             (delete-char -1)
  2260.           (forward-char -1)
  2261.           (error "Can't delete a leading label.")) )
  2262.         ((and (= cc 0) (funcall forms--char-writable-function))
  2263.          (delete-char -1))
  2264.         (t ;(message "increment %d" increment) (sit-for 2)
  2265.            (while (if (> 0 increment)
  2266.                       (not (<= (current-column) target-col))
  2267.                       (not (>= (current-column) target-col)))
  2268.            (if (> 0 increment) (forward-char -1))
  2269.            (if (not (funcall forms--char-writable-function))
  2270.                (error "Deleting not allowed in a label, column %d." 
  2271.                       (current-column))
  2272.             (delete-char 1)
  2273.             (forms--find-row-and-column)
  2274.             (if (and forms--first-advanced-row
  2275.                      (>= forms--row forms--first-advanced-row)
  2276.                      (> forms--column forms--first-advanced-column))
  2277.                 (if (not (eobp)) (forward-char 1))
  2278.                 (insert " "))
  2279.             (if (and (not (eobp)) (> 0 increment)) (forward-char -1)) )) ))))
  2280.  
  2281.  
  2282. (defun forms-clear-column (arg)
  2283.   "Clear out ARG columns after point without moving."
  2284.   (interactive "p")
  2285.   (save-excursion
  2286.    (let* ((increment (if (> arg 0) 1 -1))
  2287.           (cc (current-column))
  2288.           (target-col (+ cc arg)))
  2289.     (cond ( (and (= cc 0) (not (funcall forms--char-writable-function)))
  2290.             (if (save-excursion (forward-char -1) 
  2291.                    (and (= 0 (current-column))
  2292.                         (funcall forms--char-writable-function)))
  2293.                 (delete-char -1)
  2294.               (progn (forward-char -1)
  2295.                      (error "Can't delete a label.")) ))
  2296.           ( (and (= cc 0) (funcall forms--char-writable-function))
  2297.             (delete-char -1))
  2298.         (t (setq cc (current-column))
  2299.      (while (not (= cc target-col))
  2300.         (if (> 0 increment)
  2301.             (forward-char -1))
  2302.         (if (not (funcall forms--char-writable-function))
  2303.             (error "Deleting not allowed in a label, column %d."
  2304.                    (current-column))
  2305.          (delete-char 1)
  2306.          (if (and forms--first-advanced-row
  2307.                   (< forms--row forms--first-advanced-row))
  2308.              (insert " "))
  2309.         (if (> 0 increment)
  2310.             (forward-char -1)) )
  2311.         (setq cc (+ cc increment)) )        )))))
  2312.  
  2313.       ;; count-lines seem wrong here, often returning 1 when on the
  2314.       ;; same line
  2315.       ;(message "got %s for %d" forms--row-and-column here)
  2316. (defun forms--find-row-and-column ()
  2317.   (save-excursion
  2318.     (setq forms--column (current-column))
  2319.     (let* ((here (if (eobp)
  2320.                      (point)
  2321.                      (+ 1 (point))))
  2322.            (start (progn (forward-line) ;in case you're on it
  2323.                          (search-backward forms-record-separator nil t)
  2324.                          (beginning-of-line)
  2325.                          (point))))
  2326.       (setq forms--row (if (= 2 here)
  2327.                            1
  2328.                          (count-lines start here)))
  2329.   )))
  2330.  
  2331. ;(forms--find-current-record-number)
  2332. (defun forms--find-current-record-number ()
  2333.  (save-excursion
  2334.    (let* ( (start (progn (forward-line 1)
  2335.                          (search-backward forms-record-separator nil t)
  2336.                          (beginning-of-line)
  2337.                          (point)))
  2338.            (end (progn (search-forward " " nil t) (point))) )
  2339.     (setq forms--current-record
  2340.           (car (read-from-string (buffer-substring start end)))))))
  2341.  
  2342. (defun forms-yank (arg)
  2343.  "Doesn't use ARG, as with real yank, yet anyhow....should be:
  2344. With just c-u as argument, same but put point in front (and mark at end).
  2345. With argument n, reinsert the nth most recently killed stretch of killed text."
  2346.   (interactive "p")
  2347.   (if (not (funcall forms--char-writable-function))
  2348.       (error "Can't yank here."))
  2349.   (push-mark (point))
  2350.   (setq kill-ring-yank-pointer kill-ring)
  2351.   (forms--yank-inserter (car kill-ring-yank-pointer)))
  2352.  
  2353. (defun forms--yank-inserter (astring)
  2354.   (let  ((i 0)
  2355.          (string-size (length astring))  )
  2356.     (while (and (< i string-size)
  2357.                 (funcall forms--char-writable-function))
  2358.       (forms--char-insert (substring astring i (+ 1 i)))
  2359.       (setq i (1+ i))      )))
  2360.  
  2361. (defun forms-yank-pop ()
  2362. "Replace just-yanked stretch of killed-text with a different stretch.
  2363. This command is allowed only immediately after a  forms-yank or a forms-yank-pop.
  2364. At such a time, the region contains a stretch of reinserted
  2365. previously-killed text.  yank-pop  deletes that text and inserts in its
  2366. place a different stretch of killed text.
  2367.  
  2368. With no argument, the previous kill is inserted.
  2369. With argument n, the n'th previous kill is inserted.
  2370. If n is negative, this is a more recent kill.
  2371.  
  2372. The sequence of kills wraps around, so that after the oldest one
  2373. comes the newest one."
  2374.   (interactive)
  2375.   (if (not (or (eq last-command 'forms-yank-pop)
  2376.                (eq last-command 'forms-yank)))
  2377.       (error "Previous command was not a forms-yank")
  2378.     (delete-region (point) (mark))
  2379.     (setq kill-ring-yank-pointer (cdr kill-ring-yank-pointer))
  2380.     (forms--yank-inserter (car kill-ring-yank-pointer))) 
  2381. )
  2382.  
  2383. (defun forms-kill-region (start end)
  2384.   "Kill between point and mark preserving labels.
  2385. This is the primitive for forms programs to kill text (as opposed to deleting
  2386. it).  See kill-region (which this is modeled on) for more information."
  2387.   (interactive "r")
  2388.   (let (max-kill)
  2389.   (save-excursion
  2390.      (forms--find-row-and-column)
  2391.      (setq max-kill (forms--char-writable-region start end))
  2392.      (if max-kill
  2393.          (kill-region start max-kill))
  2394.      (if (and forms--first-advanced-row
  2395.               (< forms--row forms--first-advanced-row))
  2396.          (progn (goto-char start)
  2397.                 (insert-n-times " " (- max-kill start)))))))
  2398.  
  2399.  
  2400.         ;(forms--make-char-insertable-function)
  2401. (defun forms--make-char-insertable-function ()
  2402.   "Generate predicate for character insertion."
  2403.   (setq forms--advanced-column-keywords nil)
  2404.   (setq forms--first-advanced-row nil)
  2405.   (setq forms--first-advanced-column nil)
  2406.   (setq forms--char-writable-function 
  2407.         (forms--char-writable-maker forms-format-list))
  2408.   (setq forms--maxlength-advanced-column-keywords
  2409.       (apply 'forms--max (mapcar 'length forms--advanced-column-keywords))) )
  2410.  
  2411. (defun forms--char-writable-maker (the--format-list)
  2412.  "Returns the parser function for forms."
  2413.  (let ((the-dynamic-text forms--dynamic-text)
  2414.        char-insert-and-list
  2415.        (row 2) (column 0) )
  2416.    (` (lambda nil
  2417.         (let (here)
  2418.          (forms--find-row-and-column)
  2419.          (or (,@ (apply 'append
  2420.                         (mapcar 'forms--make-char-insertable-elt
  2421.                                 the--format-list)))
  2422.              (, (if char-insert-and-list
  2423.                     (` (and (,@ char-insert-and-list)))
  2424.                     nil))
  2425. ;             (and (, (if char-insert-and-list t nil))
  2426. ;                  (,@ char-insert-and-list))
  2427.           )) ))))
  2428.            
  2429. ;; will have to be called after a field has changed visibility
  2430. (defun forms--make-char-insertable-elt (item-list)
  2431.   ;; we also use the dynamic bindings of: the-dynamic-text
  2432.   ;; and char-insert-and-list, row, column
  2433.  (let ((el (format-item-field item-list))
  2434.        (name (format-item-name item-list))       
  2435.        (label (format-item-label item-list))
  2436.        (field-size (format-item-field-size item-list))
  2437.        (newline-p (format-item-newline-p item-list))
  2438.        (line-size (format-item-line-size item-list))
  2439.        old-column)
  2440.    ;(message "in make-elt with %s and el %s" name el) (sit-for 1)
  2441.   (if (and forms--first-advanced-row (string= "" label))
  2442.       (error "Can't have a null label in advanced rows, name is %s" name))
  2443.   (if (forms--item-list-visible-p name)
  2444.   (cond
  2445.    ((or (stringp el) (null el))
  2446.     (if forms--first-advanced-row
  2447.         (error "Error in field %s. No fixed fields after line-size=t" name))
  2448.     (if newline-p
  2449.         (progn (setq column 0)
  2450.                (setq row (+ row (forms--t-or-number-to-number newline-p)))))
  2451.     (if label (setq column (+ column (length label))))
  2452.     (cond ((null el))
  2453.           ((stringp el)
  2454.            (if (null field-size)
  2455.                (setq column (+ column (length el)))
  2456.              (setq column (+ column field-size))))
  2457.           (t (setq column (+ column field-size))) )
  2458.     nil)
  2459.    ((numberp el)
  2460.     (if newline-p (progn (setq column 0)
  2461.                  (setq row (+ row (forms--t-or-number-to-number newline-p)))))
  2462.     (if label (setq column (+ column (length label))))
  2463.     (setq old-column column)
  2464.     (if (and (eq t line-size) (not forms--first-advanced-row))
  2465.         (progn (setq forms--first-advanced-row row)
  2466.                (setq forms--first-advanced-column column)))
  2467.         ;; if you are the 1st advanced row or if not in advanced rows yet
  2468.     (if (numberp field-size) (setq column (+ column field-size)))
  2469.     (cond ( (or (eq forms--first-advanced-row row)
  2470.                 (not forms--first-advanced-row))  ;spot21
  2471.             (cond ((or (not line-size)
  2472.                        (and (numberp line-size) (= 1 line-size)))
  2473.                    (if (eq field-size t)
  2474.                        (` ((and (= forms--row (, row))
  2475.                                 (>= forms--column (, old-column)))))
  2476.                      (` ((and (= forms--row (, row))
  2477.                               (>= forms--column (, old-column))
  2478.                               (< forms--column (, column)))))))
  2479.                   ((eq line-size t)
  2480.                    (push label forms--advanced-column-keywords)
  2481.                    (push (` (forms--char-insert-not-in-label (, label) 
  2482.                                                              (, row)))
  2483.                          char-insert-and-list)
  2484.                    (` ((and (= forms--row (, row))
  2485.                             (>= forms--column (, old-column))))))
  2486.                   ((and (numberp line-size) (> line-size 1))
  2487.                    (` ((or (and (= forms--row (, row))
  2488.                                 (>= forms--column (, old-column)))
  2489.                            (,@ (forms--make-ok-rows row (- line-size 1))))))
  2490.                    )))
  2491.           ( forms--first-advanced-row
  2492.             (push label forms--advanced-column-keywords)
  2493.             (push (` (forms--char-insert-not-in-label (, label) (, row)))
  2494.                   char-insert-and-list)
  2495.             nil)    ))
  2496.    ((listp el)
  2497.     (if forms--first-advanced-row
  2498.         (error "Error in field %s. No fixed fields after line-size=t" name))
  2499.     (if newline-p
  2500.         (progn (setq column 0)
  2501.            (setq row (+ row (forms--t-or-number-to-number newline-p)))))
  2502.     (if label (setq column (+ column (length label))))
  2503.     (setq column (+ column (length (pop the-dynamic-text))))
  2504.     nil)   ))))
  2505.  
  2506.     ;(push (list 'first-row forms--first-advanced-row
  2507.      ;           'row row 'line-size line-size 'forms--row forms--row
  2508.       ;          'forms--col forms--column 'old-col old-column 
  2509.        ;         'field-size field-size)
  2510.         ;  aa)
  2511.  
  2512. ;; I believe this is dead, wrong code from spot21 above
  2513. ;(and (not forms--first-advanced-row)
  2514. ;                     (or (not line-size)
  2515. ;                         (and (numberp line-size)
  2516. ;                              (= line-size 1) )))
  2517.  
  2518. (defun forms--char-insert-not-in-label (the-string first-row)
  2519.   ;; has two fast exits, then real test
  2520.   (and (and forms--first-advanced-row 
  2521.             (> forms--row forms--first-advanced-row))
  2522.        (if (>= forms--row first-row)
  2523.            (if (> forms--column forms--maxlength-advanced-column-keywords)
  2524.                t
  2525.              (save-excursion
  2526.                (beginning-of-line)
  2527.                (if (and (looking-at the-string)
  2528.                         (< forms--column (length the-string)))
  2529.                    nil
  2530.                  t)))
  2531.          t)))
  2532.  
  2533. (defun forms--t-or-number-to-number (item)
  2534.   (if (numberp item)  item  1))
  2535.  
  2536. (defun forms--make-ok-rows (row counter)
  2537.   (let ((aresults nil))
  2538.     (while (> counter 0)
  2539.       (push (list '= 'forms--row (+ row counter)) aresults)
  2540.       (setq counter (- counter 1)))
  2541.     aresults))
  2542.  
  2543.  
  2544.  
  2545. ;;;
  2546. ;;;    XII.    Report functions
  2547. ;;;
  2548.  
  2549. (defun forms-make-report (&optional rbuffer)
  2550.  "Print to RBUFFER a plain file all the visible fields of all the visible 
  2551. records.  Must be called from a forms-file-buffer."
  2552.   (interactive)
  2553.   ;; set variables you need to use while in other buffer
  2554.   (let ((current-buffer (current-buffer))
  2555.         (current-buffer-file forms-file)
  2556.         (report-buffer (or rbuffer   
  2557.                            (if (and (boundp 'report-display-buffer)
  2558.                                     report-display-buffer)
  2559.                                report-display-buffer)
  2560.                            (get-buffer-create "*Forms-Report*"))) )
  2561.     (pop-to-buffer report-buffer)
  2562.     (erase-buffer)
  2563.     (forms--report-header current-buffer-file)
  2564.     (insert-buffer current-buffer)
  2565.     (set-buffer-modified-p nil)
  2566.     (goto-char (point-min))
  2567.     (message "Type C-x C-w to save this file.")))
  2568.  
  2569. (defun forms--draw-all-records (draw-buffer)
  2570.   ;; assumes it starts in a forms-mode buffer
  2571.   (let ( (i 1)
  2572.          (file-buffer forms--file-buffer)
  2573.          (f-format forms--format)
  2574.          (seperator forms-field-sep)  
  2575.          (f-file forms-file)
  2576.          (total forms--total-records)
  2577.          (nof forms-number-of-fields) )
  2578.     (set-buffer draw-buffer)
  2579.     (setq forms--format f-format)
  2580.     (setq forms-field-sep seperator)
  2581.     (setq forms-number-of-fields nof)
  2582.     (setq forms-file f-file)
  2583.     (while (<= i total)
  2584.       (message "Drawing %s of %s" i total)
  2585.       (setq forms--current-record i) ;; used by insert-reco
  2586.       (forms--insert-record
  2587.        (save-excursion
  2588.           (set-buffer file-buffer)
  2589.           (forms--goto-line i)
  2590.           (forms--get-record)))
  2591.       (setq i (1+ i)) )))
  2592.  
  2593. (defun forms--report-header (forms-file)
  2594.   (insert-current-time-string)
  2595.   (insert (format " - Forms (%s) report for user " forms-version))
  2596.   (insert (getenv "USER") "\n")
  2597.   (insert "For file " forms-file "\n\n")
  2598.   (insert "-------------------------------------------------------------\n"))
  2599.  
  2600.  
  2601. ;; make these be local later
  2602. (defvar forms--tally-fields-results nil
  2603.   "Where the forms--tally-fields-results are stored.")
  2604. (defvar  forms-report-buffer-name "*Forms-Report*"
  2605.   "Name of buffer where reports go.")
  2606.  
  2607.  
  2608. (defun forms-tally-fields (&optional field-number-or-name rbuffer)
  2609.   "Count the types in FIELD-NUMBER-OR-NAME (removing leading and trailing
  2610. spaces first), and print them and their counts out in RBUFFER."
  2611.   (interactive "P")
  2612.   (if (and (interactive-p)
  2613.            (buffer-modified-p)
  2614.            (y-or-n-p "Would you like to checkpoint first?"))
  2615.       (forms--update-all))
  2616.   (forms--set-mode-line)
  2617.   ;; rbuffer gets set to passed in arg, report-display-buffer if in a report
  2618.   ;; calling command, or makes its own
  2619.   (let ((report-buffer (or rbuffer
  2620.                            (if (and (boundp 'report-display-buffer)
  2621.                                     report-display-buffer)
  2622.                                report-display-buffer)
  2623.                            (get-buffer-create forms-report-buffer-name)))
  2624.         label counter (f-f forms-file)
  2625.         max-count-column max-field-column  (i 1)
  2626.         field-name)
  2627.     ;; set up the field-name and field-number, either passed a
  2628.     ;; numbered field, a name (so get the number), or have to query user
  2629.     (cond ((numberp field-number-or-name)
  2630.            (setq field-number field-number-or-name)
  2631.            (setq field-name (format " Number %d" field-number)))
  2632.           ((stringp field-number-or-name)
  2633.            (setq field-name field-number-or-name)
  2634.            (setq field-number (cdr (assoc forms--format-query-list)))
  2635.            (if (not field-number)
  2636.                (error "In forms-tally-fields with %s and %s"
  2637.                              field-number-or-name rbuffer)))
  2638.           ((not  field-number-or-name)
  2639.            (let ((pair (assoc (completing-read
  2640.                                 "Field to summarize (name with autocomplete): "
  2641.                                                forms--format-query-list nil t)
  2642.                               forms--format-query-list)))
  2643.              (setq field-number (cdr pair))
  2644.              (setq field-name (car pair))))
  2645.           (t (error "In forms-tally-fields with %s and %s"
  2646.                     field-number-or-name rbuffer)))
  2647.   (setq forms--tally-fields-results nil)
  2648.   ;; compute field
  2649.   (while (<= i forms--total-records)
  2650.     (setq label (forms--field i field-number))
  2651.     (message "Tallying %s of %s" i forms--total-records)
  2652.     (setq label (string-trim forms--blank-bag label))
  2653.     (setq counter (assoc label forms--tally-fields-results))
  2654.     (if counter
  2655.         nil
  2656.       (push (cons label 0) forms--tally-fields-results)
  2657.       (setq counter (first forms--tally-fields-results)))
  2658.     (rplacd counter (+ 1 (cdr counter)))
  2659.     (setq i (1+ i)))
  2660.   ;; sort the list to print it, high to low
  2661.   (setq forms--tally-fields-results
  2662.         (sort forms--tally-fields-results
  2663.               '(lambda (x y) (> (cdr x) (cdr y)))))
  2664.   (save-excursion
  2665.     (set-buffer report-buffer)
  2666.     (goto-char (point-max))
  2667.     (forms--report-header f-f)
  2668.     (setq max-field-column
  2669.           (apply 'forms--max (mapcar '(lambda (x) (length (car x)))
  2670.                                      forms--tally-fields-results)))
  2671.     (setq max-count-column
  2672.           (apply 'forms--max (mapcar '(lambda (x) (cdr x))
  2673.                                      forms--tally-fields-results)))
  2674.     (setq max-field-column (+ 3 max-field-column))
  2675.     (insert (format "Tallying field #%s (%s)\n\n" field-number field-name))
  2676.     (insert "VALUE")
  2677.     (setq max-field-column (max max-field-column 7)) ; room for value
  2678.     (move-to-column-force max-field-column)
  2679.     (insert "FREQUENCY\n")
  2680.    (mapcar '(lambda (x)
  2681.               (if (string= "" (car x))
  2682.                  (insert (format "\"\""))
  2683.                 (insert (format "%s   " (car x) )))
  2684.               (move-to-column-force max-field-column)
  2685.               (insert (format (format "%%%ss\n" (+ 1 (log10 max-count-column)))
  2686.                               (cdr x))))
  2687.             forms--tally-fields-results))
  2688.     (pop-to-buffer report-buffer)  ))
  2689.  
  2690. ;(popper-wrap forms-tally-fields forms-report-buffer-name)
  2691.  
  2692.  
  2693. ;;;
  2694. ;;;    XIII.    Changes to "normal" commands
  2695. ;;;
  2696.  
  2697. (defun forms-undo (amount)
  2698.   (interactive "p")
  2699.   (undo amount)
  2700.   (message "Undo %s!  But back buffer may have irreversable changes." amount))
  2701.  
  2702.  
  2703. ;;;
  2704. ;;;    XIV.    Changes to picture-mode commands
  2705. ;;;
  2706.  
  2707.  
  2708.  
  2709. ;;;
  2710. ;;;    XV.    Utility functions
  2711. ;;;
  2712.  
  2713. ;; this looks like a real gnu bug
  2714. (defun forms--re-search-forward (regexp bound on-fail repeat)
  2715.   (if (not (= 0 repeat))
  2716.      (re-search-forward regexp bound on-fail repeat)))
  2717.  
  2718. (defun forms-quick-help ()
  2719.   "Initial help."
  2720.   (interactive)
  2721.   ;; this sets up the values of forms-quick-help-* only after the keymaps are set up
  2722.   (if forms-read-only
  2723.       (message forms-quick-help-read-only)
  2724.          ;"SPC:next   DEL:prev   <:first   >:last   ?:help   q:exit"
  2725.     (message forms-quick-help-writable
  2726.      ;"C-c n:next  C-c p:prev  C-c <:first  C-c >:last  C-c ?:help  C-c q:exit"
  2727.          )
  2728.     (sit-for 1)))
  2729.  
  2730. (defun forms--max (&rest numbers)
  2731.   (if (and numbers (not (equal numbers '(nil))))
  2732.       (apply 'max numbers)
  2733.       0))
  2734.  
  2735. (defmacro forms-do-to-all-raw-records (label &rest body)
  2736.   ;; go to the start of the records, for either real files, or about to be ones
  2737.   (` 
  2738.    (let ( (i 1) 
  2739.           (remember-old-point (point)))
  2740.     (goto-char (point-min))
  2741.     (or (if (search-forward forms-comment-header-stop nil t)
  2742.             (progn (forward-char 1) t))
  2743.         (goto-char (point-min)))
  2744.     (while (not (eobp))
  2745.       (,@ body)
  2746.       (message "%s %s" (, label) i)
  2747.       (setq i (+ 1 i))
  2748.       (forward-line 1))
  2749.     (goto-char remember-old-point)      )))
  2750.  
  2751. ;(interactive "NField number to add: ")
  2752. (defun forms-add-field (field-num)
  2753.   (interactive 
  2754.   (let ((default (or forms-number-of-fields
  2755.                      (if forms--file-buffer
  2756.                          (save-excursion (set-buffer forms--file-buffer)
  2757.                             (forms--set-number-of-fields))
  2758.                          (save-excursion (forms--set-number-of-fields))))))
  2759.     (list (car (read-from-string (read-input
  2760.           (format "Add before field number [1 to %s(%s to add to end)]: "
  2761.                  default (+ 1 default)) ))))))
  2762.  "Add a field from all records in the current forms-mode buffer before 
  2763. current FIELD-NUM objects.  To insert a new first field, use 1; to 
  2764. append put one before 1 + forms-number-of-fields."
  2765.   (let (number-of-fields
  2766.         (switcharoo (or (eq major-mode 'forms-mode)
  2767.                         (eq major-mode 'spa-mode))) )
  2768.   (save-excursion
  2769.     (forms--do-field-mod-tests field-num 1 (+ 1 forms-number-of-fields))
  2770.     (setq number-of-fields  forms-number-of-fields)
  2771.     (if switcharoo (set-buffer forms--file-buffer))
  2772.   (forms-do-to-all-raw-records "Adding to"
  2773.     (cond ((= field-num 1)
  2774.            (beginning-of-line)
  2775.            (insert forms-field-sep)  )
  2776.           ((= field-num (+ 1 number-of-fields))
  2777.            (end-of-line)
  2778.            (insert forms-field-sep) )
  2779.           (t (forms--re-search-forward forms-field-sep nil nil (- field-num 1))
  2780.             (insert forms-field-sep) ))))
  2781.   (setq forms-number-of-fields (+ 1 forms-number-of-fields))
  2782.   (if switcharoo  (forms--set-mode-line))
  2783.   (message "Don't forget to update the format-list.")  ))
  2784.  
  2785. (defun forms-remove-field (field-num)
  2786.   "Remove a field from all records in the current forms-mode buffer."
  2787.   (interactive 
  2788.    (let ((default (or forms-number-of-fields
  2789.                       (if forms--file-buffer
  2790.                           (save-excursion (set-buffer forms--file-buffer)
  2791.                              (forms--set-number-of-fields))
  2792.                         (forms--set-number-of-fields)))))
  2793.      (list (car (read-from-string (read-input
  2794.      (format "Field number to remove [1 to %s]: " default) ))))))
  2795.   (let (number-of-fields 
  2796.         (switcharoo (or (eq major-mode 'forms-mode)
  2797.                         (eq major-mode 'spa-mode))))
  2798.   (save-excursion 
  2799.     (forms--do-field-mod-tests field-num 1 forms-number-of-fields)
  2800.     (setq number-of-fields forms-number-of-fields)
  2801.     (if switcharoo (set-buffer forms--file-buffer))
  2802.   (forms-do-to-all-raw-records "Removing from"
  2803.     (cond ((= field-num 1)
  2804.            (beginning-of-line)
  2805.            (delete-region (point)
  2806.               (save-excursion (re-search-forward forms-field-sep nil nil 1)
  2807.                 (point)))  )
  2808.           ((= field-num number-of-fields)
  2809.            (end-of-line)
  2810.            (delete-region (point)
  2811.               (save-excursion (re-search-backward forms-field-sep nil nil 1)
  2812.                 (point)))    )
  2813.           (t (forms--re-search-forward forms-field-sep nil nil (- field-num 1))
  2814.              (delete-region (point)
  2815.                 (save-excursion (re-search-forward forms-field-sep nil nil 1)
  2816.                   (point))) ))  ))
  2817.   (if switcharoo (forms--set-mode-line))
  2818.   ;; probably should reset format and parser here
  2819.   (setq forms-number-of-fields (- forms-number-of-fields 1))))
  2820.           
  2821. (defmacro forms--do-field-mod-tests (arg min max)
  2822.  (` 
  2823.   (progn
  2824.   (if (not forms-field-sep)
  2825.       (error "Need to set forms-field-sep variable for this buffer."))
  2826.   (if (not forms-number-of-fields) (forms--set-number-of-fields))
  2827.   (if (or (< (, arg) (, min))
  2828.           (if (, max)
  2829.               (> (, arg) (, max))))
  2830.       (if (not (y-or-n-p (format 
  2831.                            "Field-num %s out of range %s...%s, Do anyway?" 
  2832.                            (, arg) (, min) (, max))))
  2833.           (error "Field-num %s out of range %s...%s" (, arg) (, min) (, max)))
  2834. ))))
  2835.  
  2836. (defun forms--set-number-of-fields ()
  2837.   (save-excursion (goto-char (point-max))
  2838.      (forward-line -2)
  2839.      (setq forms-number-of-fields (forms-count-fields))))
  2840.  
  2841. (defun forms-count-fields ()
  2842.   (interactive)
  2843.   (save-excursion
  2844.      (beginning-of-line)
  2845.      (let ((fcf-result (+ 1 (count-regexp (or forms-field-sep "#")
  2846.                                 (save-excursion (end-of-line) (point))))))
  2847.        (if (interactive-p)
  2848.           (progn (message "%s fields found with %s seperator." fcf-result
  2849.                           (or forms-field-sep "#"))
  2850.                  fcf-result)
  2851.           fcf-result))))
  2852.  
  2853. (defun forms--renumber-buffer ()
  2854.   "Renumber each segment, from 1 to forms--total-records."
  2855.   (let ( (i 1) (renumber-old-point (point)))
  2856.     (goto-char (point-min))
  2857.     (while (<= i forms--total-records)
  2858.       (message "Renumbering %s of %d" i forms--total-records)
  2859.       (delete-region (point) (save-excursion (end-of-line) (+ 1 (point))))
  2860.       (forms--insert-line-number i)
  2861.       (setq i (+ 1 i))
  2862.       (if (<= i forms--total-records)
  2863.          (progn (search-forward forms-record-separator nil nil 1)
  2864.                 (forward-line -1))))
  2865.     (goto-char renumber-old-point)      ))
  2866.  
  2867. (defun forms--search-forward (astring arg2 arg3 arg4)
  2868.   (if (string= "" astring)
  2869.       t
  2870.     (search-forward astring arg2 arg3 arg4)))
  2871.  
  2872. (defun forms--set-mode-line ()
  2873.   "Update the mode line, particularly to show that the backup file is dirty."
  2874.   (forms--find-current-record-number)
  2875.   (setq mode-line-process
  2876.     (concat " " forms--current-record "/" forms--total-records
  2877.                 (if (buffer-modified-p forms--file-buffer) "*" ""))))
  2878.  
  2879. (defun forms-insert-blank-field (n)
  2880.  "Insert a blank field after N items."
  2881.  ;; need to take car of last field
  2882.  (beginning-of-line)
  2883.  (forms--re-search-forward forms-field-sep (point-max) t n)
  2884.  (insert forms-field-sep))
  2885.  
  2886. (defun forms--item-list-visible-p (name)
  2887.   "Return t if field NAME is visible."
  2888.   (let ((item (assoc name forms-format-list)) )
  2889.     (if item
  2890.         (format-item-visible-p item))))
  2891.  
  2892. (defun forms--next-visible-item (name)
  2893.   "Return next visible item after NAME."
  2894.   (let* ((items (cdr-assoc name forms-format-list))
  2895.          (next-item (car (cdr items))) )
  2896.     (if next-item
  2897.         (if (format-item-visible-p next-item)
  2898.             next-item
  2899.           (forms--next-visible-item (format-item-name next-item))))))
  2900.  
  2901. (defun cdr-assoc (name list)
  2902.   (cond ((null list) nil)
  2903.         ((equal name (car (car list)))
  2904.          list)
  2905.         (t (cdr-assoc name (cdr list)))))
  2906.  
  2907. (defun insert-n-times (item N)  ;(insert-n-times "a" t)
  2908.   "Insert ITEM (abs N) times."
  2909.   (if (numberp N)
  2910.       (progn
  2911.         (setq N (abs N))
  2912.         (while (> N 0)
  2913.           (insert item)
  2914.           (setq N (- N 1))))
  2915.     (if N
  2916.         (insert item))))
  2917.  
  2918. (defun forms--replace-format-item (position item value)
  2919.   (setq sub-item (nthcdr position item))
  2920.   (rplaca sub-item value)
  2921.   item)
  2922.  
  2923. ;; These are accessor functions into format-items on the format-list
  2924. (defun format-item-name (x) (nth 0 x))
  2925. (defun format-item-visible-p (x) (nth 1 x))
  2926. (defun format-item-label (x) (nth 2 x))
  2927. (defun format-item-field (x) (nth 3 x))
  2928. (defun format-item-default (x) (nth 4 x))
  2929. (defun format-item-newline-p (x) (nth 5 x))
  2930. (defun format-item-field-size (x) (nth 6 x))
  2931. (defun format-item-line-size (x) (nth 7 x))
  2932.  
  2933. (defun forms--trans (subj arg rep)
  2934.   "Replace in SUBJ all characters in ARG with character REP. ARG and REP should
  2935. be single-char strings."
  2936.   (let ((i 0)
  2937.     (x (length subj))
  2938.     (re (regexp-quote arg))
  2939.     (k (string-to-char rep)))
  2940.     (while (setq i (string-match re subj i))
  2941.       (aset subj i k)
  2942.       (setq i (1+ i)))))
  2943.  
  2944.  
  2945. (defun forms--field (record field-num)   ;(forms--field 1 3)
  2946.  "Returns the contents of RECORD corresponding to FIELD-NUM (a number) field."
  2947.  (let (start end
  2948.        (field-sep forms-field-sep)
  2949.        (number-of-fields forms-number-of-fields))
  2950.  (save-excursion
  2951.    (set-buffer forms--file-buffer)
  2952.    (forms--goto-line record)
  2953.    (forms--re-search-forward field-sep nil nil (- field-num 1))
  2954.    (setq start (point))
  2955.    (if (= field-num number-of-fields)
  2956.        (end-of-line)
  2957.      (re-search-forward forms-field-sep nil nil 1)
  2958.      (forward-char -1))
  2959.    (setq end (point))
  2960.    (buffer-substring start end)  )))
  2961.  
  2962. (defun forms--get-record ()
  2963.   "Fetch the current record from the file buffer."
  2964.   ;; This function is executed in the context of the forms--file-buffer.
  2965.   (or (bolp) (beginning-of-line nil))
  2966.   (let ((here (point)))
  2967.     (prog2 (end-of-line)
  2968.            (buffer-substring here (point))
  2969.            (goto-char here))))
  2970.  
  2971. (defun forms--show-record (the-record)
  2972.   "Format THE-RECORD according to forms-format-list,
  2973.  and display it in the current buffer."
  2974.   (forms--find-row-and-column)
  2975.   (let ((old-row forms--row)
  2976.         (old-column forms--column))
  2977.   ;; split the-record
  2978.   (let (the-result           ;this let goes away sooner
  2979.     (start-pos 0)
  2980.     found-pos
  2981.     (field-sep-length (length forms-field-sep)))
  2982.     (if forms-multi-line
  2983.     (forms--trans the-record forms-multi-line "\n"))
  2984.     ;; add an extra separator (makes splitting easy)
  2985.     (setq the-record (concat the-record forms-field-sep))
  2986.     (while (setq found-pos (string-match forms-field-sep the-record start-pos))
  2987.       (let ((ent (substring the-record start-pos found-pos)))
  2988.     (setq the-result (append the-result (list ent)))
  2989.     (setq start-pos (+ field-sep-length found-pos))))
  2990.     (setq forms--the-record-list the-result))
  2991.  
  2992.   (setq buffer-read-only nil)
  2993.   (erase-buffer)
  2994.   ;; verify the number of fields, extend forms--the-record-list if needed
  2995.   (if (= (length forms--the-record-list) forms-number-of-fields)
  2996.       nil
  2997.     (beep)
  2998.     (message "Record has %d fields instead of %d."
  2999.          (length forms--the-record-list) forms-number-of-fields)
  3000.     (setq forms-bad-count-fields 
  3001.           (list 'bad-field-count forms-bad-count-fields the-record))
  3002.     (sit-for 1)
  3003.     (if (< (length forms--the-record-list) forms-number-of-fields)
  3004.     (setq forms--the-record-list 
  3005.           (append forms--the-record-list
  3006.               (make-list 
  3007.                (- forms-number-of-fields 
  3008.               (length forms--the-record-list))
  3009.                "")))))
  3010.  
  3011.   ;; call the formatter function
  3012.   (setq forms-fields (append (list nil) forms--the-record-list nil))
  3013.   (funcall forms--format forms--the-record-list)
  3014.  
  3015.   ;; prepare
  3016.   (set-buffer-modified-p nil)
  3017.   (setq buffer-read-only forms-read-only)
  3018.   (forms--set-mode-line)
  3019.  
  3020.   ;; this should be field based
  3021.   (if forms-go-to-beginning-on-jump
  3022.       (goto-char (point-min))
  3023.     (goto-char (point-min))
  3024.     (forward-line (- old-row 1))
  3025.     (forward-char old-column))
  3026. ))
  3027.  
  3028. ;; note that this is not the interactive version
  3029. (defun forms--insert-record (the-record)
  3030.   "Format THE-RECORD according to forms-format-list,
  3031. and insert it in the current buffer."
  3032.   ;; split the-record
  3033.   (let (the-result    (start-pos 0)
  3034.     found-pos
  3035.     (field-sep-length (length forms-field-sep)))
  3036.     (forms--trans the-record forms-multi-line "\n")
  3037.     ;; add an extra separator (makes splitting easy)
  3038.     (setq the-record (concat the-record forms-field-sep))
  3039.     (while (setq found-pos (string-match forms-field-sep the-record start-pos))
  3040.       (let ((ent (substring the-record start-pos found-pos)))
  3041.     (setq the-result (append the-result (list ent)))
  3042.     (setq start-pos (+ field-sep-length found-pos))))
  3043.     (setq forms--the-record-list the-result))
  3044.   (setq buffer-read-only nil)
  3045.   ;; verify the number of fields, extend forms--the-record-list if needed
  3046.   (if (= (length forms--the-record-list) forms-number-of-fields)
  3047.       nil
  3048.     (beep)
  3049.     (message "Record has %d fields instead of %d."
  3050.          (length forms--the-record-list) forms-number-of-fields)
  3051.     (setq forms-bad-count-fields 
  3052.           (list 'bad-field-count i forms-bad-count-fields the-record))
  3053.     (sit-for 1)
  3054.     (if (< (length forms--the-record-list) forms-number-of-fields)
  3055.     (setq forms--the-record-list 
  3056.           (append forms--the-record-list
  3057.               (make-list 
  3058.                (- forms-number-of-fields 
  3059.               (length forms--the-record-list))
  3060.                "")))))
  3061.   ;; call the formatter function
  3062.   (setq forms-fields (append (list nil) forms--the-record-list nil))
  3063.   (funcall forms--format forms--the-record-list)
  3064.   (insert "\n"))
  3065.  
  3066. (defun forms--update ()
  3067.   "Update current record with contents of form.  As a side effect: sets
  3068. forms--the-record-list."
  3069.   (if forms-read-only
  3070.       (progn
  3071.     (message "Read-only buffer!")
  3072.     (beep))
  3073.  
  3074.     (let (the-record)
  3075.       ;; build new record
  3076.       (setq forms--the-record-list (forms--parse-form))
  3077.       (setq the-record
  3078.         (mapconcat 'identity forms--the-record-list forms-field-sep))
  3079.  
  3080.       ;; handle multi-line fields, if allowed
  3081.       (if forms-multi-line (forms--trans the-record "\n" forms-multi-line))
  3082.  
  3083.       ;; a final sanity check before updating
  3084.       (if (string-match "\n" the-record)
  3085.       (progn (beep)
  3086.             (message "Multi-line fields in this record - update refused!"))
  3087.     (save-excursion
  3088.           ;; in multi-records per page, this will have to be found
  3089.       (set-buffer forms--file-buffer)
  3090.       ;; Insert something before kill-line is called. See kill-line
  3091.       ;; doc. Bugfix provided by Ignatios Souvatzis.
  3092.           ;; shouldn't use kill-line, which pushes onto kill-ring
  3093.           ;; bug-fix fix provided by FER
  3094.       ;(insert "*")
  3095.       (beginning-of-line)
  3096.       ;(kill-line nil)
  3097.           (delete-region (point) 
  3098.               (save-excursion (end-of-line)
  3099.                               (point)))
  3100.       (insert the-record)
  3101.       (beginning-of-line))))))
  3102.  
  3103. (defun forms--update-all ()
  3104.  "Update all records with contents of form buffer."
  3105.  (save-excursion
  3106.  (if forms-read-only
  3107.      (progn (message "Read-only buffer!") (beep))
  3108.    ;; else
  3109.    (if (buffer-modified-p)
  3110.    (let (the-record (i 0))
  3111.      (goto-char (point-min)) (forward-line 1)
  3112.      (save-excursion (set-buffer forms--file-buffer)
  3113.                      (goto-char (point-min)))
  3114.      (while (< i forms--total-records)
  3115.        (message "Checkpointing %s of %s" (+ i 1) forms--total-records)
  3116.        ;; build new record
  3117.        (setq forms--the-record-list (forms--parse-form))
  3118.        (setq the-record
  3119.              (mapconcat 'identity forms--the-record-list forms-field-sep))
  3120.        ;; handle multi-line fields, if allowed & needed
  3121.        (if forms-multi-line (forms--trans the-record "\n" forms-multi-line))
  3122.        ;; a final sanity check before updating
  3123.        (if (string-match "\n" the-record)
  3124.            (progn (message 
  3125.                       "Multi-line fields in this record - update refused!")
  3126.                   (beep))
  3127.          (save-excursion
  3128.          ;; in multi-records per page, this will have to be found
  3129.          (set-buffer forms--file-buffer)
  3130.      ;; Insert something before kill-line is called. See kill-line
  3131.      ;; doc. Bugfix provided by Ignatios Souvatzis.
  3132.          ;; shouldn't use kill-line, which pushes onto kill-ring
  3133.          ;; bug-fix fix provided by FER
  3134.      (beginning-of-line)
  3135.          (delete-region (point) 
  3136.              (save-excursion (end-of-line)
  3137.                              (point)))
  3138.      (insert the-record)
  3139.      (forward-line 1)))
  3140.        (forward-line 2)
  3141.        (setq i (+ 1 i)))
  3142.        (set-buffer-modified-p nil))
  3143.    (message "No checkpointing necessary.")  ))))
  3144.  
  3145.  
  3146. ;; Sample:
  3147. ;; (defun my-new-record-filter (the-fields)
  3148. ;;   ;; numbers are relative to 1
  3149. ;;   (aset the-fields 4 (current-time-string))
  3150. ;;   (aset the-fields 6 (user-login-name))
  3151. ;;   the-list)
  3152. ;; (setq forms-new-record-filter 'my-new-record-filter)
  3153.  
  3154. ;; note: this is the interactive version, and it creates a new record.
  3155. ;; The below doc is not true, but for documentary purposes only
  3156. (defun forms-insert-record (arg)
  3157.   "Create a new record before the current one. With ARG: store the
  3158. record after the current one.
  3159. If a function forms-new-record-filter is defined, or forms-new-record-filter
  3160. contains the name of a function, it is called to fill (some of) the fields
  3161. with default values."
  3162.   (interactive "P")
  3163.   (let ((ln (if arg (1+ forms--current-record) forms--current-record))
  3164.         the-list the-record)
  3165.     (forms--set-mode-line)
  3166.     (if forms--new-record-filter
  3167.     ;; As a service to the user, we add a zeroth element so she
  3168.     ;; can use the same indices as in the forms definition.
  3169.     (let ((the-fields (make-vector (1+ forms-number-of-fields) "")))
  3170.       (setq the-fields (funcall forms--new-record-filter the-fields))
  3171.       (setq the-list (cdr (append the-fields nil))))
  3172.       (setq the-list (make-list forms-number-of-fields "")))
  3173.     (setq the-record (mapconcat 'identity the-list forms-field-sep))
  3174.     (save-excursion (set-buffer forms--file-buffer)
  3175.                     (forms--goto-line ln)
  3176.                     (open-line 1)
  3177.                     (insert the-record)
  3178.                     (beginning-of-line))
  3179.     (setq forms--current-record ln)
  3180.   (setq forms--total-records (1+ forms--total-records))
  3181.   (if (not arg)
  3182.       (forms-beginning-of-form 1)
  3183.       (forms-end-of-form 1))
  3184.   (forms--insert-record the-record)
  3185.   (forms--renumber-buffer)
  3186.   (forms--set-mode-line)    ))
  3187.  
  3188. (defun forms-duplicate-record ()
  3189.   "Create a duplicate record of the current one."
  3190.   (interactive)
  3191.   (forms--find-current-record-number) ;update forms--current-record
  3192.   (let ((ln forms--current-record)
  3193.         the-record)
  3194.     (message "Duplicating record %s." ln)
  3195.     (save-excursion (set-buffer forms--file-buffer)
  3196.                     (forms--goto-line ln)
  3197.                     (setq the-record (forms--get-record))
  3198.                     (open-line 1)
  3199.                     (insert the-record)
  3200.                     (beginning-of-line))
  3201.     (setq forms--total-records (1+ forms--total-records))
  3202.     (forms-beginning-of-form 1)    
  3203.     (forms--insert-record the-record)
  3204.     (forms--renumber-buffer)
  3205.     (forms--set-mode-line)
  3206.     (message "Leaving in the middle of the two duplicated records.")    ))
  3207.  
  3208. (defun forms--goto-line (N)
  3209.   (let (narrow-count wide-count)
  3210.     (save-restriction
  3211.       (setq narrow-count
  3212.             (count-lines (point-min) (point-max)))
  3213.       (widen)
  3214.       (setq wide-count
  3215.             (count-lines (point-min) (point-max)))
  3216.       (goto-line (+ (- wide-count narrow-count)
  3217.                     N))      )))
  3218.  
  3219. (defun forms-delete-record (arg)
  3220.   "Deletes a record. With ARG, don't ask."
  3221.   (interactive "P")
  3222.   (forms--set-mode-line)
  3223.   (if (or arg (y-or-n-p "Really delete this record? "))
  3224.       (let ((ln forms--current-record)
  3225.             (old-point (point)))
  3226.     (save-excursion
  3227.             (set-buffer forms--file-buffer)
  3228.             (forms--goto-line ln)
  3229.             (beginning-of-line)
  3230.             (delete-region (point)
  3231.                    (save-excursion 
  3232.                       (end-of-line)
  3233.                       (+ 1 (point)))))
  3234.         (delete-region ; in printed out buffer, save redrawing
  3235.               (save-excursion (forms-beginning-of-form 1) (point))
  3236.               (save-excursion (forms-end-of-form 1) (+ 1 (point))))
  3237.     (setq forms--total-records (1- forms--total-records))
  3238.     (if (> forms--current-record forms--total-records)
  3239.         (setq forms--current-record forms--total-records))
  3240.         ;; this could be optimized
  3241.         (forms--renumber-buffer)
  3242.         (message "Record deleted.")
  3243.         (goto-char old-point)
  3244.         (forms--set-mode-line))
  3245.      (message "Record not deleted.")))
  3246.  
  3247. (defun forms-search (regexp)
  3248.   "Search REGEXP in file buffer."
  3249.   (interactive 
  3250.    (list (read-string (concat "Search for" 
  3251.                   (if forms--search-regexp
  3252.                    (concat " ("
  3253.                        forms--search-regexp
  3254.                        ")"))
  3255.                   ": "))))
  3256.   (forms--set-mode-line)
  3257.   (if (equal "" regexp)
  3258.       (setq regexp forms--search-regexp))
  3259.   (let (the-line the-record here
  3260.          (fld-sep forms-field-sep))
  3261.     (if (save-excursion
  3262.       (set-buffer forms--file-buffer)
  3263.       (setq here (point))
  3264.       (end-of-line)
  3265.       (if (null (re-search-forward regexp nil t))
  3266.           (progn
  3267.         (goto-char here)
  3268.         (message (concat "\"" regexp "\" not found."))
  3269.         nil)
  3270.         (setq the-record (forms--get-record))
  3271.         (setq the-line (1+ (count-lines (point-min) (point))))))
  3272.     (progn (setq forms--current-record the-line)
  3273.       (forms--show-record the-record)
  3274.       (re-search-forward regexp nil t))))
  3275.   (forms--set-mode-line)
  3276.   (setq forms--search-regexp regexp))
  3277.  
  3278. (defun count-regexp (regexp limit)
  3279.   (save-excursion
  3280.     (let ((result 0))
  3281.       (while (re-search-forward regexp limit t)
  3282.          (setq result (+ 1 result)))
  3283.       result)))
  3284. ;(count-regexp "e" (point-max))
  3285.  
  3286. (defun forms-enumerate (the-fields)
  3287.   "Take a quoted list of symbols, and set their values to the numbers
  3288. 1, 2 and so on. Returns the higest number.
  3289.  
  3290. Usage: (setq forms-number-of-fields
  3291.              (forms-enumerate
  3292.               '(field1 field2 field2 ...)))"
  3293.  
  3294.   (let ((the-index 0))
  3295.     (while the-fields
  3296.       (setq the-index (1+ the-index))
  3297.       (let ((el (car-safe the-fields)))
  3298.     (setq the-fields (cdr-safe the-fields))
  3299.     (set el the-index)))
  3300.     the-index))
  3301.  
  3302.  
  3303. ;;;
  3304. ;;;    XVI.    Debugging functions
  3305. ;;;
  3306.  
  3307. (defvar forms--debug nil
  3308.   "Enables forms-mode debugging if not nil.")
  3309. ;; no leading *, not a user variable but a developer variable
  3310. ;; if it really is a user variable, better to put it at top of file.
  3311.  
  3312. (defun forms--debug (&rest args)
  3313.   "Internal - debugging routine"
  3314.   (if forms--debug
  3315.       (let ((ret nil))
  3316.     (while args
  3317.       (let ((el (car-safe args)))
  3318.         (setq args (cdr-safe args))
  3319.         (if (stringp el)
  3320.         (setq ret (concat ret el))
  3321.           (setq ret (concat ret (prin1-to-string el) " = "))
  3322.           (if (boundp el)
  3323.           (let ((vel (eval el)))
  3324.             (setq ret (concat ret (prin1-to-string vel) "\n")))
  3325.         (setq ret (concat ret "<unbound>" "\n")))
  3326.           (if (fboundp el)
  3327.           (setq ret (concat ret (prin1-to-string (symbol-function el)) 
  3328.                     "\n"))))))
  3329.     (save-excursion
  3330.       (set-buffer (get-buffer-create "*forms-mode debug*"))
  3331.       (goto-char (point-max))
  3332.       (insert ret)))))
  3333.  
  3334.  
  3335. ;;;
  3336. ;;;    N.    Final code
  3337. ;;;
  3338.  
  3339. (run-hooks 'forms-mode-load-hook)
  3340.  
  3341. ;;;
  3342. ;;;  dead code worth saving
  3343. ;;; 
  3344.  
  3345.  
  3346.  
  3347. ;;; Local Variables:
  3348. ;;; eval: (headers)
  3349. ;;; eval: (setq comment-start ";;; ")
  3350. ;;; End:
  3351.