comparison lisp/hyperbole/hypb.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4103f0995bd7
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;!emacs
2 ;;
3 ;; FILE: hypb.el
4 ;; SUMMARY: Miscellaneous Hyperbole support features.
5 ;; USAGE: GNU Emacs Lisp Library
6 ;; KEYWORDS: extensions, hypermedia
7 ;;
8 ;; AUTHOR: Bob Weiner
9 ;; ORG: Brown U.
10 ;;
11 ;; ORIG-DATE: 6-Oct-91 at 03:42:38
12 ;; LAST-MOD: 30-Oct-95 at 21:23:19 by Bob Weiner
13 ;;
14 ;; This file is part of Hyperbole.
15 ;; Available for use and distribution under the same terms as GNU Emacs.
16 ;;
17 ;; Copyright (C) 1991-1995, Free Software Foundation, Inc.
18 ;; Developed with support from Motorola Inc.
19 ;;
20 ;; DESCRIPTION:
21 ;; DESCRIP-END.
22
23 ;;; ************************************************************************
24 ;;; Other required Elisp libraries
25 ;;; ************************************************************************
26
27 (mapcar 'require '(hversion hact))
28
29 ;;; ************************************************************************
30 ;;; Public variables
31 ;;; ************************************************************************
32
33 (defconst hypb:help-buf-suffix " Hypb Help*"
34 "Suffix attached to all native Hyperbole help buffer names.")
35
36 ;;; ************************************************************************
37 ;;; Public functions
38 ;;; ************************************************************************
39
40 (defun hypb:call-process-p (program infile &optional predicate &rest args)
41 "Calls an external PROGRAM with INFILE for input.
42 If PREDICATE is given, it is evaluated in a buffer with the PROGRAM's
43 output and the result returned. If PREDICATE is nil, returns t iff
44 program has no output or just a 0-valued output.
45 Rest of ARGS are passed as arguments to PROGRAM."
46 (let ((buf (get-buffer-create "*test-output*"))
47 (found))
48 (save-excursion
49 (set-buffer buf) (setq buffer-read-only nil) (erase-buffer)
50 (apply 'call-process program infile buf nil args)
51 (setq found
52 (if predicate
53 (eval predicate)
54 (or (= (point-max) 1) ;; No output, consider cmd a success.
55 (and (< (point-max) 4)
56 (string= (buffer-substring 1 2) "0")))))
57 (set-buffer-modified-p nil)
58 (kill-buffer buf))
59 found))
60
61
62 (defun hypb:chmod (op octal-permissions file)
63 "Uses OP and OCTAL-PERMISSIONS integer to set FILE permissions.
64 OP may be +, -, xor, or default =."
65 (let ((func (cond ((eq op '+) (function logior))
66 ((eq op '-) (function
67 (lambda (p1 p2) (logand (lognot p1) p2))))
68 ((eq op 'xor) (function logxor))
69 (t (function (lambda (p1 p2) p1))))))
70 (set-file-modes file (funcall func (hypb:oct-to-int octal-permissions)
71 (file-modes file)))))
72
73 (defun hypb:cmd-key-string (cmd-sym &optional keymap)
74 "Returns a single pretty printed key sequence string bound to CMD-SYM.
75 Global keymap is used unless optional KEYMAP is given."
76 (if (and cmd-sym (symbolp cmd-sym) (fboundp cmd-sym))
77 (let* ((get-keys (function
78 (lambda (cmd-sym keymap)
79 (key-description (where-is-internal
80 cmd-sym keymap 'first)))))
81 (keys (funcall get-keys cmd-sym keymap)))
82 (concat "{"
83 (if (string= keys "")
84 (concat (funcall get-keys 'execute-extended-command nil)
85 " " (symbol-name cmd-sym) " RTN")
86 keys)
87 "}"))
88 (error "(hypb:cmd-key-string): Invalid cmd-sym arg: %s." cmd-sym)))
89
90 ;;;###autoload
91 (defun hypb:configuration (&optional out-buf)
92 "Insert Emacs configuration information at the end of optional OUT-BUF or the current buffer."
93 (save-excursion
94 (and out-buf (set-buffer out-buf))
95 (goto-char (point-max))
96 (delete-blank-lines) (delete-blank-lines)
97 (let ((start (point)))
98 (insert (format "I use:\tEditor: %s\n\tHyperbole: %s\n"
99 (if (boundp 'epoch::version)
100 epoch::version
101 (hypb:replace-match-string
102 " of .+" (emacs-version) "" t))
103 hyperb:version))
104 (if (and (boundp 'system-configuration) (stringp system-configuration))
105 (insert (format "\tSys Type: %s\n" system-configuration)))
106 (insert (format "\tOS Type: %s\n\tWindow Sys: %s\n"
107 system-type (or window-system hyperb:window-system
108 "None")))
109 (if (and (boundp 'hmail:reader) hmail:reader)
110 (insert (format "\tMailer: %s\n"
111 (cond ((eq hmail:reader 'rmail-mode) "RMAIL")
112 ((eq hmail:reader 'vm-mode)
113 (concat "VM " vm-version))
114 ((and (eq hmail:reader 'mh-show-mode)
115 (string-match "v ?\\([0-9]+.[0-9]+\\)"
116 mh-e-RCS-id))
117 (concat "MH-e "
118 (substring mh-e-RCS-id
119 (match-beginning 1)
120 (match-end 1))))
121 ((eq hmail:reader 'pm-fdr-mode)
122 (concat "PIEmail " pm-version))
123 ))))
124 (if (and (boundp 'hnews:reader) (boundp 'gnus-version) hnews:reader)
125 (insert (format "\tNews Rdr: %s\n" gnus-version)))
126 (if (and (boundp 'br-version) (stringp br-version))
127 (insert (format "\tOO-Browser: %s\n" br-version)))
128 (untabify start (point)))))
129
130 (if (fboundp 'copy-tree)
131 (fset 'hypb:copy-sublists 'copy-tree)
132 ;;
133 ;; This function is derived from a copylefted function.
134 ;; Define hypb:copy-sublists if not a builtin. This version
135 ;; is a Lisp translation of the C version in Lemacs 19.8.
136 ;; Copyright (C) 1985, 1986, 1987, 1992, 1993 Free Software Foundation, Inc.
137 ;; Available for use and distribution under the GPL.
138 ;;
139 (defun hypb:copy-sublists (obj &optional vector-p)
140 "Return a copy of a list and substructures.
141 The argument is copied, and any lists contained within it are copied
142 recursively. Circularities and shared substructures are not preserved.
143 Second arg VECP causes vectors to be copied, too. Strings are not copied."
144 (cond ((consp obj)
145 (let (rest)
146 (setq obj (copy-sequence obj)
147 rest obj)
148 (while (consp rest)
149 (let ((elt (car rest)))
150 (if quit-flag (top-level))
151 (if (or (consp elt) (vectorp elt))
152 (setcar rest (hypb:copy-sublists elt vector-p)))
153 (if (vectorp (cdr rest))
154 (setcdr rest (hypb:copy-sublists (cdr rest) vector-p)))
155 (setq rest (cdr rest))))))
156 ((and (vectorp obj) obj)
157 (let ((i (length obj))
158 (j 0)
159 elt)
160 (setq obj (copy-sequence obj))
161 (while (< j i)
162 (setq elt (aref obj j))
163 (if quit-flag (top-level))
164 (if (or (consp elt) (vectorp elt))
165 (aset obj j (hypb:copy-sublists elt vector-p)))
166 (setq j (1+ j))))))
167 obj))
168
169 (defun hypb:debug ()
170 "Loads Hyperbole hbut.el source file and sets debugging traceback flag."
171 (interactive)
172 (or (featurep 'hinit) (load "hsite"))
173 (or (and (featurep 'hbut)
174 (let ((func (hypb:indirect-function 'ebut:create)))
175 (not (or (hypb:v19-byte-code-p func)
176 (eq 'byte-code
177 (car (car (nthcdr 3 (hypb:indirect-function
178 'ebut:create)))))))))
179 (load "hbut.el"))
180 (setq debug-on-error t))
181
182 (defun hypb:domain-name ()
183 "Returns current Internet domain name with '@' prepended or nil if none."
184 (let* ((dname-cmd (or (file-exists-p "/usr/bin/domainname")
185 (file-exists-p "/bin/domainname")))
186 (dname (or (getenv "DOMAINNAME")
187 (if dname-cmd
188 (hypb:call-process-p
189 "domainname" nil
190 '(substring (buffer-string) 0 -1))))))
191 (if (or (and dname (string-match "\\." dname))
192 (let* ((src "/etc/resolv.conf")
193 (src-buf-exists-p (get-file-buffer src)))
194 (and (file-exists-p src) (file-readable-p src)
195 (save-excursion
196 (set-buffer (find-file-noselect src))
197 (goto-char (point-min))
198 (if (re-search-forward "^domain[ \t]+\\([^ \t\n]+\\)"
199 nil t)
200 (setq dname (buffer-substring (match-beginning 1)
201 (match-end 1))))
202 (or src-buf-exists-p (kill-buffer nil))
203 dname))))
204 (concat "@" dname))))
205
206 (defun hypb:error (&rest args)
207 "Signals an error typically to be caught by 'hui:menu'."
208 (let ((msg (apply 'format args)))
209 (put 'error 'error-message msg)
210 (error msg)))
211
212 (defun hypb:functionp (obj)
213 "Returns t if OBJ is a function, nil otherwise."
214 (cond
215 ((symbolp obj) (fboundp obj))
216 ((subrp obj))
217 ((hypb:v19-byte-code-p obj))
218 ((consp obj)
219 (if (eq (car obj) 'lambda) (listp (car (cdr obj)))))
220 (t nil)))
221
222 (defun hypb:function-copy (func-symbol)
223 "Copies FUNC-SYMBOL's body for overloading. Returns copy of body."
224 (if (fboundp func-symbol)
225 (let ((func (hypb:indirect-function func-symbol)))
226 (cond ((listp func) (copy-sequence func))
227 ((subrp func) (error "(hypb:function-copy): `%s' is a primitive; can't copy body."
228 func-symbol))
229 ((and (hypb:v19-byte-code-p func) (fboundp 'make-byte-code))
230 (let ((new-code (append func nil))) ; turn it into a list
231 (apply 'make-byte-code new-code)))
232 (t (error "(hypb:function-copy): Can't copy function body: %s" func))
233 ))
234 (error "(hypb:function-copy): `%s' symbol is not bound to a function."
235 func-symbol)))
236
237 (defun hypb:function-overload (func-sym prepend &rest new-forms)
238 "Redefine function named FUNC-SYM by either PREPENDing (or appending if nil) rest of quoted NEW-FORMS."
239 (let ((old-func-sym (intern
240 (concat "*hypb-old-"
241 (symbol-name func-sym)
242 "*"))))
243 (or (fboundp old-func-sym)
244 (fset old-func-sym (hypb:function-copy func-sym)))
245 (let* ((old-func (hypb:indirect-function old-func-sym))
246 (old-param-list (action:params old-func))
247 (param-list (action:param-list old-func))
248 (old-func-call
249 (list (if (memq '&rest old-param-list)
250 ;; Have to account for extra list wrapper from &rest.
251 (cons 'apply
252 (cons (list 'quote old-func-sym) param-list))
253 (cons old-func-sym param-list)))))
254 (eval (append
255 (list 'defun func-sym old-param-list)
256 (delq nil
257 (list
258 (documentation old-func-sym)
259 (action:commandp old-func-sym)))
260 (if prepend
261 (append new-forms old-func-call)
262 (append old-func-call new-forms)))))))
263
264 (defun hypb:function-symbol-replace (func-sym sym-to-replace replace-with-sym)
265 "Replaces in body of FUNC-SYM SYM-TO-REPLACE with REPLACE-WITH-SYM.
266 All occurrences within lists are replaced. Returns body of modified FUNC-SYM."
267 (let ((body (hypb:indirect-function func-sym))
268 (arg-vector) (arg))
269 (if (listp body)
270 ;; assume V18 byte compiler
271 (setq arg-vector
272 (car (delq nil (mapcar
273 (function
274 (lambda (elt)
275 (and (listp elt)
276 (vectorp (setq arg-vector (nth 2 elt)))
277 arg-vector)))
278 body))))
279 ;; assume V19 byte compiler (eq (compiled-function-p body) t)
280 (setq arg (aref body 2)
281 arg-vector (if (vectorp arg) arg))
282 )
283 (if arg-vector
284 ;; Code is byte-compiled.
285 (let ((i (1- (length arg-vector))))
286 (setq arg nil)
287 (while (and (not arg) (>= i 0))
288 (if (eq (setq arg (aref arg-vector i)) sym-to-replace)
289 (aset arg-vector i replace-with-sym)
290 (setq arg nil i (1- i)))))
291 ;; Code is not byte-compiled.
292 ;; Only replaces occurrence of symbol as an element of a list.
293 (hypb:map-sublists
294 (function
295 (lambda (atom list) (if (eq atom sym-to-replace)
296 (let ((again t))
297 (while (and again list)
298 (if (eq (car list) atom)
299 (progn (setcar list replace-with-sym)
300 (setq again nil))
301 (setq list (cdr list))))))))
302 body)
303 )
304 body))
305
306 (defun hypb:help-buf-name (&optional prefix)
307 "Returns a Hyperbole help buffer name for current buffer.
308 With optional PREFIX string, uses it rather than buffer name."
309 (let ((bn (or prefix (buffer-name))))
310 (if (string-match " Hypb " bn)
311 (buffer-name (generate-new-buffer bn))
312 (concat "*" bn hypb:help-buf-suffix))))
313
314 (defun hypb:indirect-function (obj)
315 "Return the function at the end of OBJ's function chain.
316 Resolves autoloadable function symbols properly."
317 (let ((func
318 (if (fboundp 'indirect-function)
319 (indirect-function obj)
320 (while (symbolp obj)
321 (setq obj (symbol-function obj)))
322 obj)))
323 ;; Handle functions with autoload bodies.
324 (if (and (symbolp obj) (listp func) (eq (car func) 'autoload))
325 (let ((load-file (car (cdr func))))
326 (load load-file)
327 ;; Prevent infinite recursion
328 (if (equal func (symbol-function obj))
329 (error "(hypb:indirect-function): Autoload of '%s' failed" obj)
330 (hypb:indirect-function obj)))
331 func)))
332
333 (defun hypb:insert-region (buffer start end invisible-flag)
334 "Insert into BUFFER the contents of a region from START to END in the current buffer.
335 INVISIBLE-FLAG, if non-nil, means invisible text in an outline region is
336 copied, otherwise, it is omitted."
337 (let ((from-koutline (eq major-mode 'kotl-mode)))
338 (append-to-buffer buffer start end)
339 (save-excursion
340 (set-buffer buffer)
341 (let ((first (- (point) (- end start)))
342 (last (point)))
343 ;; Remove from buffer any copied text that was hidden if invisible-flag
344 ;; is nil.
345 (if invisible-flag
346 ;; Show all hidden text within the copy.
347 (subst-char-in-region first last ?\r ?\n t)
348 ;; Remove hidden text.
349 (goto-char first)
350 (while (search-forward "\r" last t)
351 (delete-region (1- (point)) (progn (end-of-line) (point)))))
352 ;;
353 ;; If region came from a koutline, remove any characters with an
354 ;; invisible property which separate cells.
355 (if from-koutline
356 (kproperty:map
357 (function (lambda (prop) (delete-char 1))) 'invisible t))))))
358
359 (if (or hyperb:lemacs-p hyperb:emacs19-p)
360 (fset 'hypb:mark 'mark)
361 (defun hypb:mark (inactive-p)
362 "Return this buffer's mark value as integer, or nil if no mark.
363 INACTIVE-P non-nil means return value of mark even if region is not active
364 under Emacs version 19.
365 If you are using this in an editing command, you are most likely making
366 a mistake; see the documentation of `set-mark'."
367 (mark))
368 )
369 (if hyperb:lemacs-p
370 (fset 'hypb:mark-marker 'mark-marker)
371 (defun hypb:mark-marker (inactive-p)
372 "Return this buffer's mark as a marker object, or nil if no mark.
373 INACTIVE-P is unused, it is for compatibility with Lucid Emacs'
374 version of mark-marker."
375 (mark-marker))
376 )
377
378 (defun hypb:map-sublists (func list)
379 "Applies FUNC to every atom found at any level of LIST.
380 FUNC must take two arguments, an atom and a list in which the atom is found.
381 Returns values from applications of FUNC as a list with the same
382 structure as LIST. FUNC is therefore normally used just for its side-effects."
383 (mapcar (function
384 (lambda (elt) (if (atom elt)
385 (funcall func elt list)
386 (hypb:map-sublists func elt)))
387 list)))
388
389 (defun hypb:map-vector (func object)
390 "Returns list of results of application of FUNC to each element of OBJECT.
391 OBJECT should be a vector or byte-code object."
392 (if (not (or (vectorp object) (hypb:v19-byte-code-p object)))
393 (error "(hypb:map-vector): Second argument must be a vector or byte-code object."))
394 (let ((end (length object))
395 (i 0)
396 (result))
397 (while (< i end)
398 (setq result (cons (funcall func (aref object i)) result)
399 i (1+ i)))
400 (nreverse result)))
401
402 (defun hypb:mouse-help-file ()
403 "Return the full path to the Hyperbole mouse key help file."
404 (let* ((hypb-man (expand-file-name "man/" hyperb:dir))
405 (help-file (expand-file-name "hypb-mouse.txt" hypb-man)))
406 (if (or (file-exists-p help-file)
407 (file-exists-p
408 (setq help-file (expand-file-name
409 "hypb-mouse.txt" data-directory))))
410 help-file
411 (error "(hypb:mouse-help-file): Non-existent file: \"%s\"" help-file))))
412
413 (if (or hyperb:lemacs-p hyperb:emacs19-p)
414 (fset 'hypb:push-mark 'push-mark)
415 (defun hypb:push-mark (&optional location nomsg activate-region)
416 "Set mark at LOCATION (point, by default) and push old mark on mark ring.
417 If the last global mark pushed was not in the current buffer,
418 also push LOCATION on the global mark ring.
419 Display `Mark set' unless the optional second arg NOMSG is non-nil.
420 Optional third arg ACTIVATE-REGION is ignored.
421
422 Novice Emacs Lisp programmers often try to use the mark for the wrong
423 purposes. See the documentation of `set-mark' for more information."
424 (push-mark location nomsg))
425 )
426
427 (defun hypb:replace-match-string (regexp str newtext &optional literal)
428 "Replaces all matches for REGEXP in STR with NEWTEXT string.
429 Optional LITERAL non-nil means do a literal replacement.
430 Otherwise treat \\ in NEWTEXT string as special:
431 \\& means substitute original matched text,
432 \\N means substitute match for \(...\) number N,
433 \\\\ means insert one \\.
434 NEWTEXT may instead be a function of one argument, the string to replace in,
435 that returns a replacement string."
436 (if (not (stringp str))
437 (error "(hypb:replace-match-string): 2nd arg must be a string: %s" str))
438 (if (or (stringp newtext) (hypb:functionp newtext))
439 nil
440 (error "(hypb:replace-match-string): 3rd arg must be a string or function: %s"
441 newtext))
442 (let ((rtn-str "")
443 (start 0)
444 (special)
445 match prev-start)
446 (while (setq match (string-match regexp str start))
447 (setq prev-start start
448 start (match-end 0)
449 rtn-str
450 (concat
451 rtn-str
452 (substring str prev-start match)
453 (cond ((hypb:functionp newtext) (funcall newtext str))
454 (literal newtext)
455 (t (mapconcat
456 (function
457 (lambda (c)
458 (if special
459 (progn
460 (setq special nil)
461 (cond ((eq c ?\\) "\\")
462 ((eq c ?&)
463 (substring str
464 (match-beginning 0)
465 (match-end 0)))
466 ((and (>= c ?0) (<= c ?9))
467 (if (> c (+ ?0 (length
468 (match-data))))
469 ;; Invalid match num
470 (error "(hypb:replace-match-string) Invalid match num: %c" c)
471 (setq c (- c ?0))
472 (substring str
473 (match-beginning c)
474 (match-end c))))
475 (t (char-to-string c))))
476 (if (eq c ?\\) (progn (setq special t) nil)
477 (char-to-string c)))))
478 newtext ""))))))
479 (concat rtn-str (substring str start))))
480
481 (defun hypb:supercite-p ()
482 "Returns non-nil iff the Emacs add-on supercite package is in use."
483 (let (hook-val)
484 (if (memq t (mapcar
485 (function
486 (lambda (hook-var)
487 (and (boundp hook-var)
488 (progn (setq hook-val (symbol-value hook-var))
489 (cond ((listp hook-val)
490 (if (memq 'sc-cite-original hook-val)
491 t))
492 ((eq hook-val 'sc-cite-original)))))))
493 '(mail-citation-hook mail-yank-hooks)))
494 t)))
495
496 ;;; Next function is copied from a copylefted function:
497 ;;; Copyright (C) 1987, 1988 Kyle E. Jones
498 (if (or hyperb:lemacs-p hyperb:emacs19-p)
499 (defun hypb:window-list-all-frames (&optional mini)
500 "Returns a list of Lisp window objects for all Emacs windows in all frames.
501 Optional first arg MINI t means include the minibuffer window
502 in the list, even if it is not active. If MINI is neither t
503 nor nil it means to not count the minibuffer window even if it is active."
504 (let* ((first-window (next-window
505 (previous-window (selected-window) nil t t)
506 mini t t))
507 (windows (cons first-window nil))
508 (current-cons windows)
509 (w (next-window first-window mini t t)))
510 (while (not (eq w first-window))
511 (setq current-cons (setcdr current-cons (cons w nil)))
512 (setq w (next-window w mini t t)))
513 windows)))
514
515 ;;; Next function is copied from a copylefted function:
516 ;;; Copyright (C) 1987, 1988 Kyle E. Jones
517 (defun hypb:window-list (&optional mini)
518 "Returns a list of Lisp window objects for all Emacs windows in selected frame.
519 Optional first arg MINI t means include the minibuffer window
520 in the list, even if it is not active. If MINI is neither t
521 nor nil it means to not count the minibuffer window even if it is active."
522 (let* ((first-window (next-window
523 (previous-window (selected-window)) mini))
524 (windows (cons first-window nil))
525 (current-cons windows)
526 (w (next-window first-window mini)))
527 (while (not (eq w first-window))
528 (setq current-cons (setcdr current-cons (cons w nil)))
529 (setq w (next-window w mini)))
530 windows))
531
532 (defun hypb:v19-byte-code-p (obj)
533 "Return non-nil iff OBJ is an Emacs V19 byte compiled object."
534 (or (and (fboundp 'compiled-function-p) (compiled-function-p obj))
535 (and (fboundp 'byte-code-function-p) (byte-code-function-p obj))))
536
537 ;;; ************************************************************************
538 ;;; Private functions
539 ;;; ************************************************************************
540
541 (defun hypb:oct-to-int (oct-num)
542 "Returns octal integer OCTAL-NUM converted to a decimal integer."
543 (let ((oct-str (int-to-string oct-num))
544 (dec-num 0))
545 (and (string-match "[^0-7]" oct-str)
546 (error (format "(hypb:oct-to-int): Bad octal number: %s" oct-str)))
547 (mapconcat (function
548 (lambda (o)
549 (setq dec-num (+ (* dec-num 8)
550 (if (and (>= o ?0) (<= o ?7))
551 (- o ?0))))))
552 oct-str "")
553 dec-num))
554
555 ;;; ************************************************************************
556 ;;; Private variables
557 ;;; ************************************************************************
558
559 (provide 'hypb)