428
+ − 1 ;;; lisp-mnt.el --- minor mode for Emacs Lisp maintainers
+ − 2
+ − 3 ;; Copyright (C) 1992, 1994 Free Software Foundation, Inc.
+ − 4
+ − 5 ;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
+ − 6 ;; Maintainer: Eric S. Raymond <esr@snark.thyrsus.com>
+ − 7 ;; Created: 14 Jul 1992
+ − 8 ;; Keywords: docs, maint
+ − 9 ;; X-Modified-by: Bob Weiner <weiner@beopen.com>, 4/14/95, to support
+ − 10 ;; InfoDock headers.
+ − 11 ;; X-Bogus-Bureaucratic-Cruft: Gruad will get you if you don't watch out!
+ − 12
+ − 13 ;; This file is part of XEmacs.
+ − 14
+ − 15 ;; XEmacs is free software; you can redistribute it and/or modify it
+ − 16 ;; under the terms of the GNU General Public License as published by
+ − 17 ;; the Free Software Foundation; either version 2, or (at your option)
+ − 18 ;; any later version.
+ − 19
+ − 20 ;; XEmacs is distributed in the hope that it will be useful, but
+ − 21 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
+ − 22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ − 23 ;; General Public License for more details.
+ − 24
+ − 25 ;; You should have received a copy of the GNU General Public License
+ − 26 ;; along with XEmacs; see the file COPYING. If not, write to the Free
+ − 27 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ − 28 ;; 02111-1307, USA.
+ − 29
+ − 30 ;;; Synched up with: FSF 20.2.
+ − 31
+ − 32 ;;; Commentary:
+ − 33
+ − 34 ;; This minor mode adds some services to Emacs-Lisp editing mode.
+ − 35 ;;
+ − 36 ;; First, it knows about the header conventions for library packages.
+ − 37 ;; One entry point supports generating synopses from a library directory.
+ − 38 ;; Another can be used to check for missing headers in library files.
+ − 39 ;;
+ − 40 ;; Another entry point automatically addresses bug mail to a package's
+ − 41 ;; maintainer or author.
+ − 42
+ − 43 ;; This file can be loaded by your lisp-mode-hook. Have it (require 'lisp-mnt)
+ − 44
+ − 45 ;; This file is an example of the header conventions. Note the following
+ − 46 ;; features:
+ − 47 ;;
+ − 48 ;; * Header line --- makes it possible to extract a one-line summary of
+ − 49 ;; the package's uses automatically for use in library synopses, KWIC
+ − 50 ;; indexes and the like.
+ − 51 ;;
+ − 52 ;; Format is three semicolons, followed by the filename, followed by
+ − 53 ;; three dashes, followed by the summary. All fields space-separated.
+ − 54 ;;
+ − 55 ;; * Author line --- contains the name and net address of at least
+ − 56 ;; the principal author.
+ − 57 ;;
+ − 58 ;; If there are multiple authors, they should be listed on continuation
+ − 59 ;; lines led by ;;<TAB><TAB> (or multiple blanks), like this:
+ − 60 ;;
+ − 61 ;; ;; Author: Ashwin Ram <Ram-Ashwin@cs.yale.edu>
+ − 62 ;; ;; Dave Sill <de5@ornl.gov>
+ − 63 ;; ;; David Lawrence <tale@pawl.rpi.edu>
+ − 64 ;; ;; Noah Friedman <friedman@ai.mit.edu>
+ − 65 ;; ;; Joe Wells <jbw@maverick.uswest.com>
+ − 66 ;; ;; Dave Brennan <brennan@hal.com>
+ − 67 ;; ;; Eric Raymond <esr@snark.thyrsus.com>
+ − 68 ;;
+ − 69 ;; This field may have some special values; notably "FSF", meaning
+ − 70 ;; "Free Software Foundation".
+ − 71 ;;
+ − 72 ;; * Maintainer line --- should be a single name/address as in the Author
+ − 73 ;; line, or an address only, or the string "FSF". If there is no maintainer
+ − 74 ;; line, the person(s) in the Author field are presumed to be it. The example
+ − 75 ;; in this file is mildly bogus because the maintainer line is redundant.
+ − 76 ;; The idea behind these two fields is to be able to write a Lisp function
+ − 77 ;; that does "send mail to the author" without having to mine the name out by
+ − 78 ;; hand. Please be careful about surrounding the network address with <> if
+ − 79 ;; there's also a name in the field.
+ − 80 ;;
+ − 81 ;; * Created line --- optional, gives the original creation date of the
+ − 82 ;; file. For historical interest, basically.
+ − 83 ;;
+ − 84 ;; * Version line --- intended to give the reader a clue if they're looking
+ − 85 ;; at a different version of the file than the one they're accustomed to. This
+ − 86 ;; may be an RCS or SCCS header.
+ − 87 ;;
+ − 88 ;; * Adapted-By line --- this is for FSF's internal use. The person named
+ − 89 ;; in this field was the one responsible for installing and adapting the
+ − 90 ;; package for the distribution. (This file doesn't have one because the
+ − 91 ;; author *is* one of the maintainers.)
+ − 92 ;;
+ − 93 ;; * Keywords line --- used by the finder code (now under construction)
+ − 94 ;; for finding Emacs Lisp code related to a topic.
+ − 95 ;;
+ − 96 ;; * X-Bogus-Bureaucratic-Cruft line --- this is a joke and an example
+ − 97 ;; of a comment header. Headers starting with `X-' should never be used
+ − 98 ;; for any real purpose; this is the way to safely add random headers
+ − 99 ;; without invoking the wrath of any program.
+ − 100 ;;
+ − 101 ;; * Commentary line --- enables Lisp code to find the developer's and
+ − 102 ;; maintainers' explanations of the package internals.
+ − 103 ;;
+ − 104 ;; * Change log line --- optional, exists to terminate the commentary
+ − 105 ;; section and start a change-log part, if one exists.
+ − 106 ;;
+ − 107 ;; * Code line --- exists so Lisp can know where commentary and/or
+ − 108 ;; change-log sections end.
+ − 109 ;;
+ − 110 ;; * Footer line --- marks end-of-file so it can be distinguished from
+ − 111 ;; an expanded formfeed or the results of truncation.
+ − 112
+ − 113 ;;; Change Log:
+ − 114
+ − 115 ;; Tue Jul 14 23:44:17 1992 ESR
+ − 116 ;; * Created.
+ − 117
+ − 118 ;;; Code:
+ − 119
+ − 120 (require 'picture) ; provides move-to-column-force
+ − 121 ;(require 'emacsbug) ; XEmacs, not needed for bytecompilation
+ − 122
+ − 123 ;;; Variables:
+ − 124
+ − 125 (defvar lm-header-prefix "^;;*[ \t]+\\(@\(#\)\\)?[ \t]*\\([\$]\\)?"
+ − 126 "Prefix that is ignored before the tag.
+ − 127 For example, you can write the 1st line synopsis string and headers like this
+ − 128 in your Lisp package:
+ − 129
+ − 130 ;; @(#) package.el -- package description
+ − 131 ;;
+ − 132 ;; @(#) $Maintainer: Person Foo Bar $
+ − 133
+ − 134 The @(#) construct is used by unix what(1) and
+ − 135 then $identifier: doc string $ is used by GNU ident(1)")
+ − 136
+ − 137 (defvar lm-comment-column 16
+ − 138 "Column used for placing formatted output.")
+ − 139
+ − 140 (defvar lm-commentary-header "Commentary\\|Documentation"
+ − 141 "Regexp which matches start of documentation section.")
+ − 142
+ − 143 (defvar lm-history-header "Change Log\\|History"
+ − 144 "Regexp which matches the start of code log section.")
+ − 145
+ − 146 ;;; Functions:
+ − 147
+ − 148 ;; These functions all parse the headers of the current buffer
+ − 149
+ − 150 (defsubst lm-get-header-re (header &optional mode)
+ − 151 "Returns regexp for matching HEADER.
+ − 152 If called with optional MODE and with value `section',
+ − 153 return section regexp instead."
+ − 154 (cond ((eq mode 'section)
+ − 155 (concat "^;;;;* " header ":[ \t]*$"))
+ − 156 (t
+ − 157 (concat lm-header-prefix header ":[ \t]*"))))
+ − 158
+ − 159 (defsubst lm-get-package-name ()
+ − 160 "Returns package name by looking at the first line."
+ − 161 (save-excursion
+ − 162 (goto-char (point-min))
+ − 163 (if (and (looking-at (concat lm-header-prefix))
+ − 164 (progn (goto-char (match-end 0))
+ − 165 (looking-at "\\([^\t ]+\\)")
+ − 166 (match-end 1)))
+ − 167 (buffer-substring (match-beginning 1) (match-end 1))
+ − 168 )))
+ − 169
+ − 170 (defun lm-section-mark (header &optional after)
+ − 171 "Return the buffer location of a given section start marker.
+ − 172 The HEADER is the section mark string to search for.
+ − 173 If AFTER is non-nil, return the location of the next line."
+ − 174 (save-excursion
+ − 175 (let ((case-fold-search t))
+ − 176 (goto-char (point-min))
+ − 177 (if (re-search-forward (lm-get-header-re header 'section) nil t)
+ − 178 (progn
+ − 179 (beginning-of-line)
+ − 180 (if after (forward-line 1))
+ − 181 (point))
+ − 182 nil))))
+ − 183
+ − 184 (defsubst lm-code-mark ()
+ − 185 "Return the buffer location of the `Code' start marker."
+ − 186 (lm-section-mark "Code"))
+ − 187
+ − 188 (defsubst lm-commentary-mark ()
+ − 189 "Return the buffer location of the `Commentary' start marker."
+ − 190 (lm-section-mark lm-commentary-header))
+ − 191
+ − 192 (defsubst lm-history-mark ()
+ − 193 "Return the buffer location of the `History' start marker."
+ − 194 (lm-section-mark lm-history-header))
+ − 195
+ − 196 (defun lm-header (header)
+ − 197 "Return the contents of the header named HEADER."
+ − 198 (goto-char (point-min))
+ − 199 (let ((case-fold-search t))
+ − 200 (if (and (re-search-forward (lm-get-header-re header) (lm-code-mark) t)
+ − 201 ;; RCS ident likes format "$identifier: data$"
+ − 202 (looking-at "\\([^$\n]+\\)")
+ − 203 (match-end 1))
+ − 204 (buffer-substring (match-beginning 1) (match-end 1))
+ − 205 nil)))
+ − 206
+ − 207 (defun lm-header-multiline (header)
+ − 208 "Return the contents of the header named HEADER, with continuation lines.
+ − 209 The returned value is a list of strings, one per line."
+ − 210 (save-excursion
+ − 211 (goto-char (point-min))
+ − 212 (let ((res (lm-header header)))
+ − 213 (cond
+ − 214 (res
+ − 215 (setq res (list res))
+ − 216 (forward-line 1)
+ − 217
+ − 218 (while (and (looking-at (concat lm-header-prefix "[\t ]+"))
+ − 219 (progn
+ − 220 (goto-char (match-end 0))
+ − 221 (looking-at "\\(.*\\)"))
+ − 222 (match-end 1))
+ − 223 (setq res (cons (buffer-substring
+ − 224 (match-beginning 1)
+ − 225 (match-end 1))
+ − 226 res))
+ − 227 (forward-line 1))
+ − 228 ))
+ − 229 res
+ − 230 )))
+ − 231
+ − 232 ;; These give us smart access to the header fields and commentary
+ − 233
+ − 234 (defun lm-summary (&optional file)
+ − 235 "Return the one-line summary of file FILE, or current buffer if FILE is nil."
+ − 236 (save-excursion
+ − 237 (if file
+ − 238 (find-file file))
+ − 239 (goto-char (point-min))
+ − 240 (prog1
+ − 241 (if (and
+ − 242 (looking-at lm-header-prefix)
+ − 243 (progn (goto-char (match-end 0))
+ − 244 (looking-at "[^ ]+[ \t]+--+[ \t]+\\(.*\\)")))
+ − 245 (buffer-substring (match-beginning 1) (match-end 1)))
+ − 246 (if file
+ − 247 (kill-buffer (current-buffer)))
+ − 248 )))
+ − 249
+ − 250 (defun lm-crack-address (x)
+ − 251 "Split up an email address into full name and real email address.
+ − 252 The value is a cons of the form (FULLNAME . ADDRESS)."
+ − 253 (cond ((string-match "\\(.+\\) [(<]\\(\\S-+@\\S-+\\)[>)]" x)
+ − 254 (cons (substring x (match-beginning 1) (match-end 1))
+ − 255 (substring x (match-beginning 2) (match-end 2))))
+ − 256 ((string-match "\\(\\S-+@\\S-+\\) [(<]\\(.*\\)[>)]" x)
+ − 257 (cons (substring x (match-beginning 2) (match-end 2))
+ − 258 (substring x (match-beginning 1) (match-end 1))))
+ − 259 ((string-match "\\S-+@\\S-+" x)
+ − 260 (cons nil x))
+ − 261 (t
+ − 262 (cons x nil))))
+ − 263
+ − 264 (defun lm-authors (&optional file)
+ − 265 "Return the author list of file FILE, or current buffer if FILE is nil.
+ − 266 Each element of the list is a cons; the car is the full name,
+ − 267 the cdr is an email address."
+ − 268 (save-excursion
+ − 269 (if file
+ − 270 (find-file file))
+ − 271 ;; XEmacs change (Is E-MAIL an infodock header? -sb)
+ − 272 (let* ((authorlist (lm-header-multiline "author"))
+ − 273 (email-list (lm-header-multiline "E-MAIL"))
+ − 274 (authors authorlist))
+ − 275 (prog1
+ − 276 (if (null email-list)
+ − 277 (mapcar 'lm-crack-address authorlist)
+ − 278 (while (and email-list authors)
+ − 279 (setcar authors (cons (car authors) (car email-list)))
+ − 280 (setq email-list (cdr email-list)
+ − 281 authors (cdr authors)))
+ − 282 authorlist)
+ − 283 (if file
+ − 284 (kill-buffer (current-buffer))))
+ − 285 )))
+ − 286
+ − 287 (defun lm-maintainer (&optional file)
+ − 288 "Return the maintainer of file FILE, or current buffer if FILE is nil.
+ − 289 The return value has the form (NAME . ADDRESS)."
+ − 290 (save-excursion
+ − 291 (if file
+ − 292 (find-file file))
+ − 293 (prog1
+ − 294 (let ((maint (lm-header "maintainer")))
+ − 295 (if maint
+ − 296 (lm-crack-address maint)
+ − 297 (car (lm-authors))))
+ − 298 (if file
+ − 299 (kill-buffer (current-buffer))))))
+ − 300
+ − 301 (defun lm-creation-date (&optional file)
+ − 302 "Return the created date given in file FILE, or current buffer if FILE is nil."
+ − 303 (save-excursion
+ − 304 (if file
+ − 305 (find-file file))
+ − 306 (prog1
+ − 307 ;; XEmacs change (Is ORIG-DATE an Infodock header? -sb)
+ − 308 (or (lm-header "created")
+ − 309 (let ((date-and-time (lm-header "ORIG-DATE")))
+ − 310 (if date-and-time
+ − 311 (substring date-and-time 0
+ − 312 (string-match " " date-and-time)))))
+ − 313 (if file
+ − 314 (kill-buffer (current-buffer)))
+ − 315 )))
+ − 316
+ − 317 (defun lm-last-modified-date (&optional file)
+ − 318 "Return the modify-date given in file FILE, or current buffer if FILE is nil."
+ − 319 (save-excursion
+ − 320 (if file
+ − 321 (find-file file))
+ − 322 (prog1
+ − 323 (if (progn
+ − 324 (goto-char (point-min))
+ − 325 (re-search-forward
+ − 326 "\\$[I]d: [^ ]+ [^ ]+ \\([^/]+\\)/\\([^/]+\\)/\\([^ ]+\\) "
+ − 327 (lm-code-mark) t))
+ − 328 (format "%s %s %s"
+ − 329 (buffer-substring (match-beginning 3) (match-end 3))
+ − 330 (nth (string-to-int
+ − 331 (buffer-substring (match-beginning 2) (match-end 2)))
+ − 332 '("" "Jan" "Feb" "Mar" "Apr" "May" "Jun"
+ − 333 "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
+ − 334 (buffer-substring (match-beginning 1) (match-end 1)))
+ − 335 ;; XEmacs change (Infodock change? -sb)
+ − 336 (let ((date-and-time (lm-header "LAST-MOD")))
+ − 337 (if date-and-time
+ − 338 (substring date-and-time 0
+ − 339 (string-match " " date-and-time)))))
+ − 340 (if file
+ − 341 (kill-buffer (current-buffer)))
+ − 342 )))
+ − 343
+ − 344 (defun lm-version (&optional file)
+ − 345 "Return the version listed in file FILE, or current buffer if FILE is nil.
448
+ − 346 This can be found in an RCS or SCCS header to crack it out of."
428
+ − 347 (save-excursion
+ − 348 (if file
+ − 349 (find-file file))
+ − 350 (prog1
+ − 351 (or
+ − 352 (lm-header "version")
+ − 353 (let ((header-max (lm-code-mark)))
+ − 354 (goto-char (point-min))
+ − 355 (cond
+ − 356 ;; Look for an RCS header
+ − 357 ((re-search-forward "\\$[I]d: [^ ]+ \\([^ ]+\\) " header-max t)
+ − 358 (buffer-substring (match-beginning 1) (match-end 1)))
+ − 359
+ − 360 ;; Look for an SCCS header
+ − 361 ((re-search-forward
+ − 362 (concat
+ − 363 (regexp-quote "@(#)")
+ − 364 (regexp-quote (file-name-nondirectory (buffer-file-name)))
+ − 365 "\t\\([012345679.]*\\)")
+ − 366 header-max t)
+ − 367 (buffer-substring (match-beginning 1) (match-end 1)))
+ − 368
+ − 369 (t nil))))
+ − 370 (if file
+ − 371 (kill-buffer (current-buffer)))
+ − 372 )))
+ − 373
+ − 374 (defun lm-keywords (&optional file)
+ − 375 "Return the keywords given in file FILE, or current buffer if FILE is nil."
+ − 376 (save-excursion
+ − 377 (if file
+ − 378 (find-file file))
+ − 379 (prog1
+ − 380 (let ((keywords (lm-header "keywords")))
+ − 381 (and keywords (downcase keywords)))
+ − 382 (if file
+ − 383 (kill-buffer (current-buffer)))
+ − 384 )))
+ − 385
+ − 386 (defun lm-adapted-by (&optional file)
+ − 387 "Return the adapted-by names in file FILE, or current buffer if FILE is nil.
+ − 388 This is the name of the person who cleaned up this package for
+ − 389 distribution."
+ − 390 (save-excursion
+ − 391 (if file
+ − 392 (find-file file))
+ − 393 (prog1
+ − 394 (lm-header "adapted-by")
+ − 395 (if file
+ − 396 (kill-buffer (current-buffer)))
+ − 397 )))
+ − 398
+ − 399 (defun lm-commentary (&optional file)
+ − 400 "Return the commentary in file FILE, or current buffer if FILE is nil.
+ − 401 The value is returned as a string. In the text, the commentary starts
+ − 402 with tag `Commentary' and ends with tag `Change Log' or `History'."
+ − 403 (save-excursion
+ − 404 (if file
+ − 405 (find-file file))
+ − 406 (prog1
+ − 407 (let ((commentary (lm-commentary-mark))
+ − 408 (change-log (lm-history-mark))
+ − 409 (code (lm-code-mark))
+ − 410 end)
+ − 411 (cond
+ − 412 ((and commentary change-log)
+ − 413 (buffer-substring commentary change-log))
+ − 414 ((and commentary code)
+ − 415 (buffer-substring commentary code))
+ − 416 (t
+ − 417 ;; XEmacs change (Infodock headers? -sb)
+ − 418 (setq commentary (lm-section-mark "DESCRIPTION" t))
+ − 419 (setq end (lm-section-mark "DESCRIP-END"))
+ − 420 (and commentary end (buffer-substring commentary end)))))
+ − 421 (if file
+ − 422 (kill-buffer (current-buffer)))
+ − 423 )))
+ − 424
+ − 425 ;;; Verification and synopses
+ − 426
+ − 427 (defun lm-insert-at-column (col &rest strings)
+ − 428 "Insert list of STRINGS, at column COL."
+ − 429 (if (> (current-column) col) (insert "\n"))
+ − 430 (move-to-column-force col)
+ − 431 (apply 'insert strings))
+ − 432
+ − 433 (defun lm-verify (&optional file showok &optional verb)
+ − 434 "Check that the current buffer (or FILE if given) is in proper format.
+ − 435 If FILE is a directory, recurse on its files and generate a report in
+ − 436 a temporary buffer."
+ − 437 (interactive)
+ − 438 (let* ((verb (or verb (interactive-p)))
+ − 439 ret
+ − 440 name
+ − 441 )
+ − 442 (if verb
+ − 443 (setq ret "Ok.")) ;init value
+ − 444
+ − 445 (if (and file (file-directory-p file))
+ − 446 (setq
+ − 447 ret
+ − 448 (progn
+ − 449 (switch-to-buffer (get-buffer-create "*lm-verify*"))
+ − 450 (erase-buffer)
+ − 451 (mapcar
+ − 452 #'(lambda (f)
+ − 453 (if (string-match ".*\\.el$" f)
+ − 454 (let ((status (lm-verify f)))
+ − 455 (if status
+ − 456 (progn
+ − 457 (insert f ":")
+ − 458 (lm-insert-at-column lm-comment-column status "\n"))
+ − 459 (and showok
+ − 460 (progn
+ − 461 (insert f ":")
+ − 462 (lm-insert-at-column lm-comment-column "OK\n")))))))
+ − 463 (directory-files file))
+ − 464 ))
+ − 465 (save-excursion
+ − 466 (if file
+ − 467 (find-file file))
+ − 468 (setq name (lm-get-package-name))
+ − 469
+ − 470 (setq
+ − 471 ret
+ − 472 (prog1
+ − 473 (cond
+ − 474 ((null name)
+ − 475 "Can't find a package NAME")
+ − 476
+ − 477 ((not (lm-authors))
+ − 478 "Author: tag missing.")
+ − 479
+ − 480 ((not (lm-maintainer))
+ − 481 "Maintainer: tag missing.")
+ − 482
+ − 483 ((not (lm-summary))
+ − 484 "Can't find a one-line 'Summary' description")
+ − 485
+ − 486 ((not (lm-keywords))
+ − 487 "Keywords: tag missing.")
+ − 488
+ − 489 ((not (lm-commentary-mark))
+ − 490 "Can't find a 'Commentary' section marker.")
+ − 491
+ − 492 ((not (lm-history-mark))
+ − 493 "Can't find a 'History' section marker.")
+ − 494
+ − 495 ((not (lm-code-mark))
+ − 496 "Can't find a 'Code' section marker")
+ − 497
+ − 498 ((progn
+ − 499 (goto-char (point-max))
+ − 500 (not
+ − 501 (re-search-backward
+ − 502 (concat "^;;;[ \t]+" name "[ \t]+ends here[ \t]*$"
+ − 503 "\\|^;;;[ \t]+ End of file[ \t]+" name)
+ − 504 nil t
+ − 505 )))
+ − 506 (format "Can't find a footer line for [%s]" name))
+ − 507 (t
+ − 508 ret))
+ − 509 (if file
+ − 510 (kill-buffer (current-buffer)))
+ − 511 ))))
+ − 512 (if verb
+ − 513 (message ret))
+ − 514 ret
+ − 515 ))
+ − 516
+ − 517 (defun lm-synopsis (&optional file showall)
+ − 518 "Generate a synopsis listing for the buffer or the given FILE if given.
+ − 519 If FILE is a directory, recurse on its files and generate a report in
+ − 520 a temporary buffer. If SHOWALL is non-nil, also generate a line for files
+ − 521 which do not include a recognizable synopsis."
+ − 522 (interactive
+ − 523 (list
+ − 524 (read-file-name "Synopsis for (file or dir): ")))
+ − 525
+ − 526 (if (and file (file-directory-p file))
+ − 527 (progn
+ − 528 (switch-to-buffer (get-buffer-create "*lm-verify*"))
+ − 529 (erase-buffer)
+ − 530 (mapcar
+ − 531 (lambda (f) ; XEmacs - dequote
+ − 532 (if (string-match ".*\\.el$" f)
+ − 533 (let ((syn (lm-synopsis f)))
+ − 534 (if syn
+ − 535 (progn
+ − 536 (insert f ":")
+ − 537 (lm-insert-at-column lm-comment-column syn "\n"))
+ − 538 (and showall
+ − 539 (progn
+ − 540 (insert f ":")
+ − 541 (lm-insert-at-column lm-comment-column "NA\n")))))))
+ − 542 (directory-files file))
+ − 543 )
+ − 544 (save-excursion
+ − 545 (if file
+ − 546 (find-file file))
+ − 547 (prog1
+ − 548 (lm-summary)
+ − 549 (if file
+ − 550 (kill-buffer (current-buffer)))
+ − 551 ))))
+ − 552
+ − 553 (defun lm-report-bug (topic)
+ − 554 "Report a bug in the package currently being visited to its maintainer.
+ − 555 Prompts for bug subject. Leaves you in a mail buffer."
+ − 556 (interactive "sBug Subject: ")
+ − 557 (let ((package (lm-get-package-name))
+ − 558 (addr (lm-maintainer))
+ − 559 (version (lm-version)))
+ − 560 (mail nil
+ − 561 (if addr
+ − 562 (concat (car addr) " <" (cdr addr) ">")
644
+ − 563 (or (and (boundp 'report-xemacs-bug-beta-address)
+ − 564 (declare-boundp report-xemacs-bug-beta-address))
428
+ − 565 "<xemacs-beta@xemacs.org>"))
+ − 566 topic)
+ − 567 (goto-char (point-max))
+ − 568 (insert "\nIn "
+ − 569 package
+ − 570 (if version (concat " version " version) "")
+ − 571 "\n\n")
+ − 572 (message
+ − 573 (substitute-command-keys "Type \\[mail-send] to send bug report."))))
+ − 574
+ − 575 (provide 'lisp-mnt)
+ − 576
+ − 577 ;;; lisp-mnt.el ends here