send-pr-el.in revision 60882
190619Stmm;;;; -*-emacs-lisp-*- 290619Stmm;;;;--------------------------------------------------------------------------- 390619Stmm;;;; EMACS interface for send-pr (by Heinz G. Seidl, hgs@cygnus.com) 490619Stmm;;;; Slightly hacked by Brendan Kehoe (brendan@cygnus.com). 590619Stmm;;;; 690619Stmm;;;; This file is part of the Problem Report Management System (GNATS) 790619Stmm;;;; Copyright 1992, 1993 Cygnus Support 890619Stmm;;;; 990619Stmm;;;; This program is free software; you can redistribute it and/or 1090619Stmm;;;; modify it under the terms of the GNU General Public 1190619Stmm;;;; License as published by the Free Software Foundation; either 1290619Stmm;;;; version 2 of the License, or (at your option) any later version. 1390619Stmm;;;; 1490619Stmm;;;; This program is distributed in the hope that it will be useful, 1590619Stmm;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of 1690619Stmm;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1790619Stmm;;;; General Public License for more details. 1890619Stmm;;;; 1990619Stmm;;;; You should have received a copy of the GNU Library General Public 2090619Stmm;;;; License along with this program; if not, write to the Free 2190619Stmm;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2290619Stmm;;;; 2390619Stmm;;;;--------------------------------------------------------------------------- 2490619Stmm;;;; 2590619Stmm;;;; This file contains the EMACS interface to the Problem Report Management 2690619Stmm;;;; System (GNATS): 2790619Stmm;;;; 2890619Stmm;;;; - The `send-pr' command and the `send-pr-mode' for sending 2990619Stmm;;;; Problem Reports (PRs). 3090619Stmm;;;; 3190619Stmm;;;; For more information about how to send a PR see send-pr(1). 3290619Stmm;;;; 3390619Stmm;;;;--------------------------------------------------------------------------- 3490619Stmm;;;; 3590619Stmm;;;; Configuration: the symbol `DEFAULT-RELEASE' can be replaced by 3690619Stmm;;;; site/release specific strings during the configuration/installation 3790619Stmm;;;; process. 3890619Stmm;;;; 3990619Stmm;;;; Install this file in your EMACS library directory. 4090619Stmm;;;; 4190619Stmm;;;;--------------------------------------------------------------------------- 4290619Stmm;;;; 4390619Stmm;;;; $FreeBSD: head/gnu/usr.bin/send-pr/send-pr-el.in 60882 2000-05-24 14:40:25Z steve $ 4490619Stmm 4590619Stmm(provide 'send-pr) 4690619Stmm 4790619Stmm;;;;--------------------------------------------------------------------------- 4890619Stmm;;;; Customization: put the following forms into your default.el file 4990619Stmm;;;; (or into your .emacs) 5090619Stmm;;;;--------------------------------------------------------------------------- 5190619Stmm 5290619Stmm;(autoload 'send-pr-mode "send-pr" 5390619Stmm; "Major mode for sending problem reports." t) 5490619Stmm 5590619Stmm;(autoload 'send-pr "send-pr" 5690619Stmm; "Command to create and send a problem report." t) 5790619Stmm 5890619Stmm;;;;--------------------------------------------------------------------------- 5990619Stmm;;;; End of Customization Section 6090619Stmm;;;;--------------------------------------------------------------------------- 6190619Stmm 6290619Stmm(autoload 'server-buffer-done "server") 6390619Stmm(defvar server-buffer-clients nil) 6490619Stmm(defvar mail-self-blind nil) 6590619Stmm(defvar mail-default-reply-to nil) 6690619Stmm 6790619Stmm(defconst send-pr::version "3.2") 6890619Stmm 6990619Stmm(defvar gnats:root "/home/gnats" 7090619Stmm "*The top of the tree containing the GNATS database.") 7190619Stmm 7290619Stmm;;;;--------------------------------------------------------------------------- 7390619Stmm;;;; hooks 7490619Stmm;;;;--------------------------------------------------------------------------- 75129083Smux 7690619Stmm(defvar text-mode-hook nil) ; we define it here in case it's not defined 7790619Stmm(defvar send-pr-mode-hook text-mode-hook "Called when send-pr is invoked.") 7890619Stmm 7990619Stmm;;;;--------------------------------------------------------------------------- 8090619Stmm;;;; Domains and default values for (some of) the Problem Report fields; 81129083Smux;;;; constants and definitions. 82129083Smux;;;;--------------------------------------------------------------------------- 8390619Stmm 8490619Stmm(defconst gnats::emacs-19p 8590619Stmm (not (or (and (boundp 'epoch::version) epoch::version) 86111119Simp (string-lessp emacs-version "19"))) 87111119Simp "Is this emacs v19?") 8890619Stmm 8990619Stmm;;; These may be changed during configuration/installation or by the individual 9090619Stmm;;; user in his/her .emacs file. 9190619Stmm;;; 9290619Stmm(defun gnats::get-config (var) 9390619Stmm (let ((shell-file-name "/bin/sh") 9490619Stmm (buf (generate-new-buffer " *GNATS config*")) 9590619Stmm ret) 9690619Stmm (save-excursion 9790619Stmm (set-buffer buf) 9890619Stmm (shell-command (concat ". " gnats:root "/gnats-adm/config; echo $" var ) 9990619Stmm t) 100129083Smux (if (looking-at "^\\.:\\|/bin/sh:\\|\n") 10190619Stmm (setq ret nil) 10290619Stmm (setq ret (buffer-substring (point-min) (- (point-max) 1))))) 10390619Stmm (kill-buffer buf) 10490619Stmm ret)) 10590619Stmm 10690619Stmm;; const because it must match the script's value 10790619Stmm(defconst send-pr:datadir (or (gnats::get-config "DATADIR") "@DATADIR@") 10890619Stmm "*Where the `gnats' subdirectory containing category lists lives.") 109 110(defvar send-pr::sites nil 111 "List of GNATS support sites; computed at runtime.") 112(defvar send-pr:default-site 113 (or (gnats::get-config "GNATS_SITE") "freefall") 114 "Default site to send bugs to.") 115(defvar send-pr:::site send-pr:default-site 116 "The site to which a problem report is currently being submitted, or NIL 117if using the default site (buffer local).") 118 119(defvar send-pr:::categories nil 120 "Buffer local list of available categories, derived at runtime from 121send-pr:::site and send-pr::category-alist.") 122(defvar send-pr::category-alist nil 123 "Alist of GNATS support sites and the categories supported at each; computed 124at runtime.") 125 126;;; Ideally we would get all the following values from a central database 127;;; during runtime instead of having them here in the code. 128;;; 129(defconst send-pr::fields 130 (` (("Category" send-pr::set-categories 131 (, (or (gnats::get-config "DEFAULT_CATEGORY") nil)) enum) 132 ("Class" (("sw-bug") ("doc-bug") ("change-request")) 133 (, (or (gnats::get-config "DEFAULT_CONFIDENTIAL") 0)) enum) 134 ("Confidential" (("yes") ("no")) 135 (, (or (gnats::get-config "DEFAULT_CONFIDENTIAL") 1)) enum) 136 ("Severity" (("non-critical") ("serious") ("critical")) 137 (, (or (gnats::get-config "DEFAULT_SEVERITY") 1)) enum) 138 ("Priority" (("low") ("medium") ("high")) 139 (, (or (gnats::get-config "DEFAULT_PRIORITY") 1)) enum) 140 ("Release" nil 141 (, (or (gnats::get-config "DEFAULT_RELEASE") "@DEFAULT_RELEASE@")) 142 text) 143 ("Submitter-Id" nil 144 (, (or (gnats::get-config "DEFAULT_SUBMITTER") "unknown")) 145 text) 146 ("Synopsis" nil nil text 147 (lambda (a b c) (gnats::set-mail-field "Subject" c))))) 148 "AList, keyed on the name of the field, of: 1491) The field name. 1502) The list of completions. This can be a list, a function to call, or nil. 1513) The default value. 1524) The type of the field. 1535) A sub-function to call when changed.") 154 155(defvar gnats::fields nil) 156 157(defmacro gnats::push (i l) 158 (` (setq (, l) (cons (,@ (list i l)))))) 159 160(defun send-pr::set-categories (&optional arg) 161 "Get the list of categories for the current site out of 162send-pr::category-alist if there or from send-pr if not. With arg, force 163update." 164 ;; 165 (let ((entry (assoc send-pr:::site send-pr::category-alist))) 166 (or (and entry (null arg)) 167 (let ((oldpr (getenv "GNATS_ROOT")) cats) 168 (send-pr::set-sites arg) 169 (setenv "GNATS_ROOT" gnats:root) 170 (setq cats (gnats::get-value-from-shell 171 "send-pr" "-CL" send-pr:::site)) 172 (setenv "GNATS_ROOT" oldpr) 173 (if entry (setcdr entry cats) 174 (setq entry (cons send-pr:::site cats)) 175 (gnats::push entry send-pr::category-alist)))) 176 (setq send-pr:::categories (cdr entry)))) 177 178(defun send-pr::set-sites (&optional arg) 179 "Get the list of sites (by listing the contents of DATADIR/gnats) and assign 180it to send-pr::sites. With arg, force update." 181 (or (and (null arg) send-pr::sites) 182 (progn 183 (setq send-pr::sites nil) 184 (mapcar 185 (function 186 (lambda (file) 187 (or (memq t (mapcar (function (lambda (x) (string= x file))) 188 '("." ".." "pr-edit" "pr-addr"))) 189 (not (file-readable-p file)) 190 (gnats::push (list (file-name-nondirectory file)) 191 send-pr::sites)))) 192 (directory-files (format "%s/gnats" send-pr:datadir) t)) 193 (setq send-pr::sites (reverse send-pr::sites))))) 194 195(defconst send-pr::pr-buffer-name "*send-pr*" 196 "Name of the temporary buffer, where the problem report gets composed.") 197 198(defconst send-pr::err-buffer-name "*send-pr-error*" 199 "Name of the temporary buffer, where send-pr error messages appear.") 200 201(defvar send-pr:::err-buffer nil 202 "The error buffer used by the current PR buffer.") 203 204(defconst gnats::indent 17 "Indent for formatting the value.") 205 206;;;;--------------------------------------------------------------------------- 207;;;; `send-pr' - command for creating and sending of problem reports 208;;;;--------------------------------------------------------------------------- 209 210(fset 'send-pr 'send-pr:send-pr) 211(defun send-pr:send-pr (&optional site) 212 "Create a buffer and read in the result of `send-pr -P'. 213When finished with editing the problem report use \\[send-pr:submit-pr] 214to send the PR with `send-pr -b -f -'." 215 ;; 216 (interactive 217 (if current-prefix-arg 218 (list (completing-read "Site: " (send-pr::set-sites 'recheck) nil t 219 send-pr:default-site)))) 220 (or site (setq site send-pr:default-site)) 221 (let ((buf (get-buffer send-pr::pr-buffer-name))) 222 (if (or (not buf) 223 (progn (switch-to-buffer buf) 224 (cond ((or (not (buffer-modified-p buf)) 225 (y-or-n-p "Erase previous problem report? ")) 226 (erase-buffer) t) 227 (t nil)))) 228 (send-pr::start-up site)))) 229 230(defun send-pr::start-up (site) 231 (switch-to-buffer (get-buffer-create send-pr::pr-buffer-name)) 232 (setq default-directory (expand-file-name "~/")) 233 (auto-save-mode auto-save-default) 234 (let ((oldpr (getenv "GNATS_ROOT")) 235 (case-fold-search nil)) 236 (setenv "GNATS_ROOT" gnats:root) 237 (shell-command (concat "send-pr -P " site) t) 238 (setenv "GNATS_ROOT" oldpr) 239 (if (looking-at "send-pr:") 240 (cond ((looking-at "send-pr: .* does not have a categories list") 241 (setq send-pr::sites nil) 242 (error "send-pr: the GNATS site %s does not have a categories list" site)) 243 (t (error (buffer-substring (point-min) (point-max))))) 244 (save-excursion 245 ;; Clear cruft inserted by bdamaged .cshrcs 246 (re-search-forward "^SEND-PR:") 247 (delete-region 1 (match-beginning 0))))) 248 (set-buffer-modified-p nil) 249 (send-pr:send-pr-mode) 250 (setq send-pr:::site site) 251 (send-pr::set-categories) 252 (if (null send-pr:::categories) 253 (progn 254 (and send-pr:::err-buffer (kill-buffer send-pr:::err-buffer)) 255 (kill-buffer nil) 256 (message "send-pr: no categories found")) 257 (and mail-default-reply-to 258 (gnats::set-mail-field "Reply-To" mail-default-reply-to)) 259 (and mail-self-blind 260 (gnats::set-mail-field "BCC" (user-login-name))) 261 (mapcar 'send-pr::maybe-change-field send-pr::fields) 262 (gnats::position-on-field "Description") 263 (message (substitute-command-keys 264 "To send the problem report use: \\[send-pr:submit-pr]")))) 265 266(fset 'do-send-pr 'send-pr:submit-pr) ;backward compat 267(defun send-pr:submit-pr () 268 "Pipe the contents of the buffer *send-pr* to `send-pr -f -.' unless this 269buffer was loaded with emacsclient, in which case save the buffer and exit." 270 ;; 271 (interactive) 272 (cond 273 ((and (boundp 'server-buffer-clients) 274 server-buffer-clients) 275 (let ((buffer (current-buffer)) 276 (version-control nil) (buffer-backed-up nil)) 277 (save-buffer buffer) 278 (kill-buffer buffer) 279 (server-buffer-done buffer))) 280 (t 281 (or (and send-pr:::err-buffer 282 (buffer-name send-pr:::err-buffer)) 283 (setq send-pr:::err-buffer 284 (get-buffer-create send-pr::err-buffer-name))) 285 (let ((err-buffer send-pr:::err-buffer) mesg ok) 286 (save-excursion (set-buffer err-buffer) (erase-buffer)) 287 (message "running send-pr...") 288 (let ((oldpr (getenv "GNATS_ROOT"))) 289 (setenv "GNATS_ROOT" gnats:root) 290 (call-process-region (point-min) (point-max) "send-pr" 291 nil err-buffer nil send-pr:::site 292 "-b" "-f" "-") 293 (setenv "GNATS_ROOT" oldpr)) 294 (message "running send-pr...done") 295 ;; stupidly we cannot check the return value in EMACS 18.57, thus we need 296 ;; this kluge to find out whether send-pr succeeded. 297 (if (save-excursion 298 (set-buffer err-buffer) 299 (goto-char (point-min)) 300 (setq mesg (buffer-substring (point-min) (- (point-max) 1))) 301 (search-forward "problem report sent" nil t)) 302 (progn (message mesg) 303 (kill-buffer err-buffer) 304 (delete-auto-save-file-if-necessary) 305 (set-buffer-modified-p nil) 306 (bury-buffer)) 307 (pop-to-buffer err-buffer)) 308 )))) 309 310;;;;--------------------------------------------------------------------------- 311;;;; send-pr:send-pr-mode mode 312;;;;--------------------------------------------------------------------------- 313 314(defvar send-pr-mode-map 315 (let ((map (make-sparse-keymap))) 316 (define-key map "\C-c\C-c" 'send-pr:submit-pr) 317 (define-key map "\C-c\C-f" 'gnats:change-field) 318 (define-key map "\M-n" 'gnats:next-field) 319 (define-key map "\M-p" 'gnats:previous-field) 320 (define-key map "\C-\M-f" 'gnats:forward-field) 321 (define-key map "\C-\M-b" 'gnats:backward-field) 322 map) 323 "Keymap for send-pr mode.") 324 325(defconst gnats::keyword "^>\\([-a-zA-Z]+\\):") 326(defconst gnats::before-keyword "[ \t\n\f]*[\n\f]+>\\([-a-zA-Z]+\\):") 327(defconst gnats::after-keyword "^>\\([-a-zA-Z]+\\):[ \t\n\f]+") 328 329(fset 'send-pr-mode 'send-pr:send-pr-mode) 330(defun send-pr:send-pr-mode () 331 "Major mode for submitting problem reports. 332For information about the form see gnats(1) and send-pr(1). 333Special commands: \\{send-pr-mode-map} 334Turning on send-pr-mode calls the value of the variable send-pr-mode-hook, 335if it is not nil." 336 (interactive) 337 (gnats::patch-exec-path) 338 (put 'send-pr:send-pr-mode 'mode-class 'special) 339 (kill-all-local-variables) 340 (setq major-mode 'send-pr:send-pr-mode) 341 (setq mode-name "send-pr") 342 (use-local-map send-pr-mode-map) 343 (set-syntax-table text-mode-syntax-table) 344 (setq local-abbrev-table text-mode-abbrev-table) 345 (setq buffer-offer-save t) 346 (make-local-variable 'send-pr:::site) 347 (make-local-variable 'send-pr:::categories) 348 (make-local-variable 'send-pr:::err-buffer) 349 (make-local-variable 'paragraph-separate) 350 (setq paragraph-separate (concat (default-value 'paragraph-separate) 351 "\\|" gnats::keyword "[ \t\n\f]*$")) 352 (make-local-variable 'paragraph-start) 353 (setq paragraph-start (concat (default-value 'paragraph-start) 354 "\\|" gnats::keyword)) 355 (run-hooks 'send-pr-mode-hook) 356 t) 357 358;;;;--------------------------------------------------------------------------- 359;;;; Functions to read and replace field values. 360;;;;--------------------------------------------------------------------------- 361 362(defun gnats::position-on-field (field) 363 (goto-char (point-min)) 364 (if (not (re-search-forward (concat "^>" field ":") nil t)) 365 (error "Field `>%s:' not found." field) 366 (re-search-forward "[ \t\n\f]*") 367 (if (looking-at gnats::keyword) 368 (backward-char 1)) 369 t)) 370 371(defun gnats::mail-position-on-field (field) 372 (let (end 373 (case-fold-search t)) 374 (goto-char (point-min)) 375 (re-search-forward "^$") 376 (setq end (match-beginning 0)) 377 (goto-char (point-min)) 378 (if (not (re-search-forward (concat "^" field ":") end 'go-to-end)) 379 (insert field ": \n") 380 (re-search-forward "[ \t\n\f]*")) 381 (skip-chars-backward "\n") 382 t)) 383 384(defun gnats::field-contents (field &optional elem move) 385 (let (pos) 386 (unwind-protect 387 (save-excursion 388 (if (not (gnats::position-on-field field)) 389 nil 390 (setq pos (point-marker)) 391 (if (or (looking-at "<.*>$") (eolp)) 392 t 393 (looking-at ".*$") ; to set match-{beginning,end} 394 (gnats::nth-word 395 (buffer-substring (match-beginning 0) (match-end 0)) 396 elem)))) 397 (and move pos (goto-char pos))))) 398 399(defun gnats::functionp (thing) 400 (or (and (symbolp thing) (fboundp thing)) 401 (and (listp thing) (eq (car thing) 'lambda)))) 402 403(defun gnats::field-values (field) 404 "Return the possible (known) values for field FIELD." 405 (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields 406 send-pr::fields)) 407 (thing (elt (assoc field fields) 1))) 408 (cond ((gnats::functionp thing) (funcall thing)) 409 ((listp thing) thing) 410 (t (error "ACK"))))) 411 412(defun gnats::field-default (field) 413 "Return the default value for field FIELD." 414 (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields 415 send-pr::fields)) 416 (thing (elt (assoc field fields) 2))) 417 (cond ((stringp thing) thing) 418 ((null thing) "") 419 ((numberp thing) (car (elt (gnats::field-values field) thing))) 420 ((gnats::functionp thing) 421 (funcall thing (gnats::field-contents field))) 422 ((eq thing t) (gnats::field-contents field)) 423 (t (error "ACK"))))) 424 425(defun gnats::field-type (field) 426 "Return the type of field FIELD." 427 (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields 428 send-pr::fields)) 429 (thing (elt (assoc field fields) 3))) 430 thing)) 431 432(defun gnats::field-action (field) 433 "Return the extra handling function for field FIELD." 434 (let* ((fields (if (eq major-mode 'gnats:gnats-mode) gnats::fields 435 send-pr::fields)) 436 (thing (elt (assoc field fields) 4))) 437 (cond ((null thing) 'ignore) 438 ((gnats::functionp thing) thing) 439 (t (error "ACK"))))) 440 441;;;;--------------------------------------------------------------------------- 442;;;; Point movement functions 443;;;;--------------------------------------------------------------------------- 444 445(or (fboundp 'defsubst) (fset 'defsubst 'defun)) 446 447(defun send-pr::maybe-change-field (field) 448 (setq field (car field)) 449 (let ((thing (gnats::field-contents field))) 450 (and thing (eq t thing) 451 (not (eq 'multi-text (gnats::field-type field))) 452 (gnats:change-field field)))) 453 454(defun gnats:change-field (&optional field default) 455 "Change the value of the field containing the cursor. With arg, ask the 456user for the field to change. From a program, the function takes optional 457arguments of the field to change and the default value to use." 458 (interactive) 459 (or field current-prefix-arg (setq field (gnats::current-field))) 460 (or field 461 (setq field 462 (completing-read "Field: " 463 (if (eq major-mode 'gnats:gnats-mode) 464 gnats::fields 465 send-pr::fields) 466 nil t))) 467 (gnats::position-on-field field) 468 (sit-for 0) 469 (let* ((old (gnats::field-contents field)) 470 new) 471 (if (null old) 472 (error "ACK") 473 (let ((prompt (concat ">" field ": ")) 474 (domain (gnats::field-values field)) 475 (type (gnats::field-type field)) 476 (action (gnats::field-action field))) 477 (or default (setq default (gnats::field-default field))) 478 (setq new (if (eq type 'enum) 479 (completing-read prompt domain nil t 480 (if gnats::emacs-19p (cons default 0) 481 default)) 482 (read-string prompt (if gnats::emacs-19p (cons default 1) 483 default)))) 484 (gnats::set-field field new) 485 (funcall action field old new) 486 new)))) 487 488(defun gnats::set-field (field value) 489 (save-excursion 490 (gnats::position-on-field field) 491 (delete-horizontal-space) 492 (looking-at ".*$") 493 (replace-match 494 (concat (make-string (- gnats::indent (length field) 2) ?\40 ) value) t))) 495 496(defun gnats::set-mail-field (field value) 497 (save-excursion 498 (gnats::mail-position-on-field field) 499 (delete-horizontal-space) 500 (looking-at ".*$") 501 (replace-match (concat " " value) t))) 502 503(defun gnats::before-keyword (&optional where) 504 "Returns t if point is in some white space before a keyword. 505If where is nil, then point is not changed; if where is t then point is moved 506to the beginning of the keyword, otherwise it is moved to the beginning 507of the white space it was in." 508 ;; 509 (if (looking-at gnats::before-keyword) 510 (prog1 t 511 (cond ((eq where t) 512 (re-search-forward "^>") (backward-char)) 513 ((not (eq where nil)) 514 (re-search-backward "[^ \t\n\f]") (forward-char)))) 515 nil)) 516 517(defun gnats::after-keyword (&optional where) 518 "Returns t if point is in some white space after a keyword. 519If where is nil, then point is not changed; if where is t then point is moved 520to the beginning of the keyword, otherwise it is moved to the end of the white 521space it was in." 522 ;; 523 (if (gnats::looking-after gnats::after-keyword) 524 (prog1 t 525 (cond ((eq where t) 526 (re-search-backward "^>")) 527 ((not (eq where nil)) 528 (re-search-forward "[^ \t\n\f]") (backward-char)))) 529 nil)) 530 531(defun gnats::in-keyword (&optional where) 532 "Returns t if point is within a keyword. 533If where is nil, then point is not changed; if where is t then point is moved 534to the beginning of the keyword." 535 ;; 536 (let ((old-point (point-marker))) 537 (beginning-of-line) 538 (cond ((and (looking-at gnats::keyword) 539 (< old-point (match-end 0))) 540 (prog1 t 541 (if (eq where t) 542 t 543 (goto-char old-point)))) 544 (t (goto-char old-point) 545 nil)))) 546 547(defun gnats::forward-bofield () 548 "Moves point to the beginning of a field. Assumes that point is in the 549keyword." 550 ;; 551 (if (re-search-forward "[ \t\n\f]+[^ \t\n\f]" (point-max) '-) 552 (backward-char) 553 t)) 554 555(defun gnats::backward-eofield () 556 "Moves point to the end of a field. Assumes point is in the keyword." 557 ;; 558 (if (re-search-backward "[^ \t\n\f][ \t\n\f]+" (point-min) '-) 559 (forward-char) 560 t)) 561 562(defun gnats::forward-eofield () 563 "Moves point to the end of a field. Assumes that point is in the field." 564 ;; 565 ;; look for the next field 566 (if (re-search-forward gnats::keyword (point-max) '-) 567 (progn (beginning-of-line) (gnats::backward-eofield)) 568 (re-search-backward "[^ \t\n\f][ \t\n\f]*" (point-min) '-) 569 (forward-char))) 570 571(defun gnats::backward-bofield () 572 "Moves point to the beginning of a field. Assumes that point is in the 573field." 574 ;; 575 ;;look for previous field 576 (if (re-search-backward gnats::keyword (point-min) '-) 577 (gnats::forward-bofield) 578 t)) 579 580 581(defun gnats:forward-field () 582 "Move point forward to the end of the field or to the beginning of the next 583field." 584 ;; 585 (interactive) 586 (if (or (gnats::before-keyword t) (gnats::in-keyword t) 587 (gnats::after-keyword t)) 588 (gnats::forward-bofield) 589 (gnats::forward-eofield))) 590 591(defun gnats:backward-field () 592 "Move point backward to the beginning/end of a field." 593 ;; 594 (interactive) 595 (backward-char) 596 (if (or (gnats::before-keyword t) (gnats::in-keyword t) 597 (gnats::after-keyword t)) 598 (gnats::backward-eofield) 599 (gnats::backward-bofield))) 600 601(defun gnats:next-field () 602 "Move point to the beginning of the next field." 603 ;; 604 (interactive) 605 (if (or (gnats::before-keyword t) (gnats::in-keyword t) 606 (gnats::after-keyword t)) 607 (gnats::forward-bofield) 608 (if (re-search-forward gnats::keyword (point-max) '-) 609 (gnats::forward-bofield) 610 t))) 611 612(defun gnats:previous-field () 613 "Move point to the beginning of the previous field." 614 ;; 615 (interactive) 616 (backward-char) 617 (if (or (gnats::after-keyword t) (gnats::in-keyword t) 618 (gnats::before-keyword t)) 619 (progn (re-search-backward gnats::keyword (point-min) '-) 620 (gnats::forward-bofield)) 621 (gnats::backward-bofield))) 622 623(defun gnats:beginning-of-field () 624 "Move point to the beginning of the current field." 625 (interactive) 626 (cond ((gnats::in-keyword t) 627 (gnats::forward-bofield)) 628 ((gnats::after-keyword 0)) 629 (t 630 (gnats::backward-bofield)))) 631 632(defun gnats::current-field () 633 (save-excursion 634 (if (cond ((or (gnats::in-keyword t) (gnats::after-keyword t)) 635 (looking-at gnats::keyword)) 636 ((re-search-backward gnats::keyword nil t))) 637 (buffer-substring (match-beginning 1) (match-end 1)) 638 nil))) 639 640;;;;--------------------------------------------------------------------------- 641;;;; Support functions 642;;;;--------------------------------------------------------------------------- 643 644(defun gnats::looking-after (regex) 645 "Returns t if point is after regex." 646 ;; 647 (let* ((old-point (point)) 648 (start (if (eobp) 649 old-point 650 (forward-char) (point)))) 651 (cond ((re-search-backward regex (point-min) t) 652 (goto-char old-point) 653 (cond ((eq (match-end 0) start) 654 t)))))) 655 656(defun gnats::nth-word (string &optional elem) 657 "Returns the elem-th word of the string. 658If elem is nil, then the first wort is returned, if elem is 0 then 659the whole string is returned." 660 ;; 661 (if (integerp elem) 662 (cond ((eq elem 0) string) 663 ((eq elem 1) (gnats::first-word string)) 664 ((equal string "") "") 665 ((>= elem 2) 666 (let ((i 0) (value "")) 667 (setq string ; strip leading blanks 668 (substring string (or (string-match "[^ \t]" string) 0))) 669 (while (< i elem) 670 (setq value 671 (substring string 0 672 (string-match "[ \t]*$\\|[ \t]+" string))) 673 (setq string 674 (substring string (match-end 0))) 675 (setq i (+ i 1))) 676 value))) 677 (gnats::first-word string))) 678 679(defun gnats::first-word (string) 680 (setq string 681 (substring string (or (string-match "[^ \t]" string) 0))) 682 (substring string 0 (string-match "[ \t]*$\\|[ \t]+" string))) 683 684;;;;--------------------------------------------------------------------------- 685 686(defun gnats::patch-exec-path () 687 ;; 688 "Replaces `//' by `/' in `exec-path'." 689 ;; 690 ;(make-local-variable 'exec-path) 691 (let ((err-buffer (get-buffer-create " *gnats::patch-exec-path*")) 692 (ret)) 693 (setq exec-path (save-excursion (set-buffer err-buffer) 694 (prin1 exec-path err-buffer) 695 (goto-char (point-min)) 696 (replace-string "//" "/") 697 (goto-char (point-min)) 698 (setq ret (read err-buffer)) 699 (kill-buffer err-buffer) 700 ret 701 )))) 702 703(defun gnats::get-value-from-shell (&rest command) 704 "Execute shell command to get a list of valid values for `variable'." 705 ;; 706 (let ((err-buffer (get-buffer-create " *gnats::get-value-from-shell*"))) 707 (save-excursion 708 (set-buffer err-buffer) 709 (unwind-protect 710 (condition-case var 711 (progn 712 (apply 'call-process 713 (car command) nil err-buffer nil (cdr command)) 714 (goto-char (point-min)) 715 (if (looking-at "[-a-z]+: ") 716 (error (buffer-substring (point-min) (point-max)))) 717 (read err-buffer)) 718 (error nil)) 719 (kill-buffer err-buffer))))) 720 721(or (fboundp 'setenv) 722 (defun setenv (variable &optional value) 723 "Set the value of the environment variable named VARIABLE to VALUE. 724VARIABLE should be a string. VALUE is optional; if not provided or is 725`nil', the environment variable VARIABLE will be removed. 726This function works by modifying `process-environment'." 727 (interactive "sSet environment variable: \nsSet %s to value: ") 728 (if (string-match "=" variable) 729 (error "Environment variable name `%s' contains `='" variable) 730 (let ((pattern (concat "\\`" (regexp-quote (concat variable "=")))) 731 (case-fold-search nil) 732 (scan process-environment)) 733 (while scan 734 (cond 735 ((string-match pattern (car scan)) 736 (if (eq nil value) 737 (setq process-environment (delq (car scan) 738 process-environment)) 739 (setcar scan (concat variable "=" value))) 740 (setq scan nil)) 741 ((null (setq scan (cdr scan))) 742 (setq process-environment 743 (cons (concat variable "=" value) 744 process-environment))))))))) 745 746;;;; end of send-pr.el 747