home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / gnats-3.01 / send-pr / send-pr-el.in < prev    next >
Encoding:
Text File  |  1993-04-14  |  18.0 KB  |  528 lines

  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. ;;;;---------------------------------------------------------------------------
  42.  
  43.  
  44. (provide 'send-pr)
  45.  
  46. ;;;;---------------------------------------------------------------------------
  47. ;;;; Customization: put the following forms into your default.el file
  48. ;;;; (or into your .emacs)
  49. ;;;;---------------------------------------------------------------------------
  50.  
  51. ;(autoload 'send-pr-mode "send-pr"
  52. ;      "Major mode for sending problem reports." t)
  53.  
  54. ;(autoload 'send-pr "send-pr"
  55. ;            "Command to create and send a problem report." t)
  56.  
  57. ;;;;---------------------------------------------------------------------------
  58. ;;;; End of Customization Section
  59. ;;;;---------------------------------------------------------------------------
  60.  
  61. (defconst SEND-PR-VERSION "@VERSION@")
  62.  
  63. ;;;;---------------------------------------------------------------------------
  64. ;;;; hooks
  65. ;;;;---------------------------------------------------------------------------
  66.  
  67. (defvar text-mode-hook nil)   ; we define it here in case it's not defined
  68. (defvar send-pr-mode-hook text-mode-hook "Called when send-pr is invoked.")
  69.  
  70. ;;;;---------------------------------------------------------------------------
  71. ;;;; Domains and default values for (some of) the Problem Report fields;
  72. ;;;; constants and definitions.
  73. ;;;;---------------------------------------------------------------------------
  74.  
  75. ;;; These may be changed during configuration/installation or by the individual
  76. ;;; user in his/her .emacs file.
  77. ;;;
  78. (defconst DEFAULT-CATEGORY "")    ; reading this file
  79. (defconst DEFAULT-RELEASE  "@DEFAULT_RELEASE@")    ; should not cause errors
  80. (defvar gnats-default-category DEFAULT-CATEGORY
  81.   "Default category to use when submitting Problem Reports.")
  82. (defvar gnats-default-confidential "no"
  83.   "Default confidential value to use when submitting Problem Reports.")
  84. (defvar gnats-default-severity "serious"
  85.   "Default severity to use then submitting Problem Reports.")
  86. (defvar gnats-default-priority "medium"
  87.   "Default priority to use then submitting Problem Reports.")
  88. (defvar gnats-default-class "sw-bug"
  89.   "Default class to use when submitting Problem Reports.")
  90. (defvar gnats-default-release DEFAULT-RELEASE
  91.  
  92.   "Default release to use when submitting Problem Reports.")
  93.  
  94. ;;; Ideally we would get all the following values from a central database
  95. ;;; during runtime instead of having them here in the code.
  96. ;;;
  97. (defconst gnats-categories nil
  98.   "List of GNATS categories; computed at runtime.")
  99. (defconst gnats-confidential '(("yes") ("no"))
  100.   "List of GNATS confidential values.")
  101. (defconst gnats-severities '(("non-critical") ("serious") ("critical"))
  102.   "List of GNATS severities.")
  103. (defconst gnats-priorities '(("low") ("medium") ("high"))
  104.   "List of GNATS priorities.")
  105. (defconst gnats-classes '(("sw-bug") ("doc-bug") ("change-request") ("support"))
  106.   "List of GNATS classes.")
  107. (defconst gnats-releases nil
  108.   "List of GNATS releases; computed at runtime..")
  109. (defconst gnats-states 
  110.   '(("open") ("analyzed") ("feedback") ("suspended") ("closed"))
  111.   "List of GNATS states.")
  112.  
  113. (defconst gnats-state-following 
  114.       '(("open"      "analyzed")
  115.     ("analyzed"  "feedback")
  116.     ("feedback"  "closed")
  117.     ("suspended" "analyzed"))
  118.       "A list of states and possible following states (does not describe all possibilities).")
  119.  
  120. (defun gnats-set-categories ()
  121.   ;;
  122.   "Get the list of categories dynamically and assign it to gnats-categories."
  123.   ;;
  124.   (gnats-set-variable-from-shell 'gnats-categories "send-pr" "-CL"))
  125.  
  126. (defconst send-pr-buffer "*send-pr*"
  127.   "Name of the temporary buffer, where the problem report gets composed.")
  128.  
  129. (defconst send-pr-err-buffer "*send-pr-error*"
  130.   "Name of the temporary buffer, where send-pr error messages appear.")
  131.  
  132. (defconst gnats-indent 17 "Indent for formatting the value.")
  133.  
  134.  
  135. ;;;;---------------------------------------------------------------------------
  136. ;;;; `send-pr' - command for creating and sending of problem reports
  137. ;;;;---------------------------------------------------------------------------
  138.  
  139. (defun send-pr ()
  140.   ;;
  141.   "Create a buffer and read in the result of `send-pr -P'.
  142. When finished with editing the problem report use \\[do-send-pr]
  143. to send the PR with `send-pr -f -'."
  144.   ;;
  145.   (interactive)
  146.   (if (get-buffer send-pr-buffer)
  147.       (progn (switch-to-buffer send-pr-buffer)
  148.          (if (y-or-n-p "erase previous problem report? ")
  149.          (progn (erase-buffer)
  150.             (send-pr-start-up))
  151.            t))
  152.     (send-pr-start-up)))
  153.  
  154. (defun send-pr-start-up ()
  155.   (set-buffer (get-buffer-create send-pr-buffer))
  156.   (setq default-directory (expand-file-name "~/"))
  157.   (auto-save-mode auto-save-default)
  158.   (shell-command "send-pr -P" t)
  159.   (set-buffer-modified-p nil)
  160.   (if (null (send-pr-mode))
  161.       (progn
  162.     (kill-buffer (current-buffer))
  163.     (kill-buffer (get-buffer send-pr-err-buffer)))
  164.     (progn
  165.       (switch-to-buffer send-pr-buffer)
  166.       (gnats-completing-read-and-replace ">Category:" 
  167.                     gnats-categories gnats-default-category t)
  168.       (gnats-completing-read-and-replace ">Confidential:" 
  169.                     gnats-confidential gnats-default-confidential t)
  170.       (gnats-completing-read-and-replace ">Severity:"
  171.                     gnats-severities gnats-default-severity t)
  172.       (gnats-completing-read-and-replace ">Priority:"
  173.                     gnats-priorities gnats-default-priority t)
  174.       (gnats-completing-read-and-replace ">Class:"
  175.                     gnats-classes    gnats-default-class t)
  176.       (gnats-completing-read-and-replace ">Release:"
  177.                     gnats-releases   gnats-default-release)
  178.       (message
  179.        (substitute-command-keys "To send the problem report use: \\[do-send-pr]"))
  180.       (goto-char (point-min))
  181.       (re-search-forward "^Subject:")
  182.       (forward-char))))
  183.  
  184. (defun do-send-pr ()
  185.   ;;
  186.   "Pipe the contents of the buffer *send-pr* to `send-pr -f -.'"
  187.   ;;
  188.   (interactive)
  189.   (let ((err-buffer (get-buffer-create send-pr-err-buffer))
  190.     (ok nil))
  191.     (save-excursion (set-buffer err-buffer) (erase-buffer))
  192.     (message "starting send-pr ...")
  193.     (call-process-region (point-min) (point-max)
  194.              "send-pr"
  195.              nil
  196.              err-buffer
  197.              nil
  198.              "-r" "-f" "-")
  199.     ;; stupidly we cannot check the return value in EMACS 18.57, thus we need
  200.     ;; this kluge to find out, whether send-pr succeeded.
  201.     (save-excursion
  202.       (set-buffer err-buffer)
  203.       (goto-char (point-min))
  204.       (if (re-search-forward "problem report sent to" nil t) (setq ok t))
  205.       (if ok
  206.       (progn (message (buffer-substring (point-min) (- (point-max) 1)))
  207.          (if (bufferp err-buffer)
  208.              (kill-buffer err-buffer)))))
  209.     (delete-auto-save-file-if-necessary)
  210.     (set-buffer-modified-p nil)
  211.     (if ok (bury-buffer) (pop-to-buffer err-buffer)))
  212.   )
  213.  
  214. ;;;;---------------------------------------------------------------------------
  215. ;;;; Functions to read and replace field values.
  216. ;;;;---------------------------------------------------------------------------
  217.  
  218. (defun gnats-completing-read-and-replace  
  219.   (field domain &optional default require-match default-overwrites prompt)
  220.   ;;
  221.   "Reads a new value of a field and replaces the old one with it.
  222. Searches for a enum- or string-field `field', gets it's value and let
  223. one change the value with a new value from `domain'. `default' is the default
  224. value for the completing read. If `require-match' is non-nil, the the new valuemust match the domein. If `default-overwrites' is non-nil, then `default'
  225. is always used in the prompt. If `prompt' is non-nil, then it is used for the
  226. prompt. Returns the new value."
  227.   ;
  228.   (let ((old-value (gnats-find-field field)))
  229.     (if (eq old-value nil) 
  230.     (error "gnats-completing-read-and-replace: no such field %s" field))
  231.     (let* ((the-prompt 
  232.         (if prompt prompt (concat field " ")))
  233.        (the-default 
  234.         (if (or default-overwrites (eq old-value t)) default old-value))
  235.       (new-value 
  236.        (completing-read the-prompt domain nil require-match the-default)))
  237.       (delete-horizontal-space)
  238.       (looking-at ".*$")
  239.       (replace-match 
  240.        (concat (make-string (- gnats-indent (length field)) ?\40 ) new-value))
  241.       new-value)))
  242.  
  243. (defun gnats-find-field (field &optional elem)
  244.   ;;
  245.   "Search for a enum- or string-field `field'.
  246. Returns `nil', if field is not found, `t', if the value of the field is a 
  247. comment or empty and otherwise the field value.
  248. If elem is an integer, then the elem-th word of the field is returned.
  249. Point is left at the beginning of the whole found field"
  250.   ;;
  251.   (goto-char (point-min))
  252.   (if (re-search-forward (concat "^" field))
  253.       (progn
  254.     (forward-char 1)
  255.     (re-search-forward "[ \t]*")
  256.     (if (and (not (looking-at "<.*>$")) (not (eolp)))
  257.         (progn
  258.           (looking-at ".*$")    ; to set match-{beginning,end}
  259.           (gnats-nth-word 
  260.            (buffer-substring (match-beginning 0) (match-end 0))
  261.            elem))
  262.           t))
  263.     nil))
  264.  
  265. ;;;;---------------------------------------------------------------------------
  266. ;;;; send-pr-mode mode
  267. ;;;;---------------------------------------------------------------------------
  268.  
  269. (defvar send-pr-mode-map nil "Keymap for send-pr mode.")
  270.  
  271. (defun send-pr-mode ()
  272.   "Major mode for submitting problem reports.
  273. For information about the form see gnats(1) and send-pr(1).
  274. Special commands: \\{send-pr-mode-map}
  275. Turning on send-pr-mode calls the value of the variable send-pr-mode-hook,
  276. if it is not nil."
  277.   (interactive)
  278.   (gnats-patch-exec-path)
  279.   (gnats-set-categories)
  280.   (if (null gnats-categories)
  281.       nil
  282.     (progn
  283.       (put 'send-pr-mode 'mode-class 'special)
  284.       (kill-all-local-variables)
  285.       (setq major-mode 'send-pr-mode)
  286.       (setq mode-name "send-pr")
  287.       (use-local-map send-pr-mode-map)
  288.       (set-syntax-table text-mode-syntax-table)
  289.       (setq local-abbrev-table text-mode-abbrev-table)
  290.       (setq buffer-offer-save t)
  291.       (run-hooks 'send-pr-mode-hook)
  292.       t)))
  293.  
  294. (if send-pr-mode-map
  295.     nil
  296.   (setq send-pr-mode-map (make-sparse-keymap))
  297.   (define-key send-pr-mode-map "\C-c\C-c" 'do-send-pr)
  298.   (define-key send-pr-mode-map "\M-n" 'gnats-next-field)
  299.   (define-key send-pr-mode-map "\M-p" 'gnats-previous-field)
  300.   (define-key send-pr-mode-map "\C-\M-f" 'gnats-forward-field)
  301.   (define-key send-pr-mode-map "\C-\M-b" 'gnats-backward-field)
  302. )
  303.  
  304.  
  305. ;;;;---------------------------------------------------------------------------
  306. ;;;; Point movement functions
  307. ;;;;---------------------------------------------------------------------------
  308.  
  309. (defconst gnats-keyword "^>[-a-zA-Z]+:")
  310. (defconst gnats-before-keyword "[ \t\n\f]*[\n\f]+>[-a-zA-Z]+:")
  311. (defconst gnats-after-keyword "^>[-a-zA-Z]+:[ \t\n\f]+")
  312.  
  313.  
  314. (defun gnats-before-keyword (&optional where)
  315.   ;;
  316.   "Returns t if point is in some white space before a keyword.
  317. If were is nil, then point is not changed; if where is t then point is moved
  318. to the beginning of the keyword, otherwise it is moved to the beginning
  319. of the white space it was in."
  320.   ;;
  321.   (if (looking-at gnats-before-keyword)
  322.       (prog1 t
  323.     (cond  ((eq where t)
  324.         (re-search-forward "^>") (backward-char))
  325.            ((not (eq where nil))
  326.         (re-search-backward "[^ \t\n\f]") (forward-char))))
  327.        nil))
  328.  
  329. (defun gnats-after-keyword (&optional where)
  330.   ;;
  331.   "Returns t if point is in some white space after a keyword.
  332. If were is nil, then point is not changed; if where is t then point is moved
  333. to the end of the white space it was in, otherwise it is moved to the
  334. beginning of the keyword."
  335.   ;;
  336.   (if (gnats-looking-after gnats-after-keyword)
  337.       (prog1 t
  338.     (cond  ((eq where t)
  339.         (re-search-backward "^>"))
  340.            ((not (eq where nil))
  341.         (re-search-forward "[^ \t\n\f]") (backward-char))))
  342.        nil))
  343.  
  344. (defun gnats-in-keyword (&optional where)
  345.   ;;
  346.   "Returns t if point is within a keyword.
  347. If were is nil, then point is not changed; if where is t then point is moved
  348. to the beginning of the keyword."
  349.   ;;
  350.   (let ((old-point (point)))
  351.     (beginning-of-line)
  352.     (cond ((and (looking-at gnats-keyword)
  353.            (< old-point (match-end 0)))
  354.        (prog1 t
  355.          (if (eq where t) 
  356.          t
  357.            (goto-char old-point))))
  358.       (t (goto-char old-point)
  359.          nil))))
  360.  
  361.  
  362. (defun gnats-forward-bofield ()
  363.   ;;
  364.   "Moves point to the beginning of a field. Assumes that point is in the keyword." 
  365.   ;;
  366.   (if (re-search-forward "[ \t\n\f]+[^ \t\n\f]" (point-max) '-)
  367.       (backward-char)
  368.     t))
  369.  
  370. (defun gnats-backward-eofield ()
  371.   ;;
  372.   "Moves point to the end of a field. Assumes point is in the keyword."
  373.   ;;
  374.   (if (re-search-backward "[^ \t\n\f][ \t\n\f]+" (point-min) '-)
  375.       (forward-char)
  376.     t))
  377.  
  378. (defun gnats-forward-eofield ()
  379.   ;;
  380.   "Moves point to the end of a field. Assumes that point is in the field." 
  381.   ;;
  382.   (if (re-search-forward gnats-keyword (point-max) '-) ; loook for the next field
  383.       (progn (beginning-of-line) (gnats-backward-eofield))
  384.   (re-search-backward "[^ \t\n\f][ \t\n\f]*" (point-min) '-)
  385.   (forward-char)))
  386.  
  387. (defun gnats-backward-bofield ()
  388.   ;;
  389.   "Moves point to the beginning of a field. Assumes that point is in the field." 
  390.   ;;
  391.   (if (re-search-backward gnats-keyword (point-min) '-) ;look for previous field
  392.       (gnats-forward-bofield)
  393.     t))
  394.  
  395.  
  396. (defun gnats-forward-field ()
  397.   ;;
  398.   "Move point forward to the end of the field or to the beginning of the next field."
  399.   ;;
  400.   (interactive)
  401.   (if (or (gnats-before-keyword t) (gnats-in-keyword t) (gnats-after-keyword t))
  402.     (gnats-forward-bofield)
  403.     (gnats-forward-eofield)))
  404.  
  405. (defun gnats-backward-field ()
  406.   ;;
  407.   "Move point backward to the beginning/end of a field."
  408.   ;;
  409.   (interactive)
  410.   (backward-char)
  411.   (if (or (gnats-before-keyword t) (gnats-in-keyword t) (gnats-after-keyword t))
  412.       (gnats-backward-eofield)
  413.     (gnats-backward-bofield)))
  414.  
  415. (defun gnats-next-field ()
  416.   ;;
  417.   "Move point to the beginning of the next field."
  418.   ;;
  419.   (interactive)
  420.   (if (or (gnats-before-keyword t) (gnats-in-keyword t) (gnats-after-keyword t))
  421.       (gnats-forward-bofield)
  422.     (if (re-search-forward gnats-keyword (point-max) '-)
  423.     (gnats-forward-bofield)
  424.       t)))
  425.  
  426. (defun gnats-previous-field ()
  427.   ;;
  428.   "Move point to the beginning of the previous field."
  429.   ;;
  430.   (interactive)
  431.   (backward-char)
  432.   (if (or (gnats-before-keyword t) (gnats-in-keyword t) (gnats-after-keyword t))
  433.       (progn (re-search-backward gnats-keyword (point-min) '-)
  434.          (gnats-forward-bofield))
  435.     (gnats-backward-bofield)))
  436.  
  437.  
  438. ;;;;---------------------------------------------------------------------------
  439. ;;;; Support functions
  440. ;;;;---------------------------------------------------------------------------
  441.  
  442. (defun gnats-looking-after (regex)
  443.   ;;
  444.   "Returns t if point is after regex."
  445.   ;;
  446.   (let* ((old-point (point))
  447.      (start (if (eobp)
  448.            old-point
  449.          (forward-char) (point))))
  450.     (cond ((re-search-backward regex (point-min) t)
  451.        (goto-char old-point)
  452.        (cond ((eq (match-end 0) start)
  453.           t))))))
  454.  
  455.  
  456. (defun gnats-nth-word (string &optional elem)
  457.   ;;
  458.   "Returns the elem-th word of the string.
  459. If elem is nil, then the first wort is returned, if elem is 0 then
  460. the whole string is returned."
  461.    ;;
  462.   (if (integerp elem)
  463.       (cond ((eq elem 0) string)
  464.         ((eq elem 1) (gnats-first-word string))
  465.         ((equal string "") "")
  466.         ((>= elem 2) 
  467.          (let ((i 0) (value ""))
  468.            (setq string        ; strip leading blanks
  469.              (substring string (or (string-match "[^ \t]" string) 0)))
  470.            (while (< i elem)
  471.          (setq value 
  472.                (substring string 0 
  473.                   (string-match "[ \t]*$\\|[ \t]+" string)))
  474.          (setq string 
  475.                (substring string (match-end 0)))
  476.          (setq i (+ i 1)))
  477.            value)))
  478.     (gnats-first-word string)))
  479.  
  480. (defun gnats-first-word (string)
  481.   (setq string 
  482.     (substring string (or (string-match "[^ \t]" string) 0)))
  483.   (substring string 0 (string-match "[ \t]*$\\|[ \t]+" string)))
  484.  
  485.  
  486. (defun gnats-patch-exec-path ()
  487.   ;;
  488.   "Replaces `//' by `/' in `exec-path'.
  489. Uses `send-pr-err-buffer'."
  490.   ;;
  491.   ;(make-local-variable 'exec-path)
  492.   (let ((err-buffer (get-buffer-create send-pr-err-buffer))
  493.     (ret))
  494.     (setq exec-path (save-excursion 
  495.               (set-buffer err-buffer)
  496.               (erase-buffer)
  497.               (prin1 exec-path err-buffer)
  498.               (goto-char (point-min))
  499.               (replace-string "//" "/")
  500.               (goto-char (point-min))
  501.               (setq ret (read err-buffer))
  502.               (erase-buffer)
  503.               ret
  504.               ))))
  505.  
  506. (defun gnats-set-variable-from-shell (variable &rest command)
  507.   ;;
  508.   "Execute shell command to get a list of valid values for `variable'.
  509. Uses `send-pr-err-buffer'."
  510.   ;;
  511.   (if (eq (symbol-value variable) nil)
  512.       (let ((err-buffer (get-buffer-create send-pr-err-buffer)))
  513.     (save-excursion
  514.       (set-buffer err-buffer)
  515.       (erase-buffer)
  516.       (condition-case var
  517.           (progn
  518.         (apply 'call-process 
  519.                (append 
  520.             (list (car command) nil err-buffer nil) (cdr command)))
  521.         (goto-char (point-min))
  522.         (set variable (read err-buffer)))
  523.         (setq variable nil))
  524.       (erase-buffer)))))
  525.  
  526.  
  527. ;;;; end of send-pr.el
  528.