File Coverage

blib/lib/CGI/pWiki.pm
Criterion Covered Total %
statement 17 326 5.2
branch 1 170 0.5
condition 1 34 2.9
subroutine 5 29 17.2
pod 1 25 4.0
total 25 584 4.2


\n!g; \n!g;
line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3 1     1   7135 use 5.00503;
  1         4  
  1         51  
4             package CGI::pWiki;
5 1     1   5 use strict;
  1         2  
  1         39  
6 1     1   1125 use URI::Escape qw(uri_escape uri_unescape);
  1         1529  
  1         126  
7 1     1   6 use vars qw($VERSION); $VERSION = "0.15";
  1         1  
  1         5246  
8              
9             #------------------------------------------------------------------------------#
10              
11             =pod
12              
13             =head1 NAME
14              
15             CGI::pWiki - Perl Wiki Environment
16              
17             =head1 SYNOPSIS
18              
19             #!/usr/bin/perl
20             use CGI::pWiki;
21             use strict;
22             my $pWiki = new CGI::pWiki()->server();
23             0;
24              
25             =head1 DESCRIPTION
26              
27             The B class, is providing an environment for serving
28             a WikiWikiWeb for virtual hosts and multiple databases.
29              
30             =head1 USAGE
31              
32             =head2 Installation
33              
34             At first install the CGI::pWiki module either on the CPAN,
35             or the Debian or by hand as usual with :
36              
37             perl Makefile.PL &&
38             make &&
39             make test &&
40             su -c "make install"
41              
42             First check your /etc/apache/httpd.conf for the system wide
43             ScriptAlias path and directory path.
44              
45             ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
46              
47             The pWiki distibution includes a pWiki.cgi to be symlinked
48             from your install point to your system wide cgi-bin directory.
49            
50             ln -s /usr/local/bin/pWiki.cgi /usr/lib/cgi-bin/
51              
52             Next check your /etc/apache/httpd.conf to contain at least
53             those modules :
54              
55             LoadModule mime_module /usr/lib/apache/1.3/mod_mime.so
56             LoadModule dir_module /usr/lib/apache/1.3/mod_dir.so
57             LoadModule cgi_module /usr/lib/apache/1.3/mod_cgi.so
58             LoadModule alias_module /usr/lib/apache/1.3/mod_alias.so
59             LoadModule access_module /usr/lib/apache/1.3/mod_access.so
60             LoadModule auth_module /usr/lib/apache/1.3/mod_auth.so
61             LoadModule setenvif_module /usr/lib/apache/1.3/mod_setenvif.so
62             LoadModule action_module /usr/lib/apache/1.3/mod_actions.so
63              
64             Add a virtual host directive :
65              
66             NameVirtualHost *
67            
68             ServerName test.copyleft.de
69             DocumentRoot /var/www/test.copyleft.de
70             DirectoryIndex index.wiki index.xml index.html index.htm index.text
71             Action wiki-script /cgi-bin/pWiki.cgi
72             # Some Apaches need the next line, also.
73             # ErrorDocument 404 /cgi-bin/pWiki.cgi
74            
75             AddHandler wiki-script .wiki
76             AddHandler wiki-script .text
77             AddHandler wiki-script .html
78             AddHandler wiki-script .htm
79             AddHandler wiki-script .pod
80             AddHandler wiki-script .xml
81             # The next line should be in 127.0.0.1 virtual hosts, only !
82             # AddHandler wiki-script .xsl
83            
84              
85             There is no need to add any handler besides B<.wiki> and B<.text>,
86             if you dont want to manage the other files with B.
87             Handling B<.xsl> files in fact opens a wide security hole, and should
88             B be done outside a B environment.
89              
90             =head2 Security
91              
92             CGI::pWiki will offer users from outside to write files in the
93             document root of your webserver. It is therefore a possible
94             security hole. The minimal security is to constrain write access
95             by using the Unix C command. e.g. :
96              
97             mkdir /var/www/test.copyleft.de
98             echo "=location /open/index.wiki" /var/www/test.copyleft.de/index.wiki
99             mkdir /var/www/test.copyleft.de/open
100             touch /var/www/test.copyleft.de/open/index.wiki
101             chmod a+w /var/www/test.copyleft.de/open
102             chmod a+w /var/www/test.copyleft.de/open/index.wiki
103              
104             This will create a document root for the test site, installs
105             a relocation of the index page, and creates an open area and
106             its index page, and makes it world writeable, while other
107             areas will stay read only.
108              
109             A typical all public site for creating open content may want
110             to allow every directory to be writeable. Adopt the following
111             lines to migrate existing content.
112              
113             find /var/www/test.copyleft.de/ -print | xargs sudo chown kraehe.www-data
114             find /var/www/test.copyleft.de/ -type d -print | xargs chmod 6775
115             find /var/www/test.copyleft.de/ ! -type d -print | xargs chmod 664
116              
117             You may want to restrict edit access to the Wiki as a webmaster
118             by defining a directory directive :
119              
120            
121             AuthUserFile /usr/local/etc/test.copyleft.de.htpasswd
122             AuthName "For Test Only"
123             AuthType Basic
124            
125             require valid-user
126            
127            
128              
129             Or leave this as an option for .htaccess :
130              
131             AuthUserFile /usr/local/etc/test.copyleft.de.htpasswd
132             AuthName "For Test Only"
133             AuthType Basic
134            
135             require valid-user
136            
137              
138             =head2 First Test
139              
140             You can now test the pWiki by reloading Apache. Create a directories
141             for your virtual host to contain a database called pWiki. The second
142             directory needs to be writeable by the webserver, as it contains the
143             shadow pages, if people change the content online.
144              
145             mkdir -p /var/www/test.copyleft.de/pWiki
146             mkdir -p /var/lib/pWiki/test.copyleft.de/pWiki
147             chmod a+w /var/lib/pWiki/test.copyleft.de/pWiki
148              
149             Browse at your fresh created test site and enter the URL :
150              
151             http://test.copyleft.de/pWiki/index.wiki
152              
153             This should show an edit window. Submit something like the following :
154              
155             This is a test for pWiki.
156              
157             Click on the pWiki and submit the following :
158              
159             The CGI_pWiki Perl_Module is an Apache_Handler acting as a
160             wrapper around a WikiWikiWeb for creating content in a
161             [comunity] on the fly.
162              
163             Benefits :
164              
165             * rapid content creation
166             * easy formatting rules
167             * multiple authors
168              
169             CGI_pWiki is able to handle the following extensions :
170              
171             | .html | normal hypertext pages |
172             | .text | preformated text pages |
173             | .wiki | pWiki formated hypertext pages |
174             | .xml | XSL formated hypertext pages |
175             | .pod | PlainOldDocumentation |
176              
177             Ensure that there are no leading white space when cut and paste.
178              
179             =head2 Adding Style
180              
181             The CGI-pWiki distribution contains an example database.
182             Copy it to your document root :
183              
184             cp htdocs/pWiki/* /var/www/test.copyleft.de/pWiki/
185              
186             The style is defined in pairs of files with B<.lnx> and B<.moz>
187             extension. Copy the pWiki/content.{lnx,moz}-exam files to your
188             document root and define the main table of contents.
189              
190             =head2 METHODS
191              
192             =over
193              
194             =item new proto HASH
195              
196             Creates a new pWiki object. Default options are passed as key-value
197             pairs or as a single hash. Options may be changed directly in the
198             object.
199              
200             =head1 AUTHOR
201              
202             (c) 2002 GNU/GPL+Perl/Artistic Michael Koehne kraehe@copyleft.de
203              
204             =head1 SEE ALSO
205              
206             CGI
207              
208             =cut
209              
210             #------------------------------------------------------------------------------#
211              
212             my $ESCAPE1 = '(&|<|>|"|--)';
213             my $ESCAPE2 = {
214             '&' => '&',
215             '<' => '<',
216             '>' => '>',
217             '"' => '"',
218             '--' => '--'
219             };
220             my $TEMPLATE= {
221             'edit' => '
222             Edit: %TOPIC%
223            
224            
',
227             'notfound' => '
228             %TOPIC% was not found in pWiki.

229             This could be, because this page has moved,
230             or because nothing has been written yet.

231              
232            
233             You may want to
234            
235             for
236            
237            
238              
239            
240            
241             You may want to
242            
243             it now.
244            
245             ',
246             'content' => '',
247             'style' => '%HTML%'
248             };
249              
250             #------------------------------------------------------------------------------#
251              
252             sub new {
253 1     1 1 11 my $proto = shift;
254 1 50       5 my $self = ($#_ == 0) ? { %{ (shift) } } : { @_ };
  0         0  
255 1   33     8 my $class = ref($proto) || $proto;
256              
257 1         2 bless($self, $class);
258              
259 1         3 return $self;
260             }
261              
262             sub server {
263 0     0 0   my $self=shift;
264              
265 0           $self->parse_request;
266 0           my $html = $self->translate;
267              
268 0 0         if ($html ne "") {
269 0           print "Content-type: text/html\n\n";
270 0           print $html;
271             } else {
272 0           $self->error("$self->{pt} not found");
273             }
274             }
275              
276             #------------------------------------------------------------------------------#
277              
278             sub html {
279 0     0 0   my $self = shift;
280              
281 0           $_ = $self->readfile($self->{pt});
282 0 0         $self->{TITLE} = $1 if m!(.+)!i;
283 0 0         $_ = $1 if m!]*>(.+)!is;
284              
285 0           return $_;
286             }
287              
288             sub text {
289 0     0 0   my $self = shift;
290              
291 0           $_ = "\n".$self->readfile($self->{pt});
292              
293 0           return "
$_
";
294             }
295              
296             sub wiki {
297 0     0 0   my $self = shift;
298 0           my $html = "";
299              
300 0           $_ = "\n".$self->readfile($self->{pt});
301              
302             # convert old wiki tags
303 0           s!!\n=$1\n!g;
304 0           s!]*>!\n=$1 $2\n!g;
305 0           s!]+>!!g;
306              
307              
308             # handle paragraphs, lists and tables.
309 0           foreach (split /\n\n+/) {
310 0 0         next, if /^[ \t\n]*$/;
311 0 0         $_ = "\n$_" unless /^\n/;
312 0           chomp;
313 0 0         $html .= $self->format_command($_), next
314             if /^(\n=[^\n]+)+$/;
315 0 0         $html .= $self->format_list($_), next
316             if /^(\n[ \t]*[*-][^\n]+)+$/;
317 0 0         $html .= $self->format_table($_), next
318             if /^(\n[ \t]*[|][^\n]+[|][ \t]*)+$/;
319 0 0         $html .= $self->format_verbatim($_), next
320             if /^(\n[ \t]+[^\n]+)+$/;
321 0           $html .= $self->format_ordinary($_);
322             }
323              
324 0           return "$html";
325             }
326              
327             #------------------------------------------------------------------------------#
328              
329             sub error {
330 0     0 0   my $self = shift;
331 0           my $reason = shift;
332              
333 0           print "Content-type: text/html\n\n";
334              
335 0           print "
\n\n"; 
336 0           print $reason,"\n";
337 0           print "\n\n";
338              
339 0           foreach (keys %ENV) { print $_," = ",$ENV{$_},"
\n" };
  0            
340 0           exit 0;
341             }
342              
343             sub notfound {
344 0     0 0   my $self = shift;
345              
346 0           return $self->template('notfound');
347             }
348              
349             sub checkwrite {
350 0     0 0   my $self = shift;
351              
352 0           my $file = $self->{pt};
353 0           my $dir = $self->{pt};
354 0           $dir =~ s!/[^/]*$!!;
355              
356 0 0         return "this should be a POST event" unless $self->{rm} eq "POST";
357              
358 0 0         return "user $self->{ru} not authorized

"

359             if $self->{ru} eq "unknown";
360 0 0         return "directory $dir not writeable

"

361             unless -w $dir;
362 0 0 0       return "file $self->{pt} not writeable

"

363             if -r $self->{pt} && ! -w $self->{pt};
364 0 0         return "file $self->{pt} contains slashdot"
365             if $self->{pt} =~ m!/[.]!;
366 0 0         return "file $self->{pt} contains funnychars"
367             unless $self->{pt} =~ m!^[a-zA-Z0-9_./-]+$!;
368              
369 0           return;
370             }
371              
372             sub edit {
373 0     0 0   my $self = shift;
374              
375 0           $_ = $self->checkwrite();
376 0 0         return $_ if $_;
377              
378 0           $_ = $self->readfile($self->{pt});
379 0           s/$ESCAPE1/$ESCAPE2->{$1}/geo;
  0            
380 0           $self->{TEXT}=$_;
381              
382 0           return $self->template('edit');
383             }
384              
385             sub save {
386 0     0 0   my $self = shift;
387              
388 0           $_ = $self->checkwrite();
389 0 0         return $_ if $_;
390              
391 0 0         if ($self->{VAL}->{text}) {
392 0           $_ = $self->{VAL}->{text};
393 0           s/\r//g;
394              
395 0 0         if (-f $self->{pt}) {
396 0 0         rename($self->{pt}, $self->{pt}.'~') unless -f $self->{pt}.'~';
397             } else {
398 0           open OUT, ">$self->{pt}~"; print OUT "\n"; close OUT;
  0            
  0            
399             }
400 0           open OUT, ">$self->{pt}"; print OUT "$_\n"; close OUT;
  0            
  0            
401             } else {
402 0           $self->error("no text");
403             }
404              
405 0           return $self->display();
406             }
407              
408             sub search {
409 0     0 0   my $self = shift;
410 0           my $want = $self->{qs};
411 0           $want =~ s/^search=//;
412 0 0         $want = "pWiki" if $want eq "";
413 0           my $html = "

Search Results

\nmatching: $want

\n";

414 0           my $rslt = `find . -type f ! -name '*~' -print | fgrep -v /CVS/ | xargs egrep -iE '$want' 2>/dev/null`;
415 0           my $hits;
416 0           my $matches=0;
417              
418 0           SEARCHLOOP: foreach (split( /\n/, $rslt)) {
419 0           my ($file,$str) = split /:/, $_, 2;
420 0           $file =~ s/^\.//;
421 0           $str =~ s/<[^>]+>//g;
422 0 0         next SEARCHLOOP if $str =~ /^[ \t\r\n]*$/;
423 0           my $qm = quotemeta $str;
424 0 0         $hits->{$file} .= "$str
\n" if $hits->{$file} !~ m!$qm!;
425             }
426              
427 0           $html .= "
    ";
428 0           foreach (sort keys %$hits) {
429 0           $matches++;
430 0           my $tag = $_;
431 0           $tag =~ s!^\/!!;
432 0           $tag =~ s![_/]! !g;
433 0           $tag =~ s![.].*$!!;
434              
435 0           $html .= "
  • $tag
    \n$hits->{$_}";
  • 436             }
    437 0           $html .= "";
    438              
    439 0 0         $html .= "

    ... $matches matches search complete." if ($matches);

    440 0 0         $html .= "

    ... there are no matches." if (! $matches);

    441              
    442 0           return $html;
    443             }
    444              
    445             sub diff {
    446 0     0 0   my $self = shift;
    447              
    448 0           my $html = "\n

    pWiki Diff

    \n
      \n";
    449 0           my $rslt = `find . -type f ! -name '*~' -print | fgrep -v /CVS/`;
    450              
    451 0           DIFFLOOP: foreach (split( /\n/, $rslt)) {
    452 0           my $file = $_; $file =~ s!^[.]/!!;
      0            
    453 0           my $path = $_; $path =~ s!^[.]!!;
      0            
    454 0           my $old = $file."~";
    455 0 0         next DIFFLOOP unless -r $old;
    456              
    457 0           my $diff = `diff -p $old $file`;
    458 0           $diff =~ s/$ESCAPE1/$ESCAPE2->{$1}/geo;
      0            
    459              
    460 0           $html .= "
  • $file
    \n
    \n$diff\n
    ";
  • 461             }
    462 0           $html .= "";
    463              
    464 0           return $html;
    465             }
    466              
    467             #------------------------------------------------------------------------------#
    468              
    469             sub parse_request {
    470 0     0 0   my $self = shift;
    471              
    472 0   0       $self->{dr} = $ENV{DOCUMENT_ROOT} || $self->error('DOCUMENT_ROOT not defined');
    473 0   0       $self->{hh} = $ENV{HTTP_HOST} || $self->error('HTTP_HOST not defined');
    474 0   0       $self->{rm} = $ENV{REQUEST_METHOD} || $self->error('REQUEST_METHOD not defined');
    475 0   0       $self->{sn} = $ENV{SCRIPT_NAME} || $self->error('SCRIPT_NAME not defined');
    476 0   0       $self->{ur} = $ENV{REQUEST_URI} || $self->error('REQUEST_URI not defined');
    477 0   0       $self->{ru} = $ENV{REMOTE_USER} || "unknown";
    478 0           $self->{ua} = ($ENV{HTTP_USER_AGENT} =~ /(links|lynx)/i);
    479              
    480 0 0         if ($ENV{PATH_INFO}) {
    481 0           $self->{pi} = $ENV{PATH_INFO};
    482             } else {
    483 0           $self->{pi} = $self->{ur};
    484 0           $self->{pi} =~ s/\?.*//;
    485             }
    486              
    487 0 0         if ($ENV{QUERY_STRING}) {
    488 0           $self->{qs} = $ENV{QUERY_STRING};
    489             } else {
    490 0           $self->{qs} = $self->{ur};
    491 0           $self->{qs} =~ s/^[^?]*\?//;
    492             }
    493              
    494 0 0         if ($ENV{PATH_TRANSLATED}) {
    495 0           $self->{pt} = $ENV{PATH_TRANSLATED};
    496             } else {
    497 0           $self->{pt} = $self->{dr}.$self->{ur};
    498 0           $self->{pt} =~ s/\?.*//;
    499             }
    500              
    501 0 0         if ($self->{rm} eq "POST") {
    502 0           alarm(60);
    503 0           my $contlen = 0+$ENV{CONTENT_LENGTH};
    504 0 0         $contlen = 0 if ($contlen < 1);
    505 0           my $query;
    506 0           my $readlen = read(STDIN, $query, $contlen);
    507 0           alarm(0);
    508              
    509 0 0         $self->error("POST failed") if $readlen != $contlen;
    510 0           $self->{QUERY_BODY} = $query;
    511              
    512 0           $query =~ tr/+/ /; # RFC1630
    513 0           my @parts = split(/&/, $query);
    514              
    515 0           $self->{VAL}={};
    516 0           foreach (@parts) {
    517 0           my ($key, $val) = split(/=/,$_,2);
    518 0 0         $val = (defined $val) ? uri_unescape($val) : '';
    519 0           $key = uri_unescape($key);
    520 0           $self->{VAL}->{$key} = $val;
    521             }
    522              
    523 0 0         if ($self->{VAL}->{path}) {
    524 0           $self->{pi} = $self->{VAL}->{path};
    525 0           $self->{pt} = $self->{dr}.$self->{VAL}->{path};
    526             }
    527 0 0         $self->{qs} = $self->{VAL}->{query} if $self->{VAL}->{query};
    528             }
    529              
    530 0 0         $self->error("no path info") unless $self->{pi};
    531 0 0         $self->error("no query string") unless $self->{qs};
    532 0 0         $self->error("no path translated") unless $self->{pt};
    533 0 0         $self->error("can not chdir to doc root") unless chdir $self->{dr};
    534 0           umask 000;
    535             }
    536              
    537             sub translate {
    538 0     0 0   my $self = shift;
    539 0           my $html;
    540              
    541 0           $self->{URL} = "http://$self->{hh}$self->{pi}";
    542 0           $self->{SCR} = "http://$self->{hh}$self->{sn}";
    543 0           $self->{PATH} = $self->{pi};
    544 0           $self->{DIR} = $self->{pi};
    545 0           $self->{DIR} =~ s!/[^/]*$!!;
    546 0           $self->{DIR} =~ s!^/!!;
    547 0           $self->{TOPIC} = $self->{pi};
    548 0           $self->{TOPIC} =~ s!^.*/!!;
    549 0           $self->{TOPIC} =~ s![.].*$!!;
    550 0           $self->{TOPIC} =~ s!_! !g;
    551 0           $self->{TITLE} = $self->{TOPIC};
    552            
    553 0 0         QUERYCASE: {
    554 0           $html = $self->error(), last QUERYCASE if $self->{error};
    555 0 0         $html = $self->error(), last QUERYCASE if $self->{qs} =~ /^error/;
    556 0 0         $html = $self->search(), last QUERYCASE if $self->{qs} =~ /^search=/;
    557 0 0         $html = $self->diff(), last QUERYCASE if $self->{qs} eq "diff";
    558 0 0         $html = $self->edit(), last QUERYCASE if $self->{qs} eq "edit";
    559 0 0         $html = $self->save(), last QUERYCASE if $self->{qs} eq "save";
    560 0           $html = $self->display();
    561             }
    562              
    563 0           $self->{HTML} = $html;
    564 0           $self->{INDEX} = $self->template("content");
    565              
    566 0   0       return $self->template("style") || $self->{HTML};
    567             }
    568              
    569             sub display {
    570 0     0 0   my $self = shift;
    571              
    572 0 0         return $self->notfound() unless -r $self->{pt};
    573 0 0         return $self->html() if $self->{pt} =~ /\.html$/;
    574 0 0         return $self->html() if $self->{pt} =~ /\.htm$/;
    575 0 0         return $self->wiki() if $self->{pt} =~ /\.wiki$/;
    576 0 0         return $self->wiki() if $self->{pt} =~ /\.pod$/;
    577 0 0         return $self->xml() if $self->{pt} =~ /\.xml$/;
    578 0           return $self->text();
    579             }
    580              
    581             sub readfile {
    582 0     0 0   my $self = shift;
    583 0           my $file = shift;
    584              
    585 0 0         if (-r $file) {
    586 0           my $oirs = $/;
    587 0           undef $/;
    588 0           open IN, $file;
    589 0           my $html = ;
    590 0           close IN;
    591 0           $/ = $oirs;
    592 0           return $html;
    593             }
    594 0           return;
    595             }
    596              
    597             sub template {
    598 0     0 0   my $self = shift;
    599 0           my $temp = shift;
    600 0 0         my $file = $self->{ua} ? "$temp.lnx" : "$temp.moz";
    601 0           my $html = "";
    602              
    603 0 0         TEMPLCASE: {
    604 0           $html = $self->readfile("$self->{DIR}/$file"), last TEMPLCASE
    605             if -r "$self->{DIR}/$file";
    606 0 0         $html = $self->readfile("$self->{dr}/$file"), last TEMPLCASE
    607             if -r $file;
    608 0 0         $html = $self->readfile("pWiki/$file"), last TEMPLCASE
    609             if -r "pWiki/$file";
    610 0   0       $html = $TEMPLATE->{$temp} || "";
    611             }
    612 0           $html =~ s!%([A-Z]+)%!$self->{$1}!geo;
      0            
    613              
    614 0           return $html;
    615             }
    616              
    617             sub autolink {
    618 0     0 0   my ($self,$link) = @_;
    619              
    620 0 0         return $link if $link =~ /:$/; # oups ...
    621              
    622 0           $link =~ tr/[]//d;
    623 0           my $url = $link;
    624 0           my $tag = $link;
    625              
    626 0 0         if ($link =~ /(.*)[|](.*)/) {
    627 0           $url = $2;
    628 0           $tag = $1;
    629 0           $tag =~ s!_! !g;
    630 0           $url =~ s!::!-!g;
    631 0 0         $url .= ".pod" if $self->{pt} =~ /\.pod/;
    632             } else {
    633 0           $url =~ s!/".*!!g;
    634 0 0         $url =~ s!/!_!g if $self->{pt} =~ /\.wiki/;
    635 0 0         $url =~ s!/.*$!!g if $self->{pt} =~ /\.pod/;
    636 0           $url =~ s!:+!-!g;
    637 0 0         $url = "$self->{DIR}/$url" if $self->{DIR};
    638 0 0         $url = "/$url" if $url !~ m!^/!;
    639 0           $tag =~ s!_! !g;
    640              
    641 0 0         EXTCASE: {
    642 0           $url .= ".wiki", last EXTCASE if -r $self->{dr}.$url.".wiki";
    643 0 0         $url .= ".text", last EXTCASE if -r $self->{dr}.$url.".text";
    644 0 0         $url .= ".html", last EXTCASE if -r $self->{dr}.$url.".html";
    645 0 0         $url .= ".htm", last EXTCASE if -r $self->{dr}.$url.".htm";
    646 0 0         $url .= ".pod", last EXTCASE if -r $self->{dr}.$url.".pod";
    647 0 0         $url .= ".xml", last EXTCASE if -r $self->{dr}.$url.".xml";
    648              
    649 0           $_ = $self->{pt};
    650 0           m/\.([^.]+)$/;
    651 0           $url .= ".$1";
    652 0           $tag = "?".$tag."?";
    653             }
    654             }
    655              
    656 0           return "$tag";
    657             }
    658              
    659             sub expand {
    660 0     0 0   my $self = shift; my $cmd = shift; $_ = shift;
      0            
      0            
    661              
    662 0           s!([IBSCLFXE])<+(.*)!$self->expand($1,$2)!geo;
      0            
    663              
    664 0 0         return "$_" if $cmd eq "I";
    665 0 0         return "$_" if $cmd eq "B";
    666 0 0         return "$_" if $cmd =~ /[CFX]/;
    667 0 0         return $self->autolink($_) if $cmd eq "L";
    668 0 0 0       return "&".$_.";" if ($cmd eq "E") && /^[^0-9]/;
    669 0 0 0       return "\\0".$_ if ($cmd eq "E") && /^[0-9]/;
    670              
    671 0 0         s/ / /g if $cmd eq "S";
    672              
    673 0           return "$_";
    674             }
    675              
    676             sub wikify {
    677 0     0 0   my $self = shift; $_ = shift;
      0            
    678              
    679 0           s!([IBSCLFXE])<+([^>]+)>+!$self->expand($1,$2)!geo;
      0            
    680 0           s!([\n\t ])(\[[0-9A-Za-z_/:-]+\]|[A-Za-z0-9]+[A-Z_/:-][0-9A-Za-z_/:-]*)!$1.$self->autolink($2)!geo;
      0            
    681              
    682 0           return $_;
    683             }
    684              
    685             #------------------------------------------------------------------------------#
    686              
    687             sub format_table {
    688 0     0 0   my $self = shift; $_ = $self->wikify(shift);
      0            
    689              
    690 0           s!^[ \t]*[|]!\n
    !g;
    691 0           s!\n[ \t]*[|]!\n
    !g;
    692 0           s![|][ \t]*$!
    693 0           s![|][ \t]*\n!
    694 0           s![|]!!g;
    695              
    696 0           return "\n$_\n
    \n";
    697             }
    698              
    699             sub format_list {
    700 0     0 0   my $self = shift; $_ = $self->wikify(shift);
      0            
    701              
    702 0           s!\n[ \t]*[*-] !\n
  • !g;
  • 703              
    704 0           return "\n
      $_\n
    \n";
    705             }
    706              
    707             sub format_ordinary {
    708 0     0 0   my $self = shift; $_ = $self->wikify(shift);
      0            
    709              
    710 0           s!\n[ \t]+!\n
    !g;
    711              
    712 0           return "\n$_\n

    \n";

    713             }
    714              
    715             sub format_verbatim {
    716 0     0 0   my $self = shift; $_ = shift;
      0            
    717              
    718 0           s/$ESCAPE1/$ESCAPE2->{$1}/geo;
      0            
    719              
    720 0           return "\n
    $_\n
    \n";
    721             }
    722              
    723             sub format_command {
    724 0     0 0   my $self = shift; $_ = shift;
      0            
    725 0           my $html = "";
    726              
    727 0 0         if (/\n=location (.+)/i) {
    728 0           print "Location: $1\n\n";
    729 0           exit 0;
    730             }
    731 0           s!([IBSCLFXE])<([^>]+)>!$self->expand($1,$2)!geo;
      0            
    732              
    733 0 0         $self->{TITLE} = $1 if /\n=title ([^\n]+)/i;
    734 0 0         $html .= "

    $1

    " if /\n=head1 ([^\n]+)/i;
    735 0 0         $html .= "

    $1

    " if /\n=head2 ([^\n]+)/i;
    736 0 0         $html .= "

    $1

    " if /\n=head3 ([^\n]+)/i;
    737 0 0         $html .= "
    " if /\n=over.*/i;
    738 0 0         $html .= "
    $1
    " if /\n=item (.*)/i;
    739 0 0         $html .= "" if /\n=back.*/i;
    740              
    741 0           return $html;
    742             }
    743              
    744             #------------------------------------------------------------------------------#
    745              
    746             1;