Help:Dottyweb
Jump to navigation
Jump to search
Dottyweb is a system for literate programming that can be implemented using MediaWiki templates. Code sections are lines with a space at the beginning, and you must use a prefix line indicating a chunk name. You can also use <xmp> to delimit code sections, instead, in which case no templates or anything else inside is interpreted, and it is passed verbatim (this mode will not work on this wiki, it requires a MediaWiki software that supports the <xmp> tag).
Templates usable are:
- {{.def|name}} Define a code chunk that can be included elsewhere.
- {{.file|name}} Make this code chunk output to named file.
- {{.get|name|URL}} Make a file that is downloaded from a URL.
- {{.incl|name}} Read additional chunks and other things from another wiki page.
- {{.hex|name}} Make this code chunk output to named file, converting hex data in text into a binary file.
- {{.reg|command}} Make a regular expression rule.
- {{.use|name}} Include contents of other code chunk here.
Program to retrieve files
dottyweb.php =
<?php
// DottyWeb v0.2
// Public domain
// --- Configuration division ---
$config_URLs=array(
'esolang'=>'http://esolangs.org/w/index.php',
'local'=>0,
);
$config_line_end="\r\n";
// --- End of configuration ---
function get_page($wiki,$name) {
global $config_URLs;
$title=urlencode(strtr($name,' ','_'));
if($config_URLs[$wiki]==0) {
$t=file($name);
} else {
$t=file($config_URLs[$wiki].'?action=raw&ctype=text/css&smaxage=0&title='.$title);
}
if(!$t) die("Error: nonexistent file: $wiki/$name\n");
$o=array();
$s=false;
$n=false;
foreach($t as $v) {
$v=trim($v,"\0\r\n");
if($n) {
if($v=='</x'.'mp>' || $v=='</nowiki></pre>') {
$n=false;
} else {
$o[]=$v;
}
} else {
$v=preg_replace('/\{\{\.([a-z]+)\}\}/',"\x01\$1\x02\x03",$v);
$v=preg_replace('/\{\{\.([a-z]+)\|([^{}]+)\}\}/',"\x01\$1\x02\$2\x03",$v);
$v=preg_replace('/\&\#(x?[0-9A-Fa-f]+);/',"\x01chr\x02\$1\x03",$v);
$v=strtr($v,array(
'&'=>'&',
'<'=>'<',
'>'=>'>',
'"'=>'"',
));
if(preg_match('/\x01incl\x02(.*?)\x03/',$v,$m)) {
$o=array_merge($o,get_page($wiki,$m[1]));
$s=false;
} else if(preg_match('/\x01reg\x02(.*?)\x03/',$v)) {
regex_line($v);
} else if($v[0]=="\x01") {
$s=true;
} else if(strlen(trim($v)) && $v[0]!=" ") {
$s=false;
} else if($v=='<xmp>' || $v=='<pre><nowiki>') {
$s=false; $n=true;
}
if($s) {
if($v[0]==" ") $v=substr($v,1);
$o[]=$v;
}
}
}
return $o;
}
$regex_all=array();
$regex=array();
$regex_current=array();
function regex_line($text) {
global $regex_all;
preg_match('/^ ?\x01reg\x02(.*?)\x03(.*?)\x01z\x02\x03(.*)$/',$text,$m);
$q=explode(' ',$m[1]);
if($q[0][0]=="'") {
$i=substr($q[0],1);
$q=array_slice($q,1);
} else {
$i=0; while(isset($regex_all[$i])) ++$i;
}
$regex_all[$i]=array($q,$m[2],$m[3]);
return $i;
}
function regex_callback($mat) {
global $chunks,$regex_all,$regex,$regex_current;
$ru=$regex_current[0];
$data=$regex_current[2];
$off=0;
while(preg_match('/\x01use\x02(.*?)\x03/',$data,$m,PREG_OFFSET_CAPTURE,$off)) {
if(ctype_digit($m[1][0])) {
$repl=$mat[(int)($m[1][0])];
} else if($m[1][0][0]=="'") {
$repl="\x01use\x02".substr($m[1][0],1).chr(3);
} else if(isset($chunks[$m[1][0]])) {
$repl=chunk_tangle($chunks[$m[1][0]]);
} else {
$repl="";
}
$data=substr($data,0,$m[0][1]).$repl.substr($data,$m[0][1]+strlen($m[0][0]));
$off=$m[0][1]+strlen($repl);
}
$out=$mat[0];
foreach($ru as $k=>$v) {
while(preg_match('/\$([0-9])/',$v,$m,PREG_OFFSET_CAPTURE)) {
$repl=$mat[(int)($m[1][0])];
$ru[$k]=substr($v,0,$m[0][1]).$repl.substr($v,$m[0][1]+strlen($m[0][0]));
}
}
for($i=0;$i<count($ru);$i++) {
switch($ru[$i]) {
case 'append':
$x=$ru[++$i];
$chunks[$x][]=$data;
break;
case 'change':
$out=$data;
break;
case 'delete':
unset($regex[$data]);
break;
case 'erase':
$out="";
break;
case 'global':
regex_line($data);
break;
case 'local':
$q=$regex_all;
$x=regex_line($data);
$regex[$x]=$regex_all[$x];
$regex_all=$q;
break;
case 'overwrite':
$x=$ru[++$i];
$chunks[$x]=array($data);
break;
}
}
return $out;
}
function regex_select($n) {
global $regex_all,$regex;
$regex=array();
foreach($regex_all as $k=>$v) {
$q=strtr(preg_quote($v[0][0],'/'),array('\*'=>'.*','\?'=>'.?'));
$v[0]=array_slice($v[0],1);
if(preg_match('/^'.$q.'$/',$n)) $regex[$k]=$v;
}
}
function regex_do($t) {
global $regex,$regex_current;
$n=true;
while($n) {
$n=false;
foreach($regex as $d) {
$regex_current=$d;
$n|=$t!=($t=preg_replace_callback(chr(7).$d[1].chr(7),regex_callback,$t));
}
}
return $t;
}
function check_filename($x) {
if(!strlen(trim($x))) die("Bad filename: $x\n");
if($x[0]=='/' || $x[0]=='.') die("Bad filename: $x\n");
if(strpos($x,'//')) die("Bad filename: $x\n");
if(strpos($x,'/.')) die("Bad filename: $x\n");
if(!preg_match('|^[A-Za-z_0-9./-]+$|',$x)) die("Bad filename: $x\n");
}
function chunk_tangle($data) {
global $chunks,$config_line_end;
if(is_array($data)) $data=implode($config_line_end,$data);
$off=0;
while(preg_match('/\x01([a-z]+)\x02(.*?)\x03/',$data,$m,PREG_OFFSET_CAPTURE,$off)) {
$repl="";
switch($m[1][0]) {
case 'chr':
if($m[2][0][0]=='x') {
$repl=chr(hexdec(substr($m[2][0],1)));
} else {
$repl=chr((int)($m[2][0]));
}
break;
case 'use':
$repl=chunk_tangle($chunks[$m[2][0]]);
break;
}
$data=substr($data,0,$m[0][1]).$repl.substr($data,$m[0][1]+strlen($m[0][0]));
$off=$m[0][1]+strlen($repl);
}
return regex_do($data);
}
function do_download_files($data) {
global $chunks,$files,$filetypes,$filelist;
$chunks=array();
$files=array();
$filetypes=array();
$cur=array();
foreach($data as $v) {
if(!preg_match('/^\x01(.*?)\x02(.*?)\x03 *$/',$v,$m)) $m=array(,,);
switch($m[1]) {
case 'def':
if(!isset($chunks[$m[2]])) $chunks[$m[2]]=array();
$cur=&$chunks[$m[2]];
break;
case 'file':
check_filename($m[2]);
$filetypes[$m[2]]=0;
if(!isset($files[$m[2]])) $files[$m[2]]=array();
$cur=&$files[$m[2]];
break;
case 'get':
$x=explode('|',$m[2]);
check_filename($x[0]);
copy($x[1],$x[0]);
break;
case 'hex':
check_filename($m[2]);
$filetypes[$m[2]]=1;
if(!isset($files[$m[2]])) $files[$m[2]]=array();
$cur=&$files[$m[2]];
break;
default:
$cur[]=$v;
}
}
if(!count($filelist)) $filelist=array_keys($files);
foreach($filelist as $n) {
$fp=fopen($n,"w");
regex_select($n);
$d=chunk_tangle($files[$n]);
if($filetypes[$n]==0) {
fwrite($fp,$d,strlen($d));
} else if($filetypes[$n]==1) {
fwrite($fp,pack('H',preg_replace('/[^0-9A-Fa-f]/',,$d)));
}
fclose($fp);
}
}
if($argc<3) {
die("usage: dottyweb <mode> <wiki>/<page> [<file...>]\n"
."modes: 'l'=list files, 'x'=receive files\n"
."alt. usage: dottyweb c <src> <dest>\n");
}
if($argv[1]=='c') {
$schemes=array(
'ftp','gopher','http','https','irc','mailto','news',
);
$data=file($argv[2]);
$fp=fopen($argv[3],"w");
fwrite($fp,'{{.file|'.$argv[2].'}}'.$config_line_end);
foreach($data as $x) {
$x=trim($x,"\0\r\n");
$x=strtr($x,array(
'&'=>'&',
'<'=>'<',
'>'=>'>',
'[['=>'[[',
']]'=>']]',
'{{'=>'{{',
'}}'=>'}}',
));
$x=preg_replace('/('.implode('|',$schemes).')\:/','$1:',$x);
fwrite($fp,' '.$x.$config_line_end);
}
fclose($fp);
die();
}
$i=(int)strpos($argv[2],'/');
$wiki=substr($argv[2],0,$i);
$name=substr($argv[2],$i+1);
$data=get_page($wiki,$name);
$filelist=array_slice($argv,3);
switch($argv[1]) {
case 'l':
foreach($data as $v) {
if(substr($v,0,6)=="\x01file\x02") echo trim(substr($v,6),"\x03")."\n";
}
break;
case 'x':
do_download_files($data);
break;
default:
die("Invalid mode\n");
break;
}