diff --git a/manual/docbook/programlisting/README b/manual/docbook/programlisting/README new file mode 100644 index 000000000..137be4857 --- /dev/null +++ b/manual/docbook/programlisting/README @@ -0,0 +1,19 @@ +Making syntax highlighting work +------------------------------- + + ~/p/xsieve/opt/bin/xsieve -xinclude -o testdoc.html testdoc.xsl testdoc.xml + +Development in this directory will be cancelled. +Something like $XSIEVE/examples/syntax_highlighting will be used. + + +colorer.scm: the main part, uniting trees +run-colorer.scm: low-level driver to execute a colorizing program +colorer.xsl: processing of "programlisting" + +testdoc.* + test.xml: testing +run.sh: run "testdoc" conversion + +testdata.xml, test.scm: testing +test-one.scm: testing + diff --git a/manual/docbook/programlisting/colorer-html.xsl b/manual/docbook/programlisting/colorer-html.xsl new file mode 100644 index 000000000..3a428c166 --- /dev/null +++ b/manual/docbook/programlisting/colorer-html.xsl @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/manual/docbook/programlisting/colorer-one.xsl b/manual/docbook/programlisting/colorer-one.xsl new file mode 100644 index 000000000..f52f07f03 --- /dev/null +++ b/manual/docbook/programlisting/colorer-one.xsl @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/manual/docbook/programlisting/colorer.scm b/manual/docbook/programlisting/colorer.scm new file mode 100644 index 000000000..ff1d97f2d --- /dev/null +++ b/manual/docbook/programlisting/colorer.scm @@ -0,0 +1,131 @@ +; $Id: colorer.scm,v 1.8 2006/04/29 04:49:48 olpa Exp $ + +; construct a tree from the text and path +; "ignore" is a list of path element which shouldn't be added to the tree +; each path item is either symbol, which is the node name, +; either (symbol (@ ...)), which is the node name and the attribute node. +; It is supposed that elements with attributes aren't in the ignore list. +(define (colorer:path-to-tree text path ignore) + (let loop ((tree text) (path path)) + (if (null? path) + tree + (let ( + (cur (car path)) + (nodeset (cons tree '()))) + (loop + (if (pair? cur) + (append cur nodeset) + (if (memq cur ignore) tree (cons cur nodeset))) + (cdr path)))))) + +; A fragment of the text node handler +(define-macro (%colorer:on-text) + (quote (let loop ((cur cur)) + (let* ( + (len-buf (string-length buf)) + (len-cur (string-length cur)) + (len-min (min len-buf len-cur))) + (if (> len-cur 0) ; the text node in the h-tree isn't finished yet? + (let ( + (result ; result is either a tree, eiter #f + (if (zero? len-buf) ; the text node in the main tree finished? + #f + (colorer:path-to-tree + (substring buf 0 len-min) + path + ignore)))) + ; Update buffer, switch to the main tree traversing, + ; continue h-tree traversing on switching back + (set! buf (substring buf len-min len-buf)) + (call-with-current-continuation (lambda (here) + (set! walker here) + (yield result))) + (loop (substring cur len-min len-cur)))))))) + +; A fragment of the node and attribute handler +(define-macro (%colorer:on-pair) + (quote (let ((elem (car cur))) + (if (eq? elem '@) + ; attribute: attach to the path + (set-car! path (list (car path) cur)) + ; element: update path, continue traversing + (let ((path (cons (car cur) path))) + (for-each + (lambda (kid) (loop kid path)) + (cdr cur))))))) + +; generator of highlighted chunks. +; Creation: +; (define highlighter (colorer:join-markup-stepper highlight-tree ignore)) +; Usage step: +; (highlighter more-buf) +; where more-buf either text, either #f. Each step returns either a +; subtree, either #f if buffer is over. +(define (colorer:join-markup-stepper highlight-tree ignore) + (letrec ( + (buf #f) + (yield #f) + ; The main loop + (walker-loop (lambda (cur path) + (let loop ((cur cur) (path path)) + (if (pair? cur) + (%colorer:on-pair) + (%colorer:on-text))) + ; The highlighting tree is over. Stop looping. + ; If the main tree isn't over (impossible), + ; just return the data from main tree. + (set! walker (lambda (dummy) + (if (and buf (> (string-length buf) 0)) + (let ((old-buf buf)) + (set! buf #f) + (yield old-buf)) + (yield #f)))) + (walker 'dummy))) + ; Set buffer, continue looping + (walker-entry + (lambda (new-buf) + (if new-buf + (set! buf new-buf)) + (call-with-current-continuation (lambda (here) + (set! yield here) + (walker 'resume))))) + ; Use once, than re-set + (walker + (lambda (dummy) + (set! walker walker-loop) + (walker-loop highlight-tree '())))) + ; create generator + walker-entry)) + +; add the colorer namespace to the tree +(define (colorer:wrap-by-ns tree) + `(syn:syntax (@ (@ + (*NAMESPACES* (syn "http://ns.laxan.com/text-vimcolor/1")))) + ,tree)) + +; join main markup with highlighting markup +(define colorer:id (lambda x x)) +(define (colorer:join-markup main-tree highlight-tree ignore) + (let ((stepper (colorer:join-markup-stepper highlight-tree ignore))) + (colorer:wrap-by-ns + ; Walk over the main tree + (pre-post-order main-tree `( + ; Comments, entities etc are not possible, so only few special cases + (*PI* *preorder* . ,colorer:id) + (@ *preorder* . ,colorer:id) + (*default* . ,colorer:id) + ; Text node: split on highlighted subtrees + (*text* . ,(lambda (trigger str) + (let loop ( + (chunks '()) + (tree (stepper str))) + (if tree + ; Loop while trees are being generated + (loop (cons tree chunks) (stepper #f)) + ; The node is processed. If there is only one chunk, return + ; it, otherwise wrap the nodeset of chunks by a dummy + ; element. Handle also impossible case of absense of chunks. + (cond + ((null? chunks) "") + ((null? (cdr chunks)) (car chunks)) + (else (cons 'syn:syntax (reverse chunks))))))))))))) diff --git a/manual/docbook/programlisting/colorer.xsl b/manual/docbook/programlisting/colorer.xsl new file mode 100644 index 000000000..dc6e800f8 --- /dev/null +++ b/manual/docbook/programlisting/colorer.xsl @@ -0,0 +1,54 @@ + + + +/home/clients/jhassine/doctrine/trunk/manual/docbook/programlisting/vimcolor/vimcolor-wrapper +--format xml +--filetype +--output + + +(load-from-path "sxml-utils.scm") +(load-from-path "colorer.scm") +(load-from-path "run-colorer.scm") + + + + + + + + + + + + + + + + + (let* ( + (highlighted-tree (run-colorer (x:eval "string(.)") (x:eval "string($type)"))) + (current (x:current)) + (united-tree + (if (not highlighted-tree) + #f + (colorer:join-markup current highlighted-tree '())))) + (x:apply-templates + 'with-param 'colorized #t + (if united-tree + united-tree + (colorer:wrap-by-ns current)))) + + + + + + + + + diff --git a/manual/docbook/programlisting/colors/CVS/Entries b/manual/docbook/programlisting/colors/CVS/Entries new file mode 100644 index 000000000..ad1b5e69d --- /dev/null +++ b/manual/docbook/programlisting/colors/CVS/Entries @@ -0,0 +1,3 @@ +/Makefile/1.1/Mon May 22 04:24:28 2006// +/vim2xslt.pl/1.1/Mon May 22 04:24:28 2006// +D diff --git a/manual/docbook/programlisting/colors/CVS/Repository b/manual/docbook/programlisting/colors/CVS/Repository new file mode 100644 index 000000000..ad9972d74 --- /dev/null +++ b/manual/docbook/programlisting/colors/CVS/Repository @@ -0,0 +1 @@ +xsieve/experiments/programlisting/colors diff --git a/manual/docbook/programlisting/colors/CVS/Root b/manual/docbook/programlisting/colors/CVS/Root new file mode 100644 index 000000000..d6bc77dba --- /dev/null +++ b/manual/docbook/programlisting/colors/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@xsieve.cvs.sourceforge.net:/cvsroot/xsieve diff --git a/manual/docbook/programlisting/colors/Makefile b/manual/docbook/programlisting/colors/Makefile new file mode 100644 index 000000000..ef77aed8f --- /dev/null +++ b/manual/docbook/programlisting/colors/Makefile @@ -0,0 +1,9 @@ +vim_colors_dir = /usr/share/vim/colors + +vim_files := $(wildcard $(vim_colors_dir)/*.vim) +xslt_files = $(patsubst %.vim,%.xsl,$(notdir $(vim_files))) + +all: $(xslt_files) + +%.xsl: $(vim_colors_dir)/%.vim + perl vim2xslt.pl $< >$@ diff --git a/manual/docbook/programlisting/colors/vim2xslt.pl b/manual/docbook/programlisting/colors/vim2xslt.pl new file mode 100644 index 000000000..41cdf9090 --- /dev/null +++ b/manual/docbook/programlisting/colors/vim2xslt.pl @@ -0,0 +1,16 @@ +#!/usr/bin/perl +# Take vim colors file and convert it to an XSLT stylesheet + +# The only types vim-textcolor produces +my %syntax_data = ( + 'Comment' => undef, + 'Constant' => undef, + 'Identifier' => undef, + 'Statement' => undef, + 'PreProc' => undef, + 'Type' => undef, + 'Special' => undef, + 'Underlined' => undef, + 'Error' => undef, + 'Todo' => undef +); diff --git a/manual/docbook/programlisting/id.xsl b/manual/docbook/programlisting/id.xsl new file mode 100644 index 000000000..a08887d36 --- /dev/null +++ b/manual/docbook/programlisting/id.xsl @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/manual/docbook/programlisting/index.html b/manual/docbook/programlisting/index.html new file mode 100644 index 000000000..af1d6ad0a --- /dev/null +++ b/manual/docbook/programlisting/index.html @@ -0,0 +1,58 @@ + + +Syntax highlighting for DocBook program listings + + +

Syntax highlighting for DocBook program listings

+

Highlight content of ProgramListing using XSieve and Vim. Example:

+ + + + + + + + + +
DocBook +
<programlisting role="xml">
+&lt;para>Hello, <emphasis>&amp;who;</emphasis
+ >!&lt;/para> <co id="who-entity"/>
+</programlisting>
+
HTML result    +
+<para>Hello, &who;!</para> (1)
+
+ +Download xsieve-programlisting.zip. + +

Usage

+ +

Set the correct path to the script vimcolor-wrapper in colorer.xsl.

+ +

Create an XSLT wrapper which imports DocBook and colorer stylesheets:

+ +
+<xsl:stylesheet ...>
+...
+<xsl:import href="..../docbook.xsl">       (1)
+<xsl:import href="..../colorer.xsl">       (2)
+<xsl:import href="..../colorer-html.xsl">  (3)
+
+... Your DocBook customization layer ...
+
+</xsl:stylesheet>
+
+ +

(1) The path to the DocBook XSLT stylesheet. For example, /usr/share/xml/docbook/xsl-stylesheets/html/docbook.xsl
+(2) The path to the colorer XSLT stylesheet.
+(3) Or colorer-fo.xsl for FO output.

+ +

Test data

+ +

The package contains test files test.xml, testdoc.xsl and testdoc.html. Use them as the starting point:

+ +
xsieve -o testdoc.html --param callout.graphics 0 testdoc.xsl test.xml
+ + + diff --git a/manual/docbook/programlisting/mkdist.sh b/manual/docbook/programlisting/mkdist.sh new file mode 100644 index 000000000..5851cf3f8 --- /dev/null +++ b/manual/docbook/programlisting/mkdist.sh @@ -0,0 +1,6 @@ +rm -rf xsieve-programlisting.zip xsieve-programlisting +mkdir xsieve-programlisting +rsync -av --exclude CVS vimcolor xsieve-programlisting/ +cp colorer.xsl colorer-html.xsl testdoc.xsl index.html sxml-utils.scm colorer.scm run-colorer.scm test.xml testdoc.html xsieve-programlisting/ +zip xsieve-programlisting.zip -r xsieve-programlisting + diff --git a/manual/docbook/programlisting/run-colorer.scm b/manual/docbook/programlisting/run-colorer.scm new file mode 100644 index 000000000..b4b40afde --- /dev/null +++ b/manual/docbook/programlisting/run-colorer.scm @@ -0,0 +1,51 @@ +; Run colorer and return the result as SXML +; $Id: run-colorer.scm,v 1.6 2006/04/29 05:47:24 olpa Exp $ + +(define colorer-bin #f) +(define colorer-params #f) +(define colorer-param-type #f) +(define colorer-param-outfile #f) + +; Initialize colorer variables (only once) +(define (init-colorer-variables) + (if (not colorer-bin) (begin + (set! colorer-bin (x:eval "string($colorer.bin)")) + (set! colorer-params (x:eval "string($colorer.params)")) + (set! colorer-param-type (x:eval "string($colorer.param.type)")) + (set! colorer-param-outfile (x:eval "string($colorer.param.outfile)"))))) + +(define-macro (no-errors . body) + `(catch #t (lambda () ,@body) (lambda (dummy . args) #f))) + +(define (run-colorer program-text program-type) + ; Some sanity check + (init-colorer-variables) + (if (not (and program-text (> (string-length program-text) 0))) + #f + (let* ( + ; Construct command line to run the colorer + (infile (tmpnam)) ; for the program text + (outfile (tmpnam)) ; for the colored tokens + (cmdline (string-append + colorer-bin " " colorer-params " " + (if (and program-type (> (string-length program-type) 0)) + (string-append colorer-param-type program-type " ") + "") + colorer-param-outfile outfile " " infile))) + ;(display "Command line: ")(display cmdline)(newline) + ; Ignore errors + (no-errors + ; Write the program text to the file and execute the colorer + (with-output-to-file infile + (lambda () (display program-text))) + ;(system (string-append "cp " infile " lastin")) ; DEBUG + (system cmdline) + ;(system (string-append "cp " outfile " last")) ; DEBUG + ; Load the XML result, cleanup and return the result + (let* ( + (eval-str (string-append "document('file://" outfile "')")) + (tree (x:eval eval-str))) + (no-errors (delete-file outfile)) + (no-errors (delete-file infile)) + ; drop "*TOP*" and drop namespace declaration from "syn:syntax" + (cons 'syn:syntax (cdr (cdadar tree)))))))) diff --git a/manual/docbook/programlisting/run.sh b/manual/docbook/programlisting/run.sh new file mode 100644 index 000000000..277dd5f20 --- /dev/null +++ b/manual/docbook/programlisting/run.sh @@ -0,0 +1 @@ +~/p/xsieve/opt/bin/xsieve --xinclude -o testdoc.html testdoc.xsl testdoc.xml diff --git a/manual/docbook/programlisting/run2.sh b/manual/docbook/programlisting/run2.sh new file mode 100644 index 000000000..7150b7268 --- /dev/null +++ b/manual/docbook/programlisting/run2.sh @@ -0,0 +1,2 @@ +#~/p/xsieve/opt/bin/xsieve --xinclude -o testdoc.html testdoc.xsl /home/olpa/p/xsieve/doc/book/xsieve.xml +~/p/xsieve/opt/bin/xsieve -o testdoc.html testdoc.xsl xsieve.xml diff --git a/manual/docbook/programlisting/run3.sh b/manual/docbook/programlisting/run3.sh new file mode 100644 index 000000000..72bc7787f --- /dev/null +++ b/manual/docbook/programlisting/run3.sh @@ -0,0 +1 @@ +~/p/xsieve/opt/bin/xsieve --xinclude -o testdoc.html colorer-one.xsl /home/olpa/p/xsieve/example/hello/doc/listing2.xml diff --git a/manual/docbook/programlisting/run4.sh b/manual/docbook/programlisting/run4.sh new file mode 100644 index 000000000..5c8215d0f --- /dev/null +++ b/manual/docbook/programlisting/run4.sh @@ -0,0 +1,2 @@ +#~/p/xsieve/opt/bin/xsieve --xinclude -o testdoc.html testdoc.xsl /home/olpa/p/xsieve/doc/book/xsieve.xml +~/p/xsieve/opt/bin/xsieve -o testdoc.html --param callout.graphics 0 testdoc.xsl /home/olpa/p/xsieve/doc/project/xtech2006/programlisting/test.xml diff --git a/manual/docbook/programlisting/sxml-utils.scm b/manual/docbook/programlisting/sxml-utils.scm new file mode 100644 index 000000000..1b42fd514 --- /dev/null +++ b/manual/docbook/programlisting/sxml-utils.scm @@ -0,0 +1,35 @@ +; $Id: sxml-utils.scm,v 1.1 2006/03/02 04:32:58 olpa Exp $ +; A copy-paste of a part of the SXML library + +; from CVS: SSAX/lib/SXML-tree-trans.scm +(define (pre-post-order tree bindings) + (let* ((default-binding (assq '*default* bindings)) + (text-binding (or (assq '*text* bindings) default-binding)) + (text-handler ; Cache default and text bindings + (and text-binding + (if (procedure? (cdr text-binding)) + (cdr text-binding) (cddr text-binding))))) + (let loop ((tree tree)) + (cond + ((null? tree) '()) + ((not (pair? tree)) + (let ((trigger '*text*)) + (if text-handler (text-handler trigger tree) + (error "Unknown binding for " trigger " and no default")))) + ((not (symbol? (car tree))) (map loop tree)) ; tree is a nodelist + (else ; tree is an SXML node + (let* ((trigger (car tree)) + (binding (or (assq trigger bindings) default-binding))) + (cond + ((not binding) + (error "Unknown binding for " trigger " and no default")) + ((not (pair? (cdr binding))) ; must be a procedure: handler + (apply (cdr binding) trigger (map loop (cdr tree)))) + ((eq? '*preorder* (cadr binding)) + (apply (cddr binding) tree)) + ((eq? '*macro* (cadr binding)) + (loop (apply (cddr binding) tree))) + (else ; (cadr binding) is a local binding + (apply (cddr binding) trigger + (pre-post-order (cdr tree) (append (cadr binding) bindings))) + )))))))) diff --git a/manual/docbook/programlisting/test-one.scm b/manual/docbook/programlisting/test-one.scm new file mode 100644 index 000000000..d63d38e06 --- /dev/null +++ b/manual/docbook/programlisting/test-one.scm @@ -0,0 +1,13 @@ +(load "sxml-utils.scm") +(load "colorer.scm") + +(define main-tree '(programlisting (*PI* a "b") (@ (format "linespecific")) "
+ Hello + Hello World! +
")) + +(define h-tree "") + +(define result (colorer:join-markup main-tree h-tree '(h))) + +(write result) diff --git a/manual/docbook/programlisting/test.scm b/manual/docbook/programlisting/test.scm new file mode 100644 index 000000000..aa7db8615 --- /dev/null +++ b/manual/docbook/programlisting/test.scm @@ -0,0 +1,16 @@ +; $Id: test.scm,v 1.2 2006/03/02 06:01:06 olpa Exp $ + +(define (test-case main-tree h-tree expected-result) + (display "------- Running a test case...") + (let ((result (caddr (colorer:join-markup main-tree h-tree '(h))))) + (if (equal? result expected-result) + (begin + (display "Ok")(newline)) + (begin + (display "Error")(newline) + (display "Expected: ")(write expected-result)(newline) + (display "Result: ")(write result)(newline))))) + +(load "sxml-utils.scm") +(load "colorer.scm") +(load "testdata.scm") diff --git a/manual/docbook/programlisting/test.xml b/manual/docbook/programlisting/test.xml new file mode 100644 index 000000000..e4e101224 --- /dev/null +++ b/manual/docbook/programlisting/test.xml @@ -0,0 +1,9 @@ + +
+ Testing Syntax Highlighting + Testing syntax highlighting + +<para>Hello, &who;!</para> + +
diff --git a/manual/docbook/programlisting/testdata.scm b/manual/docbook/programlisting/testdata.scm new file mode 100644 index 000000000..966d2efe1 --- /dev/null +++ b/manual/docbook/programlisting/testdata.scm @@ -0,0 +1,65 @@ +; test cases for joining parallel markup +; $Id: testdata.scm,v 1.2 2006/03/02 05:58:55 olpa Exp $ +; (test-case in-xml also-xml joined-xml) + +; the simplest test, no highlighting at all +(test-case + '(i "012") + '(h "012") + '(i "012")) + +; the simplest test, some highlighting +(test-case + '(i "012") + '(h "0" (a "1") "2") + '(i (colorer:dummy "0" (a "1") "2"))) + +; the size of text is different +(test-case + '(i "0123456789") + '(h (a "01") "234" (b "56") "7") + '(i (colorer:dummy (a "01") "234" (b "56") "7" "89"))) + +(test-case + '(i "01234567") + '(h "0" (a "12") "345" (b "5789")) + '(i (colorer:dummy "0" (a "12") "345" (b "67")))) + +; the text of the main tree is not corrupted +(test-case + '(i "012345") + '(h "ab" (c "cd") "ef") + '(i (colorer:dummy "01" (c "23") "45"))) + +; attributes are saved +(test-case + '(i "012345") + '(h "01" + (a (@ (a1 "a1") (a2 "a2")) + (b (@ (b1 "b1") (b2 "b2")) + "23")) + "45") + '(i (colorer:dummy "01" + (a (@ (a1 "a1") (a2 "a2")) + (b (@ (b1 "b1") (b2 "b2")) + "23")) + "45"))) + +; ordering and nesting of empty tags +(test-case + '(i "012" (x (y)) (z) "34") + '(h "01" (a "23") "4") + '(i (colorer:dummy "01" (a "2")) (x (y)) (z) (colorer:dummy (a "3") "4"))) + +; intersecting at left +(test-case + '(i "01" (a "2345" (b "67"))) + '(h "012" (x (y "3456")) "7") + '(i "01" (a (colorer:dummy "2" (x (y "345"))) (b (colorer:dummy (x (y "6")) "7"))))) + +; intersecting at right +(test-case + '(i "01" (a "23" (b "45") "6") "78") + '(h "01234" (x (y "56")) "78") + '(i "01" (a "23" (b (colorer:dummy "4" (x (y "5")))) (x (y "6"))) "78")) + diff --git a/manual/docbook/programlisting/testdoc.html b/manual/docbook/programlisting/testdoc.html new file mode 100644 index 000000000..6e0b7839c --- /dev/null +++ b/manual/docbook/programlisting/testdoc.html @@ -0,0 +1,5 @@ + + +Article with code

Article with code


A sample code:

Example 1. 

Testing Syntax Highlighting


Testing syntax highlighting

+<para>Hello, &who;!</para> 1
+

diff --git a/manual/docbook/programlisting/testdoc.xml b/manual/docbook/programlisting/testdoc.xml new file mode 100644 index 000000000..e042e5e64 --- /dev/null +++ b/manual/docbook/programlisting/testdoc.xml @@ -0,0 +1,7 @@ +
+ Article with code + A sample code: + + + +
diff --git a/manual/docbook/programlisting/testdoc.xsl b/manual/docbook/programlisting/testdoc.xsl new file mode 100644 index 000000000..9e04280f9 --- /dev/null +++ b/manual/docbook/programlisting/testdoc.xsl @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/manual/docbook/programlisting/vimcolor/CVS/Entries b/manual/docbook/programlisting/vimcolor/CVS/Entries new file mode 100644 index 000000000..f27b0ee20 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/CVS/Entries @@ -0,0 +1,6 @@ +/README/1.1/Fri Apr 28 07:09:09 2006// +/README-Path-Class/1.1/Fri Apr 28 07:09:09 2006// +/README-Text-VimColor/1.1/Fri Apr 28 07:09:09 2006// +/text-vimcolor/1.1/Fri Apr 28 07:09:09 2006// +/vimcolor-wrapper/1.1/Fri Apr 28 07:09:09 2006// +D/lib//// diff --git a/manual/docbook/programlisting/vimcolor/CVS/Repository b/manual/docbook/programlisting/vimcolor/CVS/Repository new file mode 100644 index 000000000..53ac94a43 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/CVS/Repository @@ -0,0 +1 @@ +xsieve/experiments/programlisting/vimcolor diff --git a/manual/docbook/programlisting/vimcolor/CVS/Root b/manual/docbook/programlisting/vimcolor/CVS/Root new file mode 100644 index 000000000..d6bc77dba --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@xsieve.cvs.sourceforge.net:/cvsroot/xsieve diff --git a/manual/docbook/programlisting/vimcolor/README b/manual/docbook/programlisting/vimcolor/README new file mode 100644 index 000000000..de5a14f1b --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/README @@ -0,0 +1,8 @@ +Command-line syntax highlighting based on vim + +It uses Perl modules Text::VimColor and Path::Class. +Perl libraries of these modules are copied to "lib". + +The program "vimcolor-wrapper" sets the path to the +local copy of the libraries and runs the original +script "text-vimcolor". diff --git a/manual/docbook/programlisting/vimcolor/README-Path-Class b/manual/docbook/programlisting/vimcolor/README-Path-Class new file mode 100644 index 000000000..194b5f396 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/README-Path-Class @@ -0,0 +1,127 @@ +NAME + Path::Class - Cross-platform path specification manipulation + +SYNOPSIS + use Path::Class; + + my $dir = dir('foo', 'bar'); # Path::Class::Dir object + my $file = file('bob', 'file.txt'); # Path::Class::File object + + # Stringifies to 'foo/bar' on Unix, 'foo\bar' on Windows, etc. + print "dir: $dir\n"; + + # Stringifies to 'bob/file.txt' on Unix, 'bob\file.txt' on Windows + print "file: $file\n"; + + my $subdir = $dir->subdir('baz'); # foo/bar/baz + my $parent = $subdir->parent; # foo/bar + my $parent2 = $parent->parent; # foo + + my $dir2 = $file->dir; # bob + + # Work with foreign paths + use Path::Class qw(foreign_file foreign_dir); + my $file = foreign_file('Mac', ':foo:file.txt'); + print $file->dir; # :foo: + print $file->as_foreign('Win32'); # foo\file.txt + + # Interact with the underlying filesystem: + + # $dir_handle is an IO::Dir object + my $dir_handle = $dir->open or die "Can't read $dir: $!"; + + # $file_handle is an IO::File object + my $file_handle = $file->open($mode) or die "Can't read $file: $!"; + +DESCRIPTION + `Path::Class' is a module for manipulation of file and directory + specifications (strings describing their locations, like + `'/home/ken/foo.txt'' or `'C:\Windows\Foo.txt'') in a cross-platform + manner. It supports pretty much every platform Perl runs on, including + Unix, Windows, Mac, VMS, Epoc, Cygwin, OS/2, and NetWare. + + The well-known module `File::Spec' also provides this service, but it's + sort of awkward to use well, so people sometimes avoid it, or use it in + a way that won't actually work properly on platforms significantly + different than the ones they've tested their code on. + + In fact, `Path::Class' uses `File::Spec' internally, wrapping all the + unsightly details so you can concentrate on your application code. + Whereas `File::Spec' provides functions for some common path + manipulations, `Path::Class' provides an object-oriented model of the + world of path specifications and their underlying semantics. + `File::Spec' doesn't create any objects, and its classes represent the + different ways in which paths must be manipulated on various platforms + (not a very intuitive concept). `Path::Class' creates objects + representing files and directories, and provides methods that relate + them to each other. For instance, the following `File::Spec' code: + + my $absolute = File::Spec->file_name_is_absolute( + File::Spec->catfile( @dirs, $file ) + ); + + can be written using `Path::Class' as + + my $absolute = Path::Class::File->new( @dirs, $file )->is_absolute; + + or even as + + my $absolute = file( @dirs, $file )->is_absolute; + + Similar readability improvements should happen all over the place when + using `Path::Class'. + + Using `Path::Class' can help solve real problems in your code too - for + instance, how many people actually take the "volume" (like `C:' on + Windows) into account when writing `File::Spec'-using code? I thought + not. But if you use `Path::Class', your file and directory objects will + know what volumes they refer to and do the right thing. + + The guts of the `Path::Class' code live in the `Path::Class::File' and + `Path::Class::Dir' modules, so please see those modules' documentation + for more details about how to use them. + + EXPORT + + The following functions are exported by default. + + file + A synonym for `Path::Class::File->new'. + + dir A synonym for `Path::Class::Dir->new'. + + If you would like to prevent their export, you may explicitly pass an + empty list to perl's `use', i.e. `use Path::Class ()'. + + The following are exported only on demand. + + foreign_file + A synonym for `Path::Class::File->new_foreign'. + + foreign_dir + A synonym for `Path::Class::Dir->new_foreign'. + +Notes on Cross-Platform Compatibility + Although it is much easier to write cross-platform-friendly code with + this module than with `File::Spec', there are still some issues to be + aware of. + + * Some platforms, notably VMS and some older versions of DOS (I + think), all filenames must have an extension. Thus if you create a + file called foo/bar and then ask for a list of files in the + directory foo, you may find a file called bar. instead of the bar + you were expecting. Thus it might be a good idea to use an extension + in the first place. + +AUTHOR + Ken Williams, KWILLIAMS@cpan.org + +COPYRIGHT + Copyright (c) Ken Williams. All rights reserved. + + This library is free software; you can redistribute it and/or modify it + under the same terms as Perl itself. + +SEE ALSO + Path::Class::Dir, Path::Class::File, File::Spec + diff --git a/manual/docbook/programlisting/vimcolor/README-Text-VimColor b/manual/docbook/programlisting/vimcolor/README-Text-VimColor new file mode 100644 index 000000000..38b37e8d0 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/README-Text-VimColor @@ -0,0 +1,25 @@ +Text::VimColor +-------------- + +This module tries to markup text files according to their syntax. It can +be used to produce web pages with pretty-printed colourful source code +samples. It can produce output in the following formats: + +The module comes with a command line program, text-vimcolor, which makes +it easy to do 'ad-hoc' syntax coloring jobs. + + + Geoff Richards + + +Release procedure +----------------- + + * Update the version number in lib/Text/VimColor.pm and META.yml + * Update the changelog with a new section for a matching version number + and the correct date and time + * Copy the ChangeLog into place (from 'debian' directory in my CVS) + * Realclean, make and test + * Make the dist, take it to another machine and build and test there + * Commit everything, and set tag like 'Release_0_07-1' + * Upload to CPAN diff --git a/manual/docbook/programlisting/vimcolor/lib/CVS/Entries b/manual/docbook/programlisting/vimcolor/lib/CVS/Entries new file mode 100644 index 000000000..cc0a28410 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/CVS/Entries @@ -0,0 +1,2 @@ +D/Path//// +D/Text//// diff --git a/manual/docbook/programlisting/vimcolor/lib/CVS/Repository b/manual/docbook/programlisting/vimcolor/lib/CVS/Repository new file mode 100644 index 000000000..43c4fcd3b --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/CVS/Repository @@ -0,0 +1 @@ +xsieve/experiments/programlisting/vimcolor/lib diff --git a/manual/docbook/programlisting/vimcolor/lib/CVS/Root b/manual/docbook/programlisting/vimcolor/lib/CVS/Root new file mode 100644 index 000000000..d6bc77dba --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@xsieve.cvs.sourceforge.net:/cvsroot/xsieve diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Entries b/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Entries new file mode 100644 index 000000000..7166213fb --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Entries @@ -0,0 +1,2 @@ +/Class.pm/1.1/Fri Apr 28 07:17:33 2006// +D/Class//// diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Repository b/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Repository new file mode 100644 index 000000000..43ef3dddb --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Repository @@ -0,0 +1 @@ +xsieve/experiments/programlisting/vimcolor/lib/Path diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Root b/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Root new file mode 100644 index 000000000..d6bc77dba --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@xsieve.cvs.sourceforge.net:/cvsroot/xsieve diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/Class.pm b/manual/docbook/programlisting/vimcolor/lib/Path/Class.pm new file mode 100644 index 000000000..3829279df --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/Class.pm @@ -0,0 +1,177 @@ +package Path::Class; + +$VERSION = '0.15'; +@ISA = qw(Exporter); +@EXPORT = qw(file dir); +@EXPORT_OK = qw(file dir foreign_file foreign_dir); + +use strict; +use Exporter; +use Path::Class::File; +use Path::Class::Dir; + +sub file { Path::Class::File->new(@_) } +sub dir { Path::Class::Dir ->new(@_) } +sub foreign_file { Path::Class::File->new_foreign(@_) } +sub foreign_dir { Path::Class::Dir ->new_foreign(@_) } + + +1; +__END__ + +=head1 NAME + +Path::Class - Cross-platform path specification manipulation + +=head1 SYNOPSIS + + use Path::Class; + + my $dir = dir('foo', 'bar'); # Path::Class::Dir object + my $file = file('bob', 'file.txt'); # Path::Class::File object + + # Stringifies to 'foo/bar' on Unix, 'foo\bar' on Windows, etc. + print "dir: $dir\n"; + + # Stringifies to 'bob/file.txt' on Unix, 'bob\file.txt' on Windows + print "file: $file\n"; + + my $subdir = $dir->subdir('baz'); # foo/bar/baz + my $parent = $subdir->parent; # foo/bar + my $parent2 = $parent->parent; # foo + + my $dir2 = $file->dir; # bob + + # Work with foreign paths + use Path::Class qw(foreign_file foreign_dir); + my $file = foreign_file('Mac', ':foo:file.txt'); + print $file->dir; # :foo: + print $file->as_foreign('Win32'); # foo\file.txt + + # Interact with the underlying filesystem: + + # $dir_handle is an IO::Dir object + my $dir_handle = $dir->open or die "Can't read $dir: $!"; + + # $file_handle is an IO::File object + my $file_handle = $file->open($mode) or die "Can't read $file: $!"; + +=head1 DESCRIPTION + +C is a module for manipulation of file and directory +specifications (strings describing their locations, like +C<'/home/ken/foo.txt'> or C<'C:\Windows\Foo.txt'>) in a cross-platform +manner. It supports pretty much every platform Perl runs on, +including Unix, Windows, Mac, VMS, Epoc, Cygwin, OS/2, and NetWare. + +The well-known module C also provides this service, but +it's sort of awkward to use well, so people sometimes avoid it, or use +it in a way that won't actually work properly on platforms +significantly different than the ones they've tested their code on. + +In fact, C uses C internally, wrapping all +the unsightly details so you can concentrate on your application code. +Whereas C provides functions for some common path +manipulations, C provides an object-oriented model of the +world of path specifications and their underlying semantics. +C doesn't create any objects, and its classes represent +the different ways in which paths must be manipulated on various +platforms (not a very intuitive concept). C creates +objects representing files and directories, and provides methods that +relate them to each other. For instance, the following C +code: + + my $absolute = File::Spec->file_name_is_absolute( + File::Spec->catfile( @dirs, $file ) + ); + +can be written using C as + + my $absolute = Path::Class::File->new( @dirs, $file )->is_absolute; + +or even as + + my $absolute = file( @dirs, $file )->is_absolute; + +Similar readability improvements should happen all over the place when +using C. + +Using C can help solve real problems in your code too - +for instance, how many people actually take the "volume" (like C +on Windows) into account when writing C-using code? I +thought not. But if you use C, your file and directory objects +will know what volumes they refer to and do the right thing. + +The guts of the C code live in the C +and C modules, so please see those +modules' documentation for more details about how to use them. + +=head2 EXPORT + +The following functions are exported by default. + +=over 4 + +=item file + +A synonym for C<< Path::Class::File->new >>. + +=item dir + +A synonym for C<< Path::Class::Dir->new >>. + +=back + +If you would like to prevent their export, you may explicitly pass an +empty list to perl's C, i.e. C. + +The following are exported only on demand. + +=over 4 + +=item foreign_file + +A synonym for C<< Path::Class::File->new_foreign >>. + +=item foreign_dir + +A synonym for C<< Path::Class::Dir->new_foreign >>. + +=back + +=head1 Notes on Cross-Platform Compatibility + +Although it is much easier to write cross-platform-friendly code with +this module than with C, there are still some issues to be +aware of. + +=over 4 + +=item * + +Some platforms, notably VMS and some older versions of DOS (I think), +all filenames must have an extension. Thus if you create a file +called F and then ask for a list of files in the directory +F, you may find a file called F instead of the F you +were expecting. Thus it might be a good idea to use an extension in +the first place. + +=back + +=head1 AUTHOR + +Ken Williams, KWILLIAMS@cpan.org + +=head1 COPYRIGHT + +Copyright (c) Ken Williams. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + + +=head1 SEE ALSO + +Path::Class::Dir, Path::Class::File, File::Spec + +=cut diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Entries b/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Entries new file mode 100644 index 000000000..7414cc4c0 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Entries @@ -0,0 +1,4 @@ +/Dir.pm/1.1/Fri Apr 28 07:17:33 2006// +/Entity.pm/1.1/Fri Apr 28 07:17:33 2006// +/File.pm/1.1/Fri Apr 28 07:17:33 2006// +D diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Repository b/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Repository new file mode 100644 index 000000000..806095ae4 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Repository @@ -0,0 +1 @@ +xsieve/experiments/programlisting/vimcolor/lib/Path/Class diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Root b/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Root new file mode 100644 index 000000000..d6bc77dba --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/Class/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@xsieve.cvs.sourceforge.net:/cvsroot/xsieve diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/Class/Dir.pm b/manual/docbook/programlisting/vimcolor/lib/Path/Class/Dir.pm new file mode 100644 index 000000000..2fe96a497 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/Class/Dir.pm @@ -0,0 +1,584 @@ +package Path::Class::Dir; + +use strict; +use Path::Class::File; +use Path::Class::Entity; +use Carp(); +use base qw(Path::Class::Entity); + +use IO::Dir (); +use File::Path (); + +sub new { + my $self = shift->SUPER::new(); + my $s = $self->_spec; + + my $first = (@_ == 0 ? $s->curdir : + $_[0] eq '' ? (shift, $s->rootdir) : + shift() + ); + + ($self->{volume}, my $dirs) = $s->splitpath( $s->canonpath($first) , 1); + $self->{dirs} = [$s->splitdir($s->catdir($dirs, @_))]; + + return $self; +} + +sub is_dir { 1 } + +sub as_foreign { + my ($self, $type) = @_; + + my $foreign = do { + local $self->{file_spec_class} = $self->_spec_class($type); + $self->SUPER::new; + }; + + # Clone internal structure + $foreign->{volume} = $self->{volume}; + my ($s, $fs) = ($self->_spec, $foreign->_spec); + $foreign->{dirs} = [ map {$_ eq $s->updir ? $fs->updir : $_} @{$self->{dirs}}]; + return $foreign; +} + +sub stringify { + my $self = shift; + my $s = $self->_spec; + return $s->catpath($self->{volume}, + $s->catdir(@{$self->{dirs}}), + ''); +} + +sub volume { shift()->{volume} } + +sub file { + local $Path::Class::Foreign = $_[0]->{file_spec_class} if $_[0]->{file_spec_class}; + return Path::Class::File->new(@_); +} + +sub dir_list { + my $self = shift; + my $d = $self->{dirs}; + return @$d unless @_; + + my $offset = shift; + if ($offset < 0) { $offset = $#$d + $offset + 1 } + + return wantarray ? @$d[$offset .. $#$d] : $d->[$offset] unless @_; + + my $length = shift; + if ($length < 0) { $length = $#$d + $length + 1 - $offset } + return @$d[$offset .. $length + $offset - 1]; +} + +sub subdir { + my $self = shift; + return $self->new($self, @_); +} + +sub parent { + my $self = shift; + my $dirs = $self->{dirs}; + my ($curdir, $updir) = ($self->_spec->curdir, $self->_spec->updir); + + if ($self->is_absolute) { + my $parent = $self->new($self); + pop @{$parent->{dirs}}; + return $parent; + + } elsif ($self eq $curdir) { + return $self->new($updir); + + } elsif (!grep {$_ ne $updir} @$dirs) { # All updirs + return $self->new($self, $updir); # Add one more + + } elsif (@$dirs == 1) { + return $self->new($curdir); + + } else { + my $parent = $self->new($self); + pop @{$parent->{dirs}}; + return $parent; + } +} + +sub relative { + # File::Spec->abs2rel before version 3.13 returned the empty string + # when the two paths were equal - work around it here. + my $self = shift; + my $rel = $self->_spec->abs2rel($self->stringify, @_); + return $self->new( length $rel ? $rel : $self->_spec->curdir ); +} + +sub open { IO::Dir->new(@_) } +sub mkpath { File::Path::mkpath(shift()->stringify, @_) } +sub rmtree { File::Path::rmtree(shift()->stringify, @_) } + +sub remove { + rmdir( shift() ); +} + +sub recurse { + my $self = shift; + my %opts = (preorder => 1, depthfirst => 0, @_); + + my $callback = $opts{callback} + or Carp::croak( "Must provide a 'callback' parameter to recurse()" ); + + my @queue = ($self); + + my $visit_entry; + my $visit_dir = + $opts{depthfirst} && $opts{preorder} + ? sub { + my $dir = shift; + $callback->($dir); + unshift @queue, $dir->children; + } + : $opts{preorder} + ? sub { + my $dir = shift; + $callback->($dir); + push @queue, $dir->children; + } + : sub { + my $dir = shift; + $visit_entry->($_) foreach $dir->children; + $callback->($dir); + }; + + $visit_entry = sub { + my $entry = shift; + if ($entry->is_dir) { $visit_dir->($entry) } # Will call $callback + else { $callback->($entry) } + }; + + while (@queue) { + $visit_entry->( shift @queue ); + } +} + +sub children { + my ($self, %opts) = @_; + + my $dh = $self->open or Carp::croak( "Can't open directory $self: $!" ); + + my @out; + while (my $entry = $dh->read) { + # XXX What's the right cross-platform way to do this? + next if (!$opts{all} && ($entry eq '.' || $entry eq '..')); + push @out, $self->file($entry); + $out[-1] = $self->subdir($entry) if -d $out[-1]; + } + return @out; +} + +sub next { + my $self = shift; + unless ($self->{dh}) { + $self->{dh} = $self->open or Carp::croak( "Can't open directory $self: $!" ); + } + + my $next = $self->{dh}->read; + unless (defined $next) { + delete $self->{dh}; + return undef; + } + + # Figure out whether it's a file or directory + my $file = $self->file($next); + $file = $self->subdir($next) if -d $file; + return $file; +} + +sub subsumes { + my ($self, $other) = @_; + die "No second entity given to subsumes()" unless $other; + + $other = ref($self)->new($other) unless UNIVERSAL::isa($other, __PACKAGE__); + $other = $other->dir unless $other->is_dir; + + if ($self->is_absolute) { + $other = $other->absolute; + } elsif ($other->is_absolute) { + $self = $self->absolute; + } + + $self = $self->cleanup; + $other = $other->cleanup; + + if ($self->volume) { + return 0 unless $other->volume eq $self->volume; + } + + # The root dir subsumes everything + return 1 if $self eq ref($self)->new(''); + + my $i = 0; + while ($i <= $#{ $self->{dirs} }) { + return 0 unless exists $other->{dirs}[$i]; + return 0 if $self->{dirs}[$i] ne $other->{dirs}[$i]; + $i++; + } + return 1; +} + +1; +__END__ + +=head1 NAME + +Path::Class::Dir - Objects representing directories + +=head1 SYNOPSIS + + use Path::Class qw(dir); # Export a short constructor + + my $dir = dir('foo', 'bar'); # Path::Class::Dir object + my $dir = Path::Class::Dir->new('foo', 'bar'); # Same thing + + # Stringifies to 'foo/bar' on Unix, 'foo\bar' on Windows, etc. + print "dir: $dir\n"; + + if ($dir->is_absolute) { ... } + + my $v = $dir->volume; # Could be 'C:' on Windows, empty string + # on Unix, 'Macintosh HD:' on Mac OS + + $dir->cleanup; # Perform logical cleanup of pathname + + my $file = $dir->file('file.txt'); # A file in this directory + my $subdir = $dir->subdir('george'); # A subdirectory + my $parent = $dir->parent; # The parent directory, 'foo' + + my $abs = $dir->absolute; # Transform to absolute path + my $rel = $abs->relative; # Transform to relative path + my $rel = $abs->relative('/foo'); # Relative to /foo + + print $dir->as_foreign('MacOS'); # :foo:bar: + print $dir->as_foreign('Win32'); # foo\bar + + # Iterate with IO::Dir methods: + my $handle = $dir->open; + while (my $file = $handle->read) { + $file = $dir->file($file); # Turn into Path::Class::File object + ... + } + + # Iterate with Path::Class methods: + while (my $file = $dir->next) { + # $file is a Path::Class::File or Path::Class::Dir object + ... + } + + +=head1 DESCRIPTION + +The C class contains functionality for manipulating +directory names in a cross-platform way. + +=head1 METHODS + +=over 4 + +=item $dir = Path::Class::Dir->new( , , ... ) + +=item $dir = dir( , , ... ) + +Creates a new C object and returns it. The +arguments specify names of directories which will be joined to create +a single directory object. A volume may also be specified as the +first argument, or as part of the first argument. You can use +platform-neutral syntax: + + my $dir = dir( 'foo', 'bar', 'baz' ); + +or platform-native syntax: + + my $dir = dir( 'foo/bar/baz' ); + +or a mixture of the two: + + my $dir = dir( 'foo/bar', 'baz' ); + +All three of the above examples create relative paths. To create an +absolute path, either use the platform native syntax for doing so: + + my $dir = dir( '/var/tmp' ); + +or use an empty string as the first argument: + + my $dir = dir( '', 'var', 'tmp' ); + +If the second form seems awkward, that's somewhat intentional - paths +like C or C<\Windows> aren't cross-platform concepts in the +first place (many non-Unix platforms don't have a notion of a "root +directory"), so they probably shouldn't appear in your code if you're +trying to be cross-platform. The first form is perfectly natural, +because paths like this may come from config files, user input, or +whatever. + +As a special case, since it doesn't otherwise mean anything useful and +it's convenient to define this way, C<< Path::Class::Dir->new() >> (or +C) refers to the current directory (C<< File::Spec->curdir >>). +To get the current directory as an absolute path, do C<< +dir()->absolute >>. + +=item $dir->stringify + +This method is called internally when a C object is +used in a string context, so the following are equivalent: + + $string = $dir->stringify; + $string = "$dir"; + +=item $dir->volume + +Returns the volume (e.g. C on Windows, C on Mac OS, +etc.) of the directory object, if any. Otherwise, returns the empty +string. + +=item $dir->is_dir + +Returns a boolean value indicating whether this object represents a +directory. Not surprisingly, C objects always +return false, and C objects always return true. + +=item $dir->is_absolute + +Returns true or false depending on whether the directory refers to an +absolute path specifier (like C or C<\Windows>). + +=item $dir->cleanup + +Performs a logical cleanup of the file path. For instance: + + my $dir = dir('/foo//baz/./foo')->cleanup; + # $dir now represents '/foo/baz/foo'; + +=item $file = $dir->file( , , ..., ) + +Returns a C object representing an entry in C<$dir> +or one of its subdirectories. Internally, this just calls C<< +Path::Class::File->new( @_ ) >>. + +=item $subdir = $dir->subdir( , , ... ) + +Returns a new C object representing a subdirectory +of C<$dir>. + +=item $parent = $dir->parent + +Returns the parent directory of C<$dir>. Note that this is the +I parent, not necessarily the physical parent. It really +means we just chop off entries from the end of the directory list +until we cain't chop no more. If the directory is relative, we start +using the relative forms of parent directories. + +The following code demonstrates the behavior on absolute and relative +directories: + + $dir = dir('/foo/bar'); + for (1..6) { + print "Absolute: $dir\n"; + $dir = $dir->parent; + } + + $dir = dir('foo/bar'); + for (1..6) { + print "Relative: $dir\n"; + $dir = $dir->parent; + } + + ########### Output on Unix ################ + Absolute: /foo/bar + Absolute: /foo + Absolute: / + Absolute: / + Absolute: / + Absolute: / + Relative: foo/bar + Relative: foo + Relative: . + Relative: .. + Relative: ../.. + Relative: ../../.. + +=item @list = $dir->children + +Returns a list of C and/or C +objects listed in this directory, or in scalar context the number of +such objects. Obviously, it is necessary for C<$dir> to +exist and be readable in order to find its children. + +Note that the children are returned as subdirectories of C<$dir>, +i.e. the children of F will be F and F, not +F and F. + +Ordinarily C will not include the I and I +entries C<.> and C<..> (or their equivalents on non-Unix systems), +because that's like I'm-my-own-grandpa business. If you do want all +directory entries including these special ones, pass a true value for +the C parameter: + + @c = $dir->children(); # Just the children + @c = $dir->children(all => 1); # All entries + +=item $abs = $dir->absolute + +Returns a C object representing C<$dir> as an +absolute path. An optional argument, given as either a string or a +C object, specifies the directory to use as the base +of relativity - otherwise the current working directory will be used. + +=item $rel = $dir->relative + +Returns a C object representing C<$dir> as a +relative path. An optional argument, given as either a string or a +C object, specifies the directory to use as the base +of relativity - otherwise the current working directory will be used. + +=item $boolean = $dir->subsumes($other) + +Returns true if this directory spec subsumes the other spec, and false +otherwise. Think of "subsumes" as "contains", but we only look at the +I, not whether C<$dir> actually contains C<$other> on the +filesystem. + +The C<$other> argument may be a C object, a +C object, or a string. In the latter case, we +assume it's a directory. + + # Examples: + dir('foo/bar' )->subsumes(dir('foo/bar/baz')) # True + dir('/foo/bar')->subsumes(dir('/foo/bar/baz')) # True + dir('foo/bar' )->subsumes(dir('bar/baz')) # False + dir('/foo/bar')->subsumes(dir('foo/bar')) # False + + +=item $foreign = $dir->as_foreign($type) + +Returns a C object representing C<$dir> as it would +be specified on a system of type C<$type>. Known types include +C, C, C, C, and C, i.e. anything for which +there is a subclass of C. + +Any generated objects (subdirectories, files, parents, etc.) will also +retain this type. + +=item $foreign = Path::Class::Dir->new_foreign($type, @args) + +Returns a C object representing C<$dir> as it would +be specified on a system of type C<$type>. Known types include +C, C, C, C, and C, i.e. anything for which +there is a subclass of C. + +The arguments in C<@args> are the same as they would be specified in +C. + +=item @list = $dir->dir_list([OFFSET, [LENGTH]]) + +Returns the list of strings internally representing this directory +structure. Each successive member of the list is understood to be an +entry in its predecessor's directory list. By contract, C<< +Path::Class->new( $dir->dir_list ) >> should be equivalent to C<$dir>. + +The semantics of this method are similar to Perl's C or +C functions; they return C elements starting at +C. If C is omitted, returns all the elements starting +at C up to the end of the list. If C is negative, +returns the elements from C onward except for C<-LENGTH> +elements at the end. If C is negative, it counts backward +C elements from the end of the list. If C and +C are both omitted, the entire list is returned. + +In a scalar context, C with no arguments returns the +number of entries in the directory list; C returns +the single element at that offset; C returns +the final element that would have been returned in a list context. + +=item $fh = $dir->open() + +Passes C<$dir> to C<< IO::Dir->open >> and returns the result as an +C object. If the opening fails, C is returned and +C<$!> is set. + +=item $dir->mkpath($verbose, $mode) + +Passes all arguments, including C<$dir>, to C<< File::Path::mkpath() +>> and returns the result (a list of all directories created). + +=item $dir->rmtree($verbose, $cautious) + +Passes all arguments, including C<$dir>, to C<< File::Path::rmtree() +>> and returns the result (the number of files successfully deleted). + +=item $dir->remove() + +Removes the directory, which must be empty. Returns a boolean value +indicating whether or not the directory was successfully removed. +This method is mainly provided for consistency with +C's C method. + +=item $dir_or_file = $dir->next() + +A convenient way to iterate through directory contents. The first +time C is called, it will C the directory and read the +first item from it, returning the result as a C or +C object (depending, of course, on its actual +type). Each subsequent call to C will simply iterate over the +directory's contents, until there are no more items in the directory, +and then the undefined value is returned. For example, to iterate +over all the regular files in a directory: + + while (my $file = $dir->next) { + next unless -f $file; + my $fh = $file->open('r') or die "Can't read $file: $!"; + ... + } + +If an error occurs when opening the directory (for instance, it +doesn't exist or isn't readable), C will throw an exception +with the value of C<$!>. + +=item $dir->recurse( callback => sub {...} ) + +Iterates through this directory and all of its children, and all of +its children's children, etc., calling the C subroutine for +each entry. This is a lot like what the C module does, +and of course C will work fine on C objects, +but the advantage of the C method is that it will also feed +your callback routine C objects rather than just pathname +strings. + +The C method requires a C parameter specifying +the subroutine to invoke for each entry. It will be passed the +C object as its first argument. + +C also accepts two boolean parameters, C and +C that control the order of recursion. The default is a +preorder, breadth-first search, i.e. C<< depthfirst => 0, preorder => 1 >>. +At the time of this writing, all combinations of these two parameters +are supported I C<< depthfirst => 0, preorder => 0 >>. + +=item $st = $file->stat() + +Invokes C<< File::stat::stat() >> on this directory and returns a +C object representing the result. + +=item $st = $file->lstat() + +Same as C, but if C<$file> is a symbolic link, C +stats the link instead of the directory the link points to. + +=back + +=head1 AUTHOR + +Ken Williams, ken@mathforum.org + +=head1 SEE ALSO + +Path::Class, Path::Class::File, File::Spec + +=cut diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/Class/Entity.pm b/manual/docbook/programlisting/vimcolor/lib/Path/Class/Entity.pm new file mode 100644 index 000000000..44cf4be76 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/Class/Entity.pm @@ -0,0 +1,67 @@ +package Path::Class::Entity; + +use strict; +use File::Spec; +use File::stat (); + +use overload + ( + q[""] => 'stringify', + fallback => 1, + ); + +sub new { + my $from = shift; + my ($class, $fs_class) = (ref($from) + ? (ref $from, $from->{file_spec_class}) + : ($from, $Path::Class::Foreign)); + return bless {file_spec_class => $fs_class}, $class; +} + +sub is_dir { 0 } + +sub _spec_class { + my ($class, $type) = @_; + + die "Invalid system type '$type'" unless ($type) = $type =~ /^(\w+)$/; # Untaint + my $spec = "File::Spec::$type"; + eval "require $spec; 1" or die $@; + return $spec; +} + +sub new_foreign { + my ($class, $type) = (shift, shift); + local $Path::Class::Foreign = $class->_spec_class($type); + return $class->new(@_); +} + +sub _spec { $_[0]->{file_spec_class} || 'File::Spec' } + +sub is_absolute { + # 5.6.0 has a bug with regexes and stringification that's ticked by + # file_name_is_absolute(). Help it along. + $_[0]->_spec->file_name_is_absolute($_[0]->stringify) +} + +sub cleanup { + my $self = shift; + my $cleaned = $self->new( $self->_spec->canonpath($self) ); + %$self = %$cleaned; + return $self; +} + +sub absolute { + my $self = shift; + return $self if $self->is_absolute; + return $self->new($self->_spec->rel2abs($self->stringify, @_)); +} + +sub relative { + my $self = shift; + return $self->new($self->_spec->abs2rel($self->stringify, @_)); +} + +sub stat { File::stat::stat("$_[0]") } +sub lstat { File::stat::lstat("$_[0]") } + +1; diff --git a/manual/docbook/programlisting/vimcolor/lib/Path/Class/File.pm b/manual/docbook/programlisting/vimcolor/lib/Path/Class/File.pm new file mode 100644 index 000000000..c4d837c0f --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Path/Class/File.pm @@ -0,0 +1,311 @@ +package Path::Class::File; + +use strict; +use Path::Class::Dir; +use Path::Class::Entity; +use base qw(Path::Class::Entity); + +use IO::File (); + +sub new { + my $self = shift->SUPER::new; + my $file = pop(); + my @dirs = @_; + + my ($volume, $dirs, $base) = $self->_spec->splitpath($file); + + if (length $dirs) { + push @dirs, $self->_spec->catpath($volume, $dirs, ''); + } + + $self->{dir} = @dirs ? Path::Class::Dir->new(@dirs) : undef; + $self->{file} = $base; + + return $self; +} + +sub as_foreign { + my ($self, $type) = @_; + local $Path::Class::Foreign = $self->_spec_class($type); + my $foreign = ref($self)->SUPER::new; + $foreign->{dir} = $self->{dir}->as_foreign($type) if defined $self->{dir}; + $foreign->{file} = $self->{file}; + return $foreign; +} + +sub stringify { + my $self = shift; + return $self->{file} unless defined $self->{dir}; + return $self->_spec->catfile($self->{dir}->stringify, $self->{file}); +} + +sub dir { + my $self = shift; + return $self->{dir} if defined $self->{dir}; + return Path::Class::Dir->new($self->_spec->curdir); +} +BEGIN { *parent = \&dir; } + +sub volume { + my $self = shift; + return '' unless defined $self->{dir}; + return $self->{dir}->volume; +} + +sub basename { shift->{file} } +sub open { IO::File->new(@_) } + +sub openr { $_[0]->open('r') or die "Can't read $_[0]: $!" } +sub openw { $_[0]->open('w') or die "Can't write $_[0]: $!" } + +sub touch { + my $self = shift; + if (-e $self) { + my $now = time(); + utime $now, $now, $self; + } else { + $self->openw; + } +} + +sub slurp { + my ($self, %args) = @_; + my $fh = $self->openr; + + if ($args{chomped} or $args{chomp}) { + chomp( my @data = <$fh> ); + return wantarray ? @data : join '', @data; + } + + local $/ unless wantarray; + return <$fh>; +} + +sub remove { + my $file = shift->stringify; + return unlink $file unless -e $file; # Sets $! correctly + 1 while unlink $file; + return not -e $file; +} + +1; +__END__ + +=head1 NAME + +Path::Class::File - Objects representing files + +=head1 SYNOPSIS + + use Path::Class qw(file); # Export a short constructor + + my $file = file('foo', 'bar.txt'); # Path::Class::File object + my $file = Path::Class::File->new('foo', 'bar.txt'); # Same thing + + # Stringifies to 'foo/bar.txt' on Unix, 'foo\bar.txt' on Windows, etc. + print "file: $file\n"; + + if ($file->is_absolute) { ... } + + my $v = $file->volume; # Could be 'C:' on Windows, empty string + # on Unix, 'Macintosh HD:' on Mac OS + + $file->cleanup; # Perform logical cleanup of pathname + + my $dir = $file->dir; # A Path::Class::Dir object + + my $abs = $file->absolute; # Transform to absolute path + my $rel = $file->relative; # Transform to relative path + +=head1 DESCRIPTION + +The C class contains functionality for manipulating +file names in a cross-platform way. + +=head1 METHODS + +=over 4 + +=item $file = Path::Class::File->new( , , ..., ) + +=item $file = file( , , ..., ) + +Creates a new C object and returns it. The +arguments specify the path to the file. Any volume may also be +specified as the first argument, or as part of the first argument. +You can use platform-neutral syntax: + + my $dir = file( 'foo', 'bar', 'baz.txt' ); + +or platform-native syntax: + + my $dir = dir( 'foo/bar/baz.txt' ); + +or a mixture of the two: + + my $dir = dir( 'foo/bar', 'baz.txt' ); + +All three of the above examples create relative paths. To create an +absolute path, either use the platform native syntax for doing so: + + my $dir = dir( '/var/tmp/foo.txt' ); + +or use an empty string as the first argument: + + my $dir = dir( '', 'var', 'tmp', 'foo.txt' ); + +If the second form seems awkward, that's somewhat intentional - paths +like C or C<\Windows> aren't cross-platform concepts in the +first place, so they probably shouldn't appear in your code if you're +trying to be cross-platform. The first form is perfectly fine, +because paths like this may come from config files, user input, or +whatever. + +=item $file->stringify + +This method is called internally when a C object is +used in a string context, so the following are equivalent: + + $string = $file->stringify; + $string = "$file"; + +=item $file->volume + +Returns the volume (e.g. C on Windows, C on Mac OS, +etc.) of the object, if any. Otherwise, returns the empty string. + +=item $file->basename + +Returns the name of the file as a string, without the directory +portion (if any). + +=item $file->is_dir + +Returns a boolean value indicating whether this object represents a +directory. Not surprisingly, C objects always +return false, and C objects always return true. + +=item $file->is_absolute + +Returns true or false depending on whether the file refers to an +absolute path specifier (like C or C<\Windows\Foo.txt>). + +=item $file->cleanup + +Performs a logical cleanup of the file path. For instance: + + my $file = file('/foo//baz/./foo.txt')->cleanup; + # $file now represents '/foo/baz/foo.txt'; + +=item $dir = $file->dir + +Returns a C object representing the directory +containing this file. + +=item $dir = $file->parent + +A synonym for the C method. + +=item $abs = $file->absolute + +Returns a C object representing C<$file> as an +absolute path. An optional argument, given as either a string or a +C object, specifies the directory to use as the base +of relativity - otherwise the current working directory will be used. + +=item $rel = $file->relative + +Returns a C object representing C<$file> as a +relative path. An optional argument, given as either a string or a +C object, specifies the directory to use as the base +of relativity - otherwise the current working directory will be used. + +=item $foreign = $file->as_foreign($type) + +Returns a C object representing C<$file> as it would +be specified on a system of type C<$type>. Known types include +C, C, C, C, and C, i.e. anything for which +there is a subclass of C. + +Any generated objects (subdirectories, files, parents, etc.) will also +retain this type. + +=item $foreign = Path::Class::File->new_foreign($type, @args) + +Returns a C object representing a file as it would +be specified on a system of type C<$type>. Known types include +C, C, C, C, and C, i.e. anything for which +there is a subclass of C. + +The arguments in C<@args> are the same as they would be specified in +C. + +=item $fh = $file->open($mode, $permissions) + +Passes the given arguments, including C<$file>, to C<< IO::File->new >> +(which in turn calls C<< IO::File->open >> and returns the result +as an C object. If the opening +fails, C is returned and C<$!> is set. + +=item $fh = $file->openr() + +A shortcut for + + $fh = $file->open('r') or die "Can't read $file: $!"; + +=item $fh = $file->openw() + +A shortcut for + + $fh = $file->open('w') or die "Can't write $file: $!"; + +=item $file->touch + +Sets the modification and access time of the given file to right now, +if the file exists. If it doesn't exist, C will I it +exist, and - YES! - set its modification and access time to now. + +=item $file->slurp() + +In a scalar context, returns the contents of C<$file> in a string. In +a list context, returns the lines of C<$file> (according to how C<$/> +is set) as a list. If the file can't be read, this method will throw +an exception. + +If you want C run on each line of the file, pass a true value +for the C or C parameters: + + my @lines = $file->slurp(chomp => 1); + +=item $file->remove() + +This method will remove the file in a way that works well on all +platforms, and returns a boolean value indicating whether or not the +file was successfully removed. + +C is better than simply calling Perl's C function, +because on some platforms (notably VMS) you actually may need to call +C several times before all versions of the file are gone - +the C method handles this process for you. + +=item $st = $file->stat() + +Invokes C<< File::stat::stat() >> on this file and returns a +C object representing the result. + +=item $st = $file->lstat() + +Same as C, but if C<$file> is a symbolic link, C +stats the link instead of the file the link points to. + +=back + +=head1 AUTHOR + +Ken Williams, ken@mathforum.org + +=head1 SEE ALSO + +Path::Class, Path::Class::Dir, File::Spec + +=cut diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Entries b/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Entries new file mode 100644 index 000000000..34b726339 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Entries @@ -0,0 +1,2 @@ +/VimColor.pm/1.1/Fri Apr 28 07:16:26 2006// +D/VimColor//// diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Repository b/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Repository new file mode 100644 index 000000000..17d6158fa --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Repository @@ -0,0 +1 @@ +xsieve/experiments/programlisting/vimcolor/lib/Text diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Root b/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Root new file mode 100644 index 000000000..d6bc77dba --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Text/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@xsieve.cvs.sourceforge.net:/cvsroot/xsieve diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/VimColor.pm b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor.pm new file mode 100644 index 000000000..dfca060a0 --- /dev/null +++ b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor.pm @@ -0,0 +1,845 @@ +package Text::VimColor; +use warnings; +use strict; + +use IO::File; +use File::Copy qw( copy ); +use File::Temp qw( tempfile ); +use Path::Class qw( file ); +use Carp; + +die "Text::VimColor can't see where it's installed" + unless -f __FILE__; +our $SHARED = file(__FILE__)->dir->subdir('VimColor')->stringify; + +our $VERSION = '0.11'; +our $VIM_COMMAND = 'vim'; +our @VIM_OPTIONS = (qw( -RXZ -i NONE -u NONE -N ), "+set nomodeline"); +our $NAMESPACE_ID = 'http://ns.laxan.com/text-vimcolor/1'; + +our %VIM_LET = ( + perl_include_pod => 1, + 'b:is_bash' => 1, +); + +our %SYNTAX_TYPE = ( + Comment => 1, + Constant => 1, + Identifier => 1, + Statement => 1, + PreProc => 1, + Type => 1, + Special => 1, + Underlined => 1, + Error => 1, + Todo => 1, +); + +# Set to true to print the command line used to run Vim. +our $DEBUG = 0; + +sub new +{ + my ($class, %options) = @_; + + $options{vim_command} = $VIM_COMMAND + unless defined $options{vim_command}; + $options{vim_options} = \@VIM_OPTIONS + unless defined $options{vim_options}; + + $options{html_inline_stylesheet} = 1 + unless exists $options{html_inline_stylesheet}; + $options{xml_root_element} = 1 + unless exists $options{xml_root_element}; + + $options{vim_let} = { + %VIM_LET, + (exists $options{vim_let} ? %{$options{vim_let}} : ()), + }; + + croak "only one of the 'file' or 'string' options should be used" + if defined $options{file} && defined $options{string}; + + my $self = bless \%options, $class; + $self->_do_markup + if defined $options{file} || defined $options{string}; + + return $self; +} + +sub vim_let +{ + my ($self, %option) = @_; + + while (my ($name, $value) = each %option) { + $self->{vim_let}->{$name} = $value; + } + + return $self; +} + +sub syntax_mark_file +{ + my ($self, $file, %options) = @_; + + local $self->{filetype} = exists $options{filetype} ? $options{filetype} + : $self->{filetype}; + + local $self->{file} = $file; + $self->_do_markup; + + return $self; +} + +sub syntax_mark_string +{ + my ($self, $string, %options) = @_; + + local $self->{filetype} = exists $options{filetype} ? $options{filetype} + : $self->{filetype}; + + local $self->{string} = $string; + $self->_do_markup; + + return $self; +} + +sub html +{ + my ($self) = @_; + my $syntax = $self->marked; + + my $html = ''; + $html .= $self->_html_header + if $self->{html_full_page}; + + foreach (@$syntax) { + $html .= _xml_escape($_->[1]), next + if $_->[0] eq ''; + + $html .= "[0]\">" . + _xml_escape($_->[1]) . + ''; + } + + $html .= "\n\n \n\n" + if $self->{html_full_page}; + + return $html; +} + +sub xml +{ + my ($self) = @_; + my $syntax = $self->marked; + + my $xml = ''; + if ($self->{xml_root_element}) { + my $filename = $self->input_filename; + $xml .= "[1]), next + if $_->[0] eq ''; + + $xml .= "[0]>" . + _xml_escape($_->[1]) . + "[0]>"; + } + + $xml .= "\n" + if $self->{xml_root_element}; + + return $xml; +} + +sub marked +{ + my ($self) = @_; + + exists $self->{syntax} + or croak "an input file or string must be specified, either to 'new' or". + " 'syntax_mark_file/string'"; + + return $self->{syntax}; +} + +sub input_filename +{ + my ($self) = @_; + + my $file = $self->{file}; + return $file if defined $file && !ref $file; + + return undef; +} + +# Return a string consisting of the start of an XHTML file, with a stylesheet +# either included inline or referenced with a . +sub _html_header +{ + my ($self) = @_; + + my $input_filename = $self->input_filename; + my $title = defined $self->{html_title} ? _xml_escape($self->{html_title}) + : defined $input_filename ? _xml_escape($input_filename) + : '[untitled]'; + + my $stylesheet; + if ($self->{html_inline_stylesheet}) { + $stylesheet = "\n"; + } + else { + $stylesheet = + "{html_stylesheet_url} || + "file://$SHARED/light.css") . + "\" />\n"; + } + + "\n" . + "\n" . + " \n" . + " $title\n" . + " $stylesheet" . + " \n" . + " \n\n" . + "
";
+}
+
+# Return a string safe to put in XML text or attribute values.  It doesn't
+# escape single quotes (') because we don't use those to quote
+# attribute values.
+sub _xml_escape
+{
+   my ($s) = @_;
+   $s =~ s/&/&/g;
+   $s =~ s//>/g;
+   $s =~ s/"/"/g;
+   return $s;
+}
+
+# Actually run Vim and turn the script's output into a datastructure.
+sub _do_markup
+{
+   my ($self) = @_;
+   my $vim_syntax_script = file($SHARED, 'mark.vim')->stringify;
+
+   croak "Text::VimColor syntax script '$vim_syntax_script' not installed"
+      unless -f $vim_syntax_script && -r $vim_syntax_script;
+
+   my $filename = $self->{file};
+   my $input_is_temporary = 0;
+   if (ref $self->{file}) {
+      my $fh;
+      ($fh, $filename) = tempfile();
+      $input_is_temporary = 1;
+
+      binmode $self->{file};
+      binmode $fh;
+      copy($self->{file}, $fh);
+   }
+   elsif (exists $self->{string}) {
+      my $fh;
+      ($fh, $filename) = tempfile();
+      $input_is_temporary = 1;
+
+      binmode $fh;
+      print $fh (ref $self->{string} ? ${$self->{string}} : $self->{string});
+   }
+   else {
+      croak "input file '$filename' not found"
+         unless -f $filename;
+      croak "input file '$filename' not accessible"
+         unless -r $filename;
+   }
+
+   # Create a temp file to put the output in.
+   my ($out_fh, $out_filename) = tempfile();
+
+   # Create a temp file for the 'script', which is given to vim
+   # with the -s option.  This is necessary because it tells Vim not
+   # to delay for 2 seconds after displaying a message.
+   my ($script_fh, $script_filename) = tempfile();
+   my $filetype = $self->{filetype};
+   my $filetype_set = defined $filetype ? ":set filetype=$filetype" : '';
+   my $vim_let = $self->{vim_let};
+   print $script_fh (map { ":let $_=$vim_let->{$_}\n" }
+                     grep { defined $vim_let->{$_} }
+                     keys %$vim_let),
+                    ":filetype on\n",
+                    "$filetype_set\n",
+                    ":source $vim_syntax_script\n",
+                    ":write! $out_filename\n",
+                    ":qall!\n";
+   close $script_fh;
+
+   $self->_run(
+      $self->{vim_command},
+      @{$self->{vim_options}},
+      $filename,
+      '-s', $script_filename,
+   );
+
+   unlink $filename
+      if $input_is_temporary;
+   unlink $out_filename;
+   unlink $script_filename;
+
+   my $data = do { local $/; <$out_fh> };
+
+   # Convert line endings to ones appropriate for the current platform.
+   $data =~ s/\x0D\x0A?/\n/g;
+
+   my $syntax = [];
+   LOOP: {
+      _add_markup($syntax, $1, $2), redo LOOP
+         if $data =~ /\G>(.*?)>(.*?)<\1]+)/cgs;
+   }
+
+   $self->{syntax} = $syntax;
+}
+
+# Given an array ref ($syntax), we add a new syntax chunk to it, unescaping
+# the text and making sure that consecutive chunks of the same type are
+# merged.
+sub _add_markup
+{
+   my ($syntax, $type, $text) = @_;
+
+   # Ignore types we don't know about.  At least one syntax file (xml.vim)
+   # can produce these.  It happens when a syntax type isn't 'linked' to
+   # one of the predefined types.
+   $type = ''
+      unless exists $SYNTAX_TYPE{$type};
+
+   # Unescape ampersands and pointies.
+   $text =~ s/&l//g;
+   $text =~ s/&a/&/g;
+
+   if (@$syntax && $syntax->[-1][0] eq $type) {
+      # Concatenate consecutive bits of the same type.
+      $syntax->[-1][1] .= $text;
+   }
+   else {
+      # A new chunk of marked-up text.
+      push @$syntax, [ $type, $text ];
+   }
+}
+
+# This is a private internal method which runs a program.
+# It takes a list of the program name and arguments.
+sub _run
+{
+   my ($self, $prog, @args) = @_;
+
+   if ($DEBUG) {
+      print STDERR __PACKAGE__."::_run: $prog " .
+            join(' ', map { s/'/'\\''/g; "'$_'" } @args) . "\n";
+   }
+
+   my ($err_fh, $err_filename) = tempfile();
+   my $old_fh = select($err_fh);
+   $| = 1;
+   select($old_fh);
+
+   my $pid = fork;
+   if ($pid) {
+      my $gotpid = waitpid($pid, 0);
+      croak "couldn't run the program '$prog'" if $gotpid == -1;
+      my $error = $? >> 8;
+      if ($error) {
+         seek $err_fh, 0, 0;
+         my $errout = do { local $/; <$err_fh> };
+         $errout =~ s/\n+\z//;
+         close $err_fh;
+         unlink $err_filename;
+         my $details = $errout eq '' ? '' :
+                       "\nVim wrote this error output:\n$errout\n";
+         croak "$prog returned an error code of '$error'$details";
+      }
+      close $err_fh;
+      unlink $err_filename;
+   }
+   else {
+      defined $pid
+         or croak "error forking to run $prog: $!";
+      open STDIN, '/dev/null';
+      open STDOUT, '>/dev/null';
+      open STDERR, '>&=' . fileno($err_fh)
+         or croak "can't connect STDERR to temporary file '$err_filename': $!";
+      exec $prog $prog, @args;
+      die "\n";   # exec() will already have sent a suitable error message.
+   }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Text::VimColor - syntax color text in HTML or XML using Vim
+
+=head1 SYNOPSIS
+
+   use Text::VimColor;
+   my $syntax = Text::VimColor->new(
+      file => $0,
+      filetype => 'perl',
+   );
+
+   print $syntax->html;
+   print $syntax->xml;
+
+=head1 DESCRIPTION
+
+This module tries to markup text files according to their syntax.  It can
+be used to produce web pages with pretty-printed colourful source code
+samples.  It can produce output in the following formats:
+
+=over 4
+
+=item HTML
+
+Valid XHTML 1.0, with the exact colouring and style left to a CSS stylesheet
+
+=item XML
+
+Pieces of text are marked with XML elements in a simple vocabulary,
+which can be converted to other formats, for example, using XSLT
+
+=item Perl array
+
+A simple Perl data structure, so that Perl code can be used to turn it
+into whatever is needed
+
+=back
+
+This module works by running the Vim text editor and getting it to apply its
+excellent syntax highlighting (aka 'font-locking') to an input file, and mark
+pieces of text according to whether it thinks they are comments, keywords,
+strings, etc.  The Perl code then reads back this markup and converts it
+to the desired output format.
+
+This is an object-oriented module.  To use it, create an object with
+the C function (as shown above in the SYNOPSIS) and then call methods
+to get the markup out.
+
+=head1 METHODS
+
+=over 4
+
+=item new(I)
+
+Returns a syntax highlighting object.  Pass it a hash of options.
+
+The following options are recognised:
+
+=over 4
+
+=item file
+
+The file to syntax highlight.  Can be either a filename or an open file handle.
+
+Note that using a filename might allow Vim to guess the file type from its
+name if none is specified explicitly.
+
+If the file isn't specified while creating the object, it can be given later
+in a call to the C method (see below), allowing a single
+Text::VimColor object to be used with multiple input files.
+
+=item string
+
+Use this to pass a string to be used as the input.  This is an alternative
+to the C option.  A reference to a string will also work.
+
+The C method (see below) is another way to use a string
+as input.
+
+=item filetype
+
+Specify the type of file Vim should expect, in case Vim's automatic
+detection by filename or contents doesn't get it right.  This is
+particularly important when providing the file as a string of file
+handle, since Vim won't be able to use the file extension to guess
+the file type.
+
+The filetypes recognised by Vim are short strings like 'perl' or 'lisp'.
+They are the names of files in the 'syntax' directory in the Vim
+distribution.
+
+This option, whether or not it is passed to C, can be overridden
+when calling C and C, so you can
+use the same object to process multiple files of different types.
+
+=item html_full_page
+
+By default the C output method returns a fragment of HTML, not a
+full file.  To make useful output this must be wrapped in a CpreE>
+element and a stylesheet must be included from somewhere.  Setting the
+C option will instead make the C method return a
+complete stand-alone XHTML file.
+
+Note that while this is useful for testing, most of the time you'll want to
+put the syntax highlighted source code in a page with some other content,
+in which case the default output of the C method is more appropriate.
+
+=item html_inline_stylesheet
+
+Turned on by default, but has no effect unless C is also
+enabled.
+
+This causes the CSS stylesheet defining the colours to be used
+to render the markup to be be included in the HTML output, in a
+CstyleE> element.  Turn it off to instead use a ClinkE>
+to reference an external stylesheet (recommended if putting more than one
+page on the web).
+
+=item html_stylesheet
+
+Ignored unless C and C are both
+enabled.
+
+This can be set to a stylesheet to include inline in the HTML output (the
+actual CSS, not the filename of it).
+
+=item html_stylesheet_file
+
+Ignored unless C and C are both
+enabled.
+
+This can be the filename of a stylesheet to copy into the HTML output,
+or a file handle to read one from.  If neither this nor C
+are given, the supplied stylesheet F will be used instead.
+
+=item html_stylesheet_url
+
+Ignored unless C is enabled and C
+is disabled.
+
+This can be used to supply the URL (relative or absolute) or the stylesheet
+to be referenced from the HTML ClinkE> element in the header.
+If this isn't given it will default to using a C URL to reference
+the supplied F stylesheet, which is only really useful for testing.
+
+=item xml_root_element
+
+By default this is true.  If set to a false value, XML output will not be
+wrapped in a root element called , but will be otherwise the
+same.  This could allow XML output for several files to be concatenated,
+but to make it valid XML a root element must be added.  Disabling this
+option will also remove the binding of the namespace prefix C, so
+an C attribute would have to be added elsewhere.
+
+=item vim_command
+
+The name of the executable which will be run to invoke Vim.
+The default is C.
+
+=item vim_options
+
+A reference to an array of options to pass to Vim.  The default options are:
+
+   qw( -RXZ -i NONE -u NONE -N )
+
+=item vim_let
+
+A reference to a hash of options to set in Vim before the syntax file
+is loaded.  Each of these is set using the C<:let> command to the value
+specified.  No escaping is done on the values, they are executed exactly
+as specified.
+
+Values in this hash override some default options.  Use a value of
+C to prevent a default option from being set at all.  The
+defaults are as follows:
+
+   (
+      perl_include_pod => 1,     # Recognize POD inside Perl code
+      'b:is_bash' => 1,          # Allow Bash syntax in shell scripts
+   )
+
+These settings can be modified later with the C method.
+
+=back
+
+=item vim_let(I =E I, ...)
+
+Change the options that are set with the Vim C command when Vim
+is run.  See C for details.
+
+=item syntax_mark_file(I, I)
+
+Mark up the specified file.  Subsequent calls to the output methods will then
+return the markup.  It is not necessary to call this if a C or C
+option was passed to C.
+
+Returns the object it was called on, so an output method can be called
+on it directly:
+
+   my $syntax = Text::VimColor->new(
+      vim_command => '/usr/local/bin/special-vim',
+   );
+
+   foreach (@files) {
+      print $syntax->syntax_mark_file($_)->html;
+   }
+
+You can override the filetype set in new() by passing in a C
+option, like so:
+
+   $syntax->syntax_mark_file($filename, filetype => 'perl');
+
+This option will only affect the syntax colouring for that one call,
+not for any subsequent ones on the same object.
+
+=item syntax_mark_string(I, I)
+
+Does the same as C (see above) but uses a string as input.
+I can also be a reference to a string.
+Returns the object it was called on.  Supports the C option
+just as C does.
+
+=item html()
+
+Return XHTML markup based on the Vim syntax colouring of the input file.
+
+Unless the C option is set, this will only return a fragment
+of HTML, which can then be incorporated into a full page.  The fragment
+will be valid as either HTML and XHTML.
+
+The only markup used for the actual text will be CspanE> elements
+wrapped round appropriate pieces of text.  Each one will have a C
+attribute set to a name which can be tied to a foreground and background
+color in a stylesheet.  The class names used will have the prefix C,
+for example C.  For the full list see the section
+HIGHLIGHTING TYPES below.
+
+=item xml()
+
+Returns markup in a simple XML vocabulary.  Unless the C
+option is turned off (it's on by default) this will produce a complete XML
+document, with all the markup inside a CsyntaxE> element.
+
+This XML output can be transformed into other formats, either using programs
+which read it with an XML parser, or using XSLT.  See the
+text-vimcolor(1) program for an example of how XSLT can be used with
+XSL-FO to turn this into PDF.
+
+The markup will consist of mixed content with elements wrapping pieces
+of text which Vim recognized as being of a particular type.  The names of
+the elements used are the ones listed in the HIGHLIGHTING TYPES section
+below.
+
+The CsyntaxE> element will declare the namespace for all the
+elements prodeced, which will be C.
+It will also have an attribute called C, which will be set to the
+value returned by the C method, if that returns something
+other than undef.
+
+The XML namespace is also available as C<$Text::VimColor::NAMESPACE_ID>.
+
+=item marked()
+
+This output function returns the marked-up text in the format which the module
+stores it in internally.  The data looks like this:
+
+   use Data::Dumper;
+   print Dumper($syntax->marked);
+
+   $VAR1 = [
+      [ 'Statement', 'my' ],
+      [ '', ' ' ],
+      [ 'Identifier', '$syntax' ],
+      [ '', ' = ' ],
+       ...
+   ];
+
+The C method returns a reference to an array.  Each item in the
+array is itself a reference to an array of two items: the first is one of
+the names listed in the HIGHLIGHTING TYPES section below (or the empty
+string if none apply), and the second is the actual piece of text.
+
+=item input_filename()
+
+Returns the filename of the input file, or undef if a filename wasn't
+specified.
+
+=back
+
+=head1 HIGHLIGHTING TYPES
+
+The following list gives the names of highlighting types which will be
+set for pieces of text.  For HTML output, these will appear as CSS class
+names, except that they will all have the prefix C added.  For XML
+output, these will be the names of elements which will all be in the
+namespace C.
+
+Here is the complete list:
+
+=over 4
+
+=item *
+
+Comment
+
+=item *
+
+Constant
+
+=item *
+
+Identifier
+
+=item *
+
+Statement
+
+=item *
+
+PreProc
+
+=item *
+
+Type
+
+=item *
+
+Special
+
+=item *
+
+Underlined
+
+=item *
+
+Error
+
+=item *
+
+Todo
+
+=back
+
+=head1 RELATED  MODULES
+
+These modules allow Text::VimColor to be used more easily in particular
+environments:
+
+=over 4
+
+=item L
+
+=item L
+
+=item L
+
+=back
+
+=head1 SEE ALSO
+
+=over 4
+
+=item text-vimcolor(1)
+
+A simple command line interface to this module's features.  It can be used
+to produce HTML and XML output, and can also generate PDF output using
+an XSLT/XSL-FO stylesheet and the FOP processor.
+
+=item http://www.vim.org/
+
+Everything to do with the Vim text editor.
+
+=item http://ungwe.org/blog/
+
+The author's weblog, which uses this module.  It is used to make the code
+samples look pretty.
+
+=back
+
+=head1 BUGS
+
+Quite a few, actually:
+
+=over 4
+
+=item *
+
+Apparently this module doesn't always work if run from within a 'gvim'
+window, although I've been unable to reproduce this so far.
+CPAN bug #11555.
+
+=item *
+
+Things can break if there is already a Vim swapfile, but sometimes it
+seems to work.
+
+=item *
+
+There should be a way of getting a DOM object back instead of an XML string.
+
+=item *
+
+It should be possible to choose between HTML and XHTML, and perhaps there
+should be some control over the DOCTYPE declaration when a complete file is
+produced.
+
+=item *
+
+With Vim versions earlier than 6.2 there is a 2 second delay each time
+Vim is run.
+
+=item *
+
+It doesn't work on Windows.  I am unlikely to fix this, but if anyone
+who knows Windows can sort it out let me know.
+
+=back
+
+=head1 AUTHOR
+
+Geoff Richards Eqef@laxan.comE
+
+The Vim script F is a crufted version of F<2html.vim> by
+Bram Moolenaar EBram@vim.orgE and
+David Ne\v{c}as (Yeti) Eyeti@physics.muni.czE.
+
+=head1 COPYRIGHT
+
+Copyright 2002-2006, Geoff Richards.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+# Local Variables:
+# mode: perl
+# perl-indent-level: 3
+# perl-continued-statement-offset: 3
+# End:
+# vi:ts=3 sw=3 expandtab:
diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Entries b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Entries
new file mode 100644
index 000000000..77e5c8bf4
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Entries
@@ -0,0 +1,4 @@
+/light.css/1.1/Fri Apr 28 07:16:26 2006//
+/light.xsl/1.1/Fri Apr 28 07:16:26 2006//
+/mark.vim/1.1/Fri Apr 28 07:16:26 2006//
+D
diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Repository b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Repository
new file mode 100644
index 000000000..432233476
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Repository
@@ -0,0 +1 @@
+xsieve/experiments/programlisting/vimcolor/lib/Text/VimColor
diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Root b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Root
new file mode 100644
index 000000000..d6bc77dba
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/CVS/Root
@@ -0,0 +1 @@
+:pserver:anonymous@xsieve.cvs.sourceforge.net:/cvsroot/xsieve
diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/light.css b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/light.css
new file mode 100644
index 000000000..9a67b1cb9
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/light.css
@@ -0,0 +1,30 @@
+/*
+ * A stylesheet designed to be used with the HTML output of the
+ * Perl module Text::Highlight::Vim.
+ *
+ * This is designed to make the highlighting look like the default gvim
+ * colour scheme, with 'background=light'.
+ *
+ * Geoff Richards (qef@laxan.com)
+ *
+ * This CSS file (light.css) is public domain.  Do what you want with it.
+ * That doesn't mean that HTML with this CSS in is public domain.
+ */
+
+body { color: black; background: white none }
+
+A:link { color: #00F; background: white none }
+A:visited { color: #909; background: white none }
+A:hover { color: #F00; background: white none }
+A:active { color: #F00; background: white none }
+
+.synComment    { color: #0000FF }
+.synConstant   { color: #FF00FF }
+.synIdentifier { color: #008B8B }
+.synStatement  { color: #A52A2A ; font-weight: bold }
+.synPreProc    { color: #A020F0 }
+.synType       { color: #2E8B57 ; font-weight: bold }
+.synSpecial    { color: #6A5ACD }
+.synUnderlined { color: #000000 ; text-decoration: underline }
+.synError      { color: #FFFFFF ; background: #FF0000 none }
+.synTodo       { color: #0000FF ; background: #FFFF00 none }
diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/light.xsl b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/light.xsl
new file mode 100644
index 000000000..5b217de86
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/light.xsl
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+ 
+  
+
+   
+
+    
+    
+     
+     
+     
+    
+
+    
+    
+     
+     
+     
+    
+
+    
+     
+      
+      
+     
+    
+
+   
+
+   
+
+    
+    
+     
+      
+     
+    
+
+    
+    
+     
+      
+     
+    
+
+    
+    
+     
+      
+     
+    
+
+   
+
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+ 
+  
+ 
+
+
diff --git a/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/mark.vim b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/mark.vim
new file mode 100644
index 000000000..22ed73f58
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/lib/Text/VimColor/mark.vim
@@ -0,0 +1,67 @@
+" mark.vim - turn Vim syntax highlighting into an ad-hoc markup language that
+" can be parsed by the Text::VimColor Perl module.
+"
+" Maintainer: Geoff Richards 
+" Based loosely on 2html.vim, by Bram Moolenaar ,
+"   modified by David Ne\v{c}as (Yeti) .
+
+set report=1000000
+
+" For some reason (I'm sure it used to work) we now need to get Vim
+" to make another attempt to detect the filetype if it wasn't set
+" explicitly.
+if !strlen(&filetype)
+   filetype detect
+endif
+syn on
+
+" Set up the output buffer.
+new
+set modifiable
+set paste
+
+" Expand tabs. Without this they come out as '^I'.
+set isprint+=9
+
+wincmd p
+
+" Loop over all lines in the original text
+let s:end = line("$")
+let s:lnum = 1
+while s:lnum <= s:end
+
+  " Get the current line
+  let s:line = getline(s:lnum)
+  let s:len = strlen(s:line)
+  let s:new = ""
+
+  " Loop over each character in the line
+  let s:col = 1
+  while s:col <= s:len
+    let s:startcol = s:col " The start column for processing text
+    let s:id = synID(s:lnum, s:col, 1)
+    let s:col = s:col + 1
+    " Speed loop (it's small - that's the trick)
+    " Go along till we find a change in synID
+    while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
+
+    " Output the text with the same synID, with class set to c{s:id}
+    let s:id = synIDtrans(s:id)
+    let s:name = synIDattr(s:id, 'name')
+    let s:new = s:new . '>' . s:name . '>' . substitute(substitute(substitute(strpart(s:line, s:startcol - 1, s:col - s:startcol), '&', '\&a', 'g'), '<', '\&l', 'g'), '>', '\&g', 'g') . '<' . s:name . '<'
+
+    if s:col > s:len
+      break
+    endif
+  endwhile
+
+  exe "normal \pa" . strtrans(s:new) . "\n\e\p"
+  let s:lnum = s:lnum + 1
+  +
+endwhile
+
+" Strip whitespace from the ends of lines
+%s:\s\+$::e
+
+wincmd p
+normal dd
diff --git a/manual/docbook/programlisting/vimcolor/text-vimcolor b/manual/docbook/programlisting/vimcolor/text-vimcolor
new file mode 100755
index 000000000..12c1ee1cc
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/text-vimcolor
@@ -0,0 +1,278 @@
+#!/usr/bin/perl -w
+use strict;
+
+use Text::VimColor;
+use Getopt::Long;
+use File::Temp qw( tempfile );
+use IO::File;
+use Path::Class qw( file );
+
+my $XSL_STYLESHEET = file($Text::VimColor::SHARED, 'light.xsl');
+
+# Default values for options.
+my $filetype;
+my $format;
+my $usage;
+my $output_filename;
+my $html_full_page;
+my $html_no_inline_stylesheet;
+my @let;
+my @unlet;
+
+my $option = GetOptions(
+   'debug' => \$Text::VimColor::DEBUG,
+   'filetype=s' => \$filetype,
+   'format=s' => \$format,
+   'help' => \$usage,
+   'output=s' => \$output_filename,
+   'full-page' => \$html_full_page,
+   'no-inline-stylesheet' => \$html_no_inline_stylesheet,
+   'let=s' => \@let,
+   'unlet=s' => \@unlet,
+   'usage' => \$usage,
+);
+
+if ($usage) {
+   print STDERR
+      "Usage: $0 --format html|xml [options] filename\n",
+      "       $0 --format pdf --output foo.pdf [options] filename\n",
+      "(the output is written to standard output, except in PDF\n",
+      "mode, where you have to supply a filename for the output.)\n",
+      "\n",
+      "Options:\n",
+      "    --debug      turn on Text::VimColor debugging mode\n",
+      "    --filetype   set Vim filetype name, if it can't be guessed from\n",
+      "                 the file's name or contents\n",
+      "    --format     set format to use for output, can be xml,\n",
+      "                 html, or pdf\n",
+      "    --help       show this helpful message\n",
+      "    --output     set filename to write output to (required with\n",
+      "                 PDF format, otherwise defaults to standard output)\n",
+      "    --full-page  output a complete HTML page, not just a fragment\n",
+      "    --no-inline-stylesheet\n",
+      "                 don't include the stylesheet in a complete HTML page\n",
+      "    --let        set a Vim variable with the Vim :let command\n",
+      "    --unlet      turn off default setting of a Vim variable\n";
+}
+
+defined $format
+   or die "$0: an output format must be specified (html, pdf or xml).\n";
+
+$format = lc $format;
+$format eq 'html' || $format eq 'pdf' || $format eq 'xml'
+   or die "$0: invalid output format '$format' (must be html, pdf or xml).\n";
+
+my $output;
+if (defined $output_filename) {
+   $output = IO::File->new($output_filename, 'w')
+      or die "$0: error opening output file '$output_filename': $!\n";
+}
+else {
+   $format ne 'pdf'
+      or die "$0: an output file must be specified with '--format pdf'.\n";
+
+   $output = \*STDOUT;
+   $output_filename = '';
+}
+
+@ARGV <= 1
+   or die "$0: only one input filename should be specified.\n";
+
+my $file = @ARGV ? shift : \*STDIN;
+
+my $syntax = Text::VimColor->new(
+   filetype => $filetype,
+   html_full_page => $html_full_page,
+   html_inline_stylesheet => !$html_no_inline_stylesheet,
+);
+
+# Handle the --let and --unlet options.
+foreach (@unlet) {
+   $syntax->vim_let($_ => undef);
+}
+foreach (@let) {
+   my ($name, $value) = /^(.*?)=(.*)\z/
+      or die "$0: bad --let option '$_'\n";
+   print STDERR "[$name] [$value]\n";
+   $syntax->vim_let($name => $value);
+}
+
+$syntax->syntax_mark_file($file);
+
+if ($format eq 'xml') {
+   print $output $syntax->xml
+      or die "$0: error writing to output file '$output_filename': $!\n";
+}
+elsif ($format eq 'html') {
+   print $output $syntax->html
+      or die "$0: error writing to output file '$output_filename': $!\n";
+}
+else {   # ($format eq 'pdf')
+   my ($fh, $tmp_filename) = tempfile();
+   print $fh $syntax->xml
+      or die "$0: error writing to temporary file '$tmp_filename': $!\n";
+
+   system('fop', '-xsl', $XSL_STYLESHEET,
+                 '-xml', $tmp_filename,
+                 '-pdf', $output_filename) == 0
+      or die "$0: error running 'fop' (exit code was $?).\n";
+
+   unlink $tmp_filename
+      or die "$0: error deleting temporary file '$tmp_filename': $!\n";
+}
+
+exit 0;
+
+__END__
+
+=head1 NAME
+
+text-vimcolor - command-line program to syntax color a file in HTML, XML or PDF
+
+=head1 SYNOPSIS
+
+   $ text-vimcolor --format html --full-page FILENAME > OUTPUT.html
+   $ text-vimcolor --format xml FILENAME > OUTPUT.xml
+   $ text-vimcolor --format pdf FILENAME --output OUTPUT.pdf
+
+=head1 DESCRIPTION
+
+This program uses the Vim text editor to highlight text according to its
+syntax, and turn the highlighting into HTML, XML or PDF output.  It works
+with any file type which Vim itself can highlight.  Usually Vim will be
+able to autodetect the file format based on the filename (and sometimes the
+contents of the file).
+
+Exactly one filename should be given on the command line to name the input
+file.  If none is given input will instead be read from stdin (the standard
+input).
+
+If Vim can't guess the file type automatically, it can be specified explicitly
+using the C<--filetype> option.  For example:
+
+   $ text-vimcolor --format html --filetype prolog foo.pl > foo.html
+
+This program is a command line interface to the Perl module Text::VimColor.
+
+=head1 OPTIONS
+
+The following options are understood:
+
+=over 4
+
+=item --help
+
+Show a summary of the usage, including a list of options.
+
+=item --debug
+
+Turns on debugging in the underlying Perl module.  This makes it print
+the command used to run Vim.
+
+=item --filetype I
+
+Set the type of the file explicitly.  The I argument should be
+something which Vim will recognise when set with its C option.
+Examples are C, C (for C++) and C (for Unix shell scripts).
+These names are case sensitive, and should usually be all-lowercase.
+
+=item --format I
+
+The output format to generate.  Must be one of the following:
+
+=over 4
+
+=item html
+
+Generate XHTML output, with text marked with CspanE> elements
+with C attributes.  A CSS stylesheet should be used to define the
+coloring, etc., for the output.  See the C<--full-page> option below.
+
+=item xml
+
+Output is in a simple XML vocabulary.  This can then be used by other
+software to do further transformations (e.g., using XSLT).
+
+=item pdf
+
+XML output is generated and fed to the FOP XSL-FO processor, with an
+appropriate XSL style sheet.  The stylesheet uses XSLT to transform the
+normal XML output into XSL-FO, which is then rendered to PDF.  For this
+to work, the command C must be available.  An output file must be
+specified with C<--output> with this format.
+
+=back
+
+Full details of the HTML and XML output formats can be found in the
+documentation for Text::VimColor.
+
+=item --output I
+
+Specifies the name of the output file (which will end up containing either
+HTML, XML or PDF).  If this option is omitted, the output will be sent
+to stdout (the standard output).  This option is required when the output
+format is PDF (because of limitations in FOP).
+
+=item --full-page
+
+When the output format is HTML, this option will make the output a complete
+HTML page, rather than just a fragment of HTML.  A CSS stylesheet will be
+inserted inline into the output, so the output will be useable as it is.
+
+=item --no-inline-stylesheet
+
+When the output format is HTML and C<--fullpage> is given, a stylesheet
+is normally inserted in-line in the output file.  If this option is given it
+will instead be referenced with a ClinkE> element.
+
+=item --let I=I
+
+When Vim is run the value of I will be set to I using
+Vim's C command.  More than one of these options can be set.
+The value is not quoted or escaped in any way, so it can be an expression.
+These settings take precedence over C<--unlet> options.
+
+This option corresponds to the C setting and method in
+the Perl module.
+
+=item --unlet I
+
+Prevent the value of I being set with Vim's C command.
+This can be used to turn off default settings.
+
+This option corresponds to the C setting and method in
+the Perl module, when used with a value of C.
+
+=back
+
+=head1 BUGS
+
+=over 4
+
+=item *
+
+The PDF output option often doesn't work, because it is dependent on FOP,
+which often doesn't work.  This is also why it is mind numbingly slow.
+
+=item *
+
+FOP (0.20.3) seems to ignore the C property on
+Cfo:inlineE>.  If that's what it's meant to do, how do you set the
+background color on part of a line?
+
+=back
+
+=head1 AUTHOR
+
+Geoff Richards Eqef@laxan.comE
+
+=head1 COPYRIGHT
+
+Copyright 2002-2006, Geoff Richards.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl.
+
+=cut
+
+# vi:ts=3 sw=3 expandtab
diff --git a/manual/docbook/programlisting/vimcolor/vimcolor-wrapper b/manual/docbook/programlisting/vimcolor/vimcolor-wrapper
new file mode 100755
index 000000000..422824b45
--- /dev/null
+++ b/manual/docbook/programlisting/vimcolor/vimcolor-wrapper
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+DIR=`dirname $0`
+
+PERL5LIB=$DIR/lib:$PERL5LIB
+export PERL5LIB
+
+$DIR/text-vimcolor "$@"
+