]> git.donarmstrong.com Git - lib.git/blob - emacs_el/emacs-wiki.el
Initial user home directory commit
[lib.git] / emacs_el / emacs-wiki.el
1 ;;; emacs-wiki.el --- Maintain a local Wiki using Emacs-friendly markup
2
3 ;; Copyright (C) 2001, 2002, 2003 John Wiegley (johnw AT gnu DOT org)
4
5 ;; Emacs Lisp Archive Entry
6 ;; Filename: emacs-wiki.el
7 ;; Version: 2.40
8 ;; Date: Sun 24-Nov-2002
9 ;; Keywords: hypermedia
10 ;; Author: John Wiegley (johnw AT gnu DOT org)
11 ;;         Alex Schroeder (alex AT gnu DOT org)
12 ;; Maintainer: Damien Elmes (emacswiki AT repose DOT cx)
13 ;; Description: Maintain Emacs-friendly Wikis in a local directory
14 ;; URL: http://repose.cx/emacs/wiki
15 ;; Compatibility: Emacs20, Emacs21, XEmacs21
16
17 ;; This file is not part of GNU Emacs.
18
19 ;; The canonical URL for this file is now:
20 ;;   http://repose.cx/emacs/wiki
21 ;; Older copies and other modules which use emacs-wiki can be found at the
22 ;; original author's page:
23 ;;   http://www.gci-net.com/users/j/johnw/EmacsResources.html
24
25 ;; This is free software; you can redistribute it and/or modify it under
26 ;; the terms of the GNU General Public License as published by the Free
27 ;; Software Foundation; either version 2, or (at your option) any later
28 ;; version.
29 ;;
30 ;; This is distributed in the hope that it will be useful, but WITHOUT
31 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
32 ;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
33 ;; for more details.
34 ;;
35 ;; You should have received a copy of the GNU General Public License
36 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
37 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
38 ;; MA 02111-1307, USA.
39
40 ;;; Commentary:
41
42 ;; Wiki is a concept, more than a thing.  It is a way of creating
43 ;; document pages using plain text markup and simplified hyperlinking.
44
45 ;; By typing a name in MixedCase, a hyperlink is automatically created
46 ;; to the document "MixedCase".  Pressing return on that name will
47 ;; create the file if it doesn't exist, or visit it if it does.
48
49 ;; The markup used by emacs-wiki is intended to be very friendly to
50 ;; people familiar with Emacs.  Type C-h v emacs-wiki-publishing-markup
51 ;; after this mode is loaded for how to get started.
52
53 ;; * Startup
54
55 ;; To begin using emacs-wiki, put this in your .emacs file:
56
57 ;;   (load "emacs-wiki")
58
59 ;; Now you can type M-x emacs-wiki-find-file, give it a WikiName (or
60 ;; just hit return) and start typing!
61
62 ;; You should also type M-x customize-group, and give the name
63 ;; "emacs-wiki".  Change it to suite your preferences.  Each of the
64 ;; options has its own documentation.
65
66 ;; * Keystroke summary
67
68 ;; Here is a summary of keystrokes available in every Wiki buffer:
69
70 ;;   C-c C-a    jump to an index of all the Wiki pages
71 ;;   C-c C-b    show all pages that reference this page
72 ;;   C-c C-s    search for a word in your Wiki pages
73 ;;   C-c C-f    jump to another Wiki page; prompts for the name
74 ;;   C-c C-l    highlight/refresh the current buffer
75 ;;   C-c C-p    publish any Wiki pages that have changed as HTML
76 ;;   C-c C-r    rename wiki link at point
77 ;;   C-c C-v    change wiki project
78 ;;   C-c C-D    delete wiki link at point (binding will only work on X)
79 ;;   C-c =      diff this page against the last backup version
80 ;;   TAB        move to the next Wiki reference
81 ;;   S-TAB      move to the previous Wiki reference
82
83 ;; * Using pcomplete
84
85 ;; If you have pcomplete loaded, you can type M-TAB to complete Wiki
86 ;; names.  Hitting M-TAB twice or more time in succession, will cycle
87 ;; through all of the possibilities.  You can download pcomplete from
88 ;; my Website:
89
90 ;;   http://www.gci-net.com/~johnw/emacs.html
91
92 ;; * ChangeLog support
93
94 ;; If you use a ChangeLog (C-x 4 a) within one of your Wiki
95 ;; directories, it will be used for notifying visitors to your wiki of
96 ;; recent changes.
97
98 ;; * Changing title or stylesheet
99
100 ;; For convenience, if you want to change the visible title, or the
101 ;; stylesheet, used by a certain Wiki page during HTML publishing,
102 ;; just put:
103
104 ;; #title Hello there
105 ;; #style hello.css
106
107 ;; at the top of the page.
108
109 ;; * <lisp> tricks
110
111 ;; <lisp></lisp> tags can be used, not only to evaluate forms for
112 ;; insertion at that point, but to influence the publishing process in
113 ;; many ways.  Here's another way to change a page's stylesheet:
114
115 ;; <lisp>
116 ;; (ignore
117 ;;   ;; use special.css for this Wiki page
118 ;;   (set (make-variable-buffer-local 'emacs-wiki-style-sheet)
119 ;;        "<link rel=\"stylesheet\" type=\"text/css\" href=\"special.css\">"))
120 ;; </lisp>
121
122 ;; The 'ignore' is needed so nothing is inserted where the <lisp> tag
123 ;; occurred.  Also, there should be no blank lines before or after the
124 ;; tag (to avoid empty paragraphs from being created).  The best place
125 ;; to put this would be at the very top or bottom of the page.
126
127 ;; * Sub-lists?
128
129 ;; There is no inherent support for sub-lists, since I couldn't think
130 ;; of a simple way to do it.  But if you really need them, here's a
131 ;; trick you can use:
132
133 ;; - Hello
134 ;;   <ul>
135 ;;   <li>There
136 ;;   <li>My friend
137 ;;   </ul>
138
139 ;;; Thanks
140
141 ;; Alex Schroeder (alex AT gnu DOT org), current author of "wiki.el".
142 ;;   His latest version is here:
143 ;;       http://www.geocities.com/kensanata/wiki/WikiMode.html
144 ;;
145 ;; Frank Gerhardt (Frank.Gerhardt AT web DOT de), author of the original wiki-mode
146 ;;   His latest version is here:
147 ;;       http://www.s.netic.de/fg/wiki-mode/wiki.el
148 ;;
149 ;; Thomas Link (<t.link AT gmx DOT at)
150
151 ;;; Code:
152
153 ;; The parts of this code, and work to be done:
154 ;;
155 ;; * setup emacs-wiki major mode
156 ;; * generate WikiName list
157 ;; * utility functions to extract link parts
158 ;; * open a page
159 ;; * navigate links in the buffer
160 ;; * visit a link
161 ;; * search Wiki pages for text/backlinks
162 ;; * index generation
163 ;; * buffer highlighting (using font-lock)
164 ;; * HTML publishing
165 ;;   - Allow for alternate markup tables: DocBook, xhtml, etc.
166 ;;   - <nop> used in a line of verse doesn't have effect
167 ;; * HTTP serving (using httpd.el)
168 ;;   - Diffing (look at using highlight-changes-mode and htmlify.el)
169 ;;   - Editing (requires implementing POST method for httpd.el)
170
171 (defvar emacs-wiki-version "$Id"
172   "The version of emacs-wiki currently loaded")
173
174 (require 'derived)
175
176 ;; for caddr etc
177 ;(eval-when-compile (require 'cl))
178
179 ;; load pcomplete if it's available
180 (load "pcomplete" t t)
181
182 (defvar emacs-wiki-under-windows-p (memq system-type '(ms-dos windows-nt)))
183
184 ;;; Options:
185
186 (defgroup emacs-wiki nil
187   "Options controlling the behaviour of Emacs Wiki Mode.
188 Wiki is a concept, more than a thing.  It is a way of creating
189 document pages using plain text markup and simplified hyperlinking.
190
191 By typing a name in MixedCase, a hyperlink is automatically created
192 to the document \"MixedCase\".  Pressing return on that name will
193 create the file if it doesn't exist, or visit it if it does.
194
195 The markup used by emacs-wiki is intended to be very friendly to
196 people familiar with Emacs.  See the documentation for the variable
197 `emacs-wiki-publishing-markup' for a full description."
198   :group 'hypermedia)
199
200 (defcustom emacs-wiki-mode-hook
201   (append (if (featurep 'table)
202               '(table-recognize))
203           (unless (featurep 'httpd)
204             '(emacs-wiki-use-font-lock)))
205   "A hook that is run when emacs-wiki mode is entered."
206   :type 'hook
207   :options '(emacs-wiki-use-font-lock
208              emacs-wiki-highlight-buffer
209              flyspell-mode
210              footnote-mode
211              highlight-changes-mode)
212   :group 'emacs-wiki)
213
214 ;;;###autoload
215 (defcustom emacs-wiki-directories '("~/Wiki")
216   "A list of directories where Wiki pages can be found."
217   :require 'emacs-wiki
218   :type '(repeat :tag "Wiki directories" directory)
219   :group 'emacs-wiki)
220
221 (defcustom emacs-wiki-default-page "WelcomePage"
222   "Name of the default page used by \\[emacs-wiki-find-file]."
223   :type 'string
224   :group 'emacs-wiki)
225
226 (defcustom emacs-wiki-file-ignore-regexp
227   "\\`\\(\\.?#.*\\|.*,v\\|.*~\\|\\.\\.?\\)\\'"
228   "A regexp matching files to be ignored in Wiki directories."
229   :type 'regexp
230   :group 'emacs-wiki)
231
232 (defcustom emacs-wiki-ignored-extensions-regexp
233   "\\.\\(bz2\\|gz\\|[Zz]\\)\\'"
234   "A regexp of extensions to omit from the ending of Wiki page name."
235   :type 'string
236   :group 'emacs-wiki)
237
238 (defcustom emacs-wiki-interwiki-names
239   '(("GnuEmacs" . "http://www.gnu.org/software/emacs/emacs.html")
240     ("TheEmacsWiki" .
241      (lambda (tag)
242        (concat "http://www.emacswiki.org/cgi-bin/wiki.pl?"
243                (or tag "SiteMap"))))
244     ("MeatballWiki" .
245      (lambda (tag)
246        (concat "http://www.usemod.com/cgi-bin/mb.pl?"
247                (or tag "MeatballWiki")))))
248   "A table of WikiNames that refer to external entities.
249 The format of this table is an alist, or series of cons cells.
250 Each cons cell must be of the form:
251
252   (WIKINAME . STRING-OR-FUNCTION)
253
254 The second part of the cons cell may either be a STRING, which in most
255 cases should be a URL, or a FUNCTION.  If a function, it will be
256 called with one argument: the tag applied to the Interwiki name, or
257 nil if no tag was used.  If the cdr was a STRING and a tag is used,
258 the tag is simply appended.
259
260 Here are some examples:
261
262   (\"JohnWiki\" . \"http://alice.dynodns.net/wiki?\")
263
264 Referring to [[JohnWiki#EmacsModules]] then really means:
265
266   http://alice.dynodns.net/wiki?EmacsModules
267
268 If a function is used for the replacement text, you can get creative
269 depending on what the tag is.  Tags may contain any alphabetic
270 character, any number, % or _.  If you need other special characters,
271 use % to specify the hex code, as in %2E.  All browsers should support
272 this."
273   :type '(repeat (cons (string :tag "WikiName")
274                        (choice (string :tag "URL") function)))
275   :group 'emacs-wiki)
276
277 (defvar emacs-wiki-url-or-name-regexp nil
278   "Matches either a Wiki link or a URL.  This variable is auto-generated.")
279
280 (defvar emacs-wiki-url-or-name-regexp-group-count nil
281   "Matches either a Wiki link or a URL.  This variable is auto-generated.")
282
283 (defcustom emacs-wiki-extended-link-regexp
284   "\\[\\[\\([^] \t\n]+\\)\\]\\(\\[\\([^]\n]+\\)\\]\\)?\\]"
285   "Regexp used to match [[extended][links]]."
286   :type 'regexp
287   :group 'emacs-wiki)
288
289 (defun emacs-wiki-count-chars (string char)
290   (let ((i 0)
291         (l (length string))
292         (count 0))
293     (while (< i l)
294       (if (eq char (aref string i))
295           (setq count (1+ count)))
296       (setq i (1+ i)))
297     count))
298
299 (defun emacs-wiki-set-sym-and-url-regexp (sym value)
300   (setq emacs-wiki-url-or-name-regexp
301         (concat "\\("
302                 (if (eq sym 'emacs-wiki-name-regexp)
303                     value
304                   emacs-wiki-name-regexp) "\\|"
305                 (if (eq sym 'emacs-wiki-name-regexp)
306                     (if (boundp 'emacs-wiki-url-regexp)
307                         emacs-wiki-url-regexp
308                       "")
309                   value) "\\)")
310         emacs-wiki-url-or-name-regexp-group-count
311         (- (emacs-wiki-count-chars
312             emacs-wiki-url-or-name-regexp ?\() 2))
313   (set sym value))
314
315 (defcustom emacs-wiki-name-regexp
316   (concat "\\(" emacs-wiki-extended-link-regexp "\\|"
317           "\\<[A-Z][a-z]+\\([A-Z][a-z]+\\)+\\(#[A-Za-z0-9_%]+\\)?" "\\)")
318   "Regexp used to match WikiNames."
319   :type 'regexp
320   :set 'emacs-wiki-set-sym-and-url-regexp
321   :group 'emacs-wiki)
322
323 (defcustom emacs-wiki-url-regexp
324   (concat "\\<\\(https?:/?/?\\|ftp:/?/?\\|gopher://\\|"
325           "telnet://\\|wais://\\|file:/\\|s?news:\\|"
326           "mailto:\\)"
327           "[^]  \n \"'()<>[^`{}]*[^]    \n \"'()<>[^`{}.,;]+")
328   "A regexp used to match URLs within a Wiki buffer."
329   :type 'regexp
330   :set 'emacs-wiki-set-sym-and-url-regexp
331   :group 'emacs-wiki)
332
333 (defcustom emacs-wiki-browse-url-function 'browse-url
334   "Function to call to browse a URL."
335   :type 'function
336   :group 'emacs-wiki)
337
338 (defcustom emacs-wiki-grep-command
339   "find %D -type f ! -name '*~' | xargs egrep -n -e \"\\<%W\\>\""
340   "The name of the program to use when grepping for backlinks.
341 The string %D is replaced by `emacs-wiki-directories', space-separated.
342 The string %W is replaced with the name of the Wiki page.
343
344 Note: I highly recommend using glimpse to search large Wikis.  To use
345 glimpse, install and edit a file called .glimpse_exclude in your home
346 directory.  Put a list of glob patterns in that file to exclude Emacs
347 backup files, etc.  Then, run the indexer using:
348
349   glimpseindex -o <list of Wiki directories>
350
351 Once that's completed, customize this variable to have the following
352 value:
353
354   glimpse -nyi \"%W\"
355
356 Your searches will go much, much faster, especially for very large
357 Wikis.  Don't forget to add a user cronjob to update the index at
358 intervals."
359   :type 'string
360   :group 'emacs-wiki)
361
362 (defvar emacs-wiki-mode-map
363   (let ((map (make-sparse-keymap)))
364     (define-key map [(control ?c) (control ?a)] 'emacs-wiki-index)
365     (define-key map [(control ?c) (control ?f)] 'emacs-wiki-find-file)
366     (define-key map [(control ?c) (control ?b)] 'emacs-wiki-backlink)
367     (define-key map [(control ?c) (control ?s)] 'emacs-wiki-search)
368     (define-key map [(control ?c) (control ?p)] 'emacs-wiki-publish)
369     (define-key map [(control ?c) (control ?v)] 'emacs-wiki-change-project)
370     (define-key map [(control ?c) (control ?r)]
371                                           'emacs-wiki-rename-link-at-point)
372     (define-key map [(control ?c) (control ?D)]
373                                           'emacs-wiki-delete-link-at-point)
374
375     (define-key map [(control ?c) (control ?l)] 'font-lock-mode)
376
377     (define-key map [(control ?c) ?=]
378       (lambda ()
379         (interactive)
380         (diff-backup buffer-file-name)))
381
382     (define-key map [tab] 'emacs-wiki-next-reference)
383     (define-key map [(control ?i)] 'emacs-wiki-next-reference)
384
385     (if (featurep 'xemacs)
386         (define-key map [(shift tab)] 'emacs-wiki-previous-reference)
387       (define-key map [(shift iso-lefttab)] 'emacs-wiki-previous-reference)
388       (define-key map [(shift control ?i)] 'emacs-wiki-previous-reference))
389
390     (when (featurep 'pcomplete)
391       (define-key map [(meta tab)] 'pcomplete)
392       (define-key map [(meta control ?i)] 'pcomplete))
393
394     map)
395   "Keymap used by Emacs Wiki mode.")
396
397 (defvar emacs-wiki-local-map
398   (let ((map (make-sparse-keymap)))
399     (define-key map [return] 'emacs-wiki-follow-name-at-point)
400     (define-key map [(control ?m)] 'emacs-wiki-follow-name-at-point)
401     (if (featurep 'xemacs)
402         (define-key map [(button2)] 'emacs-wiki-follow-name-at-mouse)
403       (define-key map [mouse-2] 'emacs-wiki-follow-name-at-mouse)
404       (unless (eq emacs-major-version 21)
405         (set-keymap-parent map emacs-wiki-mode-map)))
406     map)
407   "Local keymap used by emacs-wiki while on a WikiName.")
408
409 ;; Code:
410
411 (defvar emacs-wiki-project nil)
412
413 ;;;###autoload
414 (define-derived-mode emacs-wiki-mode text-mode "Wiki"
415   "An Emacs mode for maintaining a local Wiki database.
416
417 Wiki is a hypertext and a content management system: Normal users are
418 encouraged to enhance the hypertext by editing and refactoring existing
419 wikis and by adding more.  This is made easy by requiring a certain way
420 of writing the wikis.  It is not as complicated as a markup language
421 such as HTML.  The general idea is to write plain ASCII.
422
423 Words with mixed case such as ThisOne are WikiNames.  WikiNames are
424 links you can follow.  If a wiki with that name exists, you will be
425 taken there.  If such a does not exist, following the link will create
426 a new wiki for you to fill.  WikiNames for non-existing wikis are
427 rendered as links with class \"nonexistent\", and are also displayed
428 in a warning color so that you can see wether following the link will
429 lead you anywhere or not.
430
431 In order to follow a link, hit RET when point is on the link, or use
432 mouse-2.
433
434 All wikis reside in the `emacs-wiki-directories'.
435
436 \\{emacs-wiki-mode-map}"
437   (if emacs-wiki-project
438       (emacs-wiki-change-project emacs-wiki-project))
439   ;; because we're not inheriting from normal-mode, we need to
440   ;; explicitly run file variables if the user wants to
441   (condition-case err
442       (hack-local-variables)
443     (error (message "File local-variables error: %s"
444                     (prin1-to-string err))))
445   ;; bootstrap the file-alist, if it's not been read in yet
446   (emacs-wiki-file-alist t)
447   ;; if pcomplete is available, set it up!
448   (when (featurep 'pcomplete)
449     (set (make-variable-buffer-local 'pcomplete-default-completion-function)
450          'emacs-wiki-completions)
451     (set (make-variable-buffer-local 'pcomplete-command-completion-function)
452          'emacs-wiki-completions)
453     (set (make-variable-buffer-local 'pcomplete-parse-arguments-function)
454          'emacs-wiki-current-word)))
455
456 (defsubst emacs-wiki-page-file (page &optional no-check-p)
457   "Return a filename if PAGE exists within the current Wiki."
458   (cdr (assoc page (emacs-wiki-file-alist no-check-p))))
459
460 (defsubst emacs-wiki-directory-part (path)
461   (directory-file-name (expand-file-name path)))
462
463 (defun emacs-wiki-directories-member (&optional directories)
464   "Return non-nil if the current buffer is in `emacs-wiki-directories'."
465   (let ((here (emacs-wiki-directory-part default-directory))
466         (d (or directories emacs-wiki-directories))
467         yes)
468     (while d
469       (if (string= here (emacs-wiki-directory-part (if (consp (car d))
470                                                        (caar d)
471                                                      (car d))))
472           (setq yes (car d) d nil)
473         (setq d (cdr d))))
474     yes))
475
476 (defun emacs-wiki-maybe (&optional check-only)
477   "Maybe turn Emacs Wiki mode on for this file."
478   (let ((projs emacs-wiki-projects)
479         (mode-func 'emacs-wiki-mode)
480         project yes)
481     (while (and (not yes) projs)
482       (let* ((projsyms (cdar projs))
483              (pred (assq 'emacs-wiki-predicate projsyms))
484              dirs)
485         (if pred
486             (setq yes (funcall (cdr pred)))
487           (setq dirs (assq 'emacs-wiki-directories projsyms))
488           (if dirs
489               (setq yes (emacs-wiki-directories-member (cdr dirs)))))
490         (if yes
491             (setq project (caar projs)
492                   mode-func (or (cdr (assq 'emacs-wiki-major-mode projsyms))
493                                 mode-func))))
494       (setq projs (cdr projs)))
495     (setq yes (or yes (emacs-wiki-directories-member)))
496     (if (and yes (not check-only))
497         (let ((emacs-wiki-project project))
498           (funcall mode-func)))
499     yes))
500
501 (add-hook 'find-file-hooks 'emacs-wiki-maybe)
502
503 ;;; Support WikiName completion using pcomplete
504
505 (defun emacs-wiki-completions ()
506   "Return a list of possible completions names for this buffer."
507   (while (pcomplete-here
508           (mapcar 'car (append (emacs-wiki-file-alist)
509                                emacs-wiki-interwiki-names)))))
510
511 (defun emacs-wiki-current-word ()
512   (let ((end (point)))
513     (save-restriction
514       (save-excursion
515         (skip-chars-backward "^\\[ \t\n")
516         (narrow-to-region (point) end))
517       (pcomplete-parse-buffer-arguments))))
518
519 ;;; Return an list of known wiki names and the files they represent.
520
521 (defsubst emacs-wiki-time-less-p (t1 t2)
522   "Say whether time T1 is less than time T2."
523   (or (< (car t1) (car t2))
524       (and (= (car t1) (car t2))
525            (< (nth 1 t1) (nth 1 t2)))))
526
527 (defun emacs-wiki-page-name (&optional name)
528   "Return the canonical form of the Wiki page name.
529 All this means is that certain extensions, like .gz, are removed."
530   (save-match-data
531     (unless name
532       (setq name buffer-file-name))
533     (if name
534         (let ((page (file-name-nondirectory name)))
535           (if (string-match emacs-wiki-ignored-extensions-regexp page)
536               (replace-match "" t t page)
537             page)))))
538
539 (defun emacs-wiki-page-title (&optional name)
540   "Return the canonical form of the Wiki page name.
541 All this means is that certain extensions, like .gz, are removed."
542   (or emacs-wiki-current-page-title
543       (emacs-wiki-prettify-title (emacs-wiki-page-name name))))
544
545 (defvar emacs-wiki-file-alist nil)
546
547 (defun emacs-wiki-file-alist (&optional no-check-p)
548   "Return possible Wiki filenames in `emacs-wiki-directories'.
549 On UNIX, this list is only updated if one of the directories' contents
550 have changed.  On Windows, it is always reread from disk."
551   (let* ((file-alist (assoc emacs-wiki-current-project
552                             emacs-wiki-file-alist))
553          (dirs emacs-wiki-directories)
554          (d dirs) last-mod)
555     (unless (or emacs-wiki-under-windows-p no-check-p)
556       (while d
557         (let ((mod-time (nth 5 (file-attributes (car d)))))
558           (if (or (null last-mod)
559                   (and mod-time (emacs-wiki-time-less-p last-mod mod-time)))
560               (setq last-mod mod-time)))
561         (setq d (cdr d))))
562     (if (or (and no-check-p (cadr file-alist))
563             (not (or emacs-wiki-under-windows-p
564                      (null (cddr file-alist))
565                      (null last-mod)
566                      (emacs-wiki-time-less-p (cddr file-alist) last-mod))))
567         (cadr file-alist)
568       (if file-alist
569           (setcdr (cdr file-alist) last-mod)
570         (setq file-alist (cons emacs-wiki-current-project (cons nil last-mod))
571               emacs-wiki-file-alist (cons file-alist emacs-wiki-file-alist)))
572       (save-match-data
573         (setcar
574          (cdr file-alist)
575          (let* ((names (list t))
576                 (lnames names))
577            (while dirs
578              (if (file-readable-p (car dirs))
579                  (let ((files (directory-files (car dirs) t nil t)))
580                    (while files
581                      (unless
582                          (or (file-directory-p (car files))
583                              (string-match emacs-wiki-file-ignore-regexp
584                                            (file-name-nondirectory
585                                             (car files))))
586                        (setcdr lnames
587                                (cons (cons (emacs-wiki-page-name (car files))
588                                            (car files)) nil))
589                        (setq lnames (cdr lnames)))
590                      (setq files (cdr files)))))
591              (setq dirs (cdr dirs)))
592            (cdr names)))))))
593
594 (defun emacs-wiki-complete-alist ()
595   "Return equivalent of calling (emacs-wiki-file-alist) for all projects."
596   (let ((emacs-wiki-current-project "_CompositeFileList")
597         (emacs-wiki-directories
598          (copy-alist emacs-wiki-directories))
599         (projs emacs-wiki-projects))
600     (while projs
601       (let* ((projsyms (cdar projs))
602              (dirs (cdr (assq 'emacs-wiki-directories projsyms))))
603         (while dirs
604           (add-to-list 'emacs-wiki-directories (car dirs))
605           (setq dirs (cdr dirs))))
606       (setq projs (cdr projs)))
607     (emacs-wiki-file-alist)))
608
609 ;; Utility functions to extract parts of a Wiki name
610
611 (defvar emacs-wiki-serving-p nil
612   "Non-nil when emacs-wiki is serving a wiki page directly.")
613
614 (defsubst emacs-wiki-transform-name (name)
615   "Transform NAME as per `emacs-wiki-publishing-transforms', returning NAME"
616   (save-match-data
617     (mapc (function
618            (lambda (elt)
619              (let ((reg (car elt))
620                    (rep (cdr elt)))
621                (when (string-match reg name)
622                  (setq name (replace-match rep t nil name))))))
623           emacs-wiki-publishing-transforms)
624     name))
625
626 (defsubst emacs-wiki-published-name (name &optional current)
627   "Return the externally visible NAME for a wiki page, possibly transformed
628   via `emacs-wiki-publishing-transforms'. If CURRENT is provided, convert any
629   path to be relative to it"
630   (emacs-wiki-transform-name
631    (progn
632      (when current
633        (setq name (file-relative-name name
634                                       (file-name-directory
635                                        (emacs-wiki-transform-name current)))))
636      (concat (if emacs-wiki-serving-p
637                  (unless (string-match "\\?" name) "wiki?")
638                emacs-wiki-publishing-file-prefix)
639              name
640              (if emacs-wiki-serving-p
641                  (if emacs-wiki-current-project
642                      (concat "&project=" emacs-wiki-current-project))
643                emacs-wiki-publishing-file-suffix)))))
644
645 (defsubst emacs-wiki-published-file (&optional file)
646   "Return the filename of the published file. Since this is based on the
647   published-name, it will be filtered through
648   `emacs-wiki-publishing-transforms'"
649   (expand-file-name (emacs-wiki-published-name (emacs-wiki-page-name
650                                                 file))
651                     emacs-wiki-publishing-directory))
652
653 (defcustom emacs-wiki-publishing-transforms nil
654   "A list of cons cells mapping regexps to replacements, which is applied when
655 generating the published name from the wiki file name. The replacements
656 run in order so you can chain them together.
657
658 An example is how I publish the emacs-wiki documentation. The emacs-wiki
659 homepage is in a file called EmacsWiki. With the following settings I can
660 publish directly to my webserver via tramp (the first rule catches 'WikiMarkup'
661 for instance):
662
663 (setq emacs-wiki-publishing-directory \"/webserver:/var/www/\")
664 (setq emacs-wiki-publishing-transforms
665         ((\".*Wiki.*\" . \"emacs/wiki/\\&\")
666          (\"EmacsWiki\\|WelcomePage\" . \"index\")))
667
668 Then when trying to publish a page EmacsWiki:
669
670 (emacs-wiki-published-file \"EmacsWiki\")
671
672 You get:
673
674 \"/webserver:/var/www/emacs/wiki/index.html\""
675   :type '(repeat
676           (cons
677            (regexp :tag "String to match")
678            (string :tag "Replacement string")))
679   :group 'emacs-wiki-publish)
680
681 (defsubst emacs-wiki-wiki-url-p (name)
682   "Return non-nil if NAME is a URL."
683   (save-match-data
684     (string-match emacs-wiki-url-regexp name)))
685
686 (defun emacs-wiki-wiki-visible-name (wiki-name)
687   "Return the visible part of a Wiki link.
688 This only really means something if [[extended][links]] are involved."
689   (save-match-data
690     (let ((name wiki-name))
691       (if (string-match emacs-wiki-extended-link-regexp name)
692           (if (match-string 2 name)
693               (setq name (match-string 3 name))
694             (setq name (match-string 1 name))))
695       (if (and (not (emacs-wiki-wiki-url-p name))
696                (string-match "#" name))
697           (if (= 0 (match-beginning 0))
698               (setq name (emacs-wiki-page-name))
699             (let ((base (substring name 0 (match-beginning 0))))
700               (if (assoc base emacs-wiki-interwiki-names)
701                   (setq name (concat (substring name 0 (match-beginning 0))
702                                      ":" (substring name (match-end 0))))
703                 (setq name base)))))
704       name)))
705
706 (defun emacs-wiki-wiki-tag (wiki-name)
707   (save-match-data
708     (if (string-match "#" wiki-name)
709         (substring wiki-name (match-end 0)))))
710
711 (defun emacs-wiki-wiki-link-target (wiki-name)
712   "Return the target of a Wiki link.  This might include anchor tags."
713   (save-match-data
714     (let ((name wiki-name) lookup)
715       (if (string-match "^\\[\\[\\([^]]+\\)\\]" name)
716           (setq name (match-string 1 name)))
717       (if (and emacs-wiki-interwiki-names
718                (string-match "\\`\\([^#]+\\)\\(#\\(.+\\)\\)?\\'" name)
719                (setq lookup (assoc (match-string 1 name)
720                                    emacs-wiki-interwiki-names)))
721           (let ((tag (match-string 3 name))
722                 (target (cdr lookup)))
723             (if (stringp target)
724                 (setq name (concat target tag))
725               (setq name (funcall target tag))))
726         (if (and (> (length name) 0)
727                  (eq (aref name 0) ?#))
728             (setq name (concat (emacs-wiki-page-name) name))))
729       name)))
730
731 (defun emacs-wiki-wiki-base (wiki-name)
732   "Find the WikiName or URL mentioned by a Wiki link.
733 This means without tags, in the case of a WikiName."
734   (save-match-data
735     (let ((file (emacs-wiki-wiki-link-target wiki-name)))
736       (if (emacs-wiki-wiki-url-p file)
737           file
738         (if (string-match "#" file)
739             (substring file 0 (match-beginning 0))
740           file)))))
741
742 ;;; Open a Wiki page (with completion)
743
744 (defvar emacs-wiki-history-list nil)
745
746 (defun emacs-wiki-read-name (file-alist &optional prompt)
747   "Read the name of a valid Wiki page from minibuffer, with completion."
748   (let* ((default emacs-wiki-default-page)
749          (str (completing-read
750                (format "%s(default: %s) " (or prompt "Wiki page: ") default)
751                file-alist nil nil nil 'emacs-wiki-history-list)))
752         (if (or (null str) (= (length str) 0))
753             default
754           str)))
755
756 ;;;###autoload
757 (defun emacs-wiki-find-file (wiki &optional command directory)
758   "Open the Emacs Wiki page WIKI by name.
759 If COMMAND is non-nil, it is the function used to visit the file.
760 If DIRECTORY is non-nil, it is the directory in which the Wiki page
761 will be created if it does not already exist."
762   (interactive
763    (list
764     (let ((num (prefix-numeric-value current-prefix-arg)))
765        (if (< num 16)
766            (let* ((file-alist (if (= num 4)
767                                   (emacs-wiki-complete-alist)
768                                 (emacs-wiki-file-alist)))
769                   (name (emacs-wiki-read-name file-alist)))
770              (cons name (cdr (assoc name file-alist))))
771          (let ((name (read-file-name "Open wiki file: ")))
772            (cons name name))))))
773   (unless (interactive-p)
774     (setq wiki (cons wiki
775                      (cdr (assoc wiki (emacs-wiki-file-alist))))))
776   ;; At this point, `wiki' is (GIVEN-PAGE FOUND-FILE).
777   (if (cdr wiki)
778       (let ((buffer (funcall (or command 'find-file) (cdr wiki))))
779         (if (= (prefix-numeric-value current-prefix-arg) 16)
780             (with-current-buffer buffer
781               (set (make-variable-buffer-local 'emacs-wiki-directories)
782                    (cons (file-name-directory (cdr wiki))
783                          emacs-wiki-directories))
784               (set (make-variable-buffer-local 'emacs-wiki-file-alist) nil)))
785         buffer)
786     (let* ((dirname (or directory
787                         (emacs-wiki-maybe t)
788                         (car emacs-wiki-directories)))
789            (filename (expand-file-name (car wiki) dirname)))
790       (unless (file-exists-p dirname)
791         (make-directory dirname t))
792       (funcall (or command 'find-file) filename))))
793
794 ;;; Navigate/visit links or URLs.  Use TAB, S-TAB and RET (or mouse-2).
795
796 (defun emacs-wiki-next-reference ()
797   "Move forward to next Wiki link or URL, cycling if necessary."
798   (interactive)
799   (let ((case-fold-search nil)
800         (cycled 0) pos)
801     (save-excursion
802       (if (emacs-wiki-link-at-point)
803           (goto-char (match-end 0)))
804       (while (< cycled 2)
805         (if (re-search-forward emacs-wiki-url-or-name-regexp nil t)
806             (setq pos (match-beginning 0)
807                   cycled 2)
808           (goto-char (point-min))
809           (setq cycled (1+ cycled)))))
810     (if pos
811         (goto-char pos))))
812
813 (defun emacs-wiki-previous-reference ()
814   "Move backward to the next Wiki link or URL, cycling if necessary.
815 This function is not entirely accurate, but it's close enough."
816   (interactive)
817   (let ((case-fold-search nil)
818         (cycled 0) pos)
819     (save-excursion
820       (while (< cycled 2)
821         (if (re-search-backward emacs-wiki-url-or-name-regexp nil t)
822             (setq pos (point)
823                   cycled 2)
824           (goto-char (point-max))
825           (setq cycled (1+ cycled)))))
826     (if pos
827         (goto-char pos))))
828
829 (defun emacs-wiki-visit-link (link-name)
830   "Visit the URL or link named by LINK-NAME."
831   (let ((link (emacs-wiki-wiki-link-target link-name)))
832     (if (emacs-wiki-wiki-url-p link)
833         (funcall emacs-wiki-browse-url-function link)
834       ;; The name list is current since the last time the buffer was
835       ;; highlighted
836       (let* ((base (emacs-wiki-wiki-base link-name))
837              (file (emacs-wiki-page-file base t))
838              (tag  (and (not (emacs-wiki-wiki-url-p link))
839                         (emacs-wiki-wiki-tag link))))
840         (if (null file)
841             (find-file base)
842           (find-file file)
843           (when tag
844             (goto-char (point-min))
845             (re-search-forward (concat "^\\.?#" tag) nil t)))))))
846
847 (unless (fboundp 'line-end-position)
848   (defsubst line-end-position (&optional N)
849     (save-excursion (end-of-line N) (point))))
850
851 (unless (fboundp 'line-beginning-position)
852   (defsubst line-beginning-position (&optional N)
853     (save-excursion (beginning-of-line N) (point))))
854
855 (unless (fboundp 'match-string-no-properties)
856   (defalias 'match-string-no-properties 'match-string))
857
858 (defun emacs-wiki-link-at-point (&optional pos)
859   "Return non-nil if a URL or Wiki link name is at point."
860   (if (or (null pos)
861           (and (char-after pos)
862                (not (eq (char-syntax (char-after pos)) ? ))))
863       (let ((case-fold-search nil)
864             (here (or pos (point))))
865         (save-excursion
866           (goto-char here)
867           (skip-chars-backward "^'\"<>{}( \t\n")
868           (or (looking-at emacs-wiki-url-or-name-regexp)
869               (and (search-backward "[[" (line-beginning-position) t)
870                    (looking-at emacs-wiki-name-regexp)
871                    (<= here (match-end 0))))))))
872
873 (defun emacs-wiki-follow-name-at-point ()
874   "Visit the link at point, or insert a newline if none."
875   (interactive)
876   (if (emacs-wiki-link-at-point)
877       (emacs-wiki-visit-link (match-string 0))
878     (error "There is no valid link at point")))
879
880 (defun emacs-wiki-follow-name-at-mouse (event)
881   "Visit the link at point, or yank text if none."
882   (interactive "e")
883   (save-excursion
884     (cond ((fboundp 'event-window)      ; XEmacs
885            (set-buffer (window-buffer (event-window event)))
886            (and (event-point event) (goto-char (event-point event))))
887           ((fboundp 'posn-window)       ; Emacs
888            (set-buffer (window-buffer (posn-window (event-start event))))
889            (goto-char (posn-point (event-start event)))))
890     (if (emacs-wiki-link-at-point)
891         (emacs-wiki-visit-link (match-string 0)))))
892
893 (defun emacs-wiki-rename-link (link-name new-name)
894   (when (emacs-wiki-wiki-url-p link-name)
895     (error "Can't rename a URL"))
896   (let* ((base (emacs-wiki-wiki-base link-name))
897          (file (emacs-wiki-page-file base t)))
898     (if (null file)
899         (rename-file base new-name)
900       (rename-file file new-name))))
901
902 (defun emacs-wiki-rename-link-at-point ()
903   "Rename the link under point, and the location it points to. This does not
904   work with URLs"
905   (interactive "*")
906   (let (new-name old-name)
907     (if (emacs-wiki-link-at-point)
908         (progn
909           (setq old-name (match-string 0))
910           ;; emacs21 leaves the local keymap on this string, so we must strip
911           ;; properties so the user can hit return to exit minibuf
912           (set-text-properties 0 (length old-name) nil old-name)
913           (setq new-name (read-from-minibuffer "Rename to: " old-name))
914           (emacs-wiki-rename-link old-name new-name)
915           ;; at this point, the file would have been successfully renamed, so
916           ;; it's safe to change to link name now
917           (replace-match new-name nil t))
918       (error "There is no valid link at point"))))
919
920 (defun emacs-wiki-delete-link (link-name)
921   "Delete the file which link-name corresponds to"
922   (when (emacs-wiki-wiki-url-p link-name)
923     (error "Can't rename a URL"))
924   (let* ((base (emacs-wiki-wiki-base link-name))
925          (file (emacs-wiki-page-file base t)))
926     (if (null file)
927         (delete-file base)
928       (delete-file file))))
929
930 (defun emacs-wiki-delete-link-at-point ()
931   "Delete the link under point, and the location it points to. This does not
932   work with URLs"
933   (interactive "*")
934   (let (name)
935     (if (emacs-wiki-link-at-point)
936         (progn
937           (setq name (match-string 0))
938           (when (yes-or-no-p (concat "Delete "
939                                      name "? You can not undo this. "))
940             (emacs-wiki-delete-link name)
941             (replace-match "" nil t)))
942       (error "There is no valid link at point"))))
943
944 ;;; Find text in Wiki pages, or pages referring to the current page
945
946 (defvar emacs-wiki-search-history nil)
947
948 (defun emacs-wiki-grep (string &optional grep-command)
949   "Grep for STRING in the Wiki directories. GREP-COMMAND if passed will
950   supplant emacs-wiki-grep-command."
951   (require 'compile)
952   (let ((str (or grep-command emacs-wiki-grep-command))
953         (dirs (mapconcat (lambda (dir)
954                            (shell-quote-argument (expand-file-name dir)))
955                          emacs-wiki-directories " ")))
956     (while (string-match "%W" str)
957       (setq str (replace-match string t t str)))
958     (while (string-match "%D" str)
959       (setq str (replace-match dirs t t str)))
960     (compile-internal str "No more search hits" "search"
961                       nil grep-regexp-alist)))
962
963 (defun emacs-wiki-search (text)
964   "Search for the given TEXT string in the Wiki directories."
965   (interactive
966    (list (let ((str (concat emacs-wiki-grep-command)) pos)
967            (when (string-match "%W" str)
968              (setq pos (match-beginning 0))
969              (unless (featurep 'xemacs)
970                (setq pos (1+ pos)))
971              (setq str (replace-match "" t t str)))
972            (read-from-minibuffer "Search command: "
973                                  (cons str pos)
974                                  nil nil 'emacs-wiki-search-history))))
975   (emacs-wiki-grep nil text))
976
977 (defun emacs-wiki-backlink ()
978   "Grep for the current pagename in all the Wiki directories."
979   (interactive)
980   (emacs-wiki-grep (emacs-wiki-page-name)))
981
982 ;;; Generate an index of all known Wiki pages
983
984 (defun emacs-wiki-generate-index (&optional as-list exclude-private)
985   "Generate an index of all Wiki pages."
986   (let ((project emacs-wiki-current-project))
987     (with-current-buffer (get-buffer-create "*Wiki Index*")
988       (erase-buffer)
989       (if project
990           (emacs-wiki-change-project project))
991       (let ((files (sort (copy-alist (emacs-wiki-file-alist))
992                          (function
993                           (lambda (l r)
994                             (string-lessp (car l) (car r))))))
995             file)
996         (while files
997           (unless (and exclude-private
998                        (emacs-wiki-private-p (caar files)))
999             (insert (if as-list "- " "") "[[" (caar files) "]]\n"))
1000           (setq files (cdr files))))
1001       (current-buffer))))
1002
1003 (defun emacs-wiki-index ()
1004   "Display an index of all known Wiki pages."
1005   (interactive)
1006   (message "Generating Wiki index...")
1007   (pop-to-buffer (emacs-wiki-generate-index))
1008   (goto-char (point-min))
1009   (emacs-wiki-mode)
1010   (message "Generating Wiki index...done"))
1011
1012 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1013 ;;
1014 ;; Emacs Wiki Highlighting
1015 ;;
1016 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1017
1018 (defgroup emacs-wiki-highlight nil
1019   "Options controlling the behaviour of Emacs Wiki highlighting.
1020 See `emacs-wiki-highlight-buffer' for more information."
1021   :group 'emacs-wiki)
1022
1023 (defun emacs-wiki-make-faces ()
1024   (mapc (lambda (newsym)
1025           (let (num)
1026             (setq num newsym)
1027             (setq newsym (intern (concat "emacs-wiki-header-"
1028                                          (int-to-string num))))
1029             (cond
1030              ((featurep 'xemacs)
1031               (eval `(defface ,newsym
1032                        '((t (:size
1033                              ,(nth (1- num) '("24pt" "18pt" "14pt" "12pt"))
1034                              :bold t)))
1035                        "emacs-wiki header face"
1036                        :group 'emacs-wiki-highlight)))
1037              ((< emacs-major-version 21)
1038               (copy-face 'default newsym))
1039              (t
1040               (eval `(defface ,newsym
1041                        '((t (:height ,(1+ (* 0.1 (- 5 num)))
1042                                      :inherit variable-pitch
1043                                      :weight bold)))
1044                        "emacs-wiki header face"
1045                        :group 'emacs-wiki-highlight))))))
1046         '(1 2 3 4 5 6)))
1047 (emacs-wiki-make-faces)
1048
1049 (defface emacs-wiki-link-face
1050   '((((class color) (background light))
1051      (:foreground "green" :underline "green" :bold t))
1052     (((class color) (background dark))
1053      (:foreground "cyan" :underline "cyan" :bold t))
1054     (t (:bold t)))
1055   "Face for Wiki cross-references."
1056   :group 'emacs-wiki-highlight)
1057
1058 (defface emacs-wiki-bad-link-face
1059   '((((class color) (background light))
1060      (:foreground "red" :underline "red" :bold t))
1061     (((class color) (background dark))
1062      (:foreground "coral" :underline "coral" :bold t))
1063     (t (:bold t)))
1064   "Face for bad Wiki cross-references."
1065   :group 'emacs-wiki-highlight)
1066
1067 (defcustom emacs-wiki-highlight-buffer-hook nil
1068   "A hook run after a region is highlighted.
1069 Each function receives three arguments: BEG END VERBOSE.
1070 BEG and END mark the range being highlighted, and VERBOSE specifies
1071 whether progress messages should be displayed to the user."
1072   :type 'hook
1073   :group 'emacs-wiki-highlight)
1074
1075 (defcustom emacs-wiki-inline-images (and (not (featurep 'xemacs))
1076                                          (>= emacs-major-version 21)
1077                                          window-system)
1078   "If non-nil, inline locally available images within Wiki pages."
1079   :type 'boolean
1080   :group 'emacs-wiki-highlight)
1081
1082 (defcustom emacs-wiki-image-regexp
1083   "\\.\\(eps\\|gif\\|jp\\(e?g\\)\\|p\\(bm\\|ng\\)\\|tiff\\|x\\([bp]m\\)\\)\\'"
1084   "A link matching this regexp will be published inline as an image. Remember
1085 that it must be matched as a link first - so use either [[CamelCaps]] or
1086 include a leading slash - [[./text]]. An example:
1087
1088   [[./wife.jpg][A picture of my wife]]
1089
1090 If you omit the description, the alt tag of the resulting HTML buffer will be
1091 the name of the file."
1092   :type 'regexp
1093   :group 'emacs-wiki)
1094
1095 (defcustom emacs-wiki-file-regexp
1096   "[/?]\\|\\.\\(html?\\|pdf\\|el\\|zip\\|txt\\|tar\\)\\(\\.\\(gz\\|bz2\\)\\)?\\'"
1097   "A link matching this regexp will be regarded as a link to a file. Remember
1098 that it must be matched as a link first - so use either [[CamelCaps]] or
1099 include a leading slash - [[./text]]"
1100   :type 'regexp
1101   :group 'emacs-wiki)
1102
1103 (defcustom emacs-wiki-tag-regexp
1104   "<\\([^/ \t\n][^ \t\n</>]*\\)\\(\\s-+[^<>]+[^</>]\\)?\\(/\\)?>"
1105   "A regexp used to find XML-style tags within a buffer when publishing.
1106 Group 1 should be the tag name, group 2 the properties, and group
1107 3 the optional immediate ending slash."
1108   :type 'regexp
1109   :group 'emacs-wiki)
1110
1111 (defcustom emacs-wiki-inline-relative-to 'emacs-wiki-publishing-directory
1112   "The name of a symbol which records the location relative to where images
1113   should be found. The default assumes that when editing, the images can be
1114   found in the publishing directory. Another sensible default is
1115   `default-directory', which will try and find the images relative to the
1116   local page. You can use this to store images in wikidir/images, and
1117   maintain a parallel copy on the remote host."
1118   :type 'symbol
1119   :group 'emacs-wiki)
1120
1121 (defcustom emacs-wiki-markup-tags
1122   '(("example" t nil t emacs-wiki-example-tag)
1123     ("verbatim" t nil t emacs-wiki-verbatim-tag)
1124     ("nowiki" t nil t emacs-wiki-nowiki-tag)
1125     ("verse" t nil nil emacs-wiki-verse-tag)
1126     ("numbered" t nil nil emacs-wiki-numbered-tag)
1127     ("nop" nil nil t emacs-wiki-nop-tag)
1128     ("contents" nil t nil emacs-wiki-contents-tag)
1129     ("c-source" t t t emacs-wiki-c-source-tag))
1130   "A list of tag specifications, for specially marking up Wiki text.
1131 XML-style tags are the best way to add custom markup to Emacs Wiki.
1132 This is easily accomplished by customizing this list of markup tags.
1133
1134 For each entry, the name of the tag is given, whether it expects a
1135 closing tag and/or an optional set of attributes, if the handler
1136 function can also highlight the tag, and a function that performs
1137 whatever action is desired within the delimited region.
1138
1139 The tags themselves are deleted during publishing, although not during
1140 highlighting, before the function is called.  The function is called
1141 with three arguments, the beginning and end of the region surrounded
1142 by the tags (including the tags themselves, in the case of
1143 highlighting).  The third argument indicates whether the purpose of
1144 the call is to highlight the region, or mark it up for publishing.  If
1145 properties are allowed, they are passed as a fourth argument in the
1146 form of an alist.  The `end' argument to the function is always a
1147 marker.
1148
1149 Point is always at the beginning of the region within the tags, when
1150 the function is called.  Wherever point is when the function finishes
1151 is where tag markup/highlighting will resume.
1152
1153 These tag rules are processed once at the beginning of markup, and
1154 once at the end, to catch any tags which may have been inserted
1155 in-between.  For highlighting, they are processed as they occur, in
1156 the order they occur, once per text region.
1157
1158 Here is a summary of the default tags.  This includes the dangerous
1159 tags listed in `emacs-wiki-dangerous-tags', which may not be used by
1160 outsiders.
1161
1162  verbatim
1163    Protects against highlighting and wiki interpretation, and escapes any
1164    characters which have special meaning to the publishing format. For HTML,
1165    this means characters like '<' are escaped as HTML entities.
1166
1167  example
1168    Like verbatim, but typesets in HTML using the <pre> tag, with
1169    class=example, so whitespace formatting is preserved.
1170
1171  nowiki
1172    Inhibits wiki markup, but does not do any escaping to the underlying
1173    publishing medium. Useful for embedding HTML, PHP, etc.
1174
1175  verse
1176    Typesets like a normal paragraph, but without word-wrapping.
1177    That is, whitespace is preserved.
1178
1179  redirect
1180    Using the \"url\" attribute, you can specify that a page should
1181    redirect to another page.  The remaining contents of the page will
1182    not be published.  The optional \"delay\" attribute specifies how
1183    long to wait before redirecting.
1184
1185  nop
1186    When placed before a WikiLink, it will prevent that WikiLink from
1187    being treated as such.  Good for names like DocBook.
1188
1189  contents
1190    Produces a compact table of contents for any section heading at the
1191    same level or lower than the next section header encountered.
1192    Optional \"depth\" attribute specifies how deep the table of
1193    contents should go.
1194
1195  lisp
1196    Evaluate the region as a Lisp form, and displays the result.  When
1197    highlighting, the `display' text property is used, preserving the
1198    underlying text.  Turn off font-lock mode if you wish to edit it.
1199
1200  command
1201    Pass the region to a command interpretor and insert the result,
1202    guarding it from any further expansion.  Optional \"file\"
1203    attribute specifies the shell or interpretor to use.  If none is
1204    given, and `emacs-wiki-command-tag-file' has not been configured,
1205    Eshell is used.
1206
1207  python, perl
1208    Pass the region to the Python or Perl language interpretor, and
1209    insert the result.
1210
1211  c-source
1212    Markup the region as C or C++ source code, using the c2html
1213    program, if available.  Optional boolean attribute \"numbered\"
1214    will cause source lines to be numbered.
1215
1216    Note: If c2html is not available, the region will be converted to
1217    HTML friendly text (i.e., <> turns into &lt;&gt;), and placed in a
1218    <pre> block.  In this case, line numbering is not available.
1219
1220  bookmarks
1221    Insert bookmarks at the location of the tag from the given
1222    bookmarks file.  Required attribute \"file\" specifies which file
1223    to read from, and the optional attribute \"type\" may be one of:
1224    adr (for Opera), lynx, msie, ns, xbel or xmlproc.  The default type
1225    is \"xbel\".  The optional attribute \"folder\" may be used to
1226    specify which folder (and its children) should be inserted.
1227
1228    Note that xml-parse.el version 1.5 (available from my website) and
1229    the xbel-utils package (available at least to Debian users) is
1230    required for this feature to work."
1231   :type '(repeat (list (string :tag "Markup tag")
1232                        (boolean :tag "Expect closing tag" :value t)
1233                        (boolean :tag "Parse attributes" :value nil)
1234                        (boolean :tag "Highlight tag" :value nil)
1235                        function))
1236   :group 'emacs-wiki-highlight)
1237
1238 (defcustom emacs-wiki-dangerous-tags
1239   '(("redirect" t t nil emacs-wiki-redirect-tag)
1240     ("lisp" t nil t emacs-wiki-lisp-tag)
1241     ("command" t t t emacs-wiki-command-tag)
1242     ("python" t t t emacs-wiki-python-tag)
1243     ("perl" t t t emacs-wiki-perl-tag)
1244     ("bookmarks" nil t nil emacs-wiki-bookmarks-tag))
1245   "A list of tag specifications, for specially marking up Wiki text.
1246 These tags are dangerous -- meaning represent a gaping security hole
1247 -- and therefore are not available to outsiders who happen to edit a
1248 Wiki page"
1249   :type '(repeat (list (string :tag "Markup tag")
1250                        (boolean :tag "Expect closing tag" :value t)
1251                        (boolean :tag "Parse attributes" :value nil)
1252                        (boolean :tag "Highlight tag" :value nil)
1253                        function))
1254   :group 'emacs-wiki-highlight)
1255
1256 (defvar emacs-wiki-highlight-regexp nil)
1257 (defvar emacs-wiki-highlight-vector nil)
1258
1259 (defun emacs-wiki-configure-highlighting (sym val)
1260   (setq emacs-wiki-highlight-regexp
1261         (concat "\\(" (mapconcat (function
1262                                   (lambda (rule)
1263                                     (if (symbolp (car rule))
1264                                         (symbol-value (car rule))
1265                                       (car rule)))) val "\\|") "\\)")
1266         emacs-wiki-highlight-vector (make-vector 128 nil))
1267   (let ((rules val))
1268     (while rules
1269       (if (eq (cadr (car rules)) t)
1270           (let ((i 0) (l 128))
1271             (while (< i l)
1272               (unless (aref emacs-wiki-highlight-vector i)
1273                 (aset emacs-wiki-highlight-vector i
1274                       (nth 2 (car rules))))
1275               (setq i (1+ i))))
1276         (aset emacs-wiki-highlight-vector (cadr (car rules))
1277               (nth 2 (car rules))))
1278       (setq rules (cdr rules))))
1279   (set sym val))
1280
1281 (defsubst emacs-wiki-highlight-ok-context-p (beg end str)
1282   "Ensures whitespace or punctuation comes before the position BEG, and
1283   after the string STR. A search-forward is done for STR, bounding by END, and
1284   the position of the end of the match is returned if in the correct context."
1285   (save-excursion
1286     (let ((len (length str)))
1287       (and
1288        (setq end (search-forward str end t))
1289        ;; post end, want eob or whitespace/punctuation
1290        (or (> (skip-syntax-forward ". " (1+ end)) 0)
1291            (eq nil (char-after end)))
1292        (goto-char (- end len))
1293        ;; pre end, no whitespace
1294        (eq (skip-syntax-backward " " (- end len 1)) 0)
1295        (goto-char (+ beg len))
1296        ;; post beg, no whitespace
1297        (eq (skip-syntax-forward " " (+ beg len 1)) 0)
1298        (or (backward-char len) t) ;; doesn't return anything useful
1299        ;; pre beg, want sob or whitespace/punctuation
1300        (or (< (skip-syntax-backward ". " (1- beg)) 0)
1301            (eq nil (char-before beg)))
1302        end))))
1303
1304 (defun emacs-wiki-multiline-maybe (beg end &optional predicate)
1305   "If region between beg-end is a multi-line region, and the optional
1306   predicate is true, font lock the current region as multi-line. Predicate is
1307   called with the excursion saved."
1308   (when (and (or (eq (char-before end) ?\n)
1309                  (> (count-lines beg end) 1))
1310              (or (not predicate)
1311                  (save-excursion (funcall predicate beg end))))
1312     (save-excursion
1313       ;; mark whole lines as a multiline font-lock
1314       (goto-char beg)
1315       (setq beg (line-beginning-position))
1316       (goto-char end)
1317       (setq end (line-end-position))
1318       (add-text-properties beg end '(font-lock-multiline t))
1319       t)))
1320
1321 (defun emacs-wiki-highlight-emphasized ()
1322   ;; here we need to check four different points - the start and end of the
1323   ;; leading *s, and the start and end of the trailing *s. we allow the
1324   ;; outsides to be surrounded by whitespace or punctuation, but no word
1325   ;; characters, and the insides must not be surrounded by whitespace or
1326   ;; punctuation. thus the following are valid:
1327   ;; " *foo bar* "
1328   ;; "**foo**,"
1329   ;; and the following is invalid:
1330   ;; "** testing **"
1331   (let* ((beg (match-beginning 0))
1332          (e1 (match-end 0))
1333          (leader (- e1 beg))
1334          (end end)
1335          b2 e2 face)
1336     ;; if it's a header
1337     (unless (save-excursion
1338               (goto-char beg)
1339               (when (save-match-data (looking-at "^\\*\\{1,3\\} "))
1340                 (add-text-properties
1341                  (line-beginning-position) (line-end-position)
1342                  (list 'face
1343                        (intern (concat "emacs-wiki-header-"
1344                                        (int-to-string (1+ leader))))))
1345                 t))
1346       ;; it might be an normal, emphasised piece of text
1347       (when (and
1348              (setq e2 (emacs-wiki-highlight-ok-context-p
1349                        beg end (buffer-substring-no-properties beg e1)))
1350              (setq b2 (match-beginning 0)))
1351         (cond ((= leader 1) (setq face 'italic))
1352               ((= leader 2) (setq face 'bold))
1353               ((= leader 3) (setq face 'bold-italic)))
1354         (add-text-properties beg e1 '(invisible t intangible t))
1355         (add-text-properties e1 b2 (list 'face face))
1356         (add-text-properties b2 e2 '(invisible t intangible t)))
1357       (emacs-wiki-multiline-maybe
1358        beg end
1359        ;; ensures we only mark the region as multiline if it's correctly
1360        ;; delimited at the start
1361        (lambda (beg end)
1362          (goto-char (1+ beg))
1363          (eq (skip-syntax-forward " " (1+ beg)) 0)
1364          (or (backward-char) t)
1365          (or (< (skip-syntax-backward ". " (1- beg)) 0)
1366              (eq nil (char-before beg))))))))
1367
1368 (defun emacs-wiki-highlight-underlined ()
1369   (let ((start (- (point) 2))
1370         end)
1371     (when (setq end (emacs-wiki-highlight-ok-context-p start end "_"))
1372       (add-text-properties start (+ start 1) '(invisible t intangible t))
1373       (add-text-properties (+ start 1) (- end 1) '(face underline))
1374       (add-text-properties (- end 1) end '(invisible t intangible t)))))
1375
1376 (defun emacs-wiki-highlight-verbatim ()
1377   (let ((start (- (point) 2))
1378         end)
1379     (when (setq end (emacs-wiki-highlight-ok-context-p start end "="))
1380         (search-forward "=" end t))))
1381
1382 (defcustom emacs-wiki-highlight-markup
1383   `(;; render in teletype and suppress further parsing
1384     ("=[^\t =]" ?= emacs-wiki-highlight-verbatim)
1385
1386     ;; make emphasized text appear emphasized
1387     ("\\*+" ?* emacs-wiki-highlight-emphasized)
1388
1389     ;; make underlined text appear underlined
1390     ("_[^ \t_]" ?_ emacs-wiki-highlight-underlined)
1391
1392     ;; make quadruple quotes invisible
1393     ("''''" ?\'
1394      ,(function
1395        (lambda ()
1396          (add-text-properties (match-beginning 0) (match-end 0)
1397                               '(invisible t intangible t)))))
1398
1399     ("^#title" ?\# emacs-wiki-highlight-title)
1400
1401     (emacs-wiki-url-or-name-regexp t emacs-wiki-highlight-link)
1402
1403     ;; highlight any markup tags encountered
1404     (emacs-wiki-tag-regexp ?\< emacs-wiki-highlight-custom-tags))
1405   "Expressions to highlight an Emacs Wiki buffer.
1406 These are arranged in a rather special fashion, so as to be as quick as
1407 possible.
1408
1409 Each element of the list is itself a list, of the form:
1410
1411   (LOCATE-REGEXP TEST-CHAR MATCH-FUNCTION)
1412
1413 LOCATE-REGEXP is a partial regexp, and should be the smallest possible
1414 regexp to differentiate this rule from other rules.  It may also be a
1415 symbol containing such a regexp.  The buffer region is scanned only
1416 once, and LOCATE-REGEXP indicates where the scanner should stop to
1417 look for highlighting possibilities.
1418
1419 TEST-CHAR is a char or t.  The character should match the beginning
1420 text matched by LOCATE-REGEXP.  These chars are used to build a vector
1421 for fast MATCH-FUNCTION calling.
1422
1423 MATCH-FUNCTION is the function called when a region has been
1424 identified.  It is responsible for adding the appropriate text
1425 properties to change the appearance of the buffer.
1426
1427 This markup is used to modify the appearance of the original text to
1428 make it look more like the published HTML would look (like making some
1429 markup text invisible, inlining images, etc).
1430
1431 font-lock is used to apply the markup rules, so that they can happen
1432 on a deferred basis.  They are not always accurate, but you can use
1433 \\[font-lock-fontifty-block] near the point of error to force
1434 fontification in that area.
1435
1436 Lastly, none of the regexp should contain grouping elements that will
1437 affect the match data results."
1438   :type '(repeat
1439           (list :tag "Highlight rule"
1440                 (choice (regexp :tag "Locate regexp")
1441                         (symbol :tag "Regexp symbol"))
1442                 (choice (character :tag "Confirm character")
1443                         (const :tag "Default rule" t))
1444                 function))
1445   :set 'emacs-wiki-configure-highlighting
1446   :group 'emacs-wiki-highlight)
1447
1448 (defvar font-lock-mode nil)
1449 (defvar font-lock-multiline nil)
1450
1451 (defun emacs-wiki-use-font-lock ()
1452   (set (make-local-variable 'font-lock-multiline) 'undecided)
1453   (set (make-local-variable 'font-lock-defaults)
1454        `(nil t nil nil 'beginning-of-line
1455          (font-lock-fontify-region-function . emacs-wiki-highlight-region)
1456          (font-lock-unfontify-region-function
1457           . emacs-wiki-unhighlight-region)))
1458   (set (make-local-variable 'font-lock-fontify-region-function)
1459        'emacs-wiki-highlight-region)
1460   (set (make-local-variable 'font-lock-unfontify-region-function)
1461        'emacs-wiki-unhighlight-region)
1462   (font-lock-mode t))
1463
1464 (defun emacs-wiki-mode-flyspell-verify ()
1465   "Return t if the word at point should be spell checked."
1466   (let* ((word-pos (1- (point)))
1467          (props (text-properties-at word-pos)))
1468     (not (or (bobp)
1469              (memq 'display props)
1470              (if (and font-lock-mode (cadr (memq 'fontified props)))
1471                  (memq (cadr (memq 'face props))
1472                        '(emacs-wiki-link-face emacs-wiki-bad-link-face))
1473                (emacs-wiki-link-at-point word-pos))))))
1474
1475 (put 'emacs-wiki-mode 'flyspell-mode-predicate
1476      'emacs-wiki-mode-flyspell-verify)
1477
1478 (defun emacs-wiki-eval-lisp (form)
1479   "Evaluate the given form and return the result as a string."
1480   (require 'pp)
1481   (save-match-data
1482     (let ((object (eval (read form))))
1483       (cond
1484        ((stringp object) object)
1485        ((and (listp object)
1486              (not (eq object nil)))
1487         (let ((string (pp-to-string object)))
1488           (substring string 0 (1- (length string)))))
1489        ((numberp object)
1490         (number-to-string object))
1491        ((eq object nil) "")
1492        (t
1493         (pp-to-string object))))))
1494
1495 (defun emacs-wiki-highlight-buffer ()
1496   "Re-highlight the entire Wiki buffer."
1497   (interactive)
1498   (emacs-wiki-highlight-region (point-min) (point-max) t))
1499
1500 (defun emacs-wiki-highlight-region (beg end &optional verbose)
1501   "Apply highlighting according to `emacs-wiki-highlight-markup'.
1502 Note that this function should NOT change the buffer, nor should any
1503 of the functions listed in `emacs-wiki-highlight-markup'."
1504   (let ((buffer-undo-list t)
1505         (inhibit-read-only t)
1506         (inhibit-point-motion-hooks t)
1507         (inhibit-modification-hooks t)
1508         (modified-p (buffer-modified-p))
1509         deactivate-mark)
1510     (unwind-protect
1511         (save-excursion
1512           (save-restriction
1513             (widen)
1514             ;; check to see if we should expand the beg/end area for
1515             ;; proper multiline matches
1516             (when (and font-lock-multiline
1517                        (> beg (point-min))
1518                        (get-text-property (1- beg) 'font-lock-multiline))
1519               ;; We are just after or in a multiline match.
1520               (setq beg (or (previous-single-property-change
1521                              beg 'font-lock-multiline)
1522                             (point-min)))
1523               (goto-char beg)
1524               (setq beg (line-beginning-position)))
1525             (when font-lock-multiline
1526               (setq end (or (text-property-any end (point-max)
1527                                                'font-lock-multiline nil)
1528                             (point-max))))
1529             (goto-char end)
1530             (setq end (line-beginning-position 2))
1531             ;; Undo any fontification in the area.
1532             (font-lock-unfontify-region beg end)
1533             ;; And apply fontification based on `emacs-wiki-highlight-markup'
1534             (let ((len (float (- end beg)))
1535                   (case-fold-search nil))
1536               (goto-char beg)
1537               (while
1538                   (and (< (point) end)
1539                        (re-search-forward emacs-wiki-highlight-regexp end t))
1540                 (if verbose
1541                     (message "Highlighting buffer...%d%%"
1542                              (* (/ (float (- (point) beg)) len) 100)))
1543                 (funcall (aref emacs-wiki-highlight-vector
1544                                (char-after (match-beginning 0)))))
1545               (run-hook-with-args 'emacs-wiki-highlight-buffer-hook
1546                                   beg end verbose)
1547               (if verbose (message "Highlighting buffer...done")))))
1548       (set-buffer-modified-p modified-p))))
1549
1550 (defun emacs-wiki-unhighlight-region (begin end &optional verbose)
1551   "Remove all visual highlights in the buffer (except font-lock)."
1552   (let ((buffer-undo-list t)
1553         (inhibit-read-only t)
1554         (inhibit-point-motion-hooks t)
1555         (inhibit-modification-hooks t)
1556         (modified-p (buffer-modified-p))
1557         deactivate-mark)
1558     (unwind-protect
1559         (remove-text-properties
1560          begin end '(face nil font-lock-multiline nil
1561                           invisible nil intangible nil display nil
1562                           mouse-face nil keymap nil help-echo nil))
1563       (set-buffer-modified-p modified-p))))
1564
1565 (eval-when-compile
1566   (defvar end))
1567
1568 (defun emacs-wiki-multiline-maybe (beg end &optional predicate)
1569   "If region between beg-end is a multi-line region, and the optional
1570   predicate is true, font lock the current region as multi-line. Predicate is
1571   called with the excursion saved."
1572   (when (and (or (eq (char-before end) ?\n)
1573                  (> (count-lines beg end) 1))
1574              (or (not predicate)
1575                  (save-excursion (funcall predicate beg end))))
1576     (save-excursion
1577       ;; mark whole lines as a multiline font-lock
1578       (goto-char beg)
1579       (setq beg (line-beginning-position))
1580       (goto-char end)
1581       (setq end (line-end-position))
1582       (add-text-properties beg end '(font-lock-multiline t))
1583       t)))
1584
1585 (defvar emacs-wiki-keymap-property
1586   (if (or (featurep 'xemacs)
1587           (>= emacs-major-version 21))
1588       'keymap
1589     'local-map))
1590
1591 (defsubst emacs-wiki-link-properties (help-str &optional face)
1592   (append (if face
1593               (list 'face face 'rear-nonsticky t
1594                     emacs-wiki-keymap-property emacs-wiki-local-map)
1595             (list 'invisible t 'intangible t 'rear-nonsticky t
1596                   emacs-wiki-keymap-property emacs-wiki-local-map))
1597           (list 'mouse-face 'highlight
1598                 'help-echo help-str
1599                 emacs-wiki-keymap-property emacs-wiki-local-map)))
1600
1601 (defun emacs-wiki-highlight-link ()
1602   (if (eq ?\[ (char-after (match-beginning 0)))
1603       (if (and emacs-wiki-inline-images
1604                (save-match-data
1605                  (string-match emacs-wiki-image-regexp (match-string 4))))
1606           (emacs-wiki-inline-image (match-beginning 0) (match-end 0)
1607                                    (match-string 4) (match-string 6))
1608         (let* ((link (match-string-no-properties 4))
1609                (invis-props (emacs-wiki-link-properties link))
1610                (props (emacs-wiki-link-properties link 'emacs-wiki-link-face)))
1611           (if (match-string 6)
1612               (progn
1613                 (add-text-properties (match-beginning 0)
1614                                      (match-beginning 6) invis-props)
1615                 (add-text-properties (match-beginning 6) (match-end 6) props)
1616                 (add-text-properties (match-end 6) (match-end 0) invis-props))
1617             (add-text-properties (match-beginning 0)
1618                                  (match-beginning 4) invis-props)
1619             (add-text-properties (match-beginning 4) (match-end 0) props)
1620             (add-text-properties (match-end 4) (match-end 0) invis-props)))
1621         (goto-char (match-end 0)))
1622     (if (and emacs-wiki-inline-images
1623              (save-match-data
1624                (string-match emacs-wiki-image-regexp (match-string 0))))
1625         (emacs-wiki-inline-image (match-beginning 0) (match-end 0)
1626                                  (match-string 0))
1627       (add-text-properties
1628        (match-beginning 0) (match-end 0)
1629        (emacs-wiki-link-properties
1630         (match-string-no-properties 0)
1631         (if (let ((base (emacs-wiki-wiki-base (match-string 0))))
1632               (or (emacs-wiki-page-file base t)
1633                   (save-match-data
1634                     (string-match "\\(/\\|\\`[a-z]\\{3,6\\}:\\)" base))))
1635             'emacs-wiki-link-face
1636           'emacs-wiki-bad-link-face)))
1637       (goto-char (match-end 0)))))
1638
1639 (defun emacs-wiki-inline-image (beg end url &optional desc)
1640   "Inline locally available images."
1641   (let ((filename
1642          (cond
1643           ((string-match "\\`file:\\(.+\\)" url)
1644            (match-string 1 url))
1645           ((string-match "/" url)
1646            (expand-file-name url (symbol-value
1647                                   emacs-wiki-inline-relative-to))))))
1648     (if (and filename (file-readable-p filename))
1649         (add-text-properties beg end (list 'display (create-image filename)
1650                                            'help-echo (or desc url))))))
1651
1652 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1653 ;;
1654 ;; Emacs Wiki Publishing (to HTML by default)
1655 ;;
1656 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1657
1658 (defgroup emacs-wiki-publish nil
1659   "Options controlling the behaviour of Emacs Wiki publishing.
1660 See `emacs-wiki-publish' for more information."
1661   :group 'emacs-wiki)
1662
1663 (defcustom emacs-wiki-maintainer (concat "mailto:webmaster@" (system-name))
1664   "URL where the maintainer can be reached."
1665   :type 'string
1666   :group 'emacs-wiki-publish)
1667
1668 (defcustom emacs-wiki-home-page emacs-wiki-default-page
1669   "Title of the Wiki Home page."
1670   :type 'string
1671   :group 'emacs-wiki-publish)
1672
1673 (defcustom emacs-wiki-index-page "WikiIndex"
1674   "Title of the Wiki Index page."
1675   :type 'string
1676   :group 'emacs-wiki-publish)
1677
1678 (defcustom emacs-wiki-downcase-title-words
1679   '("the" "and" "at" "on" "of" "for" "in" "an" "a")
1680   "Strings that should be downcased in a Wiki page title."
1681   :type '(repeat string)
1682   :group 'emacs-wiki-publish)
1683
1684 (defcustom emacs-wiki-use-mode-flags (not emacs-wiki-under-windows-p)
1685   "If non-nil, use file mode flags to determine page permissions.
1686 Otherwise the regexps in `emacs-wiki-private-pages' and
1687 `emacs-wiki-editable-pages' are used."
1688   :type 'boolean
1689   :group 'emacs-wiki-publish)
1690
1691 (defcustom emacs-wiki-private-pages nil
1692   "A list of regexps to exclude from public view.
1693 This variable only applies if `emacs-wiki-use-mode-flags' is nil."
1694   :type '(choice (const nil) (repeat regexp))
1695   :group 'emacs-wiki-publish)
1696
1697 (defcustom emacs-wiki-editable-pages nil
1698   "A list of regexps of pages that may be edited via HTTP.
1699 This variable only applies if `emacs-wiki-use-mode-flags' is nil."
1700   :type '(choice (const nil) (repeat regexp))
1701   :group 'emacs-wiki-publish)
1702
1703 (defcustom emacs-wiki-publishing-directory "~/WebWiki"
1704   "Directory where all wikis are published to."
1705   :type 'directory
1706   :group 'emacs-wiki-publish)
1707
1708 (defcustom emacs-wiki-publishing-file-prefix ""
1709   "This prefix will be prepended to all wiki names when publishing."
1710   :type 'string
1711   :group 'emacs-wiki-publish)
1712
1713 (defcustom emacs-wiki-publishing-file-suffix ".html"
1714   "This suffix will be appended to all wiki names when publishing."
1715   :type 'string
1716   :group 'emacs-wiki-publish)
1717
1718 (defcustom emacs-wiki-before-markup-hook nil
1719   "A hook run in the buffer where markup is done, before it is done."
1720   :type 'hook
1721   :group 'emacs-wiki-publish)
1722
1723 (defcustom emacs-wiki-after-markup-hook nil
1724   "A hook run in the buffer where markup is done, after it is done."
1725   :type 'hook
1726   :group 'emacs-wiki-publish)
1727
1728 (defcustom emacs-wiki-meta-http-equiv "Content-Type"
1729   "The http-equiv attribute used for the HTML <meta> tag."
1730   :type 'string
1731   :group 'emacs-wiki-publish)
1732
1733 (defcustom emacs-wiki-meta-content-type "text/html"
1734   "The content type used for the HTML <meta> tag."
1735   :type 'string
1736   :group 'emacs-wiki-publish)
1737
1738 (defcustom emacs-wiki-meta-content-coding
1739   (if (featurep 'mule)
1740       'detect
1741     "iso-8859-1")
1742   "If set to the symbol 'detect, use `emacs-wiki-coding-map' to try
1743   and determine the HTML charset from emacs's coding. If set to a string, this
1744   string will be used to force a particular charset"
1745   :type '(choice string symbol)
1746   :group 'emacs-wiki-publish)
1747
1748 (defcustom emacs-wiki-charset-default "iso-8859-1"
1749   "The default HTML meta charset to use if no translation is found in
1750   `emacs-wiki-coding-map'"
1751   :type 'string
1752   :group 'emacs-wiki-publish)
1753
1754 (defcustom emacs-wiki-coding-default 'iso-8859-1
1755   "The default emacs coding  use if no special characters are found"
1756   :type 'symbol
1757   :group 'emacs-wiki-publish)
1758
1759 (defcustom emacs-wiki-coding-map
1760   '((iso-2022-jp "iso-2022-jp")
1761     (utf-8 "utf-8")
1762     (japanese-iso-8bit "euc-jp"))
1763   "An alist mapping emacs coding systems to appropriate HTML charsets.
1764   Use the base name of the coding system (ie, without the -unix)"
1765   :type '(alist :key-type coding-system :value-type (group string))
1766   :group 'emacs-wiki-publish)
1767
1768 (defcustom emacs-wiki-redirect-delay 1
1769   "The number of seconds to delay before doing a page redirect."
1770   :type 'integer
1771   :group 'emacs-wiki-publish)
1772
1773 (defvar emacs-wiki-current-page-title nil
1774   "Current page title, used instead of buffer name if non-nil.
1775 This is usually set by code called by `emacs-wiki-publishing-markup'.
1776 It should never be changed globally.")
1777
1778 (defcustom emacs-wiki-anchor-on-word nil
1779   "When true, anchors surround the closest word. This allows you
1780 to select them in a browser (ie, for pasting), but has the
1781 side-effect of marking up headers in multiple colours if your
1782 header style is different to your link style."
1783   :type 'boolean
1784   :group 'emacs-wiki-publish)
1785
1786 (defcustom emacs-wiki-publishing-header
1787   "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">
1788 <html>
1789   <head>
1790     <title><lisp>(emacs-wiki-page-title)</lisp></title>
1791     <meta name=\"generator\" content=\"emacs-wiki.el\">
1792     <meta http-equiv=\"<lisp>emacs-wiki-meta-http-equiv</lisp>\"
1793           content=\"<lisp>emacs-wiki-meta-content</lisp>\">
1794     <link rev=\"made\" href=\"<lisp>emacs-wiki-maintainer</lisp>\">
1795     <link rel=\"home\" href=\"<lisp>(emacs-wiki-published-name
1796                                      emacs-wiki-home-page)</lisp>\">
1797     <link rel=\"index\" href=\"<lisp>(emacs-wiki-published-name
1798                                       emacs-wiki-index-page)</lisp>\">
1799     <lisp>emacs-wiki-style-sheet</lisp>
1800   </head>
1801   <body>
1802     <h1><lisp>(emacs-wiki-page-title)</lisp></h1>
1803     <!-- Page published by Emacs Wiki begins here -->\n"
1804   "Text to prepend to a wiki being published.
1805 This text may contain <lisp> markup tags."
1806   :type 'string
1807   :group 'emacs-wiki-publish)
1808
1809 (defcustom emacs-wiki-publishing-footer
1810   "
1811     <!-- Page published by Emacs Wiki ends here -->
1812     <div class=\"navfoot\">
1813       <hr>
1814       <table width=\"100%\" border=\"0\" summary=\"Footer navigation\">
1815         <tr>
1816           <td width=\"33%\" align=\"left\">
1817             <lisp>
1818               (if buffer-file-name
1819                   (concat
1820                    \"<span class=\\\"footdate\\\">Updated: \"
1821                    (format-time-string emacs-wiki-footer-date-format
1822                     (nth 5 (file-attributes buffer-file-name)))
1823                    (and emacs-wiki-serving-p
1824                         (emacs-wiki-editable-p (emacs-wiki-page-name))
1825                         (concat
1826                          \" / \"
1827                          (emacs-wiki-link-href
1828                           (concat \"editwiki?\" (emacs-wiki-page-name))
1829                           \"Edit\")))
1830                    \"</span>\"))
1831             </lisp>
1832           </td>
1833           <td width=\"34%\" align=\"center\">
1834             <span class=\"foothome\">
1835               <lisp>
1836                 (concat
1837                  (and (emacs-wiki-page-file emacs-wiki-home-page t)
1838                       (not (emacs-wiki-private-p emacs-wiki-home-page))
1839                       (concat
1840                        (emacs-wiki-link-href emacs-wiki-home-page \"Home\")
1841                        \" / \"))
1842                  (emacs-wiki-link-href emacs-wiki-index-page \"Index\")
1843                  (and (emacs-wiki-page-file \"ChangeLog\" t)
1844                       (not (emacs-wiki-private-p \"ChangeLog\"))
1845                       (concat
1846                        \" / \"
1847                        (emacs-wiki-link-href \"ChangeLog\" \"Changes\"))))
1848               </lisp>
1849             </span>
1850           </td>
1851           <td width=\"33%\" align=\"right\">
1852             <lisp>
1853               (if emacs-wiki-serving-p
1854                   (concat
1855                    \"<span class=\\\"footfeed\\\">\"
1856                    (emacs-wiki-link-href \"searchwiki?get\" \"Search\")
1857                    (and buffer-file-name
1858                         (concat
1859                          \" / \"
1860                          (emacs-wiki-link-href
1861                           (concat \"searchwiki?q=\" (emacs-wiki-page-name))
1862                           \"Referrers\")))
1863                    \"</span>\"))
1864             </lisp>
1865           </td>
1866         </tr>
1867       </table>
1868     </div>
1869   </body>
1870 </html>\n"
1871   "Text to append to a wiki being published.
1872 This text may contain <lisp> markup tags."
1873   :type 'string
1874   :group 'emacs-wiki-publish)
1875
1876 (defcustom emacs-wiki-footer-date-format "%Y-%m-%d"
1877   "Format of current date for `emacs-wiki-publishing-footer'.
1878 This string must be a valid argument to `format-time-string'."
1879   :type 'string
1880   :group 'emacs-wiki-publish)
1881
1882 (defcustom emacs-wiki-style-sheet
1883   "<style type=\"text/css\">
1884 a.nonexistent {
1885   font-weight: bold;
1886   background-color: #F8F8F8; color: #FF2222;
1887 }
1888
1889 a.nonexistent:visited {
1890   background-color: #F8F8F8; color: #FF2222;
1891 }
1892
1893 body {
1894   background: white; color: black;
1895   margin-left: 5%; margin-right: 5%;
1896   margin-top: 3%;
1897 }
1898
1899 em { font-style: italic; }
1900 strong { font-weight: bold; }
1901
1902 ul { list-style-type: disc }
1903
1904 dl.contents { margin-top: 0; }
1905 dt.contents { margin-bottom: 0; }
1906
1907 p.verse {
1908   white-space: pre;
1909   margin-left: 5%;
1910 }
1911
1912 pre {
1913   white-space: pre;
1914   font-family: monospace;
1915   margin-left: 5%;
1916 }
1917 </style>"
1918   "The style sheet used for each wiki page.
1919 This can either be an inline stylesheet, using <style></style> tags,
1920 or an external stylesheet reference using a <link> tag.
1921
1922 Here is an example of using a <link> tag:
1923
1924   <link rel=\"stylesheet\" type=\"text/css\" href=\"emacs-wiki.css\">"
1925   :type 'string
1926   :group 'emacs-wiki-publish)
1927
1928 (defvar emacs-wiki-publishing-p nil
1929   "Set to t while Wiki pages are being published.
1930 This can be used by <lisp> tags to know when HTML is being generated.")
1931
1932 (defcustom emacs-wiki-block-groups-regexp
1933   "\\(h[1-9r]\\|[oud]l\\|table\\|center\\|blockquote\\|pre\\)[^>]*"
1934   "This regexp identifies HTML tag which defines their own blocks.
1935 That is, they do not need to be surrounded by <p>."
1936   :type 'regexp
1937   :group 'emacs-wiki-publish)
1938
1939 (defcustom emacs-wiki-table-attributes "border=\"2\" cellpadding=\"5\""
1940   "The attribute to be used with HTML <table> tags.
1941 Note that since emacs-wiki support direct insertion of HTML tags, you
1942 can easily create any kind of table you want, as long as every line
1943 begins at column 0 (to prevent it from being blockquote'd).  To make
1944 really ANYTHING you want, use this idiom:
1945
1946   <verbatim>
1947   <table>
1948     [... contents of my table, in raw HTML ...]
1949   </verbatim></table>
1950
1951 It may look strange to have the tags out of sequence, but remember
1952 that verbatim is processed long before table is even seen."
1953   :type 'string
1954   :group 'emacs-wiki-publish)
1955
1956 (defcustom emacs-wiki-report-threshhold 100000
1957   "If a Wiki file is this size or larger, report publishing progress."
1958   :type 'integer
1959   :group 'emacs-wiki-publish)
1960
1961 (defcustom emacs-wiki-publishing-markup
1962   (list
1963    ["&\\([-A-Za-z_#0-9]+\\);" 0 emacs-wiki-markup-entity]
1964
1965    ;; change the displayed title or the stylesheet for a given page
1966    ["\\`#\\(title\\|style\\)\\s-+\\(.+\\)\n+" 0
1967     emacs-wiki-markup-initial-directives]
1968
1969    ;; process any markup tags
1970    [emacs-wiki-tag-regexp 0 emacs-wiki-markup-custom-tags]
1971
1972    ;; emphasized or literal text
1973    ["\\(^\\|[-[ \t\n<('`\"]\\)\\(=[^= \t\n]\\|_[^_ \t\n]\\|\\*+[^* \t\n]\\)"
1974     2 emacs-wiki-markup-word]
1975
1976    ;; headings, outline-mode style
1977    ["^\\(\\*+\\)\\s-+" 0 emacs-wiki-markup-heading]
1978
1979    ;; define anchor points
1980    ["^#\\([A-Za-z0-9_%]+\\)\\s-*" 0 emacs-wiki-markup-anchor]
1981
1982    ;; horizontal rule, or section separator
1983    ["^----+" 0 "<hr>"]
1984
1985    ;; footnotes section is separated by a horizontal rule in HTML
1986    ["^\\(\\* \\)?Footnotes:?\\s-*" 0 "<hr>\n<p>\n"]
1987    ;; footnote definition/reference (def if at beginning of line)
1988    ["\\[\\([1-9][0-9]*\\)\\]" 0 emacs-wiki-markup-footnote]
1989
1990    ;; don't require newlines between numbered and unnumbered lists.
1991    ;; This must come before paragraphs are calculated, so that any
1992    ;; extra newlines added will be condensed.
1993    ["^\\s-*\\(-\\|[0-9]+\\.\\)" 1 "\n\\1"]
1994
1995    ;; the beginning of the buffer begins the first paragraph
1996    ["\\`\n*" 0 "<p>\n"]
1997    ;; plain paragraph separator
1998    ["\n\\([ \t]*\n\\)+" 0 "\n\n</p>\n\n<p>\n"]
1999
2000    ;; if table.el was loaded, allow for pretty tables.  otherwise only
2001    ;; simple table markup is supported, nothing fancy.  use | to
2002    ;; separate cells, || to separate header elements, and ||| for
2003    ;; footer elements
2004    (vector
2005     (if (featurep 'table)
2006         "^\\(\\s-*\\)\\(\\+[-+]+\\+[\n\r \t]+|\\)"
2007       "^\\s-*\\(\\([^|\n]+\\(|+\\)\\s-*\\)+\\)\\([^|\n]+\\)?$")
2008     1 'emacs-wiki-markup-table)
2009
2010    ;; unnumbered List items begin with a -.  numbered list items
2011    ;; begin with number and a period.  definition lists have a
2012    ;; leading term separated from the body with ::.  centered
2013    ;; paragraphs begin with at least six columns of whitespace; any
2014    ;; other whitespace at the beginning indicates a blockquote.  The
2015    ;; reason all of these rules are handled here, is so that
2016    ;; blockquote detection doesn't interfere with indented list
2017    ;; members.
2018    ["^\\(\\s-*\\(-\\|[0-9]+\\.\\|\\(.+\\)[ \t]+::\n?\\)\\)?\\([ \t]+\\)" 4
2019     emacs-wiki-markup-list-or-paragraph]
2020
2021    ;; "verse" text is indicated the same way as a quoted e-mail
2022    ;; response: "> text", where text may contain initial whitespace
2023    ;; (see below).
2024    ["<p>\\s-+> \\(\\([^\n]\n?\\)+\\)\\(\\s-*</p>\\)?" 0
2025     emacs-wiki-markup-verse]
2026
2027    ;; join together the parts of a list
2028    ["</\\([oud]l\\)>\\s-*\\(</p>\\s-*<p>\\s-*\\)?<\\1>\\s-*" 0 ""]
2029
2030    ;; join together the parts of a table
2031    (vector
2032     (concat "</tbody>\\s-*"
2033             "</table>\\s-*" "\\(</p>\\s-*<p>\\s-*\\)?" "<table[^>]*>\\s-*"
2034             "<tbody>\\s-*") 0 "")
2035    ["</table>\\s-*\\(</p>\\s-*<p>\\s-*\\)?<table[^>]*>\\s-*" 0 ""]
2036
2037    ;; fixup paragraph delimiters
2038    (vector
2039     (concat "<p>\\s-*\\(</?" emacs-wiki-block-groups-regexp ">\\)") 0 "\\1")
2040    (vector (concat "\\(</?" emacs-wiki-block-groups-regexp
2041                    ">\\)\\s-*\\(</p>\\)") 3 "\\1")
2042
2043    ;; terminate open paragraph at the end of the buffer
2044    ["<p>\\s-*\\'" 0 ""]
2045    ;; make sure to close any open text (paragraphs)
2046    ["\\([^> \t\n]\\)\\s-*\\'" 0 "\\1\n</p>"]
2047    ;; lists have no whitespace after them, so add a final linebreak
2048    ["\\(</[oud]l>\\)\\(\\s-*\\(<hr>\\|\\'\\)\\)" 0 "\\1\n<br>\\2"]
2049
2050    ;; replace WikiLinks in the buffer (links to other pages)
2051    ;; <nop> before a WikiName guards it from being replaced
2052    ;; '''' can be used to add suffixes, such as WikiName''''s
2053    [emacs-wiki-url-or-name-regexp 0 emacs-wiki-markup-link]
2054    ["''''" 0 ""]
2055
2056    ;; bare email addresses
2057    (vector
2058     (concat
2059      "\\([^:.@/a-zA-Z0-9]\\)"
2060      "\\([-a-zA-Z0-9._]+@\\([-a-zA-z0-9_]+\\.\\)+[a-zA-Z0-9]+\\)"
2061      "\\([^\"a-zA-Z0-9]\\)")
2062     0
2063     "\\1<a href=\"mailto:\\2\">\\2</a>\\4")
2064
2065    ;; replace quotes, since most browsers don't understand `` and ''
2066    ["\\(``\\|''\\)" 0 "\""]
2067
2068    ;; insert the default publishing header
2069    (function
2070     (lambda ()
2071       (insert emacs-wiki-publishing-header)))
2072
2073    ;; insert the default publishing footer
2074    (function
2075     (lambda ()
2076       (goto-char (point-max))
2077       (insert emacs-wiki-publishing-footer)))
2078
2079    ;; process any remaining markup tags
2080    [emacs-wiki-tag-regexp 0 emacs-wiki-markup-custom-tags])
2081   "List of markup rules to apply when publishing a Wiki page.
2082 Each member of the list is either a function, or a vector of the form:
2083
2084   [REGEXP/SYMBOL TEXT-BEGIN-GROUP REPLACEMENT-TEXT/FUNCTION/SYMBOL].
2085
2086 REGEXP is a regular expression, or symbol whose value is a regular
2087 expression, which is searched for using `re-search-forward'.
2088 TEXT-BEGIN-GROUP is the matching group within that regexp which
2089 denotes the beginning of the actual text to be marked up.
2090 REPLACEMENT-TEXT is a string that will be passed to `replace-match'.
2091 If it is not a string, but a function, it will be called to determine
2092 what the replacement text should be (it must return a string).  If it
2093 is a symbol, the value of that symbol should be a string.
2094
2095 The replacements are done in order, one rule at a time.  Writing the
2096 regular expressions can be a tricky business.  Note that case is never
2097 ignored.  `case-fold-search' is always be bound to nil while
2098 processing the markup rules.
2099
2100 Here is a description of the default markup rules:
2101
2102 Headings
2103
2104  * First level
2105  ** Second level
2106  *** Third level
2107
2108  Note that the first level is actually indicated using H2, so that
2109  it doesn't appear at the same level as the page heading (which
2110  conceptually titles the section of that Wiki page).
2111
2112 Horizontal rules
2113
2114 ----
2115
2116 Emphasis
2117
2118  *emphasis*
2119  **strong emphasis**
2120  ***very strong emphasis***
2121  _underlined text_
2122  =verbatim=
2123
2124  <verbatim>This tag should be used for larger blocks of
2125  text</verbatim>.
2126
2127 Footnotes
2128
2129   A reference[1], which is just a number in square brackets,
2130   constitutes a footnote reference.
2131
2132   Footnotes:
2133
2134   [1]  Footnotes are defined by the same number in brackets
2135        occurring at the beginning of a line.  Use footnote-mode's C-c
2136        ! a command, to very easily insert footnotes while typing.  Use
2137        C-x C-x to return to the point of insertion.
2138
2139 Paragraphs
2140
2141   One or more blank lines separates paragraphs.
2142
2143 Centered paragraphs and quotations
2144
2145   A line that begins with six or more columns of whitespace (made up
2146   of tabs or spaces) indicates a centered paragraph.  I assume this
2147   because it's expected you will use M-s to center the line, which
2148   usually adds a lot of whitespace before it.
2149
2150   If a line begins with some whitespace, but less than six columns, it
2151   indicates a quoted paragraph.
2152
2153 Poetic verse
2154
2155   Poetry requires that whitespace be preserved, without resorting to
2156   the monospace typical of <pre>.  For this, the following special
2157   markup exists, which is reminiscent of e-mail quotations:
2158
2159     > A line of Emacs verse;
2160     > forgive its being so terse.
2161
2162   You can also use the <verse> tag, if you prefer:
2163
2164     <verse>
2165     A line of Emacs verse;
2166     forgive its being so terse.
2167     </verse>
2168
2169 Literal paragraphs
2170
2171   Use the HTML tags <pre></pre> to insert a paragraph and preserve
2172   whitespace.  If you're inserting a block of code, you will almost
2173   always want to use <verbatim></verbatim> *within* the <pre> tags.
2174   The shorcut for doing this is to use the <example> tag:
2175
2176     <example>
2177     Some literal text or code here.
2178     </example>
2179
2180 Lists
2181
2182   - bullet list
2183
2184   1. Enumerated list
2185
2186   Term :: A definition list
2187
2188   Blank lines between list elements are optional, but required between
2189   members of a definition list.
2190
2191 Tables
2192
2193   There are two forms of table markup supported.  If Takaaki Ota's
2194   table.el package is available, then simply create your tables using
2195   his package, and they will be rendered into the appropriate HTML.
2196
2197   If table.el is not available, then only very simple table markup is
2198   supported.  The attributes of the table are kept in
2199   `emacs-wiki-table-attributes'.  The syntax is:
2200
2201     Double bars || Separate header fields
2202     Single bars | Separate body fields
2203     Here are more | body fields
2204     Triple bars ||| Separate footer fields
2205
2206   Other paragraph markup applies to both styles, meaning that if six
2207   or more columns of whitespace precedes the first line of the table,
2208   it will be centered, and if any whitespace at all precedes first
2209   line, it will occur in a blockquote.
2210
2211 Anchors and tagged links
2212
2213   #example If you begin a line with \"#anchor\" -- where anchor
2214   can be any word that doesn't contain whitespace -- it defines an
2215   anchor at that point into the document.  This anchor text is not
2216   displayed.
2217
2218   You can reference an anchored point in another page (or even in the
2219   current page) using WikiName#anchor.  The #anchor will never be
2220   displayed in HTML, whether at the point of definition or reference,
2221   but it will cause browsers to jump to that point in the document.
2222
2223 Redirecting to another page or URL
2224
2225   Sometimes you may wish to redirect someone to another page.  To do
2226   this, put:
2227
2228     <redirect url=\"http://somewhereelse.com\"/>
2229
2230   at the top of the page.  If the <redirect> tag specifies content,
2231   this will be used as the redirection message, rather than the
2232   default.
2233
2234   The numbers of seconds to delay is defined by
2235   `emacs-wiki-redirect-delay', which defaults to 2 seconds.  The page
2236   shown will also contain a link to click on, for browsing which do
2237   not support automatic refreshing.
2238
2239 URLs
2240
2241   A regular URL is given as a link.  If it's an image URL, it will
2242   be inlined using an IMG tag.
2243
2244 Embedded lisp
2245
2246   <lisp>(concat \"This form gets\" \"inserted\")</lisp>
2247
2248 Special handling of WikiNames
2249
2250   If you need to add a plural at the end of a WikiName, separate it
2251   with four single quotes: WikiName''''s.
2252
2253   To prevent a link name (of any type) from being treated as such,
2254   surround it with =equals= (to display it in monotype), or prefix it
2255   with the tag <nop>.
2256
2257 Special Wiki links
2258
2259   Besides the normal WikiName type links, emacs-wiki also supports
2260   extended links:
2261
2262     [[link text][optional link description]]
2263
2264   An extended link is always a link, no matter how it looks.  This
2265   means you can use any file in your `emacs-wiki-directories' as a
2266   Wiki file.  If you provide an optional description, that's what will
2267   be shown instead of the link text.  This is very useful for
2268   providing textual description of URLs.
2269
2270   See the documentation to emacs-wiki-image-regexp for how to inline
2271   files and images.
2272
2273 InterWiki names
2274
2275   There are times when you will want to constantly reference pages on
2276   another website.  Rather than repeating the URL ad nauseum, you can
2277   define an InterWiki name.  This is a set of WikiNames to URL
2278   correlations, that support textual substitution using #anchor names
2279   (which are appended to the URL).  For example, MeatballWiki is
2280   defined in the variable `emacs-wiki-interwiki-names'.  It means you
2281   can reference the page \"MeatBall\" on MeatballWiki using this
2282   syntax:
2283
2284     MeatballWiki#MeatBall
2285
2286   In the resulting HTML, the link is simply shown as
2287   \"MeatballWiki:MeatBall\"."
2288   :type '(repeat
2289           (choice
2290            (vector :tag "Markup rule"
2291                    (choice regexp symbol)
2292                    integer
2293                    (choice string function symbol))
2294            function))
2295   :group 'emacs-wiki-publish)
2296
2297 (defcustom emacs-wiki-changelog-markup
2298   (list
2299    ;; process any custom markup tags
2300    [emacs-wiki-tag-regexp 0 emacs-wiki-markup-custom-tags]
2301
2302    ["&" 0 "&amp;"]
2303    ["<" 0 "&lt;"]
2304    [">" 0 "&gt;"]
2305
2306    ["^\\(\\S-+\\)\\s-+\\(.+\\)" 0 emacs-wiki-markup-changelog-section]
2307
2308    ;; emphasized or literal text
2309    ["\\(^\\|[-[ \t\n<('`\"]\\)\\(=[^= \t\n]\\|_[^_ \t\n]\\|\\*+[^* \t\n]\\)"
2310     2 emacs-wiki-markup-word]
2311
2312    ;; headings, outline-mode style
2313    ["^\\*\\s-+\\(.+\\)$" 0 "<h2>\\1</h2>"]
2314
2315    ;; escape the 'file' entries, incase they are extended wiki links
2316    ["^[ \t]+\\* \\([^:(]+\\)\\([ \t]+(\\|:\\)" 0 emacs-wiki-changelog-escape-files]
2317
2318    ;; don't require newlines between unnumbered lists.
2319    ["^\\s-*\\(\\*\\)" 1 "\n\\1"]
2320
2321    ;; the beginning of the buffer begins the first paragraph
2322    ["\\`\n*" 0 "<p>\n"]
2323    ;; plain paragraph separator
2324    ["\n\\([ \t]*\n\\)+" 0 "\n\n</p>\n\n<p>\n"]
2325
2326    ;; unnumbered List items begin with a -.  numbered list items
2327    ;; begin with number and a period.  definition lists have a
2328    ;; leading term separated from the body with ::.  centered
2329    ;; paragraphs begin with at least six columns of whitespace; any
2330    ;; other whitespace at the beginning indicates a blockquote.  The
2331    ;; reason all of these rules are handled here, is so that
2332    ;; blockquote detection doesn't interfere with indented list
2333    ;; members.
2334    ["^\\(\\s-*\\(\\*\\)\\)?\\([ \t]+\\)\\(\\([^\n]\n?\\)+\\)" 3
2335     "<ul>\n<li>\\4</ul>\n"]
2336
2337    ;; join together the parts of a list
2338    ["</\\([oud]l\\)>\\s-*\\(</p>\\s-*<p>\\s-*\\)?<\\1>\\s-*" 0 ""]
2339
2340    ;; fixup paragraph delimiters
2341    (vector
2342     (concat "<p>\\s-*\\(</?" emacs-wiki-block-groups-regexp ">\\)") 0 "\\1")
2343    (vector (concat "\\(</?" emacs-wiki-block-groups-regexp
2344                    ">\\)\\s-*\\(</p>\\)") 3 "\\1")
2345
2346    ;; terminate open paragraph at the end of the buffer
2347    ["<p>\\s-*\\'" 0 ""]
2348    ;; make sure to close any open text (paragraphs)
2349    ["\\([^> \t\n]\\)\\s-*\\'" 0 "\\1\n</p>"]
2350    ;; lists have no whitespace after them, so add a final linebreak
2351    ["\\(</[oud]l>\\)\\(\\s-*\\(<hr>\\|\\'\\)\\)" 0 "\\1\n<br>\\2"]
2352
2353    ;; bare email addresses
2354    (vector
2355     (concat
2356      "\\([^:.@/a-zA-Z0-9]\\)"
2357      "\\([-a-zA-Z0-9._]+@\\([-a-zA-z0-9_]+\\.\\)+[a-zA-Z0-9]+\\)"
2358      "\\([^\"a-zA-Z0-9]\\)")
2359     0
2360     "\\1<a href=\"mailto:\\2\">\\2</a>\\4")
2361
2362    ;; replace WikiLinks in the buffer (links to other pages)
2363    [emacs-wiki-url-or-name-regexp 0 emacs-wiki-markup-link]
2364    ["''''" 0 ""]
2365
2366    ;; insert the default publishing header
2367    (function
2368     (lambda ()
2369       (insert emacs-wiki-publishing-header)))
2370
2371    ;; insert the default publishing footer
2372    (function
2373     (lambda ()
2374       (goto-char (point-max))
2375       (insert emacs-wiki-publishing-footer))))
2376   "List of markup rules for publishing ChangeLog files.
2377 These are used when the wiki page's name is ChangeLog."
2378   :type '(repeat
2379           (choice
2380            (vector :tag "Markup rule"
2381                    (choice regexp symbol)
2382                    integer
2383                    (choice string function symbol))
2384            function))
2385   :group 'emacs-wiki-publish)
2386
2387 (defun emacs-wiki-transform-content-type (content-type)
2388   "Using `emacs-wiki-coding-map', try and resolve an emacs coding
2389   system to an associated HTML coding system. If no match is found,
2390   `emacs-wiki-charset-default' is used instead."
2391   (let ((match (assoc (coding-system-base content-type)
2392                       emacs-wiki-coding-map)))
2393     (if match
2394         (cadr match)
2395       emacs-wiki-charset-default)))
2396
2397 (defun emacs-wiki-private-p (name)
2398   "Return non-nil if NAME is a private page, and shouldn't be published."
2399   (if name
2400       (if emacs-wiki-use-mode-flags
2401           (let* ((page-file (emacs-wiki-page-file name t))
2402                  (filename (and page-file (file-truename page-file))))
2403             (if filename
2404                 (or (eq ?- (aref (nth 8 (file-attributes
2405                                          (file-name-directory filename))) 7))
2406                     (eq ?- (aref (nth 8 (file-attributes filename)) 7)))))
2407         (let ((private-pages emacs-wiki-private-pages) private)
2408           (while private-pages
2409             (if (string-match (car private-pages) name)
2410                 (setq private t private-pages nil)
2411               (setq private-pages (cdr private-pages))))
2412           private))))
2413
2414 (defun emacs-wiki-editable-p (name)
2415   "Return non-nil if NAME is a page that may be publically edited.
2416 If the page does not exist, the page will be created if: mode flags
2417 are not being checked, and it is a page listed in
2418 `emacs-wiki-editable-pages', or the first directory in
2419 `emacs-wiki-directories' is writable.  In either case, the new page
2420 will be created in the first directory in `emacs-wiki-directories'."
2421   (if (and name emacs-wiki-http-support-editing)
2422       (if emacs-wiki-use-mode-flags
2423           (let ((filename
2424                  (file-truename
2425                   (or (emacs-wiki-page-file name t)
2426                       (expand-file-name name (car emacs-wiki-directories))))))
2427             (if (file-exists-p filename)
2428                 (eq ?w (aref (nth 8 (file-attributes filename)) 8))
2429               (eq ?w (aref (nth 8 (file-attributes
2430                                    (file-name-directory filename))) 8))))
2431         (let ((editable-pages emacs-wiki-editable-pages) editable)
2432           (while editable-pages
2433             (if (string-match (car editable-pages) name)
2434                 (setq editable t editable-pages nil)
2435               (setq editable-pages (cdr editable-pages))))
2436           editable))))
2437
2438 (defun emacs-wiki-visit-published-file (&optional arg)
2439   "Visit the current wiki page's published result."
2440   (interactive "P")
2441   (if arg
2442       (find-file-other-window (emacs-wiki-published-file))
2443     (funcall emacs-wiki-browse-url-function
2444              (concat "file:" (emacs-wiki-published-file)))))
2445
2446 (defun emacs-wiki-dired-publish ()
2447   "Publish all marked files in a dired buffer."
2448   (interactive)
2449   (emacs-wiki-publish-files (dired-get-marked-files) t))
2450
2451 (defun emacs-wiki-prettify-title (title)
2452   "Prettify the given TITLE."
2453   (save-match-data
2454     (let ((case-fold-search nil))
2455       (while (string-match "\\([A-Za-z]\\)\\([A-Z0-9]\\)" title)
2456         (setq title (replace-match "\\1 \\2" t nil title)))
2457       (let* ((words (split-string title))
2458              (w (cdr words)))
2459         (while w
2460           (if (member (downcase (car w))
2461                       emacs-wiki-downcase-title-words)
2462               (setcar w (downcase (car w))))
2463           (setq w (cdr w)))
2464         (mapconcat 'identity words " ")))))
2465
2466 (defun emacs-wiki-publish (&optional arg)
2467   "Publish all wikis that need publishing.
2468 If the published wiki already exists, it is only overwritten if the
2469 wiki is newer than the published copy.  When given the optional
2470 argument ARG, all wikis are rewritten, no matter how recent they are.
2471 The index file is rewritten no matter what."
2472   (interactive "P")
2473   ;; prompt to save any emacs-wiki buffers
2474   (save-some-buffers nil (lambda ()
2475                            (eq major-mode 'emacs-wiki-mode)))
2476   ;; ensure the publishing location is available
2477   (unless (file-exists-p emacs-wiki-publishing-directory)
2478     (message "Creating publishing directory %s"
2479              emacs-wiki-publishing-directory)
2480     (make-directory emacs-wiki-publishing-directory))
2481   (if (emacs-wiki-publish-files
2482        (let* ((names (emacs-wiki-file-alist))
2483               (files (list t))
2484               (lfiles files))
2485          (while names
2486            (setcdr lfiles (cons (cdar names) nil))
2487            (setq lfiles (cdr lfiles)
2488                  names (cdr names)))
2489          (cdr files)) arg)
2490       ;; republish the index if any pages were published
2491       (with-current-buffer (emacs-wiki-generate-index t t)
2492         (emacs-wiki-replace-markup emacs-wiki-index-page)
2493         (let ((backup-inhibited t))
2494           (write-file (emacs-wiki-published-file emacs-wiki-index-page)))
2495         (kill-buffer (current-buffer))
2496         (message "All Wiki pages%s have been published."
2497                  (if emacs-wiki-current-project
2498                      (concat " for project " emacs-wiki-current-project)
2499                    "")))
2500     (message "No Wiki pages%s need publishing at this time."
2501              (if emacs-wiki-current-project
2502                  (concat " in project " emacs-wiki-current-project)
2503                ""))))
2504
2505 (defun emacs-wiki-publish-this-page ()
2506   "Force publication of the current page."
2507   (interactive)
2508   (emacs-wiki-publish-files (list buffer-file-name) t))
2509
2510 (defun emacs-wiki-publish-files (files force)
2511   "Publish all files in list FILES.
2512 If the argument FORCE is nil, each file is only published if it is
2513 newer than the published version.  If the argument FORCE is non-nil,
2514 the file is published no matter what."
2515   (let (published-some file page published)
2516     (while files
2517       (setq file (car files)
2518             files (cdr files)
2519             page (emacs-wiki-page-name file)
2520             published (emacs-wiki-published-file page))
2521       (if (and (not (emacs-wiki-private-p page))
2522                (or force (file-newer-than-file-p file published)))
2523           (with-temp-buffer
2524             (insert-file-contents file t)
2525             (cd (file-name-directory file))
2526             (emacs-wiki-maybe)
2527             (emacs-wiki-replace-markup)
2528             (let ((backup-inhibited t)
2529                   (buffer-file-coding-system
2530                    (when (boundp 'buffer-file-coding-system)
2531                      buffer-file-coding-system)))
2532               (when (eq buffer-file-coding-system 'undecided-unix)
2533                 ;; make it agree with the default charset
2534                 (setq buffer-file-coding-system
2535                       emacs-wiki-coding-default))
2536               (write-file published))
2537             (setq published-some t))))
2538     published-some))
2539
2540
2541
2542 (defun emacs-wiki-escape-html-specials (&optional end)
2543   (while (and (or (< (point) end) (not end))
2544               (re-search-forward "[<>&\"]" end t))
2545     (cond
2546      ((eq (char-before) ?\")
2547       (delete-char -1)
2548       (insert "&quot;"))
2549      ((eq (char-before) ?\<)
2550       (delete-char -1)
2551       (insert "&lt;"))
2552      ((eq (char-before) ?\>)
2553       (delete-char -1)
2554       (insert "&gt;"))
2555      ((eq (char-before) ?\&)
2556       (delete-char -1)
2557       (insert "&amp;")))))
2558
2559 ;; we currently only do this on links. this means a stray '&' in an
2560 ;; emacs-wiki document risks being misinterpreted when being published, but
2561 ;; this is the price we pay to be able to inline HTML content without special
2562 ;; tags.
2563 (defun emacs-wiki-escape-html-string (str)
2564   "Convert to character entities any non alphanumeric characters outside of a
2565   few punctuation symbols, that risk being misinterpreted if not escaped"
2566   (when str
2567     (let (pos code len)
2568       (save-match-data
2569         (while (setq pos (string-match "[^-[:alnum:]/:._=@\\?~#]" str pos))
2570           (setq code (int-to-string (aref str pos))
2571                 len (length code)
2572                 str (replace-match (concat "&#" code ";") nil nil str)
2573                 pos (+ 3 len pos)))
2574         str))))
2575
2576 (defun emacs-wiki-replace-markup (&optional title)
2577   "Replace markup according to `emacs-wiki-publishing-markup'."
2578   (let* ((emacs-wiki-meta-http-equiv emacs-wiki-meta-http-equiv)
2579          (emacs-wiki-current-page-title title)
2580          (emacs-wiki-publishing-p t)
2581          (case-fold-search nil)
2582          (inhibit-read-only t)
2583          (rules (if (string= (emacs-wiki-page-name) "ChangeLog")
2584                     emacs-wiki-changelog-markup
2585                   emacs-wiki-publishing-markup))
2586          (limit (* (length rules) (point-max)))
2587          (verbose (and emacs-wiki-report-threshhold
2588                        (> (point-max) emacs-wiki-report-threshhold)))
2589          (base 0)
2590          (emacs-wiki-meta-content
2591           (concat emacs-wiki-meta-content-type "; charset="
2592                   (if (stringp emacs-wiki-meta-content-coding)
2593                       emacs-wiki-meta-content-coding
2594                     (emacs-wiki-transform-content-type
2595                      (or buffer-file-coding-system
2596                          emacs-wiki-coding-default))))))
2597     (run-hooks 'emacs-wiki-before-markup-hook)
2598     (while rules
2599       (goto-char (point-min))
2600       (if (functionp (car rules))
2601           (funcall (car rules))
2602         (let ((regexp (aref (car rules) 0))
2603               (group (aref (car rules) 1))
2604               (replacement (aref (car rules) 2))
2605               start last-pos pos)
2606           (if (symbolp regexp)
2607               (setq regexp (symbol-value regexp)))
2608           (if verbose
2609               (message "Publishing %s...%d%%"
2610                        (emacs-wiki-page-name)
2611                        (* (/ (float (+ (point) base)) limit) 100)))
2612           (while (and regexp (setq pos (re-search-forward regexp nil t)))
2613             (if verbose
2614                 (message "Publishing %s...%d%%"
2615                          (emacs-wiki-page-name)
2616                          (* (/ (float (+ (point) base)) limit) 100)))
2617             (unless (get-text-property (match-beginning group) 'read-only)
2618               (let ((text (cond
2619                            ((functionp replacement)
2620                             (funcall replacement))
2621                            ((symbolp replacement)
2622                             (symbol-value replacement))
2623                            (t replacement))))
2624                 (when text
2625                   (condition-case nil
2626                       (replace-match text t)
2627                     (error
2628                      (replace-match "[FIXME: invalid characters]" t))))))
2629                 (if (and last-pos (= pos last-pos))
2630                     (if (eobp)
2631                         (setq regexp nil)
2632                       (forward-char 1)))
2633                 (setq last-pos pos))))
2634           (setq rules (cdr rules)
2635                 base (+ base (point-max))))
2636         (run-hooks 'emacs-wiki-after-markup-hook)
2637         (if verbose
2638             (message "Publishing %s...done" (emacs-wiki-page-name)))))
2639
2640 (defun emacs-wiki-custom-tags (&optional highlight-p)
2641   (let ((tag-info (or (assoc (match-string 1) emacs-wiki-markup-tags)
2642                       (assoc (match-string 1) emacs-wiki-dangerous-tags))))
2643     (when (and tag-info (or (not highlight-p)
2644                             (nth 3 tag-info)))
2645       (let ((closed-tag (match-string 3))
2646             (start (match-beginning 0))
2647             (beg (point)) end attrs)
2648         (when (nth 2 tag-info)
2649           (let ((attrstr (match-string 2)))
2650             (while (and attrstr
2651                         (string-match
2652                          "\\([^ \t\n=]+\\)\\(=\"\\([^\"]+\\)\"\\)?" attrstr))
2653               (let ((attr (cons (downcase
2654                                  (match-string-no-properties 1 attrstr))
2655                                 (match-string-no-properties 3 attrstr))))
2656                 (setq attrstr (replace-match "" t t attrstr))
2657                 (if attrs
2658                     (nconc attrs (list attr))
2659                   (setq attrs (list attr)))))))
2660         (if (and (cadr tag-info) (not closed-tag))
2661             (if (search-forward (concat "</" (car tag-info) ">") nil t)
2662                 (unless highlight-p
2663                   (delete-region (match-beginning 0) (point)))
2664               (setq tag-info nil)))
2665         (when tag-info
2666           (setq end (point-marker))
2667           (unless highlight-p
2668             (delete-region start beg))
2669           (goto-char (if highlight-p beg start))
2670           (let ((args (list start end)))
2671             (if (nth 2 tag-info)
2672                 (nconc args (list attrs)))
2673             (if (nth 3 tag-info)
2674                 (nconc args (list highlight-p)))
2675             (apply (nth 4 tag-info) args))))))
2676   nil)
2677
2678 (defun emacs-wiki-markup-initial-directives ()
2679   (cond
2680    ((string= (match-string 1) "title")
2681     (set (make-local-variable 'emacs-wiki-current-page-title) (match-string 2)))
2682    (t ;; "style"
2683     (set (make-local-variable 'emacs-wiki-style-sheet)
2684           (concat "<link rel=\"stylesheet\" type=\"text/css\" href=\""
2685                   (match-string 2) "\">"))))
2686   "")
2687
2688 (defalias 'emacs-wiki-markup-custom-tags 'emacs-wiki-custom-tags)
2689
2690 (defun emacs-wiki-highlight-title ()
2691   (add-text-properties (+ 7 (match-beginning 0))
2692                        (line-end-position)
2693                        '(face emacs-wiki-header-1)))
2694
2695 (defun emacs-wiki-highlight-custom-tags ()
2696   ;; Remove the match-data related to the url-or-name-regexp, which is
2697   ;; part of emacs-wiki-highlight-regexp.  All in the name of speed.
2698   (let ((match-data (match-data)))
2699     (setcdr (cdr match-data)
2700             (nthcdr (* 2 (+ 2 emacs-wiki-url-or-name-regexp-group-count))
2701                     match-data))
2702     (set-match-data match-data)
2703     (emacs-wiki-custom-tags t)))
2704
2705 (defun emacs-wiki-example-tag (beg end highlight-p)
2706   (if highlight-p
2707       (progn
2708         (emacs-wiki-multiline-maybe beg end)
2709         (goto-char end))
2710     (insert "<pre class=\"example\">")
2711     (emacs-wiki-escape-html-specials end)
2712     (when (< (point) end)
2713       (goto-char end))
2714     (insert "</pre>")
2715     (add-text-properties beg (point) '(rear-nonsticky (read-only)
2716                                                       read-only t))))
2717
2718 (defun emacs-wiki-verbatim-tag (beg end highlight-p)
2719   (if highlight-p
2720       (progn
2721         (emacs-wiki-multiline-maybe beg end)
2722         (goto-char end))
2723     (emacs-wiki-escape-html-specials end)
2724     (add-text-properties beg end '(rear-nonsticky (read-only)
2725                                                   read-only t))))
2726
2727 (defun emacs-wiki-nowiki-tag (beg end highlight-p)
2728   (if highlight-p
2729       (goto-char end)
2730     (add-text-properties
2731      beg end '(read-nonsticky (read-only) read-only t))))
2732
2733 (defun emacs-wiki-verse-tag (beg end)
2734   (save-excursion
2735     (while (< (point) end)
2736       (unless (eq (char-after) ?\n)
2737         (insert "> "))
2738       (forward-line))))
2739
2740 (defvar emacs-wiki-numbered-counter 1)
2741 (make-variable-buffer-local 'emacs-wiki-numbered-counter)
2742
2743 (defun emacs-wiki-numbered-tag (beg end)
2744   (save-excursion
2745     (goto-char beg)
2746     (setq end (copy-marker (1- end)))
2747     (insert "<table cellspacing=\"8\">")
2748     (insert (format "<tr><td valign=\"top\"><strong>%d</strong></td>
2749 <td><p><a name=\"%d\"/>" emacs-wiki-numbered-counter
2750                          emacs-wiki-numbered-counter))
2751     (setq emacs-wiki-numbered-counter
2752           (1+ emacs-wiki-numbered-counter))
2753     (while (and (< (point) end)
2754                 (re-search-forward "^$" end t))
2755       (replace-match (format "</p>
2756 </td>
2757 </tr><tr><td valign=\"top\"><strong>%d</strong></td><td>
2758 <p><a name=\"%d\"/>" emacs-wiki-numbered-counter
2759                      emacs-wiki-numbered-counter))
2760       (setq emacs-wiki-numbered-counter
2761             (1+ emacs-wiki-numbered-counter)))
2762     (goto-char end)
2763     (insert (format "</p>
2764 </td></tr></table>" (1+ emacs-wiki-numbered-counter)))))
2765
2766 (defun emacs-wiki-redirect-tag (beg end attrs)
2767   (let ((link (cdr (assoc "url" attrs))))
2768     (when link
2769       (setq emacs-wiki-meta-http-equiv "Refresh"
2770             emacs-wiki-meta-content
2771             (concat (or (cdr (assoc "delay" attrs))
2772                         (int-to-string emacs-wiki-redirect-delay))
2773                     ";\nURL=" (emacs-wiki-link-url link)))
2774       (if (= beg end)
2775           (insert "You should momentarily be redirected to [[" link "]].")
2776         (goto-char end))
2777       (delete-region (point) (point-max)))))
2778
2779 (defun emacs-wiki-nop-tag (beg end highlight-p)
2780   (if highlight-p
2781       (add-text-properties beg (point) '(invisible t intangible t)))
2782   (when (looking-at emacs-wiki-name-regexp)
2783     (goto-char (match-end 0))
2784     (unless highlight-p
2785       (add-text-properties beg (point)
2786                            '(rear-nonsticky (read-only) read-only t)))))
2787
2788 (defun emacs-wiki-insert-anchor (anchor)
2789   "Insert an anchor, either around the word at point, or within a tag."
2790   (skip-chars-forward " \t\n")
2791   (if (looking-at "<\\([^ />]+\\)>")
2792       (let ((tag (match-string 1)))
2793         (goto-char (match-end 0))
2794         (insert "<a name=\"" anchor "\" id=\"" anchor "\">")
2795         (when emacs-wiki-anchor-on-word
2796           (or (and (search-forward (format "</%s>" tag)
2797                                    (line-end-position) t)
2798                    (goto-char (match-beginning 0)))
2799               (forward-word 1)))
2800         (insert "</a>"))
2801     (insert "<a name=\"" anchor "\" id=\"" anchor "\">")
2802     (when emacs-wiki-anchor-on-word
2803       (forward-word 1))
2804     (insert "</a>")))
2805
2806 (defun emacs-wiki-contents-tag (beg end attrs)
2807   (let ((max-depth (let ((depth (cdr (assoc "depth" attrs))))
2808                      (or (and depth (string-to-int depth)) 2)))
2809         (index 1)
2810         base contents l)
2811     (save-excursion
2812       (catch 'done
2813         (while (re-search-forward "^\\(\\*+\\)\\s-+\\(.+\\)" nil t)
2814           (setq l (length (match-string 1)))
2815           (if (null base)
2816               (setq base l)
2817             (if (< l base)
2818                 (throw 'done t)))
2819           (when (<= l max-depth)
2820             (setq contents (cons (cons l (match-string-no-properties 2))
2821                                  contents))
2822             (goto-char (match-beginning 2))
2823             (emacs-wiki-insert-anchor (concat "sec" (int-to-string index)))
2824             (setq index (1+ index))))))
2825     (setq index 1 contents (reverse contents))
2826     (let ((depth 1) (sub-open 0) (p (point)))
2827       (insert "<dl class=\"contents\">\n")
2828       (while contents
2829         (insert "<dt class=\"contents\">\n")
2830         (insert "<a href=\"#sec" (int-to-string index) "\">"
2831                 (cdar contents)
2832                 "</a>\n")
2833         (setq index (1+ index))
2834         (insert "</dt>\n")
2835         (setq depth (caar contents)
2836               contents (cdr contents))
2837         (if contents
2838             (cond
2839              ((< (caar contents) depth)
2840               (let ((idx (caar contents)))
2841                 (while (< idx depth)
2842                   (insert "</dl>\n</dd>\n")
2843                   (setq sub-open (1- sub-open)
2844                         idx (1+ idx)))))
2845              ((> (caar contents) depth) ; can't jump more than one ahead
2846               (insert "<dd>\n<dl class=\"contents\">\n")
2847               (setq sub-open (1+ sub-open))))))
2848       (while (> sub-open 0)
2849         (insert "</dl>\n</dd>\n")
2850         (setq sub-open (1- sub-open)))
2851       (insert "</dl>\n")
2852       (put-text-property p (point) 'read-only t))))
2853
2854 (defun emacs-wiki-lisp-tag (beg end highlight-p)
2855   (if highlight-p
2856       (add-text-properties
2857        beg end
2858        (list 'font-lock-multiline t
2859              'display (emacs-wiki-eval-lisp
2860                        (buffer-substring-no-properties (+ beg 6) (- end 7)))
2861              'intangible t))
2862     (save-excursion
2863       (insert (emacs-wiki-eval-lisp
2864                (prog1
2865                    (buffer-substring-no-properties beg end)
2866                  (delete-region beg end)))))))
2867
2868 (defcustom emacs-wiki-command-default-file nil
2869   "If non-nil, this default program to use with <command> tags.
2870 If nil, Eshell is used, since it works on all platforms."
2871   :type '(choice file (const :tag "Use Eshell" nil))
2872   :group 'emacs-wiki-publish)
2873
2874 (defun emacs-wiki-command-tag (beg end attrs &optional highlight-p pre-tags)
2875   (if highlight-p
2876       (goto-char end)
2877     (while (looking-at "\\s-*$")
2878       (forward-line))
2879     (let ((interp (or (cdr (assoc "file" attrs))
2880                       emacs-wiki-command-default-file)))
2881       (if (null interp)
2882           (eshell-command (prog1
2883                               (buffer-substring-no-properties (point) end)
2884                             (delete-region beg end)) t)
2885         (let ((file (make-temp-file "ewiki")))
2886           (unwind-protect
2887               (let ((args (split-string interp)))
2888                 (write-region (point) end file)
2889                 (delete-region beg end)
2890                 (if pre-tags
2891                     (insert "<pre>\n"))
2892                 (apply 'call-process (car args) file t nil (cdr args))
2893                 (while (eq (char-syntax (char-before)) ? )
2894                   (backward-char))
2895                 (add-text-properties beg (point)
2896                                      '(rear-nonsticky (read-only)
2897                                                       read-only t))
2898                 (if pre-tags
2899                     (insert "</pre>\n")))
2900             (if (file-exists-p file)
2901                 (delete-file file))))))))
2902
2903 (defcustom emacs-wiki-c-to-html
2904   (if (or (featurep 'executable)
2905           (load "executable" t t))
2906       (concat (executable-find "c2html") " -c -s"))
2907   "Program to use to convert <c-source> tag text to HTML."
2908   :type 'string
2909   :group 'emacs-wiki-publish)
2910
2911 (defun emacs-wiki-c-source-tag (beg end attrs highlight-p)
2912   (if highlight-p
2913       (goto-char end)
2914     (if emacs-wiki-c-to-html
2915         (let ((c-to-html emacs-wiki-c-to-html))
2916           (if (assoc "numbered" attrs)
2917               (setq c-to-html (concat c-to-html " -n")))
2918           (emacs-wiki-command-tag beg end (list (cons "file" c-to-html))))
2919       (insert "<pre>")
2920       (emacs-wiki-escape-html-specials end)
2921       (goto-char end)
2922       (add-text-properties beg (point)
2923                            '(rear-nonsticky (read-only) read-only t))
2924       (insert "</pre>"))))
2925
2926 (defun emacs-wiki-python-tag (beg end attrs highlight-p)
2927   (emacs-wiki-command-tag
2928    beg end (list (cons "file" (executable-find "python"))) highlight-p t))
2929
2930 (defun emacs-wiki-perl-tag (beg end attrs highlight-p)
2931   (emacs-wiki-command-tag
2932    beg end (list (cons "file" (executable-find "perl"))) highlight-p t))
2933
2934 (defun emacs-wiki-insert-xbel-bookmarks (bmarks folder)
2935   "Insert a set of XBEL bookmarks as an HTML list."
2936   (while bmarks
2937     (let ((bookmark (car bmarks)))
2938       (cond
2939        ((equal (xml-tag-name bookmark) "folder")
2940         (let ((title (cadr (xml-tag-child bookmark "title"))))
2941           (unless folder
2942             (insert "<li>" title "\n<ul>\n"))
2943           (emacs-wiki-insert-xbel-bookmarks (xml-tag-children bookmark)
2944                                             (if (equal folder title)
2945                                                 nil
2946                                               folder))
2947           (unless folder
2948             (insert "</ul>\n"))))
2949        ((equal (xml-tag-name bookmark) "bookmark")
2950         (unless folder
2951           (insert "<li><a href=\"" (xml-tag-attr bookmark "href") "\">"
2952                   (cadr (xml-tag-child bookmark "title")) "</a>\n")))))
2953     (setq bmarks (cdr bmarks))))
2954
2955 (defcustom emacs-wiki-xbel-bin-directory "/usr/bin"
2956   "Directory where the xbel parsing utilities reside."
2957   :type 'directory
2958   :group 'emacs-wiki-publish)
2959
2960 (defun emacs-wiki-bookmarks-tag (beg end attrs)
2961   (require 'xml-parse)
2962   (let ((filename (expand-file-name (cdr (assoc "file" attrs))))
2963         (type (cdr (assoc "type" attrs)))
2964         (folder (cdr (assoc "folder" attrs)))
2965         (this-buffer (current-buffer))
2966         buffer)
2967     (when filename
2968       (cond
2969        (type
2970         (setq buffer (get-buffer-create " *xbel_parse*"))
2971         (with-current-buffer buffer
2972           (erase-buffer)
2973           (call-process
2974            (format "%s/%s_parse"
2975                    (directory-file-name emacs-wiki-xbel-bin-directory) type)
2976            nil t nil filename)))
2977        (t
2978         (setq buffer (find-file-noselect filename))))
2979       (insert "<ul>\n")
2980       (emacs-wiki-insert-xbel-bookmarks
2981        (with-current-buffer buffer
2982          (goto-char (point-min))
2983          (when (re-search-forward "<!DOCTYPE\\s-+xbel" nil t) ; XBEL format
2984            (goto-char (match-beginning 0))
2985            ;; the `cdr' is to skip the "title" child
2986            (cdr (xml-tag-children (read-xml))))) folder)
2987       (insert "</ul>\n")
2988       (kill-buffer buffer)))
2989   (while (eq (char-syntax (char-before)) ? )
2990     (backward-char))
2991   (add-text-properties beg (point)
2992                        '(rear-nonsticky (read-only) read-only t)))
2993
2994 (defun emacs-wiki-link-url (wiki-link)
2995   "Resolve the given WIKI-LINK into its ultimate URL form."
2996   (let ((link (emacs-wiki-wiki-link-target wiki-link)))
2997     (save-match-data
2998       (if (or (emacs-wiki-wiki-url-p link)
2999               (string-match emacs-wiki-image-regexp link)
3000               (string-match emacs-wiki-file-regexp link))
3001           link
3002         (if (assoc (emacs-wiki-wiki-base link)
3003                    (emacs-wiki-file-alist t))
3004             (if (string-match "#" link)
3005                 (concat (emacs-wiki-published-name
3006                          (substring link 0 (match-beginning 0))
3007                          (emacs-wiki-page-name)) "#"
3008                         (substring link (match-end 0)))
3009               (emacs-wiki-published-name link (emacs-wiki-page-name))))))))
3010
3011 (defsubst emacs-wiki-link-href (url name)
3012   "Return an href string for URL and NAME."
3013   (concat "<a href=\"" (emacs-wiki-published-name url) "\">" name "</a>"))
3014
3015 (defun emacs-wiki-markup-link ()
3016   "Resolve the matched wiki-link into its ultimate <a href> form.
3017 Images used the <img> tag."
3018   ;; avoid marking up urls that appear to be inside existing HTML
3019   (when (and (not (eq (char-after (point)) ?\"))
3020              (not (eq (char-after (point)) ?\>)))
3021     (let* ((wiki-link (match-string 0))
3022            (url (emacs-wiki-escape-html-string
3023                  (emacs-wiki-link-url wiki-link)))
3024            (name (emacs-wiki-escape-html-string
3025                   (emacs-wiki-wiki-visible-name wiki-link))))
3026       (if (null url)
3027           (if (and emacs-wiki-serving-p
3028                    (emacs-wiki-editable-p (emacs-wiki-wiki-base wiki-link)))
3029               (concat "<a class=\"nonexistent\" href=\"editwiki?"
3030                       (emacs-wiki-wiki-base wiki-link) "\">" name "</a>")
3031             (concat "<a class=\"nonexistent\" href=\""
3032                     emacs-wiki-maintainer "\">" name "</a>"))
3033         (if (save-match-data
3034               (string-match emacs-wiki-image-regexp url))
3035             (concat "<img src=\"" url "\" alt=\"" name "\">")
3036           (concat "<a href=\"" url "\">" name "</a>"))))))
3037
3038 (defun emacs-wiki-markup-word ()
3039   (let* ((beg (match-beginning 2))
3040          (end (1- (match-end 2)))
3041          (leader (buffer-substring-no-properties beg end))
3042          open-tag close-tag mark-read-only loc multi-line)
3043     (cond
3044      ((string= leader "_")
3045       (setq open-tag "<u>" close-tag "</u>"))
3046      ((string= leader "=")
3047       (setq open-tag "<code>" close-tag "</code>")
3048       (setq mark-read-only t))
3049      (t
3050       (setq multi-line t)
3051       (let ((l (length leader)))
3052         (cond
3053          ((= l 1) (setq open-tag "<em>" close-tag "</em>"))
3054          ((= l 2) (setq open-tag "<strong>" close-tag "</strong>"))
3055          ((= l 3) (setq open-tag "<strong><em>"
3056                         close-tag "</em></strong>"))))))
3057     (if (and (setq loc (search-forward leader nil t))
3058              (eq 0 (skip-syntax-forward "w" (1+ loc)))
3059              (or multi-line (= 1 (count-lines beg loc))))
3060         (progn
3061           (replace-match "")
3062           (insert close-tag)
3063           (save-excursion
3064             (goto-char beg)
3065             (delete-region beg end)
3066             (insert open-tag))
3067           (if mark-read-only
3068               (add-text-properties beg (point)
3069                                    '(rear-nonsticky (read-only) read-only
3070                                    t))))
3071       (backward-char))
3072     nil))
3073
3074 (defun emacs-wiki-markup-anchor ()
3075   (save-match-data
3076     (emacs-wiki-insert-anchor (match-string 1)))
3077   "")
3078
3079 (defcustom emacs-wiki-entity-table
3080   '(("#7779" . "s")
3081     ("#7717" . "h")
3082     ("#7789" . "t")
3083     ("#7716" . "H")
3084     ("#7826" . "Z"))
3085   "Substitutions to use for HTML entities which are not fully
3086 supported by all browsers -- in other words, we are pre-empting the
3087 entity mechanism and providing our own textual equivalent.  For
3088 Unicode browsers, this is usually unnecessary."
3089   :type 'sexp
3090   :group 'emacs-wiki)
3091
3092 (defun emacs-wiki-markup-entity ()
3093   (or (cdr (assoc (match-string 1)
3094                   emacs-wiki-entity-table))
3095       (concat "&" (match-string 1) ";")))
3096
3097 (defsubst emacs-wiki-surround-text (beg-tag end-tag move-func)
3098   (insert beg-tag)
3099   (funcall move-func)
3100   (insert end-tag))                     ; returns nil for us
3101
3102 (defun emacs-wiki-markup-heading ()
3103   (let ((len (1+ (length (match-string 1)))))
3104     (emacs-wiki-surround-text (format "<h%d>" len) (format "</h%d>" len)
3105                               'end-of-line)
3106     ""))
3107
3108 (defun emacs-wiki-markup-footnote ()
3109   (if (/= (line-beginning-position) (match-beginning 0))
3110       "<sup><a name=\"fnr.\\1\" href=\"#fn.\\1\">\\1</a></sup>"
3111     (prog1
3112         "<sup>[<a name=\"fn.\\1\" href=\"#fnr.\\1\">\\1</a>]</sup>"
3113       (save-excursion
3114         (save-match-data
3115           (let* ((beg (goto-char (match-end 0)))
3116                  (end (and (search-forward "\n\n" nil t)
3117                            (prog1
3118                                (copy-marker (match-beginning 0))
3119                              (goto-char beg)))))
3120             (while (re-search-forward "^[ \t]+\\([^\n]\\)" end t)
3121               (replace-match "\\1" t))))))))
3122
3123 (defsubst emacs-wiki-forward-paragraph ()
3124   (and (re-search-forward "^\\s-*$" nil t)
3125        (match-beginning 0)))
3126
3127 (defun emacs-wiki-markup-list-or-paragraph ()
3128   "Markup a list entry or quoted paragraph.
3129 The reason this function is so funky, is to prevent text properties
3130 like read-only from being inadvertently deleted."
3131   (if (null (match-string 2))
3132       (let* ((ws (match-string 4))
3133              (tag (if (>= (string-width ws) 6)
3134                       "center"
3135                     "blockquote")))
3136         (unless (and (equal tag "blockquote")
3137                      (save-excursion
3138                        (forward-line)
3139                        (or (eolp)
3140                            (looking-at "\\S-"))))
3141           (emacs-wiki-surround-text (format "<%s>\n<p>\n%s" tag ws)
3142                                     (format "\n</p>\n</%s>\n" tag)
3143                                     'emacs-wiki-forward-paragraph)))
3144     (let ((str (match-string 2)))
3145       (cond
3146        ((and (eq (aref str 0) ?-))
3147         (delete-region (match-beginning 0) (match-end 0))
3148         (emacs-wiki-surround-text
3149          "<ul>\n<li>" "</li>\n</ul>\n"
3150          (function
3151           (lambda ()
3152             (and (re-search-forward "^\\s-*\\(-\\|$\\)" nil t)
3153                  (goto-char (match-beginning 0)))))))
3154        ((and (>= (aref str 0) ?0)
3155              (<= (aref str 0) ?9))
3156         (delete-region (match-beginning 0) (match-end 0))
3157         (emacs-wiki-surround-text
3158          "<ol>\n<li>" "</li>\n</ol>\n"
3159          (function
3160           (lambda ()
3161             (and (re-search-forward "^\\s-*\\([0-9]+\\.\\|$\\)" nil t)
3162                  (goto-char (match-beginning 0)))))))
3163        (t
3164         (goto-char (match-beginning 0))
3165         (insert "<dl>\n<dt>")
3166         (save-match-data
3167           (when (re-search-forward "[ \t\n]+::[ \t\n]+" nil t)
3168             (replace-match "</dt>\n<dd>\n")))
3169         (emacs-wiki-forward-paragraph)
3170         (insert "</dd>\n</dl>\n"))))))
3171
3172 (defun emacs-wiki-markup-table ()
3173   (if (featurep 'table)
3174       (let ((leader (match-string 1))
3175             (begin (copy-marker (match-beginning 0)))
3176             table end)
3177         (goto-char (match-end 0))
3178         (setq table
3179               (with-current-buffer (table-generate-source 'html)
3180                 (prog1
3181                     (buffer-string)
3182                   (kill-buffer (current-buffer)))))
3183         (goto-char begin)
3184         (if (re-search-backward "<p>[ \t\n\r]+" nil t)
3185             (replace-match (if (>= (string-width leader) 6)
3186                                "<center>\n"
3187                              (if (> (length leader) 0)
3188                                  "<blockquote>\n"
3189                                ""))))
3190         (delete-region begin (re-search-forward "-+\\+\\s-*[\r\n]+\\s-*$"
3191                                                 nil t))
3192         (insert table)
3193         (setq end (point-marker))
3194         (goto-char begin)
3195         (while (< (point) end)
3196           (if (looking-at "^\\s-+")
3197               (replace-match ""))
3198           (forward-line))
3199         (goto-char end)
3200         (if (re-search-forward "[ \t\n\r]+</p>" nil t)
3201             (replace-match (if (>= (string-width leader) 6)
3202                                "\n</center>"
3203                              (if (> (length leader) 0)
3204                                  "\n</blockquote>"
3205                                ""))))
3206         (set-match-data (list begin begin begin begin))
3207         nil)
3208     (let* ((str (save-match-data
3209                   (if (featurep 'xemacs)
3210                       ;; more emacs divergence. :(
3211                       (replace-in-string (match-string 1) " *|+ *$" "")
3212                     (match-string 1))))
3213            (fields
3214             (append (save-match-data
3215                       (split-string str "[ \t]*|+[ \t]*"))
3216                     (list (match-string 4))))
3217            (len (length (match-string 3)))
3218            (row (cond ((= len 1) "tbody")
3219                       ((= len 2) "thead")
3220                       ((= len 3) "tfoot")))
3221            (col (cond ((= len 1) "td")
3222                       ((= len 2) "th")
3223                       ((= len 3) "td"))))
3224       (concat "<table " emacs-wiki-table-attributes ">\n"
3225               "<" row ">\n" "<tr>\n<" col ">"
3226               (mapconcat 'identity fields (format "</%s><%s>" col col))
3227               "</" col ">\n" "</tr>\n" "</" row ">\n"
3228               "</table>\n"))))
3229
3230 (defun emacs-wiki-markup-verse ()
3231   (save-match-data
3232     (let* ((lines (split-string (match-string 1) "\n"))
3233            (l lines))
3234       (while l
3235         (if (and (> (length (car l)) 2)
3236                  (string-match "\\`\\s-*> " (car l)))
3237             (setcar l (substring (car l) (match-end 0))))
3238         (setq l (cdr l)))
3239       (concat "<p class=\"verse\">"
3240               (mapconcat 'identity lines "\n") "</p>"))))
3241
3242 (defcustom emacs-wiki-pretty-changelogs nil
3243   "If non-nil, markup ChangeLog buffers using pretty tables.
3244 This rule requires that a GIF file called \"onepixel.gif\" be in your
3245 publication tree.  Here is a uuencoded version of such a file:
3246
3247 begin 644 onepixel.gif
3248 M1TE&.#EA`0`!`*$``````/___________R'Y!`'__P$`+``````!``$```(\"
3249 $3`$`.P``
3250 `
3251 end"
3252   :type 'boolean
3253   :group 'emacs-wiki-publish)
3254
3255 (defun emacs-wiki-changelog-escape-files ()
3256   (replace-match "[[\\1]]" t nil nil 1))
3257
3258 (defun emacs-wiki-markup-changelog-section ()
3259   (if (not emacs-wiki-pretty-changelogs)
3260       "* \\1 \\2"
3261     (let ((email (match-string 2))
3262           (date (match-string 1)))
3263       (goto-char (match-beginning 0))
3264       (delete-region (match-beginning 0) (match-end 0))
3265       (while (eolp)
3266         (kill-line 1))
3267       (insert (format "  <TABLE WIDTH=\"100%%\" BORDER=\"0\"
3268          CELLSPACING=\"1\" CELLPADDING=\"2\">
3269     <TR>
3270       <TD BGCOLOR=\"black\" BACKGROUND=\"onepixel.gif\">
3271         <TABLE WIDTH=\"100%%\" BORDER=\"0\"
3272                CELLPADDING=\"5\" CELLSPACING=\"0\">
3273           <TR>
3274             <TD ALIGN=\"left\" BGCOLOR=\"b0c4de\" BACKGROUND=\"onepixel.gif\">
3275               <FONT COLOR=\"navy\"> <B>%s</B> </FONT>
3276             </TD>
3277             <TD ALIGN=\"right\" VALIGN=\"bottom\" BGCOLOR=\"b0c4de\"
3278                 BACKGROUND=\"onepixel.gif\">
3279               <FONT SIZE=\"2\" COLOR=\"2f4f4f\"> %s </FONT>
3280             </TD>
3281           </TR>
3282           <TR>
3283             <TD BGCOLOR=\"fffff0\" COLSPAN=\"2\" BACKGROUND=\"onepixel.gif\">
3284               <FONT COLOR=\"black\">
3285 " email date))
3286       (add-text-properties (match-beginning 0) (point)
3287                            '(read-only t rear-nonsticky (read-only))))
3288     (if (re-search-forward "^[0-9]" nil t)
3289         (goto-char (1- (match-beginning 0)))
3290       (goto-char (point-max))
3291       (while (eq (char-before (1- (point))) ?\n)
3292         (delete-char -1)))
3293     (let ((here (1- (point))))
3294       (insert "
3295               </FONT>
3296             </TD>
3297           </TR>
3298         </TABLE>
3299       </TD>
3300     </TR>
3301   </TABLE>
3302   <br>")
3303       (add-text-properties here (point)
3304                            '(read-only t rear-nonsticky (read-only)))
3305       nil)))
3306
3307 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3308 ;;
3309 ;; Emacs Wiki HTTP Server (using httpd.el)
3310 ;;
3311 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3312
3313 (defgroup emacs-wiki-http nil
3314   "Options controlling the behaviour of the Emacs Wiki HTTP server.
3315
3316 So, you want to run a Wiki server based on Emacs?  It's simple.
3317 First, you will need two other scripts: httpd.el and cgi.el.  Both of
3318 them can be downloaded from Eric Mardsen's page:
3319
3320   http://www.chez.com/emarsden/downloads/
3321
3322 Once you have those two scripts, you must decide between two different
3323 methods of serving pages directly from Emacs:
3324
3325 * PERSISTED INVOCATION SERVER
3326
3327 This scheme keeps a dedicated Emacs process running, solely for the
3328 purpose of rendering pages.  It has the disadvantage of occupying
3329 virtual memory when no one is requesting pages.  It has the advantage
3330 of being 50 times faster than the next method.
3331
3332 To use the persisted invoctaion server, you must download the Python
3333 script `httpd-serve' from the same website where you downloaded
3334 emacs-wiki:
3335
3336   http://www.gci-net.com/~johnw/emacs.html
3337
3338 Once you have have downloaded the script, running it is simple:
3339
3340   ./httpd-serve --daemon --port 8080 --load /tmp/my-emacs-wiki \
3341       [path to your HTML files]
3342
3343 The file `/tmp/my-emacs-wiki.el' should contain all the customizations
3344 required by your Wiki setup.  This is how the server knows where to
3345 find your pages.  This script MUST contain the following line:
3346
3347   (load \"emacs-wiki\")
3348
3349 That's it.  You should now be able to access your Wiki repository at
3350 localhost:8080.  Only world-readable will be visible, and only
3351 world-writable can be edited over HTTP.
3352
3353 * AN EMACS SPAWNED PER REQUEST
3354
3355 The old method of serving Wiki pages directly is to spawn an Emacs
3356 invocation for every request.  This has the advantage of being a far
3357 simpler approach, and it doesn't consume memory if no one is
3358 requesting pages.  The disadvantage is that it's hideously slow, and
3359 multiple requests may bog down your machine's supply of virtual
3360 memory.
3361
3362 Anyway, to use this approach, add the following line to your
3363 /etc/inted.conf file:
3364
3365   8080 stream tcp nowait.10000 nobody /usr/local/bin/emacs-httpd
3366
3367 The emacs-httpd script should look something like this:
3368
3369   #!/bin/sh
3370   /usr/bin/emacs -batch --no-init-file --no-site-file \\
3371       -l httpd -l cgi -l emacs-wiki \\
3372       --eval \"(setq httpd-document-root emacs-wiki-publishing-directory \\
3373                     emacs-wiki-maintainer \\\"mailto:joe@where.com\\\")\" \\
3374       -f httpd-serve 2> /dev/null
3375
3376 Emacs-wiki will now serve pages directly on port 8080.  Note that if
3377 you need to configure any variables in emacs-wiki, you will have to
3378 repeat those configurations in the emacs-httpd script.
3379
3380 Note: If you have the 'stopafter' tool installed, it's a good idea to
3381 put a limit on how much time each Emacs process is allowed.  And if
3382 you want to render planner.el pages, you'll need to make another
3383 modification.  Here is a more complete example:
3384
3385   #!/bin/sh
3386   /usr/bin/stopafter 60 KILL /usr/bin/emacs \\
3387       -batch --no-init-file --no-site-file \\
3388       -l httpd -l cgi -l emacs-wiki -l planner \\
3389       --eval \"(progn \\
3390          (setq httpd-document-root emacs-wiki-publishing-directory \\
3391                emacs-wiki-maintainer \\\"mailto:joe@where.com\\\") \\
3392          (planner-update-wiki-project))\" \\
3393       -f httpd-serve 2> /dev/null"
3394   :group 'emacs-wiki)
3395
3396 (defcustom emacs-wiki-http-search-form
3397   "
3398 <form method=\"GET\" action=\"/searchwiki?get\">
3399   <center>
3400     Search for: <input type=\"text\" size=\"50\" name=\"q\" value=\"\">
3401     <input type=\"submit\" value=\"Search!\">
3402   </center>
3403 </form>\n"
3404   "The form presenting for doing searches when using httpd.el."
3405   :type 'string
3406   :group 'emacs-wiki-http)
3407
3408 (defcustom emacs-wiki-http-support-editing t
3409   "If non-nil, allow direct editing when serving over httpd.el.
3410 Note that a page can be edited only if it is world-writable and
3411 `emacs-wiki-use-mode-flags' is set, or if it matches one of the
3412 regexps in `emacs-wiki-editable-pages'."
3413   :type 'boolean
3414   :group 'emacs-wiki-http)
3415
3416 (defcustom emacs-wiki-http-edit-form
3417   "
3418 <form method=\"POST\" action=\"/changewiki?post\">
3419   <textarea name=\"%PAGE%\" rows=\"25\" cols=\"80\">%TEXT%</textarea>
3420   <center>
3421     <input type=\"submit\" value=\"Submit changes\">
3422   </center>
3423 </form>\n"
3424   "The form presenting for doing edits when using httpd.el."
3425   :type 'string
3426   :group 'emacs-wiki-http)
3427
3428 (defun emacs-wiki-http-send-buffer (&optional title modified code
3429                                               msg no-markup)
3430   "Markup and send the contents of the current buffer via HTTP."
3431   (unless no-markup (emacs-wiki-replace-markup title))
3432   (princ "HTTP/1.0 ")
3433   (princ (or code 200))
3434   (princ " ")
3435   (princ (or msg "OK"))
3436   (princ httpd-line-terminator)
3437   (princ "Server: emacs-wiki.el/2.26")
3438   (princ httpd-line-terminator)
3439   (princ "Connection: close")
3440   (princ httpd-line-terminator)
3441   (princ "MIME-Version: 1.0")
3442   (princ httpd-line-terminator)
3443   (princ "Date: ")
3444   (princ (format-time-string "%a, %e %b %Y %T %Z"))
3445   (princ httpd-line-terminator)
3446   (princ "From: ")
3447   (princ (substring emacs-wiki-maintainer 7))
3448   (when modified
3449     (princ httpd-line-terminator)
3450     (princ "Last-Modified: ")
3451     (princ (format-time-string "%a, %e %b %Y %T %Z" modified)))
3452   (princ httpd-line-terminator)
3453   (princ "Content-Type: text/html; charset=iso-8859-1")
3454   (princ httpd-line-terminator)
3455   (princ "Content-Length: ")
3456   (princ (1- (point-max)))
3457   (princ httpd-line-terminator)
3458   (princ httpd-line-terminator)
3459   (princ (buffer-string)))
3460
3461 (defun emacs-wiki-http-reject (title msg &optional annotation)
3462   (with-temp-buffer
3463     (insert msg ".\n")
3464     (if annotation
3465         (insert annotation "\n"))
3466     (emacs-wiki-http-send-buffer title nil 404 msg)))
3467
3468 (defvar emacs-wiki-buffer-mtime nil)
3469 (make-variable-buffer-local 'emacs-wiki-buffer-mtime)
3470
3471 (defun emacs-wiki-sort-buffers (l r)
3472   (let ((l-mtime (with-current-buffer l
3473                    emacs-wiki-buffer-mtime))
3474         (r-mtime (with-current-buffer r
3475                    emacs-wiki-buffer-mtime)))
3476     (cond
3477      ((and (null l-mtime) (null r-mtime)) l)
3478      ((null l-mtime) r)
3479      ((null r-mtime) l)
3480      (t (emacs-wiki-time-less-p r-mtime l-mtime)))))
3481
3482 (defun emacs-wiki-winnow-list (entries &optional predicate)
3483   "Return only those ENTRIES for which PREDICATE returns non-nil."
3484   (let ((flist (list t))
3485         valid p)
3486     (let ((entry entries))
3487       (while entry
3488         (if (funcall predicate (car entry))
3489             (nconc flist (list (car entry))))
3490         (setq entry (cdr entry))))
3491     (cdr flist)))
3492
3493 (defcustom emacs-wiki-max-cache-size 64
3494   "The number of pages to cache when serving over HTTP.
3495 This only applies if set while running the persisted invocation
3496 server.  See main documentation for the `emacs-wiki-http'
3497 customization group."
3498   :type 'integer
3499   :group 'emacs-wiki-http)
3500
3501 (defun emacs-wiki-prune-cache ()
3502   "If the page cache has become too large, prune it."
3503   (let* ((buflist (sort (emacs-wiki-winnow-list
3504                          (buffer-list)
3505                          (function
3506                           (lambda (buf)
3507                             (with-current-buffer buf
3508                               emacs-wiki-buffer-mtime))))
3509                         'emacs-wiki-sort-buffers))
3510          (len (length buflist)))
3511     (while (> len emacs-wiki-max-cache-size)
3512       (kill-buffer (car buflist))
3513       (setq len (1- len)))))
3514
3515 (defun emacs-wiki-render-page (name)
3516   "Render the wiki page identified by NAME.
3517 When serving from a dedicated Emacs process (see the httpd-serve
3518 script), a maximum of `emacs-wiki-max-cache-size' pages will be cached
3519 in memory to speed up serving time."
3520   (if (equal name emacs-wiki-index-page)
3521       (with-current-buffer (emacs-wiki-generate-index t t)
3522         (emacs-wiki-http-send-buffer "Wiki Index")
3523         (kill-buffer (current-buffer)))
3524     (let ((file (and (not (emacs-wiki-private-p name))
3525                      (cdr (assoc name (emacs-wiki-file-alist)))))
3526           (inhibit-read-only t))
3527       (if (null file)
3528           (emacs-wiki-http-reject "Page not found"
3529                                   (format "Wiki page %s not found" name))
3530         (set-buffer (get-buffer-create file))
3531         (let ((modified-time (nth 5 (file-attributes file))))
3532           (when (or (null emacs-wiki-buffer-mtime)
3533                     (emacs-wiki-time-less-p emacs-wiki-buffer-mtime
3534                                             modified-time))
3535             (erase-buffer)
3536             (setq emacs-wiki-buffer-mtime modified-time))
3537           (goto-char (point-max))
3538           (if (not (bobp))
3539               (emacs-wiki-http-send-buffer nil emacs-wiki-buffer-mtime
3540                                            nil nil t)
3541             (insert-file-contents file t)
3542             (cd (file-name-directory file))
3543             (emacs-wiki-maybe)
3544             (emacs-wiki-http-send-buffer nil emacs-wiki-buffer-mtime)))
3545         (set-buffer-modified-p nil)
3546         (emacs-wiki-prune-cache)))))
3547
3548 (defun emacs-wiki-wikify-search-results (term)
3549   "Convert the current buffer's grep results into a Wiki form."
3550   (goto-char (point-max))
3551   (forward-line -2)
3552   (delete-region (point) (point-max))
3553   (goto-char (point-min))
3554   (kill-line 2)
3555   (let ((results (list t)))
3556     (while (re-search-forward "^.+/\\([^/:]+\\):\\s-*[0-9]+:\\(.+\\)" nil t)
3557       (let ((page (match-string 1)))
3558         (unless (or (emacs-wiki-private-p page)
3559                     (string-match emacs-wiki-file-ignore-regexp page))
3560           (let ((text (match-string 2))
3561                 (entry (assoc page results)))
3562             (if entry
3563                 (nconc (cdr entry) (list text))
3564               (nconc results (list (cons page (list text)))))))))
3565     (delete-region (point-min) (point-max))
3566     (setq results
3567           (sort (cdr results)
3568                 (function
3569                  (lambda (l r)
3570                    (string-lessp (car l) (car r))))))
3571     (while results
3572       (unless (emacs-wiki-private-p (caar results))
3573         (insert "[[" (caar results) "]] ::\n  <p>")
3574         (let ((hits (cdar results)))
3575           (while hits
3576             (while (string-match "</?lisp>" (car hits))
3577               (setcar hits (replace-match "" t t (car hits))))
3578             (while (string-match (concat "\\([^*?[/>]\\)\\<\\(" term "\\)\\>")
3579                                  (car hits))
3580               (setcar hits (replace-match "\\1<strong>\\2</strong>"
3581                                           t nil (car hits))))
3582             (insert "  > <verbatim>" (car hits) "</verbatim>\n")
3583             (setq hits (cdr hits))))
3584         (insert "</p>\n\n"))
3585       (setq results (cdr results)))))
3586
3587 (defun emacs-wiki-setup-edit-page (page-name)
3588   (insert "<verbatim>" emacs-wiki-http-edit-form "</verbatim>")
3589   (goto-char (point-min))
3590   (search-forward "%PAGE%")
3591   (replace-match page-name t t)
3592   (search-forward "%TEXT%")
3593   (let ((beg (match-beginning 0))
3594         (file (emacs-wiki-page-file page-name))
3595         end)
3596     (delete-region beg (point))
3597     (when file
3598       (insert-file-contents file)
3599       (save-restriction
3600         (narrow-to-region beg (point))
3601         (goto-char (point-min))
3602         (emacs-wiki-escape-html-specials)))))
3603
3604 (defun emacs-wiki-http-changewiki (&optional content)
3605   "Change the contents of Wiki page, using the results of a POST request."
3606   (require 'cgi)
3607   (unless content
3608     (goto-char (point-min))
3609     (if (not (re-search-forward "Content-length:\\s-*\\([0-9]+\\)" nil t))
3610         (emacs-wiki-http-reject "Content-length missing"
3611                                 "No Content-length for POST request"
3612                                 (concat "Header received was:\n\n<example>"
3613                                         (buffer-string) "</example>\n"))
3614       (let ((content-length (string-to-number (match-string 1))))
3615         (erase-buffer)
3616         (read-event)                    ; absorb the CRLF separator
3617         (let ((i 0))
3618           (while (< i content-length)
3619             (insert (read-event))
3620             (setq i (1+ i))))))
3621     (setq content (buffer-string)))
3622   (when content
3623     (let* ((result (cgi-decode content))
3624            (page (caar result))
3625            (text (cdar result))
3626            (len (length text))
3627            (require-final-newline t)
3628            (pos 0) illegal user)
3629       (if (not (emacs-wiki-editable-p page))
3630           (emacs-wiki-http-reject
3631            "Editing not allowed"
3632            (format "Editing Wiki page %s is not allowed" page))
3633         (while (and (null illegal)
3634                     (setq pos (string-match "<\\s-*\\([^> \t]+\\)"
3635                                             text pos)))
3636           (setq pos (match-end 0))
3637           (if (assoc (match-string 1 text) emacs-wiki-dangerous-tags)
3638               (setq illegal (match-string 1 text))))
3639         (if illegal
3640             (emacs-wiki-http-reject
3641              "Disallowed tag used"
3642              (format "Public use of &lt;%s&gt; tag not allowed" illegal))
3643           (emacs-wiki-find-file page)
3644           (if (setq user (file-locked-p buffer-file-name))
3645               (emacs-wiki-http-reject
3646                "Page is locked"
3647                (format "The page \"%s\" is currently being edited by %s."
3648                        page (if (eq user t) (user-full-name) user)))
3649             (let ((inhibit-read-only t)
3650                   (delete-old-versions t))
3651               (erase-buffer)
3652               (insert (if (eq (aref text (1- len)) ?%)
3653                           (substring text 0 (1- len))
3654                         text))
3655               (goto-char (point-min))
3656               (while (re-search-forward "\r$" nil t)
3657                 (replace-match "" t t))
3658               (save-buffer)
3659               ;; this is 0666 - there is no read syntax for octals which
3660               ;; works across all emacsen
3661               (let ((oct 438))
3662                 (when (/= (file-modes buffer-file-name) oct)
3663                   (set-file-modes buffer-file-name oct)))
3664               (kill-buffer (current-buffer)))
3665             (with-temp-buffer
3666               (emacs-wiki-file-alist)   ; force re-check
3667               (insert "<redirect url=\"" page "\" delay=\"3\">")
3668               (insert "Thank you, your changes have been saved to " page)
3669               (insert ".  You will be redirected to "
3670                       "the new page in a moment.")
3671               (insert "</redirect>")
3672               (emacs-wiki-http-send-buffer "Changes Saved"))))))))
3673
3674 (defvar httpd-vars nil)
3675
3676 (defsubst httpd-var (var)
3677   "Return value of VAR as a URL variable.  If VAR doesn't exist, nil."
3678   (cdr (assoc var httpd-vars)))
3679
3680 (defsubst httpd-var-p (var)
3681   "Return non-nil if VAR was passed as a URL variable."
3682   (not (null (assoc var httpd-vars))))
3683
3684 (defun emacs-wiki-serve-page (page content)
3685   (let ((handled t))
3686     (cond
3687      ((string-match "\\`wiki\\?\\(.+\\)" page)
3688       (emacs-wiki-render-page (match-string 1 page)))
3689
3690      ((string-match "\\`editwiki\\?\\(.+\\)" page)
3691       (let ((page-name (match-string 1 page)))
3692         (if (not (emacs-wiki-editable-p page-name))
3693             (emacs-wiki-http-reject "Editing not allowed"
3694                                     "Editing this Wiki page is not allowed")
3695           (with-temp-buffer
3696             (emacs-wiki-setup-edit-page page-name)
3697             ;; this is required because of the : in the name
3698             (emacs-wiki-http-send-buffer
3699              (concat "Edit Wiki Page: " page-name))))))
3700
3701      ((string-match "\\`searchwiki\\?get" page)
3702       (with-temp-buffer
3703         (insert "<verbatim>" emacs-wiki-http-search-form "</verbatim>")
3704         (emacs-wiki-http-send-buffer "Search Wiki Pages")))
3705
3706      ((string-match "\\`searchwiki\\?q=\\(.+\\)" page)
3707       (let ((compilation-scroll-output nil)
3708             (term (match-string 1 page)))
3709         (unintern 'start-process)
3710         (require 'compile)
3711         (with-current-buffer (emacs-wiki-grep term)
3712           (emacs-wiki-wikify-search-results term)
3713           (emacs-wiki-http-send-buffer "Search Results")
3714           (kill-buffer (current-buffer)))))
3715
3716      ((string-match "\\`changewiki\\?post" page)
3717       (emacs-wiki-http-changewiki content))
3718
3719      ((string-match "\\`diffwiki\\?\\(.+\\)" page)
3720       ;; jww (2001-04-20): This code doesn't fully work yet.
3721       (emacs-wiki-find-file (match-string 1 page))
3722       (require 'vc)
3723       (require 'vc-hooks)
3724       (let ((curr-ver (vc-workfile-version buffer-file-name)))
3725         (vc-version-diff buffer-file-name
3726                          curr-ver (vc-previous-version curr-ver))
3727         (let ((inhibit-read-only t))
3728           (goto-char (point-min))
3729           (when (re-search-forward "^diff" nil t)
3730             (forward-line)
3731             (delete-region (point-min) (point)))
3732           (insert "<verbatim><pre>")
3733           (emacs-wiki-escape-html-specials)
3734           (goto-char (point-max))
3735           (if (re-search-backward "^Process.*killed" nil t)
3736               (delete-region (point) (point-max)))
3737           (insert "</verbatim></pre>")
3738           (emacs-wiki-http-send-buffer "Diff Results"))))
3739
3740      (t
3741       (setq handled nil)))
3742     handled))
3743
3744 (defun emacs-wiki-serve (page &optional content)
3745   "Serve the given PAGE from this emacs-wiki server."
3746   ;; index.html is really a reference to the main Wiki page
3747   (if (string= page "index.html")
3748       (setq page (concat "wiki?" emacs-wiki-home-page)))
3749
3750   ;; handle the actual request
3751   (let ((vc-follow-symlinks t)
3752         (emacs-wiki-report-threshhold nil)
3753         (emacs-wiki-serving-p t)
3754         httpd-vars project)
3755     (save-excursion
3756       ;; process any CGI variables, if cgi.el is available
3757       (if (string-match "\\`\\([^&]+\\)&" page)
3758           (setq httpd-vars
3759                 (and (fboundp 'cgi-decode)
3760                      (cgi-decode (substring page (match-end 0))))
3761                 page (match-string 1 page)))
3762       (setq project (httpd-var "project"))
3763       (if project
3764           (with-emacs-wiki-project project
3765             (emacs-wiki-serve-page page content))
3766         (emacs-wiki-serve-page page content)))))
3767
3768 (if (featurep 'httpd)
3769     (httpd-add-handler "\\`\\(index\\.html\\|.*wiki\\(\\?\\|\\'\\)\\)"
3770                        'emacs-wiki-serve))
3771
3772 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3773 ;;
3774 ;; Support for multile Emacs Wiki projects
3775 ;;
3776 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3777
3778 (defgroup emacs-wiki-project nil
3779   "Options controlling multi-project behavior in Emacs-Wiki."
3780   :group 'emacs-wiki)
3781
3782 (defvar emacs-wiki-current-project nil)
3783 (defvar emacs-wiki-predicate nil)
3784 (defvar emacs-wiki-major-mode nil)
3785 (defvar emacs-wiki-project-server-prefix nil)
3786
3787 (defcustom emacs-wiki-show-project-name-p t
3788   "When true, display the current project name in the mode-line"
3789   :group 'emacs-wiki
3790   :type 'boolean)
3791
3792 ;; this might go away - did anyone prefer the old behavior? tell me!
3793 (defvar emacs-wiki-old-project-change-p nil)
3794
3795 (defcustom emacs-wiki-update-project-hook
3796   '(emacs-wiki-update-project-interwikis)
3797   "A hook called whenever `emacs-wiki-projects' is modified.
3798 By default, this hook is used to update the Interwiki table so that it
3799 contains links to each project name."
3800   :type 'hook
3801   :group 'emacs-wiki-project)
3802
3803 (defun emacs-wiki-update-project-interwikis ()
3804   (let ((projs emacs-wiki-projects))
3805     (while projs
3806       (add-to-list
3807        'emacs-wiki-interwiki-names
3808        `(,(caar projs)
3809          . (lambda (tag)
3810              (emacs-wiki-project-interwiki-link ,(caar projs) tag))))
3811       (setq projs (cdr projs)))))
3812
3813 (defcustom emacs-wiki-projects nil
3814   "A list of project-specific Emacs-Wiki variable settings.
3815 Each entry is a cons cell, of the form (PROJECT VARS).
3816
3817 Projects are useful for maintaining separate wikis that vary in
3818 some way. For instance, you might want to keep your work-related
3819 wiki files in a separate directory, with a different fill-column:
3820
3821 (setq emacs-wiki-projects
3822       `((\"default\" . ((emacs-wiki-directories . (\"~/wiki\"))))
3823         (\"work\" . ((fill-column . 65)
3824                  (emacs-wiki-directories . (\"~/workwiki/\"))))))
3825
3826 You can then change between them with \\[emacs-wiki-change-project],
3827 by default bound to C-c C-v. When you use \\[emacs-wiki-find-file] to
3828 find a new file, emacs-wiki will attempt to detect which project it
3829 is part of by finding the first project where emacs-wiki-directories
3830 contains that file.
3831
3832 VARS is an alist of symbol to value mappings, to be used locally in
3833 all emacs-wiki buffers associated with that PROJECT.
3834
3835 You may also set the variable `emacs-wiki-predicate' in this alist,
3836 which should be a function to determine whether or not the project
3837 pertains to a certain buffer.  It will be called within the buffer in
3838 question.  The default predicate checks whether the file exists within
3839 `emacs-wiki-directories' for that project.
3840
3841 The variable `emacs-wiki-major-mode' can be used to determine the
3842 major mode for a specific emacs-wiki buffer, in case you have
3843 developed a customized major-mode derived from `emacs-wiki-mode'.
3844
3845 The variable `emacs-wiki-project-server-prefix' is prepended to the
3846 Interwiki URL, whenever an Interwiki reference to another project is
3847 made.  For example, if you had two projects, A and B, and in A you
3848 made a reference to B by typing B#WikiPage, A needs to know what
3849 directory or server to prepend to the WikiPage.html href.  If this
3850 variable is not set, it is assumed that both A and B publish to the
3851 same location.
3852
3853 If any variable is not customized specifically for a project, the
3854 global value is used."
3855   :type `(repeat
3856           (cons
3857            :tag "Emacs-Wiki Project"
3858            (string :tag "Project name")
3859            (repeat
3860             (choice
3861              (cons :tag "emacs-wiki-predicate"
3862                    (const emacs-wiki-predicate) function)
3863              (cons :tag "emacs-wiki-major-mode"
3864                    (const emacs-wiki-major-mode) function)
3865              (cons :tag "emacs-wiki-project-server-prefix"
3866                    (const emacs-wiki-project-server-prefix) string)
3867              ,@(mapcar
3868                 (function
3869                  (lambda (sym)
3870                    (list 'cons :tag (symbol-name sym)
3871                          (list 'const sym)
3872                          (get sym 'custom-type))))
3873                 (apropos-internal "\\`emacs-wiki-"
3874                                   (function
3875                                    (lambda (sym)
3876                                      (and (not (eq sym 'emacs-wiki-projects))
3877                                           (get sym 'custom-type))))))))))
3878   :set (function
3879         (lambda (sym val)
3880           (set sym val)
3881           (run-hooks 'emacs-wiki-update-project-hook)))
3882   :group 'emacs-wiki-project)
3883
3884 (defmacro with-emacs-wiki-project (project &rest body)
3885   "Evaluate as part of PROJECT the given BODY forms."
3886   `(with-temp-buffer
3887      (emacs-wiki-change-project ,project)
3888      ,@body))
3889
3890 (put 'with-emacs-wiki-project 'lisp-indent-function 1)
3891
3892 (defun emacs-wiki-change-project (project)
3893   "Manually change the project associated with the current buffer."
3894   (interactive (list (completing-read "Switch to project: "
3895                                       emacs-wiki-projects
3896                                       nil t nil)))
3897   (let ((projsyms (cdr (assoc project emacs-wiki-projects)))
3898         sym)
3899     (while projsyms
3900       (setq sym (caar projsyms))
3901       (unless (memq sym '(emacs-wiki-predicate emacs-wiki-major-mode))
3902         (let ((custom-set (or (get sym 'custom-set) 'set))
3903               (var (if (eq (get sym 'custom-type) 'hook)
3904                        (make-local-hook sym)
3905                      (make-local-variable sym))))
3906           (if custom-set
3907               (funcall custom-set var (cdar projsyms)))))
3908       (setq projsyms (cdr projsyms))))
3909   (when (not (string= emacs-wiki-current-project project))
3910     ;; if it was a user request to change, change to the welcome buffer first
3911     (if (and (interactive-p)
3912                (not emacs-wiki-old-project-change-p))
3913         (with-emacs-wiki-project
3914          project (emacs-wiki-visit-link emacs-wiki-default-page))
3915       (set (make-local-variable 'emacs-wiki-current-project) project)
3916       (when emacs-wiki-show-project-name-p
3917         (setq mode-name (concat "Wiki[" project "]"))))))
3918
3919 (defun emacs-wiki-project-interwiki-link (project tag)
3920   (with-emacs-wiki-project project
3921     (if emacs-wiki-publishing-p
3922         (concat emacs-wiki-project-server-prefix
3923                 (emacs-wiki-link-url (or tag emacs-wiki-home-page)))
3924       (or (emacs-wiki-page-file (or tag emacs-wiki-home-page))
3925           ;; doesn't yet exist, so we don't qualify the name, causing it to be
3926           ;; rendered as a bad link
3927           tag))))
3928
3929 (provide 'emacs-wiki)
3930 ;;; emacs-wiki.el ends here