Yet more Texinfo 5 tweaks for admin.el's make-manuals
[bpt/emacs.git] / admin / admin.el
CommitLineData
74499542
GM
1;;; admin.el --- utilities for Emacs administration
2
ba318903 3;; Copyright (C) 2001-2014 Free Software Foundation, Inc.
74499542
GM
4
5;; This file is part of GNU Emacs.
6
9ad5de0c 7;; GNU Emacs is free software: you can redistribute it and/or modify
74499542 8;; it under the terms of the GNU General Public License as published by
9ad5de0c
GM
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
74499542
GM
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
9ad5de0c 18;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
74499542
GM
19
20;;; Commentary:
21
54381691
LK
22;; add-release-logs Add ``Version X released'' change log entries.
23;; set-version Change Emacs version number in source tree.
dedfb7f8 24;; set-copyright Change Emacs short copyright string (eg as
a3045b7e 25;; printed by --version) in source tree.
74499542
GM
26
27;;; Code:
28
dcddaabb
GM
29(defvar add-log-time-format) ; in add-log
30
78cd48e7
GM
31;; Does this information need to be in every ChangeLog, as opposed to
32;; just the top-level one? Only if you allow changes the same
33;; day as the release.
34;; http://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00161.html
8c39e821 35(defun add-release-logs (root version &optional date)
74499542 36 "Add \"Version VERSION released.\" change log entries in ROOT.
8c39e821
GM
37Root must be the root of an Emacs source tree.
38Optional argument DATE is the release date, default today."
39 (interactive (list (read-directory-name "Emacs root directory: ")
40 (read-string "Version number: "
41 (format "%s.%s" emacs-major-version
42 emacs-minor-version))
43 (read-string "Release date: "
44 (progn (require 'add-log)
1a7bceef
GM
45 (let ((add-log-time-zone-rule t))
46 (funcall add-log-time-format))))))
3f4a4bdf 47 (setq root (expand-file-name root))
74499542 48 (unless (file-exists-p (expand-file-name "src/emacs.c" root))
dedfb7f8 49 (user-error "%s doesn't seem to be the root of an Emacs source tree" root))
54381691 50 (require 'add-log)
1a7bceef
GM
51 (or date (setq date (let ((add-log-time-zone-rule t))
52 (funcall add-log-time-format))))
74499542
GM
53 (let* ((logs (process-lines "find" root "-name" "ChangeLog"))
54 (entry (format "%s %s <%s>\n\n\t* Version %s released.\n\n"
8c39e821 55 date
3f4a4bdf
FP
56 (or add-log-full-name (user-full-name))
57 (or add-log-mailing-address user-mail-address)
58 version)))
74499542 59 (dolist (log logs)
8c39e821
GM
60 (find-file log)
61 (goto-char (point-min))
62 (insert entry))))
74499542 63
74499542 64(defun set-version-in-file (root file version rx)
0b52b61d 65 "Subroutine of `set-version' and `set-copyright'."
74499542
GM
66 (find-file (expand-file-name file root))
67 (goto-char (point-min))
dedfb7f8
XF
68 (unless (re-search-forward rx nil :noerror)
69 (user-error "Version not found in %s" file))
74499542
GM
70 (replace-match (format "%s" version) nil nil nil 1))
71
3f9e3ef3 72;; TODO report the progress
74499542
GM
73(defun set-version (root version)
74 "Set Emacs version to VERSION in relevant files under ROOT.
75Root must be the root of an Emacs source tree."
91ebb8c9 76 (interactive "DEmacs root directory: \nsVersion number: ")
74499542 77 (unless (file-exists-p (expand-file-name "src/emacs.c" root))
dedfb7f8 78 (user-error "%s doesn't seem to be the root of an Emacs source tree" root))
3a79600a
XF
79 ;; There's also a "version 3" (standing for GPLv3) at the end of
80 ;; `README', but since `set-version-in-file' only replaces the first
81 ;; occurence, it won't be replaced.
74499542
GM
82 (set-version-in-file root "README" version
83 (rx (and "version" (1+ space)
84 (submatch (1+ (in "0-9."))))))
c4444d16 85 (set-version-in-file root "configure.ac" version
24bbe01e
GM
86 (rx (and "AC_INIT" (1+ (not (in ?,)))
87 ?, (0+ space)
88 (submatch (1+ (in "0-9."))))))
f4f358f1
GM
89 (set-version-in-file root "doc/emacs/emacsver.texi" version
90 (rx (and "EMACSVER" (1+ space)
91 (submatch (1+ (in "0-9."))))))
adf94aa6
GM
92 (set-version-in-file root "doc/man/emacs.1" version
93 (rx (and ".TH EMACS" (1+ not-newline)
94 "GNU Emacs" (1+ space)
95 (submatch (1+ (in "0-9."))))))
70b0d280
EZ
96 (set-version-in-file root "nt/config.nt" version
97 (rx (and bol "#" (0+ blank) "define" (1+ blank)
a0c64452 98 "VERSION" (1+ blank) "\""
1fe1ef05 99 (submatch (1+ (in "0-9."))))))
9d9d12cd
EZ
100 (set-version-in-file root "msdos/sed2v2.inp" version
101 (rx (and bol "/^#undef " (1+ not-newline)
a0c64452 102 "define VERSION" (1+ space) "\""
9d9d12cd 103 (submatch (1+ (in "0-9."))))))
e3aef5c6
CS
104 (set-version-in-file root "nt/makefile.w32-in" version
105 (rx (and "VERSION" (0+ space) "=" (0+ space)
106 (submatch (1+ (in "0-9."))))))
95f76284
JR
107 ;; nt/emacs.rc also contains the version number, but in an awkward
108 ;; format. It must contain four components, separated by commas, and
109 ;; in two places those commas are followed by space, in two other
110 ;; places they are not.
111 (let* ((version-components (append (split-string version "\\.")
fe6462ee 112 '("0" "0")))
95f76284
JR
113 (comma-version
114 (concat (car version-components) ","
115 (cadr version-components) ","
d0834a5c 116 (cadr (cdr version-components)) ","
95f76284
JR
117 (cadr (cdr (cdr version-components)))))
118 (comma-space-version
119 (concat (car version-components) ", "
120 (cadr version-components) ", "
d0834a5c 121 (cadr (cdr version-components)) ", "
95f76284
JR
122 (cadr (cdr (cdr version-components))))))
123 (set-version-in-file root "nt/emacs.rc" comma-version
124 (rx (and "FILEVERSION" (1+ space)
125 (submatch (1+ (in "0-9,"))))))
126 (set-version-in-file root "nt/emacs.rc" comma-version
127 (rx (and "PRODUCTVERSION" (1+ space)
128 (submatch (1+ (in "0-9,"))))))
129 (set-version-in-file root "nt/emacs.rc" comma-space-version
130 (rx (and "\"FileVersion\"" (0+ space) ?, (0+ space)
131 ?\" (submatch (1+ (in "0-9, "))) "\\0\"")))
132 (set-version-in-file root "nt/emacs.rc" comma-space-version
133 (rx (and "\"ProductVersion\"" (0+ space) ?,
134 (0+ space) ?\" (submatch (1+ (in "0-9, ")))
b7063e7e
JR
135 "\\0\"")))
136 ;; Likewise for emacsclient.rc
137 (set-version-in-file root "nt/emacsclient.rc" comma-version
138 (rx (and "FILEVERSION" (1+ space)
139 (submatch (1+ (in "0-9,"))))))
140 (set-version-in-file root "nt/emacsclient.rc" comma-version
141 (rx (and "PRODUCTVERSION" (1+ space)
142 (submatch (1+ (in "0-9,"))))))
143 (set-version-in-file root "nt/emacsclient.rc" comma-space-version
144 (rx (and "\"FileVersion\"" (0+ space) ?, (0+ space)
145 ?\" (submatch (1+ (in "0-9, "))) "\\0\"")))
146 (set-version-in-file root "nt/emacsclient.rc" comma-space-version
95f76284
JR
147 (rx (and "\"ProductVersion\"" (0+ space) ?,
148 (0+ space) ?\" (submatch (1+ (in "0-9, ")))
dcf8834b 149 "\\0\"")))
eec5fae2 150 ;; Major version only.
dcf8834b
GM
151 (when (string-match "\\([0-9]\\{2,\\}\\)" version)
152 (setq version (match-string 1 version))
eec5fae2
GM
153 (set-version-in-file root "src/msdos.c" version
154 (rx (and "Vwindow_system_version" (1+ not-newline)
155 ?\( (submatch (1+ (in "0-9"))) ?\))))
dcf8834b
GM
156 (set-version-in-file root "etc/refcards/ru-refcard.tex" version
157 "\\\\newcommand{\\\\versionemacs}\\[0\\]\
158{\\([0-9]\\{2,\\}\\)}.+%.+version of Emacs")
159 (set-version-in-file root "etc/refcards/emacsver.tex" version
160 "\\\\def\\\\versionemacs\
161{\\([0-9]\\{2,\\}\\)}.+%.+version of Emacs"))))
162
3f4a4bdf 163
a3045b7e 164;; Note this makes some assumptions about form of short copyright.
3f9e3ef3 165;; TODO report the progress
a3045b7e
GM
166(defun set-copyright (root copyright)
167 "Set Emacs short copyright to COPYRIGHT in relevant files under ROOT.
168Root must be the root of an Emacs source tree."
169 (interactive (list
170 (read-directory-name "Emacs root directory: " nil nil t)
171 (read-string
172 "Short copyright string: "
173 (format "Copyright (C) %s Free Software Foundation, Inc."
174 (format-time-string "%Y")))))
175 (unless (file-exists-p (expand-file-name "src/emacs.c" root))
dedfb7f8 176 (user-error "%s doesn't seem to be the root of an Emacs source tree" root))
78f83752
GM
177 (set-version-in-file root "configure.ac" copyright
178 (rx (and bol "copyright" (0+ (not (in ?\")))
287d4c2c 179 ?\" (submatch (1+ (not (in ?\")))) ?\")))
5ccd466c
GM
180 (set-version-in-file root "msdos/sed2v2.inp" copyright
181 (rx (and bol "/^#undef " (1+ not-newline)
182 "define COPYRIGHT" (1+ space)
183 ?\" (submatch (1+ (not (in ?\")))) ?\")))
78f83752
GM
184 (set-version-in-file root "nt/config.nt" copyright
185 (rx (and bol "#" (0+ blank) "define" (1+ blank)
186 "COPYRIGHT" (1+ blank)
187 ?\" (submatch (1+ (not (in ?\")))) ?\")))
a3045b7e 188 (set-version-in-file root "lib-src/rcs2log" copyright
287d4c2c
GM
189 (rx (and "Copyright" (0+ space) ?= (0+ space)
190 ?\' (submatch (1+ nonl)))))
287d4c2c
GM
191 (when (string-match "\\([0-9]\\{4\\}\\)" copyright)
192 (setq copyright (match-string 1 copyright))
dcf8834b
GM
193 (set-version-in-file root "etc/refcards/ru-refcard.tex" copyright
194 "\\\\newcommand{\\\\cyear}\\[0\\]\
195{\\([0-9]\\{4\\}\\)}.+%.+copyright year")
196 (set-version-in-file root "etc/refcards/emacsver.tex" copyright
197 "\\\\def\\\\year\
198{\\([0-9]\\{4\\}\\)}.+%.+copyright year")))
a3045b7e 199
8d9101d8
CY
200;;; Various bits of magic for generating the web manuals
201
0773c610 202(defun manual-misc-manuals (root)
fe6462ee
XF
203 "Return doc/misc manuals as list of strings.
204ROOT should be the root of an Emacs source tree."
1857cd3f
GM
205 ;; Similar to `make -C doc/misc echo-info', but works if unconfigured,
206 ;; and for INFO_TARGETS rather than INFO_INSTALL.
0773c610
GM
207 (with-temp-buffer
208 (insert-file-contents (expand-file-name "doc/misc/Makefile.in" root))
1857cd3f
GM
209 ;; Should really use expanded value of INFO_TARGETS.
210 (search-forward "INFO_COMMON = ")
211 (let ((start (point)))
0773c610
GM
212 (end-of-line)
213 (while (and (looking-back "\\\\")
214 (zerop (forward-line 1)))
215 (end-of-line))
1857cd3f
GM
216 (append (split-string (replace-regexp-in-string
217 "\\(\\\\\\|\\.info\\)" ""
218 (buffer-substring start (point))))
219 '("efaq-w32")))))
0773c610 220
5cd63720 221;; TODO report the progress
c7197e52
GM
222(defun make-manuals (root &optional type)
223 "Generate the web manuals for the Emacs webpage.
fe6462ee 224ROOT should be the root of an Emacs source tree.
c7197e52
GM
225Interactively with a prefix argument, prompt for TYPE.
226Optional argument TYPE is type of output (nil means all)."
227 (interactive (let ((root (read-directory-name "Emacs root directory: "
228 source-directory nil t)))
229 (list root
230 (if current-prefix-arg
231 (completing-read
232 "Type: "
233 (append
234 '("misc" "pdf" "ps")
235 (let (res)
236 (dolist (i '("emacs" "elisp" "eintr") res)
237 (dolist (j '("" "-mono" "-node" "-ps" "-pdf"))
238 (push (concat i j) res))))
239 (manual-misc-manuals root)))))))
8d9101d8
CY
240 (let* ((dest (expand-file-name "manual" root))
241 (html-node-dir (expand-file-name "html_node" dest))
242 (html-mono-dir (expand-file-name "html_mono" dest))
f114e6c3 243 (ps-dir (expand-file-name "ps" dest))
c7197e52
GM
244 (pdf-dir (expand-file-name "pdf" dest))
245 (emacs (expand-file-name "doc/emacs/emacs.texi" root))
246 (elisp (expand-file-name "doc/lispref/elisp.texi" root))
247 (eintr (expand-file-name "doc/lispintro/emacs-lisp-intro.texi" root))
248 (misc (manual-misc-manuals root)))
249 ;; TODO this makes it non-continuable.
250 ;; Instead, delete the individual dest directory each time.
8d9101d8 251 (when (file-directory-p dest)
c7197e52 252 (if (y-or-n-p (format "Directory %s exists, delete it first? " dest))
8d9101d8 253 (delete-directory dest t)
c7197e52
GM
254 (user-error "Aborted")))
255 (if (member type '(nil "emacs" "emacs-node"))
256 (manual-html-node emacs (expand-file-name "emacs" html-node-dir)))
257 (if (member type '(nil "emacs" "emacs-mono"))
258 (manual-html-mono emacs (expand-file-name "emacs.html" html-mono-dir)))
259 (if (member type '(nil "emacs" "emacs-pdf" "pdf"))
260 (manual-pdf emacs (expand-file-name "emacs.pdf" pdf-dir)))
261 (if (member type '(nil "emacs" "emacs-ps" "ps"))
262 (manual-ps emacs (expand-file-name "emacs.ps" ps-dir)))
263 (if (member type '(nil "elisp" "elisp-node"))
264 (manual-html-node elisp (expand-file-name "elisp" html-node-dir)))
265 (if (member type '(nil "elisp" "elisp-mono"))
266 (manual-html-mono elisp (expand-file-name "elisp.html" html-mono-dir)))
267 (if (member type '(nil "elisp" "elisp-pdf" "pdf"))
268 (manual-pdf elisp (expand-file-name "elisp.pdf" pdf-dir)))
269 (if (member type '(nil "elisp" "elisp-ps" "ps"))
270 (manual-ps elisp (expand-file-name "elisp.ps" ps-dir)))
271 (if (member type '(nil "eintr" "eintr-node"))
272 (manual-html-node eintr (expand-file-name "eintr" html-node-dir)))
273 (if (member type '(nil "eintr" "eintr-node"))
274 (manual-html-mono eintr (expand-file-name "eintr.html" html-mono-dir)))
275 (if (member type '(nil "eintr" "eintr-pdf" "pdf"))
276 (manual-pdf eintr (expand-file-name "eintr.pdf" pdf-dir)))
277 (if (member type '(nil "eintr" "eintr-ps" "ps"))
278 (manual-ps eintr (expand-file-name "eintr.ps" ps-dir)))
f08b09fc 279 ;; Misc manuals
c7197e52
GM
280 (dolist (manual misc)
281 (if (member type `(nil ,manual "misc"))
282 (manual-misc-html manual root html-node-dir html-mono-dir)))
8d9101d8
CY
283 (message "Manuals created in %s" dest)))
284
285(defconst manual-doctype-string
286 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"
287\"http://www.w3.org/TR/html4/loose.dtd\">\n\n")
288
289(defconst manual-meta-string
290 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">
291<link rev=\"made\" href=\"mailto:webmasters@gnu.org\">
292<link rel=\"icon\" type=\"image/png\" href=\"/graphics/gnu-head-mini.png\">
293<meta name=\"ICBM\" content=\"42.256233,-71.006581\">
294<meta name=\"DC.title\" content=\"gnu.org\">\n\n")
295
296(defconst manual-style-string "<style type=\"text/css\">
48dde4df 297@import url('/software/emacs/manual.css');\n</style>\n")
8d9101d8 298
f08b09fc 299(defun manual-misc-html (name root html-node-dir html-mono-dir)
c7197e52 300 ;; Hack to deal with the cases where .texi creates a different .info.
278208b8 301 ;; Blech. TODO Why not just rename the .texi (or .info) files?
c7197e52 302 (let* ((texiname (cond ((equal name "ccmode") "cc-mode")
c7197e52
GM
303 (t name)))
304 (texi (expand-file-name (format "doc/misc/%s.texi" texiname) root)))
f08b09fc
CY
305 (manual-html-node texi (expand-file-name name html-node-dir))
306 (manual-html-mono texi (expand-file-name (concat name ".html")
307 html-mono-dir))))
308
8d9101d8
CY
309(defun manual-html-mono (texi-file dest)
310 "Run Makeinfo on TEXI-FILE, emitting mono HTML output to DEST.
311This function also edits the HTML files so that they validate as
312HTML 4.01 Transitional, and pulls in the gnu.org stylesheet using
313the @import directive."
c7197e52 314 (make-directory (or (file-name-directory dest) ".") t)
8d9101d8 315 (call-process "makeinfo" nil nil nil
c0765905 316 "-D" "WWW_GNU_ORG"
c69f4673
GM
317 "-I" (expand-file-name "../emacs"
318 (file-name-directory texi-file))
319 "-I" (expand-file-name "../misc"
320 (file-name-directory texi-file))
8d9101d8
CY
321 "--html" "--no-split" texi-file "-o" dest)
322 (with-temp-buffer
323 (insert-file-contents dest)
324 (setq buffer-file-name dest)
325 (manual-html-fix-headers)
326 (manual-html-fix-index-1)
327 (manual-html-fix-index-2 t)
328 (manual-html-fix-node-div)
329 (goto-char (point-max))
330 (re-search-backward "</body>[\n \t]*</html>")
517f20c5 331 ;; Close the div id="content" that fix-index-1 added.
8d9101d8
CY
332 (insert "</div>\n\n")
333 (save-buffer)))
334
335(defun manual-html-node (texi-file dir)
336 "Run Makeinfo on TEXI-FILE, emitting per-node HTML output to DIR.
337This function also edits the HTML files so that they validate as
338HTML 4.01 Transitional, and pulls in the gnu.org stylesheet using
339the @import directive."
340 (unless (file-exists-p texi-file)
fe6462ee 341 (user-error "Manual file %s not found" texi-file))
c7197e52 342 (make-directory dir t)
8d9101d8 343 (call-process "makeinfo" nil nil nil
c0765905 344 "-D" "WWW_GNU_ORG"
c69f4673
GM
345 "-I" (expand-file-name "../emacs"
346 (file-name-directory texi-file))
347 "-I" (expand-file-name "../misc"
348 (file-name-directory texi-file))
8d9101d8
CY
349 "--html" texi-file "-o" dir)
350 ;; Loop through the node files, fixing them up.
351 (dolist (f (directory-files dir nil "\\.html\\'"))
352 (let (opoint)
353 (with-temp-buffer
354 (insert-file-contents (expand-file-name f dir))
355 (setq buffer-file-name (expand-file-name f dir))
356 (if (looking-at "<meta http-equiv")
357 ;; Ignore those HTML files that are just redirects.
358 (set-buffer-modified-p nil)
359 (manual-html-fix-headers)
360 (if (equal f "index.html")
361 (let (copyright-text)
362 (manual-html-fix-index-1)
363 ;; Move copyright notice to the end.
f08b09fc
CY
364 (when (re-search-forward "[ \t]*<p>Copyright &copy;" nil t)
365 (setq opoint (match-beginning 0))
366 (re-search-forward "</blockquote>")
367 (setq copyright-text (buffer-substring opoint (point)))
368 (delete-region opoint (point)))
8d9101d8 369 (manual-html-fix-index-2)
f08b09fc
CY
370 (if copyright-text
371 (insert copyright-text))
517f20c5 372 ;; Close the div id="content" that fix-index-1 added.
f08b09fc 373 (insert "\n</div>\n"))
8d9101d8 374 ;; For normal nodes, give the header div a blue bg.
d16ec91e 375 (manual-html-fix-node-div t))
8d9101d8
CY
376 (save-buffer))))))
377
8d9101d8 378(defun manual-pdf (texi-file dest)
fe6462ee 379 "Run texi2pdf on TEXI-FILE, emitting PDF output to DEST."
c7197e52 380 (make-directory (or (file-name-directory dest) ".") t)
b9a54f5e
GM
381 (let ((default-directory (file-name-directory texi-file)))
382 (call-process "texi2pdf" nil nil nil
383 "-I" "../emacs" "-I" "../misc"
384 texi-file "-o" dest)))
8d9101d8 385
0cb70db7
GM
386(defun manual-ps (texi-file dest)
387 "Generate a PostScript version of TEXI-FILE as DEST."
c7197e52 388 (make-directory (or (file-name-directory dest) ".") t)
b9a54f5e
GM
389 (let ((dvi-dest (concat (file-name-sans-extension dest) ".dvi"))
390 (default-directory (file-name-directory texi-file)))
fe6462ee 391 ;; FIXME: Use `texi2dvi --ps'? --xfq
0cb70db7 392 (call-process "texi2dvi" nil nil nil
b9a54f5e 393 "-I" "../emacs" "-I" "../misc"
0cb70db7
GM
394 texi-file "-o" dvi-dest)
395 (call-process "dvips" nil nil nil dvi-dest "-o" dest)
396 (delete-file dvi-dest)
397 (call-process "gzip" nil nil nil dest)))
8d9101d8
CY
398
399(defun manual-html-fix-headers ()
400 "Fix up HTML headers for the Emacs manual in the current buffer."
9d1804dc
GM
401 (let ((texi5 (search-forward "<!DOCTYPE" nil t))
402 opoint)
403 ;; Texinfo 5 supplies a DOCTYPE.
404 (or texi5
405 (insert manual-doctype-string))
8d9101d8
CY
406 (search-forward "<head>\n")
407 (insert manual-meta-string)
408 (search-forward "<meta")
409 (setq opoint (match-beginning 0))
9d1804dc
GM
410 (unless texi5
411 (search-forward "<!--")
517f20c5
GM
412 (goto-char (match-beginning 0))
413 (delete-region opoint (point))
414 (search-forward "<meta http-equiv=\"Content-Style")
9d1804dc 415 (setq opoint (match-beginning 0)))
8d9101d8 416 (search-forward "</head>")
9d1804dc
GM
417 (goto-char (match-beginning 0))
418 (delete-region opoint (point))
517f20c5
GM
419 (insert manual-style-string)
420 ;; Remove Texinfo 5 hard-coding bgcolor, text, link, vlink, alink.
421 (when (re-search-forward "<body lang=\"[^\"]+\"" nil t)
422 (setq opoint (point))
423 (search-forward ">")
424 (if (> (point) (1+ opoint))
425 (delete-region opoint (1- (point))))
426 (search-backward "</head"))))
8d9101d8 427
517f20c5 428;; Texinfo 5 changed these from class = "node" to "header", yay.
d16ec91e 429(defun manual-html-fix-node-div (&optional split)
8d9101d8 430 "Fix up HTML \"node\" divs in the current buffer."
d16ec91e 431 (let (opoint div-end type)
517f20c5 432 (while (re-search-forward "<div class=\"\\(node\\|header\\)\"\\(>\\)" nil t)
d16ec91e
GM
433 (setq type (match-string 1))
434 ;; NB it is this that makes the bg of non-header cells in the
435 ;; index tables be blue. Is that intended?
436 ;; Also, if you don't remove the <hr>, the color of the first
437 ;; row in the table will be wrong.
438 ;; This all seems rather odd to me...
517f20c5 439 (replace-match " style=\"background-color:#DDDDFF\">" t t nil 2)
8d9101d8 440 (setq opoint (point))
d16ec91e
GM
441 (when (or split (equal type "node"))
442 ;; In Texinfo 4, the <hr> (and anchor) comes after the <div>.
443 (re-search-forward "</div>")
444 (setq div-end (if (equal type "node")
445 (match-beginning 0)
446 (line-end-position 2)))
447 (goto-char opoint)
448 (if (search-forward "<hr>" div-end 'move)
449 (replace-match "" t t)
450 (if split (forward-line -1))))
451 ;; In Texinfo 5, the <hr> (and anchor) comes before the <div> (?).
452 ;; Except in split output, where it comes on the line after
453 ;; the <div>. But only sometimes. I have no clue what the
454 ;; logic of where it goes is.
455 (when (equal type "header")
456 (goto-char opoint)
457 (when (re-search-backward "^<hr>$" (line-beginning-position -3) t)
458 (replace-match "")
459 (goto-char opoint))))))
460
8d9101d8
CY
461
462(defun manual-html-fix-index-1 ()
517f20c5
GM
463 "Remove the h1 header, and the short and long contents lists.
464Also start a \"content\" div."
8d9101d8 465 (let (opoint)
9d1804dc 466 (re-search-forward "<body.*>\n")
f08b09fc 467 (setq opoint (match-end 0))
517f20c5
GM
468 ;; FIXME? Fragile if a Texinfo 5 document does not use @top.
469 (or (re-search-forward "<h1 class=\"top\"" nil t) ; Texinfo 5
470 (search-forward "<h2 class=\""))
8d9101d8
CY
471 (goto-char (match-beginning 0))
472 (delete-region opoint (point))
517f20c5 473 ;; NB caller must close this div.
8d9101d8
CY
474 (insert "<div id=\"content\" class=\"inner\">\n\n")))
475
476(defun manual-html-fix-index-2 (&optional table-workaround)
d16ec91e
GM
477 "Replace the index list in the current buffer with a HTML table.
478Leave point after the table."
517f20c5 479 (if (re-search-forward "<table class=\"menu\"\\(.*\\)>" nil t)
d16ec91e 480 ;; Texinfo 5 already uses a table. Tweak it a bit.
517f20c5
GM
481 (let (opoint done)
482 (replace-match " style=\"float:left\" width=\"100%\"" nil t nil 1)
d16ec91e
GM
483 (forward-line 1)
484 (while (not done)
485 (cond ((re-search-forward "<tr><td.*&bull; \\(<a.*</a>\\)\
486:</td><td>&nbsp;&nbsp;</td><td.*>\\(.*\\)" (line-end-position) t)
487 (replace-match (format "<tr><td%s>\\1</td>\n<td>\\2"
488 (if table-workaround
489 " bgcolor=\"white\"" "")))
490 (search-forward "</td></tr>")
491 (forward-line 1))
492 ((looking-at "<tr><th.*<pre class=\"menu-comment\">\n")
493 (replace-match "<tr><th colspan=\"2\" align=\"left\" \
494style=\"text-align:left\">")
495 (search-forward "</pre></th></tr>")
496 (replace-match "</th></tr>\n"))
497 ;; Not all manuals have the detailed menu.
498 ;; If it is there, split it into a separate table.
499 ((re-search-forward "<tr>.*The Detailed Node Listing *"
500 (line-end-position) t)
501 (setq opoint (match-beginning 0))
502 (while (and (looking-at " *&mdash;")
503 (zerop (forward-line 1))))
504 (delete-region opoint (point))
505 (insert "</table>\n\n\
506<h2>Detailed Node Listing</h2>\n\n<p>")
507 ;; FIXME Fragile!
508 ;; The Emacs and Elisp manual have some text at the
509 ;; start of the detailed menu that is not part of the menu.
510 ;; Other manuals do not.
511 ;; FIXME Texinfo 4 branch does not handle this correctly.
512 ;; See eg s/emacs/manual/html_node/eintr/index.html
513 ;; start of "Detailed Node Listing".
514 (if (re-search-forward "in one step:" (line-end-position 3) t)
515 (forward-line 1))
516 (insert "</p>\n")
517 (search-forward "</pre></th></tr>")
518 (delete-region (match-beginning 0) (match-end 0))
519 (forward-line -1)
520 (or (looking-at "^$") (error "Parse error 1"))
521 (forward-line -1)
522 (if (looking-at "^$") (error "Parse error 2"))
523 (forward-line -1)
524 (or (looking-at "^$") (error "Parse error 3"))
525 (forward-line 1)
526 (insert "<table class=\"menu\" style=\"float:left\" width=\"100%\">\n\
527<tr><th colspan=\"2\" align=\"left\" style=\"text-align:left\">\n")
528 (forward-line 1)
529 (insert "</th></tr>")
530 (forward-line 1))
531 ((looking-at ".*</table")
532 (forward-line 1)
533 (setq done t)))))
517f20c5
GM
534 (let (done open-td tag desc)
535 ;; Convert the list that Makeinfo made into a table.
536 (or (search-forward "<ul class=\"menu\">" nil t)
537 ;; FIXME? The following search seems dangerously lax.
538 (search-forward "<ul>"))
539 (replace-match "<table style=\"float:left\" width=\"100%\">")
540 (forward-line 1)
541 (while (not done)
542 (cond
543 ((or (looking-at "<li>\\(<a.+</a>\\):[ \t]+\\(.*\\)$")
544 (looking-at "<li>\\(<a.+</a>\\)$"))
545 (setq tag (match-string 1))
546 (setq desc (match-string 2))
547 (replace-match "" t t)
548 (when open-td
549 (save-excursion
550 (forward-char -1)
551 (skip-chars-backward " ")
552 (delete-region (point) (line-end-position))
553 (insert "</td>\n </tr>")))
554 (insert " <tr>\n ")
555 (if table-workaround
556 ;; This works around a Firefox bug in the mono file.
517f20c5
GM
557 (insert "<td bgcolor=\"white\">")
558 (insert "<td>"))
559 (insert tag "</td>\n <td>" (or desc ""))
560 (setq open-td t))
561 ((eq (char-after) ?\n)
562 (delete-char 1)
563 ;; Negate the following `forward-line'.
564 (forward-line -1))
565 ((looking-at "<!-- ")
566 (search-forward "-->"))
567 ((looking-at "<p>[- ]*The Detailed Node Listing[- \n]*")
568 (replace-match " </td></tr></table>\n
8d9101d8 569<h3>Detailed Node Listing</h3>\n\n" t t)
517f20c5
GM
570 (search-forward "<p>")
571 (search-forward "<p>" nil t)
572 (goto-char (match-beginning 0))
573 (skip-chars-backward "\n ")
574 (setq open-td nil)
575 (insert "</p>\n\n<table style=\"float:left\" width=\"100%\">"))
576 ((looking-at "</li></ul>")
577 (replace-match "" t t))
578 ((looking-at "<p>")
579 (replace-match "" t t)
580 (when open-td
581 (insert " </td></tr>")
582 (setq open-td nil))
583 (insert " <tr>
8d9101d8 584 <th colspan=\"2\" align=\"left\" style=\"text-align:left\">")
517f20c5
GM
585 (if (re-search-forward "</p>[ \t\n]*<ul class=\"menu\">" nil t)
586 (replace-match " </th></tr>")))
587 ((looking-at "[ \t]*</ul>[ \t]*$")
588 (replace-match
589 (if open-td
590 " </td></tr>\n</table>"
591 "</table>") t t)
592 (setq done t))
593 (t
594 (if (eobp)
595 (error "Parse error in %s"
596 (file-name-nondirectory buffer-file-name)))
597 (unless open-td
598 (setq done t))))
599 (forward-line 1)))))
8d9101d8 600
58474503 601\f
fe6462ee 602;; Stuff to check new `defcustom's got :version tags.
58474503
GM
603;; Adapted from check-declare.el.
604
605(defun cusver-find-files (root &optional old)
fe6462ee
XF
606 "Find .el files beneath directory ROOT that contain `defcustom's.
607If optional OLD is non-nil, also include `defvar's."
58474503
GM
608 (process-lines find-program root
609 "-name" "*.el"
610 "-exec" grep-program
611 "-l" "-E" (format "^[ \\t]*\\(def%s"
612 (if old "(custom|var)"
613 "custom"
614 ))
615 "{}" "+"))
616
5407f8d2
GM
617(defvar cusver-new-version (format "%s.%s" emacs-major-version
618 (1+ emacs-minor-version))
fe6462ee 619 "Version number that new `defcustom's should have.")
1a316a53 620
58474503
GM
621(defun cusver-scan (file &optional old)
622 "Scan FILE for `defcustom' calls.
623Return a list with elements of the form (VAR . VER),
624This means that FILE contains a defcustom for variable VAR, with
625a :version tag having value VER (may be nil).
fe6462ee 626If optional argument OLD is non-nil, also scan for `defvar's."
58474503
GM
627 (let ((m (format "Scanning %s..." file))
628 (re (format "^[ \t]*\\((def%s\\)[ \t\n]"
1a316a53
GM
629 (if old "\\(custom\\|var\\)" "\\(custom\\|group\\)")))
630 alist var ver form glist grp)
58474503
GM
631 (message "%s" m)
632 (with-temp-buffer
633 (insert-file-contents file)
634 ;; FIXME we could theoretically be inside a string.
fe6462ee 635 (while (re-search-forward re nil :noerror)
58474503
GM
636 (goto-char (match-beginning 1))
637 (if (and (setq form (ignore-errors (read (current-buffer))))
1a316a53 638 (setq var (car-safe (cdr-safe form)))
58474503
GM
639 ;; Exclude macros, eg (defcustom ,varname ...).
640 (symbolp var))
1a316a53 641 (progn
c62a1961
GM
642 ;; FIXME It should be cus-test-apropos that does this.
643 (and (not old)
644 (equal "custom" (match-string 2))
645 (not (memq :type form))
646 (display-warning 'custom
647 (format "Missing type in: `%s'" form)))
1a316a53
GM
648 (setq ver (car (cdr-safe (memq :version form))))
649 (if (equal "group" (match-string 2))
650 ;; Group :version could be old.
651 (if (equal ver cusver-new-version)
652 (setq glist (cons (cons var ver) glist)))
653 ;; If it specifies a group and the whole group has a
654 ;; version. use that.
655 (unless ver
656 (setq grp (car (cdr-safe (memq :group form))))
657 (and grp
658 (setq grp (car (cdr-safe grp))) ; (quote foo) -> foo
659 (setq ver (assq grp glist))))
660 (setq alist (cons (cons var ver) alist))))
58474503
GM
661 (if form (message "Malformed defcustom: `%s'" form)))))
662 (message "%sdone" m)
663 alist))
664
2c35bdbb
GM
665(defun cusver-scan-cus-start (file)
666 "Scan cus-start.el and return an alist with elements (VAR . VER)."
667 (if (file-readable-p file)
668 (with-temp-buffer
669 (insert-file-contents file)
670 (when (search-forward "(let ((all '(" nil t)
671 (backward-char 1)
672 (let (var ver alist)
673 (dolist (elem (ignore-errors (read (current-buffer))))
674 (when (symbolp (setq var (car-safe elem)))
675 (or (stringp (setq ver (nth 3 elem)))
676 (setq ver nil))
677 (setq alist (cons (cons var ver) alist))))
678 alist)))))
679
58474503
GM
680(define-button-type 'cusver-xref 'action #'cusver-goto-xref)
681
682(defun cusver-goto-xref (button)
fe6462ee 683 "Jump to a Lisp file for the BUTTON at point."
58474503
GM
684 (let ((file (button-get button 'file))
685 (var (button-get button 'var)))
686 (if (not (file-readable-p file))
687 (message "Cannot read `%s'" file)
688 (with-current-buffer (find-file-noselect file)
689 (goto-char (point-min))
690 (or (re-search-forward (format "^[ \t]*(defcustom[ \t]*%s" var) nil t)
691 (message "Unable to locate defcustom"))
692 (pop-to-buffer (current-buffer))))))
693
694;; You should probably at least do a grep over the old directory
2c35bdbb
GM
695;; to check the results of this look sensible.
696;; TODO Check cus-start if something moved from C to Lisp.
697;; TODO Handle renamed things with aliases to the old names.
1a316a53 698(defun cusver-check (newdir olddir version)
fe6462ee
XF
699 "Check that `defcustom's have :version tags where needed.
700NEWDIR is the current lisp/ directory, OLDDIR is that from the
701previous release, VERSION is the new version number. A
702`defcustom' that is only in NEWDIR should have a :version tag.
703We exclude cases where a `defvar' exists in OLDDIR, since just
704converting a `defvar' to a `defcustom' does not require
705a :version bump.
58474503
GM
706
707Note that a :version tag should also be added if the value of a defcustom
708changes (in a non-trivial way). This function does not check for that."
c62a1961
GM
709 (interactive (list (read-directory-name "New Lisp directory: " nil nil t)
710 (read-directory-name "Old Lisp directory: " nil nil t)
5407f8d2
GM
711 (number-to-string
712 (read-number "New version number: "
713 (string-to-number cusver-new-version)))))
58474503 714 (or (file-directory-p (setq newdir (expand-file-name newdir)))
fe6462ee 715 (user-error "Directory `%s' not found" newdir))
58474503 716 (or (file-directory-p (setq olddir (expand-file-name olddir)))
fe6462ee 717 (user-error "Directory `%s' not found" olddir))
1a316a53 718 (setq cusver-new-version version)
fe6462ee 719 (let* ((newfiles (progn (message "Finding new files with `defcustom's...")
58474503 720 (cusver-find-files newdir)))
fe6462ee 721 (oldfiles (progn (message "Finding old files with `defcustom's...")
58474503 722 (cusver-find-files olddir t)))
fe6462ee 723 (newcus (progn (message "Reading new `defcustom's...")
58474503
GM
724 (mapcar
725 (lambda (file)
726 (cons file (cusver-scan file))) newfiles)))
dcf8834b 727 oldcus result thisfile file)
fe6462ee 728 (message "Reading old `defcustom's...")
58474503
GM
729 (dolist (file oldfiles)
730 (setq oldcus (append oldcus (cusver-scan file t))))
2c35bdbb
GM
731 (setq oldcus (append oldcus (cusver-scan-cus-start
732 (expand-file-name "cus-start.el" olddir))))
58474503
GM
733 ;; newcus has elements (FILE (VAR VER) ... ).
734 ;; oldcus just (VAR . VER).
735 (message "Checking for version tags...")
736 (dolist (new newcus)
737 (setq file (car new)
738 thisfile
739 (let (missing var)
740 (dolist (cons (cdr new))
741 (or (cdr cons)
742 (assq (setq var (car cons)) oldcus)
743 (push var missing)))
744 (if missing
745 (cons file missing))))
746 (if thisfile
747 (setq result (cons thisfile result))))
748 (message "Checking for version tags... done")
749 (if (not result)
750 (message "No missing :version tags")
751 (pop-to-buffer "*cusver*")
752 (erase-buffer)
fe6462ee 753 (insert "These `defcustom's might be missing :version tags:\n\n")
58474503
GM
754 (dolist (elem result)
755 (let* ((str (file-relative-name (car elem) newdir))
756 (strlen (length str)))
757 (dolist (var (cdr elem))
758 (insert (format "%s: %s\n" str var))
759 (make-text-button (+ (line-beginning-position 0) strlen 2)
760 (line-end-position 0)
761 'file (car elem)
762 'var var
763 'help-echo "Mouse-2: visit this definition"
764 :type 'cusver-xref)))))))
765
69c52df1
GM
766(provide 'admin)
767
d3841127 768;;; admin.el ends here