;;; wrolo-logic.el --- Performs logical retrievals on rolodex files ;; Copyright (C) 1989-1995, Free Software Foundation, Inc. ;; Developed with support from Motorola Inc. ;; Author: Bob Weiner, Brown U. ;; Maintainer: Mats Lidell ;; Keywords: hypermedia, matching ;; This file is part of GNU Hyperbole. ;; GNU Hyperbole is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 3, or (at ;; your option) any later version. ;; GNU Hyperbole is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; INSTALLATION: ;; ;; See also wrolo.el. These functions are separated from wrolo.el since many ;; users may never want or need them. They can be automatically loaded when ;; desired by adding the following to one of your Emacs init files: ;; ;; (autoload 'rolo-logic "wrolo-logic" "Logical rolodex search filters." t) ;; ;; FEATURES: ;; ;; 1. One command, 'rolo-logic' which takes a logical search expression as ;; an argument and displays any matching entries. ;; ;; 2. Logical 'and', 'or', 'not', and 'xor' rolodex entry retrieval filter ;; functions. They take any number of string or boolean arguments and ;; may be nested. NOTE THAT THESE FUNCTIONS SHOULD NEVER BE CALLED ;; DIRECTLY UNLESS THE FREE VARIABLES 'start' and 'end' ARE BOUND ;; BEFOREHAND. ;; ;; EXAMPLE: ;; ;; (rolo-logic (function ;; (lambda () ;; (rolo-and ;; (rolo-not "Tool-And-Die") ;; "secretary")))) ;; ;; would find all non-Tool-And-Die Corp. secretaries in your rolodex. ;; ;; The logical matching routines are not at all optimal, but then most ;; rolodex files are not terribly lengthy either. ;; ;;; Code: (require 'wrolo) ;;;###autoload (defun rolo-logic (func &optional in-bufs count-only include-sub-entries no-sub-entries-out) "Apply FUNC to all entries in optional IN-BUFS, display entries where FUNC is non-nil. If IN-BUFS is nil, 'rolo-file-list' is used. If optional COUNT-ONLY is non-nil, don't display entries, return count of matching entries only. If optional INCLUDE-SUB-ENTRIES flag is non-nil, FUNC will be applied across all sub-entries at once. Default is to apply FUNC to each entry and sub-entry separately. Entries are displayed with all of their sub-entries unless INCLUDE-SUB-ENTRIES is nil and optional NO-SUB-ENTRIES-OUT flag is non-nil. FUNC should use the free variables 'start' and 'end' which contain the limits of the region on which it should operate. Returns number of applications of FUNC that return non-nil." (interactive "xLogic function of no arguments, (lambda () (): ") (let ((obuf (current-buffer)) (display-buf (if count-only nil (prog1 (set-buffer (get-buffer-create rolo-display-buffer)) (setq buffer-read-only nil) (erase-buffer))))) (let ((result (mapcar (function (lambda (in-bufs) (rolo-map-logic func in-bufs count-only include-sub-entries no-sub-entries-out))) (cond ((null in-bufs) rolo-file-list) ((listp in-bufs) in-bufs) ((list in-bufs)))))) (let ((total-matches (apply '+ result))) (if (or count-only (= total-matches 0)) nil (pop-to-buffer display-buf) (goto-char (point-min)) (set-buffer-modified-p nil) (setq buffer-read-only t) (let ((buf (get-buffer-window obuf))) (if buf (select-window buf) (switch-to-buffer buf)))) (if (interactive-p) (message (concat (if (= total-matches 0) "No" total-matches) " matching entr" (if (= total-matches 1) "y" "ies") " found in rolodex."))) total-matches)))) (defun rolo-map-logic (func rolo-buf &optional count-only include-sub-entries no-sub-entries-out) "Apply FUNC to all entries in ROLO-BUF, write to buffer entries where FUNC is non-nil. If optional COUNT-ONLY is non-nil, don't display entries, return count of matching entries only. If optional INCLUDE-SUB-ENTRIES flag is non-nil, FUNC will be applied across all sub-entries at once. Default is to apply FUNC to each entry and sub-entry separately. Entries are displayed with all of their sub-entries unless INCLUDE-SUB-ENTRIES is nil and optional NO-SUB-ENTRIES-OUT flag is non-nil. FUNC should use the free variables 'start' and 'end' which contain the limits of the region on which it should operate. Returns number of applications of FUNC that return non-nil." (if (or (bufferp rolo-buf) (if (file-exists-p rolo-buf) (setq rolo-buf (find-file-noselect rolo-buf t)))) (let* ((display-buf (set-buffer (get-buffer-create rolo-display-buffer))) (buffer-read-only)) (let ((hdr-pos) (num-found 0)) (set-buffer rolo-buf) (goto-char (point-min)) (if (re-search-forward rolo-hdr-regexp nil t 2) (progn (forward-line) (setq hdr-pos (cons (point-min) (point))))) (let* ((start) (end) (end-entry-hdr) (curr-entry-level)) (while (re-search-forward rolo-entry-regexp nil t) (setq start (save-excursion (beginning-of-line) (point)) next-entry-exists nil end-entry-hdr (point) curr-entry-level (buffer-substring start end-entry-hdr) end (rolo-to-entry-end include-sub-entries curr-entry-level)) (let ((fun (funcall func))) (or count-only (and fun (= num-found 0) hdr-pos (append-to-buffer display-buf (car hdr-pos) (cdr hdr-pos)))) (if fun (progn (goto-char end) (setq num-found (1+ num-found) end (if (or include-sub-entries no-sub-entries-out) end (goto-char (rolo-to-entry-end t curr-entry-level)))) (or count-only (append-to-buffer display-buf start end))) (goto-char end-entry-hdr))))) (rolo-kill-buffer rolo-buf) num-found)) 0)) ;; ;; INTERNAL FUNCTIONS. ;; ;; Do NOT call the following functions directly. ;; Send them as parts of a lambda expression to 'rolo-logic'. (defun rolo-not (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (let ((pat)) (while (and pat-list (or (not (setq pat (car pat-list))) (and (not (eq pat t)) (goto-char start) (not (search-forward pat end t))))) (setq pat-list (cdr pat-list))) (if pat-list nil t))) (defun rolo-or (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (if (memq t pat-list) t (let ((pat)) (while (and pat-list (or (not (setq pat (car pat-list))) (and (not (eq pat t)) (goto-char start) (not (search-forward pat end t))))) (setq pat-list (cdr pat-list))) (if pat-list t nil)))) (defun rolo-xor (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (let ((pat) (matches 0)) (while (and pat-list (or (not (setq pat (car pat-list))) (and (or (eq pat t) (not (goto-char start)) (search-forward pat end t)) (setq matches (1+ matches))) t) (< matches 2)) (setq pat-list (cdr pat-list))) (= matches 1))) (defun rolo-and (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (if (memq nil pat-list) nil (let ((pat)) (while (and pat-list (setq pat (car pat-list)) (or (eq pat t) (not (goto-char start)) (search-forward pat end t))) (setq pat-list (cdr pat-list))) (if pat-list nil t)))) ;; Work with regular expression patterns rather than strings (defun rolo-r-not (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (let ((pat)) (while (and pat-list (or (not (setq pat (car pat-list))) (and (not (eq pat t)) (goto-char start) (not (re-search-forward pat end t))))) (setq pat-list (cdr pat-list))) (if pat-list nil t))) (defun rolo-r-or (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (if (memq t pat-list) t (let ((pat)) (while (and pat-list (or (not (setq pat (car pat-list))) (and (not (eq pat t)) (goto-char start) (not (re-search-forward pat end t))))) (setq pat-list (cdr pat-list))) (if pat-list t nil)))) (defun rolo-r-xor (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (let ((pat) (matches 0)) (while (and pat-list (or (not (setq pat (car pat-list))) (and (or (eq pat t) (not (goto-char start)) (re-search-forward pat end t)) (setq matches (1+ matches))) t) (< matches 2)) (setq pat-list (cdr pat-list))) (= matches 1))) (defun rolo-r-and (&rest pat-list) "Logical rolodex entry filter. PAT-LIST is a list of pattern elements. Each element may be t, nil, or a string." (if (memq nil pat-list) nil (let ((pat)) (while (and pat-list (setq pat (car pat-list)) (or (eq pat t) (not (goto-char start)) (re-search-forward pat end t))) (setq pat-list (cdr pat-list))) (if pat-list nil t)))) (provide 'wrolo-logic) ;;; wrolo-logic.el ends here