<html>
<head>
<title>Reformat config files using PHP</title>
<! FILE:    reformat.html
    PURPOSE:    reformat config files with HTML tags
    AUTHOR:    D.C. Deatrich
    DATE:    Oct 1999
    CHANGES:    
-->
</head>
<body>
<H1>Reformat config files using PHP</H1>
<HR>

<?php
  
/** Defines **/
  
define("HEADER", 1 );
  
define("TRAILER", 2 );
  
define("AUTO", "auto" );
  
define("BOURNE", "bourne" );
  
define("CSH", "csh" );
  
define("CUSTOM", "custom" );
  
define("GENERIC", "generic" );
  
define("MAX_LINE_SZ", 512 ); // arbitrary max length of an ascii line
  
define("WEBTREE", "http://penguin.epfl.ch/" );
  
define("FROM", "mailto&#058;Denice&#046;Deatrich&#064;epfl.ch http://penguin.epfl.ch/" );
  
define("RESULTS", "$DOCUMENT_ROOT/downloads" );
  
define("DOWNLOADS", "/downloads" ); // files older than 5 min rm-ed by cron

  /** Global Vars **/
  
$reserved = array();
  
$builtin  = array();
  
$commentArr1 = array();
  
$commentArr2 = array();
  
$colors = array();

  
/**    Function : find the place for temp storage on the server
   **    Returns:   tmp space pathname
   **/
  
function getTmpPath( ) {
    if( ! (
$tmp = getenv("TMPDIR")) ) {
      
$tmp = "/tmp";
//      echo "<BR>DEBUG: not set, so path is $tmp<BR>";
    
}
    return
$tmp;
  }

  
/**    Function : create a tmp pathname
   **    Returns:   pathname
   **/
  
function buildPath( $string ) {
    
$t = time( );  // use # of seconds to randomize dir name
    
$p = getTmpPath() . "/" . $t;
    if(
mkdir($p, 0700)) {
      
$p = $p . "/" . $string;
    }
    else {
      
$p = "";
    }
    return
$p;
  }

  
/**    Function : initGlobals
   **    Purpose:   initializes global arrays depending on filetype
   **    Returns:   n/a
   **/
  
function initGlobals( $filetype, $cus_comments1, $cus_comments2,
   
$cus_built, $cus_reserved )
  {
    global
$reserved, $builtin, $commentArr1, $commentArr2, $colors,
     
$foreground, $keycolor, $builtinCol, $comments1, $comments2, $strings,
     
$hilight;

    
$colors = array(
      
"#"=>$comments1,
      
";"=>$comments2,
      
"reserved"=>$keycolor,
      
"builtin"=>$builtinCol,
      
"hilight"=>$hilight,
      
"q"=>$strings
     
);

    if(
$filetype == CUSTOM ) { // then build our custom arrays
      
if( strlen($cus_comments1) > 0 ) {
        
$commentArr1 = explode( " ", $cus_comments1 );
      }
      else {
        
array_shift( $commentArr1 );
      }
      if(
strlen($cus_comments2) > 0 ) {
        
$commentArr2 = explode( " ", $cus_comments2 );
      }
      else {
        
array_shift( $commentArr2 );
      }
      if(
strlen($cus_built) > 0 ) {
        
$builtin = explode( " ", $cus_built );
      }
      else {
        
array_shift( $builtin );
      }
      if(
strlen($cus_reserved) > 0 ) {
        
$reserved = explode( " ", $cus_reserved );
      }
      else {
        
array_shift( $reserved );
      }
    }
    else {
      
$commentArr1 = array( "#" );
      if(
$filetype == BOURNE ) {
        
$builtin =
         array(
".",":","source","alias","bg","bind","break","cd",
              
"continue","declare","typeset","dirs","echo","enable",
              
"eval","exec","exit","export","fc","fg","getopts",
              
"hash","help","history","jobs","kill","let","local",
              
"logout","popd","pushd","pwd","read","readonly",
              
"then","until","while","then","until","while",
              
"return","set","shift","suspend","test","times",
              
"trap","type","ulimit","umask","unalias","unset", "wait" );
        
$reserved =
         array(
"!","case","do","done","elif","else","esac","fi","for",
              
"function","if","in","select","then","until","while", "{","}" );
      }
      else if(
$filetype == CSH ) {
        
$builtin =
         array(
":","alias","alloc","bg","bindkey","break","breaksw",
              
"builtins","bye","case","cd","chdir","complete",
              
"continue","default:","dirs","echo","echotc","else",
              
"end","endif","endsw","eval","exec","exit","fg",
              
"filetest","foreach","end","getspath","getxvers",
              
"glob","goto","hashstat","history","hup","if",
              
"then","else","inlib","jobs","kill","limit",
              
"log","login","logout","ls-F","migrate","newgrp",
              
"nice","nohup","notify","onintr","popd","printenv",
              
"pushd","rehash","repeat","sched","set","setenv",
              
"trap","type","ulimit","umask","unalias","unset",
              
"setpath","setspath","settc","setty","setxvers","shift",
              
"source","stop","suspend","switch","telltc","time",
              
"umask","unalias","uncomplete","unhash","universe",
              
"unlimit","unset","unsetenv","ver","wait","warp",
              
"watchlog","where","which","while" );
      }
    }
  }

  
/**    Function : insert an HTML header or trailer into document
   **    Returns:   n/a
   **/
  
function insertInto( $fd, $title, $type ) {
    global
$background, $foreground, $HTTP_REFERER, $PHP_SELF;
    if(
$type == HEADER ) {
      
$s = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n";
      
fputs( $fd, $s, MAX_LINE_SZ );
      
$s = "<!-- Formatted via: " . $HTTP_REFERER . "\n source webtree: " .
       
"" . WEBTREE . "\n using script: " . $PHP_SELF . "\n " . FROM .
       
" -->\n";
      
fputs( $fd, $s, MAX_LINE_SZ );
      
$s = "<!-- generated on " . date("H:i d.m.y") . " -->\n";
      
fputs( $fd, $s, MAX_LINE_SZ );
      
$s = "<html>\n<head>\n";
      
fputs( $fd, $s, MAX_LINE_SZ );
      
$s = "<title>" . $title . "</title>\n</head>\n";
      
fputs( $fd, $s, MAX_LINE_SZ );
      
$s = "<body bgcolor=\"" . $background . "\"" . " text=\"" . $foreground
       
. "\">\n<pre>\n";
      
fputs( $fd, $s, MAX_LINE_SZ );
    }
    else {
      
$s = "\n</pre>\n</body>\n";
      
fputs( $fd, $s, MAX_LINE_SZ );
    }
  }

  
/**    Function : test whether word contains a quote  \' or  \"
   **    Returns:   3 possible return values:   "    '    FALSE
   **    Notes:     
   **/
  
function hasQuote( $word ) {
    
$res = 0;
    if(
ereg("\"", $word) || ereg("\'", $word) ) {
      
$res = (ereg("\'", $word)) ? "'" : "\""; // which kind of quote
    
}
    return
$res;
  }

  
/**    Function : test whether string is empty
   **    Returns:   TRUE or FALSE
   **/
  
function isEmpty( $s ) {
    
$ns = ereg_replace( "[[:space:]]+", "", $s );
    return (
strlen($ns) == 0);
  }

  
/**    Function : generate a color string for given keyword
   **    Returns:   color string
   **/
  
function color( $keyword ) {
    global
$colors;

    
$col = $foreground;
    
reset( $colors );
    while( list(
$key, $value) = each($colors) ) {
      if(
$keyword == $key ) {
        
$col = $value;
        break;
      }
    }
    return
$col;
  }

  function
opt( $fp, $s ) {
    
fputs( $fp, $s, MAX_LINE_SZ );
  }
  
/**    Function : output the string to file; colorize if $col is non-zero
   **    Args:      $fp  - ptr to open file
   **               $s   - string
   **              $col - color of html output
   **/
  
function output( $fp, $s, $col ) {

    if(
strlen($col) == 0 ) {
      
$newstr = $s;
    }
    else {
      
$newstr = "<FONT COLOR=" . "\"$col\"> " . $s. "</FONT>";
    }

    
fwrite( $fp, $newstr, strlen($newstr) );
  }

  
/**    Function : reformat the file with HTML markup
   **    Args:      $f        - name of file to work on
   **               $realName - basename of file from user
   **    Returns:   path to reformatted file
   **   Notes:     a bit of a monster...
   **/
  
function htmlize( $f, $realName, $filetype ) {
    global
$keycolor, $builtinCol, $reserved, $builtin, $commentArr1,
     
$commentArr2, $foreground, $cus_hilight;
    
$lineNo = 0;
    
$p = tempnam( RESULTS, basename($f) ) . ".html"; // dest filename

    // open source file and create dest file
    
if( ($src = fopen($f, "r")) && ($dest = fopen($p, "w+")) ) {
      
insertInto($dest, $realName, HEADER);    // insert HTML-header

      
while( $s = fgets($src, MAX_LINE_SZ) ) { // for each line
        
$lineNo++;
        
$markup = FALSE;
        if(
$filetype == CUSTOM ) {
          
$s = htmlspecialchars( $s ); // Denice fixes special chars: "<>&
        
}
        
// handle 2 special cases: empty str & comment as first non-white char
        
if( isEmpty($s) ) {
          
fwrite( $dest, $s, MAX_LINE_SZ );
          continue;
        }
        
$strim = trim( $s );
/**
Another ? bug/feature:  this returns zero, but also false (also zero)
when NOT found
        if( strpos($strim, "#") == 0 )
**/
        // here we need to loop thru possible comment chars
        
$startComment = false;
        
reset( $commentArr1 );
        while( list(
$k, $v) = each($commentArr1) ) {
          if(
ereg("^$v", $strim) ) {
            
output( $dest, $s, color("#") );
            
$startComment = true;
            break;
          }
        }
        if( !
$startComment ) {
          
reset( $commentArr2 );
          while( list(
$k, $v) = each($commentArr2) ) {
            if(
ereg("^$v", $strim) ) {
              
output( $dest, $s, color(";") );
              
$startComment = true;
              break;
            }
          }
        }
        if( !
$startComment ) {
          
// split up line into words and work thru list of words
          
$sep = "([\t \n\r]+)";
          
$ar = split( $sep, $strim );
          
$index = 0;        // index into string
          
$wordStart = 0;    // cur position in string
          
$last = 0;        // last known posn in string
          
$quotes = 0;
          
$lastCol = $foreground;
          
$inQuotes = FALSE;
          
$inLine = TRUE;

          
$newstr = "<FONT COLOR=" . "\"$lastCol\"> "; // initialize $newstr
          
$wordCount = 0;
          while( (list(
$key, $val) = each($ar)) && $inLine ) {
            
$wordCount++;
            
$len = strlen( $val );
            
$wordStart = strpos( $s, $val, $index );
            
$last = $index;
            
$index = $wordStart + $len;

            
// test first for presence of comment
            
reset( $commentArr1 );
            while( list(
$k, $v) = each($commentArr1) ) {
              if(
ereg("$v", $val) && ! $inQuotes ) {
                
$col = color("#");
                
$newstr = $newstr .
                 
"</FONT><FONT COLOR=" . "\"$col\"> " .
                 
substr( $s, $wordStart ) . "</FONT>";
                
output( $dest, $newstr, "" );
                
$inLine = FALSE; // to abort wordlist processing
                
break;
              }
            }
            
reset( $commentArr2 );
            while( list(
$k, $v) = each($commentArr2) ) {
              if(
ereg("$v", $val) && ! $inQuotes ) {
                
$col = color(";");
                
$newstr = $newstr .
                 
"</FONT><FONT COLOR=" . "\"$col\"> " .
                 
substr( $s, $wordStart ) . "</FONT>";
                
output( $dest, $newstr, "" );
                
$inLine = FALSE; // to abort wordlist processing
                
break;
              }
            }
            if(
$inLine ) {
              if(
$cus_hilight > 0 && $wordCount == $cus_hilight &&
               
$filetype == CUSTOM ) {
                
$col = color("hilight");
                
$newstr = $newstr . substr( $s, $last, $wordStart-$last )
                 .
"<FONT COLOR=" . "\"$col\"> " . $val . "</FONT>";
              }
              
// check for quoted strings
              
else if( ($q = hasQuote($val)) ) {  // we found a quote
                
if( ! $inQuotes ) {
                  
$qType = $q;
                  
$col = color( "q" );
                  
$inQuotes = TRUE;
                  
// 1st case:  quote-char is first char in word
                  
$qStart = strpos( $val, $q );
                  
$qStop = strpos( $val, $q, $qStart+1 );
                  if(
$qStart == 0 ) {
                    
$newstr = $newstr . substr( $s, $last, $wordStart-$last ) .
                     
"</FONT><FONT COLOR=" . "\"$col\"> " . $val;
                    if(
$qStop > 0 ) {  // test for closing quote as well..
                      
$newstr = $newstr . "</FONT>";
                      
$inQuotes = FALSE;
                    }
                  }
                  else {
                    
// 2nd case:  not 1st, but char is only quote-char in word
                    
$newstr = $newstr .
                     
substr( $s, $last, $wordStart-$last ) .
                     
substr( $val, 0, $qStart ) .
                     
"</FONT><FONT COLOR=" . "\"$col\">" .
                     
substr( $val, $qStart );
                    if(
$qStop > 0 ) {  // test for closing quote as well..
                      
$newstr = $newstr . "</FONT>";
                      
$inQuotes = FALSE;
                    }
                  }
                  continue;
                }
                else {
                  if(
$q == $qType ) { // check for right kind of quote
                    
$inQuotes = FALSE;
                    
$newstr = $newstr . substr( $s, $last, $wordStart-$last ) .
                     
"<FONT COLOR=" . "\"$col\"> " . $val .
                     
"</FONT><FONT COLOR=" . "\"$$lastCol\"> ";
                    continue;
                  }
                }
              }
              else {
// check for reserved  or for builtin words
                
if( ! $inQuotes ) {
                  
reset( $reserved );
                  
$isReserved = false;
                  while( list(
$k, $v) = each($reserved) ) {
                    if(
$val == $v ) {
                      
$isReserved = true;
                      break;
                    }
                  }
                }
                if(
$isReserved && ! $inQuotes &&
                 (
$filetype == BOURNE || $filetype == CUSTOM) ) {
                  
// then colorize it
                  
$col = color( "reserved" );
                  
$newstr = $newstr . substr( $s, $last, $wordStart-$last )
                   .
"<FONT COLOR=" . "\"$col\"> " . $val . "</FONT>";
                }
                else {
// check for builtin commands
                  
if( ! $inQuotes && $filetype != GENERIC ) {
                    
$bArr = $builtin;
                    
reset( $bArr );
                    
$isBuiltin = false;
                    while( list(
$k, $v) = each($bArr) ) {
                      if(
$val == $v ) {
                        
$isBuiltin = true;
                        break;
                      }
                    }
                  }
                  if(
$isBuiltin && ! $inQuotes && $filetype != GENERIC ) {
                    
// then colorize it
                    
$col = color( "builtin" );
                    
$newstr = $newstr . substr( $s, $last, $wordStart-$last )
                     .
"<FONT COLOR=" . "\"$col\"> " . $val . "</FONT>";
                  }
                  else {
// just output the word
                    
$newstr = $newstr . substr( $s, $last, $wordStart-$last )
                     .
$val;
                  }
                }
              }
            }
          }
          if(
$inLine ) { // add the font closer to match beginning one
            
$newstr = $newstr . "</FONT>\n";
            
output( $dest, $newstr, "" );
          }
        }
      }

      
insertInto($dest, $realName, TRAILER );
      
fclose( $src );
      
fclose( $dest );

      
// finally, give web-path to file for download
      
$p = DOWNLOADS . "/" . basename($p);
    }
    else {
      
$p = "";
    }

    return
$p;
  }

  
/** 'main' starts **/

  // Vars:
  
$lineNo = 0;            // line # in downloaded file

  
if( $argv ) {
//    echo "DEBUG: Cmd line args $argv[0]<BR>";
  
}
/**
  echo "DEBUG: hint is  " . $hint . "<BR>";
  echo "DEBUG: bkgd is  " . $background . "<BR>";
  echo "DEBUG: frgd is  " . $foreground . "<BR>";
  echo "DEBUG: comments is  " . $comments . "<BR>";
  echo "DEBUG: keycolor is  " . $keycolor . "<BR>";
  echo "DEBUG: builtinCol is  " . $builtinCol . "<BR>";
  echo "DEBUG: strings is  " . $strings . "<BR>";
  echo "DEBUG: cus_comments1 is  " . $cus_comments1 . "<BR>";
  echo "DEBUG: cus_reserved is  " . $cus_reserved . "<BR>";
  echo "DEBUG: cus_built is  " . $cus_built . "<BR>";
  echo "DEBUG: cus_hilight is  " . $cus_hilight . "<BR>";
**/
  
if( file_exists($userfile) ) {
    
$f = buildPath( $userfile_name );  // generate tmp pathname
    
if( strlen($f) > 0 ) {
      if(
rename($userfile, $f) ) {  // rename the file to tmp name
        
$ar = "";
        
/** fork the 'file' command to provide funky info **/
        
$cmd = EscapeShellCmd( "/usr/bin/file -b $f" );
        
exec( $cmd, $ar, $ret );
        if(
$ret == 0 ) {
          
// need some logic to test for file type - ie: should be text-based
          
if( eregi("executable", $ar[0]) || eregi("object", $ar[0]) ||
           
eregi("relocatable", $ar[0]) || eregi("archive", $ar[0]) ||
           
eregi("data", $ar[0]) ) {
            
printf("Sorry, your input file <FONT COLOR=\"red\"><I>%s</I></FONT>
             does not seem to be a text file, but reports file type:
             <I>%s</I>"
, $userfile_name, $ar[0] );
          }
          else {
            
/** look at $hint to find how to treat the file **/
            
$filetypes = array(  // duplicate html-page types here for doc.
              
"Auto-detect"=>"auto",
              
"Bourne shell"=>"bourne",
              
"C-shell"=>"csh",
              
"Custom"=>"custom",
              
"Generic configuration file"=>"generic"
            
);
            
$ft = "auto";
            while( list(
$key, $value) = each($filetypes) ) {
              if(
$key == $hint ) {
                
$ft = $value;
                break;
              }
            }
            if(
$ft == AUTO ) {
              if(
eregi("Bourne", $ar[0]) ) {
                
$ft = BOURNE;
              }
              else if(
eregi("C shell", $ar[0]) ) {
                
$ft = CSH;
              }
              else if(
eregi("Custom", $ar[0]) ) {
                
$ft = CUSTOM;
              }
              else {
                
$ft = GENERIC;
              }
            }
//  echo "DEBUG: filetype is  " . $ft . "<BR>";

            /** now process the file && provide link for user **/
            
initGlobals( $ft, $cus_comments1, $cus_comments2, $cus_built,
             
$cus_reserved );
            
$html = htmlize( $f, $userfile_name, $ft );

            
/** output table with resulting data **/
            
$h = $REMOTE_ADDR . " (" . gethostbyaddr($REMOTE_ADDR) . ")";
            echo
"<TABLE BORDER=\"3\" WIDTH=\"400\">\n";
            echo
"<TR><TH BGCOLOR=\"#FFFF99\" VALIGN=\"top\">Information</TH>
             <TH BGCOLOR=\"#FFFF99\" VALIGN=\"top\">Result</TH></TR>\n"
;
            echo
"<TR><TD ALIGN=\"left\" BGCOLOR=\"#FFFFFF\"><B>File:</B></TD>
             <TD ALIGN=\"left\" BGCOLOR==\"#CDB5CD\">$userfile_name</TD></TR>"
;
            echo
"<TR><TD ALIGN=\"left\" BGCOLOR=\"#FFFFFF\">
             <B>File Type:</B></TD>
             <TD ALIGN=\"left\" BGCOLOR==\"#CDB5CD\">$ar[0]</TD></TR>"
;
            echo
"<TR><TD ALIGN=\"left\" BGCOLOR=\"#FFFFFF\">
             <B>Received from:</B></TD>
             <TD ALIGN=\"left\" BGCOLOR==\"#CDB5CD\">$h</TD></TR>"
;
            echo
"<TR><TD ALIGN=\"left\" BGCOLOR=\"#FFFFFF\">
             <B>Formatted file:</B></TD>
             <TD ALIGN=\"left\" BGCOLOR==\"#CDB5CD\">
              <A HREF=\"$html\">$userfile_name</A></TD></TR>"
;
            echo
"</TABLE>\n";
          }
        }

        
/** cleanup! **/
        
if( unlink($f) ) {
          
rmdir( dirname($f) );
        }
      }
    }
  }
  else {
    
printf("Sorry, your input file <FONT COLOR=\"red\"><I>%s</I></FONT>
     was invalid."
, $userfile_name );
  }
?>

<HR>
<address>
<FONT SIZE=-1><A HREF="MAILTO:Denice.Deatrich@epfl.ch">Denice Deatrich</A></FONT>
</address>
<!-- Created: Sat Oct  2 14:32:11 MEST 1999 -->
<!-- hhmts start -->
Last modified: Sat May 27 14:09:12 MEST 2000
<!-- hhmts end -->
</body>
</html>