home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / packages / edb / db-rep.el < prev    next >
Encoding:
Text File  |  1993-06-14  |  47.2 KB  |  1,255 lines

  1. ;;; db-rep.el --- part of EDB, the Emacs database
  2.  
  3. ;; See database.el for copyright notice, distribution conditions, etc.
  4.  
  5. ;; Author: Michael Ernst <mernst@theory.lcs.mit.edu>
  6. ;; Keywords: EDB
  7.  
  8. ;;; Commentary:
  9.  
  10. ;; Representation and basic operations for
  11. ;; database, link, recordfieldspec objects.
  12.  
  13. ;;; Code:
  14.  
  15.  
  16. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  17. ;;; Database abstraction
  18. ;;;
  19.  
  20. ;; A database is just a doubly-linked circular list, with some supporting
  21. ;; information.
  22.  
  23. ;; See the texinfo file for more complete descriptions of the slots of this
  24. ;; structure; the information in this file is intended to jog your memory,
  25. ;; not to instruct you.
  26.  
  27. (def-db-struct (database (:constructor old-make-database))
  28.   print-name                ; string
  29.  
  30.   first-link                ; pointer to a link
  31.   ;; this could be "size" or "length"
  32.   no-of-records                ; integer.  First link is numbered 1.
  33.  
  34.   file                    ; filename
  35.   file-local-variables            ; string, the local variables section
  36.                     ; of the file from which this db came.
  37.   aux-file                ; filename
  38.   data-display-buffers            ; list of buffers
  39.   default-format-file            ; filename
  40.  
  41.   ;; This does not appear to be used anywhere at all!
  42.   omit-functions
  43.  
  44.   ;; field information
  45.   no-of-fields                ; integer
  46.   fieldnames                ; this is repeated in the recordpsecs
  47.   fieldname-alist            ; alist of (name . number)
  48.   recordfieldspecs            ; vector of symbols or recordfieldspecs
  49.                     ;  if symbol, look up in db-recordfieldtypes
  50.  
  51.   field-priorities            ; maybe call this order-fields instead
  52.   omitted-to-end-p            ; boolean
  53.  
  54.   ;; For file i/o
  55.   internal-file-layout-p
  56.   record-sepinfo
  57.   field-sepinfo
  58.   alternative-sepinfo
  59.   read-record-from-region
  60.   write-region-from-record
  61.   sub-fieldsep-string
  62.   sub-recordsep-string
  63.  
  64.   ;; for i/o conversion (quoting the special strings)
  65.   quotation-char
  66.   quotation-char-regexp
  67.   ;; These are unprocessed; vars are set from them when reading/writing.
  68.   quoted-regexp                ; if nil, use quoted-strings
  69.   quoted-strings            ; a list; if nil, use function
  70.                     ;   quoted-strings-default
  71.   actual-quoted-regexp            ; actually used; user should never set
  72.   substitutions                ; list of (actual . stored) string pairs
  73.  
  74.   ;; I'm not convinced these are all that useful.  Maybe add them in later.
  75.   ;; max-field-size
  76.   ;; max-record-size
  77.   ;; pad-with-whitespace
  78.  
  79.   modified-p
  80.   modifiable-p
  81.  
  82.   locals                ; alist of (symbol . value) pairs
  83.   )
  84.  
  85. (defvar databases-made 0)
  86.  
  87. (defun make-database ()
  88.   (let ((db (old-make-database)))
  89.     (database-set-record-sepinfo db (make-sepinfo))
  90.     (database-set-field-sepinfo db (make-sepinfo))
  91.     (database-set-alternative-sepinfo db (make-sepinfo))
  92.  
  93.     ;; Perhaps this should only happen when necessary, not every time.
  94.     (setq databases-made (1+ databases-made))
  95.     (database-set-print-name db (concat "Unnamed Database "
  96.                     (int-to-string databases-made)))
  97.     db))
  98.  
  99. ;;; Constructor
  100.  
  101. ;; I could copy the old one and change some values, or make a new one and
  102. ;; copy some values.
  103. ;; The latter makes explicit what's being copied; but nearly everything is.
  104. (defun make-similar-database (original)
  105.   ;; Return a database similar to ORIGINAL.
  106.   (let ((result (copy-database original)))
  107.     (db-debug-message "Created result database.")
  108.     (database-set-print-name result
  109.       (concat "Copy of " (database-print-name original)))
  110.     (db-debug-message "Changed print name.")
  111.     (database-set-first-link result nil)
  112.     (database-set-no-of-records result 0)
  113.     ;; Should this go after choose-format-file?
  114.     (database-set-file result (concat (database-file original) "-COPY"))
  115.     (db-debug-message "Changed filename.")
  116.     ;; Could get info from (car (database-data-display-buffers original)) if
  117.     ;; there is no default-format-file; could also try to infer format name.
  118.     (database-set-data-display-buffers result
  119.       (list (db-setup-data-display-buffer
  120.          (choose-format-file result nil nil)
  121.          result
  122.          t)))
  123.     (db-debug-message "Created data display buffer.")
  124.     (setq db-databases (cons result db-databases))
  125.     result))
  126.  
  127. ;;; Non-primitive accessors
  128.  
  129. (defun database-last-link (database)
  130.   (link-prev (database-first-link database)))
  131. (proclaim-inline database-last-link)
  132.  
  133. ;; could also use (= 0 (database-no-of-records database)).
  134. (defun database-empty-p (database)
  135.   (null (database-first-link database)))
  136. (proclaim-inline database-empty-p)
  137.  
  138. (defun database-unnamed-p (database)
  139.   (let ((print-name (database-print-name dbc-database)))
  140.     (or (not print-name)
  141.     (equal "" print-name)
  142.     (equal "Unnamed Database " 
  143.               (substring print-name 0 (min 17 (length print-name)))))))
  144. (proclaim-inline database-unnamed-p)
  145.  
  146. (defun database-list-of-links (database)
  147.   (maplinks (function identity)
  148.         database nil nil t))
  149. (proclaim-inline database-list-of-links)
  150.  
  151. ;;; Database-local variables
  152.  
  153. ;; Possibly get rid of database-make-local altogether and make no-error
  154. ;; behavior the default in database-{gs}et-local.  Usually reliable sources
  155. ;; inform me that right now they're somewhat of a pain to use.
  156.  
  157. (defun database-make-local (symbol database &optional value)
  158.   "Declare a database-local variable named by SYMBOL for DATABASE.
  159. Each such variable should only be declared once.
  160. If optional argument VALUE is specified, the variable is set to it."
  161.   (let ((lookup (assq symbol (database-locals database))))
  162.     (if lookup
  163.     (error "%s is already defined as a local variable in %s."
  164.            symbol (database-print-name database))
  165.       (database-set-locals database (cons (cons symbol value)
  166.                       (database-locals database))))))
  167.  
  168. (defun database-set-local (symbol database value &optional no-error)
  169.   "Set the value of database-local variable SYMBOL, in DATABASE, to VALUE.
  170. SYMBOL must have been declared by a previous call to `database-make-local'
  171. unless optional argument NO-ERROR is supplied, in which case the function
  172. does that automatically."
  173.   (let ((lookup (assq symbol (database-locals database))))
  174.     (if lookup
  175.     (setcdr lookup value)
  176.       (if no-error
  177.       (database-make-local symbol database value)
  178.     (error "%s is not a database-local variable for %s."
  179.            symbol (database-print-name database))))))
  180.  
  181. (defun database-get-local (symbol database &optional no-error)
  182.   "Return the value of database-local variable SYMBOL for DATABASE.
  183. SYMBOL must have been declared by a previous call to `database-make-local'
  184. unless optional argument NO-ERROR is supplied, in which case nil is returned."
  185.   (let ((lookup (assq symbol (database-locals database))))
  186.     (cond (lookup
  187.        (cdr lookup))
  188.       (no-error
  189.        nil)
  190.       (t
  191.        (error "%s is not a database-local variable for %s."
  192.           symbol (database-print-name database))))))
  193.  
  194. (defun database-local-p (symbol database)
  195.   "Return non-nil if SYMBOL is a database-local variable for DATABASE."
  196.   (assq symbol (database-locals database)))
  197.   
  198.  
  199. ;;; Non-primitive setters
  200.  
  201. ;; This is very close to db-set-fieldname-vars now.
  202. (defun database-set-fieldnames-to-list (database fieldnames-list)
  203.   "Set DATABASE's fieldnames and record field types according to FIELDNAMES-LIST.
  204. Users should never call `database-set-fieldnames' directly.
  205. FIELDNAMES-LIST is a list of fieldnames (symbols); each list element may
  206. instead be a cons of fieldname and type to specify the field's
  207. recordfieldtype as well.  If no type is specified for a field, the value of
  208. `db-default-field-type' is used.
  209.  
  210. This function sets several database slots besides the fieldnames slot, but
  211. has no effect if the fieldnames slot of the database is already set."
  212.   (if (not (database-fieldnames database))
  213.       (db-set-fieldname-vars database fieldnames-list)))
  214.  
  215. ;;; Basic functions
  216.  
  217. (defun database-index-in-range (index database)
  218.   (and (> index 0) (<= index (database-no-of-records database))))
  219. (proclaim-inline database-index-in-range)
  220.  
  221. ;; Make INDEX be in the range 1 to (database-no-of-records database).
  222. (defun database-normalize-index (index database)
  223.   (let ((remainder (% index (database-no-of-records database))))
  224.     (if (zerop remainder) (database-no-of-records database) remainder)))
  225. (proclaim-inline database-normalize-index)
  226.  
  227. ;;   "Return the link of DATABASE at index N.  The first link is numbered 1."
  228. (defun database-link (database n)
  229.   (car (database-link-and-index database n nil nil)))
  230. (proclaim-inline database-link)
  231.  
  232. ;;; Not quite so basic functions.
  233.  
  234. ;; The string that really separates database record fields.
  235. (defun database-full-fieldsep-string (database)
  236.   (if (database-write-region-from-record database)
  237.       ;; might the write-record-function want to access this value?  I think not.
  238.       nil
  239.     (let ((field-sepinfo (database-field-sepinfo database)))
  240.       (sepinfo-sep-string field-sepinfo))))
  241.       
  242. ;; The string that really separates database records.
  243. (defun database-full-recordsep-string (database)
  244.   (let ((record-sepinfo (database-record-sepinfo database)))
  245.     (if (database-write-region-from-record database)
  246.     (sepinfo-sep-string record-sepinfo)
  247.       (let ((field-sepinfo (database-field-sepinfo database)))
  248.     (concat (sepinfo-post-last-string field-sepinfo)
  249.         (sepinfo-sep-string record-sepinfo)
  250.         (sepinfo-pre-first-string field-sepinfo))))))
  251.  
  252.  
  253. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  254. ;;; Link abstraction
  255. ;;;
  256.  
  257. ;; Perhaps marks should be format-local.  The current implementation makes
  258. ;; them database-local and so visible in all formats.
  259.  
  260. (def-db-struct (link (:constructor old-make-link))
  261.   prev
  262.   next
  263.   markedp
  264.   omittedp
  265.   summary
  266.   record)
  267.  
  268. ;; I don't know that I should be using this often; the old record will get
  269. ;; garbage-collected.  In fact, I can't think of an occasion when I should
  270. ;; use it.
  271.  
  272. (defun make-link ()            ;(&rest args)
  273.   (error "Should use make-link-from-record instead."))
  274. (make-obsolete 'make-link 'make-link-from-record)
  275.  
  276. ; This is a macro that just changes this slot.
  277. (fset 'link-set-record-slot (symbol-function 'link-set-record))
  278. (fmakunbound 'link-set-record)
  279.  
  280. ;; Should also set link-omittedp.
  281. (defun link-set-record (link result)
  282.   (link-set-record-slot link result)
  283.   (link-set-summary link nil))
  284. (proclaim-inline link-set-record)
  285.  
  286. ;;   "Place LINK1 and LINK2 in a prev-next relationship."
  287. (defun link-two (link1 link2)
  288.   (link-set-next link1 link2)
  289.   (link-set-prev link2 link1))
  290. (proclaim-inline link-two)
  291.  
  292. ;; I oughtn't ever have to use the standard make-link procedure.  Maybe even
  293. ;; have defstruct make this the standard.
  294. (defun make-link-from-record (record)
  295.   (let ((result (old-make-link)))
  296.     (link-set-record-slot result record)
  297.     result))
  298.  
  299. ;; Beware of cleverer implementations; maplinks-macro always returns nil.
  300. ;;   "Return a list of (link index) for the link containing RECORD in DATABASE.
  301. ;; Return nil if there is no such link."
  302. (defun record->link-and-index (record database)
  303.   (let (result)
  304.     (maplinks-macro
  305.      (if (eq record (link-record maplinks-link))
  306.      (progn
  307.        (setq result (list maplinks-link maplinks-index))
  308.        (maplinks-break)))
  309.      database nil)
  310.     result))
  311.  
  312.  
  313. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  314. ;;; Recordfieldspec abstraction
  315. ;;;
  316.  
  317. ;; This should be called a contentspec or something; it has to do only with
  318. ;; the value of the field.  This means that name and printname probably
  319. ;; don't belong.  Does type?  And a->s and s->a are questionable:  they're
  320. ;; only used when reading and writing variables, but then again, why not
  321. ;; keep them here instead of cluttering up the database object even more?
  322. ;; They probably belong here.
  323.  
  324. ;; Now instead of one recordfieldspec per database, there's one per field.  Two
  325. ;; values were moved into the database, and the database also contains a
  326. ;; vector of recordfieldspecs.
  327.  
  328.  
  329. (def-db-struct recordfieldspec
  330.  
  331.   ;; datatype information
  332.   type                    ; eg 'string
  333.   default-value
  334.   common-form-function
  335.   merge-function
  336.  
  337.   order-fn                ; takes two objects
  338.   sort-fn                ; takes two objects
  339.   match-function            ; takes a pattern and an object
  340.  
  341.   help-info                ; perhaps should be help-string;
  342.                     ; or should be more complicated.
  343.  
  344.   ;; Quite possibly these belong in the database; why should the recordfieldspec care?
  345.   ;; [Becuase it has to do with actual values and manipulations thereof?  Weak...]
  346.   actual->stored
  347.   stored->actual
  348.  
  349.   ;; customizations
  350.   change-hook                ; not currently used
  351.   constraint-function
  352.   )
  353.  
  354. ;;   "Return t if the databases' recordfieldspecs have the same field names and type."
  355. (defun recordfieldspecs-compatible (db1 db2)
  356.   (let ((result t)
  357.     (fno 0)
  358.     (fields1 (length (database-recordfieldspecs db1)))
  359.     (fields2 (length (database-recordfieldspecs db2)))
  360.     recordfieldspec1
  361.     recordfieldspec2)
  362.     (if (= fields1 fields2)
  363.     (progn
  364.       (while (and result (< fno fields1))
  365.         (setq recordfieldspec1 (database-recordfieldspec db1 fno)
  366.           recordfieldspec2 (database-recordfieldspec db2 fno)
  367.           ;; used to also check recordfieldspec-name here
  368.           result (eq (recordfieldspec-type recordfieldspec1)
  369.                  (recordfieldspec-type recordfieldspec2))
  370.           fno (1+ fno)))
  371.       result))))
  372.  
  373. (defun database-recordfieldspec (database record-index)
  374.   "Return the recordfieldspec of DATABASE corresponding to RECORD-INDEX.
  375. Dereferences via `recordfieldtype->recordfieldspec' any symbol found in the
  376. recordfieldspecs slot of DATABASE."
  377.   (let ((rs (aref (database-recordfieldspecs database) record-index)))
  378.     (cond ((symbolp rs)
  379.        (recordfieldtype->recordfieldspec rs))
  380.       ((recordfieldspec-p rs)
  381.        rs)
  382.       (t
  383.        (error "database-recordfieldspec:  rs = %s" rs)))))
  384.  
  385. (defun database-set-recordfieldspec (database record-index rs)
  386.   "Set the recordfieldspec of DATABASE corresponding to RECORD-INDEX to RS.
  387. Use this to redefine, on a per-field basis, subfields of the recordfieldspec."
  388.   (aset (database-recordfieldspecs database) record-index
  389.     (cond ((symbolp rs)
  390.            (recordfieldtype->recordfieldspec rs))
  391.           ((recordfieldspec-p rs)
  392.            rs)
  393.           (t
  394.            (error "database-set-recordfieldspec: Bad spec: rs= %s" rs)))))
  395.  
  396. (defun database-recordfieldspec-type (database record-index)
  397.   "Return the type of the recordfieldspec of DATABASE corresponding to RECORD-INDEX."
  398.   (let ((rs (aref (database-recordfieldspecs database) record-index)))
  399.     (cond ((symbolp rs)
  400.        rs)
  401.       ((recordfieldspec-p rs)
  402.        (recordfieldspec-type rs))
  403.       (t
  404.        (error "database-recordfieldspec:  rs = %s" rs)))))
  405.  
  406. ;; Perhaps the functions returned here should be byte-compiled (ie, call
  407. ;; byte-compile on the result to be returned), at least when they're consed
  408. ;; up at run-time.
  409.  
  410. (defun recordfieldspec-sort-function (recordfieldspec &optional reversep)
  411.   "Return a sort function for records described by RECORDFIELDSPEC.
  412. If optional argument REVERSEP is non-nil, then the sort function goes in
  413. the opposite order.
  414. If the sort-fn slot of the appropriate recordfieldspec of  database  doesn't
  415. contain one, one is made up on the fly from the order-fn slot.
  416. If the order-fn slot is also empty, the resulting function always returns
  417. nil, indicating that it is not the case that the first argument is less
  418. than the second."
  419.   (let ((sort-fn (recordfieldspec-sort-fn recordfieldspec)))
  420.     (if sort-fn
  421.     (if reversep
  422.         ;; (list 'lambda '(value1 value2)
  423.         ;;       (list sort-fn 'value2 'value1))
  424.         (` (lambda (value1 value2)
  425.          ((, sort-fn) value2 value1)))
  426.       sort-fn)
  427.       (order->sort (recordfieldspec-order-fn recordfieldspec) reversep))))
  428.  
  429. ;;   "Given an order function, return a sort function."
  430. (defun order->sort (order-fn reversep)
  431.   (if order-fn
  432.       (list 'lambda (if reversep '(value2 value1) '(value1 value2))
  433.         (list '= -1
  434.           (list 'funcall (list 'function order-fn)
  435.             'value1 'value2)))
  436.     (function nil-function)))
  437.  
  438. (defun recordfieldspec-order-function (recordfieldspec &optional reversep)
  439.   "Return an order function for records described by RECORDFIELDSPEC.
  440. If optional argument REVERSEP is non-nil, then the order function goes in
  441. the opposite order.
  442. If the order-fn slot of the appropriate recordfieldspec of  database  doesn't
  443. contain one, one is made up on the fly from the sort-fn slot; `equal'
  444. is used to determine whether two records are equal.
  445. If the sort-fn slot is also empty, the resulting function always
  446. returns 0, indicating equality."
  447.   (let ((order-fn (recordfieldspec-order-fn recordfieldspec)))
  448.     (if order-fn
  449.     (if reversep
  450.         (` (lambda (value1 value2)
  451.          ((, order-fn) value2 value1)))
  452.       order-fn)
  453.       (sort->order (recordfieldspec-sort-fn recordfieldspec) reversep))))
  454.  
  455. ;;   "Given a sort function, return an order function."
  456. (defun sort->order (sort-fn reversep)
  457.   (if sort-fn
  458.       (` (lambda (, (if reversep '(value2 value1) '(value1 value2)))
  459.        (cond ((equal value1 value2)
  460.           0)
  461.          ((funcall (function (, sort-fn))
  462.            value1 value2)
  463.           -1)
  464.          (t
  465.           1))))
  466.    
  467.     (function (lambda (value1 value2) 0))))
  468.  
  469.  
  470.  
  471. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  472. ;;; Records
  473. ;;;
  474.  
  475. ;; Abstraction
  476.  
  477. (defun make-record (database)
  478.   "Return a record with number of fields specified by argument DATABASE."
  479.   (make-vector (database-no-of-fields database) nil))
  480. (proclaim-inline make-record)
  481.  
  482. (defmacro copy-record (record)
  483.   "Return a copy of RECORD."
  484.   (list 'copy-sequence record))
  485.  
  486. (defun copy-record-to-record (source target)
  487.   "Copy the field values of the SOURCE record to the TARGET record."
  488.   (let ((fno 0)
  489.     (fields (length source)))
  490.     (while (< fno fields)
  491.       (aset target fno (aref source fno))
  492.       (setq fno (1+ fno)))))
  493.  
  494. ;;; Fieldnames and record fieldnumbers
  495.  
  496. (defun fieldname->fieldnumber (fieldname database)
  497.   "Given a FIELDNAME and DATABASE, return a record fieldnumber.
  498. Do not be fooled into thinking this is a format fieldnumber."
  499.   (cdr (assq fieldname (database-fieldname-alist database))))
  500. (proclaim-inline fieldname->fieldnumber)
  501.  
  502. (defun fieldnumber->fieldname (fieldnumber database)
  503.   "Given a record FIELDNUMBER and DATABASE, return a record fieldname.
  504. The first argument is not a format fieldnumber."
  505.   (aref (database-fieldnames database) fieldnumber))
  506. (proclaim-inline fieldnumber->fieldname)
  507.  
  508. ;;; Retrieving field values
  509.  
  510. (defmacro record-field-from-index (record fieldno)
  511.   "Return from RECORD the value of the FIELDNOth field."
  512.   (list 'aref record fieldno))
  513.  
  514. (defun record-field (record fieldname database)
  515.   "Return from RECORD the field with name FIELDNAME.  Third argument is DATABASE."
  516.   (let ((fieldnumber (fieldname->fieldnumber fieldname database)))
  517.     (if fieldnumber
  518.     (record-field-from-index record fieldnumber)
  519.       (error "No %s field in current record." fieldname))))
  520.  
  521. ;; One should check dbf-this-record-modified-p before using dbf-this-record.
  522. (defun dbf-this-record-field (fieldname)
  523.   "Return the value of the field with name FIELDNAME from `dbf-this-record'.
  524. You may want to use `dbf-displayed-record-field' instead."
  525.   (record-field dbf-this-record fieldname dbc-database))
  526. (proclaim-inline dbf-this-record-field)
  527.  
  528. ;;; Checking constraints
  529.  
  530. (defun record-check-constraint (field-value record record-index database)
  531.   (let ((constraint (recordfieldspec-constraint-function
  532.              (database-recordfieldspec database record-index))))
  533.     (if (and constraint
  534.          (not (funcall constraint field-value record record-index database)))
  535.     (error "The value `%s' does not satisfy the constraint for field %s."
  536.            field-value (fieldnumber->fieldname record-index database)))))
  537.  
  538. ;;; Setting field values
  539.  
  540. (defun record-set-field-from-index (record fieldno value database)
  541.   "Set, in RECORD, the FIELDNOth field to VALUE.
  542. Checks field constraints first if DATABASE is non-nil."
  543.   (if database
  544.       (record-check-constraint value record fieldno database))
  545.   (aset record fieldno value))
  546. (proclaim-inline record-set-field-from-index)
  547.  
  548. ;; Eventually this will be renamed record-set-field and the current
  549. ;; record-set-field (which is just a wrapper) will be deleted; this
  550. ;; superstructure is solely to permit backward compatibility after the
  551. ;; reversal of the order of the VALUE and DATABASE arguments.  5/16/93.
  552. (defun record-set-field-real (record fieldname value database &optional nocheck)
  553.   "Set, in RECORD, field FIELDNAME to VALUE.  Fourth argument DATABASE.
  554. Check constraints first unless optional fifth argument NOCHECK is non-nil."
  555.   (let ((fieldnumber (fieldname->fieldnumber fieldname database)))
  556.     (if fieldnumber
  557.     (record-set-field-from-index
  558.      record fieldnumber value (and (not nocheck) database))
  559.       (error "No %s field in current record." fieldname))))
  560.  
  561. (defun record-set-field (record fieldname value database &optional nocheck)
  562.   "Set, in RECORD, field FIELDNAME to VALUE.  Fourth argument DATABASE.
  563. Check constraints first unless optional fifth argument NOCHECK is non-nil.
  564. This version correctly deals with reversed VALUE and DATABASE arguments."
  565.   (if (database-p value)
  566.       (progn
  567.     (db-warning "Third and fourth arguments to record-set-field reversed.")
  568.     (record-set-field-real record fieldname database value nocheck))
  569.     (record-set-field-real record fieldname value database nocheck)))
  570.  
  571. ;;; Setting fields in dbf-this-record
  572.  
  573. ;; One should check dbf-this-record-modified-p before using dbf-this-record.
  574. (defun dbf-this-record-set-field (fieldname value)
  575.   "Set field with name FIELDNAME in `dbf-this-record' to VALUE.
  576. Causes the entire record to be redisplayed pretty soon.
  577. You may want to use `dbf-displayed-record-set-field' instead."
  578.   (record-set-field dbf-this-record fieldname value dbc-database)
  579.   (setq dbf-redisplay-entire-record-p t))
  580. (proclaim-inline dbf-this-record-set-field)
  581.  
  582. ;; One should check dbf-this-record-modified-p before using dbf-this-record.
  583. (defun dbf-this-record-set-field-and-redisplay (fieldname value)
  584.   "Set field with name FIELDNAME in `dbf-this-record' to VALUE.
  585. Causes the entire record to be redisplayed immediately.
  586. You may want to use `dbf-displayed-record-set-field-and-redisplay' instead."
  587.   (dbf-this-record-set-field fieldname value)
  588.   (dbf-redisplay-entire-record-maybe))
  589. (proclaim-inline dbf-this-record-set-field-and-redisplay)
  590. (make-obsolete 'dbf-this-record-set-field-and-redisplay 'dbf-displayed-record-set-field-and-redisplay)
  591.  
  592. ;;; The displayed record
  593.  
  594. (defun dbf-displayed-record-field (fieldname)
  595.   "Return the value of the field named FIELDNAME from the displayed record."
  596.   (record-field (dbf-displayed-record) fieldname dbc-database))
  597. (proclaim-inline dbf-displayed-record-field)
  598.  
  599. (defun dbf-displayed-record-set-field (fieldname value)
  600.   "Set field with name FIELDNAME in displayed record to VALUE.
  601. Cause the entire record to be redisplayed pretty soon."
  602.   ;; This call guarantees that displayed-record = this-record.
  603.   (dbf-set-this-record-modified-p t)
  604.   (dbf-this-record-set-field fieldname value))
  605.  
  606. (defun dbf-displayed-record-set-field-and-redisplay (fieldname value)
  607.   "Set field with name FIELDNAME in displayed record to VALUE.
  608. Cause the entire record to be redisplayed immediately."
  609.   ;; Is this call correct?  Maybe displayed-record != this-record.
  610.   (dbf-this-record-set-field fieldname value)
  611.   (dbf-redisplay-entire-record-maybe))
  612. (proclaim-inline dbf-displayed-record-set-field-and-redisplay)
  613.  
  614. ;;; Mapping
  615.  
  616. ;; Since EDB contains one use of both of these macros combined, perhaps I
  617. ;; don't really need them.
  618.  
  619. ;; I don't know where in the EDB source files this really belongs.
  620. (defmacro mapfields (func record database)
  621.   "Apply FUNC to each field in RECORD, with variable  mapfields-index  bound.
  622. Third argument is DATABASE."
  623.   (` (let ((mapfields-index 0)
  624.        (mapfields-record (, record))
  625.        (mapfields-fields (database-no-of-fields (, database))))
  626.        (while (< mapfields-index mapfields-fields)
  627.      (funcall (, func)
  628.           (record-field-from-index mapfields-record mapfields-index))
  629.      (setq mapfields-index (1+ mapfields-index))))))
  630.  
  631. ;; ;;  "Like mapfields, but also binds  mapfields-name."
  632. ;; (defmacro mapfields-name (func record database)
  633. ;;   ;; use fieldno->fieldname
  634. ;;   (error "Not yet implemented; if you really need it, ask me."))
  635.  
  636. (defmacro mapfields-macro (body record database)
  637.   "Execute BODY for each field of RECORD, a record of DATABASE,
  638. with variables  mapfields-field  and  mapfields-index  bound."
  639.   (` (let ((mapfields-index 0)
  640.        (mapfields-record (, record))
  641.        (mapfields-fields (database-no-of-fields (, database)))
  642.        mapfields-field)
  643.        (while (< this-field-index field-index-max)
  644.      (setq mapfields-field
  645.            (record-field-from-index this-record this-field-index))
  646.      ;; BODY must be a single form since it's the first argument
  647.      (, body)
  648.      (setq this-field-index (1+ mapfields-index))))))
  649.  
  650.  
  651. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  652. ;;; Sepinfo abstraction
  653. ;;;
  654.  
  655. ;; This tells how a list of information appears in a file.  See the texinfo
  656. ;; documentation for more details.
  657.  
  658. ;; Would it be more natural to have a function that finds the extent of a
  659. ;; record rather than of the separating space?  Well, that can probably be
  660. ;; a regexp most of the time anyway...
  661.  
  662. (def-db-struct sepinfo
  663.   pre-first-string
  664.   pre-first-regexp
  665.   pre-first-regexp-submatch
  666.   sep-string
  667.   sep-regexp
  668.   sep-regexp-submatch
  669.   sep-function                ; returns (end-pos . next-start-pos) pair
  670.                     ; takes prev-end-pos as an argument
  671.                     ; next-start-pos is nil for last record
  672.   post-last-string
  673.   post-last-regexp
  674.   post-last-regexp-submatch)
  675.  
  676.  
  677. ;; The user is asked to remember to set the -regexp slot to nil when
  678. ;; he sets the string.  Another possibility is a function that does this
  679. ;; for him, but he'd have to remember to call that just as he remembers to
  680. ;; do the setf himself, and it could be confusing to have two different
  681. ;; methods for setting the slot value with slightly different semantics.
  682. ;; Yet another possibility is a set of secret regexp slots in the sepinfo;
  683. ;; these are the ones that are really used, and they're set from the
  684. ;; visible string and regexp slots.  (They can always be safely blown away
  685. ;; and set again without danger of throwing away a user-set value.)  But
  686. ;; this would double the size of the sepinfo and would be conceptually
  687. ;; ugly.
  688.  
  689.  
  690. ;; Can't have the call to function here; that screws things up.
  691. (defun make-n-line-sep-function (n)
  692.   "Return a sep-function useful when all records have exactly N lines on disk."
  693.   (` (lambda (prev-end)
  694.        (forward-line (, n))
  695.        (cons (point) (if (not (eobp)) (point))))))
  696.  
  697.  
  698. ;; If the variable is the empty string, I should set it to nil.
  699.  
  700. ;; Should let the pre-first stuff be optional:  if it's not there, then the
  701. ;; whole thing is one value, and if it is there, then there are multiple
  702. ;; items present.
  703.  
  704. ;; To indicate the region of the buffer to be read, we could use
  705. ;; locations/marks, or we could do narrowing.  We choose the former.
  706. ;; Body-func is repeatedly called with two buffer position arguments:
  707. ;; the start and end of the region it's to operate upon.
  708. (defun read-sep-items (sepinfo beg end body-func)
  709.   (db-debug-message "read-sep-items from %s to %s." beg end)
  710.   (let* ((post-last-item-pos
  711.       (progn
  712.         (goto-char beg)
  713.         ;;        (if (sepinfo-post-last-regexp sepinfo)
  714.         ;;            (db-debug-message "post-last-regexp %s found at %s"
  715.         ;;             (sepinfo-post-last-regexp sepinfo)
  716.         ;;             (re-search-forward-maybe (sepinfo-post-last-regexp sepinfo) end t)))
  717.        
  718.         (if (and (sepinfo-post-last-regexp sepinfo)
  719.              (re-search-forward (sepinfo-post-last-regexp sepinfo) end t))
  720.         (progn
  721.           (db-debug-message "found post-last at %s (vs %s)"
  722.                (match-beginning (sepinfo-post-last-regexp-submatch sepinfo))
  723.                end)
  724.           (match-beginning (sepinfo-post-last-regexp-submatch sepinfo)))
  725.           end)))
  726.      (start-of-this-item
  727.       (progn
  728.         (goto-char beg)
  729.         (if (sepinfo-pre-first-regexp sepinfo)
  730.         (skip-regexp-forward (sepinfo-pre-first-regexp sepinfo)
  731.                      (sepinfo-pre-first-regexp-submatch sepinfo)))
  732.         (if (< (point) post-last-item-pos)
  733.         (point))))
  734.      end-of-this-item
  735.      start-of-next-item)
  736.     (while start-of-this-item
  737.       (goto-char start-of-this-item)
  738.       (if (sepinfo-sep-function sepinfo)
  739.       (let ((end-start (funcall (sepinfo-sep-function sepinfo)
  740.                     post-last-item-pos)))
  741.         (setq end-of-this-item (car end-start)
  742.           start-of-next-item (cdr end-start)))
  743.     (if (re-search-forward (sepinfo-sep-regexp sepinfo)
  744.                    post-last-item-pos t)
  745.         (setq end-of-this-item
  746.           (match-beginning (sepinfo-sep-regexp-submatch sepinfo))
  747.           start-of-next-item
  748.           (match-end (sepinfo-sep-regexp-submatch sepinfo)))
  749.       (setq end-of-this-item post-last-item-pos
  750.         start-of-next-item nil)))
  751.       (funcall body-func start-of-this-item end-of-this-item)
  752.       (setq start-of-this-item start-of-next-item))))
  753.  
  754.  
  755. ;; I sorta want this to take an item at a time and produce a bit of output
  756. ;; at a time, but I need to know when I have the last item so that I can
  757. ;; add post-last instead of sep after it.  So there also needs to be a way
  758. ;; of indicating "that last item was the last one".
  759.  
  760. ;; body could return:
  761. ;;  * t if wrote something, nil otherwise
  762. ;;  * t if this was the last one, nil otherwise (always write something)
  763. ;; The latter would be more convenient for this function; which is more
  764. ;; convenient for body?
  765.  
  766. ;; Two possible approaches:
  767. ;;  * have something that accepts a producer.
  768. ;;  * have something I can call repeatedly (in mapcar or maplinks, for example)
  769. ;;    and which would accept additional arguments to tell when a new list was
  770. ;;    being started/ended/whatever.
  771.  
  772. (defun write-sep-items (sepinfo producer)
  773.  
  774.   ;; write pre-
  775.  
  776.   ;; repeat:
  777.   ;;   call body, which should write to the buffer and return t (if it wrote
  778.   ;;     anything) or nil (if it didn't)
  779.   ;;   if body returned t
  780.  
  781.   ;; not first time through, write sep.
  782.  
  783.   ;; write post-
  784.  
  785.   )
  786.  
  787.  
  788. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  789. ;;; Fieldnumbers vs. fieldnames philosophy
  790. ;;;
  791.  
  792. ;; I will try to use numbers whereever possible and everywhere that the
  793. ;; user can't see them; the user will be able to use fieldnames, naturally.
  794. ;; I should always know whether I'm dealing with a fieldname or a
  795. ;; fieldnumber.  When the user gives me a fieldname, I should convert it to
  796. ;; a fieldnumber right away.
  797.  
  798.  
  799. ;; Proposed solution:
  800.  
  801. ;; Provide macros for user code (eg (record-fieldname1), (record-fieldname2
  802. ;; record)), so I don't have to compile it down but it executes efficiently
  803. ;; anyway.  (User may want access to slower accessors that take fieldname
  804. ;; as an argument as well; that will be easy to implement.  Eg,
  805. ;; (record-field 'fieldname [record]).)  Warn the user:  if byte-compiling
  806. ;; code that uses the macros, better make darn sure that the macros in
  807. ;; effect are those of the database that you're worried about, or that
  808. ;; there are no macros defined.
  809.  
  810. ;; In my own code, always pass around fieldnumbers.  I don't see how to
  811. ;; call macros except via eval [which probably isn't efficient enough for
  812. ;; the main field-getting routines; the user code must be eval'ed in any
  813. ;; case, but I don't want eval in my code], I don't want to have to define
  814. ;; all the functions by hand [that's a pretty weak reason], and field
  815. ;; numbers will be more efficient anyway.  Will have to have a
  816. ;; fieldnumber-fieldname assoc list anyway (for the non-macro user-level
  817. ;; accessors), so can determine fieldnames from fieldnumbers.
  818.  
  819. ;; How to switch the macros when I switch databases (or switch buffers)?
  820. ;; [But wait:  will there be buffer-local functions in Emacs 19?  If so, no
  821. ;; such worries.  Even in 18 I could bind the functions to variables and
  822. ;; then funcall.  (No, I can't.  The point of these is convenience for the
  823. ;; user, you blockhead!)]  Will there be a select-buffer-hook?  Could
  824. ;; constantly check db-name-for-record-defstruct against current-db-name.
  825. ;; The switching will be done via a simple defstruct (though unfortunately
  826. ;; that doesn't undefine the old accessors).  While the accessors don't
  827. ;; check the type of their argument, having different structure names could
  828. ;; still be a win since the bad ones won't be defined.  Or I could just do
  829. ;; the defining and undefining myself:  while no simpler, it wouldn't be
  830. ;; overly complex, and I could undefine the obsolete accessors.  (And it
  831. ;; could all be functions instead of macros, which might be nice:  no
  832. ;; compilation worries.)  For now, defstruct; later, maybe something else.
  833.  
  834. ;; Compiling down is bad:
  835. ;; * can't be sure to get everything since arbitrary Lisp expressions may
  836. ;;   define database-accessing stuff (could even appear in many files).
  837. ;; * may be inefficient to compile down user-level code on the fly
  838. ;;   [probably not too bad, though:  if compiling down is worse than
  839. ;;   executing, then neither can be all that tough]
  840. ;; * implementing it sounds like work.
  841. ;; * may not know fieldnames yet when some code is seen, or the fielnames
  842. ;;   might change later (but before we load the database proper).
  843.  
  844.  
  845. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  846. ;;; Moving from record to record
  847. ;;;
  848.  
  849. ;; None of these functions have anything to do with the display; formatting
  850. ;; the current record is done elsewhere.  These functions don't even set
  851. ;; dbc-link and dbc-index.
  852.  
  853.  
  854. ;; Will it ever be the case that I know a link number without being at the
  855. ;; link?  I suspect that this will be very rare and that typically it will
  856. ;; be fastest to go in the direction of n since it will usually be small.
  857. ;; Besides, I can't be clever if omitting is on.
  858.  
  859. ;; Perhaps I should check for the special case of going to the last record;
  860. ;; but if I know I'm doing that, then I might as well not use these
  861. ;; functions at all, and if I don't, then I'm probably in the usual case
  862. ;; anyway.
  863.  
  864.  
  865. ;; I could pass in the number of database records instead and make the
  866. ;; wraparound tests numeric instead of symbolic.  I need that number
  867. ;; regardless, for the last mod.
  868.  
  869. ;; This doesn't check that it isn't looping forever.  Maybe it should.
  870.  
  871. ;; Is the polarity of wraparoundp wrong??  That is, does it do the wrong
  872. ;; thing, or should it be renamed to no-wraparoundp?
  873.  
  874. ;; Should I have two versions of this, one that doesn't take the last three
  875. ;; arguments and one that does?  Nah, this isn't that much slower than that
  876. ;; would be, and besides, I don't want to maintain two versions of this.
  877.  
  878. ;;   "Arguments DATABASE LINK LINK-INDEX N OMITP MARKP WRAPAROUNDP.
  879. ;; Return a list of (link index) for Nth successor of LINK, whose index in
  880. ;; the database is LINK-INDEX.  N may be negative.  If MARKEDP is non-nil, find
  881. ;; the Nth marked successor of LINK.  If OMITP is non-nil, skip omitted links.
  882. ;; If WRAPAROUNDP is nil, stop at the first or last candidate link (ie,
  883. ;; properly marked and/or non-omitted, or LINK itself if no such are encountered);
  884. ;; if the end of the database stops the search in this way, the returned list
  885. ;; also contains a third element, the number of elements yet to go."
  886. (defun next-link-and-index (database link link-index n omitp markedp wraparoundp)
  887.  
  888.   ;; Recent-link and recent-index remember the last acceptable
  889.   (let ((recent-link link)
  890.     (recent-index link-index)
  891.     (test-link (and wraparoundp
  892.             (if (> n 0)
  893.                 (database-last-link database)
  894.               (database-first-link database)))))
  895.     ;;; Test was:
  896.     ;; (if omitp
  897.     ;;       (if (not (link-omittedp link))
  898.     ;;           (setq recent-link link
  899.     ;;             recent-index link-index
  900.     ;;             n (1- n)))
  901.     ;;     (setq n (1- n)))
  902.     (while (and (> n 0) (not (eq link test-link)))
  903.       (setq link (link-next link)
  904.         link-index (1+ link-index))
  905.       (if (or omitp markedp)
  906.       (if (not (or (and omitp (link-omittedp link))
  907.                (and markedp (not (link-markedp link)))))
  908.           ;; This link passes the tests.
  909.           (setq recent-link link
  910.             recent-index link-index
  911.             n (1- n))
  912.         ;; This link failed the tests.
  913.         ;; I could add infinite-loop testing here.
  914.         )
  915.     (setq n (1- n)))
  916.       )
  917.     (while (and (< n 0) (not (eq link test-link)))
  918.       (setq link (link-prev link)
  919.         link-index (1- link-index))
  920.       (if (or omitp markedp)
  921.       (if (not (or (and omitp (link-omittedp link))
  922.                (and markedp (not (link-markedp link)))))
  923.           ;; This link passes the tests.
  924.           (setq recent-link link
  925.             recent-index link-index
  926.             n (1+ n))
  927.         ;; This link failed the tests.
  928.         ;; I could add infinite-loop testing here.
  929.         )
  930.     (setq n (1+ n))))
  931.     (if (not (zerop n))
  932.     (if (or omitp markedp)
  933.         (list recent-link (database-normalize-index recent-index database) n)
  934.       (list link (database-normalize-index link-index database) n))
  935.       (list link (database-normalize-index link-index database)))))
  936.  
  937. ;; Note that here I can't be clever about counting from the end because of
  938. ;; omitting.
  939.  
  940. ;;   "Return a cons of (link . index) for the link of DATABASE with index N.
  941. ;; If OMITP is non-nil, then omitted links are skipped.
  942. ;; If MARKEDP is non-nil, only marked links count.
  943. ;; 
  944. ;; This shouldn't be used for going to fixed points in the database (like the
  945. ;; last record), both because omitting may change its semantics and
  946. ;; because, in that case, just using (link-prev (database-first-link database))
  947. ;; and (database-no-of-records database) is more efficient."
  948. (defun database-link-and-index (database n omitp markedp)
  949.  
  950.   (next-link-and-index database
  951.                (database-first-link database) 1
  952.                (1- n) omitp markedp nil))
  953. (proclaim-inline database-link-and-index)
  954.  
  955.  
  956. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  957. ;;; Manipulating database records
  958. ;;;
  959.  
  960. ;;   "Add RECORD to DATABASE.  If optional third argument LOCATION is a
  961. ;; number, insert immediately before that index; if it is nil, insert at the
  962. ;; end; if it is t, insert in order."
  963. (defun database-add-record (record database &optional location)
  964.  
  965.   (db-debug-message "database-add-record:  %s" record)
  966.   (let ((this-link (make-link-from-record record)))
  967.     (db-debug-message "database-add-record:  this-link = %s" this-link)
  968.     (if (database-empty-p database)
  969.     (progn
  970.       (db-debug-message "database-add-record:  empty database")
  971.       (link-two this-link this-link)
  972.       (database-set-first-link database this-link))
  973.       (let* ((afterlink (if (numberp location)
  974.                 (database-link database location)
  975.               (database-first-link database)))
  976.          (foo (db-debug-message "database-add-record:  afterlink set"))
  977.          (beforelink (link-prev afterlink)))
  978.     (db-debug-message "database-add-record:  nonempty database")
  979.     (link-two this-link afterlink)
  980.     (link-two beforelink this-link)
  981.     (if (equal 1 location)
  982.         (database-set-first-link database
  983.           this-link))))
  984.     (database-set-no-of-records database
  985.       (1+ (database-no-of-records database)))))
  986.  
  987.  
  988. (defun make-default-record (database)
  989.   (let ((record (make-record database))
  990.     (fno 0))
  991.     (while (< fno (database-no-of-fields database))
  992.       (record-set-field-from-index record fno
  993.                    (recordfieldspec-default-value
  994.                     (database-recordfieldspec database fno))
  995.                    nil)
  996.       (setq fno (1+ fno)))
  997.     record))
  998.  
  999.  
  1000. (defun database-delete-link (database link)
  1001.   (if (eq (link-next link) link)
  1002.       ;; This is the only link in the database.
  1003.       (database-set-first-link database nil)
  1004.     (progn
  1005.       (if (eq link (database-first-link database))
  1006.       (database-set-first-link database
  1007.         (link-next link)))
  1008.       (link-two (link-prev link) (link-next link))))
  1009.   (database-set-no-of-records database
  1010.     (1- (database-no-of-records database))))
  1011.  
  1012. (defun database-delete-record-at-index (database record-index)
  1013.  
  1014.   ;; ...
  1015.  
  1016.   )
  1017.  
  1018.  
  1019. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1020. ;;; Mapping over a database
  1021. ;;;
  1022.  
  1023. ;; The mapping functions dynamically bind maplinks-link and maplinks-index.
  1024.  
  1025. (defmacro maplinks-break ()
  1026.   "Cause the maplinks loop to quit after executing the current iteration.
  1027. This is not a nonlocal exit!  It sets a flag which prevents future iterations.
  1028. Actually, it sets  maplinks-link."
  1029.   (` (setq maplinks-link (link-prev first-link))))
  1030.  
  1031. (defun maplinks (maplinks-func database &optional omit message accumulate)
  1032.   "Apply FUNC to every link in DATABASE.
  1033. If optional third arg OMIT is non-nil, apply FUNC only to unomitted links.
  1034. If optional fourth arg MESSAGE is non-nil, it should be a format string
  1035. containing one numeric \(%d\) specifier.  That message will be issued every
  1036. `db-inform-interval' links.
  1037. If optional fifth arg ACCUMULATE is non-nil, return a list of the results;
  1038. otherwise return nil.
  1039.  
  1040. In the body, variable  maplinks-index  is bound to the index of the link being
  1041. operated upon, and  maplinks-link  is the argument to FUNC.
  1042. The loop may be short-circuited (aborted) by calling `maplinks-break'.
  1043. To avoid the per-link function call overhead, use `maplinks-macro' instead."
  1044.   (let* ((first-link (database-first-link database))
  1045.      (maplinks-link first-link)
  1046.      (maplinks-index 1)
  1047.      results
  1048.      (not-done t))
  1049.     (setq message (and db-inform-interval message))
  1050.     (if first-link
  1051.     (while not-done
  1052.       (if (not (and omit (link-omittedp maplinks-link)))
  1053.           (if accumulate
  1054.           (setq results
  1055.             (cons (funcall maplinks-func maplinks-link) results))
  1056.         (funcall maplinks-func maplinks-link)))
  1057.       (if (and message
  1058.            ;; No test for db-inform-interval because of (setq message )
  1059.            ;; db-inform-interval
  1060.            (zerop (% maplinks-index db-inform-interval)))
  1061.           (db-message message maplinks-index))
  1062.       (setq maplinks-link (link-next maplinks-link)
  1063.         maplinks-index (1+ maplinks-index))
  1064.       (if (eq maplinks-link first-link)
  1065.           (setq not-done nil))))
  1066.     (if accumulate
  1067.     (nreverse results))))
  1068.  
  1069. (defmacro maplinks-macro (maplinks-body database &optional omit message)
  1070.   "Execute BODY for each link in DATABASE, and return nil.
  1071. If optional third arg OMIT is non-nil, execute BODY only for unomitted links.
  1072. If optional fourth arg MESSAGE is non-nil, it should be a format string
  1073. containing one numeric \(%d\) specifier.  That message will be issued every
  1074. `db-inform-interval' links.
  1075.  
  1076. In the body, variable  maplinks-link  is bound to the link being operated upon,
  1077. and  maplinks-index  is bound to its index.
  1078. The loop may be short-circuited (aborted) by calling `maplinks-break'.
  1079. Speed demons should call this instead of `maplinks' to avoid a function call
  1080. overhead per link."
  1081.   (` (let* ((,@ (if omit (` ((omitp (, omit))))))
  1082.         (,@ (if message
  1083.             (` ((message-evalled (and db-inform-interval (, message)))))))
  1084.         (first-link (database-first-link (, database)))
  1085.         (maplinks-link first-link)
  1086.         (maplinks-index 1)
  1087.         (maplinks-not-done t))
  1088.        (,@ (if omit '((db-debug-message "maplinks-macro:  omitp = %s" omitp))))
  1089.        (if first-link
  1090.        (while maplinks-not-done
  1091.          (progn
  1092.            (, (if omit
  1093.               (` (if (not (and omitp
  1094.                        (link-omittedp maplinks-link)))
  1095.                  ;; Body is a single form
  1096.                  (, maplinks-body)))
  1097.             maplinks-body))
  1098.            (,@ (if message
  1099.                '((if (and message-evalled
  1100.                   (zerop (% maplinks-index db-inform-interval)))
  1101.                  (db-message message-evalled maplinks-index)))))
  1102.            (setq maplinks-link (link-next maplinks-link)
  1103.              maplinks-index (1+ maplinks-index))
  1104.            (if (eq maplinks-link first-link)
  1105.            (setq maplinks-not-done nil)))))
  1106.        nil)))
  1107. ;; The second arg is 'sexp because the macro uses its unevalled value.
  1108. (put 'maplinks-macro 'edebug-form-spec '(form sexp &optional form form))
  1109. ;; Gratuitous.
  1110. ;; (put 'maplinks-macro 'lisp-indent-hook 0)
  1111.  
  1112.  
  1113. (fset 'maprecords-break 'maplinks-break)
  1114.  
  1115. ;; The formal parameter name MAPRECORDS-FUNC is different than that of maplinks
  1116. ;; because if they're the same, then when the function created here is run and
  1117. ;; FUNC is looked up, the nearest dynamically enclosing binding of FUNC will be
  1118. ;; returned.  That won't be the one I'm hoping for.
  1119.  
  1120. (defun maprecords (maprecords-func database &optional omit message accumulate)
  1121.   "Apply FUNC to every record in DATABASE.  Return nil.
  1122. If optional third arg OMIT is non-nil, apply FUNC only to unomitted records.
  1123. If optional fourth arg MESSAGE is non-nil, it should be a format string
  1124. containing one numeric \(%d\) specifier.  That message will be issued every
  1125. `db-inform-interval' records.
  1126. If optional fifth arg ACCUMULATE is non-nil, return a list of the results;
  1127. otherwise return nil.
  1128.  
  1129. This is syntactic sugar for a call to `maplinks', which see.
  1130. See also `maprecords-macro'."
  1131.   (maplinks (function (lambda (thislink)
  1132.             (funcall maprecords-func (link-record thislink))))
  1133.         database omit message accumulate))
  1134.  
  1135. (defmacro maprecords-macro (maprecords-body database &optional omit message)
  1136.   "Execute BODY for each record in DATABASE, and return nil.
  1137. If optional third arg OMIT is non-nil, execute BODY only for unomitted records.
  1138. If optional fourth arg MESSAGE is non-nil, it should be a format string
  1139. containing one numeric \(%d\) specifier.  That message will be issued every
  1140. `db-inform-interval' links.
  1141.  
  1142. In the body, variable  maprecords-record  is bound to the record being operated
  1143. upon.
  1144. The loop may be short-circuited (aborted) by calling `maprecords-break'.
  1145.  
  1146. This is syntactic sugar for a call to `maplinks-macro', which see.
  1147. See also `maprecords'."
  1148.   (` (let (maprecords-record)
  1149.        (maplinks-macro
  1150.     (progn
  1151.       (setq maprecords-record (link-record maplinks-link))
  1152.       (, maprecords-body))
  1153.     (, database)
  1154.     (, omit)))))
  1155. (put 'maprecords-macro 'edebug-form-spec '(form sexp &optional form form))
  1156.  
  1157.  
  1158. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1159. ;;; Databases and buffers
  1160. ;;;
  1161.  
  1162. ;; (defun database-delete (database)
  1163. ;;   "Get rid of DATABASE and its associated buffers."
  1164. ;;   (map-data-display-buffers (function (lambda (buf)
  1165. ;;                     (in-buffer buf
  1166. ;;                       (db-exit t))))
  1167. ;;                 database))
  1168.  
  1169. (defun map-data-display-buffers (function database)
  1170.   "Apply FUNCTION to each data display buffer of DATABASE."
  1171.   (let ((dd-buffers (database-clean-data-display-buffers database)))
  1172.     (while dd-buffers
  1173.       (funcall function (car dd-buffers))
  1174.       (setq dd-buffers (cdr dd-buffers)))))
  1175.  
  1176.  
  1177. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1178. ;;; Printing
  1179. ;;;
  1180.  
  1181. ;; I realize that this entire section is a hack.
  1182.  
  1183.  
  1184. (defun print-database (database)
  1185.   (maprecords (function (lambda (record)
  1186.               (print-record record
  1187.                     database)))
  1188.           database))
  1189.  
  1190. (defun print-record (record database)
  1191.   (let ((fno 0)
  1192.     (no-of-fields (database-no-of-fields database))
  1193.     (fieldnames (database-fieldnames database)))
  1194.     (princ "\n")
  1195.     (while (< fno no-of-fields)
  1196.       (princ (format "%s:  %s\n"
  1197.              (aref fieldnames fno) (aref record fno)))
  1198.       (setq fno (1+ fno)))))
  1199.  
  1200. ;; This is getting a compilation error:  an open-coded lambda is being
  1201. ;; called with too few arguments.
  1202.  
  1203. ;; The records are assumed to be different.
  1204. ;; A . next to a field means an inessential difference.
  1205. ;; A * means an essential difference.
  1206. (defun print-compare-records (record1 record2 database)
  1207.   (let ((field-number 0)
  1208.     ;; maybe get rid of these bindings and trust the compiler to be smart.
  1209.     (max-field-number (database-no-of-fields database))
  1210.     recordfieldspec
  1211.     fieldname
  1212.     order-function
  1213.     field1 field2)
  1214.     (princ "\n")
  1215.     (while (< field-number max-field-number)
  1216.       (setq recordfieldspec (database-recordfieldspec database field-number)
  1217.         fieldname (fieldnumber->fieldname field-number database)
  1218.         order-function (recordfieldspec-order-function recordfieldspec)
  1219.         field1 (aref record1 field-number)
  1220.         field2 (aref record2 field-number))
  1221.       (cond ((equal field1 field2)
  1222.          (princ (format "  %s:  %s\n" fieldname field1)))
  1223.         ((and order-function
  1224.           (zerop (funcall order-function field1 field2)))
  1225.          (princ (format ". %s:  %s\n. %s:  %s\n"
  1226.                 fieldname field1 fieldname field2)))
  1227.         (t
  1228.           (princ (format "* %s:  %s\n* %s:  %s\n"
  1229.                 fieldname field1 fieldname field2))))
  1230.       (setq field-number (1+ field-number)))))
  1231.  
  1232. ;; (defun print-database-old (database)
  1233. ;;   (let ((fieldnames (database-fieldnames database)))
  1234. ;;     (maplinks (function (lambda (link)
  1235. ;;               (print-record (link-record link)
  1236. ;;                     fieldnames)))
  1237. ;;           database)))
  1238. ;;
  1239. ;; (defun print-database-alt-old (database)
  1240. ;;   (let ((fieldnames (database-fieldnames database)))
  1241. ;;     (maprecords (lambda (record)
  1242. ;;           (print-record record fieldnames))
  1243. ;;         database)))
  1244.  
  1245. ;; (defun print-record-old (record fieldnames)
  1246. ;;   (let ((slot-number 0)
  1247. ;;     (fn-length (length fieldnames)))
  1248. ;;     (princ "\n")
  1249. ;;     (while (< slot-number fn-length)
  1250. ;;       (princ (format "%s:  %s\n"
  1251. ;;              (aref fieldnames slot-number) (aref record slot-number)))
  1252. ;;       (setq slot-number (1+ slot-number)))))
  1253.  
  1254. ;;; db-rep.el ends here
  1255.