X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/384327bfd0b3b0362a61f30f81b06895238ee360..282893be819eab14f3a2078f1741ec9c9ed363b1:/ikiwiki

diff --git a/ikiwiki b/ikiwiki
index 79b7eff90..be7f86a45 100755
--- a/ikiwiki
+++ b/ikiwiki
@@ -1,452 +1,109 @@
 #!/usr/bin/perl -T
-
 $ENV{PATH}="/usr/local/bin:/usr/bin:/bin";
 
-use lib '.'; # For use without installation, removed by Makefile.
-
 package IkiWiki;
 use warnings;
 use strict;
-use File::Spec;
-use HTML::Template;
-
-use vars qw{%config %links %oldlinks %oldpagemtime %renderedfiles %pagesources};
-
-# Holds global config settings, also used by some modules.
-our %config=( #{{{
-	wiki_file_prune_regexp => qr{((^|/).svn/|\.\.|^\.|\/\.|\.html?$)},
-	wiki_link_regexp => qr/\[\[([^\s\]]+)\]\]/,
-	wiki_file_regexp => qr/(^[-A-Za-z0-9_.:\/+]+$)/,
-	verbose => 0,
-	wikiname => "wiki",
-	default_pageext => ".mdwn",
-	cgi => 0,
-	svn => 1,
-	url => '',
-	cgiurl => '',
-	historyurl => '',
-	diffurl => '',
-	anonok => 0,
-	rebuild => 0,
-	wrapper => undef,
-	wrappermode => undef,
-	srcdir => undef,
-	destdir => undef,
-	templatedir => "/usr/share/ikiwiki/templates",
-	setup => undef,
-	adminuser => undef,
-); #}}}
-
-# option parsing #{{{
-if (! exists $ENV{WRAPPED_OPTIONS}) {
-	eval q{use Getopt::Long};
-	GetOptions(
-		"setup|s=s" => \$config{setup},
-		"wikiname=s" => \$config{wikiname},
-		"verbose|v!" => \$config{verbose},
-		"rebuild!" => \$config{rebuild},
-		"wrapper:s" => sub { $config{wrapper}=$_[1] ? $_[1] : "ikiwiki-wrap" },
-		"wrappermode=i" => \$config{wrappermode},
-		"svn!" => \$config{svn},
-		"anonok!" => \$config{anonok},
-		"cgi!" => \$config{cgi},
-		"url=s" => \$config{url},
-		"cgiurl=s" => \$config{cgiurl},
-		"historyurl=s" => \$config{historyurl},
-		"diffurl=s" => \$config{diffurl},
-		"exclude=s@" => sub {
-			$config{wiki_file_prune_regexp}=qr/$config{wiki_file_prune_regexp}|$_[1]/;
-		},
-		"adminuser=s@" => sub { push @{$config{adminuser}}, $_[1] },
-		"templatedir=s" => sub { $config{templatedir}=possibly_foolish_untaint($_[1]) },
-	) || usage();
-
-	if (! $config{setup}) {
-		usage() unless @ARGV == 2;
-		$config{srcdir} = possibly_foolish_untaint(shift);
-		$config{destdir} = possibly_foolish_untaint(shift);
-		checkoptions();
-	}
-}
-else {
-	# wrapper passes a full config structure in the environment
-	# variable
-	eval possibly_foolish_untaint($ENV{WRAPPED_OPTIONS});
-	checkoptions();
-}
-#}}}
-
-sub checkoptions { #{{{
-	if ($config{cgi} && ! length $config{url}) {
-		error("Must specify url to wiki with --url when using --cgi");
-	}
-	
-	$config{wikistatedir}="$config{srcdir}/.ikiwiki"
-		unless exists $config{wikistatedir};
-	
-	if ($config{svn}) {
-		require IkiWiki::RCS::SVN;
-		$config{rcs}=1;
-	}
-	else {
-		require IkiWiki::RCS::Stub;
-		$config{rcs}=0;
-	}
-} #}}}
+use lib '.'; # For use without installation, removed by Makefile.
+use IkiWiki;
 
-sub usage { #{{{
+sub usage () { #{{{
 	die "usage: ikiwiki [options] source dest\n";
 } #}}}
 
-sub error { #{{{
-	if ($config{cgi}) {
-		print "Content-type: text/html\n\n";
-		print misctemplate("Error", "<p>Error: @_</p>");
-	}
-	die @_;
-} #}}}
-
-sub debug ($) { #{{{
-	return unless $config{verbose};
-	if (! $config{cgi}) {
-		print "@_\n";
-	}
-	else {
-		print STDERR "@_\n";
-	}
-} #}}}
-
-sub possibly_foolish_untaint { #{{{
-	my $tainted=shift;
-	my ($untainted)=$tainted=~/(.*)/;
-	return $untainted;
-} #}}}
-
-sub basename ($) { #{{{
-	my $file=shift;
-
-	$file=~s!.*/!!;
-	return $file;
-} #}}}
-
-sub dirname ($) { #{{{
-	my $file=shift;
-
-	$file=~s!/?[^/]+$!!;
-	return $file;
-} #}}}
-
-sub pagetype ($) { #{{{
-	my $page=shift;
-	
-	if ($page =~ /\.mdwn$/) {
-		return ".mdwn";
-	}
-	else {
-		return "unknown";
-	}
-} #}}}
-
-sub pagename ($) { #{{{
-	my $file=shift;
-
-	my $type=pagetype($file);
-	my $page=$file;
-	$page=~s/\Q$type\E*$// unless $type eq 'unknown';
-	return $page;
-} #}}}
-
-sub htmlpage ($) { #{{{
-	my $page=shift;
-
-	return $page.".html";
-} #}}}
-
-sub readfile ($) { #{{{
-	my $file=shift;
-
-	if (-l $file) {
-		error("cannot read a symlink ($file)");
-	}
-	
-	local $/=undef;
-	open (IN, "$file") || error("failed to read $file: $!");
-	my $ret=<IN>;
-	close IN;
-	return $ret;
-} #}}}
-
-sub writefile ($$) { #{{{
-	my $file=shift;
-	my $content=shift;
-	
-	if (-l $file) {
-		error("cannot write to a symlink ($file)");
-	}
-
-	my $dir=dirname($file);
-	if (! -d $dir) {
-		my $d="";
-		foreach my $s (split(m!/+!, $dir)) {
-			$d.="$s/";
-			if (! -d $d) {
-				mkdir($d) || error("failed to create directory $d: $!");
-			}
+sub getconfig () { #{{{
+	if (! exists $ENV{WRAPPED_OPTIONS}) {
+		%config=defaultconfig();
+		eval q{use Getopt::Long};
+		GetOptions(
+			"setup|s=s" => \$config{setup},
+			"wikiname=s" => \$config{wikiname},
+			"verbose|v!" => \$config{verbose},
+			"rebuild!" => \$config{rebuild},
+			"refresh!" => \$config{refresh},
+			"getctime" => \$config{getctime},
+			"wrappermode=i" => \$config{wrappermode},
+			"rcs=s" => \$config{rcs},
+			"no-rcs" => sub { $config{rcs}="" },
+			"anonok!" => \$config{anonok},
+			"rss!" => \$config{rss},
+			"cgi!" => \$config{cgi},
+			"discussion!" => \$config{discussion},
+			"notify!" => \$config{notify},
+			"url=s" => \$config{url},
+			"cgiurl=s" => \$config{cgiurl},
+			"historyurl=s" => \$config{historyurl},
+			"diffurl=s" => \$config{diffurl},
+			"svnrepo" => \$config{svnrepo},
+			"svnpath" => \$config{svnpath},
+			"adminemail=s" => \$config{adminemail},
+			"exclude=s@" => sub {
+				$config{wiki_file_prune_regexp}=qr/$config{wiki_file_prune_regexp}|$_[1]/;
+			},
+			"adminuser=s@" => sub {
+				push @{$config{adminuser}}, $_[1]
+			},
+			"templatedir=s" => sub {
+				$config{templatedir}=possibly_foolish_untaint($_[1])
+			},
+			"underlaydir=s" => sub {
+				$config{underlaydir}=possibly_foolish_untaint($_[1])
+			},
+			"wrapper:s" => sub {
+				$config{wrapper}=$_[1] ? $_[1] : "ikiwiki-wrap"
+			},
+			"plugin=s@" => sub {
+				push @{$config{plugin}}, $_[1];
+			},
+			"disable-plugin=s@" => sub {
+				$config{plugin}=[grep { $_ ne $_[1] } @{$config{plugin}} ];
+			},
+		) || usage();
+
+		if (! $config{setup}) {
+			usage() unless @ARGV == 2;
+			$config{srcdir} = possibly_foolish_untaint(shift @ARGV);
+			$config{destdir} = possibly_foolish_untaint(shift @ARGV);
+			checkconfig();
 		}
 	}
-	
-	open (OUT, ">$file") || error("failed to write $file: $!");
-	print OUT $content;
-	close OUT;
-} #}}}
-
-sub bestlink ($$) { #{{{
-	# Given a page and the text of a link on the page, determine which
-	# existing page that link best points to. Prefers pages under a
-	# subdirectory with the same name as the source page, failing that
-	# goes down the directory tree to the base looking for matching
-	# pages.
-	my $page=shift;
-	my $link=lc(shift);
-	
-	my $cwd=$page;
-	do {
-		my $l=$cwd;
-		$l.="/" if length $l;
-		$l.=$link;
-
-		if (exists $links{$l}) {
-			#debug("for $page, \"$link\", use $l");
-			return $l;
-		}
-	} while $cwd=~s!/?[^/]+$!!;
-
-	#print STDERR "warning: page $page, broken link: $link\n";
-	return "";
-} #}}}
-
-sub isinlinableimage ($) { #{{{
-	my $file=shift;
-	
-	$file=~/\.(png|gif|jpg|jpeg)$/;
-} #}}}
-
-sub htmllink { #{{{
-	my $page=shift;
-	my $link=shift;
-	my $noimageinline=shift; # don't turn links into inline html images
-	my $forcesubpage=shift; # force a link to a subpage
-
-	my $bestlink;
-	if (! $forcesubpage) {
-		$bestlink=bestlink($page, $link);
-	}
 	else {
-		$bestlink="$page/".lc($link);
-	}
-
-	return $link if length $bestlink && $page eq $bestlink;
-	
-	# TODO BUG: %renderedfiles may not have it, if the linked to page
-	# was also added and isn't yet rendered! Note that this bug is
-	# masked by the bug mentioned below that makes all new files
-	# be rendered twice.
-	if (! grep { $_ eq $bestlink } values %renderedfiles) {
-		$bestlink=htmlpage($bestlink);
-	}
-	if (! grep { $_ eq $bestlink } values %renderedfiles) {
-		return "<a href=\"$config{cgiurl}?do=create&page=$link&from=$page\">?</a>$link"
-	}
-	
-	$bestlink=File::Spec->abs2rel($bestlink, dirname($page));
-	
-	if (! $noimageinline && isinlinableimage($bestlink)) {
-		return "<img src=\"$bestlink\">";
-	}
-	return "<a href=\"$bestlink\">$link</a>";
-} #}}}
-
-sub indexlink () { #{{{
-	return "<a href=\"$config{url}\">$config{wikiname}</a>";
-} #}}}
-
-sub lockwiki () { #{{{
-	# 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}) {
-		mkdir($config{wikistatedir});
-	}
-	open(WIKILOCK, ">$config{wikistatedir}/lockfile") ||
-		error ("cannot write to $config{wikistatedir}/lockfile: $!");
-	if (! flock(WIKILOCK, 2 | 4)) {
-		debug("wiki seems to be locked, waiting for lock");
-		my $wait=600; # arbitrary, but don't hang forever to 
-		              # prevent process pileup
-		for (1..600) {
-			return if flock(WIKILOCK, 2 | 4);
-			sleep 1;
+		# wrapper passes a full config structure in the environment
+		# variable
+		eval possibly_foolish_untaint($ENV{WRAPPED_OPTIONS});
+		if ($@) {
+			error("WRAPPED_OPTIONS: $@");
 		}
-		error("wiki is locked; waited $wait seconds without lock being freed (possible stuck process or stale lock?)");
-	}
-} #}}}
-
-sub unlockwiki () { #{{{
-	close WIKILOCK;
-} #}}}
-
-sub loadindex () { #{{{
-	open (IN, "$config{wikistatedir}/index") || return;
-	while (<IN>) {
-		$_=possibly_foolish_untaint($_);
-		chomp;
-		my ($mtime, $file, $rendered, @links)=split(' ', $_);
-		my $page=pagename($file);
-		$pagesources{$page}=$file;
-		$oldpagemtime{$page}=$mtime;
-		$oldlinks{$page}=[@links];
-		$links{$page}=[@links];
-		$renderedfiles{$page}=$rendered;
-	}
-	close IN;
-} #}}}
-
-sub saveindex () { #{{{
-	if (! -d $config{wikistatedir}) {
-		mkdir($config{wikistatedir});
-	}
-	open (OUT, ">$config{wikistatedir}/index") || 
-		error("cannot write to $config{wikistatedir}/index: $!");
-	foreach my $page (keys %oldpagemtime) {
-		print OUT "$oldpagemtime{$page} $pagesources{$page} $renderedfiles{$page} ".
-	        	join(" ", @{$links{$page}})."\n"
-				if $oldpagemtime{$page};
-	}
-	close OUT;
-} #}}}
-
-sub misctemplate ($$) { #{{{
-	my $title=shift;
-	my $pagebody=shift;
-	
-	my $template=HTML::Template->new(
-		filename => "$config{templatedir}/misc.tmpl"
-	);
-	$template->param(
-		title => $title,
-		indexlink => indexlink(),
-		wikiname => $config{wikiname},
-		pagebody => $pagebody,
-	);
-	return $template->output;
-}#}}}
-
-sub userinfo_get ($$) { #{{{
-	my $user=shift;
-	my $field=shift;
-
-	eval q{use Storable};
-	my $userdata=eval{ Storable::lock_retrieve("$config{wikistatedir}/userdb") };
-	if (! defined $userdata || ! ref $userdata || 
-	    ! exists $userdata->{$user} || ! ref $userdata->{$user} ||
-            ! exists $userdata->{$user}->{$field}) {
-		return "";
-	}
-	return $userdata->{$user}->{$field};
-} #}}}
-
-sub userinfo_set ($$$) { #{{{
-	my $user=shift;
-	my $field=shift;
-	my $value=shift;
-	
-	eval q{use Storable};
-	my $userdata=eval{ Storable::lock_retrieve("$config{wikistatedir}/userdb") };
-	if (! defined $userdata || ! ref $userdata || 
-	    ! exists $userdata->{$user} || ! ref $userdata->{$user}) {
-		return "";
-	}
-	
-	$userdata->{$user}->{$field}=$value;
-	my $oldmask=umask(077);
-	my $ret=Storable::lock_store($userdata, "$config{wikistatedir}/userdb");
-	umask($oldmask);
-	return $ret;
-} #}}}
-
-sub userinfo_setall ($$) { #{{{
-	my $user=shift;
-	my $info=shift;
-	
-	eval q{use Storable};
-	my $userdata=eval{ Storable::lock_retrieve("$config{wikistatedir}/userdb") };
-	if (! defined $userdata || ! ref $userdata) {
-		$userdata={};
+		checkconfig();
 	}
-	$userdata->{$user}=$info;
-	my $oldmask=umask(077);
-	my $ret=Storable::lock_store($userdata, "$config{wikistatedir}/userdb");
-	umask($oldmask);
-	return $ret;
 } #}}}
 
-sub is_admin ($) { #{{{
-	my $user_name=shift;
-
-	return grep { $_ eq $user_name } @{$config{adminuser}};
-} #}}}
-
-sub glob_match ($$) { #{{{
-	my $page=shift;
-	my $glob=shift;
-
-	# turn glob into safe regexp
-	$glob=quotemeta($glob);
-	$glob=~s/\\\*/.*/g;
-	$glob=~s/\\\?/./g;
-	$glob=~s!\\/!/!g;
+sub main () { #{{{
+	getconfig();
 	
-	$page=~/^$glob$/i;
-} #}}}
-
-sub globlist_match ($$) { #{{{
-	my $page=shift;
-	my @globlist=split(" ", shift);
-
-	# check any negated globs first
-	foreach my $glob (@globlist) {
-		return 0 if $glob=~/^!(.*)/ && glob_match($page, $1);
-	}
-
-	foreach my $glob (@globlist) {
-		return 1 if glob_match($page, $glob);
+	if ($config{cgi}) {
+		lockwiki();
+		loadindex();
+		require IkiWiki::CGI;
+		cgi();
 	}
-	
-	return 0;
-} #}}}
-
-sub main () { #{{{
-	if ($config{setup}) {
+	elsif ($config{setup}) {
 		require IkiWiki::Setup;
 		setup();
 	}
-	
-	lockwiki();
-	
-	if ($config{wrapper}) {
+	elsif ($config{wrapper}) {
+		lockwiki();
 		require IkiWiki::Wrapper;
 		gen_wrapper();
-		exit;
-	}
-	
-	if ($config{cgi}) {
-		require IkiWiki::CGI;
-		cgi();
 	}
 	else {
-		loadindex() unless $config{rebuild};
+		lockwiki();
+		loadindex();
 		require IkiWiki::Render;
 		rcs_update();
+		rcs_getctime() if $config{getctime};
 		refresh();
+		rcs_notify() if $config{notify};
 		saveindex();
 	}
 } #}}}