X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/b8d7ae91d0199003173ea3c581404d66538a3fba..09b9797031f3b8962311ac3b9d46beebc50c0afa:/IkiWiki.pm?ds=sidebyside diff --git a/IkiWiki.pm b/IkiWiki.pm index aea5b7abf..0af4a4fe9 100644 --- a/IkiWiki.pm +++ b/IkiWiki.pm @@ -33,13 +33,28 @@ sub defaultconfig () { #{{{ qr/\.x?html?$/, qr/\.ikiwiki-new$/, qr/(^|\/).svn\//, qr/.arch-ids\//, qr/{arch}\//, qr/\.dpkg-tmp$/], - wiki_link_regexp => qr/\[\[(?:([^\]\|]+)\|)?([^\s\]#]+)(?:#([^\s\]]+))?\]\]/, + wiki_link_regexp => qr{ + \[\[ # beginning of link + (?: + ([^\]\|]+) # 1: link text + \| # followed by '|' + )? # optional + + ([^\s\]#]+) # 2: page to link to + (?: + \# # '#', beginning of anchor + ([^\s\]]+) # 3: anchor text + )? # optional + + \]\] # end of link + }x, wiki_file_regexp => qr/(^[-[:alnum:]_.:\/+]+$)/, web_commit_regexp => qr/^web commit (by (.*?(?=: |$))|from (\d+\.\d+\.\d+\.\d+)):?(.*)/, verbose => 0, syslog => 0, wikiname => "wiki", default_pageext => "mdwn", + htmlext => "html", cgi => 0, post_commit => 0, rcs => '', @@ -71,6 +86,7 @@ sub defaultconfig () { #{{{ adminemail => undef, plugin => [qw{mdwn inline htmlscrubber passwordauth openid signinedit lockedit conditional}], + libdir => undef, timeformat => '%c', locale => undef, sslcookie => 0, @@ -112,7 +128,7 @@ sub checkconfig () { #{{{ unless exists $config{wikistatedir}; if ($config{rcs}) { - eval qq{require IkiWiki::Rcs::$config{rcs}}; + eval qq{use IkiWiki::Rcs::$config{rcs}}; if ($@) { error("Failed to load RCS module IkiWiki::Rcs::$config{rcs}: $@"); } @@ -125,8 +141,12 @@ sub checkconfig () { #{{{ } #}}} sub loadplugins () { #{{{ + if (defined $config{libdir}) { + unshift @INC, $config{libdir}; + } + loadplugin($_) foreach @{$config{plugin}}; - + run_hooks(getopt => sub { shift->() }); if (grep /^-/, @ARGV) { print STDERR "Unknown option: $_\n" @@ -192,7 +212,7 @@ sub log_message ($$) { #{{{ sub possibly_foolish_untaint ($) { #{{{ my $tainted=shift; - my ($untainted)=$tainted=~/(.*)/; + my ($untainted)=$tainted=~/(.*)/s; return $untainted; } #}}} @@ -242,7 +262,7 @@ sub targetpage ($$) { #{{{ sub htmlpage ($) { #{{{ my $page=shift; - return targetpage($page, "html"); + return targetpage($page, $config{htmlext}); } #}}} sub srcfile ($) { #{{{ @@ -452,7 +472,7 @@ sub displaytime ($) { #{{{ sub beautify_url ($) { #{{{ my $url=shift; - $url =~ s!/index.html$!/!; + $url =~ s!/index.$config{htmlext}$!/!; $url =~ s!^$!./!; # Browsers don't like empty links... return $url; @@ -526,7 +546,12 @@ sub htmllink ($$$;@) { #{{{ $bestlink.="#".$opts{anchor}; } - return "$linktext"; + my @attrs; + if (defined $opts{rel}) { + push @attrs, ' rel="'.$opts{rel}.'"'; + } + + return "$linktext"; } #}}} sub htmlize ($$$) { #{{{ @@ -592,14 +617,24 @@ sub preprocess ($$$;$$) { #{{{ my $command=shift; my $params=shift; if (length $escape) { - return "[[$command $params]]"; + return "\\[[$command $params]]"; } elsif (exists $hooks{preprocess}{$command}) { return "" if $scan && ! $hooks{preprocess}{$command}{scan}; # Note: preserve order of params, some plugins may # consider it significant. my @params; - while ($params =~ /(?:(\w+)=)?(?:"""(.*?)"""|"([^"]+)"|(\S+))(?:\s+|$)/sg) { + while ($params =~ m{ + (?:(\w+)=)? # 1: named parameter key? + (?: + """(.*?)""" # 2: triple-quoted value + | + "([^"]+)" # 3: single-quoted value + | + (\S+) # 4: unquoted value + ) + (?:\s+|$) # delimiter to next param + }sgx) { my $key=$1; my $val; if (defined $2) { @@ -643,20 +678,42 @@ sub preprocess ($$$;$$) { #{{{ return $ret; } else { - return "[[$command $params]]"; + return "\\[[$command $params]]"; } }; - $content =~ s{(\\?)\[\[(\w+)\s+((?:(?:\w+=)?(?:""".*?"""|"[^"]+"|[^\s\]]+)\s*)*)\]\]}{$handle->($1, $2, $3)}seg; + $content =~ s{ + (\\?) # 1: escape? + \[\[ # directive open + (\w+) # 2: command + \s+ + ( # 3: the parameters.. + (?: + (?:\w+=)? # named parameter key? + (?: + """.*?""" # triple-quoted value + | + "[^"]+" # single-quoted value + | + [^\s\]]+ # unquoted value + ) + \s* # whitespace or end + # of directive + ) + *) # 0 or more parameters + \]\] # directive closed + }{$handle->($1, $2, $3)}sexg; return $content; } #}}} -sub filter ($$) { #{{{ +sub filter ($$$) { #{{{ my $page=shift; + my $destpage=shift; my $content=shift; run_hooks(filter => sub { - $content=shift->(page => $page, content => $content); + $content=shift->(page => $page, destpage => $destpage, + content => $content); }); return $content; @@ -666,7 +723,8 @@ sub indexlink () { #{{{ return "$config{wikiname}"; } #}}} -sub lockwiki () { #{{{ +sub lockwiki (;$) { #{{{ + my $wait=@_ ? shift : 1; # Take an exclusive lock on the wiki to prevent multiple concurrent # run issues. The lock will be dropped on program exit. if (! -d $config{wikistatedir}) { @@ -675,15 +733,21 @@ sub lockwiki () { #{{{ open(WIKILOCK, ">$config{wikistatedir}/lockfile") || error ("cannot write to $config{wikistatedir}/lockfile: $!"); if (! flock(WIKILOCK, 2 | 4)) { # LOCK_EX | LOCK_NB - debug("wiki seems to be locked, waiting for lock"); - my $wait=600; # arbitrary, but don't hang forever to - # prevent process pileup - for (1..$wait) { - return if flock(WIKILOCK, 2 | 4); - sleep 1; + if ($wait) { + debug("wiki seems to be locked, waiting for lock"); + my $wait=600; # arbitrary, but don't hang forever to + # prevent process pileup + for (1..$wait) { + return if flock(WIKILOCK, 2 | 4); + sleep 1; + } + error("wiki is locked; waited $wait seconds without lock being freed (possible stuck process or stale lock?)"); + } + else { + return 0; } - error("wiki is locked; waited $wait seconds without lock being freed (possible stuck process or stale lock?)"); } + return 1; } #}}} sub unlockwiki () { #{{{ @@ -737,9 +801,9 @@ sub loadindex () { #{{{ $depends{$page}=$items{depends}[0] if exists $items{depends}; $destsources{$_}=$page foreach @{$items{dest}}; $renderedfiles{$page}=[@{$items{dest}}]; - $oldrenderedfiles{$page}=[@{$items{dest}}]; $pagecase{lc $page}=$page; } + $oldrenderedfiles{$page}=[@{$items{dest}}]; $pagectime{$page}=$items{ctime}[0]; } close IN; @@ -789,7 +853,6 @@ sub template_params (@) { #{{{ return ""; } - require HTML::Template; my @ret=( filter => sub { my $text_ref = shift; @@ -804,6 +867,7 @@ sub template_params (@) { #{{{ } #}}} sub template ($;@) { #{{{ + require HTML::Template; HTML::Template->new(template_params(@_)); } #}}} @@ -968,7 +1032,21 @@ sub pagespec_translate ($) { #{{{ # Convert spec to perl code. my $code=""; - while ($spec=~m/\s*(\!|\(|\)|\w+\([^\)]+\)|[^\s()]+)\s*/ig) { + while ($spec=~m{ + \s* # ignore whitespace + ( # 1: match a single word + \! # ! + | + \( # ( + | + \) # ) + | + \w+\([^\)]+\) # command(params) + | + [^\s()]+ # any other text + ) + \s* # ignore whitespace + }igx) { my $word=$1; if (lc $word eq "and") { $code.=" &&"; @@ -1047,8 +1125,8 @@ sub match_glob ($$;@) { #{{{ # relative matching if ($glob =~ m!^\./!) { - $from=~s!/?[^/]+$!!; - $glob=~s!^\./!!; + $from=~s#/?[^/]+$##; + $glob=~s#^\./##; $glob="$from/$glob" if length $from; } @@ -1074,18 +1152,23 @@ sub match_link ($$;@) { #{{{ # relative matching if ($link =~ m!^\.! && defined $from) { - $from=~s!/?[^/]+$!!; - $link=~s!^\./!!; + $from=~s#/?[^/]+$##; + $link=~s#^\./##; $link="$from/$link" if length $from; } my $links = $IkiWiki::links{$page} or return undef; return IkiWiki::FailReason->new("$page has no links") unless @$links; my $bestlink = IkiWiki::bestlink($from, $link); - return IkiWiki::FailReason->new("no such link") unless length $bestlink; foreach my $p (@$links) { - return IkiWiki::SuccessReason->new("$page links to $link") - if $bestlink eq IkiWiki::bestlink($page, $p); + if (length $bestlink) { + return IkiWiki::SuccessReason->new("$page links to $link") + if $bestlink eq IkiWiki::bestlink($page, $p); + } + else { + return IkiWiki::SuccessReason->new("$page links to page matching $link") + if match_glob($p, $link, %params); + } } return IkiWiki::FailReason->new("$page does not link to $link"); } #}}}