#!/usr/bin/php
<?php

 
/*
  *  SLINKER 0.2 (with MDFS support)
  *   Static linker for PHP 
  *   (resolves require_once statements and includes the file)
  *
  *  USE:
  *     mdfs.slink <inputfile >outputfile
  *
  *  BUGS: 
  *     
  *   * Can not resolve relative links 
  *     - unfixable (or we should not process the stdin / need a context)
  *     - does not matter, because not used
  *
  *   * PHP start/end tags are resolved only when stand alone on the line
  *     - this way for simplicity
  *     - should be fixed for better compliance
  *
  *  CHANGELOG:
  *
  *     2005-04-29 .. 0.2 .. require_once line can have more ' characters
  *     2005-04-28 .. 0.1 .. initial version
  *  
  */
  
  
function err($text) {
    
file_put_contents"php://stderr""$text\n" );
    return 
false;
  }

  function 
addTrailingSlash$in ) {
    return (
substr($in,-1)=='/') ? $in "$in/";
  }
  
  function 
getFileFromRO$line ) {
    
// something0 require_once something1
    
$x explode("require_once",$line);
    
// ..once[space0][']filename1[']something2
    
$x explode("'",$x[1]);
    return 
$x[1];
  }

  function 
getSearchPath() {
    
// system settings
    
if (($MDFS getenv('MDFS'))===false$MDFS '/system';
    
$MDFS addTrailingSlash($MDFS)."php/";
    
// setup the search path now
    
$searchPath = array();
    
// from MDFS
    
if (is_dir($MDFS)) $searchPath[] = $MDFS;
    
// from PHP
    
foreach( explode(":",ini_get('include_path')) as $d 
     if (
is_dir($dir=addTrailingslash($d)))
      if (!
in_array($dir,$searchPath))
       
$searchPath[] = $dir;
    return 
$searchPath;
  }
  
  function 
resolveIncludedFile$file$searchPath ) {
    
// special case: absolute includes
    
if ( substr($file,0,1) == '/' )
     return (
is_file($file)) ? $file false;
    
// special case: relative include
    
if ( ( substr($file,0,2) == './'  ) ||
         ( 
substr($file,0,3) == '../' ) ) 
      return 
err"Relative includes are not supported ($file)" );
    
// search in path
    
foreach($searchPath as $prefix)
     if (
is_file($f="$prefix$file"))
      return 
$f;
    
// not found
    
return false;
  }

  function 
getRequireOnce$lines ) {
    
$out = array();
    
// scan
    
foreach($lines as $line
      
// contains the keyword?
      
if (strpos($line,'require_once')!==false) {
        
$out[] = getFileFromRo($line);
      }
    return 
$out;
  }

  function 
resolveFrom$input$sp ) {
    
$wait    = array( $input ); // to process
    
$files   = array();         // resolved files
    
$include = array();         // included files
    
while($wait) {
      foreach( 
getRequireOncearray_shift($wait) ) as $r )
      if (!isset(
$files[$r])) {
        
// try resolve
        
if (($rr resolveIncludedFile$r$sp ))===false) {
          
err("Can not resolve a file ($r)");
          die();
        }
        
// resolved
        
$include[$r] = $wait[] = file($rr);
        
$files[$r]=true;
      }
    }
    return 
$include;
  }
  
  function 
composeOutput$src$inc ) {
    while(
1) {
      
// init
      
$rep false;
      
// output
      
$out = array();
      
// browse
      
foreach($src as $line
        
// contains the keyword?
        
if ( (!$rep) && (strpos($line,'require_once')!==false) ) {
          
$r getFileFromRO($line);
          
// is in included files?
          
if (isset($inc[$r])) {
            
$on    false;
            
$comm  false;
            foreach( 
$inc[$r] as $il ) {
              
$til trim($il);
              
// turn off?
              
if ($til=='?'.'>'$on   false;
              if (
$til=='/*')    $comm true;
              
// copy
              
if ($on && (!$comm)) $out[] = $il;
              
// turn on?
              
if ($til=='*/')       $comm false;
              if (
$til=='<'.'?php'$on   true;
            }
            
$out[] = "\n";
            
// finished
            
unset($inc[$r]);
            
$rep true;
          } 
// else already included, leave out the line
        
} else {
          
// copy line
          
$out[] = $line;
        }
      
// finish?
      
if (!$rep) return $out;
      
// and again
      
$src $out;
    }
  }

  
// get included parts
  
$parts resolveFrom$main file("php://stdin")
                      , 
getSearchPath() 
                      );

  
// output
  
foreach( composeOutput$main$parts ) as $line ) echo $line;

?>