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; }