My upload of dpkg 1.3.0 didn't include the file below. It is an Emacs mode for editing the changelogs that dpkg-parsechangelog understands.
Try it (on your own packages or on the changelog from hello 1.3-7) and let me know what you think. When I know where to put the autoload definitions I'll release a dpkg version with this file. I don't propose to use the auto-mode-alist: the file is named simply `changelog' because it is in the `debian' subdirectory of the source tree, and binding that filename to dpkg-changelog-mode would IMO be too obnoxious. Instead people can perhaps use a local variables clause to set the mode. There are facilities in dpkg 1.3.0 for defining a parser for a different changelog format. I put this in because people seemed so hung up on the Emacs format, which I think is dreadful. However, if someone defines a standard way of representing the required information in an Emacs-style changelog and writes the parser for it I'll distribute it. Of course I'd be far happier if there were a (possibly rough) consensus that we should stick to my format so that I can write that into the policy manual, but for the moment I'll just mandate that you have to use a format that the latest dpkg supports (so that people don't need to find your home-grown parser to build your package). Ian. ;; dpkg-changelog.el --- change log maintenance for dpkg-style changelogs ;; Keywords: maint ;; Copyright (C) 1996 Ian Jackson ;; This file is part of dpkg. ;; ;; It is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; ;; dpkg is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with your Debian installation, in /usr/doc/copyright/GPL. ;; If not, write to the Free Software Foundation, 675 Mass Ave, ;; Cambridge, MA 02139, USA. (require 'add-log) (defvar dpkg-changelog-urgencies '((?l."LOW") (?m."MEDIUM") (?h."HIGH")) "alist of keystrokes vs. urgency values dpkg-changelog-urgency ^c^u.") (defvar dpkg-changelog-distributions '((?s."stable") (?u."unstable") (?c."contrib") (?n."non-free") (?e."experimental")) "alist of keystrokes vs. distribution values for dpkg-changelog-distribution ^c^d.") (defvar dpkg-changelog-mode-map nil "Keymap for dpkg changelog major mode.") (if dpkg-changelog-mode-map nil (setq dpkg-changelog-mode-map (make-sparse-keymap)) (define-key dpkg-changelog-mode-map "\C-c\C-a" 'dpkg-changelog-add-entry) (define-key dpkg-changelog-mode-map "\C-c\C-f" 'dpkg-changelog-finalise-last-version) (define-key dpkg-changelog-mode-map "\C-c\C-c" 'dpkg-changelog-finalise-and-save) (define-key dpkg-changelog-mode-map "\C-c\C-v" 'dpkg-changelog-add-version) (define-key dpkg-changelog-mode-map "\C-c\C-d" 'dpkg-changelog-distribution) (define-key dpkg-changelog-mode-map "\C-c\C-u" 'dpkg-changelog-urgency) (define-key dpkg-changelog-mode-map "\C-c\C-e" 'dpkg-changelog-unfinalise-last-version)) (defun dpkg-changelog-add-entry () "Add a new change entry to a dpkg-style changelog." (interactive) (if (eq (dpkg-changelog-finalised-p) t) (error "most recent version has been finalised - use ^c^e or ^c^v")) (goto-char (point-min)) (re-search-forward "\n --") (backward-char 5) (if (prog1 (looking-at "\n") (forward-char 1)) nil (insert "\n")) (insert " * ") (save-excursion (insert "\n"))) (defun dpkg-changelog-headervalue (arg re alist) (let (a b v k (lineend (save-excursion (end-of-line) (point)))) (save-excursion (goto-char (point-min)) (re-search-forward re lineend) (setq a (match-beginning 1) b (match-end 1)) (goto-char a) (if arg nil (message (mapconcat (function (lambda (x) (format "%c:%s" (car x) (cdr x)))) alist " ")) (while (not v) (setq k (read-char)) (setq v (assoc k alist)))) (delete-region a b) (if arg nil (insert (cdr v)))) (if arg (goto-char a)))) (defun dpkg-changelog-urgency (arg) "Without argument, prompt for a key for a new urgency value (using dpkg-changelog-urgencies). With argument, delete the current urgency and position the cursor to type a new one." (interactive "P") (dpkg-changelog-headervalue arg "\\;[^\n]* urgency=\\(\\sw+\\)" dpkg-changelog-urgencies)) (defun dpkg-changelog-distribution (arg) "Without argument, prompt for a key for a new distribution value (using dpkg-changelog-distributions). With argument, delete the current distribution and position the cursor to type a new one." (interactive "P") (dpkg-changelog-headervalue arg ") \\(.*\\)\\;" dpkg-changelog-distributions)) (defun dpkg-changelog-finalised-p () "Check whether the most recent dpkg-style changelog entry is finalised yet (ie, has a maintainer name and email address and a release date." (save-excursion (goto-char (point-min)) (if (re-search-forward "\n\\S-" (point-max) t) nil (goto-char (point-max))) (if (re-search-backward "\n --" (point-min) t) (forward-char 4) (beginning-of-line) (insert " --\n\n") (backward-char 2)) (cond ((looking-at "[ \n]+\\S-[^\n\t]+\\S- <[^ \t\n<>]+> \\S-[^\t\n]+\\S-[ \t]*\n") t) ((looking-at "[ \t]*\n") nil) ("finalisation line has bad format (not ` -- maintainer <email> date')")))) (defun dpkg-changelog-add-version () "Add a new version section to a dpkg-style changelog file." (interactive) (let ((f (dpkg-changelog-finalised-p))) (and (stringp f) (error f)) (or f (error "previous version not yet finalised"))) (goto-char (point-min)) (let ((headstring (if (looking-at "\\(\\S-+ ([^()\n\t ]*[^0-9\n\t()]\\)\\([0-9]+\\)\\()[^\n]*\\)") (concat (match-string 1) (number-to-string (+ 1 (string-to-number (match-string 2)))) (match-string 3)) (let ((pkg (read-string "Package name: ")) (ver (read-version "New version (including any revision): "))) (concat pkg " (" ver ") unstable; urgency=" (cdr (car dpkg-changelog-urgencies))))))) (insert headstring "\n\n * ") (save-excursion (if (re-search-backward "\;[^\n]* urgency=\\(\\sw+\\)" (point-min) t) (progn (goto-char (match-beginning 1)) (delete-region (point) (match-end 1)) (insert (cdr (car dpkg-changelog-urgencies)))))) (save-excursion (insert "\n\n --\n\n")))) (defun dpkg-changelog-finalise-and-save () "Finalise, if necessary, and then save a dpkg-style changelog file." (interactive) (let ((f (dpkg-changelog-finalised-p))) (and (stringp f) (error f)) (or f (dpkg-changelog-finalise-last-version))) (save-buffer)) (defun dpkg-changelog-finalise-last-version () "Remove the `finalisation' information (maintainer's name and email address and release date) so that new entries can be made." (interactive) (or add-log-full-name (setq add-log-full-name (user-full-name))) (or add-log-mailing-address (setq add-log-mailing-address user-mail-address)) (and (dpkg-changelog-finalised-p) (dpkg-changelog-unfinalise-last-version)) (save-excursion (goto-char (point-min)) (re-search-forward "\n --\\([ \t]*\\)") (delete-region (match-beginning 1) (match-end 1)) (insert " " add-log-full-name " <" add-log-mailing-address "> ") (let* ((dp "822-date") (r (call-process dp nil t))) (or (= r 0) (error (concat dp " returned error status " (prin1-to-string r))))) (backward-char) (or (looking-at "\n") (error (concat "expected newline after date from " dp))) (delete-char 1))) (defun dpkg-changelog-unfinalise-last-version () "Remove the `finalisation' information (maintainer's name and email address and release date) so that new entries can be made." (interactive) (if (dpkg-changelog-finalised-p) nil (error "most recent version is not finalised")) (save-excursion (goto-char (point-min)) (re-search-forward "\n --") (let ((dels (point))) (end-of-line) (delete-region dels (point))))) (defun dpkg-changelog-mode () "Major mode for editing dpkg-style change logs. Runs `dpkg-changelog-mode-hook'." (interactive) (kill-all-local-variables) (text-mode) (setq major-mode 'dpkg-changelog-mode mode-name "dpkg changelog" left-margin 2 fill-prefix " " fill-column 74) (use-local-map dpkg-changelog-mode-map) ;; Let each entry behave as one paragraph: (set (make-local-variable 'paragraph-start) "\\*") (set (make-local-variable 'paragraph-separate) "\\*\\|\\s-*$|\\S-") ;; Let each version behave as one page. ;; Match null string on the heading line so that the heading line ;; is grouped with what follows. (set (make-local-variable 'page-delimiter) "^\\<") (set (make-local-variable 'version-control) 'never) (run-hooks 'dpkg-changelog-mode-hook)) (provide 'dpkg-changelog)