{(', '/(\\$\\{[^{}]*\\})/', "wshExpand1Var(\$pagename, \$m[1])"); # This alternate markup to invoke MXes allows interaction with $FmtPV *before* # the variables are interpolated. Use as {earlymx(set --pv ...)}. Markup_e('{earlymx(', '<{$var}', '/\\{earlymx(\\(\\w+\\b.*?\\))\\}/', "MarkupExpression(\$pagename, \$m[1])"); Markup_e('{latemx(', '>if', '/\\{latemx(\\(\\w+\\b.*?\\))\\}/', "MarkupExpression(\$pagename, \$m[1])"); # This function allows non-WikiSh MXes (or other functions) to take advantage # of some of the WikiSh utility functions for variable substitution, option # setting, wildcard expansion, etc. Piping is implemented through the option # setting (usually with --xargs), some of the other generic options. # # Basically if this is *not* done then people have to use opt=val syntax instead # of --opt:val, they will not have variables expanded, and they won't have # wildcard expansion. # # The $action is bit-wise controlled using the constants defined below. The # default has wshInitOpts() being called and wshExpandVars() being called within that # but *not* wildcards being expanded. define(WIKISH_ACTION_OPTIONS, 0x01); // call wshInitOpts() define(WIKISH_ACTION_VARIABLE, 0x02); // call wshExpandVars() define(WIKISH_ACTION_WILDCARD, 0x04); // call wshExpandWildCards() define(WIKISH_ACTION_DEFAULT, WIKISH_ACTION_VARIABLE|WIKISH_ACTION_OPTIONS); function WikiShCompatible($pagename,&$opt,&$args,$action=WIKISH_ACTION_DEFAULT,$optlist='') { if ($action & WIKISH_ACTION_OPTIONS) wshInitOpts($pagename,$optlist, $opt, $args, ($action & WIKISH_ACTION_VARIABLE)); if ($action & WIKISH_ACTION_WILDCARD) wshExpandWildCards($pagename, $opt, $args); } # This markup allows forms to automatically update $InputVals (to keep the same # values displayed on the form between submissions) and $WikiShVars[] (to # allow use of those values in the rest of the page). # OPTIONS: # method=GET # method=POST # target="Group.Page" # formname= # ARGUMENTS: # QUICKFORM - automatically output the "(:input form ...:)" and # "(:input hidden name=n ...:)" directives for a quick form # PROCESS - process the fields upon submission to maintain the values # in the fields and make them available as WikiShVars # REDIRECT - redirect to another page (specified by target option above) $MarkupExpr["wikish_form"] = 'wshForm($pagename, @$argp, @$args)'; function wshForm($pagename, $opt, $args) { global $InputValues, $FmtPV, $WikiShVars, $PageTextVarPatterns, $PageExistsCache; $func = 'wshForm(' . implode(" ", $args) . ')'; wdbg(4,"$func: Entering"); SDV($opt['method'], 'GET'); $rtn = ''; foreach ($args as $arg) { $arg = strtoupper($arg); switch ($arg) { case 'QUICKFORM': $rtn = "(:input form method=$opt[method]"; if ($opt['formname']) $rtn .= " name=$opt[formname]"; $rtn .= ":)\n"; $rtn .= "(:input hidden name=n value={\$FullName}:)\n"; break; case 'REDIRECT': $target = $opt['target']; if (!$target) { $WikiShVars['STATUS'] = 2; wshStdErr($pagename, $opt, "ERROR: No target specified"); return(false); } wshExpandVars($pagename, $opt, $target); if (substr($target,0,4) == 'http') { Redirect($pagename, PSS($target)); } else { preg_match("/\?action=\S+/", $target, $m); $tgt_pg = wshMakePageName($pagename, $opt, $target); $target = $tgt_pg . $m[0]; unset($PageExistsCache[$tgt_pg]); if (!$opt['newpage'] && !PageExists($tgt_pg)) { $WikiShVars['STATUS'] = 1; wshStdErr($pagename, $opt, "ERROR: Page $tgt_pg does not exist. Cannot redirect."); return(false); } Redirect($target); } // Actually we never get here because Redirect does exit() return; break; case 'PROCESS': foreach ($_REQUEST as $k=>$v) { if (is_array($v)) { $foo = ''; foreach ($v as $i=>$val) { $tmp = htmlspecialchars($val,ENT_NOQUOTES); $InputValues[$k][$i] = stripmagic($tmp); $foo .= (($foo)?',':'') . $tmp; } } else { $foo = htmlspecialchars($v,ENT_NOQUOTES); # This keeps the field values current with the form from # submission to submission, but has nothing to do with PTV $InputValues[$k] = stripmagic($foo); } $WikiShVars[$k] = stripmagic($foo); #echo "InputValue[$k]=$InputValues[$k], WikiShVars[$k]=$WikiShVars[$k]
\n"; } if ($opt['source']) { $source = WIKIPAGEID.wshMakePageName($pagename, $opt, $opt['source']); $page = wshReadPage($pagename, $opt, $source); foreach ((array)$PageTextVarPatterns as $ptvpat) { if (preg_match_all($ptvpat, $page['text'], $match, PREG_SET_ORDER)) { foreach ($match as $m) { if (!isset($_REQUEST[$m[2]])) { $InputValues[$m[2]] = $WikiShVars[$m[2]] = stripmagic($m[3]); } } } } } break; default: break; } } return($rtn); } Markup_e('wshFunction', '<{(', '/\\{\\(function\\b\\s*(.*?)\\)\\}/i', "wshStoreFunction(\$pagename, \$m[1])"); function wshStoreFunction($pagename, $expr) { global $wshFunctionList, $MarkupExpr; $func = 'wshStoreFunction()'; wdbg(4,"$func: Entering expr=$expr"); $pieces = preg_split("/\s+/", $expr); wdbg(1,"$func: pieces=".print_r($pieces,true)); $funcname = strtolower(ltrim(array_shift($pieces))); $wshFunctionList[$funcname] = implode(" ", $pieces); wdbg(3,"$func: Storing $funcname() as \"$wshFunctionList[$funcname]\""); $MarkupExpr[$funcname] = "wshDoFunction(\$pagename, '$funcname', \$params, \$exiting)"; return(''); } function wshDoFunction($pagename, $funcname, $expr, &$exiting) { global $wshFunctionList, $MarkupExpr, $WikiShVars; $func = 'wshDoFunction()'; $opt = array(); if ($expr{0} == ' ') $expr = substr($expr, 1); // get rid of leading space wdbg(4,"$func: Entering with funcname=$funcname, expr=$expr"); # Need to check for OUTSPEC and get set for that... # If you want flags to get through to your function then you need to precede # them with a -- (or the first one as \-) # foreach (preg_split("/\\s+/", $args, -1, PREG_SPLIT_NO_EMPTY) as $arg) { $args = explode(" ", $expr); wshInitOpts($pagename, '', $opt, $args); # Set up ${0}, ${1}, etc as positional paramaters $WikiShVars[0] = $funcname; $i = 1; if ($args) { foreach ($args as $arg) { wdbg(2,"$func: arg#${i}=$arg"); $WikiShVars[$i++] = $arg; } } wdbg(0,$WikiShVars); $WikiShVars['#'] = $i - 1; for ( ; $i <= 9; $i++) unset($WikiShVars[$i]); $newexpr = $wshFunctionList[$funcname]; wdbg(2,"$func: Attempting to run expr=>>$newexpr<<"); $tmp = MarkupExpression($pagename, '(' . $newexpr . ')'); wdbg(1,"$func: Return value was >>$tmp<<"); return(wshPostProcess($pagename, $opt, explode("\n", $tmp))); } # WikiSh MX implements overall control including # if BOOL-EXPR; then EXPR; [EXPR; ...] [else EXPR; [EXPR; ...]] fi; # while BOOL-EXPR; do EXPR; [EXPR; ...] done; # for VAR in FILE... do ... done # Note that BOOL-EXPR is any normal expression which sets $WikiShVars['STATUS'] # to 0 for success or non-zero for failure. Typically this would be the WikiSh # functions: # true # false # grep (true=found something, false=found nothing) # test (see documentation of that function) # [ ... ] (synonym for test - see documentation of that function) # {any other WikiSh function will return success unless there is an attempt # to write to a file (via stdout=, etc.) and that write fails or unless # there is an attempt to read a file (for instance a text-file) and the # read attempt fails due to lack of permissions, etc.} # rm (true=success, false=failure for any reason) # sed (true=success, false=failure to write via stdout= or -i) # cat (true=success, false=failure to write via stdout=) # etc. # # while ($x = one expression (anchored at start, semi-colon terminated)) { # $cmd = first word of $x # switch ($cmd) { # if) $x = whole if statement, anchored at start, "fi\s*;" terminated # get testexpr, trueexpr, elseexpr # $negate = (!?) on testexpr # $rtn = testexpr # if (!$negate && $rtn) # $runexpr=$trueexpr # else # $runexpr=$elseexpr # process through $runexpr and run each # for) # $x = whole for statement, anchored at start, "done\s*;" terminated # wshExpandWildCards for $x (also variable substitution) # get VAR, FILELIST, EXPR-LIST # foreach (FILELIST as $WikiShVars[$VAR]) { # process through $EXPR-LIST and run each # } # unknown) # run the $x # } # get rid of $x off the front of the expression # } $MarkupExpr['wikish'] = 'WikiSh($pagename, $params)'; function WikiSh($pagename, $expr) { global $exiting, $returning; if (wshNotNow($pagename)) return(Keep('', 'P')); SDV($exiting, false); SDV($returning, false); wdbg(4,"WikiSh(): Entering"); wshUnHTMLSpecialChars($expr); $exiting = false; $continue = $break = 0; # Check for the option --expandvars if (($pos = strpos($expr, '--expandvars')) !== FALSE && $pos <= 1) { $expr = wshExpand1Var($pagename, $expr); $expr = substr($expr, $pos+strlen('--expandvars')); } // this needs to be split out like this because WikiShRunExpr calls itself // recursively and the Keep() at the end of THIS function messes things up wdbg(1,"WikiSh(): Calling WikiShRunExpr($expr)"); $rtn = WikiShRunExpr($pagename, $expr, $exiting, $break, $continue); if ($exiting && $returning) $exiting = $returning = false; wdbg(4,"WikiSh(): Exiting"); return(Keep($rtn, 'P')); } function WikiShRunExpr($pagename, $expr, &$exiting, &$break, &$continue) { global $WikiShVars; static $Level=-1; $Level++; $func = "WikiShRunExpr($Level)"; wdbg(4,"$func: Entering"); wdbg(3,"$func: expr=$expr"); # if EXPR; then; EXPR; [...] [else; EXPR; [...]] fi $iffi = '(?>if\s+)([^;]+)(?>;\s*)' . '(?>then\s*;?\s*)' . '((?:(?:(?:(?!\bif\b)(?!\belse\b)(?!\bfi\b).)+)|(?R)+)+)\s*' . '(?:' . '(?>else\s*;?\s*)' . '((?:(?:(?:(?!\bif\b)(?!\belse\b)(?!\bfi\b).)+)|(?R)+)+)' . ')?\s*' . '(?>fi;?\s*)'; $dodonere= '(?>(?:while|for))\s+([^;]+);\s*'. '(?>do\s*;?\s*)'. '('. '(?:'. '(?:(?!\bfor\b)(?!\bwhile\b)(?!\bdone\b).)+'. '|'. '(?R)'. ')+'. ')\s*(?>done\s*;?\s*)'; # source FILE; $sourceit = "^source\s+([-$}{\w.#:\/]+)\s*([^;]*);?\s*"; $rtn = ''; $expr = preg_replace("/^\s*/", "", $expr); // get rid of initial spaces $lastexpr = 'x' . $expr; while (!$exiting && !$break && !$continue && $expr && $lastexpr != $expr) { wdbg(3,"$func: Current wikish chunk: >>$expr<<"); $lastexpr = $expr; if (preg_match("/^if\b/", $expr) && preg_match("/$iffi/", $expr, $match)) { list($foo, $testexpr, $ifexpr, $elseexpr) = $match; wdbg(3,"$func: IF-THEN-ELSE MATCHED: $foo"); wdbg(1, $match); wdbg(2,"$func: testexpr=$testexpr"); wdbg(2,"$func: ifexpr=$ifexpr"); wdbg(2,"$func: elseexpr=$elseexpr"); $rtn = wshCS($rtn, WikiShRunExpr($pagename, $testexpr, $exiting, $break, $continue)); wdbg(2,"$func: STATUS=$WikiShVars[STATUS]"); if (!$exiting && !$break && !$continue) { if ($WikiShVars['STATUS'] == 0) { wdbg(2,"$func: running ifexpr=$ifexpr"); $rtn = wshCS($rtn, WikiShRunExpr($pagename, $ifexpr, $exiting, $break, $continue)); } else { wdbg(2,"$func: running elseexpr=$elseexpr"); $rtn = wshCS($rtn, WikiShRunExpr($pagename, $elseexpr, $exiting, $break, $continue)); } } } elseif (preg_match("/^for\b/", $expr) && preg_match("/$dodonere/", $expr, $match)) { list($foo, $tmp, $doexpr) = $match; # $tmp = "VAR in LIST" if (!preg_match("/(\w+)\s+in\s+(\S.*)$/", $tmp, $match)) { wdbg(4,"$func: ERROR: preg_match should not have failed >>$tmp<<"); $Level--; return (''); } list($tmp, $varname, $list) = $match; wdbg(3,"$func: FOR-IN-DO-DONE MATCHED: $foo"); wdbg(2,"$func: varname=$varname"); wdbg(2,"$func: list=$list"); wdbg(2,"$func: doexpr=$doexpr"); $list = array($list); wshExpandVars($pagename, $opt, $list); $list = explode(" ", implode(" ", $list)); // yes, I mean that. wshExpandWildCards($pagename, array(), $list, true, true, true); foreach ($list as $l) { $WikiShVars[$varname] = $l; $rtn = wshCS($rtn, WikiShRunExpr($pagename, $doexpr, $exiting, $break, $continue)); if ($continue) { $continue--; if ($continue) break; } if ($exiting) break; if ($break) { $break--; break; } } } elseif (preg_match("/^while\b/", $expr) && preg_match("/$dodonere/", $expr, $match)) { list($foo, $testexpr, $doexpr) = $match; wdbg(3,"$func: WHILE-DO MATCHED: $foo"); wdbg(2,"$func: testexpr=$testexpr"); wdbg(2,"$func: doexpr=$doexpr"); $rtn = wshCS($rtn, WikiShRunExpr($pagename, $testexpr, $exiting, $break, $continue)); while (!$exiting && $WikiShVars['STATUS'] == 0 && !$continue && !$break) { $rtn=wshCS($rtn, WikiShRunExpr($pagename, $doexpr, $exiting, $break, $continue)); if ($break) { $break--; break; } if ($continue) { $continue--; if ($continue) break; } if (!$exiting) $rtn=wshCS($rtn, WikiShRunExpr($pagename, $testexpr, $exiting, $break, $continue)); } } elseif (preg_match("/$sourceit/", $expr, $match)) { list($foo, $sourcefile, $args) = $match; wdbg(3,"$func: SOURCE MATCHED: $foo"); wdbg(2,"$func: sourcefile=$sourcefile"); $sourcefile = wshExpand1Var($pagename, $sourcefile); $WikiShVars[0] = $sourcefile; $i = 1; if ($args) { foreach (explode(" ", $args) as $arg) { wdbg(2,"$func: arg#${i}=$arg"); $WikiShVars[$i++] = $arg; } } $WikiShVars['#'] = $i - 1; wdbg(1,$WikiShVars); $page = wshReadPage($pagename, array(), WIKIPAGEID . $sourcefile, true); $code = wshParseCode(wshUnHTMLSpecialChars($page['text'])); wdbg(2,"$func: Got this code: $code"); $expr = substr($expr, 0, strlen($foo)) . $code . substr($expr, strlen($foo)); wdbg(1,"$func: Expr became this: $expr"); } elseif (preg_match("/^([^;]*)\s*\|\s*(?=while)/", $expr, $match)) { list($foo, $doexpr) = $match; wdbg(3,"$func: PIPE TO WHILE EXPR MATCHED: $foo"); wdbg(2,"$func: doexpr=$doexpr"); $looprtn = ''; WikiShRunExprNode($pagename, $doexpr, $looprtn, $exiting, $break, $continue, true); $rtn .= $looprtn; } elseif (preg_match("/^([^;]*)\s*;?\s*/", $expr, $match)) { list($foo, $doexpr) = $match; wdbg(3,"$func: SIMPLE EXPR MATCHED: $foo"); wdbg(2,"$func: doexpr=$doexpr"); WikiShRunExprNode($pagename, $doexpr, $rtn, $exiting, $break, $continue); } wdbg(1,"$func: Before stripping off just-processed chunk=>>$expr<<"); wdbg(1,"$func: Stripping off >>$foo<< (len=" . strlen($foo) . ")"); $expr = substr($expr, strlen($foo)); wdbg(1,"$func: Looping back with chunk=>>$expr<<"); } $Level--; return($rtn); } #WikiShConcatenateStrings function wshCS($str1, $str2) { return ($str1 . (($str1 && $str2) ? "\n" : "") . $str2); } # This function accepts |, &&, ||, and semi-colon-delimited lists of commands # and executes them. # It also processes back-quote-delimited expressions. # # If you are trying to read this code, my apologies. The section regarding # backquotes has become one of the messiest sections of WikiSh. The problem # is quotes within quotes and the possibility of quotes coming from data that # should not be recognized, but other quotes need to be recognized and etc. function WikiShRunExprNode($pagename, $expr, &$rtn, &$exiting, &$break, &$continue, $grab_pipe=false) { global $WikiShPipeText, $WikiShPipeActive; global $WikiShVars, $returning; global $KeepToken, $KPV, $MarkupExpr; $func = "WikiShRunExprNode($expr)"; wdbg(4, "$func: Entering"); $rpat = "/$KeepToken(\\d+P)$KeepToken/e"; $rrep = 'wshUnHTMLSpecialChars($KPV[\'$1\'], true)'; # Restore "kept" strings within quotes $expr = preg_replace($rpat, "'".CHR(1)."'.".$rrep.".'".CHR(1)."'", $expr); while (preg_match("/`([^`]+)`/", $expr, $m)) { # change all single- and double- quotes into chr(2) and chr(3) # expand variables # do a keep as if the chr(2/3) were single-/double-quotes # replace any remaining chr(2/3) characters back to quotes # run the markup expression found in the backquotes # replace in the big-picture expression the results of the backquote $tmp=''; wdbg(3,"$func: Expanding vars on \"$m[1]\""); $m[1] = str_replace(array("'", '"'), array(CHR(2), CHR(3)), $m[1]); wshExpandVars($pagename, array(), $m[1]); $m[1] = preg_replace("/([\\002\\003])([^\\1]*?)\\1/e", "Keep(PSS(str_replace(array(CHR(2), CHR(3)), array(\"'\", '\"'), '$2')),'P')", $m[1]); $m[1] = str_replace(array(CHR(2), CHR(3)), array("'", '"'), $m[1]); wdbg(3,"$func: Running backquotes on \"$m[1]\""); WikiShRunExprNode($pagename, $m[1], $tmp, $exiting, $break, $continue); wdbg(3,"$func: Replacing >>$m[1]<< with >>$tmp<<"); $expr = str_replace($m[0], $tmp, $expr); wdbg(3,"$func: Resulting expr = \"$expr\""); } wshExpandVars($pagename, array(), $expr); # "Keep" quoted strings again $expr = preg_replace('/\001([^\001]*)\001/e', "Keep(PSS('$1'),'P')", $expr); $exprlist = preg_split("/\s*;\s*/", $expr, -1, PREG_SPLIT_NO_EMPTY); foreach ($exprlist as $expr) { wdbg(2,"$func: Processing expr=>>" . wshDbgOd($expr) . "<<"); $explist = preg_split("/\s*(\|\||&&)\s*/", $expr, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE); $exlist = array(); $lastop = ''; foreach ($explist as $exp) { if ($exp == '||' || $exp == '&&') { $lastop = $exp; continue; } $exlist[] = $lastop . $exp; } foreach ($exlist as $ex) { wdbg(2,"$func: Shall we process ex=$ex"); $First2 = substr($ex, 0, 2); if (($First2 == '&&' && $WikiShVars['STATUS'] != 0) || ($First2 == '||' && $WikiShVars['STATUS'] == 0)) continue; if ($First2 == '&&' || $First2 == '||') $ex = substr($ex, 2); wdbg(2,"$func: Checking negation ex=>>$ex<<"); wdbg(2,"$func: ex=" . wshDbgOd($ex)); if (!preg_match("/^\s*(!)?\s*(.*)$/s", $ex, $match)) wshStdErr($pagename, $opt, "ERROR: $func: Non-matching regex for negation. Attempting to continue"); $negate = $match[1]; $ex = $match[2]; wdbg(2,"$func: Processing ex=$ex"); $elist = preg_split("/\s*\|\s*/", $ex, -1, PREG_SPLIT_NO_EMPTY); foreach ($elist as $e) { wdbg(2,"$func: Processing e=$e"); if (preg_match("/^\s*exit(?:\s+(\d+))?\s*$/", $e, $m)) { wdbg(4,"$func: Found exit. Getting out of dodge..."); if (!$m[1] || $m[1]===0) $WikiShVars['STATUS'] = $m[1]; $exiting = true; break 3; } elseif (preg_match("/^\s*return(?:\s+(\d+))?\s*$/", $e, $m)) { wdbg(4,"$func: Found RETURN. Getting out of dodge..."); if (!$m[1] || $m[1]===0) $WikiShVars['STATUS'] = $m[1]; $exiting = true; $returning = true; break 3; } elseif (preg_match("/^\s*continue(?:\s+(\d+))?\s*$/", $e, $m)) { wdbg(4,"$func: Found continue. Getting out of dodge..."); if ($m[1]) $continue = $m[1]; else $continue = 1; break 3; } elseif (preg_match("/^\s*break(?:\s+(\d+))?\s*$/", $e, $m)) { wdbg(4,"$func: Found break. Getting out of dodge..."); if ($m[1]) $break = $m[1]; else $break = 1; break 3; } else { $x = array($e); wshExpandVars($pagename, array(), $x); $e = $x[0]; if (preg_match('/^\\s*(\\w+)\\b/', $e, $match) && !@$MarkupExpr[$match[0]]) { $tmp = "UNKNOWN MARKUP EXPRESSION COMMAND: $match[0]"; $WikiShVars['STATUS'] = 1; } else { wdbg(2,"$func: Really processing e=" . wshDbgOd($e)); $tmp = MarkupExpression($pagename, '(' . $e . ')'); } $WikiShPipeText = $tmp; $WikiShPipeActive = true; } } if ($negate) $WikiShVars['STATUS'] = !$WikiShVars['STATUS']; $WikiShPipeActive = $WikiShPipeText = false; } if ($rtn) $rtn .= "\n" . $tmp; else $rtn = $tmp; wdbg(2,"$func: rtn til now >>$rtn<<"); } if ($grab_pipe) { wdbg(3,"$func: rtn being put in pipe"); $WikiShPipeText = $rtn; $WikiShPipeActive = true; $rtn = ''; } } # Basename() # OPTION: # --raw -- simply apply the PHP function basename() to the input # Normally apply basename() to text files and PageVar(..., $Name) to all others $MarkupExpr["${WikiShMXPrefix}basename"] = 'wshBasename($pagename, @$argp, @$args)'; function wshBasename($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func="Basename()"; wdbg(4,"$func: " . implode(" ", $args)); WikiShCompatible($pagename,$opt,$args); // handle wshInitOpts & wshExpandWildCards wdbg(1,"$func: arg0=$args[0]"); if (@$opt['raw'] or wshIsATextFile('', $args[0])) { if (wshIsATextFile('', $args[0])) $fn = substr($args[0], strlen(TEXTFILEID)); else $fn = $args[0]; $name = basename($fn); wdbg(1, "$func: name=$name, fn=$fn"); } else $name = PageVar($args[0], '$Name'); if ($name) $WikiShVars['STATUS'] = 0; else $WikiShVars['STATUS'] = 1; return ($name); } # {(cat OPTIONS PAGEPATTERN)} # conCATenate all lines of all files passed # Options: # PAGEPATTERN... - source pages from PageName or Group.Pagename # allowing wiki wildcards * and ? # (multiple files/patterns allowed) $MarkupExpr["${WikiShMXPrefix}cat"] = 'wshCat($pagename, @$argp, @$args)'; function wshCat($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func="Cat()"; wdbg(4,"Cat(): Entering"); wdbg(1,$args); wshInitOpts($pagename, '', $opt, $args); wshExpandWildCards($pagename, $opt, $args, false, false, false); wshSDOpt($opt, 'file_prefix', ''); wshSDOpt($opt, 'line_prefix', ''); wshSDOpt($opt, 'line_suffix', ''); $newrows = array(); foreach ($args as $filename) { $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: No such page: $page[filename]"); continue; } $textrows = explode("\n", $page['text']); wdbg(1,"Cat: textrows="); wdbg(1,$textrows); if ($opt['file_prefix']) $array_prefix = array(wshReplace($opt, $page, $opt['file_prefix'])); else $array_prefix = array(); $line_prefix = wshReplace($opt, $page, $opt['line_prefix']); $line_suffix = wshReplace($opt, $page, $opt['line_suffix']); if ($line_prefix || $line_suffix) for ($i = 0; $i < sizeof($textrows); $i++) { if (strstr($opt['line_prefix'], 'LINENO')) $line_prefix = wshReplace($opt, $page, $opt['line_prefix'], $i+1); $textrows[$i] = $line_prefix . $textrows[$i] . $line_suffix; } $newrows = array_merge($newrows, $array_prefix, $textrows); } $WikiShVars['STATUS'] = 0; return (wshPostProcess($pagename, $opt, $newrows, $page)); } # Chmod() # {(chmod read='secret' edit='secret' attr='secret' page(s))} # Note that wikish Chmod is pretty far removed from shell chmod and does NOT # support TEXTFILE-- files and permissions. # OPTIONS: # --read:pw Specify a read passwd # --edit:pw Specify an edit passwd # --attr:pw Specify an attr passwd # --upload:pw Specify an attr passwd # -a The new passwd(s) will be ADDED to existing rather than replacing # -p Work with page authorizations (default if neither -p nor -g # specified) # -g Work with group authorizations (Group.GroupAttributes) $MarkupExpr["${WikiShMXPrefix}chmod"]='wshChmod($pagename, @$argp, @$args)'; function wshChmod($pagename, $opt, $args) { global $WikiShVars, $GroupAttributesFmt, $EnableWikiShChmod, $wshPmAuthList; if (wshNotNow($pagename)) return(''); $func = 'Chmod()'; SDV($wshPmAuthList, array('read', 'edit', 'attr', 'upload')); if (!$EnableWikiShChmod) { wshStdErr($pagename, $opt, "ERROR: $func: not enabled. See \$EnableWikiShChmod"); return(''); } SDV($GroupAttributesFmt,'$Group/GroupAttributes'); WikiShCompatible($pagename,$opt,$args,WIKISH_ACTION_VARIABLE|WIKISH_ACTION_OPTIONS|WIKISH_ACTION_WILDCARD); // handle wshInitOpts & wshExpandWildCards if (!@$opt['g']) $opt['p'] = true; // default is page level auth if (!isset($opt['read']) && !isset($opt['edit']) && !isset($opt['attr']) && !isset($opt['upload'])) { wshStdErr($pagename, $opt, "ERROR: $func: Must specify at least one password. No password specified."); $WikiShVars['STATUS'] = 4; return(''); } if ($opt['p']) { $newargs = $args; } else { $newargs = array(); } if ($opt['g']) { foreach ($args as $a) { $ga = FmtPagename($GroupAttributesFmt, $a); if (!in_array($ga, $newargs)) $newargs[] = $ga; } } foreach ($wshPmAuthList as $plev) { if (isset($opt[$plev])) { $pw[$plev] = ''; foreach ((array)$opt[$plev] as $pass) foreach (explode(" ", $pass) as $p) $pw[$plev] .= ' ' . (preg_match('/^(@|clear|\\w+:)/',$p) ? $p : crypt($p)); $pw[$plev] = ltrim($pw[$plev]); } else $pw[$plev] = 'ignore'; } foreach ((array)$newargs as $filename) { wdbg(1,"$func: Processing $filename"); $page = $newpage = wshReadPage($pagename, $opt, $filename); if ($page['type'] != 'wiki') { wshStdErr($pagename, $opt, "ERROR: Chmod acts only on wiki pages. Invalid pagename: $filename (type=$page[type])"); continue; } // Set the various passwords in the $page array foreach ($wshPmAuthList as $plev) { if ($pw[$plev] != 'ignore') { if (@$opt['a']) $newpage['passwd'.$plev] .= ' ' . $pw[$plev]; else if ($pw[$plev] == 'clear' || !$pw[$plev]) unset($newpage['passwd'.$plev]); else $newpage['passwd'.$plev] = $pw[$plev]; } } // Now write the page if (!wshWrite($pagename, $opt, $filename, $page['type'], $newpage, $page, 'attr', 'attr')) { $WikiShVars['STATUS'] = 2; return(''); } } } $MarkupExpr["${WikiShMXPrefix}cp"] = 'wshCp($pagename, @$argp, @$args)'; function wshCp($pagename, $opt, $args) { global $EnableWikiShWritePage, $EnableWikiShOverwritePage, $EnableWikiShCreatePage; global $WorkDir; global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = "cp(" . implode(" ", $args) . ")"; wdbg(4,"$func: Entering"); SDV($EnableWikiShWritePage, false); SDV($EnableWikiShOverwritePage, false); SDV($EnableWikiShCreatePage, false); if (!$EnableWikiShWritePage) { wshStdErr($pagename, $opt, "ERROR: $func: Unable to write pages. Not enabled."); $WikiShVars['STATUS'] = 4; return (''); } wshInitOpts($pagename, '', $opt, $args); $Target = array_pop($args); // strip off the target before expanding to // avoid Group being rewritten as GroupA.Group wshExpandWildCards($pagename, $opt, $args); wshSDOpt($opt, 'q', false); wshSDOpt($opt, 'trial', false); if (wshIsATextFile('', $Target)) { $Target = substr($Target, strlen(TEXTFILEID)); $Target_Type = 'text'; if (file_exists($Target)) $TargetIsDir = is_dir($Target); else { $TargetIsDir = false; if (sizeof($args) > 1) { wshStdErr($pagename, $opt, "ERROR: $func: Cannot copy multiple files to non-directory destination $Target"); $WikiShVars['STATUS'] = 2; return (''); } } } else { $Target_Type = 'wiki'; if (!strstr($Target, '.') && !strstr($target, '/')) $TargetIsDir = true; } if (sizeof($args) > 1 && !$TargetIsDir) { wshStdErr($pagename, $opt, "ERROR: $func: Cannot copy multiple files to non-" . (($Target_Type == 'wiki') ? "group" : "directory") . " destination $Target"); $WikiShVars['STATUS'] = 2; return (''); } if (sizeof($args) < 1) { wshStdErr($pagename, $opt, "ERROR: $func: No Source file specified to copy to destination $Target"); $WikiShVars['STATUS'] = 2; return (''); } $newrows = array(); foreach ($args as $filename) { wdbg(1,"$func: Processing $filename"); $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: Cannot copy source file $page[filename]. Error reading."); $WikiShVars['STATUS'] = 6; return; } if ($page['filename'] == '-' && $page['type'] == 'inline' && $TargetIsDir) { wshStdErr($pagename, $opt, "ERROR: $func: Cannot copy inline file to directory destination $Target"); continue; } if ($TargetIsDir) { if ($Target_Type == 'wiki') { $tmp = preg_replace("/^.*[\/\.]/", "", $page[filename]); $tgt = "$Target.$tmp"; } else $tgt = "$Target/$page[filename]"; wdbg(1,"$func: Created target name $tgt"); } else $tgt = $Target; wdbg(1,"$func: Target set to $tgt"); if ($page['filename'] == $tgt) { wshStdErr($pagename, $opt, "ERROR: Source ($page[filename]) and Target ($tgt) are the same. Aborting."); $WikiShVars['STATUS'] = 5; return; } $page['type'] = $Target_Type; if ($opt['trial']) { wdbg(1,"$func: Trial-copying $page[filename] to $tgt"); if (!$opt['q']) $newrows[] = "TRIAL copying " . $page['filename'] . " to $tgt"; } else { if (!$opt['q']) $newrows[] = 'Copying: ' . $page['filename'] . " to $tgt"; if ($opt['encrypt'] && function_exists('WikiShEncrypt')) $page['text'] = WikiShEncrypt($pagename, $opt, $page['text']); # $auth will be 'overwrite' automatically unless not exist when it # will become 'create' automatically. Thus no need to specify. if (!wshWrite($pagename, $opt, $tgt, $page['type'], $page['text'], $page)) { $WikiShVars['STATUS'] = 2; return(''); } } } $WikiShVars['STATUS'] = 0; return (wshPostProcess($pagename, $opt, $newrows, $page)); } # {(cut OPTIONS PAGEPATTERN)} # cut "chunks" (fields or character-spans) from each line # Options: # -dx x becomes the pattern by which fields are split # (default = \s+) # -fx[-y] cut fields x or x-to-y, with "field" defined by -dx option (1-based) # -cx[-y] cut characters x or x-to-y (1-based) # --ofs:y output field separator (defaults to x from -dx if unspecified) $MarkupExpr["${WikiShMXPrefix}cut"] = 'wshCut($pagename, @$argp, @$args)'; function wshCut($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = 'Cut()'; wdbg(4,"$func: Entering"); wdbg(1,$args); wshInitOpts($pagename, 'd:f:c:', $opt, $args); wshSDOpt($opt, 'f', ''); wshSDOpt($opt, 'c', ''); wshSDOpt($opt, 'd', "\t"); wshSDOpt($opt, 'ofs', $opt['d']); if (is_array($opt['f'])) $flddef = implode(',', $opt['f']); else $flddef = $opt['f']; if (is_array($opt['c'])) $chardef = implode(',', $opt['c']); else $chardef = $opt['c']; wshExpandWildCards($pagename, $opt, $args, false, false, false); wshSDOpt($opt, 'file_prefix', ''); wshSDOpt($opt, 'line_prefix', ''); wshSDOpt($opt, 'line_suffix', ''); if (!$opt['f'] && !$opt['c']) { $WikiShVars['STATUS'] = 4; wshStdErr($pagename, $opt, "ERROR: $func: either -f or -c must be specified"); return(''); } $newrows = array(); foreach ($args as $filename) { $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: No such page: $page[filename]"); continue; } $textrows = explode("\n", $page['text']); wdbg(1,"$func: textrows="); wdbg(1,$textrows); if ($opt['file_prefix']) $array_prefix = array(wshReplace($opt, $page, $opt['file_prefix'])); else $array_prefix = array(); $line_prefix = wshReplace($opt, $page, $opt['line_prefix']); $line_suffix = wshReplace($opt, $page, $opt['line_suffix']); for ($i = 0; $i < sizeof($textrows); $i++) { if (strstr($opt['line_prefix'], 'LINENO')) $line_prefix = wshReplace($opt, $page, $opt['line_prefix'], $i+1); $line = ''; if ($flddef) { $flds = explode(',', $flddef); wdbg(1,"$func: fld search array"); wdbg(1,$flds); $fldcnt=0; foreach ($flds as $fld) { wdbg(1,"$func: processing fld def >>$fld<<"); $lineflds = explode("$opt[d]", $textrows[$i]); wdbg(1,"$func: fields follow"); wdbg(1,$lineflds); if (strstr($fld, '-')) { wdbg(1,"$func: working with a range: $fld"); if (!preg_match("/(\d+)-(\d*)/", $fld, $startend)) { wshStdErr($pagename, $opt, "ERROR: $func: Non-matching fld def $fld"); } else { wdbg(1,"$func: fld range from $startend[1] to $startend[2]"); if ($startend[2]) // x-y for ($j = $startend[1]-1; $j < $startend[2]; $j++) { $line .= (($fldcnt>0)?$opt['ofs']:'') . $lineflds[$j]; $fldcnt++; } else // x- (to end) for ($j = $startend[1]-1; $j < sizeof($lineflds); $j++) { $line .= (($fldcnt>0)?$opt['ofs']:'') . $lineflds[$j]; $fldcnt++; } } } else { wdbg(1,"$func: simple fld#=$fld, fld=".$lineflds[$fld-1]); $line .= (($fldcnt>0)?$opt['ofs']:'') . $lineflds[$fld-1]; $fldcnt++; } } } if ($chardef) { $chars = explode(',', $chardef); wdbg(1,"$func: char search array"); wdbg(1,$chars); foreach ($chars as $char) { wdbg(1,"$func: processing char def >>$char<<"); if (strstr($char, '-')) { wdbg(1,"$func: working with a range: $char"); if (!preg_match("/(\d+)-(\d*)/", $char, $startend)) { wshStdErr($pagename, $opt, "ERROR: $func: Non-matching char def $char"); } else { wdbg(1,"$func: range from $startend[1] to $startend[2]"); if ($startend[2]) // x-y $line .= substr($textrows[$i], $startend[1]-1, ($startend[2]-$startend[1]+1)); else // x- (to end) $line .= substr($textrows[$i], $startend[1]-1); } } else { $line .= substr($textrows[$i], $char-1, 1); } } } wdbg(1,"$func: >>$textrows[$i]<< became >>$line<<"); $textrows[$i] = $line_prefix . $line . $line_suffix; } $newrows = array_merge($newrows, $array_prefix, $textrows); } $WikiShVars['STATUS'] = 0; return (wshPostProcess($pagename, $opt, $newrows, $page)); } $MarkupExpr["${WikiShMXPrefix}diff"] = 'wshDiff($pagename, @$argp, @$args)'; function wshDiff($pagename, $opt, $args) { global $WikiShVars, $DiffFunction; if (wshNotNow($pagename)) return(''); $func = "Diff()"; wdbg(4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $args); wshSDOpt($opt, 'q', false); wshExpandWildCards($pagename, $opt, $args); if (sizeof($args) != 2) { wshStdErr($pagename, $opt, "ERROR: $func: Must specify exactly 2 pagenames."); $WikiShVars['STATUS'] = 2; return(''); } if (!($p1 = wshReadPage($pagename, $opt, $args[0]))) { wshStdErr($pagename, $opt, "ERROR: $func: Unable to read page=$args[0]."); $WikiShVars['STATUS'] = 2; return(''); } if (!($p2 = wshReadPage($pagename, $opt, $args[1]))) { wshStdErr($pagename, $opt, "ERROR: $func: Unable to read page=$args[1]."); $WikiShVars['STATUS'] = 2; return(''); } $diff_out = $DiffFunction($p1['text']."\n", $p2['text']."\n"); if ($diff_out) $WikiShVars['STATUS'] = 1; else $WikiShVars['STATUS'] = 0; if ($opt['q']) return(''); else { $diff_tmp = explode("\n", $diff_out); return (wshPostProcess($pagename, $opt, $diff_tmp)); } } # Dirname() # OPTION: # --raw -- simply apply the PHP function dirname() to the input # Normally apply basename() to text files and PageVar(..., $Group) to all others $MarkupExpr["${WikiShMXPrefix}dirname"] = 'wshDirname($pagename, @$argp, @$args)'; function wshDirname($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); WikiShCompatible($pagename,$opt,$args); // handle wshInitOpts & wshExpandWildCards if (@$opt['raw'] or wshIsATextFile('', $args[0])) { if (wshIsATextFile('', $args[0])) $fn = substr($args[0], strlen(TEXTFILEID)); else $fn = $args[0]; $group = dirname($fn); } else $group = PageVar($args[0], '$Group'); if ($group) $WikiShVars['STATUS'] = 0; else $WikiShVars['STATUS'] = 1; return ($group); } # {(echo OPTIONS ARGS)} # Print arguments $MarkupExpr["${WikiShMXPrefix}echo"] = 'wshEcho($pagename, @$argp, @$args)'; function wshEcho($pagename, $opt, $args) { global $WikiShVars, $EnableWikiShRawEcho; if (wshNotNow($pagename)) return(''); $func = "Echo()"; wdbg(4,"$func: Entering: " . implode(" ", $args)); wdbg(1,$args); wshInitOpts($pagename, '', $opt, $args); # If I expand wildcards that enables me to use wildcards which is nice, # but there's no way to suppress it because quotes are stripped in # ParseArgs()... Better to have an option for it. if ($opt['w']) wshExpandWildCards($pagename, $opt, $args, true, true); wdbg(1,$args); $out = ''; foreach ($args as $arg) { $out .= (($out) ? ' ' : '') . $arg; } $out = str_replace("\\t", "\t", $out); $out = explode("\\n", $out); $WikiShVars['STATUS'] = 0; if ($EnableWikiShRawEcho && $opt['raw']) echo print_r($out, true) . "
\n"; else return (wshPostProcess($pagename, $opt, $out, false)); } # {(false)} # return nothing. Set $STATUS to non-zero. $MarkupExpr["${WikiShMXPrefix}false"] = 'wshFalse($pagename, @$argp, @$args)'; function wshFalse($pagename, $opt, $args) { global $WikiShVars; wshNotNow($pagename); // just for RC initializations - don't care about return val $func = "False()"; wdbg(4,"$func: Entering"); $WikiShVars['STATUS'] = 1; return(''); } # # {(fetchmail OPTIONS)} # OPTIONS # --close or --end (nullifies any other options) # --next (default) # --stay (allow setting other values without going to next) # --from:fromvar # --to:tovar # --cc:ccvar # --date:datevar # --subject:subjectvar # --body:bodyvar $MarkupExpr["${WikiShMXPrefix}fetchmail"] = 'wshFetchMail($pagename, @$argp, @$args)'; function wshFetchMail($pagename, $opt, $args) { global $WikiShVars, $WikiMailErr, $EnableWikiShFetchmail; static $OpenProfiles = array(), $msg = null; if (!$EnableWikiShFetchmail) { wshStdErr($pagename, $opt, "ERROR: Fetchmail is not enabled. See \$EnableWikiShFetchmail"); $WikiShVars['STATUS'] = 5; return(''); } if (!defined('WikiMail')) { $msg = "ERROR: fetchmail depends on missing recipe wikimail."; echo "$msg"."
\n"; $WikiShVars['STATUS'] = 5; return($msg); } if (wshNotNow($pagename)) return(''); $func="FetchMail()"; $d = 1; wdbg($d*4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $args); SDV($opt['profile'], 'default'); if ($opt['close'] || $opt['end']) { if (wmClosePOP3($opt['profile']) && !$WikiMailErr) $WikiShVars['STATUS'] = 0; else { wshStdErr($pagename, $opt, "ERROR: $func: Error from wikimail: $WikiMailErr"); $WikiShVars['STATUS'] = 1; } unset($OpenProfiles[$opt['profile']]); return(''); } # If we haven't initialized this profile before then do so now if (!$OpenProfiles[$opt['profile']]) { SDV($opt['host'], 'default'); SDV($opt['user'], 'default'); SDV($opt['pass'], 'default'); wmMkProfilePOP3($opt['profile'], $opt['host'], $opt['user'], $opt['pass']); if (!wmInitPOP3($opt['profile'])) { wshStdErr($pagename, $opt, "ERROR: $func: wikimail(init) error \"$WikiMailErr\""); $WikiShVars['STATUS'] = 1; return(''); } #register_shutdown_function('wmClosePOP3($GLOBAL["wshPop3"])'); $OpenProfiles[$opt['profile']] = true; } if ($opt['stay'] && @$opt['delete'] && wmDeletePOP3($opt['profile'])) { wshStdErr($pagename, $opt, "ERROR: $func: wikimail(del) error \"$WikiMailErr\""); $WikiShVars['STATUS'] = 1; return(''); } SDV($opt['delete'], true); if (($opt['stay'] && $msg) || ($msg = wmRetrievePOP3($opt['profile'], $opt['delete']))) { list($headhash, $headers, $body) = $msg; foreach (array('from', 'to', 'cc', 'date', 'subject') as $i) if ($opt[$i]) $WikiShVars[$opt[$i]] = $headhash[$i]; if ($opt['body']) $WikiShVars[$opt['body']] = implode("\n", $body); if ($opt['headers']) $WikiShVars[$opt['headers']] = $headers; if ($WikiMailErr) $WikiShVars['STATUS'] = 2; else $WikiShVars['STATUS'] = 0; return(''); } else { $WikiShVars['STATUS'] = 1; return(''); } } # # {(grep OPTIONS PATTERN PAGEPATTERN ...)} # OPTIONS # -v - reVerse the meaning - return lines NOT matching # -i - make matching case-insensitivee (--ic and --ignorecase synonyms) # -l - return ONLY filenames, not lines # -F --fixed-strings - interpret PATTERN as fixed string instead of regex # --highlight-highlight the matching text # --targets - search "targets" instead of text # REGEX - display lines matching regex pattern # PAGEPATTERN - source pages from PageName or Group.Pagename # allowing wiki wildcards * and ? $MarkupExpr["${WikiShMXPrefix}grep"] = 'wshGrep($pagename, @$argp, @$args)'; function wshGrep($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func="Grep()"; wdbg(4,"$func: Entering"); $WikiShVars['STATUS'] = 1; // assume we find nothing wshInitOpts($pagename, '', $opt, $args); $expr = array_shift($args); wshExpandWildCards($pagename, $opt, $args, false, false, false); wshSDOpt($opt, 'file_prefix', ''); wshSDOpt($opt, 'line_prefix', ''); wshSDOpt($opt, 'line_suffix', ''); wshSDOpt($opt, 'highlight', false); wshSDOpt($opt, 'fixed-strings', false); if (@$opt['F']) $opt['fixed-strings'] = true; wshSDOpt($opt, 'q', false); wdbg(1,"$func: Expr=$expr"); if ($expr == '' || sizeof($args) == 0) { $WikiShVars['STATUS'] = 2; return ''; } if (!isset($opt['files-with-matches'])) $opt['files-with-matches'] = @$opt['l']; if (!isset($opt['line-number'])) $opt['line-number'] = @$opt['n']; $reverse = (@$opt['v'] || @$opt['reverse']); $regexopt = (@$opt['i'] || @$opt['ignorecase'] || @$opt['ic']) ? 'i' : ''; if ((sizeof($args) > 1 || (isset($opt['H']) || isset($opt['with-filename']))) && !isset($opt['h']) && !isset($opt['no-filename']) && strpos(@$opt['line_prefix'], 'PAGENAME') === FALSE) $opt['line_prefix'] = 'PAGENAME:'.@$opt['line_prefix']; $newrows = array(); foreach ($args as $filename) { $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: No such page: $page[filename]"); continue; } if ($opt['targets']) $page['text'] = str_replace(",", "\n", $page['targets']); wdbg(2,"$func: Processing: filename=$filename"); wdbg(1,"$func: Text=$page[text]"); $line_prefix = wshReplace($opt, $page, $opt['line_prefix']); $line_suffix = wshReplace($opt, $page, $opt['line_suffix']); $file_prefix = wshReplace($opt, $page, $opt['file_prefix']); # Optimize - if we're only interested in yes/no on a match for the whole # file then just figure that out and get out... if ($opt['files-with-matches']) { // -l if (($opt['fixed-strings'] && strstr($page['text'], $expr)) || (!$opt['fixed-strings'] && preg_match("/$expr/m$regexopt", $page['text']))) { if (!$reverse) { if ($file_prefix) $newrows[] = $file_prefix; $newrows[] = $line_prefix . $page['filename'] . $line_suffix; $WikiShVars['STATUS'] = 0; // indicates we found something } } elseif ($reverse) { if ($file_prefix) $newrows[] = $file_prefix; $newrows[] = $line_prefix . $page['filename'] . $line_suffix; $WikiShVars['STATUS'] = 0; // indicates we found something } continue; } # OK, if I get here then we're not interested in files-with-matches # so start looking at each individual line. $textrows = explode("\n", $page['text']); wdbg(1,"$func: " . sizeof($textrows) . " lines to process..."); wdbg(1,"$func: First Line: $textrows[0]"); $str = $textrows[0]; # If we have a regex then it must not contain a / since that is the # delimiter we use. Thus escape it. if (!$opt['fixed-strings']) $expr = preg_replace('/(?>$row<<"); if (!$reverse) { if ($opt['highlight']) { wdbg(0,"$func: Highlighting (expr=$expr, opts=$regexopt)...>>$row<<"); $row = preg_replace("/($expr)/$regexopt", "'''$1'''", $row); wdbg(0,"$func: Highlighted...>>$row<<"); } if ($opt['line-number']) $row = $lineno . ': ' . $row; $newrows[] = $line_prefix . $row . $line_suffix; $WikiShVars['STATUS'] = 0; // indicates we found something } } elseif ($reverse) { wdbg(1,"$func: NOMATCH!($expr) >>$row<<"); if ($opt['line-number']) $row = sprintf("%05d: %s", $lineno, $row); $newrows[] = $line_prefix . $row . $line_suffix; $WikiShVars['STATUS'] = 0; // indicates we found something } } } if ($opt['q']) $newrows = array(); return (wshPostProcess($pagename, $opt, $newrows, $page)); } # {(head OPTIONS PAGEPATTERN)} # Print lines from the beginning of a file $MarkupExpr["${WikiShMXPrefix}head"] = 'wshHead($pagename, @$argp, @$args)'; function wshHead($pagename, $opt, $filelist) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = 'Head()'; wdbg(4,"$func: Entering"); wshInitOpts($pagename, 'n:', $opt, $filelist); wshSDOpt($opt, 'n', 10); $opt['startline'][] = 1; $opt['endline'][] = $opt['n']; $WikiShVars['STATUS'] = 0; return(wshExtractLines($pagename, $opt, $filelist, $func)); } # # {(ls OPTIONS PAGEPATTERN ...)} # OPTIONS # --raw - don't go through pmwiki's .pageindex filter to see what's there, but # look out there and see what files actually exist # --list:x like list=x in pagelist # -a list all files (shortcut for --list:all) # -l long listing (just time & size) # -c use create time for -l listing and for -t sorting # -t sort by time # -S sort by size # -r reverse sort # --nosort don't sort (default is to sort by name) # --auth show auth with -l type of format (separate because it's slow) # PAGEPATTERN - source pages from PageName or Group.Pagename # allowing wiki wildcards * and ? $MarkupExpr["${WikiShMXPrefix}ls"] = 'wshLs($pagename, @$argp, @$args)'; function wshLs($pagename, $opt, $args) { global $WikiShVars, $SearchPatterns, $WikiLibDirs, $EnableWikiShTextRead; global $wshAuthText, $wshAuthPage; if (wshNotNow($pagename)) return(''); $WikiShVars['STATUS'] = 0; // this will be reset if we run into errors $func = "Ls():"; wdbg(4,"$func Entering"); wshInitOpts($pagename, '', $opt, $args); if (!$args) $args[] = '*.*'; if (sizeof($args) == 1 && $args[0] == '*.*' && !@$opt['line_prefix'] && !@$opt['line_suffix'] && !@$opt['file_prefix'] && !@$opt['l']) $QuickOptimize = true; if (!@$opt['list'] && @$opt['a']) $opt['list'] = 'all'; // ls -a == ls --list:all wshExpandWildCards($pagename, $opt, $args, true, false, false); // keep textID # handle --group and --name options (group= and name=) $pnfilter = array(); if (@$opt['group']) $pnfilter[] = FixGlob($opt['group'], '$1$2.*'); if (@$opt['name']) $pnfilter[] = FixGlob($opt['name'], '$1*.$2'); if ($pnfilter) $args = MatchPageNames($args, $pnfilter); if ($opt['l'] || $opt['t'] || $opt['S']) { if (@$opt['g'] || @$opt['G'] || @$opt['p'] || @$opt['P']) { wshStdErr($pagename, $opt, "ERROR: -g, -G, -p, and -P cannot be used in conjunction with -l, -t, or -S."); } foreach ($args as $k => $arg) { if (($opt['OS'] || $opt['os'] || (wshIsATextFile('', $arg)))) { $fileinfo = false; if (wshIsATextFile('', $arg)) { $arg = $args[$k] = substr($arg, strlen(TEXTFILEID)); if ($EnableWikiShTextRead && slAuthorized($arg, $wshAuthText, 'read', true) && file_exists($arg)) $fileinfo = stat($arg); } else { foreach ($WikiLibDirs as $dir) { $pagefile = $dir->pagefile($arg); if (file_exists($pagefile) && $fileinfo = stat($pagefile)) break; } } if (@$fileinfo) { wdbg(1,"$func: FOUND OS FILE $arg -> $pagefile"); if (@$foo < 3) { wdbg(1,$fileinfo); $foo = @$foo+1; } $size[$k] = $fileinfo['size']; $author[$k] = $fileinfo['uid']; #$group[$k] = $fileinfo['gid']; $time[$k]=($opt['c'] ? $fileinfo['ctime'] : $fileinfo['mtime']); } else { wdbg(1,"$func: NOT FOUND OS FILE $arg"); $WikiShVars['STATUS'] = 2; $args[$k] = "FILE NOT FOUND: $arg"; $size[$k] = 0; $author[$k] = 'UnKnOwN'; $time[$k] = 0; } } else { if (PageExists($arg)) $page = wshRetrieveAuthPage($arg, 'read', false); else $page = false; if (!$page) { wdbg(1,"$func: NOT FOUND WIKI PAGE $arg"); $WikiShVars['STATUS'] = 2; $args[$k] = "PAGE NOT FOUND: $arg"; $size[$k] = 0; $author[$k] = 'UnKnOwN'; $time[$k] = 0; } else { wdbg(1,"$func: FOUND WIKI PAGE $arg"); $size[$k] = strlen($page['text']); if ($opt['c']) { $author[$k] = ($page['cauthor']?$page['cauthor']:$page['author']); $time[$k] = $page['ctime']; } else { $author[$k] = $page['author']; $time[$k] = $page['time']; } } } } wdbg(1,"$func: array sizes - file:" . sizeof($args) . " size:" . sizeof($size) . " author:" . sizeof($author) . " time:" . sizeof($time)); } else { foreach ($args as $k => $arg) { if (wshIsATextFile('', $arg)) { $args[$k] = $arg = substr($arg, strlen(TEXTFILEID)); if (!$EnableWikiShTextRead || !slAuthorized($arg, $wshAuthText, 'read', true) || !file_exists($arg)) $args[$k] = "FILE NOT FOUND: $arg"; } elseif (wshIsABadFile('', $arg) || !PageExists($arg)) { $WikiShVars['STATUS'] = 2; $args[$k] = "PAGE NOT FOUND: $arg"; } elseif ($opt['g'] || $opt['G']) { if ($pos = strpos($arg, '.')) $args[$k] = substr($arg, 0, $pos); } elseif ($opt['p'] || $opt['P']) { if ($pos = strpos($arg, '.')) $args[$k] = substr($arg, $pos+1); } } } if (($opt['P'] || $opt['G']) && $args) $args = array_unique($args); # # handle sorting # if ($opt['sort'] !== false) { if (!$opt['t'] && !$opt['S'] && !$opt['l']) { if ($opt['r']) rsort($args); else sort($args); } else { if ($opt['t']) { wdbg(1,"$func: Sorting by time"); $k1 = &$time; $k2 = &$args; $k3 = &$size; $k4 = &$author; $k1a= SORT_NUMERIC; $k2a=SORT_STRING; $k1b = ($opt['r']?SORT_ASC:SORT_DESC); } elseif ($opt['S']) { wdbg(1,"$func: Sorting by size"); $k1 = &$size; $k2 = &$args; $k3 = &$time; $k4 = &$author; $k1a= SORT_NUMERIC; $k2a=SORT_STRING; $k1b = ($opt['r']?SORT_ASC:SORT_DESC); } else { wdbg(1,"$func: Sorting by name"); $k1 = &$args; $k2 = &$size; $k3 = &$time; $k4 = &$author; $k1a= SORT_STRING; $k2a=SORT_NUMERIC; $k1b = ($opt['r']?SORT_DESC:SORT_ASC); } array_multisort($k1, $k1a, $k1b, $k2, $k2a, $k3, $k4); } } if ($QuickOptimize) { // no need to check for existence $newrows = $args; } else { $newrows = array(); foreach ($args as $k => $filename) { wdbg(1,"$func processing filename=$filename"); $page['filename'] = $filename; $line_prefix = wshReplace($opt, $page, $opt['line_prefix']); $line_suffix = wshReplace($opt, $page, $opt['line_suffix']); $file_prefix = wshReplace($opt, $page, $opt['file_prefix']); if ($file_prefix) $newrows[] = $file_prefix; if ($opt['l']) { $newrows[] = $line_prefix . sprintf("%15.15s%8d %s %s", $author[$k], $size[$k], strftime("%b %d %H:%M", $time[$k]), $filename) . $line_suffix; } else { $newrows[] = $line_prefix . $filename . $line_suffix; } } } return (wshPostProcess($pagename, $opt, $newrows, $page)); } # {(mv OPTIONS PAGEPATTERN)} # Move pages from one name to another. Implemented via cp followed by rm. # Options: # -f file will be FULLY removed # if multiple source files are specified then the target file must be a # group/directory. $MarkupExpr["${WikiShMXPrefix}mv"] = 'wshMv($pagename, @$argp, @$args)'; function wshMv($pagename, $opt, $filelist) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = "Mv()"; wdbg(4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $filelist); wshCp($pagename, $opt, $filelist); if ($WikiShVars['STATUS'] != 0) { wshStdErr($pagename, $opt, "ERROR: $func: NOT removing source files. Errors creating destination files."); return(''); } $Target = array_pop($filelist); // get rid of the destination wshRm($pagename, $opt, $filelist); #$WikiShVars['STATUS'] = 0; // leave $WikiShVars['STATUS'] from wshRm(). return(''); } # {(null)} # Return nothing, regardless of options, arguments, etc. $MarkupExpr["${WikiShMXPrefix}null"] = 'wshNull($pagename, @$argp, @$args)'; function wshNull($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = "Null()"; wdbg(4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $args); // this is if someone wants to set debug or etc $WikiShVars['STATUS'] = 0; return(''); } # {(od)} # Return a representation of the string with special characters labeled # instead of actually output. Loosely related to the od command $MarkupExpr["${WikiShMXPrefix}od"] = 'wshOd($pagename, @$argp, @$args)'; function wshOd($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = "Od()"; wdbg(4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $args); // this is if someone wants to set debug or etc $WikiShVars['STATUS'] = 0; $rtn = array(); foreach ($args as $arg) $rtn[] = wshDbgOd($arg, true); return(wshPostProcess($pagename, $opt, $rtn)); } # {(read OPTIONS VAR1 [VAR2...] INPUTSPEC)} $MarkupExpr["${WikiShMXPrefix}read"] = 'wshRead($pagename, @$argp, @$args)'; function wshRead($pagename, $opt, $args) { global $WikiShVars, $WikiShPipeActive, $WikiShPipeText; static $ReadSet; # $ReadSet: # vars 0..n (each variable) # text 0..n (each line of text) # next (index to next line in text) # IFS (Input Field Separator - a regex to be applied to each line) if (wshNotNow($pagename)) return(''); $func = "Read()"; wdbg(4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $args, true, false); # $set represents a certain file to read and the associated pointers # it becomes the index into $ReadSet[]. if ($opt['set']) $set = $opt['set']; else $set = 0; wdbg(1,"$func: Readset[set=$set]"); wdbg(1,"$func: opt[next]=" . ($opt[next]?"TRUE":"FALSE")); # Handle --clear and get out if that was set if (@$opt['clear']) { unset($ReadSet[$set]); return(''); // we can't really do anything else if we clear it } # unset all vars in $args in case we have to get out early foreach ($args as $var) $WikiShVars[$var] = ''; # # --restoreset # if ($opt['restoreset']) { if(!session_id()) session_start(); wdbg(2,"$func: Restoring from _SESSION"); if (!isset($_SESSION['WIKISH_READ'][$set])) $WikiShVars['STATUS'] = 3; // no error message, but set a status $ReadSet[$set] = $_SESSION['WIKISH_READ'][$set]; wdbg(2,"$func: restored idx=".$ReadSet[$set]['curline']); wdbg(1,"$func: restored text (array) follows"); wdbg(1,$ReadSet[$set]['text']); # If you're not doing anything else go ahead and get out - typical # restoration at the start of a script if (!@$opt['prev'] && !@$opt['next'] && !@$opt['goto'] && !@$opt['rewind'] && !@$opt['linecount'] && !@$opt['status'] && !@$opt['initialize']) { return(''); } } if (@$opt['initialize']) unset($ReadSet[$set]); # The default action is --next:1, but there are a lot of cases in which # we don't make that the default... if (!@$opt['prev'] && !isset($opt['next']) && !@$opt['goto'] && !@$opt['rewind'] && !@$opt['linecount'] && !@$opt['status'] && !@$opt['initialize'] && isset($ReadSet[$set])) $opt['next'] = 1; # # --clearset # if (@$opt['clearset']) { if(!session_id()) session_start(); wdbg(2,"$func: Clearing set=$set from _SESSION"); unset($_SESSION['WIKISH_READ'][$set]); # If you're not doing anything else go ahead and get out - typical # clearing up at the start of a script if (!@$opt['prev'] && !@$opt['next'] && !@$opt['goto'] && !@$opt['rewind'] && !@$opt['linecount'] && !@$opt['status'] && !@$opt['initialize']) { return(''); } } # # --initialize (implicit if not already set) # if (!isset($ReadSet[$set]) && $opt['initialize'] !== false) { wdbg(4,"$func: Initializing"); if (!isset($opt['stdin']) && !$WikiShPipeActive) { # If you're just setting status or linecount then blank is # a valid return - missing stdin is OK if (@$opt['status'] || @$opt['linecount']) { return (wshPostProcess($pagename, $opt, array(''))); } wdbg(4,"$func: stdin not set. exiting with error"); wshStdErr($pagename, $opt, "ERROR: $func: stdin not set"); $WikiShVars['STATUS'] = 2; return(''); } if ($WikiShPipeActive) { wdbg(2,"$func: Reading from pipe"); $page['text'] = $WikiShPipeText; $WikiShPipeText = $WikiShPipeActive = false; } else { wdbg(2,"$func: Reading from stdin=$opt[stdin]"); if (!wshIsAWikiPage('', $opt['stdin']) && !wshIsATextFile('', $opt['stdin'])) $opt['stdin'] = WIKIPAGEID . $opt['stdin']; $page = wshReadPage($pagename, $opt, $opt['stdin']); } wdbg(2,"$func: Read text=>>$page[text]<<"); if (strstr($page['text'], "\r")) $ReadSet[$set]['text'] = explode("\r\n", $page['text']); else $ReadSet[$set]['text'] = explode("\n", $page['text']); wdbg(1,"$func: Exploded text array follows"); wdbg(1,$ReadSet[$set]['text']); $ReadSet[$set]['curline'] = 0; if (isset($opt['IFS'])) $ReadSet[$set]['IFS'] = $opt['IFS']; else $ReadSet[$set]['IFS'] = '\s'; wdbg(1,"$func: ReadSet variable follows (set=$set):"); wdbg(1,$ReadSet); } # # --next:X # if ($opt['next']) { wdbg(2,"$func: Moving index (".$ReadSet[$set]['curline'].") NEXT=$opt[next]"); if ($opt['next'] === true) $opt['next'] = 1; if (is_numeric($opt['next'])) $ReadSet[$set]['curline'] += $opt['next']; else { $found = false; for ($i = $ReadSet[$set]['curline']+1; $i <= sizeof($ReadSet[$set]['text']); $i++) { if ($opt['next'][0] == '/') { if (preg_match($opt['next'], $ReadSet[$set]['text'][$i])) { $ReadSet[$set]['curline'] = $i; $found = true; break; } } else { if (strstr($ReadSet[$set]['text'][$i], $opt['next'])) { $ReadSet[$set]['curline'] = $i; $found = true; break; } } } if (!$found) { $ReadSet[$set]['curline'] = sizeof($ReadSet[$set]['text'])+1; } } } # # --prev:X # if ($opt['prev']) { wdbg(2,"$func: Moving index (".$ReadSet[$set]['curline'].") PREV=$opt[prev]"); if ($opt['prev'] === true) $opt['prev'] = 1; if (is_numeric($opt['prev'])) $ReadSet[$set]['curline'] -= $opt['prev']; else { $found = false; for ($i = $ReadSet[$set]['curline']-1; $i >= 0; $i--) { wdbg(2,"$func: Checking idx=$i, line=".$ReadSet[$set]['text'][$i]); if ($opt['prev'][0] == '/') { if (preg_match($opt['prev'], $ReadSet[$set]['text'][$i])) { wdbg(2,"$func: regex success"); $ReadSet[$set]['curline'] = $i; $found = true; break; } } else { if (strstr($ReadSet[$set]['text'][$i], $opt['prev'])) { wdbg(2,"$func: fixed string success"); $ReadSet[$set]['curline'] = $i; $found = true; break; } } } if (!$found) { wdbg(2,"$func: not found"); $ReadSet[$set]['curline'] = -1; } } } # # --rewind (implemented as --goto:0) # --goto:X # if (@$opt['rewind']) $opt['goto'] = 0; if (@$opt['goto']) { wdbg(2,"$func: Moving index (".$ReadSet[$set]['curline'].") GOTO=$opt[goto]"); $ReadSet[$set]['curline'] = $opt['goto']; } $WikiShVars['STATUS'] = 0; // assume we're OK unless reset below wdbg(2,"$func: After move curline=".$ReadSet[$set]['curline']); # # check for EOF # if ($ReadSet[$set]['curline'] >= sizeof($ReadSet[$set]['text'])) { if (@$opt['clear'] === false) // --noclear $ReadSet[$set]['curline'] = sizeof($ReadSet[$set]['text']); else unset($ReadSet[$set]); wdbg(4,"$func: returning EOF (idx=".$ReadSet[$set]['curline'].")"); $WikiShVars['STATUS'] = 2; // status=2 -> EOF } # # check for BOF # if ($ReadSet[$set]['curline'] < 0) { if (@$opt['clear'] ===false) // --noclear $ReadSet[$set]['curline'] = -1; else unset($ReadSet[$set]); wdbg(4,"$func: returning BOF (idx=".$ReadSet[$set]['curline'].")"); $WikiShVars['STATUS'] = 1; // status=1 -> BOF } wdbg(2,"$func: After check EOF/BOF curline=".$ReadSet[$set]['curline']); # # --saveset # if ($opt['saveset']) { if(!session_id()) session_start(); wdbg(2,"$func: Saving set=$set to _SESSION (idx=".$ReadSet[$set]['curline'].")"); $_SESSION['WIKISH_READ'][$set] = $ReadSet[$set]; } # Get out if there was a BOF or EOF condition if ($WikiShVars['STATUS']) return(''); // this happens after --saveset wdbg(2,"$func: After move line=".$ReadSet[$set]['text'][$ReadSet[$set]['curline']]); # # --linecount # (note that if $ReadSet[$set] was not set it was probably handled # already, up in the --initialize section # if (@$opt['linecount']) { if (isset($ReadSet[$set])) return (wshPostProcess($pagename, $opt, array(sizeof($ReadSet[$set]['text'])))); else return (wshPostProcess($pagename, $opt, array(''))); // null set } # # --status # (note that if $ReadSet[$set] was not set it was probably handled # already, up in the --initialize section # if (@$opt['status']) { if (isset($ReadSet[$set])) return (wshPostProcess($pagename, $opt, array($ReadSet[$set]['curline']))); else return (wshPostProcess($pagename, $opt, array(''))); // null set } # # --stdout # if ($opt['stdout']) { return (wshPostProcess($pagename, $opt, array($ReadSet[$set]['text'][$ReadSet[$set]['curline']]))); } elseif ($args) { # # normal setting of vars # $idx = $ReadSet[$set]['curline']; wdbg(3,"$func: Line to split (idx=$idx): >>" . $ReadSet[$set]['text'][$idx] . "<<"); $fields = preg_split("/" . $ReadSet[$set]['IFS'] . "/", $ReadSet[$set]['text'][$idx], -1, PREG_SPLIT_OFFSET_CAPTURE); # Assign variables 0..n-1 if (sizeof($args) > 1) { for ($i = 0; $i < sizeof($args)-1; $i++) { wdbg(1,"$func: Setting ".$args[$i]." to ".$fields[$i][0]); $WikiShVars[$args[$i]] = $fields[$i][0]; } } else $i = 0; # Assign the final variable with all the remaining values (or # nothing if all text is already used up if (sizeof($fields) >= sizeof($args)) $leftover=substr($ReadSet[$set]['text'][$idx], $fields[$i][1]); else $leftover=''; wdbg(1,"$func: Setting final var to >>$leftover<<"); $WikiShVars[$args[$i]] = $leftover; } $WikiShVars['STATUS'] = 0; wdbg(4,"$func: returning next line ($idx): " . $ReadSet[$set]['text'][$idx]); return(''); } # {(rm OPTIONS PAGEPATTERN[s])} # Remove (delete) files matching page patterns. "Delete" is a relative term # since most page deletions in pmwiki are actually a renaming, but you get # the idea. # OPTIONS # -f This will cause an actual deletion (unlink()) of the file after it # has been deleted in the normal fashion. Danger, Will Robinson! # --trial (non-standard from shell perspective) debuG the delete - just list # the files that WOULD have been deleted without doing any deletion # $MarkupExpr["${WikiShMXPrefix}rm"] = 'wshRm($pagename, @$argp, @$args)'; function wshRm($pagename, $opt, $args) { global $DeleteKeyPattern, $DeleteKey; global $EnableWikiShRemove, $EnableWikiShRemoveFully,$EnableWikiShTextWrite; global $WorkDir, $WikiShVars, $wshAuthText; if (wshNotNow($pagename)) return(''); $func = "rm()"; SDV($EnableWikiShRemove, false); SDV($EnableWikiShRemoveFully, false); wdbg(4,"$func: Entering"); if (!$EnableWikiShRemove) { wshStdErr($pagename, $opt, "ERROR: $func: Unable to remove files. Not enabled."); $WikiShVars['STATUS'] = 4; return (''); } SDV($DeleteKey, 'delete'); if ($DeleteKeyPattern && !preg_match("/$DeleteKeyPattern/", $DeleteKey)) { wshStdErr($pagename, $opt, "ERROR: $func: DeleteKey ($DeleteKey) does not match \$DeleteKeyPattern. Unable to delete."); $WikiShVars['STATUS'] = 4; return (''); } wshInitOpts($pagename, '', $opt, $args); wshExpandWildCards($pagename, $opt, $args); wshSDOpt($opt, 'f', false); wshSDOpt($opt, 'trial', false); $newrows = array(); foreach ($args as $filename) { wdbg(1,"$func: Processing $filename"); $page = wshReadPage($pagename, $opt, $filename); if ($page['type'] == 'bad') continue; // probably didn't exist wdbg(1,"$func: Read Page: $filename (type=".$page['type'].')'); if ($page[filename] == '-' && $page['type'] == 'inline') { wshStdErr($pagename, $opt, "ERROR: $func: File not found: $filename"); continue; } if ($opt['trial']) { wdbg(1,"$func: Debug-deleting $page[filename]"); $newrows[] = $page['filename']; } else { if ($page['type'] == 'wiki') { if (!wshWrite($pagename, $opt, $page['filename'], $page['type'], $DeleteKey, $page, 'delete')) { $WikiShVars['STATUS'] = 2; return(''); } $newrows[] = 'Deleted: ' . $page['filename']; #wdbg(2,"TESTING GLOB()"); wdbg(1,glob("$WorkDir/*.*")); if ($opt['f']) { if (!$EnableWikiShRemoveFully) { wshStdErr($pagename, $opt, "ERROR: $func: Unable to remove file fully. Not enabled."); $WikiShVars['STATUS'] = 4; return(''); } wdbg(1,"$func: Permanently deleting $page[filename] by glob()"); foreach(glob("$WorkDir/$page[filename],del-*") as $File2Del) { $newrows[] = "Permanently Deleted: $page[filename] ($File2Del)"; wdbg(1,"$func: Permanently deleting file=$File2Del"); unlink($File2Del); } } } elseif ($page['type'] == 'text') { if (!$EnableWikiShTextWrite) { wshStdErr($pagename, $opt, "ERROR: $func: Unable to remove text files. Not enabled."); $WikiShVars['STATUS'] = 4; return(''); } if (!slAuthorized($page['filename'], $wshAuthText, 'delete', true)) { wshStdErr($pagename, $opt, "ERROR: $func: Unable to remove $page[filename]. Not (SecLayer) authorized."); $WikiShVars['STATUS'] = 4; return(''); } $newrows[] = "Deleted textfile: $page[filename]"; wdbg(1,"$func: Deleting textfile=$page[filename]"); unlink($page['filename']); } elseif ($page['type'] == 'session') { wdbg(1,"$func: Deleting (session-type) $page[filename]"); unset($_SESSION[$page['filename']]); } else { wshStdErr($pagename, $opt, "ERROR: Lazy me didn't implement rm() for $page[filename] -> $page[type] type files. Sorry."); } } } $WikiShVars['STATUS'] = 0; return (wshPostProcess($pagename, $opt, $newrows, $page)); } # {(sed OPTIONS PAGEPATTERN)} # Although sed is a HUGELY powerful tool, only very small pieces of its # functionality have been implemented here, hopefully the most commonly # used portions... # sed -n 'x,yp' # This will print from line x to line y (y can be $ for end-of-file) # sed 's/search/replace/' # This will do a simple search/replace where the search can be regex and # the replace can contain such "magic" as is supported by preg_replace() # $MarkupExpr["${WikiShMXPrefix}sed"] = 'wshSed($pagename, @$argp, @$args)'; function wshSed($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = "Sed()"; wdbg(4,"$func: Entering"); # Within wshInitOpts and then wshExpandVars the call to FmtPagename causes the # $p from '1,$p' to be sub'd. So I've got to get rid of it and then get # it back after the wshInitOpts call. $args = str_replace('$p', '$p', $args); // '1,$p' sub problem wshInitOpts($pagename, 'e:', $opt, $args); if (@$opt['i']) { $opt['inplaceedit'] = $opt['i']; unset($opt['i']); // wshExtractLines takes -i as ignore case } if (!isset($opt['e'])) { wdbg(1,"$func: Taking 1st arg since no opt[e]"); $opt['e'][] = array_shift($args); } if (!is_array($opt['e'])) { wdbg(1,"$func: Converting opt[e] into array"); $opt['e'][] = $opt['e']; } $opt['e'] = str_replace('$p', '$p', $opt['e']); // '1,$p' sub problem wdbg(2,"$func: opt[e]:"); wdbg(2,$opt['e']); foreach ($opt['e'] as $k => $e) wdbg(2, "$k => " . wshDbgOd($e)); // Now $opt['e'] is definitely an array holding our expressions... foreach ($opt['e'] as $expr) { wdbg(1,"$func: expr=" . wshDbgOd($expr)); if (preg_match("/^(\d+|\/[^\/]+\/[iADUX]*),(\d+|\\$|\/[^\/]+\/[iADUX]*)p/", $expr, $m)) { wdbg(1,"$func: Matched start/end"); $opt['startline'][] = $m[1]; $opt['endline'][] = $m[2]; } elseif (preg_match("/^s((.)(?:[^\\2]|\\\\\\2)+\\2)((?:[^\\2]|\\\\\\2)*)\\2([gFiADUXu]*)$/", $expr, $m)) { wdbg(1,"$func: Matched Find/Replace ($m[1] <--> $m[3] / $m[4])"); $m[1] = str_replace('\n', "\n", $m[1]); // allow newlines in find if (strstr($m[4], 'F')) { $opt['find'][] = substr($m[1], 1, -1); if (!strstr($m[4], 'g')) wshStdErr($pagename, $opt, "$func: ERROR: F flag requires g flag. Continuing as if g was specified."); } else $opt['find'][] = $m[1] . str_replace(array('F', 'g'), array('', ''), $m[4]); $m[3] = str_replace('\n', "\n", $m[3]); // allow newlines in replace $opt['repl'][] = $m[3]; if (strstr($m[4], 'g')) $opt['replcnt'][] = -1; else $opt['replcnt'][] = 1; $opt['flag'][] = $m[4]; } else { wdbg(1,"$func: Unmatched regex for expression $expr"); wshStdErr($pagename, $opt, "$func: ERROR: Unknown expression: >>$expr<<"); } } wdbg(1,"$func: Starts:"); wdbg(1,$opt['startline']); wdbg(1,"$func: Ends:"); wdbg(1,$opt['endline']); wdbg(1,"$func: Find:"); wdbg(1,$opt['find']); wdbg(1,"$func: Replace:"); wdbg(1,$opt['repl']); wdbg(1,"$func: Replace#:"); wdbg(1,$opt['replcnt']); wdbg(1,"$func: Flags:"); wdbg(1,$opt['flag']); $opt['printall'] = (isset($opt['n']) && $opt['n']) ? false : true; $WikiShVars['STATUS'] = 0; return(wshExtractLines($pagename, $opt, $args, $func)); } # {(mailx OPTIONS PAGE)} # Send an email # OPTIONS # -t address # --to:address to whom will the message be sent # -c address # --cc:address to whom will the message be CC'd # -b address # --bcc:address to whom will the message be BCC'd # -s SUBJ # --subject:SUBJ specify the subject of the message # -f address # --from:address the message will appear to be from this address $MarkupExpr["mailx"] = 'wshMailx($pagename, @$argp, @$args)'; function wshMailx($pagename, $opt, $args) { global $WikiShVars, $EnableWikiShMailx; if (!$EnableWikiShMailx) { wshStdErr($pagename, $opt, "ERROR: Mailx is not enabled. See \$EnableWikiShMailx"); $WikiShVars['STATUS'] = 5; return(''); } if (wshNotNow($pagename)) return(''); $func = 'wshMailx()'; $d = 0; wdbg($d*4,"$func: Entering: " . implode(" ", $args)); if (!defined('WikiMail')) return('Mailx depends on WikiMail. Aborting. Message NOT sent.'); WikiShCompatible($pagename, $opt, $args, WIKISH_ACTION_OPTIONS|WIKISH_ACTION_VARIABLE|WIKISH_ACTION_WILDCARD, 's:f:t:c:b:'); #Handle aliases s=subject, f=from, t=to, c=cc, b=bcc if ($opt['s']) $opt['subject'] = $opt['s']; if ($opt['f']) $opt['from'] = $opt['f']; if ($opt['t']) $opt['to'] = $opt['t']; if ($opt['c']) $opt['cc'] = $opt['c']; if ($opt['b']) $opt['bcc'] = $opt['b']; #Process CC and BCC for headers $headers = ''; if ($opt['cc']) // possibly an array if WikiSh handled it foreach ((array)$opt['cc'] as $cc) $headers .= "Cc:$cc\r\n"; if ($opt['bcc']) // possibly an array if WikiSh handled it foreach ((array)$opt['bcc'] as $bcc) $headers .= "Bcc:$bcc\r\n"; # Now build up the message from pages $rtn = ''; foreach ($args as $filename) { $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: No such page: $page[filename]"); continue; } $rtn = ($rtn ? $rtn . "\n" : '') . $page['text']; } $rtn = wshPostProcess($pagename, $opt, explode("\n", $rtn)); wdbg($d*3,"$func: Message=$rtn"); if ($x = wmSendMail($pagename, $opt['to'], $opt['subject'], $rtn, $headers, '', $opt['from'], ($opt['html']?'html':'text'), $WikiShVars['MAILXRULES'])) { $WikiShVars['STATUS'] = 1; $rtn = "Errors occurred. Message NOT sent. ($x)"; } else { $WikiShVars['STATUS'] = 0; $rtn = "Message Sent."; } return($opt['q']?'':$rtn); } # {(set OPTIONS VAR ARGS)} # Set $VAR to $ARGS[1] and return the value of whatever it was set to # OPTIONS # --pv Set as a PAGEVAR instead of a WikiSh var (note that PVs are # already interpolated and don't get reprocessed again) # --session Set as a _SESSION variable instead of WikiSh var # --form Set as a $InputValues[] variables instead of WikiSh var # -v Return the value assigned instead of returning nothing # -s Set a string (put quotes around it and allow non-math symbols) #CREDIT NOTE: About half of this function is taken DIRECTLY (verbatim) from #the example given in MarkupExpressionSamples under {(calc)}. I'm not trying #to steal anything -- I'm totally willing to give FULL credit to whomever did #that. $MarkupExpr["${WikiShMXPrefix}set"] = 'wshSet($pagename, @$argp, @$args)'; function wshSet($pagename, $opt, $args) { global $WikiShVars, $InputValues; global $MxCalcMathChars, $MxCalcMathFunctions, $MxCalcMathFunc1; global $FmtPV; if (wshNotNow($pagename)) return(''); $Ops = array("=","+=","-=","*=","/=",".=","%=","<","<=",">",">=","++","--"); $func = "Set()"; wdbg(4,"$func: Entering: setting: " . implode(" ", $args)); wshInitOpts($pagename, '', $opt, $args); if (!in_array($args[1], $Ops)) { wshStdErr($pagename, $opt, "ERROR: $func: Invalid assignment operator \"$args[1]\". No assignment possible. (set " . implode(" ", $args) . ")"); return(''); } wshSDOpt($opt, 'v', false); wshSDOpt($opt, 's', false); SDV($MxCalcMathChars, "/^[-+*,\\/% ()0-9.^=]+$/"); SDV($MxCalcMathFunctions, array( 'pow','sqrt','sin','cos','tan','asin','acos','atan','log', 'max','min','abs','ceil','floor','round','rand','fmod', 'pi','deg2rad','rad2deg', )); SDV($MxCalcMathFunc1, array( 'sqrt','sin','cos','tan','asin','acos','atan','log', 'abs','ceil','floor','round','deg2rad','rad2deg', )); #print_r($args); if (@$opt['session']) { if(!session_id()) session_start(); $v = $_SESSION[$args[0]]; } elseif (@$opt['pv']) $v = $FmtPV['$'.$args[0]]; else $v = $WikiShVars[$args[0]]; wdbg(1,"$func: initial value = " . wshDbgOd($v)); if ($opt['s']) { $op = $args[1]; $expr = str_replace("'", "\\'", implode(" ", array_slice($args, 2))); $expr = $op . " '" . $expr . "'"; wdbg(1,"$func: expr=>>$expr<<"); } else { $expr = implode(" ", array_slice($args, 1)); $str = $expr; foreach($MxCalcMathFunctions as $fn) { $str = str_replace($fn, '', $str); } if ($str!='' && !preg_match($MxCalcMathChars, $str, $m)) { wshStdErr($pagename, $opt, "ERROR: $func: illegal functions ($str) (did you forget -s?)"); $WikiShVars['STATUS'] = 4; return ''; } $expr = str_replace('pi', M_PI, $expr); $expr = preg_replace("/(\-?[\d.]+)\^(\-?[\d.]+)/", "pow($1, $2)", $expr); foreach($MxCalcMathFunc1 as $fn) $expr = preg_replace("/{$fn}\\s*(\-?[\d.]+)/", "{$fn}($1)", $expr); } wdbg(3,"$func: v=$v, eval'ing >>\\$v $expr;<<"); wdbg(2,"$func: expr=" . wshDbgOd($expr)); #echo "\$v $expr;
\n"; eval("\$v $expr;"); wdbg(4,"$func: after eval, v=" . $v); if ($opt['pv']) { if ($opt['s']) $FmtPV['$'.$args[0]] = "'" . $v . "'"; else $FmtPV['$'.$args[0]] = $v; wdbg(2,"$func: after set, FmtPV follows:"); foreach ($FmtPV as $key => $val) wdbg(1,"[" . wshDbgOd($key) . "] => " . wshDbgOd($val)); } elseif (@$opt['form']) { $InputValues[$args[0]] = $v; } elseif (@$opt['session']) { $_SESSION[$args[0]] = $v; wdbg(1,"$func: after set, _SESSION follows:"); wdbg(1,$_SESSION); } else { $WikiShVars[$args[0]] = $v; wdbg(1,"$func: after set, WikiShVars follows:"); wdbg(1,$WikiShVars); } $WikiShVars['STATUS'] = 0; if ($opt['v']) return($v); else return(''); } $MarkupExpr["${WikiShMXPrefix}sleep"] = 'wshSleep($pagename, @$argp, @$args)'; function wshSleep($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = "Sleep()"; wdbg(4,"$func: Entering: args[0]=$args[0]"); wshInitOpts($pagename, '', $opt, $args); $seconds = $args[0]; if (!is_numeric($seconds)) { $WikiShVars['STATUS'] = 4; wshStdErr($pagename, $opt, "ERROR: $func: non-numeric sleep argument: $seconds"); return(''); } usleep(floor($seconds*1000000)); $WikiShVars['STATUS'] = 0; return(''); } # {(sort OPTIONS PAGEPATTERN)} # sort all lines of all files passed # Options: # -tx = make x the field separator # d = dictionary-order # b = ignore-leading-blanks # f = ignore-case (fold) # -k POS1[,POS2][OPTS] # POS = X[.Y] where X is a field (1-based) and Y is a character (1-based) # within that field. POS1 is where the sorting starts and # POS2 is where the sorting stops. # Multiple -k options may be given. # OPTS consisting of d, b, and/or f may be specified for that given key to # override the overall options specified. # PAGEPATTERN... - source pages from PageName or Group.Pagename # allowing wiki wildcards * and ? # (multiple files/patterns allowed) # # UNIQ options # This function is called by wshUniq() and so these options need to be # respected to work in that capacity. Since they are respected a side effect # is that wshSort() can be called directly with this functionality as well # OPTIONS # --ignore-case (as above) # --count (prefix a line with the count of duplicates) # --unique (print only a single copy of each line, discarding dups) # (this is the default operation of uniq) # --duplicated (print only a copy of duplicated lines, discarding unique # ones) # --only-unique (discard any lines which have duplicates. print only lines # which are unique) # --no-sort (don't do the actual sort - just process the uniq/dup opts) # $MarkupExpr["${WikiShMXPrefix}sort"] = 'wshSort($pagename, @$argp, @$args)'; function wshSort($pagename, $opt, $args) { global $WikiShVars; $func = 'sort()'; if (wshNotNow($pagename)) return(''); wdbg(4,"$func: Entering"); wdbg(1,$args); wshInitOpts($pagename, 't:k:', $opt, $args); wshExpandWildCards($pagename, $opt, $args, false, false, false); wshSDOpt($opt, 'file_prefix', ''); wshSDOpt($opt, 'line_prefix', ''); wshSDOpt($opt, 'line_suffix', ''); $general_opt = ''; if (@$opt['d'] || @$opt['dictionary-order']) $general_opt .= 'd'; if (@$opt['b'] || @$opt['ignore-leading-blanks']) $general_opt .= 'b'; if (@$opt['f'] || @$opt['ignore-case']) $general_opt .= 'f'; if (@$opt['u']) $opt['unique'] = true; SDVA($opt, array('unique' => false, 'duplicated' => false, 'only-unique' => false, 'count' => false)); if (isset($opt['k'])) $opt['key'] = $opt['k']; wshSDOpt($opt, 'key', '1.1'); // default to field 1, char 1 if (!is_array($opt['key'])) $opt['key'] = array($opt['key']); $keys = array(); foreach ($opt['key'] as $key) { if (preg_match("/^(\d+)(?:\.(\d+))?(?:,(\d+)(?:\.(\d+))?)?([bdf]*)$/", $key, $match)) { wdbg(1,"$func: Matched key ($key): $match[1].$match[2],$match[3].$match[4] opt=$match[5]"); if (!$match[1]) $match[1] = 0; else $match[1]--; if (!$match[2]) $match[2] = 0; else $match[2]--; // must 0-base it (1st arg substr()) if ($match[3]) $match[3]--; #if ($match[4]) $match[4]--; // don't 0-base it (2nd arg substr()) $keys[] = array($match[1], $match[2], $match[3], $match[4], $match[5]); } else { wshStdErr($pagename, $opt, "$func: ERROR: incorrectly specified key: >>$key<<"); $keys[] = array(0,0,"",""); #what defaults? } } wdbg(1,"$func: list keys"); wdbg(1,$keys); $ofs = CHR(1); // I would use CHR(0) but preg_replace pukes on it if (isset($opt['t'])) $opt['field-separator'] = $opt['t']; wshSDOpt($opt, 'field-separator', '\s+'); $ifs = $opt['field-separator']; $newrows = array(); $to_sort = array(); foreach ($args as $filename) { $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: No such page: $page[filename]"); continue; } wdbg(1,"$func: text read = >>" . wshDbgOd($page['text']) . "<<"); $textrows = explode("\n", $page['text']); wdbg(1,"$func: textrows="); wdbg(1,$textrows); if ($opt['file_prefix']) $array_prefix = array(wshReplace($opt, $page, $opt['file_prefix'])); else $array_prefix = array(); $line_prefix = wshReplace($opt, $page, $opt['line_prefix']); $line_suffix = wshReplace($opt, $page, $opt['line_suffix']); if ($line_prefix || $line_suffix) for ($i = 0; $i < sizeof($textrows); $i++) { if (strstr($opt['line_prefix'], 'LINENO')) $line_prefix = wshReplace($opt, $page, $opt['line_prefix'], $i+1); $textrows[$i] = $line_prefix . $textrows[$i] . $line_suffix; } $newrows = array_merge($newrows, $array_prefix, $textrows); } foreach($newrows as $origstr) { wdbg(1,"$func: Applying sort keys/options to line >>$origstr<<"); $fields = preg_split("/$ifs/", $origstr); $line2sort = ''; wdbg(1,$keys); foreach ($keys as $key) { wdbg(1,"$func: key: $key[0].$key[1],$key[2].$key[3] opts=$key[4]"); $tmp = ''; if ($key[0] === $key[2] && ($key[2] || $key[2] === 0)) { wdbg(1,"$func: one-key optimize..."); if ($key[3]) { $tmp .= substr($fields[$key[0]], $key[1], $key[3]); } else { $tmp .= substr($fields[$key[0]], $key[1]); } } else { wdbg(1,"$func: multi-key full feature..."); $tmp .= substr($fields[$key[0]], $key[1]); $maxidx = ($key[2] || $key[2] === 0) ? $key[2] : sizeof($fields); for ($i = $key[0]+1; $i < $maxidx; $i++) { $tmp .= $ofs . $fields[$i]; } if ($key[2] || $key[2] === 0) { if ($key[3]) { $tmp .= $ofs . substr($fields[$key[2]], 0, $key[3]); } else { $tmp .= $ofs . $fields[$key[2]]; } } } wdbg(1,"$func: After key applied: >>$tmp<<"); # Now we've got the text for this key - now apply options to $tmp if (strpos($general_opt . $key[4], 'd') !== false) $tmp = preg_replace("/[^${ofs}A-Za-z0-9 \t]/", "", $tmp); if (strpos($general_opt . $key[4], 'b') !== false) $tmp = preg_replace("/^[ \t]*/", "", $tmp); if (strpos($general_opt . $key[4], 'f') !== false) { wdbg(0,"$func: strtolowering..."); $tmp = strtolower($tmp); } wdbg(1,"$func: After opts applied: >>$tmp<<"); # Now add $tmp onto the end of line2sort and go try the next key $line2sort .= $ofs . $tmp; } wdbg(1,"$func: Final 'line' to sort >>$line2sort<<"); $to_sort[] = array($line2sort, $origstr); } if (!@$opt['no-sort']) { wdbg(0,"$func: array to sort:"); wdbg(0,$to_sort); $WikiShVars['STATUS'] = 0; if (@$opt['r'] || @$opt['reverse']) { if (!rsort($to_sort)) { $WikiShVars['STATUS'] = 4; return(''); } } else { if (!sort($to_sort)) { $WikiShVars['STATUS'] = 4; return(''); } } } # # Now implement --unique, --duplicated, --count, --only-unique # --duplicated and --only-unique are obviously mutually exclusive # --duplicated and --only-unique over-ride --unique # $newrows = array(); if ($opt['unique'] || $opt['duplicated'] || $opt['only-unique'] || $opt['count']) { wdbg(1,"$func: array follows"); wdbg(1,$to_sort); $lastval = false; $lastline = false; $dupcnt = 0; foreach ($to_sort as $cur_line) { wdbg(1,"$func: processing lastline=$lastline, cur_line=$cur_line[1]"); if ($lastval === $cur_line[0]) { wdbg(1,"$func: dup"); $dupcnt++; } else { wdbg(1,"$func: change"); if ($lastval !== false && (($opt['duplicated'] && $dupcnt > 1) || ($opt['only-unique'] && $dupcnt == 1) || (!$opt['duplicated'] && !$opt['only-unique']))) { wdbg(1,"$func: printing"); $newrows[] = (($opt['count']) ? "$dupcnt: ":'') . $lastline; } $lastval = $cur_line[0]; $lastline = $cur_line[1]; $dupcnt = 1; } } if ($lastval !== false && (($opt['duplicated'] && $dupcnt > 1) || ($opt['only-unique'] && $dupcnt == 1) || (!$opt['duplicated'] && !$opt['only-unique']))) { wdbg(1,"$func: printing last"); $newrows[] = ($opt['count']?"$dupcnt: ":'') . $lastline; } } else { foreach($to_sort as $tinyarray) { $newrows[] = $tinyarray[1]; } } return (wshPostProcess($pagename, $opt, $newrows, $page)); } # {(tail OPTIONS PAGEPATTERN)} # Print n lines from the end of a file # Options: # file_prefix="string" - string which will become the line leading each set # of lines from the current file. # line_prefix="string" - string which will become the prefix to each line # line_suffix="string" - string which will become the prefix to each line # PAGEPATTERN... - source pages from PageName or Group.Pagename # allowing wiki wildcards * and ? # (multiple files/patterns allowed) $MarkupExpr["${WikiShMXPrefix}tail"] = 'wshTail($pagename, @$argp, @$args)'; function wshTail($pagename, $opt, $filelist) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = 'Tail()'; wdbg(4,"$func: Entering"); wshInitOpts($pagename, 'n:', $opt, $filelist); wshSDOpt($opt, 'n', -10); wdbg(1,$opt['n']); if ($opt['n'] > 0) $opt['n'] *= -1; $opt['startline'][] = $opt['n']; $opt['endline'][] = '$'; $WikiShVars['STATUS'] = 0; return(wshExtractLines($pagename, $opt, $filelist, $func)); } # {(test OPTIONS PAGEPATTERN)} # Implement various methods of boolean testing based on options # Options: # PAGEPATTERN... - source pages from PageName or Group.Pagename # allowing wiki wildcards * and ? # (multiple files/patterns allowed) # FILE OPTIONS: # -d FILE (is a directory/group) # -e FILE (exists) # -f FILE (exists) # -g GROUP (does it exist as a group) (non-shell) # -r FILE (is readable) # -s FILE (file has something - is non-zero length) # -w FILE (is writable) # FILE1 -nt FILE2 is file1 newer than file2 by modification date # (or if file1 exists and file2 does not) # FILE1 -ot FILE2 is file1 older than file2 by modification date # (or if file2 exists and file1 does not) # STRING OPTIONS # -z STRING (true if string is zero length) # -n STRING (true if string is non-zero length) # string1 == string2 # string1 != string2 # string1 < string2 # string1 > string2 # string1 ~= /pat/ (non-shell - regex match) # INTEGER OPTIONS # INT1 OPERATOR INT2 # OPERATOR: -eq, -ne, -lt, -le, -gt, -ge # $MarkupExpr["${WikiShMXPrefix}test"] = 'wshTest($pagename, @$argp, @$args)'; function wshTest($pagename, $opt, $args) { global $WikiShVars, $Conditions, $wshAuthPage, $wshAuthText; global $EnableWikiShTextRead, $EnableWikiShTextWrite; global $KeepToken, $KPV; $func = "Test()"; wdbg(4,"$func: Entering args=>>" . implode(" ", $args) . "<<"); $rpat = "/$KeepToken(\\d+P)$KeepToken/e"; $rrep = '$KPV[\'$1\']'; if (wshNotNow($pagename, true)) return(''); // Since wshTest does not call wshInitOpts() but we want a --pmwiki option // we have to do this little workaround... if (is_numeric($i = array_search('--pmwiki', $args))) { $opt['pmwiki'] = true; unset($args[$i]); } $oplist = array('-d','-e','-f','-g','-r','-s','-w', '-n','-z', '-ot','-nt', '-eq','-ne','-lt','-le','-gt','-ge', '==','!=','<','>', '>=', '<=', '~='); $state = 0; // 0=starting new, 1=arg1 collected, 2=op collected $rtn = false; wshExpandVars($pagename, $opt, $args); array_push($args, ''); // add a blank onto the end for quoting probs if (@$opt['pm'] || @$opt['pmwiki']) { $condspec = implode(" ", $args); if (!preg_match("/^\\s*(!?)\\s*(\\S*)\\s*(.*?)\\s*$/", $condspec, $match)) echo "ERROR: No Match on condspec \"$condspec\"
\n"; list($x, $not, $condname, $condparm) = $match; if (!isset($Conditions[$condname])) { $WikiShVars['STATUS'] = 4; wshStdErr($pagename, $opt, "ERROR: $func: Unknown condition \"$condname\""); return(''); } $tf = @eval("return ({$Conditions[$condname]});"); if ($tf xor $not) $WikiShVars['STATUS'] = 0; else $WikiShVars['STATUS'] = 1; return(''); } else { foreach ($args as $arg) { if ($state == 3) continue; // got the spare blank one - ignore it wdbg(2,"$func: Processing arg=\"$arg\", state=$state"); if ($state == 2) { $arg2 = $arg; switch ($op) { ## ## HANDLE INTEGER OPERATORS ## case '-ne': case '-eq': case '-lt': case '-le': case '-gt': case '-ge': wdbg(2,"$func: op=\"$op\""); if (!is_numeric($arg1)) { wshStdErr($pagename, $opt, "$func: ERROR: non-numeric arg1 ($arg1) for numeric operator ($op). Treating as 0."); $arg1 = 0; } if (!is_numeric($arg2)) { wshStdErr($pagename, $opt, "$func: ERROR: non-numeric arg2 ($arg2) for numeric operator ($op). Treating as 0."); $arg2 = 0; } switch ($op) { case '-eq': $rtn = ($arg1 == $arg2); break; case '-ne': $rtn = ($arg1 != $arg2); break; case '-lt': $rtn = ($arg1 < $arg2); break; case '-le': $rtn = ($arg1 <= $arg2); break; case '-gt': $rtn = ($arg1 > $arg2); break; case '-ge': $rtn = ($arg1 >= $arg2); break; } wdbg(2,"$func: int op $op: returning " . (($rtn) ? "TRUE" : "FALSE")); $arg1 = $op = $arg2 = $negop = ''; $state = 3; break; ## ## HANDLE STRING OPERATORS ## case '==': case '!=': case '>=': case '<=': case '~=': case '<': case '>': case '-z': case '-n': wdbg(2,"$func: STRING op=\"$op\" (arg1=@$arg1, arg2=@$arg2)"); switch ($op) { case '==': $rtn = (strcmp($arg1, $arg2) == 0); break; case '!=': $rtn = (strcmp($arg1, $arg2) != 0); break; case '>=': $rtn = (strcmp($arg1, $arg2) >= 0); break; case '<=': $rtn = (strcmp($arg1, $arg2) <= 0); break; case '~=': wdbg(3,"$func: doing preg_match(\"$arg2\", \"$arg1\")"); wdbg(2,"$func: arg1=" . wshDbgOd($arg1)); $rtn = (preg_match($arg2, $arg1)); break; case '<': $rtn = (strcmp($arg1, $arg2) < 0); break; case '>': $rtn = (strcmp($arg1, $arg2) > 0); break; case '-z': $rtn = (strlen($arg2) == 0); break; case '-n': $rtn = (strlen($arg2) > 0); break; } $arg1 = $op = $arg2 = $negop = ''; $state = 3; break; ## ## HANDLE FILE OPERATORS ## #### NOTE: I should *really* "optimize" the lines of code by #### reusing code as I've done above... case '-d': case '-g': // group/directory exists $arg2 = $arg2 . '.*'; case '-e': case '-f': // exists wdbg(2,"$func: op=\"$op\""); wdbg(2, "DEBUG: arg2=".print_r($arg2,true)."
\n"); $tmparg = array(wshMakePageName($pagename, $opt, $arg2)); wshExpandWildCards($pagename, $opt, $tmparg, true); $filename = $tmparg[0]; wdbg(2,"$func: -f|-g Checking if PageExists($filename)".wshDbgOd($filename, true)); if (wshIsATextFile("", $filename)) { $filename = preg_replace($rpat, $rrep, $filename); $filename = substr($filename, strlen(TEXTFILEID)); wdbg(1, "DEBUG: Checking text file with file_exists($filename)
\n"); $rtn = ($EnableWikiShTextRead && file_exists($filename) && slAuthorized($filename, $wshAuthText, 'read', true)); } elseif (wshIsASessionFile('', $filename) || wshIsASessionGroup($filename)) { if (wshIsASessionFile('', $filename)) $filename = substr($filename, strlen(SESSFILEID)); $rtn = isset($_SESSION[$filename]); } else $rtn = PageExists($filename); wdbg(2,"$func: -f|-g PageExists() returns " . ($rtn?"TRUE":"FALSE") . " (negop=$negop)"); $rtn = (($rtn && !$negop) || ($negop && !$rtn)); $arg1 = $op = $arg2 = $negop = ''; $state = 3; break; case '-r': // exists and is readable wdbg(2,"$func: op=\"$op\""); $tmparg = array(wshMakePageName($pagename, $opt, $arg2)); wshExpandWildCards($pagename, $opt, $tmparg, true); $filename = $tmparg[0]; wdbg(2,"$func: -r Checking if PageExists($tmparg[0]) xor >>$negop<<"); if (wshIsATextFile("", $filename)) { $filename = preg_replace($rpat, $rrep, $filename); $filename = substr($filename, strlen(TEXTFILEID)); wdbg(1, "DEBUG: Checking text file with file_exists($filename)
\n"); $rtn = ($EnableWikiShTextRead && is_readable($filename) && slAuthorized($filename, $wshAuthText, 'read', true)); } elseif (wshIsASessionFile('', $filename) || wshIsASessionGroup($filename)) { if (wshIsASessionFile('', $filename)) $filename = substr($filename, strlen(SESSFILEID)); $rtn = isset($_SESSION[$filename]); // session files always readable } else $rtn = (PageExists($filename) && slAuthorized($filename, $wshAuthPage, 'read') && (CondAuth($filename, 'read') || slAuthorized($filename, $wshAuthPage, 'forceread'))); wdbg(2,"$func: -r PageExists() && CondAuth() && slAuthorized() return " . ($rtn?"TRUE":"FALSE") . " (negop=$negop)"); $rtn = (($rtn && !$negop) || ($negop && !$rtn)); $arg1 = $op = $arg2 = $negop = ''; $state = 3; break; case '-w': // exists and is writable wdbg(2,"$func: op=\"$op\""); $tmparg = array(wshMakePageName($pagename, $opt, $arg2)); wshExpandWildCards($pagename, $opt, $tmparg, true); $filename = $tmparg[0]; wdbg(2,"$func: -w Checking if PageExists($tmparg[0]) xor >>$negop<<"); if (wshIsATextFile("", $filename)) { $filename = preg_replace($rpat, $rrep, $filename); $filename = substr($filename, strlen(TEXTFILEID)); wdbg(1, "DEBUG: Checking text file with file_exists($filename)
\n"); $rtn = ($EnableWikiShTextWrite && is_writable($filename) && slAuthorized($filename, $wshAuthText, 'overwrite', true)); } elseif (wshIsASessionFile('', $filename) || wshIsASessionGroup($filename)) { if (wshIsASessionFile('', $filename)) $filename = substr($filename, strlen(SESSFILEID)); $rtn = isset($_SESSION[$filename]); // session files always writable } else // regular pmwiki page $rtn = (PageExists($filename) && slAuthorized($filename, $wshAuthPage, 'overwrite') && (CondAuth($filename, 'edit') || slAuthorized($filename, $wshAuthPage, 'forceedit'))); wdbg(2,"$func: -w PageExists() && CondAuth() return " . ($rtn?"TRUE":"FALSE") . " (negop=$negop)"); $rtn = (($rtn && !$negop) || ($negop && !$rtn)); $arg1 = $op = $arg2 = $negop = ''; $state = 3; break; case '-s': // exists and is readable and has *something* in it wdbg(2,"$func: op=\"$op\""); $tmparg = array(wshMakePageName($pagename, $opt, $arg2)); wshExpandWildCards($pagename, $opt, $tmparg, true); $filename = $tmparg[0]; wdbg(2,"$func: -s Checking if PageExists($tmparg[0]) xor >>$negop<<"); if (wshIsATextFile("", $filename)) { $filename = preg_replace($rpat, $rrep, $filename); $filename = substr($filename, strlen(TEXTFILEID)); wdbg(1, "DEBUG: Checking text file with filesize($filename)
\n"); $rtn = ($EnableWikiShTextRead && file_exists($filename) && filesize($filename) && slAuthorized($filename, $wshAuthText, 'read', true)); } elseif (wshIsASessionFile('', $filename) || wshIsASessionGroup($filename)) { if (wshIsASessionFile('', $filename)) $filename = substr($filename, strlen(SESSFILEID)); $rtn = (boolean)@$_SESSION[$filename]['text']; } else { // regular pmwiki page $rtn = false; // assume either no exist or no contents if (PageExists($filename)) { $page = wshRetrieveAuthPage($filename, 'read', false); wdbg(2,"$func: text=>>" . $page['text'] . "<<"); if (!$page) { wshStdErr($pagename, $opt, "$func: ERROR: error reading page >>$filename<< (authorizations perhaps?)"); $WikiShVars['STATUS'] = 2; return (''); } $rtn = (boolean)$page['text']; } } if ($negop) $rtn = !$rtn; $arg1 = $op = $arg2 = $negop = ''; $state = 3; break; case '-ot': // arg1 is older than arg2 $tmp = $arg1; $arg1 = $arg2; $arg2 = $tmp; // no break intentionally... case '-nt': // arg1 is newer than arg2 wdbg(2,"$func: op=\"$op\""); $tmparg = array(wshMakePageName($pagename, $opt, $arg1)); wshExpandWildCards($pagename, $opt, $tmparg, true, true); $filename1 = $tmparg[0]; $tmparg = array(wshMakePageName($pagename, $opt, $arg2)); wshExpandWildCards($pagename, $opt, $tmparg, true, true); $filename2 = $tmparg[0]; wdbg(2,"$func: $op Checking if PageExists($filename1,$filename2)"); if (!PageExists($filename1)) { wshStdErr($pagename, $opt, "$func: ERROR: page $filename1 does not exist"); $WikiShVars['STATUS'] = 2; return (''); } if (!PageExists($filename2)) { wshStdErr($pagename, $opt, "$func: ERROR: page $filename2 does not exist"); $WikiShVars['STATUS'] = 2; return (''); } $page1 = wshRetrieveAuthPage($filename1, 'read', false); if (!$page1) { wshStdErr($pagename, $opt, "$func: ERROR: error reading page >>$filename1<<"); $WikiShVars['STATUS'] = 2; return(''); } $page2 = wshRetrieveAuthPage($filename2, 'read', false); if (!$page2) { wshStdErr($pagename, $opt, "$func: ERROR: error reading page >>$filename2<<"); $WikiShVars['STATUS'] = 2; return(''); } wdbg(2,"$func: time(" . $filename1 . ")=" . $page1['time']); wdbg(2,"$func: time(" . $filename2 . ")=" . $page2['time']); $rtn = ($page1['time']+0 > $page2['time']+0); wdbg(2,"$func: time " . $page1['time'] . " > time " . $page2['time'] . ": returned " . ($rtn?"TRUE":"FALSE")); $arg1 = $op = $arg2 = $negop = ''; $state = 3; break; default: wdbg(1,"$func: UNKNOWN op=\"$op\""); wshStdErr($pagename, $opt, "$func: ERROR: Unknown operator option \"$op\""); } continue; } if ($arg == '!') { $negop = true; // no need to change state... continue; } elseif (in_array($arg, $oplist)) { if ($state > 1) { wshStdErr($pagename, $opt, "$func: ERROR: Argument mismatch. $arg unexpected."); $WikiShVars['STATUS'] = 4; return(''); } $op = $arg; $state = 2; continue; } else { $arg1 = $arg; $state = 1; wdbg(2,"$func: arg1=$arg1"); continue; } } } wdbg(4,"$func: Returning " . ($rtn ? "TRUE" : "FALSE")); $WikiShVars['STATUS'] = ($rtn ? 0 : 1); return(''); } # {(true)} # return nothing. Set $STATUS to zero. $MarkupExpr["${WikiShMXPrefix}true"] = 'wshTrue($pagename, @$argp, @$args)'; function wshTrue($pagename, $opt, $args) { global $WikiShVars; wshNotNow($pagename); // just for RC initializations - don't care about return val $func = "True()"; wdbg(4,"$func: Entering"); $WikiShVars['STATUS'] = 0; return(''); } # {(uniq OPTIONS PAGEPATTERN)} # Return unique lines in an already sorted list # # NOTE: NON-SHELL EXCEPTION. I have chosen to use the OPTIONS from sort rather # then from uniq. Those options are much more flexible for specifying keys. # # Options: # -d only print duplicate lines # NOTE: If you want to pass -d onto sort then use --dictionary-order # -c prefix lines by a count of the number of occurrences of that line # -f,-s,-w -- THESE ARE *NOT* IMPLEMENTED -- USE -k POS1,POS2 syntax instead # -u only print unique lines (omit duplicate lines) # # As a result of the above exception this function will simply call wshSort() # with a slightly altered set of options... # $MarkupExpr["${WikiShMXPrefix}uniq"] = 'wshUniq($pagename, @$argp, @$args)'; function wshUniq($pagename, $opt, $args) { global $WikiShVars; $func = 'uniq()'; if (wshNotNow($pagename)) return(''); wdbg(4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $args); # These options could be ambiguous with similarly named options within # sort() and so they are being renamed to their long version here. wdbg(1,"$func: checking options for rename"); if (@$opt['i']) { wdbg(1,"$func: -i"); $opt['ignore-case'] = true; unset($opt['i']); } if (@$opt['c'] || @$opt['count']) { wdbg(1,"$func: -c"); $opt['count'] = true; unset($opt['c']); } if (@$opt['d'] || @$opt['duplicated']) { wdbg(1,"$func: -d"); $opt['duplicated'] = true; unset($opt['d']); } # only-unique means if a line is duplicated you print NOTHING, not even # one of them. If you're wondering how to pass the -u semantics into # sort, that is the default meaning of calling uniq, so there's no need # to specify it. if (@$opt['u'] || @$opt['unique']) { wdbg(1,"$func: -u"); $opt['only-unique'] = true; unset($opt['u']); unset($opt['unique']); } # These options make wshSort() behave like uniq. $opt['no-sort'] = true; // uniq does not imply a sort $opt['unique'] = true; // make sort -u do its stuff wdbg(1,"$func: Calling Sort()"); return (wshSort($pagename, $opt, $args)); } # {(wc OPTIONS PAGEPATTERN)} # Give stats on wordcount, charactercount, or linecount or all of the above # Options: # -l -- just linecount # -c -- just charactercount # -w -- just wordcount # -q -- suppress labels 'Line: ', 'Word: ', and 'Character: ' # --only_total only print the total, not per-file counts (total_only synonym) # (if none of the above then assume all 3 of the above) # PAGEPATTERN... - source pages from PageName or Group.Pagename # allowing wiki wildcards * and ? # (multiple files/patterns allowed) $MarkupExpr["${WikiShMXPrefix}wc"] = 'wshWc($pagename, @$argp, @$args)'; function wshWc($pagename, $opt, $args) { global $WikiShVars; if (wshNotNow($pagename)) return(''); $func = 'wc()'; wdbg(4,"$func: Entering"); wshInitOpts($pagename, '', $opt, $args); wshExpandWildCards($pagename, $opt, $args, false, false, false); wshSDOpt($opt, 'only_total', false); # This is a little messy, but nice for users to not have to remember wshSDOpt($opt, 'total_only', false); if ($opt['total_only']) $opt['only_total'] = $opt['total_only']; wshSDOpt($opt, 'file_prefix', ''); wshSDOpt($opt, 'line_prefix', ''); wshSDOpt($opt, 'line_suffix', ''); wshSDOpt($opt, 'l', false); wshSDOpt($opt, 'w', false); wshSDOpt($opt, 'c', false); wshSDOpt($opt, 'q', false); if (!$opt['l'] && !$opt['w'] && !$opt['c']) $opt['l'] = $opt['w'] = $opt['c'] = true; $newrows = array(); $total_wc = $total_lc = $total_cc = $wordcnt = $linecnt = $charcnt = 0; foreach ($args as $filename) { $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: No such page: $page[filename]"); continue; } wdbg(0,"$func: text"); wdbg(0,$page['text']); if ($opt['l']) $linecnt = substr_count($page['text'], "\n") + 1; if ($opt['c']) $charcnt = strlen($page['text']) - $linecnt; if ($opt['w']) $wordcnt = str_word_count($page['text']); $total_wc += $wordcnt; $total_lc += $linecnt; $total_cc += $charcnt; if (!$opt['only_total']) { if ($opt['file_prefix']) { $file_prefix = wshReplace($opt, $page, $opt['file_prefix']); $newrows[] = $file_prefix; } $myline = wshReplace($opt, $page, $opt['line_prefix']); if ($opt['l']) $myline .= (($opt['q']) ? "" : "lines: ") . "$linecnt "; if ($opt['w']) $myline .= (($opt['q']) ? "" : "words: ") . "$wordcnt "; if ($opt['c']) $myline .= (($opt['q']) ? "" : "characters: ") . "$charcnt "; $myline .= wshReplace($opt, $page, $opt['line_suffix']); $newrows[] = $myline; } } if (sizeof($args) > 1) { $funnypage['filename'] = 'TOTAL'; $funnypage['title'] = 'TOTAL'; if ($opt['file_prefix']) { $file_prefix = wshReplace($opt, $funnypage, $opt['file_prefix']); $newrows[] = $file_prefix; } $myline = wshReplace($opt, $funnypage, $opt['line_prefix']); if ($opt['l']) $myline .= (($opt['q']) ? "" : "lines: ") . "$total_lc "; if ($opt['w']) $myline .= (($opt['q']) ? "" : "words: ") . "$total_wc "; if ($opt['c']) $myline .= (($opt['q']) ? "" : "characters: ") . "$total_cc "; $myline .= wshReplace($opt, $funnypage, $opt['line_suffix']); $newrows[] = $myline; } $WikiShVars['STATUS'] = 0; return (wshPostProcess($pagename, $opt, $newrows, $page)); } $MarkupExpr['wikish_active'] = 'wshActive($pagename, preg_replace($rpat, $rrep, $params))'; function wshActive($pagename, $expr) { global $WikiShVars; if (wshNotNow($pagename, true)) return(Keep('', 'P')); $func = "wikishactive()"; #echo "$func: Entering
\n"; wdbg(4,"$func: Entering"); $expr = substr($expr, 1); // for some reason it gives a leading space wdbg(1,$expr); $expr = '(' . $expr . ')'; wdbg(4,"$func: evaling \"$expr\""); #echo "$func: evaling $expr
\n"; $rtn = MarkupExpression($pagename, $expr); #echo "$func: status=$WikiShVars[STATUS]
\n"; if ($WikiShVars['STATUS'] == 0) { $WikiShVars['ACTIVE'] = true; } else { $WikiShVars['ACTIVE'] = false; } #echo "$func: Leaving with active=" . ($WikiShVars['ACTIVE'] ? "TRUE" : "FALSE") . "
\n"; return(Keep($rtn, 'P')); } # {(wikish_button OPTIONS ID LABEL)} # OPTIONS: # -r - return a useful value (normally returns an empty string) of 1 # for true (the first time) and 0 for false (subsequent # invocations) # -q - don't generate a button - just activate/deactivate based on $_GET #* -n - no form header (already generated - we don't need to do it) #* -g|-p - get vs post method (-g=default) (or --method:get/post?) #* these are not implemented # ARGUMENT: # id - a string to uniquely define THIS call to once() (REQUIRED) # Non-alphanumerics (plus underscore & dash) will be ignored in # creating the actual ID. Be aware of this for uniqueness. # label - a string which appears on the button itself (Optional. Will use # ID for label if not specified for the button. Will be ignored # if -q is set.) # This function will display a button. The button will set certain GET # parameters when it is set. If those parameters are set then WikiSh will # be activated. If those parameters are not set then WikiSh will be # deactivated. # $MarkupExpr["wikish_button"] = 'wshButton($pagename, @$argp, @$args)'; function wshButton($pagename, $opt, $args) { global $WikiShVars, $wshFormStarted; if (wshNotNow($pagename, true)) return (''); #print_r($opt); #echo "
\n"; #print_r($args); #echo "
\n"; $func = 'wikish_button()'; wdbg(4,"$func: Entering"); wshInitOpts($pagename, "", $opt, $args); wshSDOpt($opt, 'r', false); wshSDOpt($opt, 'q', false); if (!@$args[1]) $args[1] = $args[0]; $args[0] = preg_replace("/[^A-Za-z_-]/", "", $args[0]); SDV($wshFormStarted, false); $rtn = ''; if (!$wshFormStarted && !$opt['q']) { $rtn .= '(:input form method="GET":)'."\n".'(:input hidden name=n value="{*$FullName}":)'; $wshFormStarted = true; } if (!$opt['q']) $rtn .= "(:input submit abc name=\"$args[0]\" value=\"$args[1]\":)"; if ($_GET[$args[0]]) { wdbg(1,"$func: activating (" . $args[0] . "->" . $_GET[$args[0]] . ")"); $WikiShVars['ACTIVE'] = true; } else { wdbg(1,"$func: DEactivating (" . $args[0] . "->" . $_GET[$args[0]] . ")"); wdbg(0,$_GET); wdbg(0,$args); $WikiShVars['ACTIVE'] = false; } wdbg(1,"$func: Returning: " . wshDbgOd($rtn)); return($rtn); } # {(wikish_once OPTIONS ONCE-ID KEY)} # OPTIONS: # -c page - a page to use instead of $WikiShControlPage to hold the static # values needed to implement (once...) # -r - return a useful value (normally returns an empty string) of 1 # for true (the first time) and 0 for false (subsequent # invocations) # ARGUMENT: # once-id - a string to uniquely define THIS call to once() # key - a string you can change to re-run it. # Note that you could just use a single string, but using the 2 strings # makes the controlpage much more readable and etc # This function is true a single time with the current set of arguments. # It requires read & write privilege to the file named in $WikiShControlPage. # If it does not have read & write privilege then it will NEVER return true. # Note that read/write privilege include setting of appropriate $Enable* vars. # Note that "return true" really means returning a STATUS of 0. $MarkupExpr["wikish_once"] = 'wshOnce($pagename, @$argp, @$args)'; function wshOnce($pagename, $opt, $args) { global $WikiShVars, $WikiShControlPage; #echo "once: entering (" . microtime() . ")
\n"; if (wshNotNow($pagename, true)) return (''); #print_r($opt); #echo "
\n"; #print_r($args); #echo "
\n"; $func = 'once()'; wdbg(4,"$func: Entering"); # Establish the real name of WikiShControlPage (prepending group if needed) wshInitOpts($pagename, 'c:', $opt, $args); wshSDOpt($opt, 'r', false); if ($opt['c']) $ControlPage = $opt['c']; else $ControlPage = $WikiShControlPage; if (!strpos($ControlPage, '.') && !strpos($ControlPage, '/')) { wdbg(1,"$func: No dot in ControlPage=$ControlPage"); // Hmmm... They forgot a group name. That doesn't work. $Grp = PageVar($pagename, '$Group'); $ControlPage = $Grp . '.' . $ControlPage; wdbg(1,"$func: Altered filename to $ControlPage (based on $pagename)"); } # Set the pattern we will search for from $pagename and $args $pat = ':' . $pagename; if (sizeof($args) > 1) { $pat .= '-' . array_shift($args); } $pat .= ':' . implode(" ", $args); # Grep for the pattern $rtn = ''; wshGrep($pagename, array('q'=>true), array("^$pat$", $ControlPage)); if ($WikiShVars['STATUS'] == 0) { # If found then we shouldn't run again - return false $WikiShVars['STATUS'] = 1; wdbg(4,"$func: Returning (a) with STATUS=" . $WikiShVars['STATUS']); return($opt['r'] ? '0' : ''); } else { # not found - WRITE the pattern to $WikiShControlPage # if successful write to $WikiShControlPage then return true wshEcho($pagename, array('stdout'=>$ControlPage, 'stdout_append'=>true), array("$pat")); # $WikiShVars['STATUS'] was set by echo - if it succeeded then we # succeed. If it didn't succeed then we don't succeed. Thus no # need to check it or set it. wdbg(4,"$func: Returning (b) with STATUS=" . $WikiShVars['STATUS']); return($opt['r'] ? '1' : ''); } } # WikiShSystemCall # # Run a given system command after doing variable substitution. # NOTE that this is *not* part of *any* MX nor markup unless the system # administrator (or another recipe) creates said MX or markup. # NOTE that this means the presence of this command is no more threatening # to security than the fact that PHP has a system() command. Both are # unavailable unless specifically enabled by the administrator. # # $security can contain one of these strings: # FULL-ARGS - use escapeshellarg() on every arg # SPACE-ARGS- put quotes around args with spaces # SEMI-ARGS - put quotes around args with spaces or | or > or < characters # FULL-LINE - use escapeshellcommand() on the whole command function WikiShSystemCall($pagename, $opt, $dir, $cmd, $args, $security = "SEMI-ARGS", $SafeVars = array()) { global $WikiShVars; #echo "SystemCall: Entering ($pagename, opt, $dir, $cmd, args, $security, safe
\n"; #print_r ($SafeVars); #echo "
\n"; $func = 'WikiShSystemCall()'; wdbg(4,"$func: Entering: $cmd " . implode(" ", $args)); wdbg(1,"$func: PWD=" . getcwd()); $cmd = array($cmd); $rtn = wshExpandVars($pagename, $opt, $cmd, $SafeVars); $cmd = $cmd[0]; #there is no opt processing - must use var=val syntax instead of --var:val $rtn = $rtn && wshExpandVars($pagename, $opt, $args, $SafeVars); if ($cmd) $command = $cmd; else $command = array_shift($args); if (!$rtn) { wshStdErr($pagename, $opt, "ERROR: $func: Insecure variable. System call aborted."); return(''); } foreach ($args as $arg) { switch ($security) { case "FULL-ARGS": $command .= ' ' . escapeshellarg($arg); break; case "SPACE-ARGS": if (strstr($arg, ' ')) $command .= ' ' . escapeshellarg($arg); else $command .= ' ' . $arg; break; case "SEMI-ARGS": if (preg_match("/[ |><;`(){}]/", $arg)) $command .= ' ' . escapeshellarg($arg); else $command .= ' ' . $arg; break; default: $command .= ' ' . $arg; break; } wdbg(3,"$func: Handling arg=$arg - now command = " . wshDbgOd($command)); } $rtn = array(); $status = 0; if ("FULL-LINE" == $security) $command = escapeshellcmd(str_replace('`', '\"', $command)); wdbg(4,"$func: Running this commmand >>" . wshDbgOd($command). "<<"); if ($dir) { $odir = getcwd(); wdbg(3,"$func: chdir'ing to this dir: $dir (from $odir)"); chdir($dir); } exec($command, $rtn, $status); if ($dir) { wdbg(3,"$func: chdir'ing back to this dir: $odir"); chdir($odir); } $WikiShVars['STATUS'] = $status; return (wshPostProcess($pagename, $opt, $rtn)); } # wshExpand1Var() # Expand a single variable function wshExpand1Var($pagename, $text) { $arrtext = array($text); wshExpandVars($pagename, array(), $arrtext); return($arrtext[0]); } # wshExpandVars() # Find any pattern '/\${\w+}/ anywhere in the $args list and # expand it according to the value found in $WikiShVars[]. # FUTURE: in general, I HAVE to have some way to suppress variable interpolation # some equivalent of single- as opposed to double-quotes... function wshExpandVars($pagename, $opt, &$args, $secure = array()) { global $WikiShVars, $MarkupTable, $Version, $RecipeInfo; $func="wshExpandVars()"; $rtn = true; wdbg(3,"$func: Entering: ".(is_array($args)?implode(",",$args):$args)); wdbg(1,"$func: WikiShVars follows"); wdbg(1,$WikiShVars); if (!is_array($args)) { $singleton = true; $args = array($args); } else $singleton = false; for ($i=0; $i< sizeof($args); $i++) { wdbg(3,"$func: Working with " . wshDbgOd($args[$i])); $id = '{$var}'; if (stristr($WikiShVars['PAGEVARS'], 'pre')) { $RefPage = (@$opt['refpage'] ? $opt['refpage'] : (@$WikiShVars['REFPAGE'] ? $WikiShVars['REFPAGE'] : $pagename)); $args[$i] = FmtPageName($args[$i], $RefPage, true); wdbg(2,"$func: After FmtPageName: " . wshDbgOd($args[$i])); } $pat = "/(?[!~])|(?P#))?(?P(?:[@*#]|\w+))(?:(?P[*@])|(?P#{1,2}|%{1,2})(?P[^}]+)|:(?P[-=+])(?P[^}]*)|\\/(?P[^\\/]+)\\/(?P[^\\}]*)|[:,](?P[-\\d]+)(?:[:,](?P[-\\d]+))?)?\\}/"; wdbg(0,"$func: pat=" . wshDbgOd($pat)); while (preg_match($pat, $args[$i], $m)) { wdbg(2,"$func: Found match: 0=$m[0], strlen=$m[strlen], var=$m[var], search=$m[search], replace=$m[replace], offset=$m[offset] length=$m[length] default=$m[default], dflt_op=$m[default_op], delete_op=$m[delete_op], delete=$m[delete], prefix=$m[prefix], prefix_op=$m[prefix_op]"); $var = $m['var']; if ((@$var||$var===0||$var==='0') && isset($WikiShVars[$var])) { #if ($secure) { print_r($secure); echo "
\n"; } if (!$secure[$var] || wshMatchPageName($WikiShVars[$var], $secure[$var])) { $val = $WikiShVars[$var]; #echo "TESTING: Expanding $var to $val " . ($secure[$val] ? "(secured)":"(unsecured)"). "
\n"; } else { wshStdErr($pagename, $opt, "ERROR: $func: Insecure value $WikiShVars[$var] for variable \${$var}."); $val = '!!ERROR!!'; $rtn = false; #echo "TESTING: NOT Expanding $var to $val " . ($secure[$val] ? "(secured)":"(unsecured)"). "
\n"; } } else { switch ($var) { case 'PWD': // Present Working Directory case 'CWD': // Current Working Directory $val = getcwd(); break; case 'WIKISH_VERSION': $val = $RecipeInfo['WikiSh']['Version']; break; case 'PMWIKI_VERSION': $val = $Version; break; case 'RANDOM': $val=rand($WikiShVars['RANDOM_MIN'], $WikiShVars['RANDOM_MAX']); break; case 'SECONDS': $val = microtime(true) - $WikiShVars['SECONDS_START']; break; case 'SECONDSLEFT': $val = ini_get('max_execution_time') - (microtime(true) - $WikiShVars['SECONDSLEFT_START']); break; case 'NOW': $val = microtime(true); break; case 'HOSTIP': $val=gethostbyname($_SERVER["SERVER_NAME"]); break; case 'HOSTNAME': $val=gethostbyaddr(gethostbyname($_SERVER["SERVER_NAME"])); break; case 'SESSIONID': $val= session_id(); break; case '*': case '@': $val = ''; for ($y = 1; isset($WikiShVars[$y]); $y++) $val .= ($val?' ':'') . $WikiShVars[$y]; break; case 'LPAREN': $val = '('; break; case 'RPAREN': $val = ')'; break; default: // truly an unset variable $val = ''; break; } } wdbg(2,"$func: before modifiers: var=$var, val=>>$val<<"); if ($m['prefix']=='!' && $m['prefix_op']) { $val = implode(" ", preg_grep("/^$var/", array_keys($WikiShVars))); } elseif ($m['prefix'] == '!') { $val = $_REQUEST[$var]; } elseif ($m['prefix'] == '~') { if(!session_id()) session_start(); $val = $_SESSION[$var]; } if ($m['default']) { if ($val) { if ($m['default_op'] == '+') $val = $m['default']; } else { if ($m['default_op'] == '-' || $m['default_op'] == '=') $val = $m['default']; if ($m['default_op'] == '=') $WikiShVars[$var] = $val; } } if ($m['search']) { // ${var/search/replace/} $search = wshGlobToPCRE($m['search']); wdbg(1,"$func: var searchpat=" . wshDbgOd($search)); $val = preg_replace("/$search/", $m['replace'], $val); } elseif ($m['offset'] !== null) { // ${var,offset,length} if ($m['length']!==null) $val = substr($val, $m['offset'], $m['length']); else $val = substr($val, $m['offset']); } elseif ($m['delete']) { $delete = wshGlobToPCRE($m['delete'], ($m['delete_op'][0] == '#'?true:false), ($m['delete_op'][0] == '%'?true:false), (strlen($m['delete_op'])>1 ? true : false)); wdbg(2,"$func: var searchpat=" . wshDbgOd($delete)); # We have to treat the ungreedy strip off the end differently # because there's no way to make PCRE ungreedy from a # end-of-string-oriented perspective. It always starts at # the beginning so we just add a "fake" regex on the front if ($m['delete_op'] == '%') $val = preg_replace("/(.*)$delete/", '$1', $val); else $val = preg_replace("/$delete/", '', $val); } if ($m['strlen']) { // ${#var} $val = strlen($val); } wdbg(2,"$func: after modifiers: var=$var, val=$val, WikiShVar=$WikiShVars[$var]"); $fullpat = preg_quote($m[0], '/'); wdbg(1,"$func: replacement pattern: " . wshDbgOd($fullpat)); # If the val contains a $1 pattern it should NOT be substituted # in this context, so we escape it. (crypt'd passwords have this) wdbg(2, "$func: before rid of backrefs: ".wshDbgOd($val)); $val = preg_replace("/(?>" . wshDbgOd($args[$i]) . "<<"); } # Sometimes a PV might have been formed by interpolating the WikiShVar # above - therefore check this one more time... if (stristr($WikiShVars['PAGEVARS'], 'post')) { wdbg(0,"$func: Before PTV replace: " . wshDbgOd($args[$i])); # Handle PTVs while (preg_match("/\\{([\\w-.\\/]*)\\$:([\\w 0-9]+)\\}/", $args[$i], $m)) { wdbg(1,"$func: Working on PTV $args[$i] (full=$m[0], 1=$m[1], 2=$m[2])"); $pn = ($m[1] ? $m[1] : $pagename); $pn = wshMakePageName($pagename, $opt, $pn); $ptv = $m[2]; $val = PageTextVar($pn, $ptv); $args[$i] = str_replace($m[0], $val, $args[$i]); wdbg(1,"$func: After PTV replacement: $args[$i]"); } wdbg(0,"$func: Before 2nd FmtPageName: " . wshDbgOd($args[$i])); $RefPage = (@$opt['refpage'] ? $opt['refpage'] : (@$WikiShVars['REFPAGE'] ? $WikiShVars['REFPAGE'] : $pagename)); $args[$i] = FmtPageName($args[$i], $RefPage, true); wdbg(2,"$func: After 2nd FmtPageName (ref=".($WikiShVars['REFPAGE']?$WikiShVars['REFPAGE']:$pagename)."): " . wshDbgOd($args[$i])); } } if ($singleton) $args = $args[0]; return($rtn); } function wshDbgOd($foo, $override = false) { global $WikiShVars; if (!$override) { if ($WikiShVars['DEBUGLEVEL'] >= 5) return($foo); if (!$WikiShVars['DEBUG_OD']) return($foo); } $repl = array(':' => 'COLON', ';' => 'SEMI-COLON', '/'=>'SLASH', '(' => 'OPEN-PAREN', ' '=>' _ ', '@' => 'AT', '\\' => 'BACKSLASH', CHR(10)=>'LF', CHR(13)=>'CR', ')'=>'CLOSE-PAREN', '.' => 'DOT', '{'=>'OPEN-CURLY', '}'=>'CLOSE-CURLY', '='=>'EQUALS', '|' => 'VERT', '#'=>'POUND', ','=>'COMMA', '$'=>'DOLLAR', '-'=>'DASH', '+'=>'PLUS', '"' => 'DOUBLE-QUOTE', "'" => 'SINGLE-QUOTE', '!' => 'BANG', '?' => 'QUESTION', '<' => 'OPEN-ANGLE', '>' => 'CLOSE-ANGLE', '*' => 'ASTERISK', '&' => 'AMPERSAND'); $rtn = ''; for ($i = 0; $i < strlen($foo); $i++) if (in_array($foo[$i], array_keys($repl))) { #echo "$foo[$i](" . ord($foo[$i]) . ") => " . $repl[$foo[$i]] . "
\n"; $rtn .= ' ' . $repl[$foo[$i]] . ' '; } elseif (ord($foo[$i]) < 65 && (ord($foo[$i])<48 || ord($foo[$i])>57)) $rtn .= 'CHR(' . ord($foo[$i]). ')'; else $rtn .= $foo[$i]; return($rtn); } # wshInitOpts() (SH NOTE: Think "getopts" but doing a little more automatically) # This function takes an array of arguments. It processes through these # looking for those arguments that begin with - or -- or are arguments to # these options. When it has gotten to the first non-option argument then # it exits having set $opt[x] for each of those options. # # EXAMPLE: $ValidOpts = 'x:' # -abc --hello:goodbye -x foo # $opt['a'] == true # $opt['b'] == true # $opt['c'] == true # $opt['hello'] == "goodbye" # $opt['x'] == "foo" # # $ValidOpts # This is a string where each character refers to a possible argument. If # a given character is followed by a colon then that option will be returned # with the following argument in the array. # SH NOTE: options NOT included in $ValidOpts do NOT cause an error as they # would in shell programming. Thus you can safely pass an empty list here # if you want. The main purpose of $ValidOpts, then is to specify options # which require an argument. I.e., if I have function foo() that can take # arguments -a, -b, -c, or -d but only -b and -c take arguments then I can # legitimately pass just 'b:c:' OR 'ab:c:d' - they are functionally identical. # $args # This is an array of arguments. The OPTIONS that we are interested in are # always preceded with a hyphen. (If a leading hyphen is preceded by a back # slash then that backslash will be stripped and the argument will NOT be # interpreted as an OPTION. # SH NOTE: This function is similar to getopts in shell, but it automatically # handles ALL options without being re-invoked. (I.e., call this function just # once). If you have required values or default values just check for those # in $opt after you have invoked this function and report errors or assign # default values as appropriate... function wshInitOpts($pagename, $ValidOpts, &$opt, &$args, $ExpandVars = true, $ProcPipe=true) { global $WikiShVars; global $WikiShPipeText, $WikiShPipeActive; $func = 'wshInitOpts'; wdbg(3,"$func: Entering: args array follows:"); wdbg(3,$args); if ($ExpandVars) { wshExpandVars($pagename, $opt, $args); } for($i=0; $i 2) { //arguments preceded by -- set that $opt[val] to true //i.e., --foo causes $opt['foo'] = true //if set to a particular value it would be --foo:xyz $foo = substr($myarg, 2); if ($colon_pos = strpos($foo, ':')) { $optname = substr($foo, 0, $colon_pos); $optval = substr($foo, $colon_pos+1); if ($opt[$optname]) { if (!is_array($opt[$optname])) { $opt[$optname] = array($opt[$optname], $optval); } else { $opt[$optname][] = $optval; } } else $opt[substr($foo, 0, $colon_pos)] = substr($foo, $colon_pos+1); wdbg(1,"$func: $myarg - set opt[" . substr($foo, 0, $colon_pos) . "] to " . substr($foo, $colon_pos+1)); } else { if (strlen($foo) == 1 && strstr($ValidOpts, $foo.':')) { $opt[$foo] = $args[$i+1]; $i++; // skip the next argument since it was arg to this opt } else { if (substr($foo, 0, 2) == 'no') { $foo = substr($foo, 2); $opt[$foo] = false; wdbg(1,"$func: $myarg - set opt[$foo] to FALSE"); } else { $opt[$foo] = true; wdbg(1,"$func: $myarg - set opt[$foo] to TRUE"); } } } } elseif ($myarg[0] == '-') { //arguments preceded by - set all following characters to true //i.e., -abc causes $opt['a'], $opt['b'], and $opt['c'] to all be //true for ($j=1; $j>".wshDbgOd(end($args))."<<"); for ($arg = end($args); $arg !== false && preg_match("/^(?:<|>|>(?:>)?|<).+$/", $arg); $arg = end($args)) { $arg = str_replace(array(">", "<"), array(">", "<"), array_pop($args)); wdbg(1,"$func: IN/OUTSPEC: arg=" . wshDbgOd($arg)); if ($arg[0] == '>') { # >pagename # >>pagename # >pagenamepagename=pattern (_TOP or _BEGIN = magic pattern) # >pagename>pattern (_BOTTOM or _END = magic pattern) # >pagename#section (write to section (and other # specs)) # >pagename$::var (deflist type of ptv) # >pagename$:var (standard ptv) # >pagename$:(var) (hidden ptv) # >pagename$var (hidden ptv) $opt['stdout'] = $arg; if (wshIsATextFile('', $opt['stdout'], ($arg[1] == '>' ? 2 : 1))) { wdbg(1,"$func: outputspec is a text file"); $opt['stdout'] = str_replace(TEXTFILEID, "", $arg); $opt['stdout_type'] = 'text'; } elseif (wshIsASessionFile('', $opt['stdout'], ($arg[1] == '>' ? 2 : 1)) || wshIsASessionGroup(substr($opt['stdout'], ($arg[1] == '>' ? 2:1)))) { if (wshIsASessionFile('', $opt['stdout'])) $opt['stdout'] = str_replace(SESSFILEID, "", $arg); $opt['stdout_type'] = 'session'; } else wshSDOpt($opt, 'stdout_type', 'wiki'); } elseif ($arg[0] == '<') { # 0) set_time_limit($opt['timeout']); if (isset($opt['debug'])) $WikiShVars['DEBUGLEVEL'] = $opt['debug']; // Now $args should hold only unprocessed values... We'll assume that $i // was left pointing at the first non-option argument. $args = array_slice($args, $i); if ($ProcPipe && $WikiShPipeActive) { wdbg(1,"wshInitOpts: Adding pipe text >>" . wshDbgOd($WikiShPipeText) . "<<"); if (!$opt['xargs']) $args[] = '-'; $args[] = $WikiShPipeText; $WikiShPipeActive = false; } } # wshExpandWildCards() # Finds any wildcards and expands them into all matching individual filenames # Honors $opt['list'] if specified to strip any undesired filenames # Honors $WikiShVars['LIST'] if specified and $opt['list'] not specified # Honors $SearchPatterns['default'] if specified and $WikiShVars['LIST'] # and $opt['list'] not specified # # Note that wshInitOpts() should be called PRIOR to this function so that all # option-type arguments are already gone. # # if $args[n] is a single hyphen then $args[n+1] is the contents of a file # rather than a filename and so we just ignore it. # # Note that $opt is currently ignored, but it will probably be important at # some point in the future and I'd rather not have to adapt every call at that # point. This leaves us with a nice, standard call... # function wshExpandWildCards($pagename, $opt, &$args, $NoPageID = false, $NoTextID = false, $NoBadID = false, $NoSessID = false) { global $WikiShVars, $SearchPatterns, $EnableWikiShTextRead, $wshAuthText; $func = "wshExpandWildCards()"; wdbg(3,"$func: Entering"); wdbg(1,$args); if ($opt['list']) $list = $opt['list']; elseif ($WikiShVars['LIST']) $list = $WikiShVars['LIST']; elseif (isset($SearchPatterns['default'])) $list = 'default'; else $list = ''; $grp = PageVar($pagename, '$Group'); $FileList = array(); for ($i = 0; $i < sizeof($args); $i++) { $myarglist = explode("\n", $args[$i]); // can be newline-separated list foreach ($myarglist as $myarg) { wdbg(1,"$func: myarg=$myarg"); if ($myarg == '-') { $FileList[] = $args[$i+1]; $i++; // skip the next argument - we've already handled it continue; } elseif (wshIsASessionFile('', $myarg) || wshIsASessionGroup($myarg)) { if (wshIsASessionFile('', $myarg)) $myarg = substr($myarg, strlen(SESSFILEID)); $orig_arg = $myarg; if (strstr($myarg, '#')) { if (preg_match("/(#.*)$/", $myarg, $matches)) $mysection = $matches[1]; $myarg = MakePageName($pagename, $myarg); wdbg(1,"$func: orig=$orig_arg, myarg=$myarg, section=$mysection"); } else $mysection = ''; $prpat = GlobToPCRE(FixGlob($myarg)); //make list from preg name pattern wdbg(3,"$func: session pattern=$prpat[0]"); if(!session_id()) session_start(); $ShortList = preg_grep("/$prpat[0]/i", array_keys($_SESSION)); foreach ($ShortList as $filename) { if ($mysection) $filename .= $mysection; wdbg(3, "$func: session file: $filename"); $FileList[] = ((!$NoSessID) ? SESSFILEID : '') . $filename; } } elseif (wshIsATextFile('', $myarg)) { // strip off the initial identifier $pat = substr($myarg, strlen(TEXTFILEID)); // make list from preg name pattern $globlist = glob("$pat"); $ShortList = array(); foreach ($globlist as $x) if ($EnableWikiShTextRead && slAuthorized($x, $wshAuthText, 'read', true)) $ShortList[] = $x; if ($ShortList) { wdbg(1,"$func: $myarg ($pat) - translated to these pages"); wdbg(1,$ShortList); foreach ($ShortList as $filename) { if (!HasGlob($pat) || slAuthorized($filename, $wshAuthText, 'read', true)) $FileList[] = ((!$NoTextID) ? TEXTFILEID : '') . $filename; } } else { // Pass it through "unharmed" if no file match. Better to // show it as an error than to just have it disappear. $FileList[] = $myarg; } continue; } else { //OK, we will assume this is a WIKI filename or filename pattern //check for group.name pattern wdbg(1,"$func: $myarg - assuming file name/pattern"); if ($z = strpos($myarg, '@')) { $since = substr($myarg, $z); $myarg = substr($myarg, 0, $z); } else $since = ''; $orig_arg = $myarg; if (strstr($myarg, '#')) { if (preg_match("/(#.*)$/", $myarg, $matches)) $mysection = $matches[1]; $myarg = MakePageName($pagename, $myarg); wdbg(1,"$func: orig=$orig_arg, myarg=$myarg, section=$mysection"); } else $mysection = ''; if (strstr($myarg,'.')) $pat = $myarg; else $pat = $grp.".".$myarg; //make preg pattern from wildcard pattern $prpat = GlobToPCRE(FixGlob($pat)); //make list from preg name pattern $ShortList = ListPages("/$prpat[0]/i"); wdbg(1,"Pages listed by $prpat[0]:"); wdbg(1,$ShortList); if ($ShortList) { wdbg(1,"$func: $myarg ($prpat[0]) - translated to these pages"); wdbg(1,$ShortList); if (HasGlob($pat) && $list) { $ShortList = MatchPageNames($ShortList, $SearchPatterns[$list]); wdbg(1,"$func: After MatchPageNames($list)"); wdbg(1,$ShortList); } foreach ($ShortList as $filename) { if ($mysection) $filename .= $mysection; if ($since) $filename .= $since; $FileList[] = ((!$NoPageID) ? WIKIPAGEID : '') . $filename; } } else { wdbg(1,"$func: $myarg - unmatched $prpat[0]. Passing on."); // Pass it through "unharmed" if no file match. Better to // show it as an error than to just have it disappear. $FileList[] = ($NoBadID?'':BADFILEID) . $myarg; } } } } wdbg(1,"$func: ending with this array of args..."); wdbg(1,$FileList); $args = $FileList; } function wshReadPage($pagename, $opt, $filename, $onlyfile=false) { global $EnableWikiShTextRead, $wshAuthText, $wshAuthPage; $func = "wshReadPage()"; wdbg(3,"ShReadpage($pagename, ..., $filename): Entering"); SDV($EnableWikiShTextRead, false); $orig_filename = $filename; if (wshIsASessionFile('', $filename) || wshIsASessionGroup($filename)) { if (wshIsASessionFile('', $filename)) $filename = substr($filename, strlen(SESSFILEID)); wdbg(1,"$func: Reading (session-type) $filename"); if(!session_id()) session_start(); if (isset($_SESSION[$filename])) { wdbg(1,"$func: SESSION[$filename] exists"); $page = $_SESSION[$filename]; $page['filename'] = $filename; $page['type'] = 'session'; } else { wdbg(1,"$func: SESSION[$filename] does NOT exist"); #wdbg(1, $_SESSION); $page = array('text'=>'', 'filename' => $filename, 'type' => 'bad'); } } elseif (wshIsAWikiPage('', $filename)) { wdbg(1,"ShReadpage(): processing WIKI type"); # If they specified a specific version of page then strip it off... if (preg_match('/(^[^@]+)@(\d+)$/', $filename, $m)) { $filename = $m[1]; $since = $m[2]; } else $since = 0; $filename = substr($filename, strlen(WIKIPAGEID)); $filename = wshMakePageName($pagename, $opt, $filename); #if ($filename==$pagename) return(array('')); wdbg(1,"wshReadPage(): Reading from $filename"); if (strstr($filename, '#')) $filename = MakePageName($pagename, $filename); if (!strpos($filename, '.') && !strpos($filename, '/')) { wdbg(1,"wshReadPage: No dot in filename=$filename"); // Hmmm... They forgot a group name. That doesn't work. $Grp = PageVar($pagename, '$Group'); $filename = $Grp . '.' . $filename; wdbg(1,"wshReadPage: Altered filename to $filename (based on $pagename)"); } # Even if they have 'forceread' permission they still need 'read' # permission. Since we want a special message for this, it is double- # checked, once here and once in wshRetrieveAuthPage(). if (!slAuthorized($filename, $wshAuthPage, 'read')) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Read: You do not have \"read\" authorization for page $filename. See SecLayer configuration"); $page = array('filename' => $filename, 'type' => 'bad'); } else # If you read this with $since then it loses history and saves # that version in the page read cache so no older history can ever # be read in this run of PHP $page = wshRetrieveAuthPage($filename, 'read', false, $since); if ($page === FALSE) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Read: You do not have \"read\" authorization for page $filename. See PmWiki authorizations"); $page = array('filename' => $filename, 'type' => 'bad'); } # If they specified a version, calculate that if ($since) { $page['text'] = RestorePage($filename, $page, $junk, 'diff:'.$since.':999'); } wdbg(1,"$func: Checking for section with filename=$orig_filename"); if (strstr($orig_filename, '#')) { wdbg(0,"$func: Looking for sub-section $orig_filename: >>" . $page['text'] . "<<"); $page['text'] = TextSection($page['text'], $orig_filename); wdbg(1,"$func: Got sub-section: >>" . wshDbgOd($page['text']) . "<<"); } $page['filename'] = $filename; $page['type'] = 'wiki'; wdbg(1,"ShReadpage(): Read text=$page[text]"); } elseif (wshIsATextFile('', $filename)) { $filename = substr($filename, strlen(TEXTFILEID)); wdbg(1,"wshReadPage(): Reading TEXT from $filename"); if ($EnableWikiShTextRead && slAuthorized($filename, $wshAuthText, 'read', true)) { clearstatcache(); // otherwise filesize() might get confused if (file_exists($filename) && ($fh = fopen($filename, 'r'))) { $page['text'] = fread($fh, filesize($filename)); wdbg(1,"wshReadPage(): text=>>$page[text]<<"); fclose($fh); $page['filename'] = $filename; $page['type'] = 'text'; $page['ctime'] = $Now; $page['time'] = $Now; $page['title'] = '$filename'; } else { $page['text'] = ''; $page['filename'] = $filename; $page['type'] = 'bad'; } } else { if (!$EnableWikiShTextRead) $msg = 'Text Reading not enabled. See $EnableWikiShTextRead configuration'; else $msg = "You do not have \"read\" authorization for text file $filename. See SecLayer configuration"; wshStdErr($pagename, $opt, "ERROR: WikiSh.Read: Unable to read from text file. $msg"); } } elseif (wshIsABadFile('', $filename)) { $filename = substr($filename, strlen(BADFILEID)); wdbg(1,"wshReadPage(): Unable to read TEXT from BAD $filename"); wshStdErr($pagename, $opt, "ERROR: WikiSh.Read: Unable to read $filename. Does not exist."); $page = array('filename' => $filename, 'type' => 'bad'); } else { // Actual contents of file is contained here due to nesting of MX // Create an approximation of a $page $page = array(); $page['filename'] = '-'; $page['ctime'] = $Now; $page['time'] = $Now; $page['title'] = '-'; $page['type'] = 'inline'; if ($onlyfile) { wshStdErr($pagename, $opt, "ERROR: wshReadPage: page $filename does not exist."); wdbg(1,"ShReadpage(): nonexistent file. returning nothing."); $page['text'] = ''; } else { wdbg(1,"ShReadpage(): processing NESTED MX type"); $page['text'] = $filename; } } if ($opt['decrypt'] && function_exists('WikiShDecrypt')) $page['text'] = WikiShDecrypt($pagename, $opt, $page['text']); return($page); } # wshWrite # pagename - just for reference purposes, to get the group name, etc. # filename - the name to write out # type - the type (wiki, text, or inline (or session)) # newtext - the text of the file/page to be written (OR overloaded with # secondary definition as the page array) # page - an existing $page array to base the new one off of (may be false) # auth - what authorization is needed to do this write # (possibilities = insert,append,prepend,overwrite) function wshWrite($pagename, $opt, $filename, $type, $newtext, $page, $slauth='overwrite', $pmauth='edit') { global $EnableWikiShTextWrite, $EnableWikiShWritePage, $WikiShWriting, $EnableWikiShCreatePage, $EnableWikiShOverwritePage, $Author, $WikiShVars, $wshAuthPage, $wshAuthText; $func = 'wshWrite'; #echo "wshWrite: Entering with filename = $filename
\n"; #global $EditFunctions; #for ($i=0; $i < sizeof($EditFunctions); $i++) wdbg(2,"wshWrite($filename): Entering"); wdbg(1,"$func: newtext=$newtext"); wdbg(1,"$func: oldtext=$page[text]"); wdbg(1,"$func: isarray(page)=".(is_array($page)?"YES":"NO")); if (wshIsATextFile('', $filename)) { $filename = substr($filename, strlen(TEXTFILEID)); if ($type != 'text') { wshStdErr($pagename, $opt, "ERROR: Write to $filename: file ID (TEXTFILEID) and type ($type) do not match. Attempting to continue."); } } if (wshIsAWikiPage('', $filename)) { $filename = substr($filename, strlen(WIKIPAGEID)); if ($type != 'wiki') { wshStdErr($pagename, $opt, "ERROR: Write to $filename: file ID (WIKIPAGEID) and type ($type) do not match. Attempting to continue."); } } wdbg(2,"wshWrite: entering: filename=$filename, type=$type"); if ($filename == "/dev/null" || $filename == "null" || $filename == "nul") { return (true); // this is not an error condition, just a statement // that we don't want to do anything... } $section = ''; $ptvname = ''; if (($pos=strpos($filename, '#')) !== FALSE) { $section = substr($filename, $pos); $filename = substr($filename, 0, $pos); } elseif (preg_match("/(?P[\w.\/]+)\\$(?P[.:]*)?(?P\()?(?P[-\w]+)(?P\))?/", $filename, $m)) { $filename = $m['file']; $ptvname = $m['varname']; if ($m['colon'] == '::') $ptvtype = 'deflist'; elseif ($m['paren1'] || $m['colon'] == '.' || !$m['colon']) $ptvtype = 'hidden'; else $ptvtype = 'text'; } wdbg(1,"$func: filename=$filename"); if (wshIsASessionFile('', $filename) || wshIsASessionGroup($filename)) { if (wshIsASessionFile('', $filename)) $filename = substr($filename, strlen(SESSFILEID)); if (is_array($newtext)) $newpage = $newtext; else { $newpage = $page; $newpage['text'] = $newtext; $newpage['csum'] = "WikiSh automatic edit"; $newpage['filename'] = $filename; $newpage['type'] = 'session'; } if(!session_id()) session_start(); $_SESSION[$filename] = $newpage; } elseif (wshIsAWikiPage(array('type'=>$type))) { if (!$EnableWikiShWritePage) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: Unable to write to wiki pages. Not enabled."); return (false); } $filename = wshMakePageName($pagename, $opt, $filename); wdbg(1,"wshWrite(): Writing out wikifile $filename"); $page_exists = PageExists($filename); if (!is_array($page)) { if ($page_exists) { if (!($page = wshRetrieveAuthPage($filename, $pmauth, false))) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: wshRetrieveAuthPage($filename, '$pmauth', ...) failed. Might there be a problem with page authorization?"); return(false); } } else { $page = array(); } } if ($section) { if (!defined('toolbox')) { wshStdErr($pagename, $opt, "ERROR: Redirecting to a section depends on installation of toolbox.php"); $WikiShVars['STATUS'] = 4; return(false); } list($a, $b, $c) = tbTextSection($page['text'], $section); wdbg(1,"$func: section=$section"); wdbg(1,"$func: a=$a"); wdbg(1,"$func: b=$b"); wdbg(1,"$func: c=$c"); if (is_array($newtext)) $newtext['text'] = $a.$newtext['text'].$c; else $newtext = $a.$newtext.$c; } elseif ($ptvname) { if (!defined('toolbox')) { wshStdErr($pagename, $opt, "ERROR: Redirecting to PTVs depends on installation of toolbox.php"); $WikiShVars['STATUS'] = 4; return(false); } wdbg(1,"$func: filename=$filename"); wdbg(1,"$func: ptvname=$ptvname"); wdbg(1,"$func: ptvtype=$ptvtype"); if (is_array($newtext)) $newtext['text'] = ptv2text($page['text'], $ptvname, $newtext['text'], $ptvtype); else $newtext = ptv2text($page['text'], $ptvname, $newtext, $ptvtype); } if (is_array($newtext)) $newpage = $newtext; else { $newpage = $page; $newpage['text'] = $newtext; $newpage['csum'] = "WikiSh automatic edit"; } wdbg(1,"$func: text being written follows"); wdbg(1,$newpage['text']); if (!$EnableWikiShCreatePage && !$page_exists) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: Unable to create pages. Not enabled."); return(false); } if (!$EnableWikiShOverwritePage && $page_exists) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: Unable to overwrite pages. Not enabled."); return(false); } # Check for SecLayer permissions according to the $slauth passed in if ($slauth != 'create' && $slauth != 'attr' && !$page_exists) $slauth = 'create'; if (!slAuthorized($filename, $wshAuthPage, $slauth)) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: You do not have \"$slauth\" authorization on the page \"$filename\"."); return(false); } if (!CondAuth($filename, $pmauth) && !slAuthorized($filename, $wshAuthPage, 'forceedit')) { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: Unable to write to $filename. No pmwiki authorization for '$pmauth'."); return(false); } #echo "wshWrite: Writing to $filename
\n"; if (!isset($Author) || empty($Author)) $Author = $WikiShVars['AUTHOR']; wdbg(1,"$func: calling UpdatePage()"); if ($WikiShWriting) { wdbg(1,"$func: File Write during recursive call of UpdatePage() SUPPRESSED."); } else { $WikiShWriting = true; if (!UpdatePage($filename, $page, $newpage)) { $WikiShWriting = false; wshStdErr($pagename, $opt, "ERROR: $func: Failure writing to $filename"); return(false); } $WikiShWriting = false; } wdbg(1,"$func: UpdatePage() has come back..."); } elseif (wshIsATextFile(array('type'=>$type))) { if ($slauth != 'create' && !file_exists($filename)) $slauth = 'create'; if ($EnableWikiShTextWrite && slAuthorized($filename, $wshAuthText, $slauth, true)) { wdbg(1,"wshWrite(): Trying to write out textfile $filename"); if ($fp = @fopen("$filename", "w")) { wdbg(1,"wshWrite(): Writing out textfile $filename"); if (is_array($newtext)) $newtext = $newtext['text']; $fwstat = fwrite($fp, $newtext); fclose($fp); if ($fwstat !== false) return(true); else { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: Unable to fwrite to $filename."); return(false); } } else { wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: Unable to open $filename."); return(false); } } else { wdbg(1, "$func: No write to $filename"); if (!$EnableWikiShTextWrite) $msg = 'Text Writing not enabled. See $EnableWikiShTextWrite configuration'; else $msg = "You do not have \"$slauth\" authorization for text file \"$filename\". See SecLayer configuration."; wshStdErr($pagename, $opt, "ERROR: WikiSh.Write: Unable to write to text file. $msg"); } } else { wshStdErr($pagename, $opt, "ERROR: wshWrite: Unknown file type=$type"); } wdbg(1,"$func: returning true"); return(true); } # This function takes an array of strings representing all the lines that # should be returned from this MXP, makes sure there is a double-backslash # at the end of each of them, and then implodes them all with \n and returns # that value. # NOTE that wshPostProcess WILL change the exit status if it is being asked to # write to a file and it is unsuccessful in that write... function wshPostProcess($pagename, $opt, $outrows, $page = array('type' => 'inline', 'text' => '', 'filename' => '-')) { global $WikiShVars; $func = 'wshPostProcess'; #for ($i = 0; $i < count($outrows); $i++) { #$outrows[$i] = "$i: $outrows[$i]"; #if (substr($outrows[$i], -2) != '\\\\') $outrows[$i] .= '\\\\'; #} #wdbg(1,implode("\n", $outrows)); wdbg(2,"$func: Entering"); wdbg(1,"$func: opt[stdout]=$opt[stdout]"); $nl = ((isset($opt['newline'])) ? $opt['newline'] : "\n"); if ($outrows) $final = implode($nl, $outrows); else $outrows = ''; if (isset($opt['markup'])) { $final = str_replace( array('{', '}', '(', ')', '<', '>', '[', ']', "'"), array('{','}','(',')','‹', '›', '[', ']', '''), $final); } $rtn = $final; wdbg(1,"$func: stdout=$opt[stdout] stdout_type=$opt[stdout_type]"); if (isset($opt['stdout'])) { $auth = 'overwrite'; //default unless overridden if (!isset($opt['stdout_type'])) { if (wshIsATextFile('', $opt['stdout'])) $opt['stdout_type'] = 'text'; elseif (wshIsASessionFile('', $opt['stdout']) || wshIsASessionGroup($opt['stdout'])) $opt['stdout_type'] = 'session'; else $opt['stdout_type'] = 'wiki'; } if (isset($opt['stdout_op']) && isset($opt['stdout_loc'])) { $stdout = $opt['stdout']; $stdout_op = $opt['stdout_op']; $stdout_loc = $opt['stdout_loc']; } elseif (preg_match("/^>>([-\w.\/]+)$/", $opt['stdout'], $match)) { wdbg(1, "$func: >> shortcut match"); $stdout = $match[1]; if (wshIsATextFile('', $stdout)) $stdout = substr($stdout, strlen(TEXTFILEID)); if (wshIsASessionFile('', $stdout)) $stdout = substr($stdout, strlen(SESSFILEID)); $stdout_op = '>'; $stdout_loc = '_END'; } elseif (preg_match("/^>([-\w.\/]+)(?:([<=>])(.+))?$/", $opt['stdout'], $match)) { wdbg(1, "$func: typical stdout match"); $stdout = $match[1]; $stdout_op = $match[2]; $stdout_loc = $match[3]; } else $stdout = $opt['stdout']; wdbg(1,"$func: OUTPUTSPEC=" . htmlspecialchars($opt['stdout']) . ", stdout=$stdout, loc=" . htmlspecialchars($stdout_loc) . ", op=" . htmlspecialchars($stdout_op)); if ($stdout_op) { wdbg(1,"$func: Inserting ($stdout_op-$stdout_loc) somewhere into stdout=".$opt['stdout']. " (stdout_type=$opt[stdout_type])"); if (!wshIsATextFile('', $stdout) && !wshIsASessionFile('', $stdout) && !wshIsAWikiPage('', $stdout)) { if ($opt['stdout_type'] == 'text') $tmp_stdout = TEXTFILEID . $stdout; elseif ($opt['stdout_type'] == 'session') $tmp_stdout = SESSFILEID . $stdout; else $tmp_stdout = WIKIPAGEID . $stdout; } wdbg(0,"$func: tmp_stdout=$tmp_stdout"); $oldpage = wshReadPage($pagename, $opt, $tmp_stdout); #$page = $oldpage; // NOTE THIS IS UNTESTED - HOPING TO FIX THE HISTORY LOSS PROBLEM #echo "ShPostProc: Reading
\n"; wdbg(1,"$func: Before (prepend/append/insert) text=$oldpage[text]"); if ($oldpage['text']) { if ($stdout_op == '>' && ($stdout_loc == '_END' || $stdout_loc == '_BOTTOM')) { wdbg(1, "$func: loc=$stdout_loc: optimized _END match"); $auth = 'append'; $final = $oldpage['text'] . "\n" . $final; } elseif ($stdout_op == '<' && ($stdout_loc == '_TOP' || $stdout_loc == '_BEGIN')) { wdbg(1, "$func: loc=$stdout_loc: optimized _TOP match"); $auth = 'prepend'; $final = $final . "\n" . $oldpage['text']; } elseif ($stdout_op == '=') { wdbg(1, "$func: loc=$stdout_loc, replacing"); if (preg_match('/^([^a-zA-Z0-9\\\\])(?:[^\1]|\\\1)*\1[^e\1]*$/', $stdout_loc)) { $final = preg_replace($stdout_loc, $final, $oldpage['text']); wdbg(1,"oldpage=$oldpage[text]"); wdbg(1,"final=$final"); } else wshStdErr($pagename, $opt, "ERROR: Location on stdout mis-formed. \"$stdout_loc\" is illegal. (must be surrounded with legal delimiters and no e modifier)"); } else { $auth = 'insert'; $rows = explode("\n", $oldpage['text']); if ($stdout_loc == '_TOP' || $stdout_loc == '_BEGIN') { wdbg(1, "$func: loc=$stdout_loc: _TOP match"); $rowloc = 0; } elseif ($stdout_loc == '_END' || $stdout_loc == '_BOTTOM') { wdbg(1, "$func: loc=$stdout_loc: _END match"); $rowloc = sizeof($rows); } else { for ($i = 0; $i < sizeof($rows); $i++) { $foundrow = false; if (preg_match("/$stdout_loc/", $rows[$i])) { $rowloc = $i; $foundrow = true; wdbg(1, "$func: loc=$stdout_loc: found pat on line $i ($rows[$i])"); break; } } if (!$foundrow) { if ($stdout_op == '<') $rowloc = 0; else $rowloc = sizeof($rows); wdbg(1, "$func: no found loc=$stdout_loc: defaulting to line $rowloc"); } } if ($stdout_op == '>') $rowloc++; if ($rowloc > 0) $final = implode("\n", array_slice($rows, 0, $rowloc)) . "\n" . $final; if ($rowloc < sizeof($rows)) $final .= "\n" . implode("\n", array_slice($rows, $rowloc)); } } wdbg(1,"$func: After (prepend/append/insert) text=$final"); } if ($opt['encrypt'] && function_exists('WikiShEncrypt')) $final = WikiShEncrypt($pagename, $opt, $final); wdbg(2,"$func: writing out to stdout=$stdout"); #echo "$func: writing out to stdout=$stdout
\n"; #echo "ShPostProc: Writing
\n"; if (!wshWrite($pagename, $opt, $stdout, $opt['stdout_type'], $final, $page, $auth)) { $WikiShVars['STATUS'] = 2; return(''); } if (!@$opt['tee']) { wdbg(2,"$func: Emptying output - no tee."); $final = ''; $rtn = ''; } } if ($opt['display'] || $opt['html']) $rtn = PVSE($rtn); wdbg(1,"$func:($stdout) returning >>" . wshDbgOd($rtn) . "<<"); return ($rtn); } # SDOpt() = set default option function wshSDOpt(&$opt, $key, $val) { if (!isset($opt[$key])) $opt[$key] = $val; } # wshExtractLines # This is the central function called for Head(), Tail(), Sed(), etc. function wshExtractLines($pagename, $opt, $filelist, $func) { wdbg(3,"wshExtractLines(): Entering"); $func .= '-EL'; wshExpandWildCards($pagename, $opt, $filelist, false, false, false); wshSDOpt($opt, 'file_prefix', ''); wshSDOpt($opt, 'line_prefix', ''); wshSDOpt($opt, 'line_suffix', ''); wshSDOpt($opt, 'inplaceedit', false); wshSDOpt($opt, 'printall', false); // Only sed would default this false # Some chars (notably slashes) need to be escaped via backslash to make it # through the search/replace pattern in Sed(). However, we don't want to # leave those backslashes in, so this chunk gets rid of escapes. if ($opt['repl']) { foreach ($opt['repl'] as $k => $v) { wdbg(0,"$func: repl before: " . wshDbgOd($v)); $opt['repl'][$k] = preg_replace("/\\\\(.)/", "$1", $v); wdbg(0,"$func: repl after: " . wshDbgOd($opt['repl'][$k])); } } wdbg(1,"opt[startline]:"); wdbg(1,$opt['startline']); wdbg(1,"opt[endline]:"); wdbg(1,$opt['endline']); wdbg(1,"opt[find]:"); wdbg(1,$opt['find']); wdbg(1,"opt[repl]:"); wdbg(1,$opt['repl']); wdbg(1,"opt[replcnt]:"); wdbg(1,$opt['replcnt']); wdbg(1,"opt[flag]:"); wdbg(1,$opt['flag']); $newrows = array(); $postrows = array(); foreach ($filelist as $filename) { $page = wshReadPage($pagename, $opt, $filename); if (wshIsABadFile($page)) { wshStdErr($pagename, $opt, "ERROR: $func: No such page: $page[filename]"); continue; } $textrows = explode("\n", $page['text']); wdbg(0,"wshExtractLines: filename=" . $page['filename'] . " textrows: "); wdbg(0,$textrows); $file_prefix_printed = false; $line_prefix = wshReplace($opt, $page, $opt['line_prefix']); $line_suffix = wshReplace($opt, $page, $opt['line_suffix']); $startlines = $opt['startline']; $endlines = $opt['endline']; // handle some specific situations for line numbers: // negative numbers should become offset from end of file // $ should become last line of file for ($i=0; $i= $start[$i]) || (!is_numeric($start[$i]) && preg_match($start[$i], $line))) $matchstatus[$i] = 1; if ($matchstatus[$i] == 1) $matched = true; // at this point I'd like to break out as an optimization, but I've got // to make sure all my $matchstatus values are set... if (!is_numeric($end[$i]) && preg_match($end[$i], $line)) $matchstatus[$i] = 0; // inclusive, just don't go on next line if (is_numeric($end[$i]) && $curlineno >= $end[$i]) $matchstatus[$i] = 2; } wdbg(0, "LineShdPrint: Returning " . (($matched)?"True":"False")); return($matched); } # # wshReplace() # Given a string, replace certain keywords with values related to the current # page. # ARGUMENTS # $opt - (currently unused, but keeping it in there because probably we'll # want it at some point) # $page - array containing at minimum $page['filename'] # $string - string to be operated on function wshReplace($opt, $page, $string, $lineno='') { wdbg(0,"wshReplace($string): Entering"); if ($string == '') return ''; $find_str = array('PAGENAME', 'PAGELINK'); $repl_str = array($page['filename'], "[[" . $page['filename'] . "]]"); if (is_numeric($lineno)) { $find_str[] = 'LINENO'; $repl_str[] = $lineno; } if (strstr($string, 'PAGETITLE')) { $find_str[] = 'PAGETITLE'; $repl_str[] = PageVar($page['filename'], '$Title'); } $string = str_replace($find_str, $repl_str, $string); wdbg(0,"wshReplace($string): Returning"); return($string); } # wshRetrieveAuthPage() # This function acts as RetrieveAuthPage() except: # (1) SecLayer 'read' authorization is required (WikiSh doesn't support the # notion of a "write-only" authorization # (2) if SecLayer 'forceread' authorization is present on a 'read' # attempt then pmwiki authorization is ignored (i.e., we go directly # to ReadPage().) Similarly on an 'edit' attempt if 'forceedit' is # present in SecLayer then pmwiki 'edit' authorization is not required - # we go straight to ReadPage(). function wshRetrieveAuthPage($pagename, $auth, $passprompt=false, $since=0) { global $wshAuthPage; $func='wshRetrieveAuthPage($pagename, $auth)'; $d=1; if (!slAuthorized($pagename, $wshAuthPage, 'read')) { wdbg($d*1, "$func: No 'read' SecLayer authorization. Returning FALSE."); # No wshStdErr() because the calling function should handle it. return(false); } if (($auth == 'read' && slAuthorized($pagename, $wshAuthPage, 'forceread')) || ($auth == 'edit' && slAuthorized($pagename, $wshAuthPage, 'forceedit'))) return(ReadPage($pagename, $since)); else return(RetrieveAuthPage($pagename, $auth, $passprompt, $since)); } # wshNotNow # (1) Pages get processed after clicking "save" on an edit and before the new # page gets loaded. That messes things up if you are writing to a file or # doing something with "wikish_once" or something, so I suppress that # processing of WikiSh MXes. # (2) Pages get processed during the UpdatePage(). This has potential to cause # an infinite loop if pagea writes to pageb and pageb writes to pagea. # Thus this processing needs to be suppressed as well (technically I could # just suppress the writing of files, but I think it'll speed things up if # I suppress all WikiSh markup processing). # Note that since wshNotNow() is called at the start of every wikish MX it is # a convenient place to handle RC file processing. Doesn't fit with the name # of the function, but what's a guy to do?! :-) function wshNotNow($pagename, $OverrideActive = false) { global $WikiShVars, $WikiShRCPages, $WikiShRCRules, $action, $WikiShWriting, $MXWhileEditing, $wshAuthPage; static $RCDone = false; $OKActions = array('browse', 'search', 'approvesites'); $func = 'wshNotNow()'; wdbg(1,"$func: Entering (action=$action, writing=".($WikiShWriting?"TRUE":"FALSE").")"); # Since this function is called at the very beginning of nearly every # WikiSh MX I go ahead and run the RC commands here. It would be better # to do it in the mainline, but we don't have other necessary # initializations done at that point. if (!$RCDone) { $RCDone = true; // to prevent never-ending recursion $OldDfltDbg = $WikiShVars['DEFAULT_DEBUG']; $OldDbg = $WikiShVars['DEBUGLEVEL']; $WikiShVars['DEBUGLEVEL'] = $WikiShVars['RC_DEBUG']; foreach ($WikiShRCPages as $k => $rcpagename) { wdbg(3,"$func: RCPage=$rcpagename (pagename=$pagename)"); $tmp = wshMakePageName($pagename, array(), FmtPagename($rcpagename, $pagename)); wdbg(1,"$func:post RCPage=$rcpagename (pagename=$pagename)"); if (!$tmp || !PageExists($tmp)) { wdbg(3,"$func: RCPage=$rcpagename does not exist"); continue; } if (!($page = wshRetrieveAuthPage($tmp, 'read', false))) { wshStdErr($pagename, array(), "ERROR: $func: no read authorization for $tmp"); continue; } if (defined('toolbox')) $text = RunMarkupRules($pagename, $WikiShRCRules, $page['text']); else $text = $page['text']; $text = wshParseCode($text); MarkupExpression($rcpagename, "(wikish $text)"); } if ($OldDfltDbg == $WikiShVars['DEFAULT_DEBUG']) $WikiShVars['DEBUGLEVEL'] = $OldDbg; // back to prior debug level else // they explicitly set a new default in RC processing - use it $WikiShVars['DEBUGLEVEL'] = $WikiShVars['DEFAULT_DEBUG']; } if ((!$OverrideActive && !$WikiShVars['ACTIVE']) || (!in_array($action, $OKActions) && !$MXWhileEditing) || ($MXWhileEditing && $action != 'edit') || $WikiShWriting) { wdbg(1,"$func: returning true"); return(true); } else { wdbg(1,"$func: returning false"); return(false); } } function wshMakePageName($pagename, $opt, $tgt) { if (wshIsATextFile('', $tgt)) return($tgt); return(MakePageName($pagename, $tgt)); } # wshUnHTMLSpecialChars() # This function will undo the most commonly replaced strings when converting # to HTML special chars # If $onlyescaped is true then the < has to have a backslash before it in # order for it to be replaced - this facilitates sed 's/\ '<', ($onlyescaped?'\\':'').'≤' => '<=', ($onlyescaped?'\\':'').'>' => '>', ($onlyescaped?'\\':'').'≥' => '>=', ($onlyescaped?'\\':'').' ' => ' ', ($onlyescaped?'\\':'').'&' => '&' ); $str = str_replace(array_keys($rplc), array_values($rplc), $str); wdbg(1,"unhtml: returning >>" . wshDbgOd($str) . "<<"); return($str); } # wshParseCode # This function reads code from an external file and converts it into the # single-line, semi-colon delineated format the wikish so loves... # Comments will be stripped as long as there is whitespace immediately # preceding the pound sign (or if the # is at the start of the line). # Functions will be defined (and stripped) as appropriate. function wshParseCode($code, $SuppressKeep = false) { global $wshFunctionList, $MarkupExpr; global $KeepToken, $KPV; $rpat = "/$KeepToken(\\d+P)$KeepToken/e"; $rrep = '$KPV[\'$1\']'; $func = "ParseCode"; wdbg(2, "$func: Entering"); wdbg(1,"$func: code=>>" . wshDbgOd($code) . "<<"); #$code = str_replace("\r", "", $code); wdbg(1,"$func: after linefeed replace code=>>" . wshDbgOd($code) . "<<"); $newlines = array(); if (preg_match_all("/^\\s*(?:function\\s+(?P\\w+)\\s*(?:\\(\\))?|(?P\\w+)\\s*\\(\\))\\s*\\{(?P.*?)^\\}/sim", $code, $m, PREG_SET_ORDER)) { foreach ($m as $funcdef) { wdbg(1, "$func: fname1=$funcdef[fname1], fname2=$funcdef[fname2], fdef=$funcdef[fdef]"); $funcname = ($funcdef['fname1']?$funcdef['fname1']:$funcdef['fname2']); wdbg(1, "$func: funcname=$funcname"); $wshFunctionList[$funcname] = 'wikish ' . wshParseCode($funcdef['fdef']); $MarkupExpr[$funcname] = "wshDoFunction(\$pagename, '$funcname', \$params, \$exiting)"; $code = str_replace($funcdef[0], "", $code); } } wdbg(1,"wshFunctionList:"); wdbg(1,$wshFunctionList); $codelines = explode("\n", $code); foreach ($codelines as $line) { if ($SuppressKeep) { # we've got to get rid of quoted strings so they can contain a # # without it being stripped as a comment. wdbg(1, "$func: Before dumping quotes: " . wshDbgOd($line)); $line = preg_replace('/(([\'"]).*?\\2)/e', "Keep(PSS('$1'),'P')", $line); } else { $line = preg_replace('/([\'"])(.*?)\\1/e', "Keep(PSS('$2'),'P')", $line); $line = preg_replace('/\\(\\W/e', "Keep(PSS('$2'),'P')", $line); } wdbg(1,"$func: After dumping quotes: >>" . wshDbgOd($line) . "<<"); # Note that the whitespace before the comment is necessary so that # we can have page sections specified as MyGroup.MyPage#MySection $line = preg_replace("/(?:^|\s)\s*#.*$/", "", $line); wdbg(1,"$func: After dumping comments: >>" . wshDbgOd($line) . "<<"); if ($SuppressKeep) { $line = preg_replace($rpat, $rrep, $line); } wdbg(1,"$func: After replacing quotes: >>" . wshDbgOd($line) . "<<"); if ($line && !preg_match("/^\s*$/", $line)) $newlines[] = $line; } $newcode = implode(";", $newlines); $newcode = preg_replace("/;*$/", "", $newcode) . ';'; wdbg(1,"$func: returning >>" . wshDbgOd($newcode) . "<<"); return($newcode); } # wshStdErr() # This function handles error messages # Error messages are routed based on the values of # $opt['stderr'] # ! stdout - just put the error messages in with stdout # messages - put error messages in $MessagesFmt[] for (:messages:) # * echo - echo error messages directly/immediately via echo # ! PAGE/FILE- write to this page or file # ! PAGE>PAT - write to page after pattern (_END or _BOTTOM valid) # ! PAGE
\n"; } else { $MessagesFmt[] = $text . "\n"; } break; case 'echo': if (is_array($text)) { echo "
" . print_r($text,true) . "

\n"; } else { echo $text . "
\n"; } break; case 'nul': case 'null': case '/dev/null': // Do nothing break; default: echo "ERROR: Unhandled stderr=$opt[stderr]
\n"; } } if (!function_exists("wdbg")) { # Printlevel indicates what level of importance this line is. # 0=never print # 1=very detailed debugging # 2=a little less detailed # 3=fairly normal debugging # 4=high-level, print it without thinking about it # (you can go higher, but the indentation doesn't work) function wdbg($printlevel, $text, $txt2 = '', $txt3 = '', $txt4='', $txt5='') { global $MessagesFmt, $WikiShVars, $EnableWikiShDebug; #if ($printlevel >= 3) echo "$text (" . (microtime(true) - $WikiShVars['SECONDS_START']) . ")
\n"; if (!@$EnableWikiShDebug || $printlevel<$WikiShVars['DEBUGLEVEL']) return; foreach (array($text, $txt2, $txt3) as $txt) { if ($txt == '') break; elseif (is_array($txt)) { $MessagesFmt[] = "
" . print_r($txt,true) . "
"; } else { $suffix = ""; $prefix = ''; for ($i = 4-$printlevel; $i > 0; $i--) { $prefix .= "
"; $suffix .= "
"; } $prefix .= "
  • "; $MessagesFmt[] = $prefix . $txt . $suffix . "\n"; } #echo $MessagesFmt[sizeof($MessagesFmt)-1] . "
    \n"; } } } // if !function_exists() # Generic markup used for testing... $MarkupExpr["wikish_devtest"] = 'wshDevTest($pagename, @$argp, @$args)'; function wshDevTest($page, $opts, $args) { global $WikiShVars, $InputValues, $FmtV, $foo; echo "PRE: " . print_r($opts,true) . "
    \n"; } ## wshMatchPageName is blatantly copied and slightly modified from ## MatchPageName() in pmwiki.php. Credit is thus deserved and duly rendered. ## A page is handed to wshMatchPageName (not a list of pages as before). ## An array of patterns is also passed in. As with MatchPageNames, the ## patterns can be specified as follows: ## Patterns can be either regexes to include ('/'), regexes to exclude ('!'), ## or wildcard patterns (all others) (wildcard patterns optionally ## preceded by - or ! to indicate that they are to be excluded. ## The difference with wshMatchPageName is that all INCLUSIVE elements in ## the array will be logically joined with OR instead of AND. EXCLUSIVE ## elements are still logically joined to all other conditions with the ## boolean "AND NOT". ## ## A page MUST match some INCLUSIVE condition and must NOT match any EXCLUSIVE ## condition if it is to be matched. ## function wshMatchPageName($page, $pat, $allowslash = false) { $MatchedInclusive = false; foreach((array)$pat as $p) { if (!$p) continue; switch ($p{0}) { case '/': if (preg_match($p, $page)) $MatchedInclusive = true; continue; case '!': if (preg_match($p, $page)) return(false); continue; default: if ($allowslash) list($inclp, $exclp) = GlobToPCRE($p); else list($inclp, $exclp) = GlobToPCRE(str_replace('/', '.', $p)); if ($exclp && preg_match("/$exclp/i", $page)) return(false); if ($inclp && preg_match("/$inclp/i", $page)) $MatchedInclusive = true; } } return $MatchedInclusive; } ## wshGlobToPCRE is yet another prime example of prudent borrowing. ## All credit to PM -- I needed this function to not automatically assuming ## anchors and it was easier to copy it in and modify it slightly. I also ## got rid of the - and the ! and the exclude array and etc. ## Basically now it converts a wildcard pattern into a pcre pattern function wshGlobToPCRE($pat, $anchor_beg=false, $anchor_end=false, $greedy=true) { $pat = preg_quote($pat, '/'); $pat = str_replace(array('\\*', '\\?', '\\[', '\\]', '\\^'), array('.*' . ($greedy?'':'?'), '.', '[', ']', '^'), $pat); $incl = array(); foreach(preg_split('/,+\s?/', $pat, -1, PREG_SPLIT_NO_EMPTY) as $p) { $incl[] = ($anchor_beg?'^':'') . "$p" . ($anchor_end?'$':''); } return (implode('|', $incl)); } #BUGLIST # Add in usage messages obtained with -? option on all commands # # Not a bug, but I need to update the appropriate page in pmwiki to tell them # that [@...@] surrounding (:markup:)[= ... =] gets ignored. That is, the # [@ ... @] gets ignored -- the stuff gets processed. # # bug: echo abc >Test.Abc#abc # does not seem to update history #ROADMAP (in addition to that on the web) # # Document more "pmwiki" type of options (stdout=PAGE instead of >PAGE kind # of thing) # # --checkpoint:seconds argument to sed and perhaps others (capability # exists using read --saveset and --restoreset, but it would be nice to # avoid having to write a loop when sed can handle it completely...) # # -w or -g option for sed to use glob patterns # # -e for grep to have multiple patterns # -f for grep to get patterns (fixed strings?) from file # -h (--no-filename) and -H (--with-filename) for grep (default -H if # multi-args) # -n for line numbers in grep # # ${body/wikibox-password:/wikibox-password:xyz/} ended up putting a / on the # end of the replaced text... # # I've temporarily added the $WikiShVars['MAILXRULES'] but really it should be # more flexible with a $WikiShRules['a'], ...['b'], etc. that the user can # then choose different rulesets rather than being tied to a single one... # # If you are accessing a temporary/virtual/session file then you shouldn't # check for CondAuth() -- there could be a site-wide password that you could # be stepping on when non-pmwiki-pages should not be subject to that kind # of restriction