1 ;;; emacs-wiki.el --- Maintain a local Wiki using Emacs-friendly markup
3 ;; Copyright (C) 2001, 2002, 2003 John Wiegley (johnw AT gnu DOT org)
5 ;; Emacs Lisp Archive Entry
6 ;; Filename: emacs-wiki.el
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
17 ;; This file is not part of GNU Emacs.
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
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
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
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.
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.
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.
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.
55 ;; To begin using emacs-wiki, put this in your .emacs file:
57 ;; (load "emacs-wiki")
59 ;; Now you can type M-x emacs-wiki-find-file, give it a WikiName (or
60 ;; just hit return) and start typing!
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.
66 ;; * Keystroke summary
68 ;; Here is a summary of keystrokes available in every Wiki buffer:
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
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
90 ;; http://www.gci-net.com/~johnw/emacs.html
92 ;; * ChangeLog support
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
98 ;; * Changing title or stylesheet
100 ;; For convenience, if you want to change the visible title, or the
101 ;; stylesheet, used by a certain Wiki page during HTML publishing,
104 ;; #title Hello there
107 ;; at the top of the page.
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:
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\">"))
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.
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:
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
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
149 ;; Thomas Link (<t.link AT gmx DOT at)
153 ;; The parts of this code, and work to be done:
155 ;; * setup emacs-wiki major mode
156 ;; * generate WikiName list
157 ;; * utility functions to extract link parts
159 ;; * navigate links in the buffer
161 ;; * search Wiki pages for text/backlinks
162 ;; * index generation
163 ;; * buffer highlighting (using font-lock)
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)
171 (defvar emacs-wiki-version "$Id"
172 "The version of emacs-wiki currently loaded")
177 ;(eval-when-compile (require 'cl))
179 ;; load pcomplete if it's available
180 (load "pcomplete" t t)
182 (defvar emacs-wiki-under-windows-p (memq system-type '(ms-dos windows-nt)))
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.
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.
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."
200 (defcustom emacs-wiki-mode-hook
201 (append (if (featurep 'table)
203 (unless (featurep 'httpd)
204 '(emacs-wiki-use-font-lock)))
205 "A hook that is run when emacs-wiki mode is entered."
207 :options '(emacs-wiki-use-font-lock
208 emacs-wiki-highlight-buffer
211 highlight-changes-mode)
215 (defcustom emacs-wiki-directories '("~/Wiki")
216 "A list of directories where Wiki pages can be found."
218 :type '(repeat :tag "Wiki directories" directory)
221 (defcustom emacs-wiki-default-page "WelcomePage"
222 "Name of the default page used by \\[emacs-wiki-find-file]."
226 (defcustom emacs-wiki-file-ignore-regexp
227 "\\`\\(\\.?#.*\\|.*,v\\|.*~\\|\\.\\.?\\)\\'"
228 "A regexp matching files to be ignored in Wiki directories."
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."
238 (defcustom emacs-wiki-interwiki-names
239 '(("GnuEmacs" . "http://www.gnu.org/software/emacs/emacs.html")
242 (concat "http://www.emacswiki.org/cgi-bin/wiki.pl?"
243 (or tag "SiteMap"))))
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:
252 (WIKINAME . STRING-OR-FUNCTION)
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.
260 Here are some examples:
262 (\"JohnWiki\" . \"http://alice.dynodns.net/wiki?\")
264 Referring to [[JohnWiki#EmacsModules]] then really means:
266 http://alice.dynodns.net/wiki?EmacsModules
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
273 :type '(repeat (cons (string :tag "WikiName")
274 (choice (string :tag "URL") function)))
277 (defvar emacs-wiki-url-or-name-regexp nil
278 "Matches either a Wiki link or a URL. This variable is auto-generated.")
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.")
283 (defcustom emacs-wiki-extended-link-regexp
284 "\\[\\[\\([^] \t\n]+\\)\\]\\(\\[\\([^]\n]+\\)\\]\\)?\\]"
285 "Regexp used to match [[extended][links]]."
289 (defun emacs-wiki-count-chars (string char)
294 (if (eq char (aref string i))
295 (setq count (1+ count)))
299 (defun emacs-wiki-set-sym-and-url-regexp (sym value)
300 (setq emacs-wiki-url-or-name-regexp
302 (if (eq sym 'emacs-wiki-name-regexp)
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
310 emacs-wiki-url-or-name-regexp-group-count
311 (- (emacs-wiki-count-chars
312 emacs-wiki-url-or-name-regexp ?\() 2))
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."
320 :set 'emacs-wiki-set-sym-and-url-regexp
323 (defcustom emacs-wiki-url-regexp
324 (concat "\\<\\(https?:/?/?\\|ftp:/?/?\\|gopher://\\|"
325 "telnet://\\|wais://\\|file:/\\|s?news:\\|"
327 "[^] \n \"'()<>[^`{}]*[^] \n \"'()<>[^`{}.,;]+")
328 "A regexp used to match URLs within a Wiki buffer."
330 :set 'emacs-wiki-set-sym-and-url-regexp
333 (defcustom emacs-wiki-browse-url-function 'browse-url
334 "Function to call to browse a URL."
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.
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:
349 glimpseindex -o <list of Wiki directories>
351 Once that's completed, customize this variable to have the following
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
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)
375 (define-key map [(control ?c) (control ?l)] 'font-lock-mode)
377 (define-key map [(control ?c) ?=]
380 (diff-backup buffer-file-name)))
382 (define-key map [tab] 'emacs-wiki-next-reference)
383 (define-key map [(control ?i)] 'emacs-wiki-next-reference)
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))
390 (when (featurep 'pcomplete)
391 (define-key map [(meta tab)] 'pcomplete)
392 (define-key map [(meta control ?i)] 'pcomplete))
395 "Keymap used by Emacs Wiki mode.")
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)))
407 "Local keymap used by emacs-wiki while on a WikiName.")
411 (defvar emacs-wiki-project nil)
414 (define-derived-mode emacs-wiki-mode text-mode "Wiki"
415 "An Emacs mode for maintaining a local Wiki database.
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.
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.
431 In order to follow a link, hit RET when point is on the link, or use
434 All wikis reside in the `emacs-wiki-directories'.
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
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)))
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))))
460 (defsubst emacs-wiki-directory-part (path)
461 (directory-file-name (expand-file-name path)))
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))
469 (if (string= here (emacs-wiki-directory-part (if (consp (car d))
472 (setq yes (car d) d nil)
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)
481 (while (and (not yes) projs)
482 (let* ((projsyms (cdar projs))
483 (pred (assq 'emacs-wiki-predicate projsyms))
486 (setq yes (funcall (cdr pred)))
487 (setq dirs (assq 'emacs-wiki-directories projsyms))
489 (setq yes (emacs-wiki-directories-member (cdr dirs)))))
491 (setq project (caar projs)
492 mode-func (or (cdr (assq 'emacs-wiki-major-mode projsyms))
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)))
501 (add-hook 'find-file-hooks 'emacs-wiki-maybe)
503 ;;; Support WikiName completion using pcomplete
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)))))
511 (defun emacs-wiki-current-word ()
515 (skip-chars-backward "^\\[ \t\n")
516 (narrow-to-region (point) end))
517 (pcomplete-parse-buffer-arguments))))
519 ;;; Return an list of known wiki names and the files they represent.
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)))))
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."
532 (setq name buffer-file-name))
534 (let ((page (file-name-nondirectory name)))
535 (if (string-match emacs-wiki-ignored-extensions-regexp page)
536 (replace-match "" t t page)
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))))
545 (defvar emacs-wiki-file-alist nil)
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)
555 (unless (or emacs-wiki-under-windows-p no-check-p)
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)))
562 (if (or (and no-check-p (cadr file-alist))
563 (not (or emacs-wiki-under-windows-p
564 (null (cddr file-alist))
566 (emacs-wiki-time-less-p (cddr file-alist) last-mod))))
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)))
575 (let* ((names (list t))
578 (if (file-readable-p (car dirs))
579 (let ((files (directory-files (car dirs) t nil t)))
582 (or (file-directory-p (car files))
583 (string-match emacs-wiki-file-ignore-regexp
584 (file-name-nondirectory
587 (cons (cons (emacs-wiki-page-name (car files))
589 (setq lnames (cdr lnames)))
590 (setq files (cdr files)))))
591 (setq dirs (cdr dirs)))
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))
601 (let* ((projsyms (cdar projs))
602 (dirs (cdr (assq 'emacs-wiki-directories projsyms))))
604 (add-to-list 'emacs-wiki-directories (car dirs))
605 (setq dirs (cdr dirs))))
606 (setq projs (cdr projs)))
607 (emacs-wiki-file-alist)))
609 ;; Utility functions to extract parts of a Wiki name
611 (defvar emacs-wiki-serving-p nil
612 "Non-nil when emacs-wiki is serving a wiki page directly.")
614 (defsubst emacs-wiki-transform-name (name)
615 "Transform NAME as per `emacs-wiki-publishing-transforms', returning NAME"
619 (let ((reg (car elt))
621 (when (string-match reg name)
622 (setq name (replace-match rep t nil name))))))
623 emacs-wiki-publishing-transforms)
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
633 (setq name (file-relative-name name
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)
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)))))
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
651 emacs-wiki-publishing-directory))
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.
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'
663 (setq emacs-wiki-publishing-directory \"/webserver:/var/www/\")
664 (setq emacs-wiki-publishing-transforms
665 ((\".*Wiki.*\" . \"emacs/wiki/\\&\")
666 (\"EmacsWiki\\|WelcomePage\" . \"index\")))
668 Then when trying to publish a page EmacsWiki:
670 (emacs-wiki-published-file \"EmacsWiki\")
674 \"/webserver:/var/www/emacs/wiki/index.html\""
677 (regexp :tag "String to match")
678 (string :tag "Replacement string")))
679 :group 'emacs-wiki-publish)
681 (defsubst emacs-wiki-wiki-url-p (name)
682 "Return non-nil if NAME is a URL."
684 (string-match emacs-wiki-url-regexp name)))
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."
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))))
706 (defun emacs-wiki-wiki-tag (wiki-name)
708 (if (string-match "#" wiki-name)
709 (substring wiki-name (match-end 0)))))
711 (defun emacs-wiki-wiki-link-target (wiki-name)
712 "Return the target of a Wiki link. This might include anchor tags."
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)))
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))))
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."
735 (let ((file (emacs-wiki-wiki-link-target wiki-name)))
736 (if (emacs-wiki-wiki-url-p file)
738 (if (string-match "#" file)
739 (substring file 0 (match-beginning 0))
742 ;;; Open a Wiki page (with completion)
744 (defvar emacs-wiki-history-list nil)
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))
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."
764 (let ((num (prefix-numeric-value current-prefix-arg)))
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).
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)))
786 (let* ((dirname (or directory
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))))
794 ;;; Navigate/visit links or URLs. Use TAB, S-TAB and RET (or mouse-2).
796 (defun emacs-wiki-next-reference ()
797 "Move forward to next Wiki link or URL, cycling if necessary."
799 (let ((case-fold-search nil)
802 (if (emacs-wiki-link-at-point)
803 (goto-char (match-end 0)))
805 (if (re-search-forward emacs-wiki-url-or-name-regexp nil t)
806 (setq pos (match-beginning 0)
808 (goto-char (point-min))
809 (setq cycled (1+ cycled)))))
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."
817 (let ((case-fold-search nil)
821 (if (re-search-backward emacs-wiki-url-or-name-regexp nil t)
824 (goto-char (point-max))
825 (setq cycled (1+ cycled)))))
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
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))))
844 (goto-char (point-min))
845 (re-search-forward (concat "^\\.?#" tag) nil t)))))))
847 (unless (fboundp 'line-end-position)
848 (defsubst line-end-position (&optional N)
849 (save-excursion (end-of-line N) (point))))
851 (unless (fboundp 'line-beginning-position)
852 (defsubst line-beginning-position (&optional N)
853 (save-excursion (beginning-of-line N) (point))))
855 (unless (fboundp 'match-string-no-properties)
856 (defalias 'match-string-no-properties 'match-string))
858 (defun emacs-wiki-link-at-point (&optional pos)
859 "Return non-nil if a URL or Wiki link name is at point."
861 (and (char-after pos)
862 (not (eq (char-syntax (char-after pos)) ? ))))
863 (let ((case-fold-search nil)
864 (here (or pos (point))))
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))))))))
873 (defun emacs-wiki-follow-name-at-point ()
874 "Visit the link at point, or insert a newline if none."
876 (if (emacs-wiki-link-at-point)
877 (emacs-wiki-visit-link (match-string 0))
878 (error "There is no valid link at point")))
880 (defun emacs-wiki-follow-name-at-mouse (event)
881 "Visit the link at point, or yank text if none."
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)))))
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)))
899 (rename-file base new-name)
900 (rename-file file new-name))))
902 (defun emacs-wiki-rename-link-at-point ()
903 "Rename the link under point, and the location it points to. This does not
906 (let (new-name old-name)
907 (if (emacs-wiki-link-at-point)
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"))))
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)))
928 (delete-file file))))
930 (defun emacs-wiki-delete-link-at-point ()
931 "Delete the link under point, and the location it points to. This does not
935 (if (emacs-wiki-link-at-point)
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"))))
944 ;;; Find text in Wiki pages, or pages referring to the current page
946 (defvar emacs-wiki-search-history nil)
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."
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)))
963 (defun emacs-wiki-search (text)
964 "Search for the given TEXT string in the Wiki directories."
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)
971 (setq str (replace-match "" t t str)))
972 (read-from-minibuffer "Search command: "
974 nil nil 'emacs-wiki-search-history))))
975 (emacs-wiki-grep nil text))
977 (defun emacs-wiki-backlink ()
978 "Grep for the current pagename in all the Wiki directories."
980 (emacs-wiki-grep (emacs-wiki-page-name)))
982 ;;; Generate an index of all known Wiki pages
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*")
990 (emacs-wiki-change-project project))
991 (let ((files (sort (copy-alist (emacs-wiki-file-alist))
994 (string-lessp (car l) (car r))))))
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))))
1003 (defun emacs-wiki-index ()
1004 "Display an index of all known Wiki pages."
1006 (message "Generating Wiki index...")
1007 (pop-to-buffer (emacs-wiki-generate-index))
1008 (goto-char (point-min))
1010 (message "Generating Wiki index...done"))
1012 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1014 ;; Emacs Wiki Highlighting
1016 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1018 (defgroup emacs-wiki-highlight nil
1019 "Options controlling the behaviour of Emacs Wiki highlighting.
1020 See `emacs-wiki-highlight-buffer' for more information."
1023 (defun emacs-wiki-make-faces ()
1024 (mapc (lambda (newsym)
1027 (setq newsym (intern (concat "emacs-wiki-header-"
1028 (int-to-string num))))
1031 (eval `(defface ,newsym
1033 ,(nth (1- num) '("24pt" "18pt" "14pt" "12pt"))
1035 "emacs-wiki header face"
1036 :group 'emacs-wiki-highlight)))
1037 ((< emacs-major-version 21)
1038 (copy-face 'default newsym))
1040 (eval `(defface ,newsym
1041 '((t (:height ,(1+ (* 0.1 (- 5 num)))
1042 :inherit variable-pitch
1044 "emacs-wiki header face"
1045 :group 'emacs-wiki-highlight))))))
1047 (emacs-wiki-make-faces)
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))
1055 "Face for Wiki cross-references."
1056 :group 'emacs-wiki-highlight)
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))
1064 "Face for bad Wiki cross-references."
1065 :group 'emacs-wiki-highlight)
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."
1073 :group 'emacs-wiki-highlight)
1075 (defcustom emacs-wiki-inline-images (and (not (featurep 'xemacs))
1076 (>= emacs-major-version 21)
1078 "If non-nil, inline locally available images within Wiki pages."
1080 :group 'emacs-wiki-highlight)
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:
1088 [[./wife.jpg][A picture of my wife]]
1090 If you omit the description, the alt tag of the resulting HTML buffer will be
1091 the name of the file."
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]]"
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."
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."
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.
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.
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
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.
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.
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
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.
1168 Like verbatim, but typesets in HTML using the <pre> tag, with
1169 class=example, so whitespace formatting is preserved.
1172 Inhibits wiki markup, but does not do any escaping to the underlying
1173 publishing medium. Useful for embedding HTML, PHP, etc.
1176 Typesets like a normal paragraph, but without word-wrapping.
1177 That is, whitespace is preserved.
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.
1186 When placed before a WikiLink, it will prevent that WikiLink from
1187 being treated as such. Good for names like DocBook.
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
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.
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,
1208 Pass the region to the Python or Perl language interpretor, and
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.
1216 Note: If c2html is not available, the region will be converted to
1217 HTML friendly text (i.e., <> turns into <>), and placed in a
1218 <pre> block. In this case, line numbering is not available.
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.
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)
1236 :group 'emacs-wiki-highlight)
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
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)
1254 :group 'emacs-wiki-highlight)
1256 (defvar emacs-wiki-highlight-regexp nil)
1257 (defvar emacs-wiki-highlight-vector nil)
1259 (defun emacs-wiki-configure-highlighting (sym val)
1260 (setq emacs-wiki-highlight-regexp
1261 (concat "\\(" (mapconcat (function
1263 (if (symbolp (car rule))
1264 (symbol-value (car rule))
1265 (car rule)))) val "\\|") "\\)")
1266 emacs-wiki-highlight-vector (make-vector 128 nil))
1269 (if (eq (cadr (car rules)) t)
1270 (let ((i 0) (l 128))
1272 (unless (aref emacs-wiki-highlight-vector i)
1273 (aset emacs-wiki-highlight-vector i
1274 (nth 2 (car rules))))
1276 (aset emacs-wiki-highlight-vector (cadr (car rules))
1277 (nth 2 (car rules))))
1278 (setq rules (cdr rules))))
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."
1286 (let ((len (length str)))
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)))
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))
1311 (save-excursion (funcall predicate beg end))))
1313 ;; mark whole lines as a multiline font-lock
1315 (setq beg (line-beginning-position))
1317 (setq end (line-end-position))
1318 (add-text-properties beg end '(font-lock-multiline t))
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:
1329 ;; and the following is invalid:
1331 (let* ((beg (match-beginning 0))
1337 (unless (save-excursion
1339 (when (save-match-data (looking-at "^\\*\\{1,3\\} "))
1340 (add-text-properties
1341 (line-beginning-position) (line-end-position)
1343 (intern (concat "emacs-wiki-header-"
1344 (int-to-string (1+ leader))))))
1346 ;; it might be an normal, emphasised piece of text
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
1359 ;; ensures we only mark the region as multiline if it's correctly
1360 ;; delimited at the start
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))))))))
1368 (defun emacs-wiki-highlight-underlined ()
1369 (let ((start (- (point) 2))
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)))))
1376 (defun emacs-wiki-highlight-verbatim ()
1377 (let ((start (- (point) 2))
1379 (when (setq end (emacs-wiki-highlight-ok-context-p start end "="))
1380 (search-forward "=" end t))))
1382 (defcustom emacs-wiki-highlight-markup
1383 `(;; render in teletype and suppress further parsing
1384 ("=[^\t =]" ?= emacs-wiki-highlight-verbatim)
1386 ;; make emphasized text appear emphasized
1387 ("\\*+" ?* emacs-wiki-highlight-emphasized)
1389 ;; make underlined text appear underlined
1390 ("_[^ \t_]" ?_ emacs-wiki-highlight-underlined)
1392 ;; make quadruple quotes invisible
1396 (add-text-properties (match-beginning 0) (match-end 0)
1397 '(invisible t intangible t)))))
1399 ("^#title" ?\# emacs-wiki-highlight-title)
1401 (emacs-wiki-url-or-name-regexp t emacs-wiki-highlight-link)
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
1409 Each element of the list is itself a list, of the form:
1411 (LOCATE-REGEXP TEST-CHAR MATCH-FUNCTION)
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.
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.
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.
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).
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.
1436 Lastly, none of the regexp should contain grouping elements that will
1437 affect the match data results."
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))
1445 :set 'emacs-wiki-configure-highlighting
1446 :group 'emacs-wiki-highlight)
1448 (defvar font-lock-mode nil)
1449 (defvar font-lock-multiline nil)
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)
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)))
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))))))
1475 (put 'emacs-wiki-mode 'flyspell-mode-predicate
1476 'emacs-wiki-mode-flyspell-verify)
1478 (defun emacs-wiki-eval-lisp (form)
1479 "Evaluate the given form and return the result as a string."
1482 (let ((object (eval (read form))))
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)))))
1490 (number-to-string object))
1491 ((eq object nil) "")
1493 (pp-to-string object))))))
1495 (defun emacs-wiki-highlight-buffer ()
1496 "Re-highlight the entire Wiki buffer."
1498 (emacs-wiki-highlight-region (point-min) (point-max) t))
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))
1514 ;; check to see if we should expand the beg/end area for
1515 ;; proper multiline matches
1516 (when (and font-lock-multiline
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)
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)
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))
1538 (and (< (point) end)
1539 (re-search-forward emacs-wiki-highlight-regexp end t))
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
1547 (if verbose (message "Highlighting buffer...done")))))
1548 (set-buffer-modified-p modified-p))))
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))
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))))
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))
1575 (save-excursion (funcall predicate beg end))))
1577 ;; mark whole lines as a multiline font-lock
1579 (setq beg (line-beginning-position))
1581 (setq end (line-end-position))
1582 (add-text-properties beg end '(font-lock-multiline t))
1585 (defvar emacs-wiki-keymap-property
1586 (if (or (featurep 'xemacs)
1587 (>= emacs-major-version 21))
1591 (defsubst emacs-wiki-link-properties (help-str &optional 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
1599 emacs-wiki-keymap-property emacs-wiki-local-map)))
1601 (defun emacs-wiki-highlight-link ()
1602 (if (eq ?\[ (char-after (match-beginning 0)))
1603 (if (and emacs-wiki-inline-images
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)
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
1624 (string-match emacs-wiki-image-regexp (match-string 0))))
1625 (emacs-wiki-inline-image (match-beginning 0) (match-end 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)
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)))))
1639 (defun emacs-wiki-inline-image (beg end url &optional desc)
1640 "Inline locally available images."
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))))))
1652 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1654 ;; Emacs Wiki Publishing (to HTML by default)
1656 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1658 (defgroup emacs-wiki-publish nil
1659 "Options controlling the behaviour of Emacs Wiki publishing.
1660 See `emacs-wiki-publish' for more information."
1663 (defcustom emacs-wiki-maintainer (concat "mailto:webmaster@" (system-name))
1664 "URL where the maintainer can be reached."
1666 :group 'emacs-wiki-publish)
1668 (defcustom emacs-wiki-home-page emacs-wiki-default-page
1669 "Title of the Wiki Home page."
1671 :group 'emacs-wiki-publish)
1673 (defcustom emacs-wiki-index-page "WikiIndex"
1674 "Title of the Wiki Index page."
1676 :group 'emacs-wiki-publish)
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)
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."
1689 :group 'emacs-wiki-publish)
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)
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)
1703 (defcustom emacs-wiki-publishing-directory "~/WebWiki"
1704 "Directory where all wikis are published to."
1706 :group 'emacs-wiki-publish)
1708 (defcustom emacs-wiki-publishing-file-prefix ""
1709 "This prefix will be prepended to all wiki names when publishing."
1711 :group 'emacs-wiki-publish)
1713 (defcustom emacs-wiki-publishing-file-suffix ".html"
1714 "This suffix will be appended to all wiki names when publishing."
1716 :group 'emacs-wiki-publish)
1718 (defcustom emacs-wiki-before-markup-hook nil
1719 "A hook run in the buffer where markup is done, before it is done."
1721 :group 'emacs-wiki-publish)
1723 (defcustom emacs-wiki-after-markup-hook nil
1724 "A hook run in the buffer where markup is done, after it is done."
1726 :group 'emacs-wiki-publish)
1728 (defcustom emacs-wiki-meta-http-equiv "Content-Type"
1729 "The http-equiv attribute used for the HTML <meta> tag."
1731 :group 'emacs-wiki-publish)
1733 (defcustom emacs-wiki-meta-content-type "text/html"
1734 "The content type used for the HTML <meta> tag."
1736 :group 'emacs-wiki-publish)
1738 (defcustom emacs-wiki-meta-content-coding
1739 (if (featurep 'mule)
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)
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'"
1752 :group 'emacs-wiki-publish)
1754 (defcustom emacs-wiki-coding-default 'iso-8859-1
1755 "The default emacs coding use if no special characters are found"
1757 :group 'emacs-wiki-publish)
1759 (defcustom emacs-wiki-coding-map
1760 '((iso-2022-jp "iso-2022-jp")
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)
1768 (defcustom emacs-wiki-redirect-delay 1
1769 "The number of seconds to delay before doing a page redirect."
1771 :group 'emacs-wiki-publish)
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.")
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."
1784 :group 'emacs-wiki-publish)
1786 (defcustom emacs-wiki-publishing-header
1787 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">
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>
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."
1807 :group 'emacs-wiki-publish)
1809 (defcustom emacs-wiki-publishing-footer
1811 <!-- Page published by Emacs Wiki ends here -->
1812 <div class=\"navfoot\">
1814 <table width=\"100%\" border=\"0\" summary=\"Footer navigation\">
1816 <td width=\"33%\" align=\"left\">
1818 (if buffer-file-name
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))
1827 (emacs-wiki-link-href
1828 (concat \"editwiki?\" (emacs-wiki-page-name))
1833 <td width=\"34%\" align=\"center\">
1834 <span class=\"foothome\">
1837 (and (emacs-wiki-page-file emacs-wiki-home-page t)
1838 (not (emacs-wiki-private-p emacs-wiki-home-page))
1840 (emacs-wiki-link-href emacs-wiki-home-page \"Home\")
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\"))
1847 (emacs-wiki-link-href \"ChangeLog\" \"Changes\"))))
1851 <td width=\"33%\" align=\"right\">
1853 (if emacs-wiki-serving-p
1855 \"<span class=\\\"footfeed\\\">\"
1856 (emacs-wiki-link-href \"searchwiki?get\" \"Search\")
1857 (and buffer-file-name
1860 (emacs-wiki-link-href
1861 (concat \"searchwiki?q=\" (emacs-wiki-page-name))
1871 "Text to append to a wiki being published.
1872 This text may contain <lisp> markup tags."
1874 :group 'emacs-wiki-publish)
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'."
1880 :group 'emacs-wiki-publish)
1882 (defcustom emacs-wiki-style-sheet
1883 "<style type=\"text/css\">
1886 background-color: #F8F8F8; color: #FF2222;
1889 a.nonexistent:visited {
1890 background-color: #F8F8F8; color: #FF2222;
1894 background: white; color: black;
1895 margin-left: 5%; margin-right: 5%;
1899 em { font-style: italic; }
1900 strong { font-weight: bold; }
1902 ul { list-style-type: disc }
1904 dl.contents { margin-top: 0; }
1905 dt.contents { margin-bottom: 0; }
1914 font-family: monospace;
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.
1922 Here is an example of using a <link> tag:
1924 <link rel=\"stylesheet\" type=\"text/css\" href=\"emacs-wiki.css\">"
1926 :group 'emacs-wiki-publish)
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.")
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>."
1937 :group 'emacs-wiki-publish)
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:
1948 [... contents of my table, in raw HTML ...]
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."
1954 :group 'emacs-wiki-publish)
1956 (defcustom emacs-wiki-report-threshhold 100000
1957 "If a Wiki file is this size or larger, report publishing progress."
1959 :group 'emacs-wiki-publish)
1961 (defcustom emacs-wiki-publishing-markup
1963 ["&\\([-A-Za-z_#0-9]+\\);" 0 emacs-wiki-markup-entity]
1965 ;; change the displayed title or the stylesheet for a given page
1966 ["\\`#\\(title\\|style\\)\\s-+\\(.+\\)\n+" 0
1967 emacs-wiki-markup-initial-directives]
1969 ;; process any markup tags
1970 [emacs-wiki-tag-regexp 0 emacs-wiki-markup-custom-tags]
1972 ;; emphasized or literal text
1973 ["\\(^\\|[-[ \t\n<('`\"]\\)\\(=[^= \t\n]\\|_[^_ \t\n]\\|\\*+[^* \t\n]\\)"
1974 2 emacs-wiki-markup-word]
1976 ;; headings, outline-mode style
1977 ["^\\(\\*+\\)\\s-+" 0 emacs-wiki-markup-heading]
1979 ;; define anchor points
1980 ["^#\\([A-Za-z0-9_%]+\\)\\s-*" 0 emacs-wiki-markup-anchor]
1982 ;; horizontal rule, or section separator
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]
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"]
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"]
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
2005 (if (featurep 'table)
2006 "^\\(\\s-*\\)\\(\\+[-+]+\\+[\n\r \t]+|\\)"
2007 "^\\s-*\\(\\([^|\n]+\\(|+\\)\\s-*\\)+\\)\\([^|\n]+\\)?$")
2008 1 'emacs-wiki-markup-table)
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
2018 ["^\\(\\s-*\\(-\\|[0-9]+\\.\\|\\(.+\\)[ \t]+::\n?\\)\\)?\\([ \t]+\\)" 4
2019 emacs-wiki-markup-list-or-paragraph]
2021 ;; "verse" text is indicated the same way as a quoted e-mail
2022 ;; response: "> text", where text may contain initial whitespace
2024 ["<p>\\s-+> \\(\\([^\n]\n?\\)+\\)\\(\\s-*</p>\\)?" 0
2025 emacs-wiki-markup-verse]
2027 ;; join together the parts of a list
2028 ["</\\([oud]l\\)>\\s-*\\(</p>\\s-*<p>\\s-*\\)?<\\1>\\s-*" 0 ""]
2030 ;; join together the parts of a table
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 ""]
2037 ;; fixup paragraph delimiters
2039 (concat "<p>\\s-*\\(</?" emacs-wiki-block-groups-regexp ">\\)") 0 "\\1")
2040 (vector (concat "\\(</?" emacs-wiki-block-groups-regexp
2041 ">\\)\\s-*\\(</p>\\)") 3 "\\1")
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"]
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]
2056 ;; bare email addresses
2059 "\\([^:.@/a-zA-Z0-9]\\)"
2060 "\\([-a-zA-Z0-9._]+@\\([-a-zA-z0-9_]+\\.\\)+[a-zA-Z0-9]+\\)"
2061 "\\([^\"a-zA-Z0-9]\\)")
2063 "\\1<a href=\"mailto:\\2\">\\2</a>\\4")
2065 ;; replace quotes, since most browsers don't understand `` and ''
2066 ["\\(``\\|''\\)" 0 "\""]
2068 ;; insert the default publishing header
2071 (insert emacs-wiki-publishing-header)))
2073 ;; insert the default publishing footer
2076 (goto-char (point-max))
2077 (insert emacs-wiki-publishing-footer)))
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:
2084 [REGEXP/SYMBOL TEXT-BEGIN-GROUP REPLACEMENT-TEXT/FUNCTION/SYMBOL].
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.
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.
2100 Here is a description of the default markup rules:
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).
2120 ***very strong emphasis***
2124 <verbatim>This tag should be used for larger blocks of
2129 A reference[1], which is just a number in square brackets,
2130 constitutes a footnote reference.
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.
2141 One or more blank lines separates paragraphs.
2143 Centered paragraphs and quotations
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.
2150 If a line begins with some whitespace, but less than six columns, it
2151 indicates a quoted paragraph.
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:
2159 > A line of Emacs verse;
2160 > forgive its being so terse.
2162 You can also use the <verse> tag, if you prefer:
2165 A line of Emacs verse;
2166 forgive its being so terse.
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:
2177 Some literal text or code here.
2186 Term :: A definition list
2188 Blank lines between list elements are optional, but required between
2189 members of a definition list.
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.
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:
2201 Double bars || Separate header fields
2202 Single bars | Separate body fields
2203 Here are more | body fields
2204 Triple bars ||| Separate footer fields
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.
2211 Anchors and tagged links
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
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.
2223 Redirecting to another page or URL
2225 Sometimes you may wish to redirect someone to another page. To do
2228 <redirect url=\"http://somewhereelse.com\"/>
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
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.
2241 A regular URL is given as a link. If it's an image URL, it will
2242 be inlined using an IMG tag.
2246 <lisp>(concat \"This form gets\" \"inserted\")</lisp>
2248 Special handling of WikiNames
2250 If you need to add a plural at the end of a WikiName, separate it
2251 with four single quotes: WikiName''''s.
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
2259 Besides the normal WikiName type links, emacs-wiki also supports
2262 [[link text][optional link description]]
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.
2270 See the documentation to emacs-wiki-image-regexp for how to inline
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
2284 MeatballWiki#MeatBall
2286 In the resulting HTML, the link is simply shown as
2287 \"MeatballWiki:MeatBall\"."
2290 (vector :tag "Markup rule"
2291 (choice regexp symbol)
2293 (choice string function symbol))
2295 :group 'emacs-wiki-publish)
2297 (defcustom emacs-wiki-changelog-markup
2299 ;; process any custom markup tags
2300 [emacs-wiki-tag-regexp 0 emacs-wiki-markup-custom-tags]
2306 ["^\\(\\S-+\\)\\s-+\\(.+\\)" 0 emacs-wiki-markup-changelog-section]
2308 ;; emphasized or literal text
2309 ["\\(^\\|[-[ \t\n<('`\"]\\)\\(=[^= \t\n]\\|_[^_ \t\n]\\|\\*+[^* \t\n]\\)"
2310 2 emacs-wiki-markup-word]
2312 ;; headings, outline-mode style
2313 ["^\\*\\s-+\\(.+\\)$" 0 "<h2>\\1</h2>"]
2315 ;; escape the 'file' entries, incase they are extended wiki links
2316 ["^[ \t]+\\* \\([^:(]+\\)\\([ \t]+(\\|:\\)" 0 emacs-wiki-changelog-escape-files]
2318 ;; don't require newlines between unnumbered lists.
2319 ["^\\s-*\\(\\*\\)" 1 "\n\\1"]
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"]
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
2334 ["^\\(\\s-*\\(\\*\\)\\)?\\([ \t]+\\)\\(\\([^\n]\n?\\)+\\)" 3
2335 "<ul>\n<li>\\4</ul>\n"]
2337 ;; join together the parts of a list
2338 ["</\\([oud]l\\)>\\s-*\\(</p>\\s-*<p>\\s-*\\)?<\\1>\\s-*" 0 ""]
2340 ;; fixup paragraph delimiters
2342 (concat "<p>\\s-*\\(</?" emacs-wiki-block-groups-regexp ">\\)") 0 "\\1")
2343 (vector (concat "\\(</?" emacs-wiki-block-groups-regexp
2344 ">\\)\\s-*\\(</p>\\)") 3 "\\1")
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"]
2353 ;; bare email addresses
2356 "\\([^:.@/a-zA-Z0-9]\\)"
2357 "\\([-a-zA-Z0-9._]+@\\([-a-zA-z0-9_]+\\.\\)+[a-zA-Z0-9]+\\)"
2358 "\\([^\"a-zA-Z0-9]\\)")
2360 "\\1<a href=\"mailto:\\2\">\\2</a>\\4")
2362 ;; replace WikiLinks in the buffer (links to other pages)
2363 [emacs-wiki-url-or-name-regexp 0 emacs-wiki-markup-link]
2366 ;; insert the default publishing header
2369 (insert emacs-wiki-publishing-header)))
2371 ;; insert the default publishing footer
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."
2380 (vector :tag "Markup rule"
2381 (choice regexp symbol)
2383 (choice string function symbol))
2385 :group 'emacs-wiki-publish)
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)))
2395 emacs-wiki-charset-default)))
2397 (defun emacs-wiki-private-p (name)
2398 "Return non-nil if NAME is a private page, and shouldn't be published."
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))))
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))))
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
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))))
2438 (defun emacs-wiki-visit-published-file (&optional arg)
2439 "Visit the current wiki page's published result."
2442 (find-file-other-window (emacs-wiki-published-file))
2443 (funcall emacs-wiki-browse-url-function
2444 (concat "file:" (emacs-wiki-published-file)))))
2446 (defun emacs-wiki-dired-publish ()
2447 "Publish all marked files in a dired buffer."
2449 (emacs-wiki-publish-files (dired-get-marked-files) t))
2451 (defun emacs-wiki-prettify-title (title)
2452 "Prettify the given TITLE."
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))
2460 (if (member (downcase (car w))
2461 emacs-wiki-downcase-title-words)
2462 (setcar w (downcase (car w))))
2464 (mapconcat 'identity words " ")))))
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."
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))
2486 (setcdr lfiles (cons (cdar names) nil))
2487 (setq lfiles (cdr lfiles)
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)
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)
2505 (defun emacs-wiki-publish-this-page ()
2506 "Force publication of the current page."
2508 (emacs-wiki-publish-files (list buffer-file-name) t))
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)
2517 (setq file (car 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)))
2524 (insert-file-contents file t)
2525 (cd (file-name-directory file))
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))))
2542 (defun emacs-wiki-escape-html-specials (&optional end)
2543 (while (and (or (< (point) end) (not end))
2544 (re-search-forward "[<>&\"]" end t))
2546 ((eq (char-before) ?\")
2549 ((eq (char-before) ?\<)
2552 ((eq (char-before) ?\>)
2555 ((eq (char-before) ?\&)
2557 (insert "&")))))
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
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"
2569 (while (setq pos (string-match "[^-[:alnum:]/:._=@\\?~#]" str pos))
2570 (setq code (int-to-string (aref str pos))
2572 str (replace-match (concat "&#" code ";") nil nil str)
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)))
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)
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))
2606 (if (symbolp regexp)
2607 (setq regexp (symbol-value regexp)))
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)))
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)
2619 ((functionp replacement)
2620 (funcall replacement))
2621 ((symbolp replacement)
2622 (symbol-value replacement))
2626 (replace-match text t)
2628 (replace-match "[FIXME: invalid characters]" t))))))
2629 (if (and last-pos (= pos last-pos))
2633 (setq last-pos pos))))
2634 (setq rules (cdr rules)
2635 base (+ base (point-max))))
2636 (run-hooks 'emacs-wiki-after-markup-hook)
2638 (message "Publishing %s...done" (emacs-wiki-page-name)))))
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)
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)))
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))
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)
2663 (delete-region (match-beginning 0) (point)))
2664 (setq tag-info nil)))
2666 (setq end (point-marker))
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))))))
2678 (defun emacs-wiki-markup-initial-directives ()
2680 ((string= (match-string 1) "title")
2681 (set (make-local-variable 'emacs-wiki-current-page-title) (match-string 2)))
2683 (set (make-local-variable 'emacs-wiki-style-sheet)
2684 (concat "<link rel=\"stylesheet\" type=\"text/css\" href=\""
2685 (match-string 2) "\">"))))
2688 (defalias 'emacs-wiki-markup-custom-tags 'emacs-wiki-custom-tags)
2690 (defun emacs-wiki-highlight-title ()
2691 (add-text-properties (+ 7 (match-beginning 0))
2693 '(face emacs-wiki-header-1)))
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))
2702 (set-match-data match-data)
2703 (emacs-wiki-custom-tags t)))
2705 (defun emacs-wiki-example-tag (beg end highlight-p)
2708 (emacs-wiki-multiline-maybe beg end)
2710 (insert "<pre class=\"example\">")
2711 (emacs-wiki-escape-html-specials end)
2712 (when (< (point) end)
2715 (add-text-properties beg (point) '(rear-nonsticky (read-only)
2718 (defun emacs-wiki-verbatim-tag (beg end highlight-p)
2721 (emacs-wiki-multiline-maybe beg end)
2723 (emacs-wiki-escape-html-specials end)
2724 (add-text-properties beg end '(rear-nonsticky (read-only)
2727 (defun emacs-wiki-nowiki-tag (beg end highlight-p)
2730 (add-text-properties
2731 beg end '(read-nonsticky (read-only) read-only t))))
2733 (defun emacs-wiki-verse-tag (beg end)
2735 (while (< (point) end)
2736 (unless (eq (char-after) ?\n)
2740 (defvar emacs-wiki-numbered-counter 1)
2741 (make-variable-buffer-local 'emacs-wiki-numbered-counter)
2743 (defun emacs-wiki-numbered-tag (beg end)
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>
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)))
2763 (insert (format "</p>
2764 </td></tr></table>" (1+ emacs-wiki-numbered-counter)))))
2766 (defun emacs-wiki-redirect-tag (beg end attrs)
2767 (let ((link (cdr (assoc "url" attrs))))
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)))
2775 (insert "You should momentarily be redirected to [[" link "]].")
2777 (delete-region (point) (point-max)))))
2779 (defun emacs-wiki-nop-tag (beg end 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))
2785 (add-text-properties beg (point)
2786 '(rear-nonsticky (read-only) read-only t)))))
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)))
2801 (insert "<a name=\"" anchor "\" id=\"" anchor "\">")
2802 (when emacs-wiki-anchor-on-word
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)))
2813 (while (re-search-forward "^\\(\\*+\\)\\s-+\\(.+\\)" nil t)
2814 (setq l (length (match-string 1)))
2819 (when (<= l max-depth)
2820 (setq contents (cons (cons l (match-string-no-properties 2))
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")
2829 (insert "<dt class=\"contents\">\n")
2830 (insert "<a href=\"#sec" (int-to-string index) "\">"
2833 (setq index (1+ index))
2835 (setq depth (caar contents)
2836 contents (cdr contents))
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)
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)))
2852 (put-text-property p (point) 'read-only t))))
2854 (defun emacs-wiki-lisp-tag (beg end highlight-p)
2856 (add-text-properties
2858 (list 'font-lock-multiline t
2859 'display (emacs-wiki-eval-lisp
2860 (buffer-substring-no-properties (+ beg 6) (- end 7)))
2863 (insert (emacs-wiki-eval-lisp
2865 (buffer-substring-no-properties beg end)
2866 (delete-region beg end)))))))
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)
2874 (defun emacs-wiki-command-tag (beg end attrs &optional highlight-p pre-tags)
2877 (while (looking-at "\\s-*$")
2879 (let ((interp (or (cdr (assoc "file" attrs))
2880 emacs-wiki-command-default-file)))
2882 (eshell-command (prog1
2883 (buffer-substring-no-properties (point) end)
2884 (delete-region beg end)) t)
2885 (let ((file (make-temp-file "ewiki")))
2887 (let ((args (split-string interp)))
2888 (write-region (point) end file)
2889 (delete-region beg end)
2892 (apply 'call-process (car args) file t nil (cdr args))
2893 (while (eq (char-syntax (char-before)) ? )
2895 (add-text-properties beg (point)
2896 '(rear-nonsticky (read-only)
2899 (insert "</pre>\n")))
2900 (if (file-exists-p file)
2901 (delete-file file))))))))
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."
2909 :group 'emacs-wiki-publish)
2911 (defun emacs-wiki-c-source-tag (beg end attrs highlight-p)
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))))
2920 (emacs-wiki-escape-html-specials end)
2922 (add-text-properties beg (point)
2923 '(rear-nonsticky (read-only) read-only t))
2924 (insert "</pre>"))))
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))
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))
2934 (defun emacs-wiki-insert-xbel-bookmarks (bmarks folder)
2935 "Insert a set of XBEL bookmarks as an HTML list."
2937 (let ((bookmark (car bmarks)))
2939 ((equal (xml-tag-name bookmark) "folder")
2940 (let ((title (cadr (xml-tag-child bookmark "title"))))
2942 (insert "<li>" title "\n<ul>\n"))
2943 (emacs-wiki-insert-xbel-bookmarks (xml-tag-children bookmark)
2944 (if (equal folder title)
2948 (insert "</ul>\n"))))
2949 ((equal (xml-tag-name bookmark) "bookmark")
2951 (insert "<li><a href=\"" (xml-tag-attr bookmark "href") "\">"
2952 (cadr (xml-tag-child bookmark "title")) "</a>\n")))))
2953 (setq bmarks (cdr bmarks))))
2955 (defcustom emacs-wiki-xbel-bin-directory "/usr/bin"
2956 "Directory where the xbel parsing utilities reside."
2958 :group 'emacs-wiki-publish)
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))
2970 (setq buffer (get-buffer-create " *xbel_parse*"))
2971 (with-current-buffer buffer
2974 (format "%s/%s_parse"
2975 (directory-file-name emacs-wiki-xbel-bin-directory) type)
2976 nil t nil filename)))
2978 (setq buffer (find-file-noselect filename))))
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)
2988 (kill-buffer buffer)))
2989 (while (eq (char-syntax (char-before)) ? )
2991 (add-text-properties beg (point)
2992 '(rear-nonsticky (read-only) read-only t)))
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)))
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))
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))))))))
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>"))
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))))
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>"))))))
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)
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))
3051 (let ((l (length leader)))
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))))
3065 (delete-region beg end)
3068 (add-text-properties beg (point)
3069 '(rear-nonsticky (read-only) read-only
3074 (defun emacs-wiki-markup-anchor ()
3076 (emacs-wiki-insert-anchor (match-string 1)))
3079 (defcustom emacs-wiki-entity-table
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."
3092 (defun emacs-wiki-markup-entity ()
3093 (or (cdr (assoc (match-string 1)
3094 emacs-wiki-entity-table))
3095 (concat "&" (match-string 1) ";")))
3097 (defsubst emacs-wiki-surround-text (beg-tag end-tag move-func)
3100 (insert end-tag)) ; returns nil for us
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)
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>"
3112 "<sup>[<a name=\"fn.\\1\" href=\"#fnr.\\1\">\\1</a>]</sup>"
3115 (let* ((beg (goto-char (match-end 0)))
3116 (end (and (search-forward "\n\n" nil t)
3118 (copy-marker (match-beginning 0))
3120 (while (re-search-forward "^[ \t]+\\([^\n]\\)" end t)
3121 (replace-match "\\1" t))))))))
3123 (defsubst emacs-wiki-forward-paragraph ()
3124 (and (re-search-forward "^\\s-*$" nil t)
3125 (match-beginning 0)))
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)
3136 (unless (and (equal tag "blockquote")
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)))
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"
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"
3161 (and (re-search-forward "^\\s-*\\([0-9]+\\.\\|$\\)" nil t)
3162 (goto-char (match-beginning 0)))))))
3164 (goto-char (match-beginning 0))
3165 (insert "<dl>\n<dt>")
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"))))))
3172 (defun emacs-wiki-markup-table ()
3173 (if (featurep 'table)
3174 (let ((leader (match-string 1))
3175 (begin (copy-marker (match-beginning 0)))
3177 (goto-char (match-end 0))
3179 (with-current-buffer (table-generate-source 'html)
3182 (kill-buffer (current-buffer)))))
3184 (if (re-search-backward "<p>[ \t\n\r]+" nil t)
3185 (replace-match (if (>= (string-width leader) 6)
3187 (if (> (length leader) 0)
3190 (delete-region begin (re-search-forward "-+\\+\\s-*[\r\n]+\\s-*$"
3193 (setq end (point-marker))
3195 (while (< (point) end)
3196 (if (looking-at "^\\s-+")
3200 (if (re-search-forward "[ \t\n\r]+</p>" nil t)
3201 (replace-match (if (>= (string-width leader) 6)
3203 (if (> (length leader) 0)
3206 (set-match-data (list begin begin begin begin))
3208 (let* ((str (save-match-data
3209 (if (featurep 'xemacs)
3210 ;; more emacs divergence. :(
3211 (replace-in-string (match-string 1) " *|+ *$" "")
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")
3220 ((= len 3) "tfoot")))
3221 (col (cond ((= len 1) "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"
3230 (defun emacs-wiki-markup-verse ()
3232 (let* ((lines (split-string (match-string 1) "\n"))
3235 (if (and (> (length (car l)) 2)
3236 (string-match "\\`\\s-*> " (car l)))
3237 (setcar l (substring (car l) (match-end 0))))
3239 (concat "<p class=\"verse\">"
3240 (mapconcat 'identity lines "\n") "</p>"))))
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:
3247 begin 644 onepixel.gif
3248 M1TE&.#EA`0`!`*$``````/___________R'Y!`'__P$`+``````!``$```(\"
3253 :group 'emacs-wiki-publish)
3255 (defun emacs-wiki-changelog-escape-files ()
3256 (replace-match "[[\\1]]" t nil nil 1))
3258 (defun emacs-wiki-markup-changelog-section ()
3259 (if (not emacs-wiki-pretty-changelogs)
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))
3267 (insert (format " <TABLE WIDTH=\"100%%\" BORDER=\"0\"
3268 CELLSPACING=\"1\" CELLPADDING=\"2\">
3270 <TD BGCOLOR=\"black\" BACKGROUND=\"onepixel.gif\">
3271 <TABLE WIDTH=\"100%%\" BORDER=\"0\"
3272 CELLPADDING=\"5\" CELLSPACING=\"0\">
3274 <TD ALIGN=\"left\" BGCOLOR=\"b0c4de\" BACKGROUND=\"onepixel.gif\">
3275 <FONT COLOR=\"navy\"> <B>%s</B> </FONT>
3277 <TD ALIGN=\"right\" VALIGN=\"bottom\" BGCOLOR=\"b0c4de\"
3278 BACKGROUND=\"onepixel.gif\">
3279 <FONT SIZE=\"2\" COLOR=\"2f4f4f\"> %s </FONT>
3283 <TD BGCOLOR=\"fffff0\" COLSPAN=\"2\" BACKGROUND=\"onepixel.gif\">
3284 <FONT COLOR=\"black\">
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)
3293 (let ((here (1- (point))))
3303 (add-text-properties here (point)
3304 '(read-only t rear-nonsticky (read-only)))
3307 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3309 ;; Emacs Wiki HTTP Server (using httpd.el)
3311 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3313 (defgroup emacs-wiki-http nil
3314 "Options controlling the behaviour of the Emacs Wiki HTTP server.
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:
3320 http://www.chez.com/emarsden/downloads/
3322 Once you have those two scripts, you must decide between two different
3323 methods of serving pages directly from Emacs:
3325 * PERSISTED INVOCATION SERVER
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.
3332 To use the persisted invoctaion server, you must download the Python
3333 script `httpd-serve' from the same website where you downloaded
3336 http://www.gci-net.com/~johnw/emacs.html
3338 Once you have have downloaded the script, running it is simple:
3340 ./httpd-serve --daemon --port 8080 --load /tmp/my-emacs-wiki \
3341 [path to your HTML files]
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:
3347 (load \"emacs-wiki\")
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.
3353 * AN EMACS SPAWNED PER REQUEST
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
3362 Anyway, to use this approach, add the following line to your
3363 /etc/inted.conf file:
3365 8080 stream tcp nowait.10000 nobody /usr/local/bin/emacs-httpd
3367 The emacs-httpd script should look something like this:
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
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.
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:
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 \\
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"
3396 (defcustom emacs-wiki-http-search-form
3398 <form method=\"GET\" action=\"/searchwiki?get\">
3400 Search for: <input type=\"text\" size=\"50\" name=\"q\" value=\"\">
3401 <input type=\"submit\" value=\"Search!\">
3404 "The form presenting for doing searches when using httpd.el."
3406 :group 'emacs-wiki-http)
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'."
3414 :group 'emacs-wiki-http)
3416 (defcustom emacs-wiki-http-edit-form
3418 <form method=\"POST\" action=\"/changewiki?post\">
3419 <textarea name=\"%PAGE%\" rows=\"25\" cols=\"80\">%TEXT%</textarea>
3421 <input type=\"submit\" value=\"Submit changes\">
3424 "The form presenting for doing edits when using httpd.el."
3426 :group 'emacs-wiki-http)
3428 (defun emacs-wiki-http-send-buffer (&optional title modified code
3430 "Markup and send the contents of the current buffer via HTTP."
3431 (unless no-markup (emacs-wiki-replace-markup title))
3433 (princ (or code 200))
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)
3444 (princ (format-time-string "%a, %e %b %Y %T %Z"))
3445 (princ httpd-line-terminator)
3447 (princ (substring emacs-wiki-maintainer 7))
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)))
3461 (defun emacs-wiki-http-reject (title msg &optional annotation)
3465 (insert annotation "\n"))
3466 (emacs-wiki-http-send-buffer title nil 404 msg)))
3468 (defvar emacs-wiki-buffer-mtime nil)
3469 (make-variable-buffer-local 'emacs-wiki-buffer-mtime)
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)))
3477 ((and (null l-mtime) (null r-mtime)) l)
3480 (t (emacs-wiki-time-less-p r-mtime l-mtime)))))
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))
3486 (let ((entry entries))
3488 (if (funcall predicate (car entry))
3489 (nconc flist (list (car entry))))
3490 (setq entry (cdr entry))))
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."
3499 :group 'emacs-wiki-http)
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
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)))))
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))
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
3536 (setq emacs-wiki-buffer-mtime modified-time))
3537 (goto-char (point-max))
3539 (emacs-wiki-http-send-buffer nil emacs-wiki-buffer-mtime
3541 (insert-file-contents file t)
3542 (cd (file-name-directory file))
3544 (emacs-wiki-http-send-buffer nil emacs-wiki-buffer-mtime)))
3545 (set-buffer-modified-p nil)
3546 (emacs-wiki-prune-cache)))))
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))
3552 (delete-region (point) (point-max))
3553 (goto-char (point-min))
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)))
3563 (nconc (cdr entry) (list text))
3564 (nconc results (list (cons page (list text)))))))))
3565 (delete-region (point-min) (point-max))
3570 (string-lessp (car l) (car r))))))
3572 (unless (emacs-wiki-private-p (caar results))
3573 (insert "[[" (caar results) "]] ::\n <p>")
3574 (let ((hits (cdar results)))
3576 (while (string-match "</?lisp>" (car hits))
3577 (setcar hits (replace-match "" t t (car hits))))
3578 (while (string-match (concat "\\([^*?[/>]\\)\\<\\(" term "\\)\\>")
3580 (setcar hits (replace-match "\\1<strong>\\2</strong>"
3582 (insert " > <verbatim>" (car hits) "</verbatim>\n")
3583 (setq hits (cdr hits))))
3584 (insert "</p>\n\n"))
3585 (setq results (cdr results)))))
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))
3596 (delete-region beg (point))
3598 (insert-file-contents file)
3600 (narrow-to-region beg (point))
3601 (goto-char (point-min))
3602 (emacs-wiki-escape-html-specials)))))
3604 (defun emacs-wiki-http-changewiki (&optional content)
3605 "Change the contents of Wiki page, using the results of a POST request."
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))))
3616 (read-event) ; absorb the CRLF separator
3618 (while (< i content-length)
3619 (insert (read-event))
3621 (setq content (buffer-string)))
3623 (let* ((result (cgi-decode content))
3624 (page (caar result))
3625 (text (cdar result))
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]+\\)"
3636 (setq pos (match-end 0))
3637 (if (assoc (match-string 1 text) emacs-wiki-dangerous-tags)
3638 (setq illegal (match-string 1 text))))
3640 (emacs-wiki-http-reject
3641 "Disallowed tag used"
3642 (format "Public use of <%s> 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
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))
3652 (insert (if (eq (aref text (1- len)) ?%)
3653 (substring text 0 (1- len))
3655 (goto-char (point-min))
3656 (while (re-search-forward "\r$" nil t)
3657 (replace-match "" t t))
3659 ;; this is 0666 - there is no read syntax for octals which
3660 ;; works across all emacsen
3662 (when (/= (file-modes buffer-file-name) oct)
3663 (set-file-modes buffer-file-name oct)))
3664 (kill-buffer (current-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"))))))))
3674 (defvar httpd-vars nil)
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)))
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))))
3684 (defun emacs-wiki-serve-page (page content)
3687 ((string-match "\\`wiki\\?\\(.+\\)" page)
3688 (emacs-wiki-render-page (match-string 1 page)))
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")
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))))))
3701 ((string-match "\\`searchwiki\\?get" page)
3703 (insert "<verbatim>" emacs-wiki-http-search-form "</verbatim>")
3704 (emacs-wiki-http-send-buffer "Search Wiki Pages")))
3706 ((string-match "\\`searchwiki\\?q=\\(.+\\)" page)
3707 (let ((compilation-scroll-output nil)
3708 (term (match-string 1 page)))
3709 (unintern 'start-process)
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)))))
3716 ((string-match "\\`changewiki\\?post" page)
3717 (emacs-wiki-http-changewiki content))
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))
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)
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"))))
3741 (setq handled nil)))
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)))
3750 ;; handle the actual request
3751 (let ((vc-follow-symlinks t)
3752 (emacs-wiki-report-threshhold nil)
3753 (emacs-wiki-serving-p t)
3756 ;; process any CGI variables, if cgi.el is available
3757 (if (string-match "\\`\\([^&]+\\)&" page)
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"))
3764 (with-emacs-wiki-project project
3765 (emacs-wiki-serve-page page content))
3766 (emacs-wiki-serve-page page content)))))
3768 (if (featurep 'httpd)
3769 (httpd-add-handler "\\`\\(index\\.html\\|.*wiki\\(\\?\\|\\'\\)\\)"
3772 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3774 ;; Support for multile Emacs Wiki projects
3776 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3778 (defgroup emacs-wiki-project nil
3779 "Options controlling multi-project behavior in Emacs-Wiki."
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)
3787 (defcustom emacs-wiki-show-project-name-p t
3788 "When true, display the current project name in the mode-line"
3792 ;; this might go away - did anyone prefer the old behavior? tell me!
3793 (defvar emacs-wiki-old-project-change-p nil)
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."
3801 :group 'emacs-wiki-project)
3803 (defun emacs-wiki-update-project-interwikis ()
3804 (let ((projs emacs-wiki-projects))
3807 'emacs-wiki-interwiki-names
3810 (emacs-wiki-project-interwiki-link ,(caar projs) tag))))
3811 (setq projs (cdr projs)))))
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).
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:
3821 (setq emacs-wiki-projects
3822 `((\"default\" . ((emacs-wiki-directories . (\"~/wiki\"))))
3823 (\"work\" . ((fill-column . 65)
3824 (emacs-wiki-directories . (\"~/workwiki/\"))))))
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
3832 VARS is an alist of symbol to value mappings, to be used locally in
3833 all emacs-wiki buffers associated with that PROJECT.
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.
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'.
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
3853 If any variable is not customized specifically for a project, the
3854 global value is used."
3857 :tag "Emacs-Wiki Project"
3858 (string :tag "Project name")
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)
3870 (list 'cons :tag (symbol-name sym)
3872 (get sym 'custom-type))))
3873 (apropos-internal "\\`emacs-wiki-"
3876 (and (not (eq sym 'emacs-wiki-projects))
3877 (get sym 'custom-type))))))))))
3881 (run-hooks 'emacs-wiki-update-project-hook)))
3882 :group 'emacs-wiki-project)
3884 (defmacro with-emacs-wiki-project (project &rest body)
3885 "Evaluate as part of PROJECT the given BODY forms."
3887 (emacs-wiki-change-project ,project)
3890 (put 'with-emacs-wiki-project 'lisp-indent-function 1)
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: "
3897 (let ((projsyms (cdr (assoc project emacs-wiki-projects)))
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))))
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 "]"))))))
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
3929 (provide 'emacs-wiki)
3930 ;;; emacs-wiki.el ends here