1;; gsdoc.el 2;; 3;; GNU emacs (19.34) functions to help working with the HTML form of 4;; Ghostscript documentation. 5;; 6;; Pete Kaiser 8 September 1998 V1.2 7;; 2 December 1999 V1.3 Correct improper "--" to "==" 8;; in HTML marker comments 9;;============================================================ 10;; One global key setting, which runs the function to bind some keys 11;; locally -- presumably in a buffer containing HTML code. Plus that 12;; function itself. 13 14(global-set-key [?\C-\S-k] 'gskeys) 15 16(defun gskeys () 17 18"Set the keys in this buffer to use with Ghostscript HTML docs." 19 20(interactive) 21(local-set-key [?\C-\S-b] 'gs-bold) 22(local-set-key [?\C-\S-c] 'gs-code) 23(local-set-key [?\C-\S-e] 'gs-emphatic) 24(local-set-key [?\C-\S-g] 'gs-get-anchor) 25(local-set-key [?\C-\S-h] 'gs-href) 26(local-set-key [?\C-\S-i] 'gs-italic) 27(local-set-key [?\C-\S-m] 'gs-mailto) 28(local-set-key [?\C-\S-n] 'gs-name) 29(local-set-key [?\C-\S-p] 'gs-put-anchor) 30(local-set-key [?\C-\S-q] 'gs-quote) 31(local-set-key [?\C-\S-r] 'gs-row-of-table) 32(local-set-key [?\C-\S-s] 'gs-selfref) 33(local-set-key [?\C-\S-t] 'gs-table) 34(local-set-key [?\C-\S-u] 'gs-tag) 35(local-set-key [?\C-\S-x] 'gs-example) 36) 37 38;;============================================================ 39;; Each of these next few functions just wraps a region in a 40;; <TAG>...</TAG>, or two nested tags. Where there are two, the first one 41;; is inner. See the inner function ~gsregion. 42 43(defun gs-bold () "Make text strong (bold)." 44(interactive) 45(~gsregion "b")) 46 47(defun gs-code () "Make text strong code (bold TT)." 48(interactive) 49(~gsregion "tt") 50(~gsregion "b")) 51 52(defun gs-emphatic () "Make text emphatic (bold italic)." 53(interactive) 54(~gsregion "em") 55(~gsregion "b")) 56 57(defun gs-italic () "Make text italic." 58(interactive) 59(~gsregion "em")) 60 61;;============================================================ 62 63(defun gs-quote () 64 65"Indent a region with BLOCKQUOTE and separate it with empty lines from 66surrounding text." 67 68(interactive) 69 70(save-restriction (narrow-to-region (region-beginning) (region-end)) 71 (goto-char (point-min)) (insert "\n\n") 72 (push-mark (1+ (point-min)) t) 73 (goto-char (point-max)) 74 (~gsregion "blockquote") 75 (insert "\n\n") 76 ) 77) 78 79;;============================================================ 80 81(defun gs-example () 82 83"Make an indented literatim example BLOCKQUOTE PRE and separate it with 84empty lines from surrounding text." 85 86(interactive) 87 88(save-restriction (narrow-to-region (region-beginning) (region-end)) 89 (goto-char (point-min)) (insert "\n") 90 (push-mark (point-min) t) 91 (goto-char (point-max)) 92 (~gsregion "pre") 93 (~gsregion "blockquote") 94 (insert "\n") 95 ) 96) 97 98;;============================================================ 99 100(defun gs-get-anchor () 101 102"Beginning at the head of this line, pick up the next anchor name for later 103use along with its HTML file name. This is useful when picking up an 104anchor name from a file in one buffer and using it in another buffer 105containing a different file." 106 107(interactive) 108 109;; From the beginning of this line find and pick up the next non-empty 110;; anchor, which might, of course not be right here -- though that's how it 111;; ought to be used, to pick up an anchor for immediate use. The regular 112;; expression picks up only the name itself. 113 114(beginning-of-line) 115(re-search-forward "<a name=\"?\\([^\">]+\\)\"?></a>" nil t) 116(setq gs-anchor (buffer-substring (match-beginning 1) (match-end 1))) 117 118;; Get the name of this buffer, treating it as the filename. 119 120(setq gs-anchor-file (buffer-name)) 121) 122 123;;============================================================ 124 125(defun gs-href () 126 127"Wrap a region in an empty link and leave point in the middle of the 128emptiness to write the link. Maybe some day read the URL and put it 129there." 130 131(interactive) 132 133(save-restriction 134 (narrow-to-region (region-beginning) (region-end)) 135 (goto-char (point-min)) (insert "<a href=\"#\">") 136 (setq HREF (- (point) 2)) 137 (goto-char (point-max)) (insert "</a>") 138 (goto-char HREF) 139 ) 140) 141 142;;============================================================ 143 144(defun gs-mailto () 145 146"Turn an address into a proper \"mailto:\" visually bracketed with <>." 147 148(interactive) 149 150(save-restriction 151 (narrow-to-region (region-beginning) (region-end)) 152 (setq gs-address (buffer-substring (point-min) (point-max))) 153 (goto-char (point-min)) (insert "<<a href=\"mailto:") 154 (goto-char (point-max)) (insert "\">" gs-address "</a>>") 155 ) 156) 157 158;;============================================================ 159 160(defun gs-tag (Tag) 161 162"Bracket a region with some arbitrary tag read from the minibuffer, leaving 163point right after the opening word of the opening tag, and the end of the 164region at the end of the closing tag. Leaving point there makes it 165possible, for instance, to enter additional matter in a <FONT> tag. Get to 166the end of a region with ^x-^x." 167 168(interactive "*sTag: ") 169 170 (~gsregion Tag) 171 (exchange-point-and-mark) (forward-word 1) 172) 173 174;;============================================================ 175 176(defun gs-toc () 177 178"[Re]build the table of contents by picking up all the <Hn> lines and 179converting them to properly indented <UL> entries, placing the TOC within 180the standard TOC markers. Note that several of the original Ghostscript 181HTML files have hand-modified TOCs, so it's wise to check before running 182this function. It can be run from anywhere within the HTML file. 183 184This function relies on the specific format of the structure comments for 185the table of contents, which are set by the g~marker function used in 186defvars run when this package is loaded." 187 188(interactive) 189 190(setq g~html-buffer (buffer-name)) 191 192(save-restriction (save-excursion 193 (widen) 194 195;; Since we're building the TOC, delete any current TOC. Locate the place 196;; for the TOC using the standard markers, deleting everything between the 197;; TOC-beginning and TOC-end markers. The new TOC is built entirely in the 198;; work buffer before being copied into the HTML buffer at that point. 199 200 (goto-char (point-min)) 201 (search-forward g~toc-begin nil t) 202 (next-line 1) (beginning-of-line) (setq g~toc-insert (point)) 203 (search-forward g~toc-end nil t) 204 (beginning-of-line) (delete-region g~toc-insert (point)) 205 206;; Empty the work buffer by copying nothing into it. 207 208 (copy-to-buffer gs-work-buffer 1 1) 209 210;; Now collect all the following header lines into a buffer to work on 211;; them. The later append-to-buffer needs point to be in the middle of the 212;; empty list, so go there before entering the work buffer. 213 214 (save-excursion (while (re-search-forward "^<h[1-6][^>]*>" nil t) 215 (beginning-of-line) (setq BOH (point)) 216 (re-search-forward "</h[1-6]>\n" nil t) 217 (append-to-buffer gs-work-buffer BOH (point)) 218 )) 219 (goto-char g~toc-insert) 220 221;; All the useful header lines should be in the work buffer now. 222 223 (save-excursion 224 (set-buffer gs-work-buffer) 225 226;; Formatting as list entries: insert <ul> when the level deepens and </ul> 227;; when it rises. 228 229 (goto-char (point-min)) 230 (while (search-forward "</a>" nil t) (replace-match "")) 231 (goto-char (point-min)) 232 (while (re-search-forward "</h[1-6]>" nil t) (replace-match "</a>")) 233 (goto-char (point-min)) 234 (while (re-search-forward "<a name=\"" nil t) (replace-match "<a href=\"#")) 235 236;; Change <h[1-6]> to <li>, inserting <ul>...</ul> as needed. Pick up the 237;; upmost level from the first header, usually <h1>, and save a copy to 238;; use to insert any terminating </ul>. 239 240 (goto-char (point-min)) 241 (re-search-forward "^<h\\([1-6]\\)[^>]*>" nil t) 242 (setq First (string-to-number 243 (buffer-substring (match-beginning 1) (match-end 1)))) 244 (setq Previous First) 245 (replace-match "<li>" t t) 246 247;; Got the first one, now handle the rest. 248 249 (while (re-search-forward "^<h\\([1-6]\\)[^>]*>" nil t) 250 (setq This (string-to-number 251 (buffer-substring (match-beginning 1) (match-end 1)))) 252 (setq Hold This) 253 (replace-match "<li>" t t) (beginning-of-line) 254 255;; No point being too fancy with conditionals: the "while" statements here 256;; make at most one logically unnecessary test. 257 258 (while (> This Previous) (insert "<ul>\n") (setq This (1- This))) 259 (while (< This Previous) (insert "</ul>\n") (setq This (1+ This))) 260 (setq Previous Hold) 261 ) 262 263;; Done with the loop. Clean up by inserting at the end any </ul> needed 264;; to get back to the top level. 265 266 (goto-char (point-max)) 267 (while (> Previous First) (insert "</ul>\n") (setq Previous (1- Previous))) 268 269;; Finally add the trailing whitespace and leading whitespace and header line. 270 271 (insert "</ul></blockquote>\n\n") 272 (goto-char (point-min)) 273 (insert "\n<h2>Table of contents</h2>\n\n<blockquote><ul>\n") 274 275;; The TOC is now entirely built in the work buffer. Move it to where it's 276;; supposed to be in the original buffer. 277 278 (append-to-buffer g~html-buffer (point-min) (point-max)) 279 ) 280 )) 281) 282 283;;============================================================ 284 285(defun gs-name () 286 287"Insert a name anchor at point and leave point ready to enter the anchor's 288name. Anchors are always empty (that is, <a name=...></a>)." 289 290(interactive) 291 292(insert "<a name=\"\"></a>") 293(backward-char 6) 294) 295 296;;============================================================ 297 298(defun gs-put-anchor () 299 300"Insert around the current region the last anchor picked up with 301gs-get-anchor. This includes the HTML file name if the href is put in a 302file other than the anchor." 303 304(interactive) 305 306(save-restriction 307 (narrow-to-region (region-beginning) (region-end)) 308 (goto-char (point-min)) 309 (insert (concat 310 "<a href=\"" 311 312;; Insert the filename (buffer name) picked up by gs-get-anchor only if 313;; this is a different buffer. 314 315 (if (string-equal gs-anchor-file (buffer-name)) "" gs-anchor-file) 316 317;; And finish unconditionally with the specific anchor name. 318 319 "#" gs-anchor "\">")) 320 (goto-char (point-max)) (insert "</a>")) 321) 322 323;;============================================================ 324 325(defun gs-row-of-table () 326 327"Set up a row of a table from the line containing point. 328 329Insert the right things at beginning and end, and in between convert tab 330and \"|\" into column breaks with a nonbreaking space in between -- which 331means that no entry can contain a tab or \"|\". Format the HTML nicely 332for readability. 333 334Between each two substantive columns this function puts a column containing 335a single nonbreaking space to provide a visual break. Generally in the 336first row of a table those columns should be given enough NBSPs to make 337the table look right on screen and when converted to text, but this has to 338be done by hand." 339 340(interactive) 341 342(save-restriction 343 (end-of-line) (setq EOL (point)) 344 (beginning-of-line) (narrow-to-region (point) EOL) 345 (insert "<tr valign=top>\t<td>") 346 (while (re-search-forward "[|\t]" nil t) 347 (replace-match "\n\t<td> \n\t<td>" t t)) 348 (goto-char (point-max)) 349 ) 350(next-line 1) (beginning-of-line) 351) 352 353;;============================================================ 354 355(defun gs-selfref () 356 357"Wrap an URL to make it its own link. This is useful for links that should 358be visible when converted to text." 359 360(interactive) 361 362(save-restriction 363 (narrow-to-region (region-beginning) (region-end)) 364 (goto-char (point-min)) 365 (if (not (looking-at "http:\\|ftp:")) (insert "http://")) 366 (setq g~url (buffer-substring (point-min) (point-max))) 367 (goto-char (point-min)) 368 (insert "<a href=\"") 369 (goto-char (point-max)) (insert "\">" g~url "</a>") 370 ) 371) 372 373;;============================================================ 374 375(defun gs-table () 376 377"Set up an indented table around this region, leaving plenty of white space 378around the table within the HTML. The number of columns in the table is 379hardcoded here as 3, so that number must be changed by hand if the table 380has more than 3 columns. See gs-row-of-table for how rows are built: a 381table with N visible columns generally has 2N-1 HTML columns, including the 382columns that provide vertical white space." 383 384(interactive) 385 386(save-restriction 387 (narrow-to-region (region-beginning) (region-end)) 388 (indent-rigidly (region-beginning) (region-end) -80) 389 (goto-char (point-min)) 390 (insert (concat 391 "\n\n<blockquote><table cellpadding=0 cellspacing=0>\n" 392 "<tr><th colspan=3 bgcolor=\"#CCCC00\"><hr><font size=\"+1\">XXXXXXXXXX</font><hr>\n" 393 "<tr valign=bottom>\n" 394 "\t<th align=left>\n" 395 "\t<td> \n" 396 "\t<th align=left>\n" 397 "<tr>\t<td colspan=3><hr>\n" 398 )) 399 (goto-char (point-max)) 400 (insert "</table></blockquote>\n") 401 ) 402) 403 404;;============================================================ 405 406(defun gs-text-chars () 407 408"Prepare text for inclusion in HTML by converting \"&\", \"<\", and \">\" into 409their HTML special forms. The function acts from point to end-of-region or 410end-of-buffer, whichever comes first. 411 412This function is NOT idempotent -- running it twice on the same text will 413certainly do the wrong thing, unless at first the text contained none of 414those characters." 415 416(interactive) 417(setq BEGIN (point)) 418 419;; Replace significant characters: "&", "<", and ">". 420 421(while (search-forward "&" nil t) (replace-match "&" t t)) 422 423(goto-char BEGIN) 424(while (search-forward "<" nil t) (replace-match "<" t t)) 425 426(goto-char BEGIN) 427(while (search-forward ">" nil t) (replace-match ">" t t)) 428 429(goto-char BEGIN) 430) 431 432;;============================================================ 433 434(defun gs-wrap-textfile () 435 436"Prepare a text file for inclusion between <pre> and </pre>, then put a 437header and footer around it. One would generally run this function on a 438buffer containing only an original text file; it is how the original 439history and news files were first prepared. At this point it's likely to 440be most useful in preparing new sections for the news document." 441 442(interactive) 443 444(widen) 445 446;; First prepare the entire text by replacing special characters. 447 448(goto-char (point-min)) 449(gs-text-chars) 450 451;; At the end of the file, end as-is text and add the standard footer. 452 453(goto-char (point-max)) 454(backward-word 1) (next-line 1) (beginning-of-line) 455(delete-region (point) (point-max)) 456(insert "\n</pre>\n") 457(insert-file "Footer.htm") 458 459;; At the beginning of the file, begin as-is text and delete everything 460;; up to the identity string (if any), saving the identity string. 461 462(goto-char (point-min)) 463(insert "<pre>\n") (setq g~pre-point (point)) 464(setq g~ID " [No pre-existing ID] ") 465(if (re-search-forward "^\\$Id:\\( [^ ]+ \\)\\$" nil t) (progn 466 (setq g~ID (buffer-substring (match-beginning 1) (match-end 1))) 467 (next-line 1) (beginning-of-line) (delete-region g~pre-point (point)) 468 )) 469 470;; Insert the standard header and fill in the identity string. 471 472(goto-char (point-min)) (insert-file "Header.htm") 473(goto-char (point-min)) (search-forward "<!--" nil t) 474(delete-horizontal-space) (insert g~ID) 475(search-forward "<pre>\n" nil t) 476) 477 478;;============================================================ 479 480(defun ~gsregion (Tag) 481 482"Tag a region, leaving point at its end and the region around the whole 483thing including the new surrounding tags; thus invoking this function twice 484successively makes the first invocation the inner tags and the second the 485outer. 486 487Not intended for interactive use; for that use gs-tag, which gives a little 488bit of additional service." 489 490(interactive) 491 492(if (not (= 0 (length Tag))) (save-restriction 493 (narrow-to-region (region-beginning) (region-end)) 494 (goto-char (point-min)) (insert "<" Tag ">") 495 (goto-char (point-max)) (insert "</" Tag ">") 496 (push-mark (point-min) t) 497 (goto-char (point-max)) 498 ) 499 ) 500) 501 502;;============================================================ 503 504(defun gs-structure () 505 506"For historical interest only: add structuring commentary to a Ghostscript 507HTML file. It's crude, but it did most of the work. Future files will 508acquire their structure through plagiarism, like any other code. 509 510Now they've all been structured, and this function was used to do it. The 511placement of table-of-contents lines never worked, because most of the 512files didn't yet have TOCS. Now all files that should have TOCs have 513properly placed markers, but that's history." 514 515(interactive) 516 517(setq g~thisfile (buffer-name)) 518 519(widen) 520(goto-char (point-min)) 521 522;; Replace the RCS $Id if one can be found in exactly the right format, and 523;; otherwise insert one just after the title, along with a warning message. 524 525(if (re-search-forward "<!-- $Id: *\\([^ ]*\\) $ -->" nil t) 526 (progn 527 (setq Original (buffer-substring (match-beginning 1) (match-end 1))) 528 (replace-match g~thisfile t t nil 1) 529 ) 530 (progn 531 (search-forward "</title>" nil t) (end-of-line) 532 (insert (concat "\n<!-- $Id: " g~thisfile " $ -->")) 533 (setq Original "(UNSET by gs-structure)") 534 ) 535 ) 536 537(end-of-line) 538(insert (concat "\n<!-- Originally: " Original " -->")) 539 540;; Place the visible header marker immediately after <BODY>. 541 542(re-search-forward "<body[^>]*>" nil t) 543 (end-of-line) (forward-char 1) 544 (insert (concat g~header-begin "\n\n")) 545 546;; Place the headline marker before the first <table> block. 547 548(search-forward "<table" nil t) (search-backward "\n\n" nil t) 549 (forward-word 1) (beginning-of-line) 550 (insert (concat g~headline-begin "\n\n")) 551 552;; After the first table block place the end-headline marker and both 553;; table-of-contents markers, without worrying where the table of contents 554;; really is. The TOC markers can easily be moved by hand later. 555 556(search-forward "\n\n" nil t) 557 (backward-word 1) (end-of-line) (forward-char 1) 558 (insert (concat 559 "\n" 560 g~headline-end "\n\n" 561 g~toc-begin "\n\n" 562 g~toc-end "\n\n")) 563 564;; The hints section begins with the first paragraph after where the TOC 565;; markers are placed, and ends with <HR>. This isn't precise, and in fact 566;; fails for several files, but once again only an approximation is needed 567;; because it'll be edited by hand later. 568 569(search-forward "<p>" nil t) (beginning-of-line) 570 (insert (concat g~hint-begin "\n\n")) 571 572(search-forward "<hr>" nil t) (beginning-of-line) 573 (insert (concat g~hint-end "\n\n")) 574 575;; The visible header ends with (and includes) the first <HR>, and the 576;; contents begin immediately thereafter. 577 578(search-forward "<hr>\n" nil t) 579 (insert (concat "\n" g~header-end "\n\n")) 580 581(forward-word 1) (beginning-of-line) 582 (insert (concat g~contents-begin "\n\n")) 583 584;; The contents end before the final <HR> and the trailer begins 585;; immediately thereafter. 586 587(goto-char (point-max)) (search-backward "<hr>" nil t) 588 (backward-word 1) (end-of-line) (forward-char 1) 589 (insert (concat 590 "\n" 591 g~contents-end "\n\n" 592 g~trailer-begin "\n\n")) 593 594;; The trailer ends with </BODY>. 595 596(goto-char (point-max)) (search-backward "</body>" nil t) 597 (insert (concat "\n" g~trailer-end "\n\n")) 598 599;; We may have introduced trailing whitespace and extra empty lines. 600;; Remove them. 601 602(goto-char (point-min)) 603(while (re-search-forward "[ \t\240\r]+$" nil t) (replace-match "" t t)) 604(goto-char (point-min)) 605(while (re-search-forward "\n\n\n+" nil t) (replace-match "\n\n" t t)) 606 607) 608 609;;============================================================ 610;; When this file is loaded into emacs, define the structure markers for GS 611;; HTML files. These markers have two purposes: first, to make the HTML 612;; more readable, and second, to enable these functions to locate sections 613;; unambiguously (see gs-toc, the table of contents builder). Note that 614;; the markers do not include LF. 615 616(defun g~marker (basic) 617 618"Build a complete Ghostscript HTML file marker from its text-only part. 619gs-toc relies entirely on this function, so if it's ever changed, gs-toc 620and existing markers would also have to be changed to keep pace. 621 622Intended only for initialization, not interactive use. 623 624All the existing files are now marked up, and since any future ones are 625(properly) likely to be created by plagiarism, it's difficult to imagine 626why anyone would want to change this unless they want to go to the trouble 627of coming up with a much more useful marking scheme." 628 629(interactive) 630 631(setq HEAD (concat "<!-- [" basic "] ")) 632(concat HEAD 633 (substring 634 "====================================================================== -->" 635 (- (length HEAD) 80) 636 )) 637) 638 639;;============================================================ 640;; Initialization code that must run after functions are defined. 641;; 642;; Look in a Ghostscript HTML file to see how these markers are used, 643;; generally 644;; 645;; begin visible header 646;; begin headline 647;; end headline 648;; begin table of contents 649;; end table of contents 650;; begin hint 651;; end hint 652;; end visible header 653;; begin contents 654;; end contents 655;; begin visible trailer 656;; end visible trailer 657;; 658;; although the TOC is in slightly different positions in a few files. 659 660(defvar g~header-begin (g~marker "1.0 begin visible header") 661 "Begin the HTML file's visible header material") 662 663(defvar g~header-end (g~marker "1.0 end visible header") 664 "End the HTML file's visible header") 665 666(defvar g~headline-begin (g~marker "1.1 begin headline") 667 "Begin the conspicuous headline") 668 669(defvar g~headline-end (g~marker "1.1 end headline") 670 "End the conspicuous headline") 671 672(defvar g~toc-begin (g~marker "1.2 begin table of contents") 673 "Begin the table of contents") 674 675(defvar g~toc-end (g~marker "1.2 end table of contents") 676 "End the table of contents") 677 678(defvar g~hint-begin (g~marker "1.3 begin hint") 679 "Begin the \"for other information\" section") 680 681(defvar g~hint-end (g~marker "1.3 end hint") 682 "End the \"for other information\" section") 683 684(defvar g~contents-begin (g~marker "2.0 begin contents") 685 "Begin the main contents") 686 687(defvar g~contents-end (g~marker "2.0 end contents") 688 "End the main contents") 689 690(defvar g~trailer-begin (g~marker "3.0 begin visible trailer") 691 "Begin the visible standard trailer material") 692 693(defvar g~trailer-end (g~marker "3.0 end visible trailer") 694 "End the visible standard trailer material") 695 696;;============================================================ 697;; Some working variables 698 699(defvar gs-anchor "JUNK" "*Anchor name to insert") 700(defvar gs-anchor-file "JUNKFILE" "*Anchor filename to insert") 701(defvar gs-work-buffer "*GS work*" "*Ghostscript working buffer") 702