php/envOut.php
<?php
/* output handling for cli or html
procedural design
*/
###----------- out: output formatting, for commandline or html -----------
if (! OutHtml) { # commandline formatting
function outPush($last, $fun, $fmt = '') { # push for commandline formatting
global $outStack;
$tos = count($outStack)-1;
$nn = substr("\n", (int) ($last == '+nl' | $last == '-h2' | $last == '+be' )); # nl needed because not at new line
if ($fun == 'be') {
return '';
} elseif ($fun[0] == 'w') {
if ($last[1] == 'w')
return substr(' ', $fun[1] === 'a');
elseif ($last == '-ol' or $last == '-ul' or $last == '-tb')
return "\n" . ($outStack[$tos][0] == 'li' ? str_repeat(' ', $outStack[$tos][1] * 4 +2) : '');
} elseif ($fun == 'nl') {
return "\n" . ($outStack[$tos][0] == 'li' ? str_repeat(' ', $outStack[$tos][1] * 4 +2) : '');
} elseif ($fun == 'h2') {
return "$nn\n--- ";
} elseif ($fun == 'ol' or $fun == 'ul') {
$outStack[$tos][2] = 0;
return $nn;
} elseif ($fun == 'li') {
return ($last == '-li' ? "\n" : '') . str_repeat(' ', $outStack[$tos-1][1] * 4)
. (($lt = $outStack[$tos-1][0]) == 'ol' ? ++$outStack[$tos-1][2] : '')
. substr($lt == 'ul' ? '?*** ' : '?... ', min($outStack[$tos-1][1], 3));
} elseif ($fun == 'tb') {
return $nn;
} elseif ($fun == 'tr') {
$outStack[0]['tb']['tr'] = [];
} elseif ($fun == 'td' or $fun == 'th') {
outWork('#s'); # store cell data
$outStack[0]['tb']['rcf'][$outStack[0]['tb']['rx']][$outStack[0]['tb']['cx']] = "$fun$fmt";
} else {
err("bad terminal outPush($fun)");
}
return '';
}
function outPop($last, $fun) { # pop for commandline
global $outStack;
if ($fun == 'be')
return '';
elseif ($fun == 'h2')
return " ---\n";
elseif ($fun == 'ol' or $fun == 'ul' or $fun == 'li')
return '';
elseif ($fun == 'li')
return "";
elseif ($fun == 'tb')
return outGenTb($outStack[0]['tb']); # gen stored table
elseif ($fun == 'tr')
$outStack[0]['tb'][] = $outStack[0]['tb']['tr'];
elseif ($fun == 'td' or $fun = 'th')
$outStack[0]['tb']['tr'][] = outWork('#r');
else
err("bad terminal outPop($fun)");
return '';
}
function outGenTb($t) {
global $outStack;
for($rL = count($t); $rL > 0 and !isset($t[$rL-1]); $rL--) {}
$cM = 0;
$w = [];
$h = array_fill(0, $rL, 0);
for ($i=0; $i < $rL; $i++) {
$cM = max($cM, $c = count($t[$i]));
for ($k=0; $k < $c; $k++) {
$a = $t[$i][$k] = explode("\n", $t[$i][$k]);
$h[$i] = max($h[$i], count($a));
$l = ($t['rcf'][$i][$k][1] == 'h' ? 2 : 0) + (isset($a[0]) ? strlen($a[0]) : 0);
for ($x=1; $x<count($a); $x++)
$l = max($l, iconv_strlen($a[$x]));
$w[$k] = isset($w[$k]) ? max($w[$k], $l+2) : $l + 2;
}
}
$hL = count($h);
$wL = count($w);
$r = '';
for ($i=0; $i < $hL; $i++) {
for ($j=0; $j < $h[$i]; $j++) {
$r .= "\n";
$br = $j == 0 ? '+' : '|';
for ($k=0; $k < $wL; $k++) {
if (isset($t['rcf'][$i][$k]))
$ff = $t['rcf'][$i][$k];
$v = ('' == $v = isset($t[$i][$k][$j]) ? $t[$i][$k][$j] : '') ? '' : " $v " ;
$p = str_repeat(($ff[1] == 'h' and $j == 0) ? '*' : ' ', $w[$k] - iconv_strlen($v));
$r .= ($ff[1] == 'h' ? '*' : $br) . ($ff[2] == 'r' ? "$p$v" : "$v$p");
}
$r .= $br;
}
}
$tt = str_repeat('-', array_sum($w) + count($w) + 1);
return "$tt$r\n$tt";
}
function highlightNum($fi) { # highlight file with lineNumbers
}
# end terminal mode
} else { # generate html output
function outPush($last, $fun, $fmt = '') {
global $outStack;
if ($fun == 'be')
return "<html>\n<head>"
. '<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0, user-scalable=yes"/>'
. ($fmt == '' ? '' : "<title>$fmt</title>")
. "\n</head>\n<body style='font-family: monospace;' >\n";
elseif ($fun[0] == 'w')
return substr(' ', $last[1] !== 'w' or $fun[1] === 'a'); # space between words
elseif ($fun == 'nl')
return '<br>';
elseif ($fun == 'ol' or $fun == 'ul')
return ($last == '+li'? " " : '') . "<$fun>"; # bug in firefox, shows <li><ol> shows on same line!
elseif ($fun == 'td' or $fun == 'th')
return "<$fun style='text-align: " . ($fmt == 'r' ? 'right' : 'left') . ";'>";
elseif ($fun == 'li' or $fun == 'h2' or $fun == 'td' or $fun == 'th' or $fun == 'tr')
return "<$fun>";
elseif ($fun == 'tb')
return "<table border=2>";
else
err("bad html outPush($last, $fun, $fmt)");
}
function outPop($last, $fun) {
global $outStack;
if ($fun == 'be')
return (($f = $outStack[0]['endFile']) ? "$f<br><br>" . highlightNum($f) : '') . "\n</body>\n</html>\n";
elseif ($fun == 'ol' or $fun == 'ul' or $fun == 'li' or $fun == 'h2' or $fun == 'td' or $fun == 'th' or $fun == 'tr')
return "</$fun>\n";
elseif ($fun == 'tb')
return "</table>";
else
err("bad outPop($fun)");
}
function highlightNum($fi) { # highlight file with lineNumbers
$s = highlight_file($fi, true);
$ln = 1;
$hn = fn ($no) => '<span style="color: #ffffff; background-color: #a0e0a0;">'
. strtr(sprintf('%6u ', $no), [' ' => ' ']) . '</span> ';
$cy = strpos($s, '<br />');
if (false !== ($sx = strpos($s, '<span')) and false !== ($sy = strpos($s, '>', $sx)) and $sy < $cy) {
# lineNo 1 after first span and possibly \n which must remain after first span
$cx = $sy + 1 + (substr($s, $sy, 2) === ">\n");
$r = substr($s, 0, $cx) . $hn($ln) . substr($s, $cx, $cy + 6 -$cx);
}
else {
$r = '*** code does not have a span berfore first <br>***' . substr($s, 0, $cy + 6);
}
while (FALSE !== ($cy = strpos($s, '<br />', $cx=$cy+6))) {
$r .= $hn(++$ln) . substr($s, $cx, $cy + 6 - $cx);
}
return $r . substr($s, $cx);
}
} # end generate html output
function outWork() { /* handle stack and call output specific functions outPush and outPop
variable number of inputs
-ty* space separated list of types to pop from stack
#ty assert ty is on top of stack and pop it
ty push ty and if it is wo, wa or h2 add data in next argument
missing pops or push's are generated
*/
global $outStack;
static $last = '+00',
$oo = '',
$ooStack = [],
$doEcho = true;
$fna = func_num_args();
for ($ax=0; $ax < $fna; $ax++) {
$fun = func_get_arg($ax);
$tos = count($outStack)-1;
$toTy = $outStack[$tos][0]; # top of stack type
if ($fun[0] == '-') {
while (true) {
if ($tos < 0)
err("stack popped empty tos=$tos");
$ty = $outStack[$tos][0];
if (strpos($fun, $ty) === false)
break;
outWork("#$ty");
if ($tos != count($outStack))
err("fun=$fun bad pop $tos ==>", count($outStack));
$tos--;
}
} else if ($fun[0] == '#') {
if (strlen($fun) == 3) {
if ($toTy != substr($fun, 1))
err("fun=$fun but topOfStack " . a2str($outStack[$tos]));
$oo .= outPop($last, $toTy);
$last = "-$toTy";
array_pop($outStack);
if ($toTy == 'tb')
$outStack[0]['tb'] = array_pop($outStack[0]['tbStack']); # null if empty
} elseif ($fun == '#s') {
if ($doEcho)
echo $oo;
else
$ooStack[] = $oo;
$oo = '';
$doEcho = false;
} elseif ($fun == '#r') {
if ($doEcho)
err('#r but $doEcho');
$r = $oo;
if ( null === $oo = array_pop($ooStack)) {
$doEcho = true;
$oo = '';
}
return $r;
} else {
err("bad fun $fun in outWork");
}
} else {
if ($toTy == 'ol' or $toTy == 'ul') { # generate missing li
$outStack[] = [$toTy = 'li', $outStack[$tos][1]];
$oo .= outPush($last, 'li');
$last = "+li";
}
if ($toTy == 'tb') { # generate missing tr
$outStack[] = [$toTy = 'tr'];
$outStack[0]['tb']['rx'] ++;
$outStack[0]['tb']['cx'] = -1;
$oo .= outPush($last, 'tr');
$last = "+tr";
}
if ($toTy == 'tr' and $fun != 'tr') { # generate missing td or th
$cx = ++$outStack[0]['tb']['cx'];
$fx = count($outStack[0]['tb']['cf']);
$f = $outStack[0]['tb']['cf'][$fx > $cx ? $cx : $fx-1];
if ($fH = (strpos($f, '!') !== false)) {
$f = str_replace('!', '', $f);
if ($fun == 'td')
$fun = 'th';
}
$outStack[] = [$toTy = ($fH or $fun == 'th') ? 'th' : 'td', 0];
$oo .= outPush($last, $toTy, $f);
$last = "+$toTy";
}
if ($fun == 'li' or $fun == 'td' or $fun == 'th' or $fun == 'tr') {
if ($fun != $toTy or "+$fun" != $last)
err("mismatch fun=$fun <> old=$toTy"); # should be created above !
} else {
if ($fun == $toTy or $fun == 'nl' or $fun[0] == 'w') { # no push on $outstack
} elseif ($fun == 'ol' or $fun == 'ul') {
$outStack[] = [$fun, $outStack[count($outStack)-1][1]+1]; # increase list level
} else {
$outStack[] = [$fun];
if ($fun == 'tb') {
if (isset($outStack[0]['tb']))
$outStack[0]['tbStack'][] = $outStack[0]['tb'];
else
$outStack[0]['tbStack'] = [];
$outStack[0]['tb'] = ['rx' => -1, 'cf' => ['l']];
}
}
$oo .= outPush($last, $fun, ($fun === 'be' and ++$ax < $fna) ? func_get_arg($ax) : '');
$last = "+$fun";
}
if ( $fun[0] == 'w' or $fun == 'h2' )
$oo .= func_get_arg(++$ax);
}
}
if ($doEcho) {
echo "$oo";
$oo = '';
}
}
function outBegin () { # first call, initialize everything, write title
global $outStack, $dbg;
$outStack[] = ['be', 0, 'last' => '+00'];
$i = [];
$t = [];
if (func_num_args() > 0) {
$t[] = a2str(func_get_arg(0));
$i[] = "begin " . (func_num_args() == 1 ? $t[0] : i2str(func_get_args()));
}
$t[] = basename(envScript());
$i[] = implode(", ", envArgs());
outWork('be', $t[0]);
outH($i[0]);
$o = ini_set('zend.assertions', 1);
$n = ini_get('zend.assertions');
if ($dbg) {
outUL();
for ($k=1; $k < count($i); $k++)
outLi($i[0]);
outLi("phpversion=" . phpversion());
outLi("zend.assertions=$n", $n == 1 ? " from $o" : "could not activate");
outULEnd();
}
}
function outEnd ($t = false) { # last call, print source ($t) and cleanup
global $outStack;
outH("end " . basename($t ? $t : envScript()) . ': ' . implode(', ', envArgs()));
$outStack[0]['endFile'] = $t;
outWork('#be');
}
function out() { # data and nl
if (func_num_args() > 0)
outWork('wo', func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()), 'nl');
else
outWork('nl');
}
function outNL() { #nl and data
if (func_num_args() > 0)
outWork('nl', 'wo', func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
else
outWork('nl');
}
function outEC() { # data in format $ = $, ... and nl
if (func_num_args() > 0)
outWork('wo', func_num_args() == 1 ? a2str(func_get_arg(0)) : i2strEC(func_get_args()), 'nl');
else
outWork('nl');
}
function outW() { # data (without any nl)
outWork('wo', func_num_args() == 0 ? false
: ( func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args())));
}
function outA() { # data (without any nl)
outWork('wa', func_num_args() == 0 ? false
: ( func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args(), '')));
}
function outWEC() { # data (without any nl) in format equal comma: $ = $, ...
outWork('wo', func_num_args() == 0 ? false
: ( func_num_args() == 1 ? a2str(func_get_arg(0)) : i2strEC(func_get_args())));
}
function outACE() { # data (without any nl) in format comma equal: , $ = $, ...
outWork('wa', ', ' . ( func_num_args() == 1 ? a2str(func_get_arg(0)) : i2strEC(func_get_args())));
}
function outH() { # header
outWork('-li ol ul', 'h2',
func_num_args() <= 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()), '#h2');
}
function outOL() { # ordered=numbered list, optional with first item
outWork('ol');
if (func_num_args() > 0)
outLi(func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
}
function outLi() { # listitem (in ol or ul)
outWork('-li','li');
if (func_num_args() > 0)
outWork('wo', func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
}
function outOLEnd() { # end ordered list
if (func_num_args() > 0)
outLi(func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
outWork('-li', '#ol');
}
function outUL() { # undordered= bulleted list, optional with first item
outWork('ul');
if (func_num_args() > 0)
outLi(func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
}
function outULEnd() { # end undordered list
if (func_num_args() > 0)
outLi(func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
outWork('-li', '#ul');
}
function outTb() { # table begin, optional with first td
outWork('tb');
if (func_num_args() > 0)
outTD(func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
}
function outTCF() { # table column format, currently only l (left align default) and r (right align)
global $outStack;
$f = func_get_args();
if ( 0 >= $c = count($f))
$f =['l'];
else
for ($i=0; $i < $c; $i++)
if (! (isset($f[$i]) and ($f[$i] == 'l' or $f[$i] == 'r' or $f[$i] == '!l' or $f[$i] == '!r' )))
err('bad format in outTCF(', $f, ')');
$outStack[0]['tb']['cf'] = $f;
}
function outTR() { # row begin, optional with first td
outWork('-li ol ul td th tr', 'tr');
if (func_num_args() > 0)
outTD( func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
}
function outTRD() { # row begin, with 0 to n datacells
outTR();
foreach (func_get_args() as $d)
outTD($d);
}
function outTRH() { # row begin, with 0 to n headercells
outTR();
foreach (func_get_args() as $h)
outTH($h);
}
function outTD() { # td = dataCell begin, optional with first words
outWork('-li ol ul td th', 'td');
if (func_num_args() > 0)
outWork('wo', func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
}
function outTDD() { # tdd = 0 to n dataCells
foreach (func_get_args() as $d)
outTD($d);
}
function outTH() { # th = headerCell begin, optional with first words
outWork('-li ol ul td th', 'th');
if (func_num_args() > 0)
outWork('wo', func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
}
function outTHH() { # 0 to n headerCell
foreach (func_get_args() as $h)
outTH($h);
}
function outTbEnd() { # table end
if (func_num_args() > 0)
outTd(func_num_args() == 1 ? a2str(func_get_arg(0)) : i2str(func_get_args()));
outWork('-li ol ul td th tr', '#tb');
}