Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
#!/usr/bin/perl -w
use DB_File;
use Compress::Zlib;
use Digest::MD5 qw(md5 md5_base64);
die "usage: ppDump lang project" unless @ARGV==2;
($lang, $project) = @ARGV; @ARGV = ();
$datadir = "data";
mkdir $datadir unless -d $datadir;
$revsPerBlock=50;
$makeNEdits=1;
$saveText=1;
$delayIdx=0;
# lws = length of word sequences; One Chinese character is treated as one word.
$lws = ($lang =~ /^(zh|ja|ko)$/)?8:5;
sub xmlunesc {
my $text = shift;
$text =~ s/</</sg;
$text =~ s/>/>/sg;
$text =~ s/'/'/sg;
$text =~ s/"/"/sg;
$text =~ s/&/&/sg;
$text;
}
open MW, ">$datadir/$lang$project.mw";
$mw = <>;
print MW $mw;
close MW;
sub xmlsiteinfo {
while($si =~ /<namespace key="(.*?)"(?: \/>|>(.*?)<\/namespace>)/sg) {
$namespace{$2} = $1 if defined $2;
$image = $2 if $1==6;
$category = $2 if $1==14;
}
open SITEINFO, ">$datadir/$lang$project.si";
print SITEINFO $si;
close SITEINFO;
}
while(<>) {
$si .= $_;
if(/^\s*<\/siteinfo/) {
xmlsiteinfo();
last;
}
}
use Inline C => <<'END';
#include "unicodeAttributes.h"
void DJBHashes(unsigned char* sec, int revNr, int secNr, HV* firstOccurrence, HV* secNrsBySeq, int lws) {
int wStart[lws], wEnd[lws]; // ring buffers: start and end(excl) of words
int pos=0, wNr=0;
int b1, b2, b3, b4, c, inWord=0, inCJK=0;
unsigned char hashHex[9];
do {
int pos0 = pos;
b1=sec[pos++];
if (b1<128) {
c = b1;
} else if(b1<192) {
continue;
} else if(b1<224) {
b2 = sec[pos++];
c = ((b1&31)<<6) | (b2&63);
} else if(b1<240) {
b2 = sec[pos++];
b3 = sec[pos++];
c = ((b1&15)<<12) | ((b2&63)<<6) | (b3&63);
} else if(b1<248) {
b2 = sec[pos++];
b3 = sec[pos++];
b4 = sec[pos++];
c = ((b1&7)<<18) | ((b2&63)<<12) | ((b3&63)<<6) | (b4&63);
} else {
continue;
}
if(c>=17*65536)
continue;
unsigned char generalCategory = unicodeGC[c] & 0x7f;
unsigned char isCJK = unicodeGC[c] >= 0x80;
int isAlNum =
generalCategory == unicode_Lu ||
generalCategory == unicode_Ll ||
generalCategory == unicode_Lt ||
generalCategory == unicode_Lm ||
generalCategory == unicode_Lo ||
generalCategory == unicode_Nd ||
generalCategory == unicode_Nl ||
generalCategory == unicode_No;
int newWord = 0;
if(inCJK) {
wEnd[wNr++%lws]=pos0;
inCJK=0;
newWord=1;
} else if(inWord && (!isAlNum || isCJK)) {
wEnd[wNr++%lws]=pos0;
inWord=0;
newWord=1;
}
if(newWord && wNr>=lws) {
unsigned int hash=5381;
int j;
for(j=wNr-lws; j<wNr; j++) {
int k;
for(k=wStart[j%lws]; k<wEnd[j%lws]; k++)
hash = 33*hash+sec[k];
if(j<wNr-1)
hash = 33*hash+' ';
}
SV* hashSV = sv_2mortal(newSViv(hash));
if(!hv_exists_ent(firstOccurrence, hashSV, 0))
hv_store_ent(firstOccurrence, hashSV, newSViv(revNr), 0);
AV* secNrs;
HE* secNrsHE = hv_fetch_ent(secNrsBySeq, hashSV, 0, 0);
if(!secNrsHE) {
secNrs = newAV();
hv_store_ent(secNrsBySeq, hashSV, newRV_noinc((SV*)secNrs), 0);
} else {
secNrs = (AV*)SvRV(HeVAL(secNrsHE));
}
av_push(secNrs, newSViv(secNr));
}
if(isCJK) {
wStart[wNr%lws] = pos0;
inCJK=1;
} else if(isAlNum && !inWord) {
wStart[wNr%lws] = pos0;
inWord=1;
}
} while(c!=0);
}
END
%entityToUnicode = (
zwnj => 8204, zwj => 8205, lrm => 8206, rlm => 8207,
nbsp => 160, iexcl => 161, cent => 162, pound => 163,
curren => 164, yen => 165, brvbar => 166, sect => 167,
uml => 168, copy => 169, ordf => 170, laquo => 171,
not => 172, shy => 173, reg => 174, macr => 175,
deg => 176, plusmn => 177, sup2 => 178, sup3 => 179,
acute => 180, micro => 181, para => 182, middot => 183,
cedil => 184, sup1 => 185, ordm => 186, raquo => 187,
frac14 => 188, frac12 => 189, frac34 => 190, iquest => 191,
Agrave => 192, Aacute => 193, Acirc => 194, Atilde => 195,
Auml => 196, Aring => 197, AElig => 198, Ccedil => 199,
Egrave => 200, Eacute => 201, Ecirc => 202, Euml => 203,
Igrave => 204, Iacute => 205, Icirc => 206, Iuml => 207,
ETH => 208, Ntilde => 209, Ograve => 210, Oacute => 211,
Ocirc => 212, Otilde => 213, Ouml => 214, times => 215,
Oslash => 216, Ugrave => 217, Uacute => 218, Ucirc => 219,
Uuml => 220, Yacute => 221, THORN => 222, szlig => 223,
agrave => 224, aacute => 225, acirc => 226, atilde => 227,
auml => 228, aring => 229, aelig => 230, ccedil => 231,
egrave => 232, eacute => 233, ecirc => 234, euml => 235,
igrave => 236, iacute => 237, icirc => 238, iuml => 239,
eth => 240, ntilde => 241, ograve => 242, oacute => 243,
ocirc => 244, otilde => 245, ouml => 246, divide => 247,
oslash => 248, ugrave => 249, uacute => 250, ucirc => 251,
uuml => 252, yacute => 253, thorn => 254, yuml => 255,
fnof => 402, Alpha => 913, Beta => 914, Gamma => 915,
Delta => 916, Epsilon => 917, Zeta => 918, Eta => 919,
Theta => 920, Iota => 921, Kappa => 922, Lambda => 923,
Mu => 924, Nu => 925, Xi => 926, Omicron => 927,
Pi => 928, Rho => 929, Sigma => 931, Tau => 932,
Upsilon => 933, Phi => 934, Chi => 935, Psi => 936,
Omega => 937, alpha => 945, beta => 946, gamma => 947,
delta => 948, epsilon => 949, zeta => 950, eta => 951,
theta => 952, iota => 953, kappa => 954, lambda => 955,
mu => 956, nu => 957, xi => 958, omicron => 959,
pi => 960, rho => 961, sigmaf => 962, sigma => 963,
tau => 964, upsilon => 965, phi => 966, chi => 967,
psi => 968, omega => 969, thetasym => 977, upsih => 978,
piv => 982, bull => 8226, hellip => 8230, prime => 8242,
Prime => 8243, oline => 8254, frasl => 8260, weierp => 8472,
image => 8465, real => 8476, trade => 8482, alefsym => 8501,
larr => 8592, uarr => 8593, rarr => 8594, darr => 8595,
harr => 8596, crarr => 8629, lArr => 8656, uArr => 8657,
rArr => 8658, dArr => 8659, hArr => 8660, forall => 8704,
part => 8706, exist => 8707, empty => 8709, nabla => 8711,
isin => 8712, notin => 8713, ni => 8715, prod => 8719,
sum => 8721, minus => 8722, lowast => 8727, radic => 8730,
prop => 8733, infin => 8734, ang => 8736, and => 8743,
or => 8744, cap => 8745, cup => 8746, int => 8747,
there4 => 8756, sim => 8764, cong => 8773, asymp => 8776,
ne => 8800, equiv => 8801, le => 8804, ge => 8805,
sub => 8834, sup => 8835, nsub => 8836, sube => 8838,
supe => 8839, oplus => 8853, otimes => 8855, perp => 8869,
sdot => 8901, lceil => 8968, rceil => 8969, lfloor => 8970,
rfloor => 8971, lang => 9001, rang => 9002, loz => 9674,
spades => 9824, clubs => 9827, hearts => 9829, diams => 9830,
quot => 34, amp => 38, lt => 60, gt => 62,
OElig => 338, oelig => 339, Scaron => 352, scaron => 353,
Yuml => 376, circ => 710, tilde => 732, ensp => 8194,
emsp => 8195, thinsp => 8201, zwnj => 8204, zwj => 8205,
lrm => 8206, rlm => 8207, ndash => 8211, mdash => 8212,
lsquo => 8216, rsquo => 8217, sbquo => 8218, ldquo => 8220,
rdquo => 8221, bdquo => 8222, dagger => 8224, Dagger => 8225,
permil => 8240, lsaquo => 8249, rsaquo => 8250, euro => 8364,
);
sub entityToUnicodeChar {
my $entity = shift;
if($entity =~ /&(\w+);/) {
if(defined $entityToUnicode{$1}) {
return pack "U", $entityToUnicode{$1};
} else {
return "";
}
} elsif($entity =~ /&#(\d+);/) {
if($1<65536) {
return chr $1;
} else {
return "";
}
} elsif($entity =~ /&#x([0-9A-Fa-f]+);/) {
my $ucVal = hex $1;
if($ucVal<65536) {
return pack "U", $ucVal;
} else {
return "";
}
}
}
sub findWordSequences {
my ($sec, $secNr, $revNr) = @_;
return if do { use bytes; length $sec } > 200000;
$sec = xmlunesc($sec);
# $sec =~ s/^=+.*?=+//;
$sec =~ s/\[\[(?:.{2,3}|minnan|simple):.+?\]\]//isg;
$sec =~ s/\[\[(?!(?:$category|$image|category|image):)([^\]\|]*?\|)?([^\|]+?)\]\]/$2/isg;
while($sec =~ s/\{\{((?!\{\{).)*?\}\}//sg) {}
while($sec =~ s/\{\|((?!\{\|).)*?\|\}//sg) {}
while($sec =~ s/<table((?!<table).)*?<\/table>//isg) {}
$sec =~ s/\[\[(?:$category|$image|category|image):.*?\]\]//isg;
$sec =~ s/\*? ?\[(?:http|ftp|mailto).*?\]//isg;
$sec =~ s/\*? ?(?:http|ftp|mailto):\S*//isg;
$sec =~ s/<math>.*?<\/math>//sg;
$sec =~ s/<(?:div|font|span).*?>//isg;
$sec =~ s/<.{1,10}?>/ /sg;
$sec =~ s/(&\w+|#(?:\d+|x[0-9A-Fa-f]+);)/entityToUnicodeChar($1)/esg;
DJBHashes($sec, $revNr, $secNr, \%firstOccurrence, \%secNrsBySeq, $lws);
}
sub updateLastOccurrences {
foreach my $seq (keys %secNrsBySeq) {
for(my $i=0; $i<@{$secNrsBySeq{$seq}}; $i++) {
my $lOtmp = $secLastOccurrence[$secNrsBySeq{$seq}[$i]];
$lastOccurrence{$seq} = $lOtmp if !defined($lastOccurrence{$seq}) || $lOtmp>$lastOccurrence{$seq};
}
}
}
sub flushSeqs {
$nSeqs = keys %firstOccurrence;
for my $seq (keys %firstOccurrence) {
print SEQ pack("N3", $seq, $revId[$firstOccurrence{$seq}], $revId[$lastOccurrence{$seq}]);
}
}
sub text {
my ($t1, $t2, $t3, $dontSplit, $revNr) = @_;
# return "$t1$t2$t3";
my $lenTxt = do { use bytes; length $t2 };
if(!$dontSplit) {
my @s = ();
@s = split(/\n(?===[^\n]*?==\r?\n)/, $t2);
my @sNrs = (); my @sLen = (); my @sMd5 = ();
for(my $i=0; $i<@s; $i++) {
$s[$i] .= "\n" if $i<@s-1;
my $smd5 = md5_base64 $s[$i];
push @sMd5, $smd5;
my $secTitle = "0,";
if($s[$i] =~ /^([=]{2,}) *([^\n]*?) *\1\r?\n/s) {
$secTitle = length($1).",$2";
}
if(defined $secNrByMd5->{$smd5}) {
push @sNrs, $secNrByMd5->{$smd5};
$secLastOccurrence[$secNrByMd5->{$smd5}] = $revNr;
} else {
my $sNr = @$secs;
push @sNrs, $sNr;
$secNrByMd5->{$smd5} = $sNr;
push @{$secNrsByTitle->{$secTitle}}, $sNr;
push @$secs, $s[$i];
push @$secsMd5, $smd5;
push @$secsTitle, $secTitle;
push @secLastOccurrence, $revNr;
findWordSequences($s[$i], @$secs-1, $revNr);
}
}
my $md5 = md5_base64(join(" ", @sMd5));
$t1 = "<text type=\"sectionlist\" length=\"$lenTxt\" md5=\"$md5\">";
$t2 = join(" ", @sNrs);
return "$t1$t2$t3";
} else {
my $md5 = md5_base64($t2);
return $txtByMd5{$md5} if defined $txtByMd5{$md5};
if($lenTxt>100) {
($gzTxt, $status) = deflateInit(-WindowBits => 0 - MAX_WBITS);
($buf, $status) = $gzTxt->deflate($t2);
print TXT $buf if $saveText;
my $lenTxtGz = do { use bytes; length $buf };
($buf, $status) = $gzTxt->flush();
print TXT $buf if $saveText;
$lenTxtGz += do { use bytes; length $buf };
$t1 = "<text offset=\"$posTxt\" lengthGz=\"$lenTxtGz\" length=\"$lenTxt\" md5=\"$md5\" />";
} else {
print TXT $t2 if $saveText;
$t1 = "<text offset=\"$posTxt\" length=\"$lenTxt\" md5=\"$md5\" />";
}
$posTxt += $lenTxt;
$txtByMd5{$md5} = $t1;
return $t1;
}
}
sub flushSecs {
if(@$secs==0) {
$xml = " <sectiongroup />\n";
} else {
($gzTxt, $status) = deflateInit(-WindowBits => 0 - MAX_WBITS);
my $lenTxt = 0;
my $sPos = 0; my @sPos = (); my @sLen = ();
foreach my $secTitle (keys %$secNrsByTitle) {
for(my $i=0; $i<@{$secNrsByTitle->{$secTitle}}; $i++) {
my $sNr = $secNrsByTitle->{$secTitle}[$i];
$sPos[$sNr] = $sPos;
$sLen[$sNr] = do { use bytes; length $secs->[$sNr] };
$sPos += $sLen[$sNr];
($buf, $status) = $gzTxt->deflate($secs->[$sNr]);
print TXT $buf if $saveText;
$lenTxt += do { use bytes; length $buf };
}
}
($buf, $status) = $gzTxt->flush();
print TXT $buf if $saveText;
$lenTxt += do { use bytes; length $buf };
$xml = " <sectiongroup offset=\"$posTxt\" length=\"$lenTxt\">\n";
for(my $i=0; $i<@$secs; $i++) {
$xml .= " <section offset=\"$sPos[$i]\" length=\"$sLen[$i]\" ".
"md5=\"$secsMd5->[$i]\" title=\"$secsTitle->[$i]\" />\n";
}
$xml .= " </sectiongroup>\n";
$posTxt += $lenTxt;
}
($buf, $status) = $gzRev->deflate($xml);
print REV $buf;
$lenRev += do { use bytes; length $buf };
}
sub flushRevs {
my $lastBlock = $_[0];
if($revNr == @revs) {
if(@revs==1) {
$revs[0] =~ s/(<text.*?>)(.*?)(<\/text>)/text($1,$2,$3,1,$revNrs[$i])/es;
$xml = $page . $revs[0] . " </page>\n";
print REV $xml;
$lenRev = do { use bytes; length $xml };
$cm = 0;
return;
}
($gzRev, $status) = deflateInit(-WindowBits => 0 - MAX_WBITS);
($buf, $status) = $gzRev->deflate($page);
print REV $buf;
$lenRev = do { use bytes; length $buf };
$cm = 1;
}
$secs = []; $secNrByMd5 = {}; $secNrsByTitle = {}; $secsMd5 = []; $secsTitle = [];
%secNrsBySeq = (); @secLastOccurrence = ();
for(my $i=0; $i<@revs; $i++) {
$revs[$i] =~ s/(<text.*?>)(.*?)(<\/text>)/text($1,$2,$3,0,$revNrs[$i])/es;
}
flushSecs();
updateLastOccurrences();
for($i=0; $i<@revs; $i++) {
($buf, $status) = $gzRev->deflate($revs[$i]);
print REV $buf;
$lenRev += do { use bytes; length $buf };
}
if($lastBlock) {
($buf, $status) = $gzRev->deflate(" </page>\n");
print REV $buf;
$lenRev += do { use bytes; length $buf };
($buf, $status) = $gzRev->flush();
print REV $buf;
$lenRev += do { use bytes; length $buf };
}
@revs = ();
@revNrs = ();
}
unlink "$datadir/$lang$project.idx", "$datadir/$lang$project.nEdits";
tie %idx, "DB_File", "$datadir/$lang$project.idx" unless $delayIdx;
open REV, ">$datadir/$lang$project.rev";
open TXT, ">$datadir/$lang$project.txt" if $saveText;
open SEQ, ">$datadir/$lang$project.seq";
$posRev = 0; $posTxt = 0; $posSeq = 0;
while(<>) {
if((!$inPage || !$nsOk) && /^\s*<page/) {
$page = $_;
@revs = ();
$revNr = 0;
$inPage = 1;
$nsOk = 1;
$lenRev = 0;
%txtByMd5 = ();
%firstOccurrence = ();
%lastOccurrence = ();
@revId = ();
next;
}
next unless $nsOk;
if($inRev) {
if($inText) {
$rev .= $_;
if(/<\/text>$/) {
$inText = 0;
}
} else {
if(/^(\s*<text[^>]*?)( \/)?>/) {
$_ = "$1></text>\n" if defined $2;
$rev .= $_;
$inText = 1 unless defined $2 || $' =~/<\/text>$/;
} elsif(/^\s*<\/revision/) {
$rev .= $_;
push @revs, $rev;
push @revNrs, $revNr++;
flushRevs(0) if @revs==$revsPerBlock;
$inRev = 0;
} elsif(/^\s*<username>(.*?)<\/username>/) {
$rev .= $_;
$nEdits{$1}++ if $makeNEdits;
} elsif(/^\s*<contributor>/) {
$rev .= $_;
$inContributor = 1;
} elsif(/^\s*<\/contributor>/) {
$rev .= $_;
$inContributor = 0;
} elsif(/^\s*<id>(.*?)<\/id>/ && !$inContributor) {
$rev .= $_;
#print STDERR "$title, revNr=$revNr, id=$1\n";
$revId[$revNr] = $1;
} else {
$rev .= $_;
}
}
next;
}
if(/^\s*<revision/) {
$rev = $_;
$inRev = 1;
next;
}
if(/^\s*<\/page/) {
flushRevs(1);
flushSeqs();
$idx{$title} = "$posRev $lenRev $cm $posSeq $nSeqs";
$posRev += $lenRev;
$posSeq += 12*$nSeqs;
$inPage = 0;
#last if $title eq "Aussagenlogik";
next;
}
$page .= $_;
if(/^\s*<title(?:.*?)>(.*?)<\/title>/) {
$title = xmlunesc($1);
if($title =~ /(.+?):(.+)/ && defined $namespace{$1}) {
$namespace = $namespace{$1};
} else {
$namespace = 0;
}
$title =~ s/\s/_/g;
$nsOk = 0 if $namespace!=0;
}
}
close REV;
close TXT if $saveText;
close SEQ;
if($delayIdx) {
tie %idx2, "DB_File", "$datadir/$lang$project.idx";
%idx2 = %idx;
untie %idx2;
} else {
untie %idx;
}
if($makeNEdits) {
tie %nEdits2, "DB_File", "$datadir/$lang$project.nEdits";
%nEdits2 = %nEdits;
untie %nEdits2;
}