[comp.emacs] GNU Emacs rolodex system

weiner@novavax.UUCP (Bob Weiner) (06/17/89)

Here is a feature laden rolodex system I cooked up so that I could
rapidly find phone numbers and all sorts of random bits of information
that were previously scattered in many different files.  A simpler
version of the rolodex for simpler folks is also included.

This shar archive also includes an update of my smart-menu package (used
in conjunction with smart-key) which includes a number of improvements
and supports the rolodex system.

Please let me know any innovative uses that you find for the rolodex
system.

=================================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.el rolo-logic.el rolo-simple.el smart-menu.el
# Wrapped by root@ar_weiner on Fri Jun 16 18:09:17 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'rolo.el' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rolo.el'\"
else
echo shar: Extracting \"'rolo.el'\" \(16075 characters\)
sed "s/^X//" >'rolo.el' <<'END_OF_FILE'
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(defconst rolo-file-list '("~/.rolodex.otl" "~ex594/.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_FILE
if test 16075 -ne `wc -c <'rolo.el'`; then
    echo shar: \"'rolo.el'\" unpacked with wrong size!
fi
# end of 'rolo.el'
fi
if test -f 'rolo-logic.el' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rolo-logic.el'\"
else
echo shar: Extracting \"'rolo-logic.el'\" \(7746 characters\)
sed "s/^X//" >'rolo-logic.el' <<'END_OF_FILE'
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_FILE
if test 7746 -ne `wc -c <'rolo-logic.el'`; then
    echo shar: \"'rolo-logic.el'\" unpacked with wrong size!
fi
# end of 'rolo-logic.el'
fi
if test -f 'rolo-simple.el' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rolo-simple.el'\"
else
echo shar: Extracting \"'rolo-simple.el'\" \(2466 characters\)
sed "s/^X//" >'rolo-simple.el' <<'END_OF_FILE'
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_FILE
if test 2466 -ne `wc -c <'rolo-simple.el'`; then
    echo shar: \"'rolo-simple.el'\" unpacked with wrong size!
fi
# end of 'rolo-simple.el'
fi
if test -f 'smart-menu.el' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'smart-menu.el'\"
else
echo shar: Extracting \"'smart-menu.el'\" \(6580 characters\)
sed "s/^X//" >'smart-menu.el' <<'END_OF_FILE'
X;;!emacs
X;;
X;; FILE:         smart-menu.el
X;; SUMMARY:      Display subsystem menu for use with 'smart-key.el' package.
X;; USAGE:        GNU Emacs Lisp Library
X;;
X;; AUTHOR:       Bob Weiner, Applied Research, Motorola, Inc.
X;; E-MAIL:       USENET:  weiner@novavax.UUCP
X;; ORIG-DATE:    20-Apr-89
X;; LAST-MOD:     15-Jun-89 at 23:11:21 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;;;
X;;; This code is machine independent.
X;;;
X;;; To install:
X;;;
X;;;   See smart-key.el
X;;;
X;;; You need this function if you don't already have it.
X;;; (defun alist-value (compar-op tag alist)
X;;;   "Use COMPAR-OP (comparison operator) and TAG to extract matching value from ALIST."
X;;;   (let ((val))
X;;;     (while (and (null val) alist)
X;;;       (if (funcall compar-op (car (car alist)) tag)
X;;; 	  (setq val (cdr (car alist))))
X;;;       (setq alist (cdr alist)))
X;;;     val))
X;;;
X;; DESCRIP-END.
X
X(require 'rolo)
X
X(defconst smart-menu-at-bottom t
X  "Non-nil means display menu below current window, nil means above.")
X
X(defconst smart-menu-alist
X  (list
X    (cons "Buffer-Menu" '(buffer-menu nil))
X    (cons "Calendar" '(calendar))
X    (cons "Dired-Home-Dir" '(dired "~"))
X    (cons "<Help>" '(smart-menu-help))
X    (cons "Info" '(info))
X    (cons "Rmail" '(rmail))
X    (cons "Mail" '(mail))
X    (cons "Shell" '(shell))
X    (cons "Tutorial" '(help-with-tutorial))
X    (cons "Search-Rolodex-for-String" '(call-interactively 'rolo-fgrep))
X    (cons "Search-Rolodex-for-Regexp" '(call-interactively 'rolo-grep))
X    (cons "Search-Rolodex-Using-Logical-Expression" '(call-interactively
X						       'rolo-logic))
X    (cons "Edit-Personal-Rolodex" '(rolo-edit))
X    (cons "Sort-Personal-Rolodex" '(rolo-sort))
X    (cons "<Quit>" t))
X  "Alist of smart menu items vs corresponding expressions to execute.
XEach element looks like (STRING . EXPRESSION).
XSelecting an item that matches STRING causes evaluation of EXPRESSION.")
X
X(defconst smart-menu-buffer "*Smart Menu*"
X  "Name of the buffer used for the smart subsystem menu.")
X
X(defvar *smart-menu-window-config* nil
X  "Window configuration prior to entry of smart menu mode.")
X
X(defvar *smart-menu-prev-mode* nil
X  "Records major-mode prior to entry of smart-menu.")
X
X(defvar *smart-menu-height* 0
X  "Records window height to use for smart menu display.")
X
X(defvar smart-menu-mode-map nil
X  "Keymap containing smart-menu commands.")
X(if smart-menu-mode-map
X    nil
X  (setq smart-menu-mode-map (copy-keymap text-mode-map))
X  (define-key smart-menu-mode-map "h" 'smart-menu-help)
X  (define-key smart-menu-mode-map "q" 'smart-menu-quit)
X  (define-key smart-menu-mode-map " " 'smart-menu-select)
X  (define-key smart-menu-mode-map "\C-m" 'smart-menu-select))
X
X;; Smart menu mode is suitable only for specially formatted data.
X(put 'smart-menu-mode 'mode-class 'special)
X
X(defun smart-menu-mode ()
X  "Smart-menu mode provides a menu of commands for entering subsystems.
XSee also the documentation for 'smart-menu'.
X
X\\[smart-menu-select] or \\[smart-key] selects entries in the menu.
X\\[smart-menu-quit] or \\[smart-key-meta] quits from the menu."
X  (kill-all-local-variables)
X  (use-local-map smart-menu-mode-map)
X  (setq major-mode 'smart-menu-mode)
X  (setq mode-name "smart-menu")
X  (set-syntax-table text-mode-syntax-table)
X  (setq local-abbrev-table text-mode-abbrev-table)
X  (setq case-fold-search t)
X  (setq buffer-read-only t)
X  (run-hooks 'smart-menu-mode-hook))
X
X(defun smart-menu ()
X  "Smart-menu pops up a window with a menu of commands for entering subsystems.
X\\[smart-menu-select] or \\[smart-key] selects entries in the menu.
X\\[smart-menu-quit] or \\[smart-key-meta] quits from the menu."
X  (interactive)
X  (setq *smart-menu-window-config* (current-window-configuration))
X  (let ((buf (get-buffer-create smart-menu-buffer))
X	(pop-up-windows t))
X    (setq *smart-menu-prev-mode* major-mode)
X    (split-window-vertically)
X    (if smart-menu-at-bottom
X	(pop-to-buffer buf)
X      (switch-to-buffer buf))
X    (or (eq major-mode 'smart-menu-mode)
X	(smart-menu-mode))
X    ;; If empty buffer
X    (if (= (save-excursion (end-of-buffer) (point)) 1)
X	(let ((buffer-read-only nil)
X	      (item-str)
X	      (len 0))
X	  (mapcar
X	    '(lambda (s)
X	       (setq item-str
X		     (concat item-str
X			     (if (>= (+ len (length s) 2)
X				     (window-width))
X				 (progn
X				   (setq len 0
X					 *smart-menu-height*
X					 (1+ *smart-menu-height*))
X				   "\n"))
X			     s "  "))
X	       (setq len (+ len (length s) 2)))
X	    (mapcar 'car smart-menu-alist))
X	  (setq *smart-menu-height* (+ *smart-menu-height* 3))
X	  (insert "\n" item-str)
X	  (goto-char (point-min))
X	  (set-buffer-modified-p nil)))
X    ;; Smallest size you can shrink a window
X    (let ((height (window-height)))
X      (shrink-window (min (- height *smart-menu-height*) (- height 4))))))
X
X(defun smart-menu-select (&optional arg)
X  "Select smart menu item that point is within and execute associated command.
XAlso quits from smart menu mode.  With prefix ARG, quits only."
X  (interactive "P")
X  (if arg
X      (smart-menu-quit t)
X    (let ((item (extract-item-around-point)))
X      (if (string= item "")
X	  (error "No command selected.")
X	(let ((exec-cmd (alist-value 'string-match item smart-menu-alist)))
X	  (if exec-cmd
X	      (progn (smart-menu-quit)
X		     (eval exec-cmd))
X	    (beep)
X	    (message (format "Unimplemented command selected, '%s'." item))))))))
X
X(defun smart-menu-help ()
X  "Displays description of major mode prior to smart-menu invocation."
X  (interactive)
X  (describe-function *smart-menu-prev-mode*))
X
X(defun smart-menu-quit (&optional arg)
X  "Quit from smart menu.  Restore previous window configuration.
XOptional ARG kills the smart menu buffer rather than just burying it."
X  (interactive "P")
X  (let ((buf (get-buffer smart-menu-buffer)))
X    (if (not buf)
X	nil
X      (if (not arg)
X	  (bury-buffer buf)
X	(kill-buffer buf)
X	(setq *smart-menu-height* 0))
X      ;; Restore window configuration prior to smart menu entry.
X      (if *smart-menu-window-config*
X	  (progn
X	    (set-window-configuration *smart-menu-window-config*)
X	    (setq *smart-menu-window-config* nil))))))
X
X(defun extract-item-around-point ()
X  "Return whitespace separated menu item that point is within or before."
X  (interactive)
X  (save-excursion
X    (skip-chars-backward "^ \t\n")
X    (let ((start (point)))
X      (skip-chars-forward "^ \t\n")
X      (buffer-substring start (point)))))
X
X(provide 'smart-menu)
END_OF_FILE
if test 6580 -ne `wc -c <'smart-menu.el'`; then
    echo shar: \"'smart-menu.el'\" unpacked with wrong size!
fi
# end of 'smart-menu.el'
fi
echo shar: End of shell archive.
exit 0
--
-- 
Bob Weiner, Motorola, Inc.,   USENET:  ...!gatech!uflorida!novavax!weiner
(407) 738-2087