
(defvar rolo-email-format "%s		<%s>" "\
Format string to use when adding an entry with e-mail addr from a mail msg.
It must contain a %s indicating where to put the entry name and a second
%s indicating where to put the e-mail address.")

(defvar rolo-file-list (quote ("~/.rolodex.otl")) "\
*List of files containing rolodex entries.
The first file should be a user-specific rolodex file, typically in the home
directory.  The second file is often a shared, group-specific rolodex file.

A rolo-file consists of:
   (1) an optional header beginning with and ending with a line which matches
       rolo-hdr-regexp;
   (2) one or more rolodex entries which each begin with
       rolo-entry-regexp and may be nested.")

(defvar rolo-kill-buffers-after-use nil "\
*Non-nil means kill rolodex file buffers after searching them for entries.
Only unmodified buffers are killed.")

(defvar rolo-save-buffers-after-use t "\
*Non-nil means save rolodex file after an entry is killed.")

(defun rolo-add (name &optional file) "\
Adds a new entry in personal rolodex for NAME.
Last name first is best, e.g. \"Smith, John\".
With prefix argument, prompts for optional FILE to add entry within.
NAME may be of the form: parent/child to insert child below a parent
entry which begins with the parent string." (interactive (byte-code "ÆÇ!†	 ÈÉ!ˆÊ @A@ËÌ	† 
\"
…- 	…- ÍÎÏ!P	\"ƒ8 Ð
#‚9 D,‡" [lst name email entry rolo-email-format current-prefix-arg fboundp mail-fetch-field require mail-utils rolo-name-and-email read-string "Name to add to rolo: " string-match "\\`" regexp-quote format] 10)) (byte-code ";?†
 ÒÓ\"… ÔÕ\"ˆt… 	…# Ö×ØÙ
\"\"‰ˆ	?…- 
@‰ˆ	…< 	;?†< Ò	Ó\"ƒF ÔÚ	\"‚e Û	!…P Ü	!?ƒZ ÔÝ	\"‚e Þ	!?…e Ôß	\"ˆà	!†o á	!qˆt…y âã\"ˆÓÓÇä ˆåbˆæç\"‰…Ä èOTG^ÇO‰ˆéêëì!RÇÈ#ƒ» íîå!ïå!\"‰‚À Ôð	#ˆ‚† ˆñ`òÈ\"ˆ`\"ˆebˆG	óP

GÓÓÇÇÈ…ý éêôQÇÅ#…`íîå!ïå!\"‰ˆõG\"ƒòÈ\"‚\\í``	\\\"íîö!ïö!\"‰ˆ÷\"ƒCòÈ\"‚\\÷\"ƒUÇ‰ˆø ‚\\ÇÈ‰ˆ‚ï ˆÇ‰ˆƒoÇ‚�ù†xóPÒÓ\"ƒ„ú‚†û$ˆüå!ˆä ˆýp!ˆ`bˆt…Ÿâþ!.+‡" [name file rolo-file-list parent level end rolo-entry-regexp nil t len name-level level-len entry entry-spc entry-level match again buffer-read-only string= "" error "(rolo-add): Invalid name: '%s'" completing-read "File to add to: " mapcar list "(rolo-add): Invalid file: '%s'" file-exists-p file-readable-p "(rolo-add): File not readable: '%s'" file-writable-p "(rolo-add): File not writable: '%s'" get-file-buffer find-file-noselect message "Locating insertion point for '%s'..." widen 1 string-match "/" 0 re-search-forward "\\(" "\\)[ 	]*" regexp-quote buffer-substring match-beginning match-end "(rolo-add): '%s' category not found in \"%s\"." narrow-to-region rolo-to-entry-end "*" "\\)\\([ 	]*\\)" /= 2 string< beginning-of-line insert "   " "
" backward-char pop-to-buffer "Edit entry at point."] 45))

(defun rolo-display-matches nil "\
Re-display buffer of previously found rolodex matches." (interactive) (byte-code "ÇÀ!ƒ
 ‚ pÇÁ!ƒ 	‚ È
!É	!ˆÄ‰ˆÊË!…* Ë ˆÌ ˆebˆÍÄ!ˆÅ‰ˆÎ!ƒJ Ï!‚N Ð!)*‡" [obuf display-buf rolo-display-buffer buffer-read-only nil t buf boundp get-buffer pop-to-buffer fboundp ep:but-create rolo-shrink-window set-buffer-modified-p get-buffer-window select-window switch-to-buffer] 12))

(defun rolo-edit (&optional name file) "\
Edits a rolodex entry given by optional NAME within 'rolo-file-list'.
With prefix argument, prompts for optional FILE to locate entry within.
With no NAME arg, simply displays FILE or first entry in 'rolo-file-list' in an
editable mode.  NAME may be of the form: parent/child to edit child below a
parent entry which begins with the parent string." (interactive "sName to edit in rolo: 
P") (byte-code "… ;?† ÈÉ\"… ÊË\"ˆt… 	…' ÌÍÎÏ\"\"‰ˆ
ƒ1 
C‚2 
†; @‰ˆ?ƒW Ð
!?…L ÊÑ
\"ˆÒ
!ˆÆ‰‚™ Ó\"ƒ} ‰ˆÐ
!ƒo Æ‰‚v ÔÕ
\"ˆÖ ˆ×p!‚– ÔØ\"ˆÖ ˆ×Ù@!†‘ Ú@!!ˆÆ‰ˆÛ )ˆ`b‡" [name current-prefix-arg file rolo-file-list file-list buffer-read-only nil buffer-file-name string= "" error "(rolo-edit): Invalid name: '%s'" completing-read "Entry's File: " mapcar list file-writable-p "(rolo-edit): File not writable: '%s'" find-file-other-window rolo-to message "(rolo-edit): Entry found but file not writable: '%s'" beep pop-to-buffer "(rolo-edit): '%s' not found." get-file-buffer find-file-noselect widen] 19))

(defun rolo-fgrep (string &optional max-matches rolo-file count-only no-display) "\
Display rolodex entries matching STRING.
To a maximum of optional prefix arg MAX-MATCHES, in file(s) from optional
ROLO-FILE or rolo-file-list.  Default is to find all matching entries.  Each
entry is displayed with all of its sub-entries.  Optional COUNT-ONLY non-nil
means don't retrieve and don't display matching entries.  Optional NO-DISPLAY
non-nil means retrieve entries but don't display.

Nil value of MAX-MATCHES means find all matches, t value means find all matches
but omit file headers, negative values mean find up to the inverse of that
number of entries and omit file headers.

Returns number of entries matched.  See also documentation for the variable
rolo-file-list." (interactive "sRolodex string to match: 
P") (byte-code "ÆÇ	!
%t…' ÈÉÊUƒ Ë‚ ÌUƒ% Í‚& Î#ˆ)‡" [total-matches string max-matches rolo-file count-only no-display rolo-grep regexp-quote message "%s matching entr%s found in rolodex." 0 "No" 1 "y" "ies"] 7))

(defun rolo-grep (regexp &optional max-matches rolo-bufs count-only no-display) "\
Display rolodex entries matching REGEXP.
To a maximum of prefix arg MAX-MATCHES, in buffer(s) from optional ROLO-BUFS or
rolo-file-list.  Default is to find all matching entries.  Each entry is
displayed with all of its sub-entries.  Optional COUNT-ONLY non-nil means don't
retrieve and don't display matching entries.  Optional NO-DISPLAY non-nil
means retrieve entries but don't display.

Nil value of MAX-MATCHES means find all matches, t value means find all matches
but omit file headers, negative values mean find up to the inverse of that
number of entries and omit file headers.

Returns number of entries matched.  See also documentation for the variable
rolo-file-list." (interactive "sRolodex regular expression to match: 
P") (byte-code "	?ƒ	 ‚ 	<ƒ 	‚ 	Cpƒ Å‚\" Ð!qÑÑ
Ë=†6 Ò
!…6 
ÑWÅ	ƒJ Å‚V Å‰ˆ	†V Ó ˆ@‰…p Ò
!?†p 

[]W…¬ AÔ
$\\‰ˆÒ
!…¨ 
ÑYƒ  
Z‚¥ 
\\‰
ˆ‚W ˆ†¿ †¿ 	†¿ ÑUƒÆ Å‚È Õ ˆt…é Ö×ÑUƒÚ Ø‚Ü ÙUƒç Ú‚è Û#ˆ.‡" [rolo-file-list rolo-bufs obuf display-buf count-only nil rolo-display-buffer total-matches num-matched inserting max-matches t file buffer-read-only regexp no-display get-buffer-create 0 integerp erase-buffer rolo-grep-file rolo-display-matches message "%s matching entr%s found in rolodex." "No" 1 "y" "ies"] 13))

(defun rolo-kill (name &optional file) "\
Kills a rolodex entry given by NAME within 'rolo-file-list'.
With prefix argument, prompts for optional FILE to locate entry within.
NAME may be of the form: parent/child to kill child below a parent entry
which begins with the parent string.
Returns t if entry is killed, nil otherwise." (interactive "sName to kill in rolo: 
P") (byte-code ";?†
 ÐÑ\"… ÒÓ\"ˆt… 	…# ÔÕÖ×\"\"‰ˆ
ƒ- 
C‚. Ì
†9 @‰ˆØ\"ƒÂ ‰ˆÙ
!ƒ¸ ÚÌÌÌ
	Ì‰ˆÛÌÎ#ˆÜÝ!‰	ˆÞ ˆ`ß	\"‰
ˆ	bˆàá!ˆtƒ° ß``â\\ã ˆ`^\"äåæ\"!ƒ© ç!ˆèé!‚¬ èê!)‚´ ç!,‚¿ èë
\"ˆì ‚É èí\"ˆì ˆ*‡" [name current-prefix-arg file rolo-file-list file-list killed buffer-file-name kill-op start end level buffer-read-only nil rolo-entry-regexp t entry-line string= "" error "(rolo-kill): Invalid name: '%s'" completing-read "Entry's File: " mapcar list rolo-to file-writable-p (progn (kill-region start (rolo-to-entry-end t level)) (setq killed t) (rolo-save-buffer) (rolo-kill-buffer)) re-search-backward match-end 0 beginning-of-line buffer-substring skip-chars-forward " 	" 60 end-of-line y-or-n-p format "Kill `%s...' " eval message "Killed" "Aborted" "(rolo-kill): Entry found but file not writable: '%s'" beep "(rolo-kill): '%s' not found."] 24))

(defun rolo-sort (&optional rolo-file) "\
Sorts up to 14 levels of entries in ROLO-FILE (default is personal rolo).
Assumes entries are delimited by one or more '*'characters.
Returns list of number of groupings at each entry level." (interactive (byte-code "ÅÆÇÈÉÊ
… ÃËÌ\">ƒ 
‚ @‰!\"ËÍ\"\"‰ˆÎ	Å\"ƒ2 ‚3 	*C‡" [default file buffer-file-name t rolo-file-list "" nil completing-read format "Sort rolo file (default %s): " file-name-nondirectory mapcar (lambda (file) (equal buffer-file-name (expand-file-name file))) list string=] 8)) (byte-code "?†	 ÅÆ\"… 	@‰ˆ;… Ç!?…! ÈÉ\"ˆÊË!ÌÌÅ
Æ\"?…Y Í
\"‰ˆÎÏ\"†A …K ÐC\"‰ˆ
Ï
GÑZO‰ˆ‚* ˆ+‡" [rolo-file rolo-file-list level-regexp entries-per-level-list n equal "" file-readable-p error "(rolo-sort): Invalid or unreadable file: %s" regexp-quote "**************" nil rolo-sort-level /= 0 append 2] 13))

(defun rolo-sort-level (rolo-file level-regexp &optional max-groupings) "\
Sorts groupings of entries in ROLO-FILE at hierarchy level LEVEL-REGEXP.
To a maximum of optional MAX-GROUPINGS.  Nil value of MAX-GROUPINGS means all
groupings at the given level.  LEVEL-REGEXP should simply match the text of
any rolodex entry of the given level, not the beginning of a line (^); an
example, might be (regexp-quote \"**\") to match level two.  Returns number
of groupings sorted." (interactive "sRolodex file to sort: 
Regexp for level's entries: 
P") (rolo-map-level (quote (lambda (start end) (sort-lines nil start end))) rolo-file level-regexp max-groupings))

(defun rolo-toggle-narrow-to-entry nil "\
Toggle between display of current entry and display of all matched entries.
Useful when bound to a mouse key." (interactive) (byte-code "Ã ƒ
 Ä ‚! Å!† ÆÁÂ#…! Ç ˆÈ`SÉ \"ˆÊ ˆeb‡" [rolo-entry-regexp nil t rolo-narrowed-p widen looking-at re-search-backward forward-char narrow-to-region rolo-display-to-entry-end rolo-shrink-window] 9))

(defun rolo-yank (name &optional regexp-p) "\
Inserts at point the first rolodex entry matching NAME.
With optional prefix arg, REGEXP-P, treats NAME as a regular expression instead
of a string." (interactive "sName to insert record for: 
P") (byte-code "p	ƒ Ã
Ä\"‚ ÃÅ
!Ä\")‡" [rolo-display-buffer regexp-p name rolo-grep -1 regexp-quote] 5))

(defun rolo-fgrep-file (rolo-buf string &optional max-matches count-only) "\
Retrieve entries in ROLO-BUF matching STRING to a maximum of optional MAX-MATCHES.
Nil value of MAX-MATCHES means find all matches, t value means find all matches
but omit file headers, negative values mean find up to the inverse of that
number of entries and omit file headers.  Optional COUNT-ONLY non-nil
means don't retrieve matching entries.
Returns number of matching entries found." (rolo-grep-file rolo-buf (regexp-quote string) max-matches count-only))

(defun rolo-grep-file (rolo-buf regexp &optional max-matches count-only) "\
Retrieve entries in ROLO-BUF matching REGEXP to a maximum of optional MAX-MATCHES.
Nil value of MAX-MATCHES means find all matches, t value means find all matches
but omit file headers, negative values mean find up to the inverse of that
number of entries and omit file headers.  Optional COUNT-ONLY non-nil
means don't retrieve matching entries.
Returns number of matching entries found." (byte-code "ÉÉ
?† 
Ã=† Ö
!…+ ×!‰†+ Ø!…+ ÙÃ\"Ã‰ƒDÉÚÉÃ
…Y 
Ã=ƒL ÉÉ‰‚Y 
ÚW…Y É
[‰ˆ	qˆ…e Ã‰
ˆŠŒÛ ˆÜbˆÝÞÉÃ#…x ß ˆàÉÃá$…‹ â ˆe`B‰ˆàÉÃ#ˆ
?†œ 
W…¥ àÉÃ#…7ãÉÃ#ˆ`ÉàÉÃ#ˆäÃå`\"‰\"ˆ†ÚU…Ö …æ	!†à 	çèÓ!ƒî ‚ï éêPë!\"qˆdbˆƒì	@A#ˆíî\"‚ç\"cˆ	q*ˆT‰ˆ†2ï`#*ˆ‚“ ))ˆð	!ˆ,‚EÚ*‡" [new-buf-p actual-buf max-matches t rolo-buf hdr-pos num-found curr-entry-level incl-hdr nil buffer-read-only rolo-hdr-regexp rolo-entry-regexp regexp start next-entry-exists count-only src src-line hbut:source-prefix rolo-display-buffer rolo-hdr-format integerp rolo-buffer-exists-p file-exists-p find-file-noselect 0 widen 1 search-forward "" show-all re-search-forward 2 forward-line re-search-backward rolo-to-entry-end buffer-substring buffer-file-name format boundp "@loc> " "%s" prin1-to-string insert-buffer-substring insert "

" append-to-buffer rolo-kill-buffer] 27))

(defun rolo-map-level (func rolo-buf level-regexp &optional max-groupings) "\
Perform FUNC on groupings of ROLO-BUF entries at level LEVEL-REGEXP,
to a maximum of optional argument MAX-GROUPINGS.  Nil value of MAX-GROUPINGS
means all groupings at the given level.  FUNC should take two arguments, the
start and the end of the region that it should manipulate.  LEVEL-REGEXP
should simply match the text of any rolodex entry of the given level, not the
beginning of a line (^); an example, might be (regexp-quote \"**\") to match
level two.  Returns number of groupings matched." (byte-code "ÍÍ
?† ×
W…% Ø!‰†% Ù!…% ÚÄ\"Ä‰ƒ-	qˆ×ÛÜQ	ÍÍ
ÝÞ!?…H ßà!ˆebˆáÍÄâ$…X ã ˆ
?†a 
W…j áÍÄ#… T‰ˆ`ä ˆ`†Š SZÍÍÍÍá	ÍÄâ$‰ƒ¿ `ä ˆ`ZWX‰‚É ÄÄ‰ˆdbˆ`bˆå ˆƒÞ ‚ï á	ÍÄ#ƒî ä ˆ`‚ï d‰ˆb)…ý ?…Íˆ‚˜ ˆ`bˆæ#ˆb).ˆ‚Y ˆç ˆè	!ˆ-‚.×*‡" [new-buf-p actual-buf max-groupings rolo-buf t num-found exact-level-regexp level-regexp outline-regexp rolo-entry-regexp buffer-read-only level-len rolo-hdr-regexp nil opoint grouping-start start next-level-len next-entry-exists grouping-end no-subtree end func 0 rolo-buffer-exists-p file-exists-p find-file-noselect "^\\(" "\\)[ 	
]" boundp outline-mode-map load-library "outline" re-search-forward 2 forward-line beginning-of-line hide-subtree funcall show-all rolo-kill-buffer] 19))

(defun rolo-buffer-exists-p (rolo-buf) "\
Returns buffer given by ROLO-BUF or nil.
ROLO-BUF may be a file-name, buffer-name, or buffer." (byte-code "Á;…	 Â!† !Ã >@‡" [rolo-buf get-buffer get-file-buffer buffer-list] 5))

(defun rolo-display-to-entry-end nil "\
Go to end of current entry, ignoring sub-entries." (byte-code "ÄÅ	QÂÃ#ƒ Æ ˆ`‚ db‡" [rolo-hdr-regexp rolo-entry-regexp nil t re-search-forward "\\|" beginning-of-line] 4))

(defun rolo-format-name (name-str first last) "\
Reverse order of NAME-STR field given my regexp match field FIRST and LAST." (byte-code "Ã!… 	Ã!Ä!OÅ	Ã
!Ä
!OQ‡" [last name-str first match-beginning match-end ", "] 10))

(defun rolo-kill-buffer (&optional rolo-buf) "\
Kills optional ROLO-BUF if unchanged and 'rolo-kill-buffers-after-use' is t.
Default is current buffer." (byte-code "† p‰ˆ	… Â!?… Ã!‡" [rolo-buf rolo-kill-buffers-after-use buffer-modified-p kill-buffer] 3))

(defun rolo-name-and-email nil "\
If point is in a mail message, returns list of (name email-addr) of sender.
Name is returned as 'last, first-and-middle'." (byte-code "ŠŒebˆÅÆÀÁ#… Çe`\"ˆÀÀÀÈÉ!ÈÊ!‰ˆ…i ËÌÍÎQ\"ƒI ÏÐÑ#‰ˆ
†F ÒÓ!ÔÓ!O‰‚i ËÕÖ×Q\"…i ÏØÐ#‰ˆ
†i ÒÑ!ÔÑ!O‰ˆ†o 
…u 
D+))‡" [nil t email name from search-forward "

" narrow-to-region mail-fetch-field "reply-to" "from" string-match "^\\([^\"<>() 	
]+\\)" "\\([ 	]*[(\"][ 	]*\\([^\"()]+\\)[ 	]+" "\\([^\" 	()]+\\)[ 	]*[)\"]\\)?[ 	]*$" rolo-format-name 3 4 match-beginning 1 match-end "^\\(\"?\\([^\"<>()
]+\\)[ 	]+" "\\([^\" 	()<>]+\\)\"?[ 	]+\\)?" "<\\([^\"<>() 	
]+\\)>[ 	]*$" 2] 15))

(defun rolo-name-at nil "\
If point is at start of an entry in 'rolo-display-buffer', returns entry.
Otherwise, returns nil." (byte-code "ÄÅ \"…8 ŠÆ	!† Ç ˆÈ	ÂÃ#…7 ÉÊ!bˆËÌ!ˆÆÍ!†, ÆÎ!…7 ÏÐÊ!ÉÊ!\")‡" [rolo-display-buffer rolo-entry-regexp nil t string= buffer-name looking-at end-of-line re-search-backward match-end 0 skip-chars-forward " 	" "[^ 	
]+ ?, ?[^ 	
]+" "\\( ?[^ 	
]+\\)+" buffer-substring match-beginning] 14))

(defun rolo-narrowed-p nil (byte-code "ÀeÁ\"† ÀÂ Td\"‡" [/= 1 buffer-size] 5))

(defun rolo-save-buffer (&optional rolo-buf) "\
Saves optional ROLO-BUF if changed and 'rolo-save-buffers-after-use' is t.
Default is current buffer.  Used, for example, after a rolo entry is killed." (byte-code "† p‰ˆ	… Â!… q… Ã ‡" [rolo-buf rolo-save-buffers-after-use buffer-modified-p save-buffer] 3))

(defun rolo-shrink-window nil (byte-code "Äed\"Å ÆÇ	Z!SÈY…? ÉÈ\"…? Ê 	TV…? ËÈWƒ9 	ÌÊ Æ\"Z]‚> 	
Z^!,‡" [lines height window-min-height desired-shrinkage count-lines window-height 2 min 0 /= screen-height shrink-window /] 12))

(defun rolo-to (name &optional file-list) "\
Moves point to entry for NAME within optional FILE-LIST.
'rolo-file-list' is used as default when FILE-LIST is nil.
Leaves point immediately after match for NAME within entry.
Switches internal current buffer but does not alter the screen.
Returns non-nil iff matching entry is found." (byte-code "† 	‰ˆËË
?… …6@A‰ˆ…+ ;?†+ ÑÒ\"ƒ5 ÓÔ\"‚F Õ!…? Ö!?…F Ó×\"ˆØ!†P Ù!qˆÅÒËË
	Ú ˆÛbˆÜÝ\"‰
…ñ Ë	Þ
O
TG^ËO‰ˆ	?…˜ ßËÅ#…½ Šà ˆáâãä!R!…¸ åæÛ!çÛ!\"‰	)ˆ‚Œ ˆ	†Û è\"†Û Ë‰ˆép!ˆÓê#ˆ	…í ë`ŠìÅ	\"ˆ`)\"ˆ‚g ˆebˆG	íPGßËÅ#…!Šà ˆáâãä!R!‰)?…)Ëˆ‚+-ˆ
†2î ˆ‚ ˆÚ ˆ
*‡" [file-list rolo-file-list found file case-fold-search t real-name name parent level end nil rolo-entry-regexp buffer-read-only len name-level level-len string= "" error "(rolo-to): Invalid file: '%s'" file-exists-p file-readable-p "(rolo-to): File not readable: '%s'" get-file-buffer find-file-noselect widen 1 string-match "/" 0 search-forward beginning-of-line looking-at "\\(" "\\)[ 	]*" regexp-quote buffer-substring match-beginning match-end equal pop-to-buffer "(rolo-to): '%s' part of name not found in \"%s\"." narrow-to-region rolo-to-entry-end "*" rolo-kill-buffer] 30))

(defun rolo-to-entry-end (&optional include-sub-entries curr-entry-level) "\
Goes to end of whole entry if optional INCLUDE-SUB-ENTRIES is non-nil.
CURR-ENTRY-LEVEL is a string whose length is the same as the last found entry
header.  If INCLUDE-SUB-ENTRIES is nil, CURR-ENTRY-LEVEL is not needed.
Returns current point." (byte-code "Æ	ÂÃ#‰… … `ŠÇ ˆ`)ZGV…! Âˆ‚  ˆƒ- Ç ˆ`‚/ db‡" [next-entry-exists rolo-entry-regexp nil t include-sub-entries curr-entry-level re-search-forward beginning-of-line] 4))

(defvar rolo-display-buffer "*Rolodex*" "\
Buffer used to display set of last matching rolodex entries.")

(defvar rolo-entry-regexp "^\\*+" "\
Regular expression to match the beginning of a rolodex entry.
This pattern must match the beginning of the line.  Entries may be nested
through the use of increasingly longer beginning patterns.")

(defconst rolo-hdr-format (concat "======================================================================
" "%s
" "======================================================================
") "Header to insert preceding a file's first rolodex entry match when
file has none of its own.  Used with one argument, the file name.")

(defconst rolo-hdr-regexp "^===" "\
Regular expression to match the first and last lines of rolodex file headers.
This header is inserted into rolo-display-buffer before any entries from the
file are added.")

(provide (quote wrolo))
