[comp.emacs] Is there an Address List program?

aks@HUB.UCSB.EDU (Alan Stebbens) (01/03/91)

> Has anyone written programs to do address and telephone lists within
> emacs? It would need to handle a database with name, address, and
> telephone fields, sort by names, and output reports.

Here is a copy of the "rolodex" package for Emacs.  I am not the author;
read the enclosed stuff.

Alan Stebbens        <aks@hub.ucsb.edu>             (805) 893-3221
     Center for Computational Sciences and Engineering (CCSE)
          University of California, Santa Barbara (UCSB)
           3111 Engineering I, Santa Barbara, CA 93106


============================= cut here ===================================
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  rolo-logic.el rolo-simple.el rolo.el
# Wrapped by aks@anywhere on Wed Jan  2 12:24:19 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f rolo-logic.el -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"rolo-logic.el\"
else
echo shar: Extracting \"rolo-logic.el\" \(7746 characters\)
sed "s/^X//" >rolo-logic.el <<'END_OF_rolo-logic.el'
X;;!emacs
X;;
X;; FILE:         rolo-logic.el
X;; SUMMARY:      Performs logical retrievals on rolodex files
X;; USAGE:        GNU Emacs Lisp Library
X;;
X;; AUTHOR:       Bob Weiner
X;; ORG:          Motorola, Inc., Communications Sector, Applied Research
X;; E-MAIL:       USENET:  weiner@novavax.UUCP
X;;
X;; ORIG-DATE:    13-Jun-89 at 22:57:33
X;; LAST-MOD:     16-Jun-89 at 18:05:05 by Bob Weiner
X;;
X;; Copyright (C) 1989 Bob Weiner and Free Software Foundation, Inc.
X;; Available for use and distribution under the same terms as GNU Emacs.
X;;
X;; This file is not yet part of GNU Emacs.
X;;
X;; DESCRIPTION:  
X;;
X;;  INSTALLATION:
X;;
X;;   See also rolo.el.  These functions are separated from rolo.el since many
X;;   users may never want or need them.  They can be automatically loaded when
X;;   desired by adding the following to one of your Emacs init files:
X;;
X;;    (autoload 'rolo-logic "rolo-logic"
X;;      "Logical rolodex search filters."
X;;     t)
X;;
X;;  FEATURES:
X;;
X;;   1.  One command, 'rolo-logic' which takes a logical search expression as
X;;       an argument and displays any matching entries.
X;;
X;;   2.  Logical 'and', 'or', 'not', and 'xor' rolodex entry retrieval filter
X;;       functions. They take any number of string or boolean arguments and
X;;       may be nested.  NOTE THAT THESE FUNCTIONS SHOULD NEVER BE CALLED
X;;       DIRECTLY UNLESS THE FREE VARIABLES 'start' and 'end' ARE BOUND
X;;       BEFOREHAND.
X;;
X;;  EXAMPLE:
X;;
X;;     (rolo-logic '(lambda ()
X;;                     (rolo-and
X;;                        (rolo-xor "secretary" "Tool-And-Die")
X;;                        "secretary")))
X;;
X;;   would find all non-Tool-And-Die Corp. secretaries in your rolodex.
X;;
X;;
X;;
X;;   The logical matching routines are not really optimal, but then most
X;;   rolodex files are not terribly lengthy either.
X;;
X;; DESCRIP-END.
X
X(require 'rolo)
X
X(defun rolo-logic (func &optional in-bufs count-only include-sub-entries
X			      no-sub-entries-out)
X  "Apply FUNC to all entries in optional IN-BUFS, display entries where FUNC is non-nil.
XIf IN-BUFS is nil, 'rolo-file-list' is used.  If optional COUNT-ONLY is
Xnon-nil, don't display entries, return count of matching entries only.  If
Xoptional INCLUDE-SUB-ENTRIES flag is non-nil, FUNC will be applied across all
Xsub-entries at once.  Default is to apply FUNC to each entry and sub-entry
Xseparately.  Entries are displayed with all of their sub-entries unless
XINCLUDE-SUB-ENTRIES is nil and optional NO-SUB-ENTRIES-OUT flag is non-nil.
XFUNC should use the free variables 'start' and 'end' which contain the limits
Xof the region on which it should operate.  Returns number of applications of
XFUNC that return non-nil."
X  (interactive "xLogic function of no arguments, (lambda () (<function calls>): ")
X  (let ((obuf (current-buffer))
X	(display-buf (if count-only
X			 nil
X		       (prog1 (set-buffer (get-buffer-create rolo-display-buffer))
X			 (setq buffer-read-only nil)
X			 (erase-buffer)))))
X    (let ((result
X	    (mapcar
X	      '(lambda (in-bufs)
X		 (rolo-map-logic func in-bufs count-only include-sub-entries
X				 no-sub-entries-out))
X	      (cond ((null in-bufs) rolo-file-list)
X		    ((listp in-bufs) in-bufs)
X		    ((list in-bufs))))))
X      (let ((total-matches (apply '+ result)))
X	(if (or count-only (= total-matches 0))
X	    nil
X	  (pop-to-buffer display-buf)
X	  (goto-char (point-min))
X	  (set-buffer-modified-p nil)
X	  (setq buffer-read-only t)
X	  (let ((buf (get-buffer-window obuf)))
X	    (if buf (select-window buf) (switch-to-buffer buf))))
X	(if (interactive-p)
X	    (message (concat (if (= total-matches 0) "No" total-matches)
X			     " matching entr"
X			     (if (= total-matches 1) "y" "ies")
X			     " found in rolodex.")))
X	total-matches))))
X
X(defun rolo-map-logic (func rolo-buf &optional count-only
X			    include-sub-entries no-sub-entries-out)
X  "Apply FUNC to all entries in ROLO-BUF, write to buffer entries where FUNC is non-nil.
XIf optional COUNT-ONLY is non-nil, don't display entries, return count of
Xmatching entries only.  If optional INCLUDE-SUB-ENTRIES flag is non-nil, FUNC
Xwill be applied across all sub-entries at once.  Default is to apply FUNC to
Xeach entry and sub-entry separately.  Entries are displayed with all of their
Xsub-entries unless INCLUDE-SUB-ENTRIES is nil and optional NO-SUB-ENTRIES-OUT
Xflag is non-nil.  FUNC should use the free variables 'start' and 'end' which
Xcontain the limits of the region on which it should operate.  Returns number
Xof applications of FUNC that return non-nil."
X  (if (or (bufferp rolo-buf)
X	  (if (file-exists-p rolo-buf)
X	      (setq rolo-buf (find-file-noselect rolo-buf t))))
X      (let* ((display-buf (set-buffer (get-buffer-create rolo-display-buffer)))
X	     (buffer-read-only))
X	(let ((hdr-pos) (num-found 0))
X	  (set-buffer rolo-buf)
X	  (goto-char (point-min))
X	  (if (re-search-forward rolo-hdr-regexp nil t 2)
X	      (progn (forward-line)
X		     (setq hdr-pos (cons (point-min) (point)))))
X	  (let* ((start)
X		 (end)
X		 (end-entry-hdr)
X		 (curr-entry-level))
X	    (while (re-search-forward rolo-entry-regexp nil t)
X	      (setq start (save-excursion (beginning-of-line) (point))
X		    next-entry-exists nil
X		    end-entry-hdr (point)
X		    curr-entry-level (buffer-substring start end-entry-hdr)
X		    end (rolo-to-entry-end include-sub-entries curr-entry-level))
X	      (let ((fun (funcall func)))
X		(or count-only 
X		    (and fun (= num-found 0) hdr-pos
X			 (append-to-buffer display-buf
X					   (car hdr-pos) (cdr hdr-pos))))
X		(if fun 
X		    (progn (goto-char end)
X			   (setq num-found (1+ num-found)
X				 end (if (or include-sub-entries
X					     no-sub-entries-out)
X					 end
X				       (goto-char (rolo-to-entry-end
X						    t curr-entry-level))))
X			   (or count-only
X			       (append-to-buffer display-buf start end)))
X		  (goto-char end-entry-hdr)))))
X	  num-found))
X    0))
X
X
X;;
X;; INTERNAL FUNCTIONS.
X;;
X
X;; Do NOT call the following functions directly.
X;; Send them as parts of a lambda expression to 'rolo-logic'.
X
X(defun rolo-not (&rest list-of-pats)
X  "Logical <not> rolodex entry filter.  LIST-OF-PATS is a list of pattern elements.
XEach element may be t, nil, or a string."
X  (let ((pat-list list-of-pats)
X	(pat))
X    (while (and pat-list
X		(or (not (setq pat (car pat-list)))
X		    (and (not (eq pat t))
X			 (goto-char start)
X			 (not (search-forward pat end t)))))
X      (setq pat-list (cdr pat-list)))
X    (if pat-list nil t)))
X
X(defun rolo-or (&rest list-of-pats)
X  "Logical <or> rolodex entry filter.  LIST-OF-PATS is a list of pattern elements.
XEach element may be t, nil, or a string."
X  (let ((pat-list list-of-pats)
X	(pat))
X    (while (and pat-list
X		(or (not (setq pat (car pat-list)))
X		    (and (not (eq pat t))
X			 (goto-char start)
X			 (not (search-forward pat end t)))))
X      (setq pat-list (cdr pat-list)))
X    (if pat-list t nil)))
X
X(defun rolo-xor (&rest list-of-pats)
X  "Logical <xor> rolodex entry filter.  LIST-OF-PATS is a list of pattern elements.
XEach element may be t, nil, or a string."
X  (let ((pat-list list-of-pats)
X	(pat)
X	(matches 0))
X    (while (and pat-list
X		(or (not (setq pat (car pat-list)))
X		    (and (or (eq pat t)
X			     (not (goto-char start))
X			     (search-forward pat end t))
X			 (setq matches (1+ matches)))
X		    t)
X		(< matches 2))
X      (setq pat-list (cdr pat-list)))
X    (= matches 1)))
X
X(defun rolo-and (&rest list-of-pats)
X  "Logical <and> rolodex entry filter.  LIST-OF-PATS is a list of pattern elements.
XEach element may be t, nil, or a string."
X  (let ((pat-list list-of-pats)
X	(pat))
X    (while (and pat-list
X		(setq pat (car pat-list))
X		(or (eq pat t)
X		    (not (goto-char start))
X		    (search-forward pat end t)))
X      (setq pat-list (cdr pat-list)))
X    (if pat-list nil t)))
X
X(provide 'rolo-logic)
END_OF_rolo-logic.el
if test 7746 -ne `wc -c <rolo-logic.el`; then
    echo shar: \"rolo-logic.el\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f rolo-simple.el -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"rolo-simple.el\"
else
echo shar: Extracting \"rolo-simple.el\" \(2466 characters\)
sed "s/^X//" >rolo-simple.el <<'END_OF_rolo-simple.el'
X;;!emacs
X;;
X;; FILE:         rolo-simple.el
X;; SUMMARY:      Very simple routines to display entries matching a string
X;;                 from a rolodex file.  For those who find "rolo.el" too
X;;                 intimidating.
X;; USAGE:        GNU Emacs Lisp Library
X;;
X;; AUTHOR:       Bob Weiner
X;; ORG:          Motorola, Inc., Communications Sector, Applied Research
X;; E-MAIL:       USENET:  weiner@novavax.UUCP
X;;
X;; ORIG-DATE:     7-Jun-89 at 22:08:29
X;; LAST-MOD:     16-Jun-89 at 00:27:20 by Bob Weiner
X;;
X;; Copyright (C) 1989 Bob Weiner and Free Software Foundation, Inc.
X;; Available for use and distribution under the same terms as GNU Emacs.
X;;
X;; This file is not part of GNU Emacs.
X;;
X;; DESCRIPTION:  
X;; DESCRIP-END.
X
X(defconst rolo-file "~/.rolodex.otl"
X  "User-specific file in which rolodex entries are stored.")
X
X(defconst rolo-entry-regexp "^\*+"
X  "Regular expression to match the beginning of a rolodex entry.")
X
X(defconst rolo-hdr-regexp "^==="
X  "Regular expression to match the last line of the rolodex file header.
XThis header is inserted into rolo-display-buffer before any entries are
Xadded.")
X
X(defconst rolo-display-buffer "*Rolodex*"
X  "Buffer used to display set of last matching rolodex entries.")
X
X(defun rolo-fgrep (string)
X  "Find entries in rolo-file matching STRING.
XThe rolo-file consists of a header terminated by a line which matches
Xrolo-hdr-regexp.  And rolodex entries beginning with rolo-entry-regexp."
X  (interactive "sRolodex string to match: ")
X  (let ((obuf (current-buffer)))
X    (save-excursion
X      (set-buffer (get-buffer-create rolo-display-buffer))
X      (erase-buffer)
X      (find-file rolo-file)
X      (goto-char (point-min))
X      (save-excursion
X	(if (re-search-forward rolo-hdr-regexp nil t)
X	    (progn (forward-line)
X		   (append-to-buffer rolo-display-buffer
X				     (point-min) (point)))))
X      (re-search-forward rolo-entry-regexp nil t)
X      (beginning-of-line)
X      (while (search-forward string nil t)
X	(re-search-backward rolo-entry-regexp nil t)
X	(let ((start (point)))
X	  (if (re-search-forward rolo-entry-regexp nil t 2)
X	      (beginning-of-line)
X	    (goto-char (point-max)))
X	  (append-to-buffer rolo-display-buffer start (point)))))
X    (pop-to-buffer rolo-display-buffer)
X    (set-buffer-modified-p nil)
X    (select-window (get-buffer-window obuf))))
X
X(defun rolo-edit ()
X  "Display user-specific rolodex file for editing."
X  (interactive)
X  (find-file rolo-file))
X
X(provide 'rolo)
END_OF_rolo-simple.el
if test 2466 -ne `wc -c <rolo-simple.el`; then
    echo shar: \"rolo-simple.el\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f rolo.el -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"rolo.el\"
else
echo shar: Extracting \"rolo.el\" \(16052 characters\)
sed "s/^X//" >rolo.el <<'END_OF_rolo.el'
X;;!emacs
X;;
X;; FILE:         rolo.el
X;; SUMMARY:      Retrieves and sorts entries from a list of rolodex files
X;; USAGE:        GNU Emacs Lisp Library
X;;
X;; AUTHOR:       Bob Weiner
X;; ORG:          Motorola, Inc., Communications Sector, Applied Research
X;; E-MAIL:       USENET:  weiner@novavax.UUCP
X;;
X;; ORIG-DATE:     7-Jun-89 at 22:08:29
X;; LAST-MOD:     16-Jun-89 at 14:56:11 by Bob Weiner
X;;
X;; Copyright (C) 1989 Bob Weiner and Free Software Foundation, Inc.
X;; Available for use and distribution under the same terms as GNU Emacs.
X;;
X;; This file is not yet part of GNU Emacs.
X;; This could use a key or field limited searching capability.
X;;
X;; DESCRIPTION:  
X;;
X;;  All I wanted to do was look up a phone number quickly . . .
X;;
X;;  FEATURES:
X;;
X;;   1.  Multiple rolodex files.
X;;
X;;   2.  Hierarchical rolodex entries as in:
X;;        *    Company
X;;        **     Manager
X;;        ***      Underlings
X;;
X;;       Searching for Manager turns up all Underlings.  Searching for
X;;       Company retrieves all listed employees.
X;;
X;;       This hierarchical system has proved very effective for retrieving
X;;       computer system administration problem reports by vendor name,
X;;       problem number or by subject area without having to resort to a
X;;       database system.
X;;
X;;   4.  String and regular expression searching capabilities.  Ability to
X;;       restrict number of matches or to report number of matches without
X;;       displaying entries.
X;;
X;;   5.  Smart sorting of entries by hierarchy level.
X;;
X;;   See "rolo-logic.el" for logical search functions (and, or, not, xor).
X;;
X;;
X;;  FOR NON-PROGRAMMERS:
X;;
X;;   Modify the second file entry in the definition of 'rolo-file-list'
X;;   before using this package.
X;;
X;;   To add personal files to rolo-file-list--when you find these functions are
X;;   useful for any sort of list lookup--add the following to your ~/.emacs
X;;   file (substituting where you see <fileN>):
X;;
X;;      (require 'rolo)
X;;      (setq rolo-file-list (append rolo-file-list '("<file1>" "<file2>")))
X;;
X;;   The only command you absolutely need that is defined here is
X;;   'rolo-fgrep'; it locates any matching entries in a set of rolodex files.
X;;   I recommend that you add the following key binding to one of your site
X;;   specific Emacs initialization files:
X;;
X;;         (global-set-key "\C-x4r" 'rolo-fgrep)
X;;
X;;   Calling 'rolo-fgrep' with a prefix argument limits the number of matches
X;;   to the specified number of entries.
X;;
X;;   The following commands are also provided:
X;;     'rolo-grep' finds all entries matching a regular expression in a set
X;;       of rolodex files;
X;;     'rolo-edit' edits one's personal rolodex file;
X;;     'rolo-sort' sorts all levels of entries in a rolodex file.
X;;
X;;   To make the 'rolo' library load whenever you initially call any of these
X;;   functions, add the following to any of your Emacs init files:
X;;
X;;     (autoload 'rolo-fgrep "rolo"
X;;       "Find entries in rolodex."
X;;       t)	 
X;;     (autoload 'rolo-grep "rolo"
X;;       "Find entries in rolodex."
X;;       t)	 
X;;     (autoload 'rolo-edit "rolo"
X;;       "Edit personal rolodex file."
X;;       t)
X;;     (autoload 'rolo-sort "rolo"
X;;       "Sort rolodex file."
X;;       t)
X;;     
X;;     
X;;   Entries in rolodex files are separated by patterns matching
X;;   'rolo-entry-regexp'.  Each entry may have any number of sub-entries
X;;   which represent the next level down in the entry hierarchy.
X;;   Sub-entries' separator patterns are always longer than their parents'.
X;;   For example, if an entry began with '*' then its sub-entries would begin
X;;   with '**' and so on.  Blank lines in rolodex files will not end up where
X;;   you want them if you use the rolo-sort commands; therefore, blank lines
X;;   are not recommended.
X;;
X;;   The reasons that the entries in 'rolo-file-list' have ".otl" suffixes
X;;   are so that they do not conflict with file names that other rolodex
X;;   programs might use and so that they are edited in 'outline-mode' by
X;;   default.  If you want the latter behavior, uncomment and add something
X;;   like the following to one of your GNU Emacs initialization files:
X;;
X;;     ;; Add to the list of suffixes that causes automatic mode invocation
X;;     (setq auto-mode-alist
X;;        (append '(("\\.otl$" . outline-mode)) auto-mode-alist))
X;;
X;;   Here is a snippet from our group rolodex file (the ';'s should be
X;;   removed of course and the '*'s should begin at the start of the line):
X;;
X;;=============================================================================
X;;			      GROUP ROLODEX
X;;    <Last Name>, <First Name>  <Co/Categ>   W<Work #>   H<Home #>  P<Pager #>
X;;					      F<Fax #>    M<Modem #> C<Cellular #>
X;;					      R<Other-radio #>
X;;        <Address>	   <Miscellaneous Info, Key Words>
X;;=============================================================================
X;;*   EX594, Digital-Systems-Research
X;;**  Weiner, Bob		  Motorola    W2087	             P7-7489
X;;	  FL19, L-1035
X;;
X;;
X;;  FOR PROGRAMMERS:
X;;
X;;   If you change the value of 'rolo-entry-regexp', you will have to modify
X;;   'rolo-sort'.
X;;
X;;   The following additional functions are provided:
X;;     'rolo-sort-level' sorts a specific level of entries in a rolodex file;
X;;     'rolo-map-level' runs a user specified function on a specific level of
X;;       entries in a rolodex file.
X;;     'rolo-fgrep-file', same as 'rolo-fgrep' but operates on a single file
X;;     'rolo-grep-file, same as 'rolo-grep' but operates on a single file
X;;
X;;   The buffers containing the rolodex files are not killed after a search
X;;   on the assumption that another search is likely to follow within this
X;;   Emacs session.  You may wish to change this behavior.
X;;
X;;   This code works fine on properly formatted rolodex files but probably
X;;   will fail on certain improperly formatted ones.
X;;
X;; DESCRIP-END.
X
X(defvar rolo-file-list '("~/.rolodex.otl")
X  "*List of files containing rolodex entries.
XThe first file should be a user-specific rolodex file, typically in the home
Xdirectory.  The second file is often a shared, group-specific rolodex file.
X
XA rolo-file consists of:
X   (1) an optional header beginning with and ending with a line which matches
X       rolo-hdr-regexp;
X   (2) one or more rolodex entries beginning with rolo-entry-regexp, which
X       may be nested.")
X
X(defconst rolo-entry-regexp "^\*+"
X  "Regular expression to match the beginning of a rolodex entry.
XThis pattern must match the beginning of the line.  Entries may be nested
Xthrough the use of increasingly longer beginning patterns.")
X
X(defconst rolo-hdr-regexp "^==="
X  "Regular expression to match the first and last lines of rolodex file headers.
XThis header is inserted into rolo-display-buffer before any entries from the
Xfile are added.")
X
X(defconst rolo-display-buffer "*Rolodex*"
X  "Buffer used to display set of last matching rolodex entries.")
X
X(defun rolo-fgrep (string &optional max-matches rolo-file count-only)
X  "Display rolodex entries matching STRING, to a maximum of prefix arg MAX-MATCHES,
Xin file(s) from optional ROLO-FILE or rolo-file-list.  Default is to find all
Xmatching entries.  Each entry is displayed with all of its sub-entries.
XOptional COUNT-ONLY non-nil means don't display matching entries.
XReturns number of entries matched.  See also documentation for the variable
Xrolo-file-list."
X  (interactive "sRolodex string to match: \nP")
X  (let ((total-matches
X	  (rolo-grep (regexp-quote string) max-matches rolo-file count-only)))
X    (if (interactive-p)
X	(message (concat (if (= total-matches 0) "No" total-matches)
X			 " matching entr"
X			 (if (= total-matches 1) "y" "ies")
X			 " found in rolodex.")))
X    total-matches))
X
X(defun rolo-grep (regexp &optional max-matches rolo-bufs count-only)
X  "Display rolodex entries matching REGEXP, to a maximum of prefix arg MAX-MATCHES,
Xin buffer(s) from optional ROLO-BUFS or rolo-file-list.  Default is to find all
Xmatching entries.  Each entry is displayed with all of its sub-entries.
XOptional COUNT-ONLY non-nil means don't display matching entries.
XReturns number of entries matched.  See also documentation for the variable
Xrolo-file-list."
X  (interactive "sRolodex regular expression to match: \nP")
X  (let ((rolo-file-list
X	  (cond ((null rolo-bufs) rolo-file-list)
X		((listp rolo-bufs) rolo-bufs)
X		((list rolo-bufs))))
X	(obuf (current-buffer))
X	(display-buf (if count-only
X			 nil
X		       (set-buffer (get-buffer-create rolo-display-buffer))))
X	(total-matches 0))
X    (if count-only nil (setq buffer-read-only nil) (erase-buffer))
X    (mapcar '(lambda (file)
X	       (if (or (null max-matches) (> max-matches 0))
X		   (let ((num-matched
X			   (rolo-grep-file file regexp max-matches count-only)))
X		     (setq total-matches (+ total-matches num-matched))
X		     (or (null max-matches)
X			 (setq max-matches (- max-matches num-matched))))))
X	    rolo-file-list)
X    (if (or count-only (= total-matches 0))
X	nil
X      (pop-to-buffer display-buf)
X      (goto-char (point-min))
X      (set-buffer-modified-p nil)
X      (setq buffer-read-only t)
X      (let ((buf (get-buffer-window obuf)))
X	(if buf (select-window buf) (switch-to-buffer buf))))
X    (if (interactive-p)
X	(message (concat (if (= total-matches 0) "No" total-matches)
X			 " matching entr"
X			 (if (= total-matches 1) "y" "ies")
X			 " found in rolodex.")))
X    total-matches))
X
X(defun rolo-edit ()
X  "Display personal rolodex file for editing."
X  (interactive)
X  (find-file (car rolo-file-list)))
X
X(defun rolo-sort (&optional rolo-file)
X  "Sort up to 14 levels of entries in ROLO-FILE (default is personal rolodex file).
XUses default rolo-entry-regexp for sort.  Returns list of number of groupings
Xat each entry level." 
X  (interactive "fRolodex file to sort: ")
X  (if (not rolo-file) (setq rolo-file (car rolo-file-list)))
X  (let ((level-regexp (regexp-quote "**************"))
X	(entries-per-level-list)
X	(n))
X    (while (not (equal level-regexp ""))
X      (setq n (rolo-sort-level rolo-file level-regexp))
X      (if (or (/= n 0) entries-per-level-list)
X	  (setq entries-per-level-list
X		(append (list n) entries-per-level-list)))
X      (setq level-regexp (substring level-regexp 0 (- (length level-regexp) 2))))
X    entries-per-level-list))
X
X(defun rolo-sort-level (rolo-file level-regexp &optional max-groupings)
X  "Sort groupings of entries in ROLO-FILE at hierarchy level given by LEVEL-REGEXP
Xto a maximum of optional MAX-GROUPINGS.  Nil value of MAX-GROUPINGS means all
Xgroupings at the given level.  LEVEL-REGEXP should simply match the text of
Xany rolodex entry of the given level, not the beginning of a line (^); an
Xexample, might be (regexp-quote \"**\") to match level two.  Returns number
Xof groupings sorted."
X  (interactive "sRolodex file to sort: \nRegexp to match text of level's entries: \nP")
X  (rolo-map-level
X    '(lambda (start end) (sort-lines nil start end))
X    rolo-file
X    level-regexp
X    max-groupings))
X
X(defun rolo-map-level (func rolo-buf level-regexp &optional max-groupings)
X  "Perform FUNC on each grouping of ROLO-BUF entries at hierarchy level LEVEL-REGEXP
Xto a maximum of optional argument MAX-GROUPINGS.  Nil value of MAX-GROUPINGS
Xmeans all groupings at the given level.  FUNC should take two arguments, the
Xstart and the end of the region that it should manipulate.  LEVEL-REGEXP
Xshould simply match the text of any rolodex entry of the given level, not the
Xbeginning of a line (^); an example, might be (regexp-quote \"**\") to match
Xlevel two.  Returns number of groupings matched."
X  (if (and (or (null max-groupings) (< 0 max-groupings))
X	   (or (bufferp rolo-buf)
X	       (if (file-exists-p rolo-buf)
X		   (setq rolo-buf (find-file-noselect rolo-buf t)))))
X      (let ((num-found 0)
X	    (exact-level-regexp (concat "^\\(" level-regexp "\\)[ \t\n]"))
X	    (outline-regexp rolo-entry-regexp)
X	    (level-len))
X	;; Load 'outline' library since its functions are used here.
X	(if (not (boundp 'outline-mode-map))
X	    (load-library "outline"))
X	(set-buffer rolo-buf)
X	(goto-char (point-min))
X	;; Pass buffer header if it exists
X	(if (re-search-forward rolo-hdr-regexp nil t 2)
X	    (forward-line))
X	(while (and (or (null max-groupings) (< num-found max-groupings))
X		    (re-search-forward exact-level-regexp nil t))
X	  (setq num-found (1+ num-found))
X	  (let* ((opoint (prog1 (point) (beginning-of-line)))
X		 (grouping-start (point))
X		 (start grouping-start)
X		 (level-len (or level-len (- (1- opoint) start)))
X		 (next-level-len)
X		 (next-entry-exists)
X		 (grouping-end)
X		 (no-subtree))
X	    (while (and (progn
X			  (if (setq next-entry-exists
X				    (re-search-forward rolo-entry-regexp nil t 2))
X			      (setq next-level-len (- (point)
X						      (progn (beginning-of-line)
X							     (point)))
X				    grouping-end (< next-level-len level-len)
X				    no-subtree (<= next-level-len level-len))
X			    (setq grouping-end t no-subtree t)
X			    (goto-char (point-max)))
X			  (let ((end (point)))
X			    (goto-char start)
X			    (hide-subtree) ; And hide multiple lines of entry
X			    ;; Move to start of next entry at equal or higher level
X			    (setq start
X				  (if no-subtree
X				      end
X				    (if (re-search-forward rolo-entry-regexp
X							   nil t)
X					(progn (beginning-of-line) (point))
X				      (point-max))))
X			    ;; Remember last expression in 'progn' must always
X			    ;; return non-nil
X			    (goto-char start)))
X			(not grouping-end)))
X	    (let ((end (point)))
X	      (goto-char grouping-start)
X	      (funcall func grouping-start end)
X	      (goto-char end))))
X	(show-all)
X	num-found)
X    0))
X
X(defun rolo-fgrep-file (rolo-buf string &optional max-matches count-only)
X  "Retrieve entries in ROLO-BUF matching STRING to a maximum of optional MAX-MATCHES.
XNil value of MAX-MATCHES means find all matches.  Optional COUNT-ONLY non-nil
Xmeans don't retrieve matching entries.  Returns number of matching entries
Xfound."
X  (rolo-grep-file rolo-buf (regexp-quote string) max-matches count-only))
X
X(defun rolo-grep-file (rolo-buf regexp &optional max-matches count-only)
X  "Retrieve entries in ROLO-BUF matching REGEXP to a maximum of optional MAX-MATCHES.
XNil value of MAX-MATCHES means find all matches.  Optional COUNT-ONLY non-nil
Xmeans don't retrieve matching entries.  Returns number of matching entries
Xfound."
X  (if (and (or (null max-matches) (< 0 max-matches))
X	   (or (bufferp rolo-buf)
X	       (if (file-exists-p rolo-buf)
X		   (setq rolo-buf (find-file-noselect rolo-buf t)))))
X      (let ((hdr-pos) (num-found 0) (curr-entry-level))
X	(set-buffer rolo-buf)
X	(goto-char (point-min))
X	(if (re-search-forward rolo-hdr-regexp nil t 2)
X	    (progn (forward-line)
X		   (setq hdr-pos (cons (point-min) (point)))))
X	(re-search-forward rolo-entry-regexp nil t)
X	(while (and (or (null max-matches) (< num-found max-matches))
X		    (re-search-forward regexp nil t))
X	  (re-search-backward rolo-entry-regexp nil t)
X	  (let ((start (point))
X		(next-entry-exists))
X	    (re-search-forward rolo-entry-regexp nil t)
X	    (rolo-to-entry-end
X	      t (setq curr-entry-level (buffer-substring start (point))))
X	    (or count-only
X		(and (= num-found 0) hdr-pos
X		     (progn (append-to-buffer rolo-display-buffer
X					      (car hdr-pos) (cdr hdr-pos)))))
X	    (setq num-found (1+ num-found))
X	    (or count-only
X		(append-to-buffer rolo-display-buffer start (point)))))
X	num-found)
X    0))
X
X;;
X;; INTERNAL FUNCTIONS.
X;;
X
X(defun rolo-to-entry-end (&optional include-sub-entries curr-entry-level)
X"Go to end of whole entry if optional INCLUDE-SUB-ENTRIES is non-nil.
XCURR-ENTRY-LEVEL is a string whose length is the same as the last found entry
Xheader.  If INCLUDE-SUB-ENTRIES is nil, CURR-ENTRY-LEVEL is not needed."
X  (while (and (setq next-entry-exists
X		    (re-search-forward rolo-entry-regexp nil t))
X	      include-sub-entries
X	      (> (- (point) (save-excursion
X			      (beginning-of-line)
X			      (point)))
X		 (length curr-entry-level))))
X  (if next-entry-exists
X      (progn (beginning-of-line) (point))
X    (goto-char (point-max))))
X
X	  
X(provide 'rolo)
END_OF_rolo.el
if test 16052 -ne `wc -c <rolo.el`; then
    echo shar: \"rolo.el\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0