<?php
/**
* The UniSearcher function library. It sets some 'config' variables too. They come first.
**/

$G=array();
ExtractGlobals(); // Slightly safer than $_REQUEST.

define('CLIPBOARD_LINK''<a href="Javascript: PopIt(\'\')">');

$hrcolor="cc66cc";
$DbConnector="/etc/unisearch/db.php";
$maxresults=1000// the most glyphs to ever show at once on a glyph-list page
$pageblocks=array();
$page=hexpad($G['page'], 2); // Will default to "00" if $G['page'] isn't set yet.
if (isset($G['subpage']))
    
$subpage=substr($G['subpage'], 01); // should be only one digit long...
else
    unset(
$subpage);

// Unfortunately, hexdec("YerMama") returns 3,754. I blame PHP. But it means I can't check
// hex strings for validity, other than seeing if it evals to more than it should...
// The only solution would be to pass *everything* as decimal instead of hex. Lots of rewriting.
if (hexdec($page) < || hexdec($page) > 0xFF$page="00";           // can't exceed 255
if (hexdec($subpage) < || hexdec($subpage) > 0xF) unset($subpage); // can't exceed 15
if (hexdec($glyph) < || hexdec($glyph) > 0xFFFFF) unset($glyph);   // can't exceed 1,048,575

if ($_SERVER["GATEWAY_INTERFACE"]=="") {
    
// We're running the parser from cmdline, which is the only time we need to know $DbPacketMax.
    
$DbPacketMax=FALSE;
    
$res=dosql("show variables");
    if (
mysql_num_rows($res)>0) {
        while (
$row=mysql_fetch_row($res)) {
            if (
$row[0]=="max_allowed_packet") {
                
$DbPacketMax=$row[1];
                break;
            }
        }
    }
    if (!
$DbPacketMax) { $DbPacketMax=1024*1024; } // We'll default to one meg ("1M" in mysqlspeak)
}

// I consider 'glyph' a search var because it doesn't lead to the 256-char chart page.
$SearchVars=array('shiftjis''unihex''unidecimal''utf8hex''utf8''desctext''deftext''prontext''glyph');

// Special unicode control characters:
define('EMBED_LR',      0x202A);
define('EMBED_RL',      0x202B);
define('POP_DF',        0x202C);
define('OVER_LR',       0x202D);
define('OVER_RL',       0x202E);
define('ZERO_SPACE',    0x200B);
define('ZERO_NJOINER',  0x200C);
define('ZERO_JOINER',   0x200D);
define('ZERO_NBSP',     0xFEFF);
define('VARSEL1',       0xFE00);
define('VARSEL2',       0xFE01);
define('VARSEL3',       0xFE02);
define('VARSEL4',       0xFE03);
define('VARSEL5',       0xFE04);
define('VARSEL6',       0xFE05);
define('VARSEL7',       0xFE06);
define('VARSEL8',       0xFE07);
define('VARSEL9',       0xFE08);
define('VARSEL10',      0xFE09);
define('VARSEL11',      0xFE0A);
define('VARSEL12',      0xFE0B);
define('VARSEL13',      0xFE0C);
define('VARSEL14',      0xFE0D);
define('VARSEL15',      0xFE0E);
define('VARSEL16',      0xFE0F);

function 
dosql($sq) {
    
// This executes the SQL query specified in $sq.
    
global $dblink$DbConnector;
    require_once(
$DbConnector);
//  echo "<h3>$sq</h3>\n";
    
$res=mysql_query($sq$dblink);
    if (
mysql_error($dblink)) {
        echo 
"<h3>SQL Query Failure!</h3>\n<I>" .
            
mysql_error($dblink) . ":</I><P>\n$sq<P>\n";
    }
    return 
$res;
}

function 
MakeQueryString() {
    global 
$PHP_SELF;
    if (empty(
$_GET) && empty($_POST)) return $PHP_SELF;
    if (
substr($PHP_SELF01) != '/') {
        
$out="/$PHP_SELF";
    } else {
        
$out=$PHP_SELF;
    }
    
$tmparr=array_merge($_GET$_POST); // the 2nd array takes precedence when an element from each has the same key.
    
foreach (array_keys($tmparr) as $k) {
        
$tmparr[$k]=rawurlencode($tmparr[$k]);
    }
    
$tmpstr=ImplodeQueryString($tmparr);
    if (
$tmpstr=='') return $out;
    
$out .= CleanupQueryString($tmpstr);
    return 
$out;
}

function 
ExplodeQueryString($in) {
    
// Returns an assoc. array holding a querystring's variables, leaving them url-encoded (or not).
    
$out=array();
    if (!
preg_match('/.*?\?(.+)/'$in$regs)) return $out;
    
$vars=trim($regs[1]);
    foreach (
preg_split('/\&/'$vars, -1PREG_SPLIT_NO_EMPTY) as $pair) {
        if (!
preg_match('/(.+?)=(.+)/'$pair$regs)) continue; // auto-deletes empty vars
        
$out[trim($regs[1])]=trim($regs[2]);
    }
    return 
$out;
}

function 
ImplodeQueryString($vars) {
    
// This only returns the querystring part, starting with '?'. Add your own script filename before it.
    // Doesn't touch a var's url-encoding, just like the Explode version; rawurlencode() your vars before calling this.
    
if (empty($vars)) return '';
    
$out='?';
    foreach (
$vars as $k=>$v) {
        
$out.="$k=$v&";
    }
    if (
$out == '?') return '';
    return 
substr($out0, -1);
}

function 
CleanupQueryString($in) {
    
// Removes variables from $in that aren't set to anything (in "?a=&b=2&c=", a and c would be removed).
    
$vars=ExplodeQueryString($in);
    if (empty(
$vars)) return $in;
    
$out=array();
    foreach (
array_keys($vars) as $k) {
        if (
$k=='' || empty($vars[$k])) {
            continue;
        }
        
$out[$k]=$vars[$k];
    }
    return 
ImplodeQueryString($out);
}

function 
footer() {
  global 
$start;
  echo 
"<P align=\"center\"><font size=\"-1\"><I>(c)2005, <a href=\"mailto:phee@isthisthingon.org\">Brett Baugh</a> " .
      
"- but it's open source.</I> (<a href=\"ex.html\" target=\"_blank\">Brief Description</a> | " .
      
"<a href=\"unisearch-1.0.tgz\">Source Code Distribution</a>)</font></P>\n" .
      
"</body></html>\n";
  die;
  return;
}

function 
show_blocks($start$end$pf=FALSE) {
  
// Makes the little "key" table at the top of 256-char charts for color-coding each char block.
  
global $pageblocks;
  if (
$pf$pf=TRUE; else $pf=FALSE;
  
$allblocks=loadblocks();
  
$Dstart=hexdec($start);
  
$Dend=hexdec($end);
  
$tmp=array();
  foreach (
$allblocks as $b) {
    if (
$b[1]<$Dstart || $b[0]>$Dend) { continue; }
    
$tmp[$b[0]]=$b;
  }
  
asort($tmp);
  
$out="<table border=1 cellpadding=3 cellspacing=0>\n<tr>";
  
$lenchk=strlen($out);
  
$i=0;
  foreach (
$tmp as $block) {
    
$pageblocks[]=array($block[0], $block[1]);
    
$hs=strtoupper(hexpad(dechex($block[0])));
    
$he=strtoupper(hexpad(dechex($block[1])));
    
$color=bgcolor($block[0]);
    if (
$pf)
      
$out.="{$block[2]}&nbsp;(0x$hs-0x$he), ";
    else
      
$out.="<td bgcolor=\"$color\"><B>{$block[2]}</B>&nbsp;(0x$hs-0x$he)</td>";
    if (!
$pf && ++$i%3==0) { $out.="</tr>\n<tr>"; }
  }
  if (
strlen($out)==$lenchk) { return FALSE; }
  if (!
$pf && $i%&& $i>3) {
    
$cs=3-($i%3);
    
$out.="<td bgcolor=\"#999999\" colspan=\"$cs\">&nbsp;</td>";
  }
  if (
$pf) {
    
$out=substr($out0, -2);
  } else {
    
$out .= "</tr>\n</table>\n";
  }
  return 
$out;
}

function 
block_menu() {
  
// Makes the select field for picking what char block to zip to.
  
global $allblocks;
  
$out="<select name=\"codeblock\" onChange=\"submit();\">\n" .
    
"<option value=\"\">--Jump To Character Block--</option>\n";
  foreach (
$allblocks as $block) {
    
$starthex=strtoupper(hexpad(dechex($block[0])));
    
$endhex=strtoupper(hexpad(dechex($block[1])));
    
$s=substr($starthex03);
    
$out .= "<option value=\"$s\">0x$starthex-0x$endhex: {$block[2]}</option>\n";
  }
  
$out .= "</select>\n";
  return 
$out;
}

function 
split_pagenum($in) {
  
// Turns "3" OR "03" into array("0", "3"); "1F" into array("1", "F"); etc.
  
$in=hexpad((string)$in2);
  if (
preg_match('/^(.)(.)$/'$in$regs)) {
    
$upage=(string)$regs[1];
    
$rpage=(string)$regs[2];
  } elseif (
preg_match('/^(.)$/'$in$regs)) {
    
$upage="0";
    
$rpage=(string)$regs[1];
  } else {
    
$upage="0";
    
$rpage="0";
  }
  if (
$upage==''$upage="0";
  if (
$rpage==''$rpage="0";
  return array(
$upage$rpage);
}

function 
show_upag() {
  
// Shows the top row of the pretty pagination selector menu table.
  
global $page$subpage$PHP_SELF;
  echo 
"<tr>\n<th align=right class=\"range\">Top-level:&nbsp;</th>\n<td>\n" .
    
"<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\">\n<tr>\n";
  list(
$upage$rpage)=split_pagenum($page);
  for (
$i=0$i 16$i++) {
    
$hex=strtoupper(dechex($i));
    if (
$upage==$hex) { $c="pagsel"; } else { $c="pag"; }
    echo 
"<th valign=\"bottom\" width=\"20\" class=\"$c\">" .
      
"<a class=\"$c\" href=\"$PHP_SELF?page=$hex$rpage\">$hex</a></th>\n";
  }
  echo 
"<td align=\"center\" class=\"range\"><I>Page/Subpage:</I></td>\n</tr>\n</table>\n</td></tr>\n";
  return;
}

function 
show_pag() {
  
// Shows the middle row of the pretty pagination selector menu table.
  
global $page$subpage$PHP_SELF;
  list(
$upage$rpage)=split_pagenum($page);
  echo 
"<tr>\n<th align=right class=\"range\">Next-level:&nbsp;</th>\n<td>\n" .
    
"<table border=0 cellpadding=3 cellspacing=0>\n<tr>\n";
  for (
$i=0$i 16$i++) {
    
$hex=strtoupper(dechex($i));
    if (
trim($rpage)!="" && $rpage==$hex) { $c="pagsel"; } else { $c="pag"; }
    echo 
"<th valign=\"bottom\" width=\"20\" class=\"$c\">" .
      
"<a class=\"$c\" href=\"$PHP_SELF?page=$upage$hex\">$hex</a></th>\n";
  }
  echo 
"<th align=left class=\"range\">";
  if (
trim($rpage)!="") {
    
$from=$page."000";
    
$to=$page."FFF";
    
$dfrom=number_format(hexdec($from));
    
$dto=number_format(hexdec($to));
    echo 
"$from - $to ($dfrom - $dto)";
  } else {
    echo 
"&nbsp;";
  }
  echo 
"</th></tr>\n</table>\n</td></tr>\n";
  return;
}

function 
show_sub() {
  
// Shows the bottom row of the pretty pagination selector menu table.
  
global $page$subpage$PHP_SELF;
  echo 
"<tr>\n<th align=\"right\" class=\"range\">Last-level:&nbsp;</th>\n<td>\n" .
    
"<table width=\"100%\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\">\n<tr>\n";
  for (
$i=0$i<16$i++) {
    
$hex=strtoupper(dechex($i));
    if (
trim($subpage)!="" && $subpage==$hex) { $c="pagsel"; } else { $c="pag"; }
    echo 
"<th valign=\"bottom\" width=\"20\" class=\"$c\">" .
      
"<a class=\"$c\" href=\"$PHP_SELF?page=$page&subpage=$hex\">$hex</a></th>\n";
  }
  echo 
"<th align=\"left\" class=\"range\">";
  if (
trim($subpage)!="") {
    
$from=$page.$subpage."00";
    
$to=$page.$subpage."FF";
    
$dfrom=number_format(hexdec($from));
    
$dto=number_format(hexdec($to));
    echo 
"$from - $to ($dfrom - $dto)";
  } else {
    echo 
"&nbsp;";
  }
  echo 
"</th></tr>\n</table>\n</td></tr>\n";
  echo 
show_prevnext();
  return;
}

function 
show_prevnext() {
  
// Shows the "previous/next page" links for under the other 'pag' tables.
  // Only show it if there is a $subpage set.
  
global $page$subpage$PHP_SELF;
  if (!isset(
$subpage)) return '';
  
$dpage=hexdec($page);
  
$dsub=hexdec($subpage);
  
$prevP=$nextP=$dpage;
  
$prevS=$dsub-1;
  
$nextS=$dsub+1;
  if (
$prevS 0) {
    
$prevS=15;
    --
$prevP;
  }
  if (
$prevP 0$prevP=FALSE;
  if (
$nextS == 16) {
    
$nextS=0;
    ++
$nextP;
  }
  if (
$nextP 255$nextP=FALSE;
  foreach (array(
'prevP''nextP') as $k) {
    if ($
$k===FALSE) continue;
    $
$k=hexpad(strtoupper(dechex($$k)), 2);
  }
  foreach (array(
'prevS''nextS') as $k) {
    $
$k=strtoupper(dechex($$k));
  }

  
$out="<tr>\n<th nowrap align=\"right\" class=\"range\">Block Nav:&nbsp;</th>\n<td>\n" .
    
"<table width=\"448\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\">\n<tr>\n" .
    
'<th nowrap class="pag" align="left" valign="top">';
  if (
$prevP!==FALSE) {
    
$out .= "<a class=\"pag\" href=\"$PHP_SELF?page=$prevP&subpage=$prevS\">Previous&nbsp;(0x$prevP$prevS"."00)</a>";
    if (
$nextP!==FALSE$out .= ' | ';
  }
  if (
$nextP!==FALSE) {
    
$out .= "<a class=\"pag\" href=\"$PHP_SELF?page=$nextP&subpage=$nextS\">Next&nbsp;(0x$nextP$nextS"."00)</a>";
  }
  
$out.="</th>\n</tr>\n</table>\n";
  return 
$out;
}

function 
bgcolor($val) {
  
// Returns one of several colors to use in color-coding char blocks in a chart page.
  
global $pageblocks;
  
$blockcolor=array("#e0ffff""#ffe0ff""#ffffe0");
  
$tmp=FALSE;
  foreach (
array_keys($pageblocks) as $b) {
    if (
$pageblocks[$b][0] <= $val && $pageblocks[$b][1] >= $val) {
      
$tmp=$b;
      break;
    }
  }
  if (
$tmp===FALSE) {
    
// $val isn't in ANY block.
    
return "#dddddd";
  }
  
$curblock=$tmp count($blockcolor);
  return 
$blockcolor[$curblock];
}

function 
show_pf_chart() {
  
// Makes a printer-friendly 256-char fullpage chart.
  
global $page$subpage$allcodes$PHP_SELF;
  
$combiners=CombiningGlyphs();
  
$start="$page$subpage";
  
$out "<table cellpadding=\"1\" cellspacing=\"0\" class=\"PFtable\">\n<tr>\n";
  for (
$i=0$i<256$i++) {
    
$ihex=hexpad(dechex($i), 2);
    
$hex="$start$ihex";
    
$hex2=hexpad(dechex(hexdec($hex)));
    
$uni=hexdec($hex);
    
$u8h=Codepoint2Utf8Hex(array($uni));
    
$unichar="&#$uni;";
    if (
$combiners[$uni]) {
        
$comb='<span class="PFcombiner">' CombinedChar($hex) . '</span>';
    } else {
        
$comb='<span class="PFglyph">' $unichar '</span>';
    }

    
$out .= "<td class=\"PFcode\">" .
      
'<span class="PFinfo">' .
      
"$hex2<BR>" .
      
"$u8h<BR>" .
      
"&amp;#$uni;" .
      
"<BR></span>" .
      
"$comb</td>\n";

    if (
$i%16==15) {
      
$out .= "</tr>\n<tr>\n";
    }
  }
  
$out .= "</tr>\n</table>\n";
  echo 
$out;
}

function 
show_chart() {
  
// Makes the big 256-char fullpage chart.
  
global $page$subpage$allcodes$PHP_SELF$hilite;
  
$combiners=CombiningGlyphs();
  
$start="$page$subpage";
  
$blocks=show_blocks($start."00"$start."FF");
  if (
$blocks) echo "<font size=-1>Character block(s) on this page:<BR>\n$blocks<P>\n";
  else echo 
"<font size=-1>This page contains no Unicode-defined character blocks.<P>\n";
  echo 
"<I>Click on a character to copy it to the " CLIPBOARD_LINK .
      
"Unisearch Clipboard</a>.</I><BR>\n" .
      
"<I>Click a character's Hex Codepoint (top row) to see its details.</I><BR>\n" .
      
'<a target="_blank" href="pfpopup.php?page='.$page.'&subpage='.$subpage.'">Printer-Friendly Version</a></font><BR>' .
      
"<table border=\"1\" cellpadding=\"3\" cellspacing=\"0\">\n<tr>\n";
  for (
$i=0$i<256$i++) {
    
$ihex=hexpad(dechex($i), 2);
    
$hex="$start$ihex";
    
$hex2=hexpad(dechex(hexdec($hex)));
    
$uni=hexdec($hex);
    
$jis=$allcodes[$hex];
    
$u8h=Codepoint2Utf8Hex(array($uni));
    
$deturl="$PHP_SELF?page=$page&subpage=$subpage&glyph=$hex";
    if (
$jis) {
      
$jis=hexpad(dechex(hexdec($jis)), 4);
    } else {
      
$jis="<I>None</I>";
    }
    if (
$hilite && hexdec($hilite)==hexdec($hex)) {
      
$bgc="#ffff66";
    } else {
      
$bgc=bgcolor($uni);
    }
    
$unichar="&#$uni;";
    if (
$combiners[$uni]) {
        
$comb='<font color="#CC0000">' CombinedChar($hex) . '</font>';
    } else {
        
$comb=$unichar;
    }
    echo 
"<td nowrap valign=\"top\" align=\"center\" class=\"code\" bgcolor=\"$bgc\">" .
      
'<font style="font-size: 8pt">' .
      
"<a class=\"plain\" href=\"$deturl\">" .
      
"$hex2</a><BR>" .
      
"$u8h<BR>" .
      
"$jis<BR>" .
      
"&amp;#$uni;" .
      
"<BR>" .
      
"<BR></font>" .
      
"<div onMouseOver=\"myHint.show($uni);\" onMouseOut=\"myHint.hide();\">" .
      
"<a class=\"glyph\" href=\"Javascript: PopIt($uni)\">$comb</a></div>" .
      
"</td>\n";
    if (
$i%16==15) {
      echo 
"</tr>\n<tr>\n";
    }
  }
  echo 
"</tr>\n</table>\n";
  return;
}

function 
show_row($sjis$descr$pron$def$hexcode) {
  
// Utility function for show_glyph()...
    
global $PHP_SELF;
    
$combiners=CombiningGlyphs();
    
$out="";
    
$idx=hexpad($hexcode);
    
$pagetmp=substr($idx0, -3);
    
$subtmp=substr($idx, -31);
    
$url="$PHP_SELF?page=$pagetmp&subpage=$subtmp&hilite=$idx";
    
$dec=hexdec($idx);
    if (
$combiners[$dec]) {
        
$comb='<font color="#CC0000">' CombinedChar($idx) . '</font>';
    } else {
        
$comb="&#$dec;";
    }
    
$u8h=Codepoint2Utf8Hex(array($dec));
    if (
$descr==""$descr="<I>No Description</I>";
    if (
$sjis==""$sjis="<I>None</I>";
    else 
$sjis=hexpad(dechex(hexdec($sjis)), 4);
    
$block=show_blocks($idx$idx);
    
$out .= "<table border=\"1\" cellpadding=\"3\" cellspacing=\"0\">\n" .
      
"<tr><td rowspan=\"5\" align=\"center\" valign=\"middle\" bgcolor=\"#ffffff\" width=\"80\">" .
      
"<a class=\"glyph\" href=\"Javascript: PopIt($dec)\">" .
      
"<font style=\"font-size: 48pt\">$comb</font></a></td>\n" .
      
"<td><a class=\"plain\" href=\"Javascript: PopIt($dec)\">$descr</a></td></tr>\n" .
      
"<tr><td>Unicode (Hex): <a class=\"plain\" href=\"$url\"><B>$idx</B></a></td></tr>\n" .
      
"<tr><td>UTF-8 (Hex): <B>$u8h</B></td></tr>\n" .
      
"<tr><td>Shift-JIS (Hex): <B>$sjis</B></td></tr>\n" .
      
"<tr><td>Unicode (HTML): <B>&amp;#$dec;</B></td></tr>\n" .
      
"</tr>\n</table>\n";
    if (
$block$out .= $block;
    
$RS='<tr><th nowrap align="right" valign="top">';
    
$RM='</th><td align="left" valign="top">';
    
$RE="</td></tr>\n";
    
$out .= "<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\">\n";
    if (
$def$out .= $RS "Definition:$RM$def$RE";
    if (
$pron$out .= $RS "Pronunciation(s):$RM$pron$RE";
    
$out .= "</table>\n<hr noshade size=1>\n";
    return 
$out;
}

function 
show_glyph(&$idxarr) {
  
// Displays all details about the character whose codepoint is $idxarr if it's a hex string.
  // If it's an array, does it for all elements in it, doing the SQL query all at once.
  // All input elements MUST be the unicode codepoint for one character and must be a hex string
  // (like, "0A" for an ascii 10, "63A2" for a 0xE68EA2 in UTF8, etc).

  
global $allcodes$alldescs$alldefs$allprons$desctext$deftext$prontext$maxresults;
  if (!
is_array($idxarr)) $idxarr=array($idxarr);
  if (
count($idxarr) > $maxresults) {
    echo 
"Too many results found (over " number_format($maxresults) .
        
"); please refine the search parameters and try again...<BR>\n";
    return;
  }
  
$thestring='';
  
$mpop='';
  
$where="deccode in (";
  
$lenchk=strlen($where);
  foreach (
$idxarr as $k=>$idx) {
    
$idx=HexOnly($idx);
    
$dec=hexdec($idx);
    if (
$idx=='' || $dec 1) { // Make sure this one doesn't get shown...
        
unset($idxarr[$k]);
        continue;
    }
    
$where .= "$dec,";
    
$mpop .= "$dec:";
    
$thestring .= Codepoint2Utf8(array($dec));
  }
  if (
strlen($where)==$lenchk) {
    echo 
"No results found (illegal input perhaps?).<BR>\n";
    return;
  }
  
$where=substr($where0, -1) . ")";
  
$mpop=substr($mpop0, -1);
//echo "WHERE: $where<BR>MPOP: $mpop<BR>";
  
$sq="select * from codepoint where $where";
//echo "SQ: $sq<BR>";
  
$res=dosql($sq);
  
$out="<hr noshade size=1>\nClick on a character (or its name) to copy it to the " .
    
CLIPBOARD_LINK "Unisearch Clipboard</a>.<BR>\n" .
    
"<a class=\"plain\" href=\"Javascript: PopMulti('$mpop')\">Click to copy " .
    (
$desctext || $deftext || $prontext ?
      
"all these characters"  :  "\"<font style=\"font-size: 14pt\">$thestring</font>\""
    
) . " to the clipboard.</a></I><BR>\n" .
    
"<hr noshade size=1>\n";
  if (
mysql_num_rows($res) > 0) {
    
$tmpout=array();
    while (
$row=ass(mysql_fetch_assoc($res))) {
      
$h=$row['hexcode'];
      if (!isset(
$tmpout[$h]) && $h$tmpout[$h]=$row;
    }
    
// MySql reorders the results by deccode, the PK. How to tell it not to do that?
    // Answer: You can't. So I have to re-order them myself based on the $idxarr input. Yay.
    
foreach ($idxarr as $h) {
      
$r=$tmpout[$h];
      if (!
is_array($r)) $r=array('hexcode'=>$h'descr'=>'<I>Not In Unicode Database</I>');
      
$out .= show_row($r['sjis'], $r['descr'], $r['pron'], $r['def'], $r['hexcode']);
    }
  } elseif (
$idxarr[0]) { // I'm hoping it held at least ONE entry... but this should never happen, anyway.
    
$out .= show_row(""""""""$idxarr[0]);
  }
  echo 
$out;
  return;
}

function 
HexOnly($in) {
  
// Forces $in to only have valid hex digits in it.
  
return preg_replace(array('/0x/i''/[^a-f\d]/i'), ''trim($in));
}

function 
is_hex($in) {
  
// Tells you whether $in *looks* like valid hexadecimal or not.
  
return (preg_match('/^[a-f\d]+$/i'HexOnly($in)));
}

function 
hexpad($in$chars=5$reverse=FALSE) {
  
// Makes sure $in is at least $chars characters long by left-padding it with "0".
  // Setting $reverse makes it RIGHT-pad it with "0" instead.
  
$in=strtoupper(trim($in)); // because lower-case hex makes me ***TWITCH!!!***
  
if ($chars==FALSE) return $in;
  
$in=preg_replace('/^0+/'''$in); // trim off leading 0's first... it's not counter-productive, trust me.
  
if ($reverse$out=str_pad($in$chars"0"STR_PAD_RIGHT);
  else 
$out=str_pad($in$chars"0"STR_PAD_LEFT);
  return 
$out;
}

function 
ss($in) {
  return 
htmlspecialchars(stripslashes($in));
}

function 
ass($array) {
    if (!
is_array($array)) return stripslashes($array);
    foreach (
array_keys($array) as $k) {
        if (
is_array($array[$k])) $array[$k]=ass($array[$k]);
        else 
$array[$k]=stripslashes($array[$k]);
    }
    return 
$array;
}

function 
ArraySupersort(&$in$sortby$reverse=FALSE) {
    
$out=$sorter=array();
    foreach (
array_keys($in) as $k) {
        
$sorter[$k]=$in[$k][$sortby];
    }
    
natcasesort($sorter); // This can change to meet your needs... asort(), uksort(), whateversort().
    
if ($reverse$sorter=array_reverse($sorterTRUE);
    foreach (
array_keys($sorter) as $k) {
        
$out[]=$in[$k];
    }
    
$in=$out;
}

function 
GMT() {
    
// A wrapper for microtime to get it as a float instead of a string with a space in it... :'/
    
$tmp=preg_split('/\s+/'microtime(), -1PREG_SPLIT_NO_EMPTY);
    return (float)(
$tmp[0]+$tmp[1]);
}

function 
SQuote($in) {
    if (!
is_array($in))
        return 
mysql_real_escape_string(ass($in));
    foreach (
array_keys($in) as $k) {
        
$in[$k]=SQuote($in[$k]);
    }
    return 
$in;
}  

function 
jsquote($in) {
/*
 * Cleans up a string meant to be put into javascript as a variable's setting delimited by ' marks.
 * JS doesn't like \n in the middle of a definition, so this also breaks it up by replacing every
 * CR/LF with " ' +\n  '", which even indents the code nicely. Like, this:

HINTS_ITEMS[20496]='<I>Cantonese:</I> Suk-1<BR>
<I>Japanese On:</I> Shuku<BR>
<BR><I>Hastily; suddenly</I>';

 * ...becomes this:

HINTS_ITEMS[20496]='<I>Cantonese:</I> Suk-1<BR> ' +
  '<I>Japanese On:</I> Shuku<BR> ' +
  '<BR><I>Hastily; suddenly</I>';

 * See how much nicer that looks? :) Just don't replace ' with \' AFTER calling this or you'll suffer THE PAIN!!
 */

  
$from=array("/\\'/""/'/"'/[\r\n]+/');
  
$to=array("'""\\'"" ' +\n  '");
  return 
preg_replace($from$to$in);
}

function 
qs($in) {
    
// Fixes values for insertion into <form> fields' value="" attribute.
    
return preg_replace('/"/''&quot;'$in);
}

function 
progressbar($current$total) {
  
// makes the 0% - 100% status bar thingy. Used in the parser only.
  
if ($current>$total) return;
  if (
$total==1) {
    echo 
"100%"flush();
    return;
  }
  if (
$current==1) {
    echo 
"0%-"flush();
    return;
  }
  if (
$current==$total) {
    echo 
"100%"flush();
    return;
  }
  
$percent=floor(($current/$total)*100);
  if (
$percent 10 == && (($current-1)/$total*100) < $percent)
    echo 
"$percent%-"flush();
}

function 
loadblocks($nameorder=TRUE$usehex=FALSE) {
  
// This loads up ALL blocks, since they're all needed on every page for the dropdown. Return array:
  // $allblocks[n]=array(0=>block_start_integer, 1=>block_end_integer, 2=>block_name_string)
  // If $nameorder is FALSE, it will be ordered by the start - otherwise, by block name.
  // If $usehex isn't FALSE, it will return hex strings for start/end instead of integers.
  
static $allblocks;
  if (
$nameorder==FALSE || $usehex) unset($allblocks); // Only cache the default-params output!
  
if (!isset($allblocks)) {
    
$allblocks=array();
    if (
$usehex$sq="select hex(start), hex(end), name from block"; else $sq="select start, end, name from block";
    if (
$nameorder$sq.=" order by name"; else $sq.=" order by start";
    
$res=dosql($sq);
    while (
$row=ass(mysql_fetch_row($res))) {
      
$allblocks[]=array($row[0], $row[1], $row[2]);
    }
  }
  
$out=$allblocks;
  if (
$nameorder==FALSE || $usehex) unset($allblocks); // Only cache the default-params output!
  
return $out;
}

function 
loadpage($start) {
  
// Loads up the descs, defs, prons, and sjis ("codes") arrays for the
  // 256 characters starting at $start (in DECIMAL).
  
global $alldescs$allprons$alldefs$allcodes;
  
$end=$start+255// We always show exactly 256 characters per chart page. ALWAYS. Even if none exist.
  
$sq="select hexcode, sjis, descr, pron, def from codepoint where deccode >= $start and deccode <= $end order by deccode";
  
$res=dosql($sq);
  while (
$row=ass(mysql_fetch_row($res))) {
    
$cp=$row[0];                // hexadecimal codepoint
    
$allcodes[$cp]=$row[1];     // SJIS code, if any
    
$alldescs[$cp]=$row[2]; // Character description
    
$allprons[$cp]=$row[3]; // Pronunciation, if any
    
$alldefs[$cp]=$row