Help:Dottyweb

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 &lt;xmp&gt; 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 &lt;xmp&gt; tag).

Templates usable are:
 * Define a code chunk that can be included elsewhere.
 * Make this code chunk output to named file.
 * Make a file that is downloaded from a URL.
 * Read additional chunks and other things from another wiki page.
 * Make this code chunk output to named file, converting hex data in text into a binary file.
 * Make a regular expression rule.
 * Include contents of other code chunk here.

Program to retrieve files
&lt;?php // DottyWeb v0.2 // Public domain // --- Configuration division --- $config_URLs=array(    'esolang'=&gt;'http&#x3A;//esoteric.voxelperfect.net/w/index.php',     'local'=&gt;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&amp;ctype=text/css&amp;smaxage=0&amp;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=='&lt;/x'.'mp&gt;' || $v=='&lt;/nowiki&gt;&lt;/pre&gt;') { $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('/\&amp;\#(x?[0-9A-Fa-f]+);/',"\x01chr\x02\$1\x03",$v); $v=strtr($v,array( '&amp;amp;'=&gt;'&amp;', '&amp;lt;'=&gt;'&lt;', '&amp;gt;'=&gt;'&gt;', '&amp;quot;'=&gt;'"',      ));       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)) &amp;&amp; $v[0]!=" ") {         $s=false;       } else if($v=='&lt;xmp&gt;' || $v=='&lt;pre&gt;&lt;nowiki&gt;') {         $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]&#x5D;)) { $repl=chunk_tangle($chunks[$m[1][0]&#x5D;); } 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=&gt;$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&lt;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=&gt;$v) { $q=strtr(preg_quote($v[0][0],'/'),array('\*'=&gt;'.*','\?'=&gt;'.?')); $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]&#x5D;); 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]&#x5D;)) $chunks[$m[2]&#x5D;=array; $cur=&amp;$chunks[$m[2]&#x5D;; break; case 'file': check_filename($m[2]); $filetypes[$m[2]&#x5D;=0; if(!isset($files[$m[2]&#x5D;)) $files[$m[2]&#x5D;=array; $cur=&amp;$files[$m[2]&#x5D;; 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]&#x5D;=1; if(!isset($files[$m[2]&#x5D;)) $files[$m[2]&#x5D;=array; $cur=&amp;$files[$m[2]&#x5D;; 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&lt;3) { die("usage: dottyweb &lt;mode&gt; &lt;wiki&gt;/&lt;page&gt; [&lt;file...&gt;]\n"     ."modes: 'l'=list files, 'x'=receive files\n"      ."alt. usage: dottyweb c &lt;src&gt; &lt;dest&gt;\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,'&#x7B;{.file|'.$argv[2].'&#x7D;}'.$config_line_end); foreach($data as $x) { $x=trim($x,"\0\r\n"); $x=strtr($x,array( '&amp;'=&gt;'&amp;amp;', '&lt;'=&gt;'&amp;lt;', '&gt;'=&gt;'&amp;gt;', '[&#x5B;'=&gt;'[&amp;#x5B;', ']&#x5D;'=&gt;']&amp;#x5D;', '&#x7B;{'=&gt;'&amp;#x7B;{', '&#x7D;}'=&gt;'&amp;#x7D;}', ));    $x=preg_replace('/('.implode('|',$schemes).')\:/','$1&amp;#x3A;',$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; }