Location: PHPKode > projects > Sierra-php PHP Application Framework > sierra/lib/util/SRA_File.php
<?php
// {{{ Header
/*
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
 | SIERRA : PHP Application Framework  http://code.google.com/p/sierra-php |
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
 | Copyright 2005 Jason Read                                               |
 |                                                                         |
 | Licensed under the Apache License, Version 2.0 (the "License");         |
 | you may not use this file except in compliance with the License.        |
 | You may obtain a copy of the License at                                 |
 |                                                                         |
 |     http://www.apache.org/licenses/LICENSE-2.0                          |
 |                                                                         |
 | Unless required by applicable law or agreed to in writing, software     |
 | distributed under the License is distributed on an "AS IS" BASIS,       |
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.|
 | See the License for the specific language governing permissions and     |
 | limitations under the License.                                          |
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
 */
// }}}

// {{{ Constants
/**
 * identifies the tar compression format which uses the extension ".tar"
 * @type string
 */
define ('SRA_FILE_ARCHIVE_TYPE_TAR', 'tar');

/**
 * identifies the tar gzip compression format which uses the extension ".tar.gz"
 * @type string
 */
define ('SRA_FILE_ARCHIVE_TYPE_TAR_GZ', 'tar.gz');

/**
 * identifies the tar gzip compression format which uses the extension ".tgz"
 * @type string
 */
define ('SRA_FILE_ARCHIVE_TYPE_TAR_GZ1', 'tgz');

/**
 * identifies the zip compression format which uses the extension ".zip"
 * @type string
 */
define ('SRA_FILE_ARCHIVE_TYPE_ZIP', 'zip');

/**
 * Specifies the mime type file to use. This must be specified in order to use the getMimeType method
 * @type   String
 * @access public
 */
define('SRA_FILE_MIME_TYPE_FILE', "/etc/mime.types");

/**
 * Specifies whether or not the SRA_File class should operate in debug mode (outputs what it is doing to
 * the active console window).
 * @type   boolean
 * @access public
 */
define('SRA_FILE_DEBUG', false);

/**
 * The location where the cache csv arrays should be stored.
 * 
 * @type   String
 * @access public
 */
define('SRA_FILE_CACHE_DIR', SRA_DIR . "/tmp");
// }}}

// {{{ Includes
// }}}

// {{{ SRA_File

/**
 * SRA_File Class. This class encapsulates the basic file system function. It
 * wraps the functions suppressing normal PHP error reporting and insteads uses
 * SRA_Error.
 *
 * Note: For debugging turn track_errors on in the php.ini. The error messages
 * from this class will then be clearer because $php_errormsg is passed as part
 * of the message.
 *
 * Current Methods:
 * copy($src, $dest)
 * rename($src, $dest)
 * _mkdir($pathname, $mode)
 * mkdir($path, $mode=0777, $parents=TRUE)
 * chmod($pathname, $mode)
 * unlink($file)
 * symlink($target, $link)
 * touch($file)
 * _rmdir($dir)
 * rmdir($dir, $children=FALSE)
 * umask($mode)
 * read($file, &$rBuffer)
 * _write($file, &$rBuffer)
 * write($file, &$rBuffer, $parents=TRUE, $mode=0777)
 * fileMTime($file)
 * compareMTimes($file1, $file2)
 * parseIniFile($pathname, $processSections=FALSE)
 *
 * @author	Jason Read <hide@address.com>
 * @package sierra.util
 */

class SRA_File {
    
  // {{{ copy
  /**
   * used to copy a file from one location to another
   * @param	string $src source path and name file to copy. this file may be 
   * remote (http, https, ftp), and if it is, it will be downloaded using wget
   * (wget must be installed)
   * @param	string $dest destination directory or directory + file name. if 
   * $dest is a directory, the name of the file in $dest will be the same name 
   * as $src. if the $dest directory does not exist, it will be created
   * @param boolean $recursive whether or not the copy should be recursive. does 
   * not apply to remote $src files
   * @return void
   */
  function copy($src, $dest, $recursive=FALSE) {
    $dir = is_dir($dest) ? $dest : dirname($dest);
    if (!is_dir($dir)) { SRA_File::mkdir($dir); }
    if (strpos($src, '://') && ($wget = SRA_File::findInPath('wget'))) {
      if (is_dir($dest)) {
        $dest = SRA_Util::endsWith($dest, '/') ? $dest : $dest . '/';
        $dest .= basename($src);
      }
      system("$wget -O $dest $src");
    }
    else {
      system(SRA_File::findInPath('cp') . ' -f' . ($recursive ? ' -r' : '') . " $src $dest");
    }
  }
  // }}}
  
  // {{{ findInPath
  /**
   * looks for the program $bin in the $PATH environment variable. returns the 
   * absolute path to the program. returns NULL if $bin does not exist in the 
   * path
   * @param	string $bin the name of the program to look for
   * @param mixed $addlPathDirs an optional array of additional directories to 
   * consider in the path search. this value can also be a single path or 
   * multiple paths represented as a single string each separated by :
   * @return string
   */
  function findInPath($bin, $addlPathDirs=NULL) {
    $path = explode(':', getenv('PATH'));
    if ($addlPathDirs) { $path = array_merge(is_array($addlPathDirs) ? $addlPathDirs : explode(':', $addlPathDirs), $path); }
    return SRA_File::findFile($bin, $path);
  }
  // }}}
    
  // {{{ move
  /**
   * move a file
   * @param	string $src source path and name file to copy
   * @param	string $dest destination path and name of new file
   * @return void
   */
  function move($src, $dest) {
    system(SRA_File::findInPath('mv') . " -f $src $dest");
  }
  // }}}
    
    // {{{ rename()

    /**
     * Rename a file or directory. Hint: Use absolute paths if possible to avoid
     * confustion of where src and dest are located.
     *
     * @param	src		String. Source file/directory to rename.
     * @param	dest	String. Destination file/directory name.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function rename($src, $dest)
    {

        if (FALSE === @rename($src, $dest)) {// Copy FAILED. Log and return err.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::rename: Failed - Cannot rename $src to $dest. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Worked. Log and return TRUE.

            return TRUE;
        }
    }
    // }}}
    // {{{ _mkdir()

    /**
     * Create (make) a directory.
     *
     * @param	pathname	String. Path and name of new directory. This method
     *						only creates a single directory. It's used by mkdir.
     * @param	mode		Int. The mode (permissions) of the new directory. If
     *						using octal add leading 0. eg. 0777. Mode is affect
     *						by the umask system setting.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     * @access	private.
     */

    function _mkdir($pathname, $mode)
    {

        // Throw a warning if mode is 0. PHP converts illegal octal numbers to
        // 0 so 0 might not be what the user intended.

        if ($mode == 0) {
            $msg = "SRA_File::_mkdir: Warning - Creating a directory with permissions of 0. Is this what you wanted? Possible out of range octal number for mode.";
            return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM);
        }

        $str_mode = decoct($mode); // Show octal in messages.

        mkdir($pathname);
        if (!is_dir($pathname)) {// Mkdir FAILED.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::_mkdir: Failed - Cannot mkdir $pathname. Mode $str_mode. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Mkdir worked. Log and return TRUE.
            chmod($pathname, $mode);
            return TRUE;
        }
    }
    // }}}
    // {{{ mkdir()

    /**
     * This method makes one directory or recursively make directories in path.
     *
     * @param	path	String. Path of directories.
     * @param	mode	Int. The mode (permissions) of the new directory. If
     *					using octal add leading 0. eg. 0777. Mode is affect
     *					by the umask system setting.
     * @param	parents	Boolean.	True: Make parent directories as needed.
     *								False: Do not make parent directories.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     * @access	public
     */

    function mkdir($path, $mode=0777, $parents=TRUE)
    {

        // If dir already exists return TRUE.
        if (is_dir($path)) {
            return TRUE;
        }

        // Throw a warning if mode is 0. PHP converts illegal octal numbers to
        // 0 so 0 might not be what the user intended.

        if ($mode == 0) {
            $msg = "SRA_File::mkdir: Warning - Creating a directory with permissions of 0. Is this what you wanted? Possible out of range octal number for mode.";
            return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM);
        }

        $str_mode = decoct($mode); // Show octal in messages.

        // Only make directory if parents=FALSE
        if (FALSE === $parents) {

            // Call _mkdir.
            $error = SRA_File::_mkdir($path, $mode);

                if (SRA_Error::isError($error)) { // error.

                    $msg = "SRA_File::mkdir: Failed - Cannot mkdir $path. Mode $str_mode. ". $error->getErrorMessage();

                    return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                }

        } else { // Make parents and directory.

            // Break up path.
            $path_parts = preg_split("/\//", $path);

            if ($path_parts[0] == '') { // start at root.

                $dir_to_make = '/';

                // Pop off [0]
                array_shift($path_parts);

            } else {
                $dir_to_make = '';
            }

            foreach ($path_parts as $dir) {

                $dir_to_make .= $dir.'/';

                if (!is_dir($dir_to_make)) { // mkdir if not existing.

                    $error = SRA_File::_mkdir($dir_to_make, $mode);

                    if (SRA_Error::isError($error)) { // error.

                        $msg = "SRA_File::mkdir: Failed - Cannot mkdir $path. Mode $str_mode. ". $error->getErrorMessage();

                        return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                    }

                }

            }
        }
            // Mkdir worked. Log and return TRUE.

            return TRUE;

    }
    // }}}
    
    
  // {{{ chgrp
  /**
   * changes the group ownership of $file to $group
   * @param	string $file the file to change the group ownership of
   * @param string $group the name of the group to change group ownership to
   * @param boolean $recursive whether or not to change the group permissions 
   * recursively if $file is a directory
   * @return boolean
   */
  function chgrp($file, $group, $recursive=FALSE) {
    if (($results = chgrp($file, $group)) && is_dir($file) &&$recursive) {
      foreach(SRA_File::getFileList($file, '*', TRUE, 3) as $file) {
        SRA_File::chgrp($file, $group, TRUE);
      }
    }
    return $results;
  }
  // }}}
  
  
  // {{{ chown
  /**
   * changes the ownership of $file to $user
   * @param	string $file the file to change the ownership of
   * @param string $user the name of the user to change ownership to
   * @param boolean $recursive whether or not to change the permissions 
   * recursively if $file is a directory
   * @return boolean
   */
  function chown($file, $user, $recursive=FALSE) {
    if (($results = chown($file, $user)) && is_dir($file) &&$recursive) {
      foreach(SRA_File::getFileList($file, '*', TRUE, 3) as $file) {
        SRA_File::chown($file, $user, TRUE);
      }
    }
    return $results;
  }
  // }}}
  
  
  // {{{ chmod
  /**
   * change the permissions of a file or directory
   * @param	string $file the file to change the permissions for
   * @param	int $mode the new mode (permissions) for $file. if using octal add 
   * leading 0: i.e. 0777
   * @param boolean $recursive whether or not to change the file permissions 
   * recursively if $file is a directory
   * @return boolean
   */
  function chmod($file, $mode, $recursive=FALSE) {
    if (($results = chmod($file, $mode)) && is_dir($file) &&$recursive) {
      foreach(SRA_File::getFileList($file, '*', TRUE, 3) as $file) {
        SRA_File::chmod($file, $mode, TRUE);
      }
    }
    return $results;
  }
  // }}}
	
  
  // {{{ unlink
  /**
   * Delete a file or files. returns TRUE on success, FALSE otherwise (when 
   * $files is an array, FALSE will be returned if ANY of the files in that 
   * array could not be deleted)
   * @param	string $file path and/or name of file to delete. if this is an 
   * array, all of the files in the array will be deleted.
   * @return	boolean
   */
  function unlink($file) {
    $results = TRUE;
    // Multiple files
    if (is_array($file)) {
      foreach ($file as $f) {
        if (!SRA_File::unlink($f)) { $results = FALSE; }
      }
    }
    // Single file
    else {
      $results = unlink($file);
    }
    return $results;
  }
  // }}}
	
    // {{{ symlink()
    /**
     * Symbolically link a file to another name. Currently symlink is not
     * implemented on Windows.
     * Don't use if the application is to be portable.
     *
     * @param	target	String. Path and/or name of file to link.
     * @param	link	String. Path and/or name of link to be created.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function symlink($target, $link)
    {

        // If Windows OS then symlink() will report it is not supported in
        // the build. Use this error instead of checking for Windows as the OS.

        if (FALSE === @symlink($target, $link)) { // FAILED.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::symlink: Failed - Cannot symlink $target to $link. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Worked. Log and return TRUE.

            return TRUE;
        }

    }
    // }}}
    // {{{ touch()

    /**
     * Set the modification and access time on a file to the present time.
     *
     * @param	file	String. Path and/or name of file to touch.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function touch($file, $time=FALSE)
    {
        $fp = fopen($file, 'w');
        fwrite($fp, '');
        fclose($fp);
        return file_exists($file) ? TRUE : SRA_Error::logError("SRA_File::touch: Failed - Cannot touch $file. $php_errormsg", __FILE__, __LINE__, SRA_ERROR_PROBLEM);
    }
    // }}}
    // {{{ dirlist
    
    /**
     * Returns all file listings in a directory. This includes . and ..
     * Errors can occur if $dir is not a directory, or if the directory cannot be opened (read permissions, file handles, etc)
     * 
     * @param dir String. Full path of the directory to get a listing for
     * 
     * @return array on success. Err object on failure
     * @author Matthew Barlocker <hide@address.com>
     */
     
     function dirlist($dir)
     {
     	$ret = null;
     	
     	if (is_dir($dir))
     	{
     		if ($handle = opendir($dir))
     		{
     			$file = '';
     			$ret = array();
     			while (($file = readdir($handle)) !== false)
     			{
     				$ret[] = $file;
     			}
     		}
     		else
     		{
     			$ret = SRA_Error::logError('Directory could not be opened: ' . $dir, __FILE__, __LINE__, SRA_ERROR_OPERATIONAL);
     		}
     	}
     	else
     	{
     		$ret = SRA_Error::logError('Invalid directory: ' . $dir, __FILE__, __LINE__, SRA_ERROR_OPERATIONAL);
     	}
     	
     	return $ret;
     }
    // }}}
    // {{{ _rmdir()

    /**
     * Delete an empty directory.
     *
     * @param	dir	String. Path and/or name of empty directory to delete. Used
     * 						by rmdir.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function _rmdir($dir)
    {

        if (FALSE === @rmdir($dir)) { // FAILED.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::_rmdir: Failed - Cannot rmdir $dir. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Worked. Log and return TRUE.

            return TRUE;
        }

    }
    // }}}
    // {{{ rmdir()

    /**
     * Delete an empty directory OR a directory and all of its contents.
     *
     * @param	dir	String. Path and/or name of directory to delete.
     * @param	children	Boolean.	False: don't delete directory contents.
     *									True: delete directory contents.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function rmdir($dir, $children=FALSE)
    {

        // If children=FALSE only delete dir if empty.
        if (FALSE === $children) {

            // Call _rmdir.
            $error = SRA_File::_rmdir($dir);

            if (SRA_Error::isError($error)) { // error.

                $msg = "SRA_File::rmdir: Failed - Cannot rmdir $dir. ". $error->getErrorMessage();

                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

            }

        } else { // delete contents and dir.

            $handle = @opendir($dir);

            if (FALSE === $handle) { // SRA_Error.

                $msg = "SRA_File::rmdir: Failed - Cannot opendir() $dir. $php_errormsg";

                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

            } else { // Read from handle.

                // Don't error on readdir().
                while (false !== ($entry = @readdir($handle))) {

                    if ($entry != '.' &&$entry != '..') {

                        // Only add / if it isn't already the last char.
                        // This ONLY serves the purpose of making the
                        // output look nice:)

                        if (strpos(strrev($dir), '/') == 0) {// there is a /
                            $next_entry = $dir . $entry;
                        } else { // no /
                            $next_entry = $dir.'/'.$entry;
                        }

                        // NOTE: As of php 4.1.1 is_dir doesn't return FALSE it
                        // returns 0. So use == not ===.

                        // Don't error on is_dir()
                        if (is_link($next_entry) || !is_dir($next_entry)) { // Is file.

                            $error = SRA_File::unlink($next_entry); // Delete.

                            if (SRA_Error::isError($error)) { // error and return.

                                $msg = "SRA_File::rmdir: Failed - Cannot SRA_File::unlink() $next_entry. ". $error->getErrorMessage();

                                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                            }

                        } else { // Is directory.

                            $error = SRA_File::rmdir($next_entry, TRUE); // Delete

                            if (SRA_Error::isError($error)) { // error and return.

                                $msg = "SRA_File::rmdir: Failed - Cannot SRA_File::rmdir() $next_entry. ". $error->getErrorMessage();

                                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                            }

                        } // end is_dir else
                    } // end .. if
                } // end while
            } // end handle if

            // Don't error on closedir()
            @closedir($handle);

            $error = SRA_File::_rmdir($dir);

            if (SRA_Error::isError($error)) { // error and return.

                $msg = "SRA_File::rmdir: Failed - Cannot SRA_File::_rmdir() $dir. ". $error->getErrorMessage();

                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

            }

        }

        // Worked. Log and return TRUE.

        return TRUE;

    }
    // }}}
    // {{{ umask()

    /**
     * Set the umask for file and directory creation.
     *
     * @param	mode	Int. Permissions ususally in ocatal. Use leading 0 for
     *					octal. Number between 0 and 0777.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function umask($mode)
    {

        // Throw a warning if mode is 0. PHP converts illegal octal numbers to
        // 0 so 0 might not be what the user intended.

        if ($mode == 0) {
            trigger_error("SRA_File::umask: Warning - Creating a directory with permissions of 0. Is this what you wanted? Possible out of range octal number for mode.", SRA_ERROR_OPERATIONAL);
        }

        $str_mode = decoct($mode); // Show octal in messages.

        if (FALSE === @umask($mode)) { // FAILED.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::umask: Failed - Value $mode. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Worked. Log and return TRUE.

            return TRUE;
        }

    }
    // }}}
    // {{{ read()

    /**
     * Reads a file and stores the data in the variable passed by reference.
     *
     * @param	file	String. Path and/or name of file to read.
     * @param	rBuffer	Reference. Variable of where to put contents.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function read($file, &$rBuffer)
    {

        $fp = @fopen($file, "rb");  // b is for binary and used on Windows
                                    // ignored on *nix.

        if (FALSE === $fp) { // fopen FAILED.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::read: Failed - Cannot fopen $file. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // fopen worked. Log and try to lock it.

            if (FALSE) { // Locks don't seem to work on windows??? HELP!!!!!!!!!
            //if (FALSE === @flock($fp, LOCK_EX)) { // FAILED.
                // Add error from php to end of log message. $php_errormsg.
                $msg = "SRA_File::read: Failed - Cannot acquire flock on $file. $php_errormsg";

                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                return $resource;

            } else { // Try to get file size.

                $fs = @filesize($file);
                if (FALSE === $fs) { // FAILED.

                    // Add error from php to end of log message. $php_errormsg.
                    $msg = "SRA_File::read: Failed - Cannot get filesize of $file. $php_errormsg";

                    return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                } else { // Read file then close.

                    $rBuffer = @fread($fp, $fs);

                    if (FALSE === @fclose($fp)) { // FAILED.
                            // Add error from php to end of log message. $php_errormsg.
                            $msg = "SRA_File::read: Failed - Cannot fclose $file. $php_errormsg";

                            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                    } else {

                        return TRUE;

                    } // End fclose if

                } // End filesize if

            } // End flock if

        } // End fopen if
    }
    // }}}
    // {{{ _write()

    /**
     * _write the passed buffer to filename. Overwrites existing file if any.
     *
     * @param	file	Path and/or name of file to write.
     * @param	rBuffer	Reference. String to write.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function _write($file, &$rBuffer)
    {

        $fp = @fopen($file, "w");  // b is for binary and used on Windows
                                    // ignored on *nix.

		// fopen FAILED.
        if (FALSE === $fp) 
		{

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::_write: Failed - Cannot fopen $file. $php_errormsg";
            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        }
		// fopen worked. Log and try to lock it.
		else 
		{

			// FAILED
            if (FALSE === @flock($fp, LOCK_EX)) 
			{

                // Add error from php to end of log message. $php_errormsg.
                $msg = "SRA_File::_write: Failed - Cannot acquire flock on $file. $php_errormsg";

                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

            }
			// Write file.
			else 
			{
				// FAILED.
                if (-1 === @fwrite($fp, $rBuffer)) 
				{

                    // Add error from php to end of log message. $php_errormsg.
                    $msg = "SRA_File::_write: Failed - Cannot fwrite $file. $php_errormsg";

                    return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                }
				// Close.
				else 
				{
					// FAILED.
                    if (FALSE === @fclose($fp)) 
					{
                            // Add error from php to end of log message. $php_errormsg.
                            $msg = "SRA_File::_write: Failed - Cannot fclose $file. $php_errormsg";

                            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

                    } 
					else 
					{
                        return TRUE;
                    } // End fclose if

                } // End fwrite if

            } // End flock if

        } // End fopen if
    }
    // }}}
    // {{{ write()

    /**
     * Write() writes a file and makes directories in path if they don't
     * exist. additionally, this method handle synchronization issues using a 
     * temp lock file. it will wait up to 30 seconds before generating an error 
     * object when the file is locked at the time this method is invoked
     *
     * @param	file	String. Path and/or name of file to create.
     * @param	rBuffer	Reference. Contents to write.
     * @param	parents	Boolean. Create parent directories if they don't exist.
     * @param	mode	Int. The mode (permissions) of the new directories.
     *					If using octal add leading 0. eg. 0777. Mode is
     *					affect by the umask system setting.
     *
     * @return	TRUE on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function write($file, &$rBuffer, $parents=true, $mode=0777)
    {
      // try to get a lock on this file
      $lockFile = SRA_Controller::getSysTmpDir() . '/' . '.fwlock' . basename($file);
      $startTime = time();
      $curPid = file_exists($lockFile) ? SRA_File::toString($lockFile)*1 : NULL;
      while($curPid && SRA_Util::isProcessActive($curPid) && time() < ($startTime + (30))) {
        sleep(1);
      }
      if (file_exists($lockFile) && (!$curPid || SRA_Util::isProcessActive($curPid))) {
        $msg = "SRA_File::write: Failed - Cannot write to $file because a lock could not be obtained due to existing lock held by process " . $curPid;
        return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM);
      }
      $fp = fopen($lockFile, 'w');
      fwrite($fp, getmypid());
      fclose($fp);
      
        // If  already exists OR parents=FALSE. Write file and return.
        if (is_file($file) OR FALSE === $parents) 
		{
            $error = SRA_File::_write($file, $rBuffer);
			// error.
            if (SRA_Error::isError($error)) 
			{
                SRA_File::unlink($lockFile);
                $msg = "SRA_File::write: Failed - Cannot write() $file. ". $error->getErrorMessage();
                return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

            }
            SRA_File::unlink($lockFile);
            // Success.
            return TRUE;
        }

        // Throw a warning if mode is 0. PHP converts illegal octal numbers to
        // 0 so 0 might not be what the user intended.

        if ($mode == 0) 
		{
            trigger_error("SRA_File::write: Warning - Creating a directory with permissions of 0. " . 
						  "Is this what you wanted? Possible out of range octal number for mode.", SRA_ERROR_OPERATIONAL);
        }

        $str_mode = decoct($mode); // Show octal in messages.

        // Get path parts. Don't error.
        $path_parts = @pathinfo($file);

        // Make path.
        $error = SRA_File::mkdir($path_parts["dirname"], $mode, TRUE);
		
		// error.
        if (SRA_Error::isError($error)) 
		{

            SRA_File::unlink($lockFile);
            $msg = "SRA_File::write: Failed - Cannot write() $file. ". $error->getErrorMessage();
            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        }

        // Directory structure has been made write file.
        $error = SRA_File::_write($file, $rBuffer);
		
		// error.
        if (SRA_Error::isError($error)) 
		{
            SRA_File::unlink($lockFile);
            $msg = "SRA_File::write: Failed - Cannot write() $file. ". $error->getErrorMessage();
            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        }

        // Worked. Log and return TRUE.
        SRA_File::unlink($lockFile);
        return TRUE;

    }
    // }}}
    // {{{ fileMTime()

    /**
     * Get the modified time for a file.
     *
     * @param	file	String. Path and name of file.
     *
     * @return	Int. Unix timestamp on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function fileMTime($file)
    {

        $mtime = @fileMTime($file);

        if (FALSE === $mtime) { // FAILED. Log and return err.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::fileMTime: Failed - Cannot can not get modified time of $file. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Worked.
            return $mtime;
        }
    }
    // }}}
    // {{{ compareMTimes()

    /**
     * Compare the modified time of two files.
     *
     * @param	file1	String. Path and name of file1.
     * @param	file2	String. Path and name of file2.
     *
     * @return	Int. 	1 if file1 is newer.
                        -1 if file2 is newer.
                        0 if files have the same time.
                        Err object on failure.

     * @author  Charlie Killian, hide@address.com
     */

    function compareMTimes($file1, $file2)
    {

        $mtime1 = SRA_File::fileMTime($file1);
        $mtime2 = SRA_File::fileMTime($file2);

        if (SRA_Error::isError($mtime1)) { // FAILED. Log and return err.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::compareMTimes: Failed - Cannot can not get modified time of $file1. $mtime1->getErrorMessage";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } elseif (SRA_Error::isError($mtime2)) { // FAILED. Log and return err.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::compareMTimes: Failed - Cannot can not get modified time of $file2. $mtime2->getErrorMessage";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Worked. Log and return compare.

            // Compare mtimes.
            if ($mtime1 == $mtime2) {
                return 0;
            } else {
                return ($mtime1 < $mtime2) ? -1 : 1;
            } // end compare
        }
    }
    // }}}
    // {{{ parseIniFile()

    /**
     * Proccess an ini file returning an array of values.
     *
     * @param	pathname		String. Path and name to ini file.
     * @param	processSections	Boolean. TRUE include sections as associative
     *							array keys. See PHP manual.
     *
     * @return	Associative array of values on success. Err object on failure.
     * @author  Charlie Killian, hide@address.com
     */

    function parseIniFile($pathname, $processSections=FALSE)
    {

        $iniArray = @parseIni_file($pathname, $processSections);

        // parseIni_file() returns 0 not FALSE as of PHP 4.1.1 so don't use ===
        if (FALSE == $iniArray) { // Copy FAILED. Log and return err.

            // Add error from php to end of log message. $php_errormsg.
            $msg = "SRA_File::parseIniFile: Failed - Cannot parse $pathname. processSections=$processSections. $php_errormsg";

            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));

        } else { // Copy worked. Log and return TRUE.

            return $iniArray;
        }
    }
    // }}}

  // {{{ getFileList
  /**
   * returns an array of paths to files names that meet the specified criteria
   * @param	string $path	the base path to search
   * @param	string $pattern regular expression pattern or exact name to match. 
   * use '*' to match all. default is '*'
   * @param boolean $recursive whether or not the search should descend into 
   * child directories
   * @param int $type bitmask identifying the file types to return where 1 is 
   * files and 2 is directories (3=both files and directories). default is 1
   * @return string[]
   */
  function getFileList($path, $pattern='*', $recursive=FALSE, $type=1) {
    $files = array();
    $path = substr($path, -1) !== '/' ? $path .= '/' : $path;
    $pattern = $pattern == '*' ? '/^.*$/' : $pattern;
    if (is_dir($path) &&$pattern) {
      $d = dir($path);
      while(FALSE !== ($file = $d->read())) {
        $filePath = $path . $file;
        if ($file != '.' &&$file != '..' && is_dir($filePath) &&$recursive) { $files = array_merge($files, SRA_File::getFileList($filePath, $pattern, $recursive, $type)); }
        if ($file == '.' || $file == '..' || ($pattern != $file && (((substr($pattern, 0, 1) == '/') && !preg_match($pattern, $file)) || substr($pattern, 0, 1) != '/'))) { continue; }
        if ((is_dir($filePath) && ($type & 2)) || (!is_dir($filePath) && ($type & 1))) { $files[] = $filePath; }
      }
      $d->close();
    }
    return $files;
  }
  // }}}

  // {{{ createRandomFile
  /**
   * creates a temp file prefixed by $pre and postfixed by $post in $dir with 
   * the contents $base. if $base is a path to a local or remote file the 
   * SRA_File::copy method will be used to copy it into the created file and the 
   * created file extension will be the same as the original file. the absolute 
   * path to the random file created will be returned
   * @param	string $dir the directory where the file should be created. if not 
   * specified, the SRA_Controller::getAppTmpDir will be used
   * @param	string $pre a string to add to the beginning of the file. this 
   * attribute is optional. The default is ''
   * @param string $post a string to add to the end of the file. this attribute 
   * is optional. The default is ''.
   * @param string $base optional file contents or path to an existing file to 
   * copy into the newly created random file
   * @param boolean $delete whether or not to delete this file automatically 
   * upon termination of the current php process
   * @param boolean $mkdir whether or not the file should be created as a 
   * directory
   * @return string
   */
  function createRandomFile($dir=NULL, $pre='', $post='', $base='', $delete=FALSE, $mkdir=FALSE) {
    $dir = is_dir($dir) ? $dir : SRA_Controller::getAppTmpDir();
    $dir = SRA_Util::endsWith($dir, '/') ? $dir : $dir . '/';
    $file = uniqid($pre);
    $file = $dir . $file . $post;
    if (is_file($base) || strpos($base, '://') && !strpos($base, ' ')) {
      $ext = SRA_Util::getFileExtension($base);
      if (!SRA_Util::endsWith($file, $ext)) { $file .= '.' . $ext; }
      SRA_File::copy($base, $file);
      if (!file_exists($file)) {
        $fp = fopen($file, 'w');
        fclose($fp);
      }
    }
    else {
      if ($mkdir) {
        SRA_File::mkdir($file);
      }
      else {
        $fp = fopen($file, 'w');
        fwrite($fp, $base);
        fclose($fp);
      }
    }
    if ($delete && is_file($file)) {
      static $_sraFileDeleteRandomFiles;
      SRA_File::deleteRandomFiles($file);
      if (!$_sraFileDeleteRandomFiles) {
        SRA_Controller::registerShutdownMethod($className = 'SRA_File', 'deleteRandomFiles');
        $_sraFileDeleteRandomFiles = TRUE;
      }
    }
    return $file;
  }
  // }}}
  
  // {{{ deleteRandomFiles
  /**
   * deletes any random files created using SRA_File::createRandomFile when 
   * $delete is TRUE
   * @param	string $file the file to delete, if NULL, the files will be deleted
   * @return void
   */
  function deleteRandomFiles($file=NULL) {
    static $_sraFileRandomFiles = array();
    if ($file) {
      $_sraFileRandomFiles[] = $file;
    }
    else {
      foreach($_sraFileRandomFiles as $file) {
        if (file_exists($file)) { SRA_File::unlink($file); }
      }
    }
  }
  // }}}

  // {{{ toString()
  /**
   * this method converts a file to a string. it returns an SRA_Error object if 
   * it is unable to find or open the file
   * @param	string $file the absolute or relative path to the file
   * @param	int $err the level of the error that should be thrown if the file 
   * cannot be found or opened. this value will correspond to one of the 
   * SRA_Error class error level constants. By default this value is 
   * SRA_ERROR_PROBLEM
   * @return string
   * @author Jason Read <hide@address.com>
   */
  function &toString($file, $err=SRA_ERROR_PROBLEM) {
    $ret = FALSE !== ($content = file(file_exists($file) ? $file : SRA_File::getRelativePath(NULL, $file))) ? ($ret = implode('', $content)) : SRA_Error::logError('SRA_File::toString - Failed: Could not open the file "' . $file . '"', __FILE__, __LINE__, $err);
    return $ret;
  }
  // }}}

    // {{{ getMimeType()
    /**
     * This method attempts to lookup the mime type for the file provided (based on the file extension)
     *
     * @param	fileName String. The name of the file to lookup the mime type for (this file must have an extension)
     *
     * @return	String
     * @author  Jason Read <hide@address.com>
     */

    function getMimeType( $file )
    {
        if (isset($file))
        {
            /* build an array keyed on the file ext */
            if (is_readable(SRA_FILE_MIME_TYPE_FILE))
            {
                $mime_types=array();
                /* open our mime.types file for reading */
                $mt_fd=fopen(SRA_FILE_MIME_TYPE_FILE,"r");
                while (!feof($mt_fd))
                {
                    /* pull a line off the file */
                    $mt_buf=trim(fgets($mt_fd,1024));
                    /* discard if the line was blank or started with a comment */
                    if (strlen($mt_buf) > 0) if (substr($mt_buf,0,1) != "#")
                    {
                        /* make temp array of the mime.types line we just read */
                        $mt_tmp=preg_split("/[\s]+/", $mt_buf, -1, PREG_SPLIT_NO_EMPTY);
                        $mt_num=count($mt_tmp);
                        /* if $mt_num = 1 then we got no file extensions for the type */
                        if ($mt_num > 1)
                        {
                            for ($i=1;$i<$mt_num;$i++)
                            {
                                /* if we find a comment mid-line, stop processing */
                                if (strstr($mt_tmp[$i],"#"))
                                {
                                    break;
                                /* otherwise stick the type in an array keyed by extension */
                                }
                                else
                                {
                                    $mime_types[$mt_tmp[$i]]=$mt_tmp[0];
                                }
                            }
                        /* zero the temporary array */
                        unset($mt_tmp);
                        }
                    }
                }
                /* close the mime.types file we were reading */
                fclose($mt_fd);
                $filePieces=explode(".", $file);
                if (array_key_exists($filePieces[count($filePieces)-1], $mime_types))
                    return $mime_types[$filePieces[count($filePieces)-1]];
            }
            else
                return SRA_Error::logError("SRA_File::getMimeType() failed: Unable to open mime.types file.",
                                        __FILE__, __LINE__, SRA_ERROR_PROBLEM);
        }
        else
            return SRA_Error::logError("SRA_File::getMimeType() failed: Invalid file parameter.",
                                    __FILE__, __LINE__, SRA_ERROR_PROBLEM);
    }
    // }}}
    
  // {{{ getArchiveType
  /**
   * returns the archive type identifier for the $archive specified
   * @param	string $archive the name of the archive
   * @return string
   */
  function getArchiveType($archive) {
    return SRA_Util::endsWith($archive, SRA_FILE_ARCHIVE_TYPE_TAR) ? SRA_FILE_ARCHIVE_TYPE_TAR : (SRA_Util::endsWith($archive, SRA_FILE_ARCHIVE_TYPE_TAR_GZ) || SRA_Util::endsWith($archive, SRA_FILE_ARCHIVE_TYPE_TAR_GZ1) ? SRA_FILE_ARCHIVE_TYPE_TAR_GZ : (SRA_Util::endsWith($archive, SRA_FILE_ARCHIVE_TYPE_ZIP) ? SRA_FILE_ARCHIVE_TYPE_ZIP : NULL));
  }
  // }}}

  // {{{ uncompress
  /**
   * uncompresses an archive
   * @param	string $archive the name of the archive to uncompress
   * @param string $dest the destination path where $archive should be 
   * uncompressed into. if not specified, the archive will be uncompressed into 
   * the parent directory of $archive
   * @param boolean $delete whether or not $archive should be deleted after it 
   * is uncompressed
   * @param string $archiveType the archive type (if $archive does not end with 
   * one of the SRA_FILE_ARCHIVE_TYPE_* file extensions
   * @return boolean
   */
  function uncompress($archive, $dest=NULL, $delete=FALSE, $archiveType=NULL) {
    $archiveType = $archiveType ? $archiveType : SRA_File::getArchiveType($archive);
    $dest = $dest ? $dest : dirname($archive);
    SRA_Util::printDebug('SRA_File::uncompress: Attempting to uncompress ' . $archive . ' to ' . $dest, SRA_FILE_DEBUG, __FILE__, __LINE__);
    if ($archiveType && file_exists($archive) && is_dir($dest) && is_writable($dest)) {
      switch ($archiveType) {
        case SRA_FILE_ARCHIVE_TYPE_TAR:
        case SRA_FILE_ARCHIVE_TYPE_TAR_GZ:
          if ($bin = SRA_File::findInPath('tar')) {
            exec("cd $dest; " . $bin . ' -x' . ($archiveType == SRA_FILE_ARCHIVE_TYPE_TAR_GZ ? ' -z' : '') . ' -f ' . $archive);
            if ($delete) unlink($archive);
            return TRUE;
          }
          break;
        case SRA_FILE_ARCHIVE_TYPE_ZIP:
          SRA_Util::printDebug('SRA_File::uncompress: Using zip uncompress', SRA_FILE_DEBUG, __FILE__, __LINE__);
          if ($bin = SRA_File::findInPath('unzip')) {
            exec("cd $dest; " . $bin . ' ' . $archive);
            if ($delete) unlink($archive);
            return TRUE;
          }
          break;
      }
    }
    return FALSE;
  }
  // }}}

  // {{{ compress
  /**
   * this method creates a compressed archive of $file. it returns the name of 
   * the of the compressed archive on success, FALSE otherwise
   * @param	string $file the path to the file or directory to compress
   * @param	string $archiveType the archive type to create. should be one of 
   * the SRA_FILE_ARCHIVE_TYPE_* constants
   * @param string $archive the name of the archive to create. if not specified 
   * the archive name will be the name $file plus the correct file extension 
   * based on the $archiveType specified and placed in the same directory as 
   * $file
   * @param	string $directory is $file is a directory, this parameter may be 
   * used to specify the starting directory for the archive. if not specified, 
   * the archive will start at the root directory ('/')
   * @return mixed
   */
  function compress($file, $archiveType=SRA_FILE_ARCHIVE_TYPE_TAR_GZ, $archive=NULL, $directory=NULL) {
    if ($directory && !SRA_Util::endsWith($directory, '/')) $directory .= '/';
    $archive = $archive ? $archive : $file . '.' . $archiveType;
    SRA_Util::printDebug('SRA_File::compress: Attempting to compress ' . $file . ' using archive type ' . $archiveType . ' and archive name ' . $archive, SRA_FILE_DEBUG, __FILE__, __LINE__);
    if (file_exists($file)) {
      switch ($archiveType) {
        case SRA_FILE_ARCHIVE_TYPE_TAR:
        case SRA_FILE_ARCHIVE_TYPE_TAR_GZ:
        case SRA_FILE_ARCHIVE_TYPE_TAR_GZ1:
          if ($bin = SRA_File::findInPath('tar')) {
            exec($bin . ($directory ? ' -C ' . $directory : '') . ($archiveType == SRA_FILE_ARCHIVE_TYPE_TAR_GZ || $archiveType == SRA_FILE_ARCHIVE_TYPE_TAR_GZ1 ? ' -z' : '') . ' -c -f ' . $archive . ' ' . ($directory ? str_replace($directory, '', $file) : $file));
            return $archive;
          }
        case SRA_FILE_ARCHIVE_TYPE_ZIP:
          if ($bin = SRA_File::findInPath('zip')) {
            exec(($directory ? "cd $directory;" : '') . $bin . ' -q -r ' . $archive . ' ' . ($directory ? str_replace($directory, '', $file) : $file));
            return $archive;
          }
      }
    }
    return FALSE;
  }
  // }}}

  // {{{ findFile
  /**
   * looks for $file in $dirs and returns the absolute path if it is found or 
   * NULL if it is not
   * @param	string $file the name of the file to look for
   * @param	array $dirs the array of directories to look in
   * @return string
   */
  function findFile($file, $dirs) {
    foreach($dirs as $dir) {
      $dir = SRA_Util::endsWith($dir, '/') ? $dir : $dir . '/';
      if (is_file($dir . $file)) { return $dir . $file; }
    }
    return NULL;
  }
  // }}}

    // {{{ getContents()
    /**
     * This method returns the contents of a file as a string limited by the parameters specified.
     * It returns false if there are no more blocks in the file that meet the criteria.
     *
     * @param	fp - A reference to the file pointer for the file.
     * @param	options - A reference to a key based options array. The possible values for options are as follows:
     * 			'start_string' => 	String that should appear at the start of a block of text to be parsed. This value will be
     *								converted to an array if it contains a "&". In this case, all values in the array must exist in the line.
     *								If this value contains a ':', the end string criteria will be converted to an array of arrays each containing
     * 								one set of strings that must be present for the end to have been found.
     * 			'start_line_num' => String that should appear at the start of a block of text to be parsed.
     *					   			This option is overriden if 'start_string' is specified.
     * 			'end_string' => 	String that should appear at the end of a block of text to be parsed
     *				   				(either this text or an EOF should exist). This value will be
     *								converted to an array if it contains a "&". In this case, all values in the array must exist in the line.
     *								If this value contains a ':', the end string criteria will be converted to an array of arrays each containing
     * 								one set of strings that must be present for the end to have been found.
     * 			'header_lines' => 	The # of lines that make up the file header. These lines will be ignored.
     *								This value is only taken into consideration when at BOF.
     * 			'line_count' => 	The # of lines to be returned. This option is overriden if 'end_string' is specified.
     * 			'min_line_count' => This option specifies the minimum # of lines that should exist in a text block.
     *
     * @return	String
     * @author  Jason Read <hide@address.com>
     */

    function &getContents( &$fp, &$options )
    {
        SRA_Util::printDebug("getContents - called with the following options:",
                        SRA_FILE_DEBUG, __FILE__, __LINE__);
        SRA_Util::printDebug($options, SRA_FILE_DEBUG, __FILE__, __LINE__);

        $string='';
        $buffer='';
        $start=false;
        $searchStartVals=false;
        $searchStopVals=false;
        $headerChecked=false;
        if (ftell($fp)!=0 || !array_key_exists("header_lines", $options))
            $headerChecked=true;

        $lineNum=0;
        $lineCount=0;
        while (!feof ($fp))
        {
            $prevPos=ftell($fp);
            $buffer=fgets($fp, 4096);
            $lineNum++;

            SRA_Util::printDebug("getContents - lineNum=$lineNum - Processing buffer: '$buffer'",
                            SRA_FILE_DEBUG, __FILE__, __LINE__);

            // Check for start
            if (!$start)
            {
                SRA_Util::printDebug("getContents - lineNum=$lineNum - Checking if buffer should be included in return value.",
                                SRA_FILE_DEBUG, __FILE__, __LINE__);

                // check if header has been skipped
                if (!$headerChecked && array_key_exists("header_lines", $options) && ($lineNum - 1) == $options["header_lines"])
                {
                    SRA_Util::printDebug("getContents - lineNum=$lineNum - Header has been checked.",
                                    SRA_FILE_DEBUG, __FILE__, __LINE__);
                    $lineNum=0;
                    $headerChecked=true;
                }

                // start_string overrides start_line_num
                if ($headerChecked && array_key_exists("start_string", $options))
                {
                    if (!$searchStartVals)
                    {
                        $search=str_replace("#EOL#", '', $options["start_string"]);
                        $search=str_replace("\\n", "\n", $search);
                        $searchStartVals=explode(':', $search);
                        for ($i=0; $i<count($searchStartVals); $i++)
                            $searchStartVals[$i]=explode("&", $searchStartVals[$i]);
                    }
                    SRA_Util::printDebug("getContents - lineNum=$lineNum - Checking for existence of start search strings.",
                                    SRA_FILE_DEBUG, __FILE__, __LINE__);
                    for ($i=0; $i<count($searchStartVals); $i++)
                    {
                        $start=true;
                        foreach ($searchStartVals[$i] as $search)
                        {
                            if (!strpos(' ' . $buffer, $search))
                            {
                                SRA_Util::printDebug("getContents - lineNum=$lineNum - Start search string $i '$search' NOT found in buffer. Proceeding to next
                                                 search start row...", SRA_FILE_DEBUG, __FILE__, __LINE__);
                                $start=false;
                                break;
                            }
                            else
                            {
                                SRA_Util::printDebug("getContents - lineNum=$lineNum - Start search string $i '$search' found in buffer.",
                                                SRA_FILE_DEBUG, __FILE__, __LINE__);
                            }
                        }
                        if ($start==true)
                            break;
                    }
                }
                // start line num has been reached
                else if ($headerChecked && array_key_exists("start_line_num", $options) && ($lineNum - 1) == $options["start_line_num"])
                {
                    SRA_Util::printDebug("getContents - lineNum=$lineNum - Start line num has been reached. Adding buffer.",
                                    SRA_FILE_DEBUG, __FILE__, __LINE__);
                    $start=true;
                }
                else if ($headerChecked && !array_key_exists("start_line_num", $options))
                {
                    SRA_Util::printDebug("getContents - lineNum=$lineNum - No start line num or start string specified. Adding buffer.",
                                    SRA_FILE_DEBUG, __FILE__, __LINE__);
                    $start=true;
                }
            }

            // Check for end
            // end_string overrides line_count
            if ($lineCount>0 &&$start && array_key_exists("end_string", $options))
            {
                SRA_Util::printDebug("getContents - lineNum=$lineNum - Checking if end of contents has been reached.",
                                SRA_FILE_DEBUG, __FILE__, __LINE__);
                if (!$searchStopVals)
                {
                    $stopSearch=str_replace("#EOL#", '', $options["end_string"]);
                    $stopSearch=str_replace("\\n", "\n", $stopSearch);
                    $searchStopVals=explode(':', $stopSearch);
                    for ($i=0; $i<count($searchStopVals); $i++)
                        $searchStopVals[$i]=explode("&", $searchStopVals[$i]);
                }

                for ($i=0; $i<count($searchStopVals); $i++)
                {
                    $stop=true;
                    foreach ($searchStopVals[$i] as $stopSearch)
                    {
                        if (!strpos(' ' . $buffer, $stopSearch) &&$stopSearch != "\n")
                        {
                            SRA_Util::printDebug("getContents - lineNum=$lineNum - Stop search string $i '$stopSearch' NOT found in buffer. Proceeding to next row...",
                                            SRA_FILE_DEBUG, __FILE__, __LINE__);
                            $stop=false;
                            break;
                        }
                        else
                        {
                            SRA_Util::printDebug("getContents - lineNum=$lineNum - Stop search string $i '$stopSearch' found in buffer.",
                                            SRA_FILE_DEBUG, __FILE__, __LINE__);;
                        }
                    }
                    if ($stop==true)
                        break;
                }

                if ($stop)
                {
                    if (array_key_exists("min_line_count", $options))
                    {
                        SRA_Util::printDebug("getContents - lineNum=$lineNum - Checking if min line count requirement has been fulfilled.",
                                        SRA_FILE_DEBUG, __FILE__, __LINE__);
                        if ($lineCount >= $options["min_line_count"])
                        {
                            fseek($fp, $prevPos);
                            break;
                        }
                        else
                        {
                            SRA_Util::printDebug("getContents - lineNum=$lineNum - Min line count " . $options["min_line_count"] . " has NOT been reached.",
                                            SRA_FILE_DEBUG, __FILE__, __LINE__);
                        }
                    }
                    else
                    {
                        fseek($fp, $prevPos);
                        break;
                    }
                }
            }
            // line count has been reached
            else if (array_key_exists("line_count", $options) &&$lineCount == $options["line_count"])
            {
                SRA_Util::printDebug("getContents - lineNum=$lineNum - line count " . $options["line_count"] . " has been reached.",
                                SRA_FILE_DEBUG, __FILE__, __LINE__);
                fseek($fp, $prevPos);
                break;
            }

            if ($start)
            {
                SRA_Util::printDebug("getContents - lineNum=$lineNum - Adding buffer to return string.",
                                SRA_FILE_DEBUG, __FILE__, __LINE__);
                $string .= $buffer;
                $lineCount++;
            }

        }
        if ($string=='')
            return FALSE;
        else
            return $string;
    }
    // }}}
	
    // {{{ appendToFile()
    /**
     * This method is used to append a data to a file. It is useful for such things as 
	 * writing to data files such as csv files. If the file does not exist it is created. 
	 * This is a static method of the SRA_File class. All data appened to the file will be
	 * followed by a newline.
     *
     * @param	fileName. String. The name of the file to write to. This file may use 
	 * 			dynamic Date/Time tags. It will be passed through the SRA_GregorianDate::parseString 
	 * 			method. If the file does not exist, it will be created.
     * @param	data. String. The data to append to the text. 
	 * @param	header. String. An optional header to add to the file if it is created 
	 * 			(does not already exist).
	 * @param	chownUser. String. A user to chown the file to if it is created.
     *
     * @return	String - false if not found
     * @author  Jason Read <hide@address.com>
     */

    function appendToFile( $fileName, $data, $header=false, $chownUser=false )
    {
		$dateTime = new SRA_GregorianDate();
        $fileName = $dateTime->parseString($fileName);
		if (!file_exists($fileName))
		{
			if (!$fp = @fopen($fileName, "w"))
			{
				return SRA_Error::logError("SRA_File::appendToFile: Failed - Unable to create file '$fileName'", __FILE__, __LINE__, SRA_ERROR_PROBLEM);
			}
			if ($header)
			{
				if (-1 === @fwrite($fp, $header . "\n"))
				{
					return SRA_Error::logError("SRA_File::appendToFile: Failed - Unable to write header to file.", __FILE__, __LINE__, SRA_ERROR_PROBLEM);
				}
			}
			fclose($fp);
			if ($chownUser)
			{
				if (!chown($fileName, $chownUser))
				{
					return SRA_Error::logError("SRA_File::appendToFile: Failed - Unable to chown file '$fileName' to user '$chownUser'", 
											__FILE__, __LINE__, SRA_ERROR_PROBLEM);
				}
			}
		}
		if (!$fp = @fopen($fileName, "a"))
		{
			return SRA_Error::logError("SRA_File::appendToFile: Failed - Unable to open file '$fileName'", __FILE__, __LINE__, SRA_ERROR_PROBLEM);
		}
		if (-1 === @fwrite($fp, $data . "\n"))
		{
			return SRA_Error::logError("SRA_File::appendToFile: Failed - Unable to write data to file.", __FILE__, __LINE__, SRA_ERROR_PROBLEM);
		}
		fclose($fp);
    }
    // }}}

    // {{{ parseIni()
    /**
     * Load in the ini file specified in filename, and return
     * the settings in an associative array. By setting the
     * last $processSections parameter to true, you get a
     * multidimensional array, with the section names and
     * settings included. The default for processSections is
     * false.
     *
     * This functions should replace php native parseIni_file which has too
     * many shortcomings as of v 4.2.2.
     *
     * @param   fileName. String. ini file to parse.
     * @param   processSections. boolean. Optional parameter that specifies 
	 * whether or not to break ini sections out into seperate elements in 
	 * the returned array. The default value for this is false.
     *
     * @access	public static
     * @return	String[]: An associative array containing the data
     * @author	<hide@address.com>
     * @author	Sebastien Cevey <hide@address.com>
     * @author	Jason Read <hide@address.com>
     */
    function parseIni($fileName, $processSections=false)
    {
        if (!is_readable($fileName))
        {
            // SRA_Error.
            $msg = "SRA_File::parseIni: Failed - Passed fileName isn't readable, $fileName.";
            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));
        }

        if (!is_bool($processSections))
        {
            // SRA_Error.
            $msg = "SRA_File::parseIni: Failed - Passed processSections isn't a boolean. It's a ". gettype($processSections);
            return(SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM));
        }

        $iniArray = array();
        $secName = '';

        $lines = file($fileName);

        foreach($lines as $line)
        {
            $line = trim($line);

            // Don't process blank lines or comments.
            if($line == '' || $line{0} == ";" || $line{0} == "#")
            {
                continue;
            }

            if($line{0} == "[" &&$line{strlen($line) - 1} == "]")
            {
                $secName = substr($line, 1, strlen($line) - 2);
            }
            else
            {
                $pos      = strpos($line, "=");
                $property = trim(substr($line, 0, $pos));
                $value    = trim(substr($line, $pos + 1));

                if($processSections)
                {
                    $iniArray[$secName][$property] = $value;
                }
                else
                {
                    $iniArray[$property] = $value;
                }
            }
        }

        return($iniArray);
    }
    // }}}
	
    // {{{ arrayToFile()
    /**
     * This method converts an array to a file. 
     *
	 * @param	$fileName. String. The name of the file to write to. Any existing data 
	 * 			in this file will be overwritten.
	 * @param	$arrayName. String. The name to specify for the array in the file. 
	 * @param	$array. String[]. The array to write to the file. 
	 * @param	$append. boolean. Whether or not the array should be appended to the 
	 * 			file if it already exists. The default value for this parameter is 
	 * 			false: existing data will be overwritten.
     * @access	private static
     * @return	boolean
     * @author	Jason Read <hide@address.com>
     */
    function arrayToFile($fileName, $arrayName, &$array, $append = false)
    {
		// SRA_File is not writable
		if (!is_dir(dirname($fileName)) || !is_writable(dirname($fileName)))
		{
			return SRA_Error::logError("SRA_File::arrayToFile: Failed - Directory '" . dirname($fileName) . 
								   "' does not exist or is not writable", __FILE__, __LINE__, SRA_ERROR_PROBLEM);
		}
		// No array name provided
		if (!is_scalar($arrayName))
		{
			return SRA_Error::logError("SRA_File::arrayToFile: Failed - Array name not provided", 
								   __FILE__, __LINE__, SRA_ERROR_PROBLEM);
		}
		// No array given
		if (!is_array($array))
		{
			return SRA_Error::logError("SRA_File::arrayToFile: Failed - array parameter is not an array", 
								   __FILE__, __LINE__, SRA_ERROR_PROBLEM);
		}
		
		// Write array to file
		$buffer = SRA_Util::bufferArray($array, $arrayName);
		$buffer = "<?php\n" . $buffer . "\n?>\n";
		if ($append)
		{
			SRA_File::appendToFile($fileName, $buffer);
		}
		else
		{
			SRA_File::write($fileName, $buffer);
		}
	}
	// }}}
	
	// {{{ fileToArray
	/**
	 * Converts a file to an array. Similiar to the 'file' function but also 
	 * strips out ending newlines
	 * @param file : String - the path to the file to convert
   * @access  public
	 */
	function &fileToArray($file) {
		$data = array();
    if (file_exists($file) && is_readable($file)) {
      $lines = file($file);
      $keys = array_keys($lines);
      foreach ($keys as $key) {
        $data[] = str_replace("\n", '', $lines[$key]);
      }
    }
		return $data;
	}
	// }}}
	
    // {{{ serialize()
    /**
     * Serializes an object to a file. Returns an SRA_Error object if any occur.  
     *
	 * @param	object : String - The object to serialize. An SRA_Error will be 
	 * 			returned if this parameter is a scalar value.
	 * @param	file : String - The file to serialize the object to. If this 
	 * 			parameter is not specified, a random file will be created and 
	 * 			the name of that file returned.
     * @access	public static
     * @return	String
     * @author	Jason Read <hide@address.com>
     */
    function serialize($object, $file = false)
    {
		// Validate parameters
		if (is_scalar($object))
		{
			$msg = "SRA_File::serialize: Failed - object parameter '$object' is scalar";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_CONTROLLER_DEBUG);
		}
		if ($file && !file_exists($file))
		{
			$msg = "SRA_File::serialize: Failed - file parameter '$file' does not exist";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_CONTROLLER_DEBUG);
		}
		
		// Get random file if necessary
		if (!$file)
		{
			if (SRA_Error::isError($conf =& SRA_Controller::getSysConf()))
			{
				$msg = "SRA_File::serialize: Failed - Could not obtain reference to sys conf";
				return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_CONTROLLER_DEBUG);
			}
			if (SRA_Error::isError($file = SRA_File::createRandomFile(SRA_Controller::getAppTmpDir())))
			{
				$msg = "SRA_File::serialize: Failed - Random file could not be created in directory: '" . SRA_Controller::getAppTmpDir() . "'";
				return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_ASYNC_PROCESS_DEBUG);
			}
		}
		
		// Write serialized object to file
		$object = @serialize($object);
		if (FALSE === $object || SRA_Error::isError(SRA_File::write($file, $object)))
		{
			unlink($file);
			$msg = "SRA_File::serialize: Failed - Serialized object could not be serialized or written to file '$file'";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_ASYNC_PROCESS_DEBUG);
		}
		
		return $file;
		
	}
	// }}}
	
    // {{{ unserialize()
    /**
     * Unserializes an object from a file. Returns an SRA_Error object if any occur.
     *
	 * @param	file : String - The name of the file to unserialize.
	 * @param	deleteFile : boolean - Whether or not the serialization file should
	 *			be deleted if the object is unserialized successfully.
     * @access	public static
     * @return	Object
     * @author	Jason Read <hide@address.com>
     */
    function &unserialize($file, $deleteFile = false)
    {
		// Validate parameters
		if (!file_exists($file))
		{
			$msg = "SRA_File::unserialize: Failed - file parameter '$file' does not exist";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_CONTROLLER_DEBUG);
		}
		
		// Unserialize object
		$object = @unserialize(SRA_File::toString($file));
		if (FALSE === $object)
		{
			$msg = "SRA_File::unserialize: Failed - object could not be unserialized from file '$file'";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_CONTROLLER_DEBUG);
		}
		
		// Delete file 
		if ($deleteFile)
		{
			unlink($file);
		}
		
		return $object;
		
	}
	// }}}
	
    // {{{ propertiesArrayToFile()
    /**
     * This method performs the reverse of the propertiesFileToArray method. It writes an 
	 * associative properties file array to a properties file. 
     *
	 * @param	file : String -  The file to write to. Any existing data will be overwritten.
	 * @param	data : String[] - An associative array of properties to write.
     * @access	public static
     * @return	String[]
     * @author	Jason Read <hide@address.com>
     */
    function &propertiesArrayToFile($file, &$data)
	{
		// Validate parameters
		if (!is_array($data))
		{
			$msg = "SRA_File::propertiesArrayToFile: Failed - data parameter '" .gettype($data) . "' is not an array";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
		}
		
		$buffer = '';
		foreach ($data as $key => $value)
		{
			$buffer .= $key . "=" . $value . "\n";
		}
		
		if (SRA_Error::isError(SRA_File::write($file, $buffer)))
		{
			$msg = "SRA_File::propertiesArrayToFile: Failed - Could not write data to file '$file'";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
		}
		
	}
	// }}}
	
  // {{{ propertiesFileToArray()
  /**
   * This method converts a properties file to an associative array. It also performs 
	 * caching of this data to temp files in the {SRA_DIR}/tmp/l10n directory. 
	 * The property or key may also include imbedded php code through use of the 
	 * php:: {code} ::php convention. The code itself should be a simple statement that 
	 * returns a value. This value will be inserted into the key or property in the 
	 * location in which they exist (multiple code segments may exist in various locations 
	 * of a key or property).
   *
	 * @param	file : String - The file to convert. if this value is a string (and 
   * not a file), it will be written to a temporary file, converted to a 
   * properties array, and returned
	 * @param	keyCase : int - The key case to use. By default, this method will maintain
	 * 			the case used in the properties file (0). Optionally, you may specify -1
	 * 			in this parameter for lowercase only keys, and 1 for uppercase only keys.
	 * @param	languageCode : String - The language code
   * @param boolean $cache whether or not to cache files
   * @access	public static
   * @return	String[]
   * @author	Jason Read <hide@address.com>
   */
    function &propertiesFileToArray($file, $keyCase = 0, $languageCode = '', $cache=TRUE)
    {
      static $_sraFilePropertiesFileToArrayCache = array();
    
    $isRemote = strpos($file, '://') ? TRUE : FALSE;
    if ($cache &&$isRemote && isset($_sraFilePropertiesFileToArrayCache[$file])) { return $_sraFilePropertiesFileToArrayCache[$file]; }
    
    $tempFile = NULL;
    if (!$isRemote && is_string($file) && !file_exists($file)) {
      $tempFile = SRA_File::createRandomFile(FALSE, '', '', $file);
      $file = $tempFile;
    }
		
		// Validate parameters
		if (!$isRemote && !file_exists($file))
		{
			$msg = "SRA_File::propertiesFileToArray: Failed - file parameter '$file' does not exist";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
		}
		if ($keyCase != 0 &&$keyCase != -1 &&$keyCase != 1)
		{
			$msg = "SRA_File::propertiesFileToArray: Failed - keyCase parameter '$keyCase' is not valid - see api for valid values";
			return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
		}
		
		// Check for cached version
		$cacheFile = SRA_Controller::getAppTmpDir() . '/' . str_replace('/', '.', $file) . $languageCode;
		if ($cache && !$isRemote && !$tempFile && file_exists($cacheFile))
		{
			$msg = "SRA_File::propertiesFileToArray: Cache file found for properties file: '" . basename($file) . "'";
			SRA_Util::printDebug($msg, SRA_FILE_DEBUG, __FILE__, __LINE__);
			
			// Verify that cache file is up to date
			if (SRA_File::compareMTimes($cacheFile, $file) != -1)
			{
				include($cacheFile);
				if (!isset($properties) || !is_array($properties))
				{
					unlink($cacheFile);
					$msg = "SRA_File::propertiesFileToArray: Failed - Cache file '" . $cacheFile . 
						   "' does not contain valid data. Deleting, and continuing.";
					SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
				}
				else
				{
					$msg = "SRA_File::propertiesFileToArray: Accessed properties from cache file '$cacheFile'";
					SRA_Util::printDebug($msg, SRA_FILE_DEBUG, __FILE__, __LINE__);
				}
			}
			else
			{
				$msg = "SRA_File::propertiesFileToArray: Cache file is out of date and will be deleted for properties file: '" . basename($file) . "'";
				SRA_Util::printDebug($msg, SRA_FILE_DEBUG, __FILE__, __LINE__);
				unlink($cacheFile);
			}
		}
		
		// Cache not found... parse and process
		if (!isset($properties))
		{
			$properties = array();
			if (SRA_Error::isError($lines = file($file)))
			{
				$msg = "SRA_File::propertiesFileToArray: Failed - file parameter '$file' could not be opened";
				return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
			}
			$start = false;
			$fromLanguage = false;
			$translationFailed = false;
			foreach ($lines as $line)
			{
				$line = trim($line);
				if (substr($line, 0, 1) != '#' && strstr($line, '='))
				{
					$line = str_replace("\n", '', $line);
					$pair = explode('=', $line);
					$pkey = trim($pair[0]);
					$pvalue = substr(strstr($line, '='), 1);
					
					// check for line breaks
          $pvalue = str_replace("\\\\n", "[#BREAK#]", $pvalue);
					$pvalue = str_replace("\\n", "\n", $pvalue);
          $pvalue = str_replace("[#BREAK#]", "\\n", $pvalue);
					
					if ($keyCase == 1)
					{
						$pkey = strtoupper($pkey);
					}
					else if ($keyCase == -1)
					{
						$pkey = strtolower($pkey);
					}
					
					// Check for embedded php code
					if (strstr($pkey, "php::"))
					{
						$pkey = preg_replace_callback("'php::(.*?)::php'si", "codeToString", $pkey);
					}
					if (strstr($pvalue, "php::"))
					{
						$pvalue = preg_replace_callback("'php::(.*?)::php'si", "codeToString", $pvalue);
					}
					
					$properties[$pkey] = $pvalue;
				}
			}
			
			// Cache data
      if ($cache && !$isRemote) {
        $msg = "SRA_File::propertiesFileToArray: " . count($properties) . " properties loaded for properties file: '" . basename($file) . "'";
        SRA_Util::printDebug($msg, SRA_FILE_DEBUG, __FILE__, __LINE__);
        if ($tempFile) {
          SRA_File::unlink($tempFile);
        }
        else {
          SRA_File::arrayToFile($cacheFile, "properties", $properties);
          SRA_File::chmod($cacheFile, 0666);
        }
      }
      else if ($cache) { 
        $_sraFilePropertiesFileToArrayCache[$file] = $properties; 
      }
		}
		
		return $properties;
	}
	// }}}
	
	
	// {{{ cvsToArray
	/**
	 * Used to convert a CSV file into a two dimensional array
	 * {@see SRA_Util#arrayToCsv(String[][], boolean)}
	 * This method maintains a cache
	 * @param String file - the name of the csv file to convert
   * @param mixed indexCol - if the returned array should be indexed by one of 
   * the column values, this parameter should identify the 0-based index of that 
   * column. thus, if duplicate values exist in the database for that column, 
   * only a single instance of it will be returned (the last instance). this 
   * parameter may also be an array where the row indexes will be a concatendated 
   * value consisting of all of the values specified in that array where a 
   * numeric value will be pulled from the row and a string will be inserted into 
   * the key as-is. 
   * @param boolean cache whether or not to cache the results
   * @param char stringDelim - the string delimiter. strings may contain this 
   * delimiter value, but it should be preceded by \ or the same delimiter when 
   * this occurs
   * @access  private
	 * @return a two dimensional array representation of the csv file or FALSE
	 * if the there is a problem with the file
	 */
	function &cvsToArray($file, $indexCol=NULL, $cache=TRUE, $stringDelim='"') {
		static $arrays = array();
		if (!$arrays[$file] && file_exists($file)) {
			// Check cache for array
			$cacheFile = SRA_FILE_CACHE_DIR . "/." . str_replace('/', ".", $file);
			if ($cache && file_exists($cacheFile)) {
				// Verify that cache file is up to date
				if (SRA_File::compareMTimes($cacheFile, $file) != -1) {
          $arr = unserialize(SRA_File::toString($cacheFile));
					if (!isset($arr) || !is_array($arr)) {
						unlink($cacheFile);
						$msg = "SRA_File::cvsToArray: Failed - Cache file '" . $cacheFile . 
								 "' does not contain valid data. Deleting, and continuing: ";
						SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
					}
					else {
						$msg = "SRA_File::cvsToArray: Accessing array from cache ($cacheFile) for file: " . $file;
						SRA_Util::printDebug($msg, SRA_FILE_DEBUG, __FILE__, __LINE__);
						$arrays[$file] =&$arr;
					}
				}
				else {
					unlink($cacheFile);
					$msg = "SRA_File::cvsToArray: Cache file '" . $cacheFile . "' has expired. Deleting and continuing.";
					SRA_Util::printDebug($msg, SRA_FILE_DEBUG, __FILE__, __LINE__);
				}
			}
			// array needs to be created
			if (!$arrays[$file]) {
				$arrays[$file] = array();
        $fp = fopen($file, 'r');
        // 0 start column/line
        // 1 start column
        // 2 parsing non-string
        // 3 parsing string
        // 4 parsing string in break
        $state = 0;
        $buff = '';
        $line = 0;
        $row = array();
        $previousColCount = NULL;
        while(!feof($fp)) {
          $char = fgetc($fp);
          if ($state < 3 && ($char == ',' || $char == "\n" || feof($fp))) {
            if ($char != ',' &&$char != "\n") { $buff .= $char; }
            $row[] = $buff;
            $state = feof($fp) || $char == "\n" ? 0 : 1;
            if ($state == 0 && count($row) > 1) {
              if (is_array($indexCol)) {
                $key = '';
                foreach($indexCol as $idx) {
                  $key .= is_numeric($idx) ? $row[$idx] : $idx;
                }
                $arrays[$file][$key] = $row;
              }
              else {
                $indexCol !== NULL ? $arrays[$file][$row[$indexCol]] = $row : $arrays[$file][] = $row;
              }
              $line++;
              $previousColCount = count($row);
              $row = array();
            }
            $buff = '';
            continue;
          }
          else if ($char == $stringDelim &&$state <= 1) {
            $state = 3;
            continue;
          }
          else if ($char == $stringDelim &&$state == 3) {
            $state = fgetc($fp) == $stringDelim ? 4 : 2;
            fseek($fp, -1, SEEK_CUR);
            continue;
          }
          else if ($char == '\\' &&$state == 3) {
            $state = 4;
          }
          else if ($state == 4) {
            $state = 3;
          }
          else if ($state <= 1) {
            $state = 2;
          }
          $buff .= $char;
          // echo "Char: $char State: $state Buff: $buff Line: $line Col: " . count($row) . " EOF: " . feof($fp) . "\n";
        }
        fclose($fp);
        if ($cache) {
          $fp = fopen($cacheFile, 'w');
          fwrite($fp, serialize($arrays[$file]));
          fclose($fp);
        }
			}
		}
		return $arrays[$file];
	}
	// }}}
	
	// {{{ findFirstMatchingPath()
	/**
	 * returns the first matching path using the following search algorithm
	 * 	1) $baseDir1/$prefix[/$dir]/$postfix[/$file] (if both $prefix and $postfix specified)
	 * 	2) $baseDir1/$prefix[/$dir][/$file] (if $prefix specified)
	 * 	3) $baseDir1[/$dir]/$postfix[/$file] (if $postfix specified)
	 * 	4) $baseDir1[/$dir][/$file] (if exists)
	 *	... $baseDirN ...
	 *	4N + 1) FALSE
	 *
	 * @param array or scalar $baseDirs - the base directories to check
	 * @param	string $dir - the directory name
	 * @param string $file - an optional file to look for in the directory
	 * @param string $prefix - a prefix to check in the app and SRA_DIR
	 * @param string $postfix - a postfix to check in the app and SRA_DIR
	 * @access	public static
	 * @return	String or FALSE
	 */
	function findFirstMatchingPath($baseDirs, $dir = FALSE, $file=FALSE, $prefix = '', $postfix = '') {
		if (substr($file, 0, 1) == '/' && file_exists($file)) {
			return $file;
		}
		if (strstr($file, '/')) {
			$pieces = explode('/', $file);
			for($i=0; $i<count($pieces) - 1; $i++) {
				$dir .= '/' . $pieces[$i];
			}
			$file = $pieces[$i];
		}
		if ($baseDirs && !is_array($baseDirs)) {
			$baseDirs = array($baseDirs);
		}
		if (is_array($baseDirs) && ($dir || $file)) {
			foreach ($baseDirs as $baseDir) {
				$paths = array();
				$sep = '';
				if ($dir) {
					$sep = '/' . $dir;
				}
				if ($prefix &&$postfix) {
					$paths[] = $baseDir . '/' . $prefix . $sep . '/' . $postfix;
				}
				if ($prefix) {
					$paths[] = $baseDir . '/' . $prefix . $sep;
				}
				if ($postfix) {
					$paths[] = $baseDir . $sep . '/' . $postfix;
				}
				if ($dir) {
					$paths[] = $baseDir . '/' . $dir;
				}
        if ($paths) {
          foreach ($paths as $path) {
            if ($file) {
              $path .= '/' . $file;
            }
            $path = str_replace('//', '/', $path);
            $path = str_replace('//', '/', $path);
            // echo "CHECK $path<br >\n";
            if (($file && is_file($path)) || (!$file && is_dir($path))) {
              return $path;
            }
          }
        }
        else if ($file && !$dir && !$prefix && !$postfix && is_file($baseDir . '/' . $file)) {
          return $baseDir . '/' . $file;
        }
			}
		}
		return FALSE;
	}
	// }}}
  
	// {{{ mergedFilesCached
	/**
	 * returns TRUE if an invocation to SRA_File::renderMergedFiles will result 
   * in cached output
	 * @param	mixed $files either an array of file names or a space separated list 
   * of file names. if any file in this value is not valid, an error will be 
   * logged, but the rendering will continue
   * @param int $obfuscate if the $files are javascript or css source code 
   * files, whether or not those files should be obfuscated. obfuscation, 
   * removes all unnecessary comments and whitespace from the source files in 
   * order to reduce the file size. obfuscation depends on ext/phpJSO.php. use 
   * 1 for javascript obfuscation and 2 for css obfuscation
	 * @access	public static
	 * @return	boolean
	 */
	function mergedFilesCached($files, $obfuscate=FALSE, $removeWhiteSpace) {
    if (!is_array($files)) { $files = explode(' ', $files); }
    // create cache file name and determine latest modification time
    $mergeFiles = array();
    $cacheFileName = '.rmf_';
    foreach($files as $file) {
      if (file_exists($file)) {
        $cacheFileName .= str_replace(strstr(basename($file), '.'), '', basename($file));
        $maxModTime = !$maxModTime || filemtime($file) > $maxModTime ? filemtime($file) : $maxModTime;
        $mergeFiles[] = $file;
      }
      else {
        $msg = "SRA_File::renderMergedFiles: File '$file' is not valid";
        SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
      }
    }
    while(strlen($cacheFileName) > 50) {
      for($i=5; $i<strlen($cacheFileName); $i+=3) {
        $cacheFileName = substr($cacheFileName, 0, $i-1) . '*' . substr($cacheFileName, $i);
      }
      $cacheFileName = str_replace('*', '', $cacheFileName);
    }
    $cacheFileName = SRA_Controller::getAppTmpDir() . '/' . $cacheFileName . $obfuscate . $removeWhiteSpace;
    
    return count($mergeFiles) && file_exists($cacheFileName) && filemtime($cacheFileName) >= $maxModTime;
	}
	// }}}
  
	// {{{ renderMergedFiles
	/**
	 * this method merges multiple static or dynamic files and outputs their 
   * contents. to increase performance, the $files are cached into a single 
   * temporary file first and the contents of that file are output. the 
   * temporary file is re-written automatically whenever any of the $files are 
   * subsequently modified
	 * @param	mixed $files either an array of file names or a space separated list 
   * of file names. if any file in this value is not valid, an error will be 
   * logged, but the rendering will continue
   * @param int $obfuscate if the $files are javascript or css source code 
   * files, whether or not those files should be obfuscated. obfuscation, 
   * removes all unnecessary comments and whitespace from the source files in 
   * order to reduce the file size. obfuscation depends on ext/phpJSO.php. use 
   * 1 for javascript obfuscation and 2 for css obfuscation
   * @param boolean $removeWhiteSpace whether or not to remove whitespace from 
   * the merged files
   * @param boolean $processPhp whether or not to process php files (files that 
   * end with the SRA_SYS_PHP_EXTENSION extension)
   * @param string $search an optional value to replace with $replace. this can 
   * contain multiple values each separated by a pipe (|)
   * @param string $replace the value to replace $search with. this can contain 
   * multiple values each separated by a pipe (|)
	 * @access	public static
	 * @return	void
	 */
	function renderMergedFiles($files, $obfuscate=FALSE, $removeWhiteSpace=FALSE, $processPhp=TRUE, $search=NULL, $replace=NULL) {
    if (!is_array($files)) { $files = explode(' ', $files); }
    // create cache file name and determine latest modification time
    $mergeFiles = array();
    $cacheFileName = '.rmf_';
    foreach($files as $file) {
      if (file_exists($file)) {
        $cacheFileName .= str_replace(strstr(basename($file), '.'), '', basename($file));
        $maxModTime = !$maxModTime || filemtime($file) > $maxModTime ? filemtime($file) : $maxModTime;
        $mergeFiles[] = $file;
      }
      else {
        $msg = "SRA_File::renderMergedFiles: File '$file' is not valid";
        SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_FILE_DEBUG);
      }
    }
    while(strlen($cacheFileName) > 50) {
      for($i=5; $i<strlen($cacheFileName); $i+=3) {
        $cacheFileName = substr($cacheFileName, 0, $i-1) . '*' . substr($cacheFileName, $i);
      }
      $cacheFileName = str_replace('*', '', $cacheFileName);
    }
    $cacheFileName = SRA_Controller::getAppTmpDir() . '/' . $cacheFileName . $obfuscate . $removeWhiteSpace;
    
    // merge files if necessary
    if ((count($mergeFiles) && !file_exists($cacheFileName)) || filemtime($cacheFileName) < $maxModTime) {
      if ($obfuscate) { require_once('ext/phpJSO.php'); }
      $contents = '';
      foreach($mergeFiles as $file) {
        if ($processPhp && SRA_Util::endsWith($file, SRA_SYS_PHP_EXTENSION)) {
          ob_start();
          include($file);
          $str = ob_get_contents();
          ob_end_clean();
        }
        else {
          $str = & SRA_File::toString($file);
        }
        if ($obfuscate == 1) {
          $str = phpJSO_compress($str);
        }
        else if ($obfuscate == 2) {
          phpJSO_strip_strings_and_comments($str, $str_array, substr(md5(time()), 10, 2));
          $str = str_replace("\t", '', str_replace("\n", '', $str));
          phpJSO_restore_strings($str, $str_array);
          $str = str_replace("\n", '', $str);
        }
        $contents .= $str;
      }
      if ($search) {
        $replace = explode('|', $replace);
        foreach(explode('|', $search) as $key => $search) {
          $contents = str_replace($search, isset($replace[$key]) ? $replace[$key] : $replace[0], $contents);
        }
      }
      SRA_File::write($cacheFileName, $contents);
    }
    if (file_exists($cacheFileName)) {
      echo SRA_File::toString($cacheFileName);
    }
	}
	// }}}
	
	// {{{ getRelativePath()
	/**
	 * returns the full path for a app relative directory based on $dir 
	 * using the following search order: 
	 * 	1) app directory/$prefix[/$dir]/$postfix[/$file] (if both $prefix and $postfix specified)
	 * 	2) app directory/$prefix[/$dir][/$file] (if $prefix specified)
	 * 	3) app directory[/$dir]/$postfix[/$file] (if $postfix specified)
	 * 	4) app directory[/$dir][/$file] (if exists)
	 * 	5) SRA_File::getSysRelativePath($dir, $file, $prefix, $postfix) (otherwise)
	 *
	 * @param	string $dir - the base directory name (optional)
	 * @param string $file - an optional file to look for in the directory. either 
	 * $dir or $file should be specified or the app directory will always be 
	 * returned
	 * @param string $prefix - a prefix to check in the app and SRA_DIR
	 * @param string $postfix - a postfix to check in the app and SRA_DIR
	 * @access	public static
	 * @return	String or FALSE
	 */
	function getRelativePath($dir = FALSE, $file=FALSE, $prefix = '', $postfix = '') {
		if (SRA_Controller::isAppInitialized() && 
				($path = SRA_File::findFirstMatchingPath(SRA_Controller::getAppDir(), $dir, $file, $prefix, $postfix))) {
			return $path;
		}
    else if (file_exists($dir)) {
      return $dir;
    }
    else {
      return SRA_File::getSysRelativePath($dir, $file, $prefix, $postfix);
    }
	}
	// }}}
	
	// {{{ getSysRelativePath()
	/**
	 * returns the full path for the system relative directory based on $dir 
	 * using the following search order: 
	 * 	1) SRA_DIR/$prefix/$dir/$postfix[/$file] (if both $prefix and $postfix specified)
	 * 	2) SRA_DIR/$prefix/$dir[/$file] (if $prefix specified)
	 * 	3) SRA_DIR/$dir/$postfix[/$file] (if $postfix specified)
	 * 	4) SRA_DIR/$dir[/$file] (if exists)
	 * 	5) [$dir][/$file] (fixed path - if exists)
	 *	6) FALSE (otherwise)
	 *
	 * @param	string $dir - the directory name (optional)
	 * @param string $file - an optional file to look for in the directory. either 
	 * $dir or $file should be specified or the SRA_DIR will always be returned
	 * @param string $prefix - a prefix to check in the SRA_DIR
	 * @param string $postfix - a postfix to check in the SRA_DIR
	 * @access	public static
	 * @return	String or FALSE
	 */
	function getSysRelativePath($dir = FALSE, $file=FALSE, $prefix = '', $postfix = '') {
		return SRA_File::findFirstMatchingPath(array(SRA_DIR, '/'), $dir, $file, $prefix, $postfix);
	}
	// }}}
  
  // {{{ getFileSize
  /**
   * returns the size of $file in bytes
   * @param	string $file the file to return the size of
   * @param boolean $useLs whether or not to use the ls command instead of the 
   * built in filesize php function
   * @return int
   */
  function getFileSize($file, $useLs=FALSE) {
    $size = NULL;
    if ($useLs || (file_exists($file) && !($size = filesize($file)) && $size !== 0)) {
      if ($pieces = explode(' ', shell_exec(SRA_File::findInPath('ls') . ' -l ' . $file))) {
        $size = $pieces[4]*1;
      }
    }
    return $size;
  }
  // }}}
  
  // {{{ getFileSizeString
  
  function getFileSizeString($file, $useLs = FALSE, $decimals = 2)
  {
  	$size = $bytes = SRA_File::getFileSize($file, $useLs);
  	
  	$labels = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
  	for ($i = 0; $size >= 1024 && $i < count($labels); $i++)
  	{
  		$size = $size / 1024;
  	}
  	
  	return number_format($size, $decimals) . ' ' . $labels[$i];
  }
  // }}}
  
  // {{{ getUid
  /**
   * returns the uid of the owner of $file. this function works even if stat 
   * does not
   * @param	string $file the file to return the owner of
   * @return int
   */
  function getUid($file) {
    if (file_exists($file) && !($owner = fileowner($file)) && $owner !== 0) {
      if ($pieces = explode(' ', shell_exec(SRA_File::findInPath('ls') . ' -ln ' . $file))) {
        $owner = $pieces[2]*1;
      }
    }
    return $owner;
  }
  // }}}
  
  // {{{ getOwner
  /**
   * returns the name of the user that owns $file. this function works even if stat 
   * does not
   * @param	string $file the file to return the owner of
   * @return int
   */
  function getOwner($file) {
    if (($owner = SRA_File::getUid($file)) || $owner === 0) {
      $tmp = posix_getpwuid($owner);
      $owner = $tmp['name'];
    }
    return $owner;
  }
  // }}}
  
  // {{{ getGid
  /**
   * returns the gid of the group owner of $file. this function works even if stat 
   * does not
   * @param	string $file the file to return the owner of
   * @return int
   */
  function getGid($file) {
    if (file_exists($file) && !($owner = filegroup($file)) && $owner !== 0) {
      if ($pieces = explode(' ', shell_exec(SRA_File::findInPath('ls') . ' -ln ' . $file))) {
        $owner = $pieces[3]*1;
      }
    }
    return $owner;
  }
  // }}}
  
  // {{{ getGroupOwner
  /**
   * returns the name of the group that owns $file. this function works even if stat 
   * does not
   * @param	string $file the file to return the owner of
   * @return int
   */
  function getGroupOwner($file) {
    if (($owner = SRA_File::getGid($file)) || $owner === 0) {
      $tmp = posix_getgrgid($owner);
      $owner = $tmp['name'];
    }
    return $owner;
  }
  // }}}
  
  // {{{ getPermissions
  /**
   * returns the octal permissions of $file (i.e. 775 or 664). this function 
   * works even if stat does not
   * @param	string $file the file to return the permissions for
   * @param boolean $octal if TRUE, the return value will be an octal. otherwise 
   * it will be an int
   * @return mixed
   */
  function getPermissions($file, $octal=FALSE) {
    if (file_exists($file) && !($perms = fileperms($file))) {
      if ($pieces = explode(' ', shell_exec(SRA_File::findInPath('ls') . ' -l ' . $file))) {
        $operms = substr($pieces[0], 1, 3);
        $gperms = substr($pieces[0], 4, 3);
        $wperms = substr($pieces[0], 7, 3);
        $perms = '';
        for($i=1; $i<strlen($pieces[0]); $i++) {
          $perm = 0;
          if (substr($pieces[0], $i++, 1) != '-') { $perm += 4; }
          if (substr($pieces[0], $i++, 1) != '-') { $perm += 2; }
          if (substr($pieces[0], $i, 1) != '-') { $perm += 1; }
          $perms .= $perm;
        }
      }
    }
    else {
      $perms = sprintf('%o', $perms);
    }
    if ($perms) { return substr($perms, -3); }
  }
  // }}}
  
  // {{{ base64Decode
  /**
   * used the linux base64 command (if present) to decode $encoded. this 
   * function was created to avoid some unknown problems with the php 
   * base64_decode function
   * @param	string $encoded the base64 encoded string to convert
   * @param string $output an optional file path that the encoded contents 
   * should be written to. if specified, the return value will be NULL
   * @return mixed
   */
  function &base64Decode(&$encoded, $output=NULL) {
    //createRandomFile($dir=NULL, $pre='', $post='', $base='', $delete=FALSE, $mkdir=FALSE)
    if ($base64cmd = SRA_File::findInPath('base64')) {
      $tmp1 = is_file($encoded) ? $encoded : $tmp1 = SRA_File::createRandomFile();
      if (!is_file($encoded)) SRA_File::write($tmp1, $encoded);
      passthru($base64cmd . ' -d -i ' . $tmp1 . ' > ' . ($output ? $output : ($tmp2 = SRA_File::createRandomFile())));
      if (!is_file($encoded)) unlink($tmp1);
      
      if (!$output) {
        $decoded =& SRA_File::toString($tmp2);
        SRA_File::unlink($tmp2);
        return $decoded;
      }
      else {
        return NULL;
      }
    }
    else {
      $decoded = base64_decode(is_file($encoded) ? SRA_File::toString($encoded) : $encoded);
      if ($output) {
        SRA_File::write($output, $decoded);
      }
      else {
        return $decoded;
      }
    }
  }
  // }}}
	
}

// }}}

?>
Return current item: Sierra-php PHP Application Framework