Bontiv-Sourceer source code viewer
Root | Help
./bontiv-sourceer/sourceer.php
<?php

/*
Bontiv-Sourceer 1.0, 30 Jan 2012
GPL v3 license
Forked from Sourceer (http://bioinformatics.org/phplabware/internal_utilities/)
A PHP Labware internal utility - www.bioinformatics.org/phplabware/internal_utilities
*/

/*
PHP file/directory utility software/script; a directory/file lister/browser with source code (highlighted) viewer. Among other things, useful for presenting source code of software projects (a much simple alternative to Trac, PHPDoc, Doxygen, SVN/CVS systems, etc.) Uses code of DirPHP v1.0 by Stuart Montgomery.

Put sourceer.php (can be renamed) in appropriate directory. Set PARAMETERS at top. sourceer.php can be included in another script; the root directory then will be the directory of that script. Delete/comment out the PARAMETERS code above MAIN CALL code if using that code in the parent file.
*/

error_reporting(E_ALL | (defined('E_STRICT') ? E_STRICT : 0));
ini_set('display_errors', 0); // 1 to debug

// PARAMETERS - the 4 arrays can be empty; one or more $cfg elements may not be present

$sec_files = array(); // files not to be shown - put in filepaths (no trailing slashes, /) relative to $cfg 'root' ; depending on other parameters, may not be accessible as well; e.g., "array('./a.php', '../b.php')". PHP PCRE-compatible regular expressions specified by putting in an array like "array('!\.htaccess$!i', '!\.ini$!i')"

$sec_dirs = array(); // as $sec_files but for directories; no trailing slashes (/)

$src_filetypes = array(''=>'txt', 'css'=>'css', 'htaccess'=>'txt', 'htm'=>'html', 'html'=>'html',
   
'info'=>'txt', 'inc'=>'txt', 'js'=>'js', 'php'=>'php', 'txt'=>'txt', 'xml'=>'xml', 'c' => 'c',
   
'h' => 'c', 'cc' => 'c', 'hh' => 'h', 'am' => 'txt', 'in' => 'txt', 'ac' => 'txt', 'pl' => 'pl',
   
'xml' => 'xml', 'py' => 'py', 'cpp' => 'c'); // source code viewable for these types - lower-case extensions and equivalent filetype as key-value pairs; the empty extension '' is for file-names without extensions;

$cfg = array(
 
// title for the web-pages
 
'title' => 'Bontiv-Sourceer source code viewer',
 
// root directory for browsing. Use '.' if same as sourceer.php (or the parent script when sourceer.php is included), or './..' for the directory above it, and so on.
 
'root' => './src_dir',
 
// 1 to allow move to higher level than root
 
'up_root' => 0,
 
// if 1, a password or correct IP address needed
 
'auth' => 0,
 
// MD5 hash of password prefixed with 'sourceer' [the code here generates the hash for the default password 'pass' (change 'pass' to something else; for more security, create MD5 hash of your new password prefixed with 'sourceer' and enter that hash - e.g., 'fgdjhg467sfhj87654gsg')]. A password is needed if 'auth' is 1 above and IP address is not in 'ok_ips' below
 
'hash' => md5('sourceer'. 'pass'),
 
// function to use for highlighting. If 0, PHP's highlighting function, which really works only for PHP file-types, will be used. If set to a string, will call the named function -- the raw source code, the file-type, the file-path and the config charset will be provided as arguments. The function should return an array with these four non-keyed values: the formatted code, CSS declarations, JS code, and code for foot that will be added to the HTML.
 
'hiliter' => 0,
 
// allowed IP addresses; like "array('129.0.0.1', '129.0.0.2')"
 
'ok_ips' => array(),
 
// CSS and Javascript declarations and HTML to add before and after the output. E.g., '<div id="xx">' or '<body>' for 'head', and '' or '</table>' for foot. To have a short and context-specific title auto-inserted by Sourceer, use '_Sourceer_dynamic_title_' in the text. Set to 0 or remove to let Sourceer create them. If set as an array, like "array('<p>Home</p>')", will get appended to default head  or CSS or JS (or prepended to foot) created by Sourceer. If not array but string, will replace.
 
'css' => 0,
 
'js' => 0,
 
'head' => 0,
 
'foot' => 0,
 
 
// ** Unlikely to change anything below
 
 // charset
 
'charset' => 'utf-8', // best if same as filesystem's
 // compress output
 
'compress' => 1,
 
// cookie name to use for auto-login (1 h validity) when using password
 
'cookie' => 'sourceer',
 
// date format; PHP's date() function-compatible
 
'date_type' => 'm/d/y',
 
// if $src_filetypes file-types downloadable
 
'dl' => 0,
 
// to indicate file-sizes & mod. times
 
'file_info' => 1,
 
// language - RFC3066 specified-values such as 'en' for English
 
'lang' => 'en',
 
// string to append to URLs. E.g., if sourceer.php is used at URL 'domain.com/wiki.php?page=home', you may want to set it to 'page=home'
 
'query_plus' => '',
 
// if $sec_dirs can be looked into
 
'sec_dir_into' => 0,
 
// show $sec_dirs in directory content lists
 
'sec_dir_list' => 0,
 
// if $sec_files downloadable
 
'sec_file_dl' => 0,
 
// show $sec_files in directory content lists
 
'sec_file_list' => 0,
 
// show source of $sec_files
 
'sec_file_src' => 0,
 
// turn off checking files/dirs for being secured
 
'sec_check_off' => 0,
 
// show source code of $src_filetypes file-types
 
'src' => 1,
 
// script execution time limit, in seconds
 
'timeout' => 300,
 
// base URL (URL to sourceer.php, or to the parent script using it); code here figures it out automatically, but you can change it to, e.g., "http://domain.com/source/sourceer.php", "sourceer.php", "domain.com/wiki.php?page=home", etc.
 
'base_url' => (!empty($_SERVER['PHP_SELF']) ? htmlspecialchars($_SERVER['PHP_SELF'], ENT_COMPAT) : preg_replace('`(\?.*)?$`','',$_SERVER['REQUEST_URI'])),
);
// ends PARAMETERS

// MAIN CALL

$sourceer = new Sourceer($sec_dirs, $sec_files, $src_filetypes, $cfg); // the 4 parameters may not even be specified ("sourceer();")
$sourceer->work(); // ends MAIN CALL

// CLASS DEFINITION

class Sourceer{
 
 var
$cfg;
 var
$foot;
 var
$list_total;
 var
$neck;
 var
$out;
 var
$sec_dirs;
 var
$sec_files;
 var
$self;
 var
$src_filetypes;

 function
Sourceer($sec_dirs = array(), $sec_files = array(), $src_filetypes = array(), $cfg = array()){ // constructor function

  // defaults
 
$cfg = is_array($cfg) ? $cfg : array();
 
$cfg['auth'] = (isset($cfg['auth']) and $cfg['auth'] == 1) ? 1 : 0;
 
$cfg['charset'] = isset($cfg['charset']) ? $cfg['charset'] : 'utf-8';
 
$cfg['base_url'] = isset($cfg['base_url']) ? $cfg['base_url'] : (!empty($_SERVER['PHP_SELF']) ? htmlspecialchars($_SERVER['PHP_SELF'], ENT_COMPAT, $cfg['charset']) : preg_replace('`(\?.*)?$`','',$_SERVER['REQUEST_URI']));
 
$cfg['compress'] = (isset($cfg['compress']) and $cfg['compress'] == 1) ? 1 : 0;
 
$cfg['cookie'] = (isset($cfg['cookie']) and !isset($cfg['cookie'][64])) ? $cfg['cookie'] : 'sourceer';
 
$cfg['date_type'] = isset($cfg['date_type']) ? $cfg['date_type'] : 'm/d/y';
 
$cfg['dl'] = (isset($cfg['dl']) and $cfg['dl'] == 1) ? 1 : 0;
 
$cfg['file_info'] = (isset($cfg['file_info']) and $cfg['file_info'] == 1) ? 1 : 0;
 
$cfg['hash'] = isset($cfg['hash']) ? $cfg['hash'] : md5('sourceer'. 'pass');
 
$cfg['hiliter'] = (!empty($cfg['hiliter']) and function_exists($cfg['hiliter'])) ? $cfg['hiliter'] : 0;
 
$cfg['lang'] = isset($cfg['lang']) ? $cfg['lang'] : 'en';
 
$cfg['ok_ips'] = (isset($cfg['ok_ips']) and is_array($cfg['ok_ips'])) ? $cfg['ok_ips'] : array();
 
$cfg['query_plus'] = isset($cfg['query_plus']) ? $cfg['query_plus'] : '';
 
$cfg['root'] = (isset($cfg['root']) and strlen($cfg['root'] = trim(preg_replace('`///*`', '/', $cfg['root']), '/'))) ? $cfg['root'] : '.';
 
$cfg['sec_check_off'] = (isset($cfg['sec_check_off']) and $cfg['sec_check_off'] == 1) ? 1 : 0;
 
$cfg['sec_file_dl'] = (isset($cfg['sec_file_dl']) and $cfg['sec_file_dl'] == 1) ? 1 : 0;
 
$cfg['sec_dir_into'] = (isset($cfg['sec_dir_into']) and $cfg['sec_dir_into'] == 1) ? 1 : 0;
 
$cfg['sec_dir_list'] = (isset($cfg['sec_dir_list']) and $cfg['sec_dir_list'] == 1) ? 1 : 0;
 
$cfg['sec_file_list'] = (isset($cfg['sec_file_list']) and $cfg['sec_file_list'] == 1) ? 1 : 0;
 
$cfg['sec_file_src'] = (isset($cfg['sec_file_src']) and $cfg['sec_file_src'] == 1) ? 1 : 0;
 
$cfg['src'] = (isset($cfg['src']) and $cfg['src'] == 1) ? 1 : 0;
 
$cfg['timeout'] = (isset($cfg['timeout']) and ctype_digit($cfg['timeout']) and $cfg['timeout'] > 10) ? $cfg['timeout'] : 300;
 
$cfg['title'] = isset($cfg['title']) ? str_replace(array('<', '>', '"'), array('&lt;', '&gt;', '&quot;'), $cfg['title']) : 'Bontiv-Sourceer file and code viewer';
 
$cfg['up_root'] = (isset($cfg['up_root']) and $cfg['up_root'] == 1) ? 1 : 0;

 
// CSS in HTML head
 
if(!isset($cfg['css']) or $cfg['css'] === 0 or is_array($cfg['css'])){
   
$cfg['css'] = "<style type=\"text/css\" media=\"all\" id=\"sourceerStyle\"><!--/*--><![CDATA[/*><!--*/\na:link, a:visited { text-decoration: none; color: blue; }\na:hover, a:active { text-decoration: underline; }\nbody, div, html, p { font-size: 14px; font-family:\'Lucida Grande\', \'Lucida Sans Unicode\', \'Helvetica\', \'Verdana\', sans-serif; }\ndiv.Smsg { color: red; padding-top: 5px; padding-bottom: 5px; }\ndiv.Sdir { margin-bottom: 1px; margin-top: 3px; } /* directory item */\ndiv.Sfile1 { margin-bottom: 1px; margin-top: 3px; } /* source-viewable file item */\na.Sfile1:link, a.Sfile1:visited { color: green; }\ndiv.Sfile2 { margin-bottom: 1px; margin-top: 3px; } /* file item */\ndiv.Sprop { margin-left: 50px; display:inline; font-size: 80%; color: gray; }\ndiv.Ssubtle { font-size: 80%; color: gray; text-align: right; }\nspan.Snew { color: black; } /* new item */\nspan.Syoung { color: #333333; } /* young item */\nspan.Sold { color: #999999; } /* old item */\nspan.Sancient { color: #cccccc; } /* ancient item */\n#Sbody {}\n#Scode { background-color: #efefef; padding: 5px; margin-bottom: 5px; font-family:\'Bitstream Vera Sans Mono\', \'Courier New\', \'Courier\', monospace; } /* source code */\n#Sfoot { margin-top: 10px; padding-top: 5px; padding-bottom: 5px; border-top: 1px solid gray; }\n#Shead { margin-bottom: 10px; padding-top: 5px; padding-bottom: 5px; border-bottom: 1px solid gray; }\n#Slist {} /* file listing */\n#Slog { padding-top: 5px; padding-bottom: 5px; } /* login */\n#Smain { margin: auto; width: 95%; font-family:\'Lucida Grande\', \'Lucida Sans Unicode\', \'Helvetica\', \'Verdana\', sans-serif; } /* outer-most */\n#Sneck { margin-bottom: 10px; padding-top: 5px; padding-bottom: 5px; font-size: 130%; border-bottom: 1px dotted gray; } /* path */\n#Stotal { display: none; margin-left: 50px; font-size: 70%; color: gray; } /* with JS-based sort links */\n/*]]>*/--></style>". (is_array($cfg['css']) ? $cfg['css'][0] : '');
  }

 
// JS in HTML head
 
if(!isset($cfg['js']) or $cfg['js'] === 0 or is_array($cfg['js'])){
   
$cfg['js'] = "<script type=\"text/javascript\"><!--//--><![CDATA[//><!--   //--><!]]></script>". (is_array($cfg['js']) ? $cfg['js'][0] : '');
  }

 
// HTML head
 
if(!isset($cfg['head']) or $cfg['head'] === 0 or is_array($cfg['head'])){
   
ob_start();
   echo
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"><html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"", $cfg['lang'], "\" lang=\"", $cfg['lang'], "\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=", $cfg['charset'], "\" /><meta http-equiv=\"Content-Language\" content=\"", $cfg['lang'], "\" /><meta name=\"description\" content=\"", $cfg['title'], "; PHP Labware Bontiv-Sourceer code, file, directory browser, viewer\" /><meta name=\"keywords\" content=\"", $cfg['title'], ", Bontiv-Sourceer, PHP code/file/directory viewer/browser, PHP Labware, code, file, directory, browse, view, PHP\" />\n_Sourceer_dynamic_css_\n_Sourceer_dynamic_js_\n<title>_Sourceer_dynamic_title_ | ", $cfg['title'], "</title>\n</head>\n<body>\n<div id=\"Smain\">\n<div id=\"Shead\">", $cfg['title'], (is_array($cfg['head']) ? $cfg['head'][0] : ''), "</div><!-- ended div Shead -->\n";
   
$cfg['head'] = ob_get_contents();
   
ob_end_clean();
  }

 
// HTML foot
 
if(!isset($cfg['foot']) or $cfg['foot'] === 0 or is_array($cfg['foot'])){
   
$cfg['foot'] = "<div id=\"Sfoot\">". (is_array($cfg['foot']) ? $cfg['foot'][0] : ''). "</div><!-- ended div Sfoot -->\n</div><!-- ended div Smain -->\n</body>\n</html>";
  }

 
$this->cfg = $cfg;
  unset(
$cfg);

 
$this->list_total = 0; // dir content list
 
$this->neck = '';
 
$this->out = array('filepath'=>0, 'task'=>'instantiate', 'title'=>''); // returned by work()
 
$this->sec_dirs = is_array($sec_dirs) ? $sec_dirs : array();
 
$this->sec_files = is_array($sec_files) ? $sec_files : array();
 
$this->self = htmlspecialchars($this->cfg['base_url']. ((strpos($this->cfg['base_url'], '?') !== false) ? '' : '?'). $this->cfg['query_plus'], ENT_COMPAT, $this->cfg['charset']); // for making URLs
 
$this->src_filetypes = is_array($src_filetypes) ? $src_filetypes : array(''=>'txt', 'css'=>'css', 'htaccess'=>'txt', 'htm'=>'html', 'html'=>'html', 'js'=>'js', 'php'=>'php', 'txt'=>'txt', 'xml'=>'xml');
 
 
# Not allowed in safe mode
  #set_time_limit($this->cfg['timeout']);  
 
}

 function
auth(){ // authenticate user
  // cookie
 
if(isset($_COOKIE[$this->cfg['cookie']]) && $_COOKIE[$this->cfg['cookie']] == $this->cfg['hash']){
   return
1;
  }
 
// IP
 
if(count($this->cfg['ok_ips'])){
   if(isset(
$_SERVER['HTTP_X_FORWARDED_FOR']) and $_SERVER['HTTP_X_FORWARDED_FOR'] !=''){
   
$ip = str_replace(array('\\', '|'), ' ', $_SERVER['HTTP_X_FORWARDED_FOR']);
   }else{
    if(isset(
$_SERVER['HTTP_CLIENT_IP']) and $_SERVER['HTTP_CLIENT_IP'] !=''){
     
$ip = $_SERVER['HTTP_CLIENT_IP'];
    }elseif(isset(
$_SERVER['REMOTE_ADDR']) and $_SERVER['REMOTE_ADDR'] !=''){
     
$ip = $_SERVER['REMOTE_ADDR'];
    }else{
     
$ip = '0.0.0.0';
    }
   }
   if(
in_array($ip, $this->cfg['ok_ips'])){
    return
1;
   }
  }
 
// password
 
if(isset($_POST['Sp'])){
   if(
md5('sourceer'. $_POST['Sp']) == $this->cfg['hash']){  
   
$to = isset($_POST['St']) ? str_replace('&Sp=', '&', base64_decode(str_replace(array('-','_','.'), array('+','/','='), $_POST['St']))) : htmlspecialchars_decode($this->self, ENT_COMPAT);
    @
ob_end_clean();
   
setcookie($this->cfg['cookie'], $this->cfg['hash'], time()+3600);
   
header('Location: '. $to);
    exit;
   }
  }
  return
0;
 }

 function
do_dl($f, $fn){ // download handling
 
if($this->cfg['dl'] == 0){
   
$this->out['error'] = 'Downloads through PHP is turned off';
   return
'off';
  }
  if(!
array_key_exists(strtolower(substr(strrchr($fn, '.'), 1)), $this->src_filetypes) or ($this->is_sec($f) && $this->cfg['sec_file_dl'] == 0)){
   
$this->out['error'] = 'Download through PHP of this file is prohibited';
   return
'denied';
  }
  if(
is_file($f)){
   if(
filesize($f)){
    if(
$fh = fopen($f, 'rb')){
     
$m = array('css' => 'text/css', 'htm' => 'text/html', 'html' => 'text/html', 'js' => 'application/x-javascript', 'rtf' => 'text/rtf', 'c' => 'text/plain', 'txt' => 'text/plain'); // some mimes
     
$e = strtolower(substr(strrchr($f, '.'), 1));
     @
ob_end_clean();
     
header('Accept-Ranges: bytes');
     
header('Content-Type: '. (array_key_exists($e, $m) ? $m[$e] : 'application/octet-stream'));
     
header("Content-Transfer-Encoding: binary\n");
     
header('Content-Disposition: inline; filename="'.$fn.'"');
     
set_time_limit(0);
     while((!
feof($fh)) and (connection_status()==0)){
      print(
fread($fh, 1024*2));
     }
     
fclose($fh);
     exit;
    }else{
     
$this->out['error'] = 'File could not be sent, possibly because of file-permission issues';
     return
'issue';
    }
   }else{
   
$this->out['error'] = 'File appears to be empty';
    return
'empty';
   }
  }else{
   
$this->out['error'] = 'Specified file likely does not exist';
   return
'absent';
  }
 
$this->out['error'] = 'Error';
  return
'error';
 }
 
 function
do_src($f, $fn){ // source code view handling
 
if($this->cfg['src'] == 0){
   
$this->out['error'] = 'Source code viewing through PHP is turned off';
   return
'off';
  }
 
$e = strtolower(substr(strrchr($fn, '.'), 1));
  if(!
array_key_exists($e, $this->src_filetypes) or ($this->is_sec($f) && $this->cfg['sec_file_src'] == 0)){
   
$this->out['error'] = 'Source code viewing for this file is prohibited';
   return
'denied';
  }elseif(
is_file($f)){
   if(
filesize($f)){
    if((
$c = file_get_contents($f)) === false){
     
$this->out['error'] = 'File contents could not be retrieved, possibly because of file-permission issues';
     return
'issue';
    }
   
$c = $this->fixed_charset($c);
   
$ft = $this->src_filetypes[$e];
    if(
$this->cfg['hiliter']){ // external highlighting function
     
$x = $this->cfg['hiliter']($c, $ft, $this->out['filepath'], $this->cfg['charset']);
     if(
is_array($x)){
      return
$x;
     }
    }    

    if (
$e == '')
    {
        if (
preg_match('`#\! */bin/sh`i', $c)) $ft = 'shell';
        if (
preg_match('`#\! */bin/bash`i', $c)) $ft = 'shell';
    }

    if (
in_array($ft, array('c', 'pl', 'xml', 'shell', 'py')))
    {
     return array(
'<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js" type="text/javascript"></script><pre class="brush: '
. $ft .'">'
     
. htmlspecialchars($c)
     .
'</pre><script type="text/javascript">
     SyntaxHighlighter.all()
</script>'
);
    }
   
   
// highlight for PHP; for others, mere format
   
return array(str_replace(array('<br />', '<br>', '&nbsp;', '<font color="', '</font>', "\n ", '  '), array("\n<br />\n", "\n<br>\n", ' ', '<span style="color: ', '</span>', "\n&nbsp;", ' &nbsp;'), "\n". ($ft == 'php' ? highlight_string($c, true) : nl2br(htmlspecialchars($c, ENT_COMPAT, $this->cfg['charset'])))));
   }else{
   
$this->out['error'] = 'File appears to be empty';
    return
'empty';
   }
  }else{
   
$this->out['error'] = 'Specified file likely does not exist';
   return
'absent';
  }
 
$this->out['error'] = 'Error';
  return
'error';
 }
 
 function
fixed_charset($in){ // weak attempt to fix; lot of code needed otherwise
 
$enc = strtolower(str_replace('-', '', $this->cfg['charset']));
  if(
$enc == 'utf8'){
   if(
preg_match('`^.{1}`us', $in) or !preg_match('`[^\x00-\x7F]`', $in)){ // is utf-8
   
return $in;
   }
   return
utf8_encode($in);
  }
  if(
function_exists('mb_detect_encoding')){
   return
mb_convert_encoding($in, $this->cfg['charset'], mb_detect_encoding($in));
  }
  return
$in;
 }
 
 function
get_prop($p, $f, $n = 0){ // get file property
 
if(!$this->cfg['file_info']){
   return
'';
  }
  if(
$p == 'size'){
   
$s = filesize($f);
   if(
$s === false){
    return array(
0, 0);
   }elseif(
$s > 1024 && $s < 1048576){
    return array(
round($s / 1024, 2). ' <small>KB</small>', $s);
   }elseif (
$s > 1048576){
    return array(
round($s / 1048576, 2). ' <small>MB</small>', $s);
   }elseif (
$s > 1073741824){
    return array(
round($s / 1073741824, 2). ' <small>GB</small>', $s);
   }else{
    return array(
$s . ' <small>B</small>', $s);
   }
  }elseif(
$p == 'mod_time' && ($t = filemtime($f)) !== false){
   
$d = $n-$t;
   
$a = $d < 86400 ? round($d/3600, 1). ' h' : ($d < 2630880 ? round($d/86400, 1). ' d' : ($d < 31570560 ? round($d/2630880, 1). ' m' : round($d/31570560, 1). ' y'));
   return array(
'<span class="S'. ($d < 1000000 ? 'new' : ($d < 9000000 ? 'young' : ($d < 31570560 ? 'old' : 'ancient'))). '" onmouseover="javascript: Sloct = new Date(1000*'.gmdate('U', $t).'); this.title=Sloct.toLocaleString() + \', local time\';">'. gmdate($this->cfg['date_type'], $t). ' <small>GMT</small>, '. $a. ' old</span>', $t);
  }
  return array(
0, 0);
 }
 
 function
is_sec($f, $dir = 0){ // if file/directory is a secured (restricted) one; $f is path to file/dir relative to cfg['root']
 
if($this->cfg['sec_check_off']){
   return
0;
  }
 
$s = !$dir ? $this->sec_files : $this->sec_dirs;
  if(!(
$c = count($s))){
   return
0;
  }
  for(
$i=$c; --$i>=0;){
   if(!
is_array(($j = $s[$i]))){
    if(
$f == $j){
     return
1;
    }
    continue;
   }
   foreach(
$j as $k){
    if(
preg_match($k, $f)){return 1;}
   }
  }
  return
0;
 }

 function
rel_path($dest, $root = '', $dir_sep = '/', $pre = '.'){ // gets relative path between two absolute or real paths
 
$root = explode($dir_sep, $root);
 
$dest = explode($dir_sep, $dest);
 
$fix = '';
 
$path = $pre;
 
$diff = 0;
  for(
$i = -1; ++$i < max(($rC = count($root)), ($dC = count($dest)));){
   if(isset(
$root[$i]) and isset($dest[$i])){
    if(
$diff){
     
$path .= $dir_sep. '..';
     
$fix .= $dir_sep. $dest[$i];
     continue;
    }
    if(
$root[$i] != $dest[$i]){
     
$diff = 1;
     
$path .= $dir_sep. '..';
     
$fix .= $dir_sep. $dest[$i];
     continue;
    }
   }elseif(!isset(
$root[$i]) and isset($dest[$i])){
    for(
$j = $i-1; ++$j < $dC;){
     
$fix .= $dir_sep. $dest[$j];
    }
    break;
   }elseif(isset(
$root[$i]) and !isset($dest[$i])){
    for(
$j = $i-1; ++$j < $rC;){
     
$fix = $dir_sep. '..'. $fix;
    }
    break;
   }
  }
  return
$path. $fix;
 }

 function
set_cfg($k, $v){ // config set
 
$this->cfg[$k] = $v;
 }
 
 function
show_dir($d, $s){ // list dir content; $d is true relative path starting with './'; $s, faux; list items given name 'item' and unique IDs of form 'sort-number_bytesize_filemtime' for enduser to manipulate list by JS etc.
 
if(!is_dir($d)){
   
$this->out['error'] = 'Directory specified probably doesn\'t exist or is not a directory';
   return
'absent';
  }
  if(!empty(
$this->sec_dirs) and !$this->cfg['sec_check_off'] and !$this->cfg['sec_dir_into'] and $this->is_sec($d, 1)){
   
$this->out['error'] = 'Traversal of directory specified is prohibited';
   return
'denied';
  }
  if((
$dh = opendir($d)) === false){
   
$this->out['error'] = 'Directory specified could not be opened, possibly due to file-permission issues';
   return
'issue';
  }
 
$Sl = rawurlencode(trim($s, '/')); // for the locator Sl GET parameter in URL
 
$self = $this->self;
 
$no = 0; // count items
  // gets all files/dirs, noting sec level
 
$sec_off_f = ($this->cfg['sec_check_off'] == 1) ? 1 : (!empty($this->sec_files) ?  0 : 1);
 
$sec_off_d = ($this->cfg['sec_check_off'] == 1) ? 1 : (!empty($this->sec_dirs) ?  0 : 1);
 
$now = time();
  while((
$fn = readdir($dh)) !== false){
   if(
$fn == '.' || $fn == '..'){
    continue;
   }
   
$f = $d. '/'. $fn;
   
$f2 = $s. '/'. $fn;
   if(
is_dir($f)){
    if(
$sec_off_d  or ($sec = $this->is_sec($f2, 1)) !== 2){
     
$sec = isset($sec) ? $sec : 1;
     
$df[$fn] = array($this->get_prop('mod_time', $f, $now), $sec);
     unset(
$sec);  
    }    
   }elseif(
$sec_off_f or ($sec = $this->is_sec($f2)) !== 2){
   
$sec = isset($sec) ? $sec : 1;
   
$e = strtolower(substr(strrchr($fn, '.'), 1));
   
$ndf[$e][$fn] = array($this->get_prop('size', $f), $this->get_prop('mod_time', $f, $now), $sec);
    unset(
$sec);
   }
  }
 
closedir($dh);
 
// show list
 
$enc = $this->cfg['charset'];
 
$no = 0;
 
ob_start();
  echo
'<table border=0 cellpadding=0 cellspacing=0>';
  if(isset(
$df)){ // dirs
   
ksort($df, SORT_STRING);
   
$sec_ord = ($sec_off_d or ($this->cfg['sec_dir_list'] and $this->cfg['sec_dir_into'])) ? 1 : 0;
   foreach(
$df as $k => $v){
    if(
$sec_ord or !$v[1]){ // no hiding
     
++$no;
     echo
'<tr><td><div name="item" class="Sdir" id="', $no, '_0_', $v[0][1], '"><a href="', $self, '&amp;Sd='. rawurlencode($k), '&amp;Sl=', htmlspecialchars($Sl, ENT_COMPAT, $enc), '" title="browse directory">', htmlspecialchars($this->fixed_charset($k), ENT_COMPAT, $enc), '/</a>', (isset($v[0][0][0]) ? ' </td><td><div class="Sprop">'. $v[0][0]. '</div>' : ''), "</div></td></tr>\n";
     continue;
    }
    if(
$this->cfg['sec_dir_list']){
     ++
$no;
     echo
'<tr><td><div name="item" class="Sdir" id="', $no, '_0_', $v[0][1], '">', htmlspecialchars($this->fixed_charset($k), ENT_COMPAT, $enc), '/', (isset($v[0][0][0]) ? ' </td><td><div class="Sprop">'. $v[0][0]. '</div>' : ''), "</div></td></tr>\n";
    }  
   }
  }
  if(isset(
$ndf)){ // files
   
ksort($ndf, SORT_STRING);
   foreach(
$ndf as $v){
   
ksort($v, SORT_STRING);
   }
   
$sec_src = $sec_dl = 0;
   
$sec_ord = ($sec_off_f or ($this->cfg['sec_file_list'] and $this->cfg['sec_file_dl'] and $this->cfg['sec_file_src'])) ? 1 : 0;
   if(!
$sec_ord and $this->cfg['src'] and $this->cfg['sec_file_src']){
   
$sec_src = 1;
   }
   if(!
$sec_ord and $this->cfg['dl'] and $this->cfg['sec_file_dl']){
   
$sec_dl = 1;
   }
   
// for non-Sourceer (direct) URL for file
   
$d_enc = implode('/', array_map('rawurlencode', explode('/', trim($d, '/'))));
   
   foreach(
$ndf as $k1 => $v1){
    if(
array_key_exists($k1, $this->src_filetypes)){
     foreach(
$v1 as $k2 => $v2){
     
$k3 = rawurlencode($k2);
      if(
$sec_ord or !$v2[2]){
       ++
$no;
       echo
'<tr><td><div name="item" class="Sfile1" id="', $no, '_', $v2[0][1], '_', $v2[1][1], '"><a href="', (($this->cfg['src']) ? $self. '&amp;Sfs='. $k3. '&amp;Sl='. htmlspecialchars($Sl, ENT_COMPAT, $enc). '" title="view source" class="Sfile1' : htmlspecialchars($Sl, ENT_COMPAT, $enc). '/'. $k3. '" class="Sfile2'), '">', htmlspecialchars($this->fixed_charset($k2), ENT_COMPAT, $enc), '</a> </td><td><div class="Sprop">', (!empty($v2[0][0]) ? $v2[0][0] : '?'), ((isset($v2[0][0][0]) and isset($v2[1][0][0])) ? '; ' : ' '), (!empty($v2[1][0]) ? $v2[1][0] : '?'), '', (($this->cfg['dl'] && !empty($v2[0][0])) ? ' <a href="'. $self. '&amp;Sfd='. $k3. '&amp;Sl='. htmlspecialchars($Sl, ENT_COMPAT, $enc). '" title="visit" class="Sfile1">download</a>' : ''), "</div></div></td></tr>\n";
       continue;
      }
      if(
$this->cfg['sec_file_list']){
       ++
$no;
       echo
'<tr><td><div name="item" class="Sfile1" id="', $no, '_', $v2[0][1], '_', $v2[1][1], '">', ($sec_src ? '<a href="'. $self. '&amp;Sfs='. $k3. '&amp;Sl='. htmlspecialchars($Sl, ENT_COMPAT, $enc). '" title="view source" class="Sfile1">' : ''), htmlspecialchars($this->fixed_charset($k2), ENT_COMPAT, $enc), ($sec_src ? '</a>' : ''), ' </td><td><div class="Sprop">', $v2[0][0], ((isset($v2[0][0][0]) and isset($v2[1][0][0])) ? '; ' : ''), $v2[1][0], ($sec_dl ? ' <a href="'. $self. '&amp;Sfd='. $k3. '&amp;Sl='. htmlspecialchars($Sl, ENT_COMPAT, $enc). '" title="visit" class="Sfile1">download</a>' : ''), "</div></div></td></tr>\n";
      }
     }
    }else{
     foreach(
$v1 as $k2 => $v2){
      if(
$sec_ord or !$v2[2]){
       ++
$no;
       echo
'<tr><td><div name="item" class="Sfile2" id="', $no, '_', $v2[0][1], '_', $v2[1][1], '"><a href="', $d_enc, '/', rawurlencode($k2), '" class="Sfile2" title="visit">', htmlspecialchars($this->fixed_charset($k2), ENT_COMPAT, $enc), '</a> </td><td><div class="Sprop">', $v2[0][0], ((isset($v2[0][0][0]) and isset($v2[1][0][0])) ? ', ' : ''), $v2[1][0], '', "</div></div></td></tr>\n";
       continue;
      }
      if(
$this->cfg['sec_file_list']){
       ++
$no;
       echo
'<tr><td><div name="item" class="Sfile2" id="', $no, '_', $v2[0][1], '_', $v2[1][1], '">', htmlspecialchars($this->fixed_charset($k2), ENT_COMPAT, $enc), ' </td><td><div class="Sprop">', $v2[0][0], ((isset($v2[0][0][0]) and isset($v2[1][0][0])) ? ', ' : ''), $v2[1][0], '', "</div></div></td></tr>\n";
      }
     }
    }
   }
  }
  echo
'</table>';
 
$o = ob_get_contents();
  @
ob_end_clean();
  if(!
$no){
   
$this->out['error'] = 'Directory specified may be empty, or all of its content may be hidden';
   return
'empty';
  }
 
$this->list_total = $no;
  return array(
$o);
 
$this->out['error'] = 'Error';
  return
'error';
 }

 function
show_foot(){ // ending HTML
 
echo "\n", '<div class="Ssubtle">Presented with <a href="http://remi.bonnetchangai.free.fr/">Bontiv-Sourceer</a></div>', "\n", '</div><!-- ended div Sbody -->';
  echo
$this->cfg['foot'];
 }

 function
show_head(){ // sends HTML doctype, head, and body's top
 
if(!headers_sent()){
   
header('Content-Type: text/html; charset='. $this->cfg['charset']);
  }
 
$enc = $this->cfg['charset'];
  if(!
strlen($this->neck) and isset($this->out['filepath'])){
   if(
$this->out['filepath'] == './'){
   
$this->neck = '.<big>/</big> <small><em>root</em></small>';
   }else{
   
$n = $l = '';
    for(
$i = 0, $c = count(($nA = explode('/', trim($this->out['filepath'], '/')))); $i < $c; ++$i){
     if(
$c-$i == 1){
     
$n .= htmlspecialchars($this->fixed_charset($nA[$i]), ENT_COMPAT, $enc);
      continue;
     }
     
$l .= $nA[$i]. '/';
     
$n .= '<a href="'. $this->self. '&amp;Sl='. rawurlencode(trim($l, '/')). '" title="browse this directory">'. htmlspecialchars($this->fixed_charset($nA[$i]), ENT_COMPAT, $enc). '</a><big>/</big>';
    }
   
$this->neck = $n;  
   }
   if(
$this->cfg['file_info'] and $this->list_total > 2){
   
$this->neck .= '<span id="Stotal" style="display:none;">'. $this->list_total. ' items sorted by <a href="#" onclick="javascript:Ssort(\'a\'); return false;" title="directories and alphabetically ordered files grouped by extension">type</a>, <a href="#" onclick="javascript:Ssort(\'s\'); return false;" title="excludes directories">size</a> or <a href="#" onclick="javascript:Ssort(\'t\'); return false;" title="since last modified">age</a></span>';
   }
  }
  echo
str_replace(array('_Sourceer_dynamic_title_', '_Sourceer_dynamic_css_', '_Sourceer_dynamic_js_', '_Sourceer_dynamic_title_'), array(htmlspecialchars($this->fixed_charset($this->out['title']), ENT_COMPAT, $enc), $this->cfg['css'], $this->cfg['js']), $this->cfg['head']), "<div class=\"Ssubtle\">", trim((($this->out['filepath'] !== './') ? '<a href="'. $this->self. '&amp;Sl=." title="top-most directory, or home">Root</a>' : ''). ($this->out['task'] != 'help' ? ' | <a href="'. $this->self. '&amp;Sh=1" title="Sourceer help">Help</a>' : ''). (($this->cfg['auth'] and isset($_COOKIE[$this->cfg['cookie']])) ? ' | <a href="'. $this->self. '&amp;So=1" title="logout from Sourceer">Logout</a>' : ''), '| '), '</div>', "\n", '<div id="Sneck">', $this->neck, "</div><!-- ended div Sneck -->\n", (isset($this->out['error']) ? '<div class="Smsg">'. $this->out['error']. ($this->out['task'] != 'login' ? '<p><small><a href="'. $this->self. '&amp;Sl=." title="top-most directory / home">Go to root</a></small></p>' : ''). '</div>' : ''), '<div id="Sbody">';
 }

 function
work(){ // main handler
 
 
if(get_magic_quotes_gpc()){
      if (isset(
$_GET['Sd']))
       
$_GET['Sd'] = stripslashes($_GET['Sd']);
   if (isset(
$_GET['Sfd']))
       
$_GET['Sfd'] = stripslashes($_GET['Sfd']);
   if (isset(
$_GET['Sfs']))
       
$_GET['Sfs'] = stripslashes($_GET['Sfs']);
   if (isset(
$_GET['Sl']))
       
$_GET['Sl'] = stripslashes($_GET['Sl']);
  }

 
// silently secure _GET vars - no ./, ../, //, etc., except Sl that can't have //
 
$_GET['Sd'] = (isset($_GET['Sd']) and strlen(($_GET['Sd'] = preg_replace('`^\.+$`', '', basename($_GET['Sd']))))) ? $_GET['Sd'] : false; // dir to view
 
$_GET['Sfd'] = (isset($_GET['Sfd']) and strlen(($_GET['Sfd'] = preg_replace('`^\.+$`', '', basename($_GET['Sfd']))))) ? $_GET['Sfd'] : false; // file to dl
 
$_GET['Sfs'] = (isset($_GET['Sfs']) and strlen(($_GET['Sfs'] = preg_replace('`^\.+$`', '', basename($_GET['Sfs']))))) ? $_GET['Sfs'] : false; // file for code
 
$_GET['Sl'] = (isset($_GET['Sl']) and strlen($_GET['Sl'] = trim(preg_replace('`///*`', '/', $_GET['Sl']), '/'))) ? $_GET['Sl'] : ''; // dir locator
 
  // compression
 
if($_GET['Sfd'] === false and $this->cfg['compress'] and function_exists('gzencode') and isset($_SERVER['HTTP_ACCEPT_ENCODING']) and preg_match('`gzip|deflate`i', $_SERVER['HTTP_ACCEPT_ENCODING']) and !ini_get('zlib.output_compression')){
   
ob_start('ob_gzhandler');
  }else{
   
ob_start();
  }
 
 
// help
 
if(isset($_GET['Sh'])){
   
$this->out['title'] = $this->neck = 'Help';
   
$this->out['task'] = 'help';
   
$this->show_head();
   echo
'Here you can browse files and directories (indicated with a trailing slash, /) inside a <em><a href="', $this->self, '&amp;Sl=." title="see root">root</a></em> directory specified by the site administrator.',  (!$this->cfg['file_info'] ? '' : ' A file\'s <strong>size</strong>, modification <strong>time</strong> (in GMT; hover cursor on a time to get the local time) and <strong>age</strong> will be indicated (on some systems, indicated directory modification times may be incorrect). Files and sub-directories are listed alphabetically and by type. On most browsers if use of Javascript is enabled, it is possible to <strong>sort</strong> the list by age or size as well -- use the provided links; re-clicking reverses the sort order.'), ' Sub-directories can similarly be browsed.', (!$this->cfg['src'] ? '' : '<br /><br />The <strong>source code</strong> (possibly syntax-highlighted'. ($this->cfg['jssrc'] ? ' which also might require the use of Javascript in your browser' : ''). ') of certain types of files '. (count($this->src_filetypes) ? ' - <code>'. implode('</code>, <code>', array_map('htmlspecialchars', array_unique($this->src_filetypes))). '</code> - ' : ''). 'can be viewed'. (!$this->cfg['dl'] ? '' : ' and such files <strong>downloaded</strong>'). '.'), ' Such file items would be shown in a different style in the directory content lists. Other files may possibly, depending on the server configuration, be downloaded (visited) by clicking the links shown on their names.<br /><br />Depending on the configuration set up by the administrator, some files and directories might not be shown or be <strong>inaccessible</strong>. Also, files and directories with atypical non-English characters in their names may not be accessible and/or may have their names displayed with strange characters.', (!$this->cfg['auth'] ? '' : ' Access to these pages requires being at a specific IP address, or a password.'), '<br /><br />If you are trying to access a specific file or directory by manipulating the <strong>URL query string</strong>, note that the slash (/) character is permitted only in the value for <em>Sl</em>. ', ($this->cfg['up_root'] ? 'Sub-strings like \'/..\' may be used in <em>Sl</em> to browse up-root.' : 'Sub-strings like \'/..\' are removed from the query string.'), '<br /><br />The content shown is generated using the single-file <a href="http://remi.bonnetchangai.free.fr" title="PHP Labware">Bontiv-Sourceer</a> PHP software.';
   
$this->show_foot();
   @
ob_end_flush();
   return
$this->out;  
  }
 
 
// auth work if needed
 
if($this->cfg['auth']){
   
// logout
   
if(isset($_GET['So']) and isset($_COOKIE[$this->cfg['cookie']])){
    @
ob_end_clean();
   
setcookie($this->cfg['cookie'], '', time()-10000);
   
header("Location: " . htmlspecialchars_decode($this->self, ENT_COMPAT));
    exit;
   }
   
// login
   
if($this->cfg['auth'] && $this->auth() == 0){
    if(empty(
$_SERVER['REQUEST_URI'])){
     
$arr = explode('/', $_SERVER['PHP_SELF']);
     
$_SERVER['REQUEST_URI'] = '/'. $arr[count($arr)-1];
     if(isset(
$_SERVER['argv'][0]) && $_SERVER['argv'][0] != ''){
     
$_SERVER['REQUEST_URI'] .= '?'. $_SERVER['argv'][0];
     }elseif(isset(
$_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != ''){
     
$_SERVER['REQUEST_URI'] .= '?'. $_SERVER['QUERY_STRING'];
     }
    }
   
$to = str_replace(array('+','/','='), array('-','_','.'), base64_encode($_SERVER['REQUEST_URI']));
   
$this->out['title'] = $this->neck = 'Login';
   
$this->out['task'] = 'login';
    if(isset(
$_POST['Sp'])){
     
$this->out['error'] = 'Right password needed';
    }
   
$this->show_head();
    echo
'<form id="Sform" action="', $this->self, '" method="post"><div id="Slog">Password: <input type="password" id="Sp" name="Sp" />&nbsp;<input type="submit" value="Log in"><input type="hidden" id="St" name="St" value="', htmlspecialchars($to, ENT_COMPAT, $enc), '" />';
   
$qplus = $this->cfg['query_plus'];
    if(!empty(
$qplus)){
     
$enc = $this->cfg['charset'];
     
$qplus = explode('&', $qplus);
     foreach(
$qplus as $v){
      if((
$v1 = strpos($v, '=')) !== false){
       echo
'<input type="hidden" name="', ($v2 = htmlspecialchars(substr($v, 0, $v1), ENT_COMPAT, $enc)), '" id="', $v2, '" value="', htmlspecialchars(substr($v, $v1+1), ENT_COMPAT, $enc), '" />';
      }
     }
    }
    echo
'</div></form>';
   
$this->show_foot();
    @
ob_end_flush();
    return
$this->out;
   }
  }

 
// path check for existence, security, etc.
 
$this->out['task'] = 'pathcheck';
 
$dPth = preg_replace('`///*`', '/', $this->cfg['root']. ($_GET['Sl'] != '' ? '/'. $_GET['Sl'] : '')); // true working dir relative path
 
if(($dTruPth = realpath($dPth)) !== false and ($rTruPth = realpath($this->cfg['root'])) !== false and is_dir($dTruPth)){
   
$dTruPth = str_replace(DIRECTORY_SEPARATOR, '/', $dTruPth); // true working-dir realpath
   
$rTruPth = str_replace(DIRECTORY_SEPARATOR, '/', $rTruPth);
   
$dRelPthT = trim($this->rel_path($dTruPth, $rTruPth, '/', $this->cfg['root']), '/'); // true working-dir relative path
   
$dRelPthF = trim($this->rel_path($dTruPth, $rTruPth, '/', '.'), '/'); // faux working-dir relative path
   
$this->out['filepath'] = $dRelPthF. '/'. ($_GET['Sfd'] ? $_GET['Sfd'] : ($_GET['Sfs'] ? $_GET['Sfs'] : ($_GET['Sd'] ? $_GET['Sd']. '/' : ''))); // faux working-dir relative path
   
if((preg_match('`(^|/)\.\.(/|$)`', $dRelPthF) and !$this->cfg['up_root']) or (!empty($this->sec_dirs) and $this->is_sec($dRelPthF, 1) and !$this->cfg['sec_dir_into'])){
   
$this->out['title'] = 'prohibited directory: '. $this->out['filepath'];
   
$this->neck = 'Prohibited directory: '. str_replace('/', '<big>/</big>', htmlspecialchars($this->out['filepath'], ENT_COMPAT, $enc));
   
$this->out['error'] = 'Traversal of directory specified is prohibited';
   
$this->show_head();
   
$this->show_foot();
    @
ob_end_flush();
    return
$this->out;
   }  
  }else{  
   
$this->out['title'] = 'absent?: '. $dPth. '/';
   
$this->neck = 'Absent directory?: '. str_replace('/', '<big>/</big>', htmlspecialchars($dPth, ENT_COMPAT, $enc). '/');
   
$this->out['error'] = 'Directory specified probably doesn\'t exist, or is not a directory, or could not be accessed, possibly because of file-permission or server configuration issues';
   
$this->show_head();
   
$this->show_foot();
   @
ob_end_flush();
   return
$this->out;
  }
 
 
// download
 
if($_GET['Sfd'] !== false){
   
$this->cfg['compress'] = 0;
   
$this->out['task'] = 'download';
   if((
$x = $this->do_dl($dRelPthT. '/'. $_GET['Sfd'], $_GET['Sfd'])) !== 1){
   
$this->out['title'] = $x. ': '. $dRelPthF. '/'. $_GET['Sfd'];
   
$this->show_head();
   
$this->show_foot();
    @
ob_end_flush();
    return
$this->out;
   }
  }    
 
 
// source code view
 
if($_GET['Sfs'] !== false){
   
$this->out['task'] = 'source';
   
$x = $this->do_src($dRelPthT. '/'. $_GET['Sfs'], $_GET['Sfs']);
   
$this->out['title'] = (is_array($x) ? 'source code' : $x). ': '. $dRelPthF. '/'. $_GET['Sfs'];
   if(
is_array($x)){
   
$this->cfg['css'] .= !empty($x[1]) ? $x[1] : '';
   
$this->cfg['js'] .= !empty($x[2]) ? $x[2] : '';
   
$this->cfg['foot'] = (!empty($x[3]) ? $x[3] : ''). $this->cfg['foot'];
   }
   
$this->show_head();
   if(
is_array($x)){
     echo
"\n", '<div id="Scode">', "\n", $x[0], "\n", '</div><!-- ended div Scode -->', "\n";
   }
   
$this->show_foot();
   @
ob_end_flush();
   
$this->out['css'] = $this->cfg['css'];
   
$this->out['js'] = $this->cfg['js'];
   
$this->out['foot'] = $this->cfg['foot'];
   return
$this->out;
  }
   
 
// directory listing
 
$this->out['task'] = 'browse';
 
$x = $this->show_dir($dRelPthT. ($_GET['Sd'] !== false ? '/'. $_GET['Sd'] : ''), $dRelPthF. ($_GET['Sd'] !== false ? '/'. $_GET['Sd'] : ''));
 
$this->out['title'] = (is_array($x) ? 'directory' : $x). ': '. $dRelPthF. '/'. ($_GET['Sd'] !== false ? $_GET['Sd']. '/' : '');
  if(
is_array($x)){
   
$this->cfg['foot'] = '<script type="text/javascript"><!--//--><![CDATA[//><!--
   // sorting script; also unhides div Stotal only if JS ability
   var Se = document.getElementById(\'Stotal\'); if(Se != null){if(Se.style.display == \'none\'){Se.style.display = \'inline\';}}
   var So = 1;
   function Ssort(o){var d = So == 1 ? 0 : 1; So = d; var e = document.getElementById(\'Slist\'); var i = [];
   for(var x = e.firstChild; x != null; x = x.nextSibling){if(x.nodeType == 1){i.push(x.getAttribute(\'id\'));}}
   i.sort(function(j,k){if(o==\'a\'){j1=parseInt(j); k1=parseInt(k); if(j1>k1){return (d == 0 ? 1 : -1);}else if(j1<k1){return d == 1 ? 1 : -1;}else{return 0;}}else if(o==\'s\'){j1=j.substring(j.indexOf(\'_\')+1, j.lastIndexOf(\'_\')); k1=parseInt(k.substring(k.indexOf(\'_\')+1, k.lastIndexOf(\'_\'))); if(j1>k1){return d == 0 ? 1 : -1;}else if(j1<k1){return d == 1 ? 1 : -1;}else{return 0;}}else{j1=parseInt(j.substring(j.lastIndexOf(\'_\')+1)); k1=parseInt(k.substring(k.lastIndexOf(\'_\')+1)); if(j1<k1){return d == 0 ? 1 : -1;}else if(j1>k1){return d == 1 ? 1 : -1;}else{return 0;}}}); for(var m = 0; m < i.length; m++) {e.appendChild(document.getElementById(i[m]));}}  
   // ended sorting script
   //--><!]]></script>'
. $this->cfg['foot'];
  }
 
$this->show_head();  
  if(
is_array($x)){
   echo
"\n", '<div id="Slist">', "\n", $x[0], "\n", '</div><!-- ended div Slist -->', "\n";
  }
 
$this->show_foot();
  @
ob_end_flush();
 
$this->out['css'] = $this->cfg['css'];
 
$this->out['js'] = $this->cfg['js'];
 
$this->out['foot'] = $this->cfg['foot'];
  return
$this->out;
 }
 
}
// ends CLASS DEFINITION
Presented with Bontiv-Sourceer