Expand PMF_FN_* macros.
[netbsd-mini2440.git] / gnu / usr.bin / send-pr / send-pr-el.in
bloba7e90409cb87e0983ba4e395ac3fe01cc39c4e27
1 ;;;; -*-emacs-lisp-*-
2 ;;;;---------------------------------------------------------------------------
3 ;;;;    EMACS interface for send-pr (by Heinz G. Seidl, hgs@cygnus.com)
4 ;;;;    Slightly hacked by Brendan Kehoe (brendan@cygnus.com).
5 ;;;;
6 ;;;;    This file is part of the Problem Report Management System (GNATS)
7 ;;;;    Copyright 1992, 1993 Cygnus Support
8 ;;;;
9 ;;;;    This program is free software; you can redistribute it and/or
10 ;;;;    modify it under the terms of the GNU General Public
11 ;;;;    License as published by the Free Software Foundation; either
12 ;;;;    version 2 of the License, or (at your option) any later version.
13 ;;;;
14 ;;;;    This program is distributed in the hope that it will be useful,
15 ;;;;    but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;;;;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 ;;;;    General Public License for more details.
18 ;;;;
19 ;;;;    You should have received a copy of the GNU Library General Public
20 ;;;;    License along with this program; if not, write to the Free
21 ;;;;    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 ;;;;
23 ;;;;---------------------------------------------------------------------------
24 ;;;;
25 ;;;;    This file contains the EMACS interface to the Problem Report Management
26 ;;;;    System (GNATS):
27 ;;;;
28 ;;;;            - The `send-pr' command and the `send-pr-mode' for sending 
29 ;;;;              Problem Reports (PRs).
30 ;;;;
31 ;;;;    For more information about how to send a PR see send-pr(1).
32 ;;;;
33 ;;;;---------------------------------------------------------------------------
34 ;;;;
35 ;;;;    Configuration: the symbol `DEFAULT-RELEASE' can be replaced by
36 ;;;;    site/release specific strings during the configuration/installation
37 ;;;;    process.
38 ;;;;
39 ;;;;    Install this file in your EMACS library directory.
40 ;;;;
41 ;;;;---------------------------------------------------------------------------
43 (provide 'send-pr)
45 ;;;;---------------------------------------------------------------------------
46 ;;;; Customization: put the following forms into your default.el file
47 ;;;; (or into your .emacs)
48 ;;;;---------------------------------------------------------------------------
50 ;(autoload 'send-pr-mode "send-pr"
51 ;         "Major mode for sending problem reports." t)
53 ;(autoload 'send-pr "send-pr"
54 ;                 "Command to create and send a problem report." t)
56 ;;;;---------------------------------------------------------------------------
57 ;;;; End of Customization Section
58 ;;;;---------------------------------------------------------------------------
60 (autoload 'server-buffer-done "server")
61 (defvar server-buffer-clients nil)
62 (defvar mail-self-blind nil)
63 (defvar mail-default-reply-to nil)
65 (defconst send-pr::version "xVERSIONx")
67 (defvar gnats:root "xGNATS_ROOTx"
68   "*The top of the tree containing the GNATS database.")
70 ;;;;---------------------------------------------------------------------------
71 ;;;; hooks
72 ;;;;---------------------------------------------------------------------------
74 (defvar text-mode-hook nil)   ; we define it here in case it's not defined
75 (defvar send-pr-mode-hook text-mode-hook "Called when send-pr is invoked.")
77 ;;;;---------------------------------------------------------------------------
78 ;;;; Domains and default values for (some of) the Problem Report fields;
79 ;;;; constants and definitions.
80 ;;;;---------------------------------------------------------------------------
82 (defconst gnats::emacs-19p
83   (not (or (and (boundp 'epoch::version) epoch::version)
84            (string-lessp emacs-version "19")))
85   "Is this emacs v19?")
87 ;;; This has to be here rather than at the bottom of this file with
88 ;;; the other utility functions because it is used by
89 ;;; gnats::get-config, which is called when send-pr.el is being
90 ;;; loaded (see the "defconst" below), before the whole file has been
91 ;;; loaded.
93 (defun gnats::find-safe-default-directory (&optional buffer)
94 "If the directory referred to by `default-directory' for the current
95 buffer (or for optional argument BUFFER) does not exist, set it to the home
96 directory of the current user if that exists, or to `/'.
98 Returns the final value of default-directory in the buffer."
99   (let ((homedir (expand-file-name "~/")))
100     (save-excursion
101       (if buffer (set-buffer buffer))
102       (if (not (file-exists-p default-directory))
103           (if (file-exists-p homedir)
104               (setq default-directory homedir)
105             (setq default-directory "/")))
106       default-directory)))
108 ;;; These may be changed during configuration/installation or by the individual
109 ;;; user in his/her .emacs file.
111 (defun gnats::get-config (var)
112   (let ((shell-file-name "/bin/sh")
113         (buf (generate-new-buffer " *GNATS config*"))
114         ret)
115     (save-excursion
116       (set-buffer buf)
117       (shell-command-on-region
118        (point-min) (point-max)
119        (concat ". " gnats:root "/gnats-adm/config; echo $" var ) t)
120       (goto-char (point-min))
121       ; We have to use get-buffer, since shell-command-on-region will wipe
122       ; out the buffer if there's no output from the command.
123       (if (or (not (get-buffer "*Shell Command Output*"))
124               (looking-at "/bin/sh:\\|\.:\\|\n"))
125           (setq ret nil)
126         (setq ret (buffer-substring (point-min) (- (point-max) 1)))))
127     (kill-buffer buf)
128     ret))
130 ;; const because it must match the script's value
131 (defconst send-pr:datadir (or (gnats::get-config "DATADIR") "xDATADIRx")
132   "*Where the `gnats' subdirectory containing category lists lives.")
134 (defvar send-pr::sites nil
135   "List of GNATS support sites; computed at runtime.")
136 (defvar send-pr:default-site
137   (or (gnats::get-config "GNATS_SITE") "xGNATS_SITEx")
138   "Default site to send bugs to.")
139 (defvar send-pr:::site send-pr:default-site
140   "The site to which a problem report is currently being submitted, or NIL
141 if using the default site (buffer local).")
143 (defvar send-pr:::categories nil
144   "Buffer local list of available categories, derived at runtime from
145 send-pr:::site and send-pr::category-alist.")
146 (defvar send-pr::category-alist nil
147   "Alist of GNATS support sites and the categories supported at each; computed
148 at runtime.")
150 ;;; Ideally we would get all the following values from a central database
151 ;;; during runtime instead of having them here in the code.
153 (defconst send-pr::fields
154   (` (("Category" send-pr::set-categories
155        (, (or (gnats::get-config "DEFAULT_CATEGORY") nil)) enum)
156       ("Class" (("sw-bug") ("doc-bug") ("change-request") ("support"))
157        (, (or (gnats::get-config "DEFAULT_CLASS") 0)) enum)
158       ("Confidential" (("yes") ("no"))
159        (, (or (gnats::get-config "DEFAULT_CONFIDENTIAL") 1)) enum)
160       ("Severity" (("non-critical") ("serious") ("critical"))
161        (, (or (gnats::get-config "DEFAULT_SEVERITY") 1)) enum)
162       ("Priority" (("low") ("medium") ("high"))
163        (, (or (gnats::get-config "DEFAULT_PRIORITY") 1)) enum)
164       ("Release" nil
165        (, (or (gnats::get-config "DEFAULT_RELEASE") "xDEFAULT_RELEASEx"))
166        text)
167       ("Submitter-Id" nil
168        (, (or (gnats::get-config "SUBMITTER") "xSUBMITTERx")) text)
169       ("Synopsis" nil nil text
170        (lambda (a b c) (gnats::set-mail-field "Subject" c)))))
171   "AList, keyed on the name of the field, of:
172 1) The field name.
173 2) The list of completions.  This can be a list, a function to call, or nil.
174 3) The default value.
175 4) The type of the field.
176 5) A sub-function to call when changed.")
178 (defvar gnats::fields nil)
180 (defmacro gnats::push (i l)
181   (` (setq (, l) (cons (,@ (list i l))))))
183 (defun send-pr::set-categories (&optional arg)
184   "Get the list of categories for the current site out of
185 send-pr::category-alist if there or from send-pr if not.  With arg, force
186 update."
187   ;;
188   (let ((entry (assoc send-pr:::site send-pr::category-alist)))
189     (or (and entry (null arg))
190         (let ((oldpr (getenv "GNATS_ROOT")) cats)
191           (send-pr::set-sites arg)
192           (setenv "GNATS_ROOT" gnats:root)
193           (setq cats (gnats::get-value-from-shell
194                       "send-pr" "-CL" send-pr:::site))
195           (setenv "GNATS_ROOT" oldpr)
196           (if entry (setcdr entry cats)
197             (setq entry (cons send-pr:::site cats))
198             (gnats::push entry send-pr::category-alist))))
199     (setq send-pr:::categories (cdr entry))))
201 (defun send-pr::set-sites (&optional arg)
202   "Get the list of sites (by listing the contents of DATADIR/gnats) and assign
203 it to send-pr::sites.  With arg, force update."
204   (or (and (null arg) send-pr::sites)
205       (progn
206         (setq send-pr::sites nil)
207         (mapcar
208          (function
209           (lambda (file)
210             (or (memq t (mapcar (function (lambda (x) (string= x file)))
211                                 '("." ".." "pr-edit" "pr-addr")))
212                 (not (file-readable-p file))
213                 (gnats::push (list (file-name-nondirectory file))
214                             send-pr::sites))))
215          (directory-files (format "%s/gnats" send-pr:datadir) t))
216         (setq send-pr::sites (reverse send-pr::sites)))))
218 (defconst send-pr::pr-buffer-name "*send-pr*"
219   "Name of the temporary buffer, where the problem report gets composed.")
221 (defconst send-pr::err-buffer-name "*send-pr-error*"
222   "Name of the temporary buffer, where send-pr error messages appear.")
224 (defvar send-pr:::err-buffer nil
225   "The error buffer used by the current PR buffer.")
227 (defvar send-pr:::spawn-to-send nil
228   "Whether or not send-pr-mode should spawn a send-pr process to send the PR.")
230 (defconst gnats::indent 16 "Indent for formatting the value.")
232 ;;;;---------------------------------------------------------------------------
233 ;;;; `send-pr' - command for creating and sending of problem reports
234 ;;;;---------------------------------------------------------------------------
236 (fset 'send-pr 'send-pr:send-pr)
237 (defun send-pr:send-pr (&optional site)
238   "Create a buffer and read in the result of `send-pr -P'.
239 When finished with editing the problem report use \\[send-pr:submit-pr]
240 to send the PR with `send-pr -b -f -'."
241   ;;
242   (interactive
243    (if current-prefix-arg
244        (list (completing-read "Site: " (send-pr::set-sites 'recheck) nil t
245                               send-pr:default-site))))
246   (or site (setq site send-pr:default-site))
247   (let ((buf (get-buffer send-pr::pr-buffer-name)))
248     (if (or (not buf)
249             (progn (switch-to-buffer buf)
250                    (cond ((or (not (buffer-modified-p buf))
251                               (y-or-n-p "Erase previous problem report? "))
252                           (erase-buffer) t)
253                          (t nil))))
254         (send-pr::start-up site))))
256 (defun send-pr::start-up (site)
257   (switch-to-buffer (get-buffer-create send-pr::pr-buffer-name))
258   (setq default-directory (expand-file-name "~/"))
259   (auto-save-mode auto-save-default)
260   (let ((oldpr (getenv "GNATS_ROOT"))
261         (case-fold-search nil))
262     (setenv "GNATS_ROOT" gnats:root)
263     (send-pr::insert-template site)
264     (setenv "GNATS_ROOT" oldpr)
265     (goto-char (point-min))
266     (if (looking-at "send-pr:")
267         (cond ((looking-at "send-pr: .* does not have a categories list")
268                (setq send-pr::sites nil)
269                (error "send-pr: the GNATS site %s does not have a categories list" site))
270               (t (error (buffer-substring (point-min) (point-max)))))
271       (save-excursion
272         ;; Clear cruft inserted by bdamaged .cshrcs
273         (goto-char 1)
274         (re-search-forward "^SEND-PR:")
275         (delete-region 1 (match-beginning 0)))))
276   (set-buffer-modified-p nil)
277   (send-pr:send-pr-mode)
278   (setq send-pr:::site site)
279   (setq send-pr:::spawn-to-send t)
280   (send-pr::set-categories)
281   (if (null send-pr:::categories)
282       (progn
283         (and send-pr:::err-buffer (kill-buffer send-pr:::err-buffer))
284         (kill-buffer nil)
285         (message "send-pr: no categories found"))
286     (if (eq mail-default-reply-to t)
287         (setq mail-default-reply-to (getenv "REPLYTO")))
288     (and mail-default-reply-to
289          (gnats::set-mail-field "Reply-To" mail-default-reply-to))
290     (and mail-self-blind
291          (gnats::set-mail-field "BCC" (user-login-name)))
292     (mapcar 'send-pr::maybe-change-field send-pr::fields)
293     (gnats::position-on-field "Description")
294     (message (substitute-command-keys
295               "To send the problem report use: \\[send-pr:submit-pr]"))))
297 (defvar send-pr::template-alist nil
298   "An alist containing the output of send-pr -P <sitename> for various sites.")
300 (defun send-pr::insert-template (site)
301   (let ((elt (assoc site send-pr::template-alist)))
302     (if elt
303         (save-excursion (insert (cdr elt)))
304       (shell-command (concat "send-pr -P " site) t)
305       (save-excursion
306         (setq send-pr::template-alist
307               (cons (cons site (buffer-substring (point-min) (point-max)))
308                     send-pr::template-alist))))))
310 (fset 'do-send-pr 'send-pr:submit-pr)   ;backward compat
311 (defun send-pr:submit-pr ()
312   "Pipe the contents of the buffer *send-pr* to `send-pr -f -.' unless this
313 buffer was loaded with emacsclient, in which case save the buffer and exit."
314   ;;
315   (interactive)
316   (cond
317    ((and (boundp 'server-buffer-clients)
318          server-buffer-clients)
319     (let ((buffer (current-buffer))
320           (version-control nil) (buffer-backed-up nil))
321       (save-buffer buffer)
322       (kill-buffer buffer)
323       (server-buffer-done buffer)))
324    (send-pr:::spawn-to-send
325     (or (and send-pr:::err-buffer
326              (buffer-name send-pr:::err-buffer))
327         (setq send-pr:::err-buffer
328               (get-buffer-create send-pr::err-buffer-name)))
329     (let ((err-buffer send-pr:::err-buffer) mesg ok)
330       (save-excursion (set-buffer err-buffer) (erase-buffer))
331       (message "running send-pr...")
332       (let ((oldpr (getenv "GNATS_ROOT")))
333         (setenv "GNATS_ROOT" gnats:root)
334         (call-process-region (point-min) (point-max) "send-pr"
335                              nil err-buffer nil send-pr:::site
336                              "-b" "-f" "-")
337         (setenv "GNATS_ROOT" oldpr))
338       (message "running send-pr...done")
339       ;; stupidly we cannot check the return value in EMACS 18.57, thus we need
340       ;; this kluge to find out whether send-pr succeeded.
341       (if (save-excursion
342             (set-buffer err-buffer)
343             (goto-char (point-min))
344             (setq mesg (buffer-substring (point-min) (- (point-max) 1)))
345             (search-forward "problem report sent" nil t))
346           (progn (message mesg)
347                  (kill-buffer err-buffer)
348                  (delete-auto-save-file-if-necessary)
349                  (set-buffer-modified-p nil)
350                  (bury-buffer))
351         (pop-to-buffer err-buffer))
352     ))
353    (t
354     (save-buffer)
355     (message "Exit emacs to send the PR."))))
356    
357 ;;;;---------------------------------------------------------------------------
358 ;;;; send-pr:send-pr-mode mode
359 ;;;;---------------------------------------------------------------------------
361 (defvar send-pr-mode-map
362   (let ((map (make-sparse-keymap)))
363     (define-key map "\C-c\C-c" 'send-pr:submit-pr)
364     (define-key map "\C-c\C-f" 'gnats:change-field)
365     (define-key map "\M-n" 'gnats:next-field)
366     (define-key map "\M-p" 'gnats:previous-field)
367     (define-key map "\C-\M-f" 'gnats:forward-field)
368     (define-key map "\C-\M-b" 'gnats:backward-field)
369     map)
370   "Keymap for send-pr mode.")
372 (defconst gnats::keyword "^>\\([-a-zA-Z]+\\):")
373 (defconst gnats::before-keyword "[ \t\n\f]*[\n\f]+>\\([-a-zA-Z]+\\):")
374 (defconst gnats::after-keyword "^>\\([-a-zA-Z]+\\):[ \t\n\f]+")
376 (fset 'send-pr-mode 'send-pr:send-pr-mode)
377 (defun send-pr:send-pr-mode ()
378   "Major mode for submitting problem reports.
379 For information about the form see gnats(1) and send-pr(1).
380 Special commands: \\{send-pr-mode-map}
381 Turning on send-pr-mode calls the value of the variable send-pr-mode-hook,
382 if it is not nil."
383   (interactive)
384   (gnats::patch-exec-path)
385   (put 'send-pr:send-pr-mode 'mode-class 'special)
386   (kill-all-local-variables)
387   (setq major-mode 'send-pr:send-pr-mode)
388   (setq mode-name "send-pr")
389   (use-local-map send-pr-mode-map)
390   (set-syntax-table text-mode-syntax-table)
391   (setq local-abbrev-table text-mode-abbrev-table)
392   (setq buffer-offer-save t)
393   (make-local-variable 'send-pr:::site)
394   (make-local-variable 'send-pr:::categories)
395   (make-local-variable 'send-pr:::err-buffer)
396   (make-local-variable 'send-pr:::spawn-to-send)
397   (make-local-variable 'paragraph-separate)
398   (setq paragraph-separate (concat (default-value 'paragraph-separate)
399                                    "\\|" gnats::keyword "[ \t\n\f]*$"))
400   (make-local-variable 'paragraph-start)
401   (setq paragraph-start (concat (default-value 'paragraph-start)
402                                 "\\|" gnats::keyword))
403   (run-hooks 'send-pr-mode-hook)
404   t)
406 ;;;;---------------------------------------------------------------------------
407 ;;;; Functions to read and replace field values.
408 ;;;;---------------------------------------------------------------------------
410 (defun gnats::position-on-field (field &optional quiet)
411   (goto-char (point-min))
412   (if (not (re-search-forward (concat "^>" field ":") nil t))
413       (if quiet
414           nil
415         (error "Field `>%s:' not found." field))
416     (re-search-forward "[ \t\n\f]*")
417     (if (looking-at gnats::keyword)
418         (backward-char 1))
419     t))
421 (defun gnats::mail-position-on-field (field)
422   (let (end
423         (case-fold-search t))
424     (goto-char (point-min))
425     (re-search-forward "^$")
426     (setq end (match-beginning 0))
427     (goto-char (point-min))
428     (if (not (re-search-forward (concat "^" field ":") end 'go-to-end))
429         (insert field ": \n")
430       (re-search-forward "[ \t\n\f]*"))
431     (skip-chars-backward "\n")
432     t))
434 (defun gnats::field-contents (field &optional elem move)
435   (let (pos)
436     (unwind-protect
437         (save-excursion
438           (if (not (gnats::position-on-field field t))
439               nil
440             (setq pos (point-marker))
441             (if (or (looking-at "<.*>$") (eolp))
442                 t
443               (looking-at ".*$")        ; to set match-{beginning,end}
444               (gnats::nth-word 
445                (buffer-substring (match-beginning 0) (match-end 0))
446                elem))))
447       (and move pos (goto-char pos)))))
449 (defun gnats::functionp (thing)
450   (or (and (symbolp thing) (fboundp thing))
451       (and (listp thing) (eq (car thing) 'lambda))))
453 (defun gnats::field-values (field)
454   "Return the possible (known) values for field FIELD."
455   (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
456                    send-pr::fields))
457          (thing (elt (assoc field fields) 1)))
458     (cond ((gnats::functionp thing) (funcall thing))
459           ((listp thing) thing)
460           (t (error "ACK")))))
462 (defun gnats::field-default (field)
463   "Return the default value for field FIELD."
464   (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
465                    send-pr::fields))
466          (thing (elt (assoc field fields) 2)))
467     (cond ((stringp thing) thing)
468           ((null thing) "")
469           ((numberp thing) (car (elt (gnats::field-values field) thing)))
470           ((gnats::functionp thing)
471            (funcall thing (gnats::field-contents field)))
472           ((eq thing t) (gnats::field-contents field))
473           (t (error "ACK")))))
475 (defun gnats::field-type (field)
476   "Return the type of field FIELD."
477   (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
478                    send-pr::fields))
479          (thing (elt (assoc field fields) 3)))
480     thing))
482 (defun gnats::field-action (field)
483   "Return the extra handling function for field FIELD."
484   (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields
485                    send-pr::fields))
486          (thing (elt (assoc field fields) 4)))
487     (cond ((null thing) 'ignore)
488           ((gnats::functionp thing) thing)
489           (t (error "ACK")))))
491 ;;;;---------------------------------------------------------------------------
492 ;;;; Point movement functions
493 ;;;;---------------------------------------------------------------------------
495 (or (fboundp 'defsubst) (fset 'defsubst 'defun))
497 (defun send-pr::maybe-change-field (field)
498   (setq field (car field))
499   (let ((thing (gnats::field-contents field)))
500     (and thing (eq t thing)
501          (not (eq 'multi-text (gnats::field-type field)))
502          (gnats:change-field field))))
503     
504 (defun gnats:change-field (&optional field default)
505   "Change the value of the field containing the cursor.  With arg, ask the
506 user for the field to change.  From a program, the function takes optional
507 arguments of the field to change and the default value to use."
508   (interactive)
509   (or field current-prefix-arg (setq field (gnats::current-field)))
510   (or field
511       (setq field
512             (completing-read "Field: "
513                              (if (eq major-mode 'gnats:gnats-mode)
514                                  gnats::fields
515                                send-pr::fields)
516                              nil t)))
517   (gnats::position-on-field field)
518   (sit-for 0)
519   (let* ((old (gnats::field-contents field))
520          new)
521     (if (null old)
522         (error "ACK")
523       (if (or (interactive-p) t)
524           (let ((prompt (concat ">" field ": "))
525                 (domain (gnats::field-values field))
526                 (type (gnats::field-type field)))
527             (or default (setq default (gnats::field-default field)))
528             (setq new
529                   (if (eq type 'enum)
530                       (completing-read prompt domain nil t 
531                                        (if gnats::emacs-19p (cons default 0)
532                                          default))
533                     (read-string prompt (if gnats::emacs-19p (cons default 1)
534                                           default)))))
535         (setq new default))
536       (gnats::set-field field new)
537       (funcall (gnats::field-action field) field old new)
538       new)))
540 (defun gnats::set-field (field value)
541   (save-excursion
542     (gnats::position-on-field field)
543     (delete-horizontal-space)
544     (looking-at ".*$")
545     (replace-match
546      (concat (make-string (- gnats::indent (length field) 2) ?\40 ) value) t)))
548 (defun gnats::set-mail-field (field value)
549   (save-excursion
550     (gnats::mail-position-on-field field)
551     (delete-horizontal-space)
552     (looking-at ".*$")
553     (replace-match (concat " " value) t)))
554   
555 (defun gnats::before-keyword (&optional where)
556   "Returns t if point is in some white space before a keyword.
557 If where is nil, then point is not changed; if where is t then point is moved
558 to the beginning of the keyword, otherwise it is moved to the beginning
559 of the white space it was in."
560   ;;
561   (if (looking-at gnats::before-keyword)
562       (prog1 t
563         (cond  ((eq where t)
564                 (re-search-forward "^>") (backward-char))
565                ((not (eq where nil))
566                 (re-search-backward "[^ \t\n\f]") (forward-char))))
567        nil))
569 (defun gnats::after-keyword (&optional where)
570   "Returns t if point is in some white space after a keyword.
571 If where is nil, then point is not changed; if where is t then point is moved
572 to the beginning of the keyword, otherwise it is moved to the end of the white
573 space it was in."
574   ;;
575   (if (gnats::looking-after gnats::after-keyword)
576       (prog1 t
577         (cond  ((eq where t)
578                 (re-search-backward "^>"))
579                ((not (eq where nil))
580                 (re-search-forward "[^ \t\n\f]") (backward-char))))
581        nil))
583 (defun gnats::in-keyword (&optional where)
584   "Returns t if point is within a keyword.
585 If where is nil, then point is not changed; if where is t then point is moved
586 to the beginning of the keyword."
587   ;;
588   (let ((old-point (point-marker)))
589     (beginning-of-line)
590     (cond ((and (looking-at gnats::keyword)
591                (< old-point (match-end 0)))
592            (prog1 t
593              (if (eq where t) 
594                  t
595                (goto-char old-point))))
596           (t (goto-char old-point)
597              nil))))
599 (defun gnats::forward-bofield ()
600   "Moves point to the beginning of a field. Assumes that point is in the
601 keyword." 
602   ;;
603   (if (re-search-forward "[ \t\n\f]+[^ \t\n\f]" (point-max) '-)
604       (backward-char)
605     t))
607 (defun gnats::backward-eofield ()
608   "Moves point to the end of a field. Assumes point is in the keyword."
609   ;;
610   (if (re-search-backward "[^ \t\n\f][ \t\n\f]+" (point-min) '-)
611       (forward-char)
612     t))
614 (defun gnats::forward-eofield ()
615   "Moves point to the end of a field. Assumes that point is in the field." 
616   ;;
617   ;; look for the next field
618   (if (re-search-forward gnats::keyword (point-max) '-) 
619       (progn (beginning-of-line) (gnats::backward-eofield))
620   (re-search-backward "[^ \t\n\f][ \t\n\f]*" (point-min) '-)
621   (forward-char)))
623 (defun gnats::backward-bofield ()
624   "Moves point to the beginning of a field. Assumes that point is in the
625 field." 
626   ;;
627   ;;look for previous field
628   (if (re-search-backward gnats::keyword (point-min) '-)
629       (gnats::forward-bofield)
630     t))
633 (defun gnats:forward-field ()
634   "Move point forward to the end of the field or to the beginning of the next
635 field."
636   ;;
637   (interactive)
638   (if (or (gnats::before-keyword t) (gnats::in-keyword t)
639           (gnats::after-keyword t))
640         (gnats::forward-bofield)
641     (gnats::forward-eofield)))
643 (defun gnats:backward-field ()
644   "Move point backward to the beginning/end of a field."
645   ;;
646   (interactive)
647   (backward-char)
648   (if (or (gnats::before-keyword t) (gnats::in-keyword t)
649           (gnats::after-keyword t))
650       (gnats::backward-eofield)
651     (gnats::backward-bofield)))
653 (defun gnats:next-field ()
654   "Move point to the beginning of the next field."
655   ;;
656   (interactive)
657   (if (or (gnats::before-keyword t) (gnats::in-keyword t)
658           (gnats::after-keyword t))
659       (gnats::forward-bofield)
660     (if (re-search-forward gnats::keyword (point-max) '-)
661         (gnats::forward-bofield)
662       t)))
664 (defun gnats:previous-field ()
665   "Move point to the beginning of the previous field."
666   ;;
667   (interactive)
668   (backward-char)
669   (if (or (gnats::after-keyword t) (gnats::in-keyword t)
670           (gnats::before-keyword t))
671       (progn (re-search-backward gnats::keyword (point-min) '-)
672              (gnats::forward-bofield))
673     (gnats::backward-bofield)))
675 (defun gnats:beginning-of-field ()
676   "Move point to the beginning of the current field."
677   (interactive)
678   (cond ((gnats::in-keyword t)
679          (gnats::forward-bofield))
680         ((gnats::after-keyword 0))
681         (t
682          (gnats::backward-bofield))))
684 (defun gnats::current-field ()
685   (save-excursion
686     (if (cond ((or (gnats::in-keyword t) (gnats::after-keyword t))
687                (looking-at gnats::keyword))
688               ((re-search-backward gnats::keyword nil t)))
689         (buffer-substring (match-beginning 1) (match-end 1))
690       nil)))
692 ;;;;---------------------------------------------------------------------------
693 ;;;; Support functions
694 ;;;;---------------------------------------------------------------------------
696 (defun gnats::looking-after (regex)
697   "Returns t if point is after regex."
698   ;;
699   (let* ((old-point (point))
700          (start (if (eobp)
701                    old-point
702                  (forward-char) (point))))
703     (cond ((re-search-backward regex (point-min) t)
704            (goto-char old-point)
705            (cond ((eq (match-end 0) start)
706                   t))))))
708 (defun gnats::nth-word (string &optional elem)
709   "Returns the elem-th word of the string.
710 If elem is nil, then the first wort is returned, if elem is 0 then
711 the whole string is returned."
712    ;;
713   (if (integerp elem)
714       (cond ((eq elem 0) string)
715             ((eq elem 1) (gnats::first-word string))
716             ((equal string "") "")
717             ((>= elem 2) 
718              (let ((i 0) (value ""))
719                (setq string             ; strip leading blanks
720                      (substring string (or (string-match "[^ \t]" string) 0)))
721                (while (< i elem)
722                  (setq value 
723                        (substring string 0 
724                                   (string-match "[ \t]*$\\|[ \t]+" string)))
725                  (setq string 
726                        (substring string (match-end 0)))
727                  (setq i (+ i 1)))
728                value)))
729     (gnats::first-word string)))
731 (defun gnats::first-word (string)
732   (setq string 
733         (substring string (or (string-match "[^ \t]" string) 0)))
734   (substring string 0 (string-match "[ \t]*$\\|[ \t]+" string)))
736 ;;;;---------------------------------------------------------------------------
738 (defun gnats::patch-exec-path ()
739   ;;
740   "Replaces `//' by `/' in `exec-path'."
741   ;;
742   ;(make-local-variable 'exec-path)
743   (let ((err-buffer (get-buffer-create " *gnats::patch-exec-path*"))
744         (ret))
745     (setq exec-path (save-excursion (set-buffer err-buffer)
746                                     (prin1 exec-path err-buffer)
747                                     (goto-char (point-min))
748                                     (replace-string "//" "/")
749                                     (goto-char (point-min))
750                                     (setq ret (read err-buffer))
751                                     (kill-buffer err-buffer)
752                                     ret
753                                     ))))
755 (defun gnats::get-value-from-shell (&rest command)
756   "Execute shell command to get a list of valid values for `variable'."
757   ;;
758   (let ((err-buffer (get-buffer-create " *gnats::get-value-from-shell*")))
759     (save-excursion
760       (set-buffer err-buffer)
761       (unwind-protect
762           (condition-case var
763               (progn
764                 (apply 'call-process
765                        (car command) nil err-buffer nil (cdr command))
766                 (goto-char (point-min))
767                 (if (looking-at "[-a-z]+: ")
768                     (error (buffer-substring (point-min) (point-max))))
769                 (read err-buffer))
770             (error nil))
771         (kill-buffer err-buffer)))))
773 (or (fboundp 'setenv)
774     (defun setenv (variable &optional value)
775       "Set the value of the environment variable named VARIABLE to VALUE.
776 VARIABLE should be a string.  VALUE is optional; if not provided or is
777 `nil', the environment variable VARIABLE will be removed.  
778 This function works by modifying `process-environment'."
779       (interactive "sSet environment variable: \nsSet %s to value: ")
780       (if (string-match "=" variable)
781           (error "Environment variable name `%s' contains `='" variable)
782         (let ((pattern (concat "\\`" (regexp-quote (concat variable "="))))
783               (case-fold-search nil)
784               (scan process-environment))
785           (while scan
786             (cond
787              ((string-match pattern (car scan))
788               (if (eq nil value)
789                   (setq process-environment (delq (car scan)
790                                                   process-environment))
791                 (setcar scan (concat variable "=" value)))
792               (setq scan nil))
793              ((null (setq scan (cdr scan)))
794               (setq process-environment
795                     (cons (concat variable "=" value)
796                           process-environment)))))))))
798 ;;;; end of send-pr.el