#!/usr/bin/php
<?php
/**** ***
* *** ***
* Pckii
* Packer by Im Ieee :)
* v 1.1.0 - 2012
* ieee[[at]]bk.ru
* http://wapsait.pp.ru
* * * * *
*/
/*default output file*/
define('DEF_OUT', 'pckii_compr.php');
/*zlib library was not found :(*/
define('NO_ZLIB', "I need zlib!\n");
/*no -d argument*/
define('SPECIFY_DIR', "Please, specify dir, which I should pack.\n");
/*can not read something*/
define('NOT_RDBL', "Could not read '%s'.\n");
/*output file can not be opened*/
define('CANT_OP_OUT', "Could not open output file: %s.\n");
/*writing something*/
define('WR_DIR', "Writing directory %s.\n");
define('WR_FIL', "Writing file %s.\n");
/*final message*/
define('OK', "Created file %s, contains %u dirs, %u files. Compression: %.2f%%.\n");
define('HELP',
'
Pckii archiver tool v1.1.0
creates self-unpacking php scripts
usage pckii -d[irectory] DIRECTORY
other options:
-o[utput] FILE specifies an output file (default: '.DEF_OUT.')
-b[egin] FILE includes a file at the beginning of the script
-e[nd] FILE includes a file at the end of the script
-r[oot] DIR specifies a directory, which will contain packed files
-c[ompat] enable compatibility with php 5.0 (default - with php 5.1)
-v[erbose] write file and directory names (turned off by default)
-h[elp] show this help
(c) Im ieee, 2011-2012
'
);
/*can not create something*/
define('CANT_CREATE_DIR','Could not create directory:');
define('CANT_CREATE_FILE','Could not create file:');
/*parsing options*/
$argv[]=$begin=$end=$r_start=$r_end='';
$cmp=false;
for($i=0; $i<$argc; $i++){
switch($argv[$i]){
case '-b': case '-begin':
(is_readable($argv[++$i])&&is_file($argv[$i])) || die(sprintf(NOT_RDBL, $argv[$i]));
$begin=file_get_contents($argv[$i]); break;
case '-e': case '-end':
(is_readable($argv[++$i])&&is_file($argv[$i])) || die(sprintf(NOT_RDBL, $argv[$i]));
$end=file_get_contents($argv[$i]); break;
case '-d': case '-directory':
(is_readable($argv[++$i])&&is_dir($argv[$i])) || die(sprintf(NOT_RDBL, $argv[$i]));
$start_dir=rtrim($argv[$i], '/\\'); break;
case '-r': case '-root':
$r_start=str_replace('\\', '', rtrim($argv[++$i], '/\\')).'\\';
$r_end='\\'; break;
case '-c': case '-compat':
$cmp=true; break;
case '-o': case '-output':
$out_f=$argv[++$i]; break;
case '-h': case '-help': die(HELP);
case '-v': case '-verbose': $verb=true; break;
}
}
/*some checks*/
function_exists('gzeof') || die(NO_ZLIB);
isset($start_dir) || die(SPECIFY_DIR);
isset($out_f) || $out_f=DEF_OUT;
(@$fp_out=fopen($out_f, 'wb')) || die(sprintf(CANT_OP_OUT, $out_f));
/*main unpacking code*/
$main_code=str_replace(array("\n", "\r"), array('',''),'
$s=DIRECTORY_SEPARATOR;
$f=$p="";
while(!feof($g))
{
if(($c=fgetc($g))=="\\\\")
{
if($f!="")
{
@mkdir($p.=$f.$s)||print"'.addcslashes(CANT_CREATE_DIR, '$"\\').'".$p.PHP_EOL;
$f="";
}
else
$p=($b=strrpos(rtrim($p,"\\/"),$s))?substr($p,0,$b+1):"";
}
elseif($c=="/")
{
@(file_put_contents($p.$f,($c=fgetc($g))?fread($g,array_pop(unpack("V",fread($g,$c)."\\0\\0\\0"))):"")!==false)||print"'.addcslashes(CANT_CREATE_FILE, '$\\"').'".$p.PHP_EOL;
$f="";
}
else
$f.=$c;
}
');
/*gzip check and the end of php code ($off,$q - in $archive)*/
fwrite($fp_out,'<?php function_exists("gzeof")||die("'.addcslashes(NO_ZLIB,'$"\\').'");$q=');
$off=ftell($fp_out);
if($cmp){
/*we need to create a temporary file*/
$temp_f=tempnam('', 'pck');
$fp_write=gzopen($temp_f, 'wb9');
} else {
/*writing header*/
$main_code=';eval(gzinflate(\''.
addcslashes(gzdeflate($begin.
'fseek($g=fopen($_SERVER["SCRIPT_FILENAME"],"rb"),$q);stream_filter_append($g,"zlib.inflate",STREAM_FILTER_READ,9);'.$main_code.$end,9),"'\\")
.'\'));__halt_compiler();';
/*oh yes, i could use CHO there but it is 24 bytes long :(*/
$off+=strlen($main_code);
$t=strlen($off);
$off+=$t;
(strlen($off)==$t) || $off++;
fwrite($fp_out, $off.$main_code);
$fp_write=&$fp_out;
stream_filter_append($fp_write,"zlib.deflate",STREAM_FILTER_WRITE,9);
}
$dir_c=$tot_size=$file_c=0;
/*it is called recursively, so it is function*/
function pack_dir($path, $dir, $start=false){
global $fp_write, $dir_c, $file_c, $verb, $tot_size, $r_start, $r_end;
/*root directory*/
fwrite($fp_write, ($start)?$r_start:$dir.'\\');
$dir=$path.$dir.DIRECTORY_SEPARATOR;
($verb) && printf(WR_DIR, $dir);
$dir_arr=scandir($dir);
foreach($dir_arr as $node){
if($node=='.'||$node=='..'){
continue;
}else if(!is_readable($dir.$node)){
printf(NOT_RDBL, $dir.$node);
continue;
}else{
$node=str_replace('\\', '', $node);
if(is_file($dir.$node)){
$file_c++;
($verb) && printf(WR_FIL, $dir.$node);
$size=filesize($dir.$node);
$tot_size+=$size;
/*size is packed as unsigned long, little endian*/
$pck=rtrim(pack('V',$size), chr(0));
fwrite($fp_write,$node.'/'.strlen($pck).$pck.file_get_contents($dir.$node));
}else{
$dir_c++;
pack_dir($dir, $node);
}
}
}
fwrite($fp_write, ($start)?$r_end:'\\');
}
pack_dir($start_dir, '', true);
fclose($fp_write);
if($cmp){
$fp_write=fopen($temp_f, 'r+');
/*looking for <? and <% and replacing them with #? and #%*/
define('BUF_SIZE', 4096);
/*contains positions of < symbols*/
$mtcs=array();
$last=0;
do{
$buf=preg_split('/\<(?=\?|%|$)/i', fread($fp_write, BUF_SIZE));
$not_end=!feof($fp_write);
$len=sizeof($buf)-1;
for($i=0; $i<$len; $i++){
$mtcs[]=strlen($buf[$i])+$last;
$last=0;
}
$last+=strlen($buf[$len]);
$buf=join('#', $buf);
fseek($fp_write, -strlen($buf), SEEK_CUR);
fwrite($fp_write, $buf);
/*we can not use feof there, because we're in the "write mode" :(*/
}while($not_end);
/*code that replaces #? with <?*/
$repl_code=(sizeof($mtcs))?
'foreach(array('.join(',',$mtcs).') as $b){stream_copy_to_stream($f,$g,$b,$q);$q+=$b+1;fputs($g,"<");}'.(($last)?'stream_copy_to_stream($f,$g,-1,$q);':''):
'stream_copy_to_stream($f,$g);';
$main_code=';eval(gzinflate(\''.
addcslashes(gzdeflate($begin.
'fseek($f=fopen($_SERVER["SCRIPT_FILENAME"],"rb"),$q);$g=fopen($n=tempnam("","pck"),"wb");'.$repl_code.'fclose($g);$g=gzopen($n,"rb");'.$main_code.'fclose($g);unlink($n);'.$end,9),"'\\")
.'\'));die?>';
/*we need to determine the end of php code in archive ($q)*/
$off+=strlen($main_code);
$t=strlen($off);
$off+=$t;
(strlen($off)==$t) || $off++;
fwrite($fp_out, $off.$main_code);
/*copying data from the temporary file*/
fseek($fp_write, 0);
stream_copy_to_stream($fp_write, $fp_out);
fclose($fp_out);
fclose($fp_write);
unlink($temp_f);
}
printf(OK, $out_f, $dir_c, $file_c, ($tot_size/filesize($out_f)-1)*100);
?>