<?php

/**
 * Filesystem lib
 * provides basic filesystem handling functions.
 *
 * @author NoWhereMan <nowhereman@phreaker.net>
 */
class fs_filelister {

	var $_list = array();

	var $_directory = null;

	// constructor
	function __construct($directory = null) {
		if ($directory)
			$this->_directory = $directory;
		$this->_listFiles($this->_directory);
	}

	function _checkFile($directory, $file) {
		if (!is_dir("$directory/$file"))
			array_push($this->_list, $file);
		return 0;
	}

	function _exitingDir($directory, $file) {
	}

	function _listFiles($directory) {

		// Try to open the directory
		if (!file_exists($directory))
			return array();

		if ($dir = opendir($directory)) {
			// Add the files
			while ($file = readdir($dir)) {
				if (!fs_is_directorycomponent($file)) {
					$action = $this->_checkFile($directory, $file);

					// $action == 0: ok, go on
					// $action == 1: recurse
					// $action == 2: exit function

					switch ($action) {
						case (1):
							{
								$this->_listFiles("$directory/$file");
								$this->_exitingDir($directory, $file);
								break;
							}
						case (2):
							{
								return false;
							}
					}
				}
			}

			// Finish off the function
			closedir($dir);
			return true;
		} else
			return false;
	}

	function getList() {
		// $this->_listFiles($this->_directory);
		return $this->_list;
	}

	function count() {
		if (!isset($this->count))
			$this->count = count($this->_list);
		return $this->count;
	}

}

class fs_pathlister extends fs_filelister {

	function _checkFile($directory, $file) {
		$f = "$directory/$file";
		if (!is_dir($f))
			array_push($this->_list, $f);
		else
			return 1;
	}

}

// dir list
function fs_list_dirs($dir) {
	$dh = opendir($dir);
	while (false !== ($filename = readdir($dh))) {
		if (($filename [0] != '.')) {
			// $id = lang_id($filename);
			$files [] = $filename;
		}
	}
	sort($files);
	return $files;
}

/**
 * function fs_mkdir
 *
 * <p>Function from : {@link http://www.php.net/function.mkdir.php}</p>
 *
 * <p>Recursively creates dirs.</p>
 * <p>Returns true on success, else false</p>
 *
 * @param string $path
 *        	Directory or directories to create
 * @param int $mode
 *        	octal mode value; same as UNIX chmod; defaults to 0777 (rwrwrw);
 * @return bool
 *
 * @todo cleanup & check bool return value
 *      
 */
function fs_mkdir($dir, $mode = DIR_PERMISSIONS) {
	if (is_dir($dir) || (@mkdir($dir, $mode))) {
		@chmod($dir, $mode);
		return TRUE;
	}
	if (!fs_mkdir(dirname($dir), $mode))
		return FALSE;
	return (@mkdir($dir, $mode) && @chmod($dir, $mode));
}

/**
 * function fs_delete
 *
 * Deletes a file and recursively deletes dirs, if they're empty
 */
function fs_delete($path) {
	if (file_exists($path)) {

		$fsuccess = unlink($path);
		$dsuccess = true;

		while ($dsuccess) {

			$path = dirname($path);
			$dsuccess = @rmdir($path);
		}

		// unlink can return both 0 and false -__-'
		return ($fsuccess);
	}

	// in our particular implementation
	// you can always delete a non existent file;
	// anyway, we'll return a value != false
	// so that we can anyway track it back

	return 2;
}

/**
 * function fs_recursive_chmod
 *
 * Perform a recursive reset of file permission in the given $path
 * and its subdirectories to 0777
 *
 * @param $fpath dir
 *        	path
 * @return bool
 *
 */
class fs_chmodder extends fs_filelister {

	var $_chmod_dir;

	var $_chmod_file;

	function __construct($directory, $ch_file = FILE_PERMISSIONS, $ch_dir = DIR_PERMISSIONS) {
		$this->_directory = $directory;
		$this->_chmod_file = $ch_file;
		$this->_chmod_dir = $ch_dir;
		parent::__construct();
	}

	function _checkFile($directory, $file) {
		$retval = 0;
		$path = "$directory/$file";
		if (is_dir($path))
			$retval = 1;
		if (!@chmod($path, ($retval ? $this->_chmod_dir : $this->_chmod_file)))
			array_push($this->_list, $path);

		return $retval;
	}

}

function fs_chmod_recursive($fpath = FP_CONTENT) {
	$obj = new fs_chmodder($fpath);
	return $obj->getList();
}

/**
 * recursive deletion
 * deletes all files and directories recursively in the given $path
 *
 * @param $fpath dir
 *        	path
 * @return bool
 */

/*
 * class fs_deleter extends fs_filelister {
 *
 * function fs_deleter($directory) {
 * $this->_directory = $directory;
 * parent::__construct();
 * }
 *
 * function _checkFile($directory, $file) {
 *
 * $path = "$directory/$file";
 *
 * /*
 * open dir handle prevents directory deletion of php5 (and probably win)
 * thanks to cimangi <cimangi (at) yahoo (dot) it> for noticing and
 * giving a possible solution:
 *
 * filenames are cached and then deleted
 * //
 *
 * if ( is_dir($path) ) {
 * return 1;
 * } elseif ( file_exists($path) ) {
 * array_push($this->_list, $path);
 * return 0;
 * } else {
 * return 2;
 * }
 *
 * }
 *
 * }
 *
 */

/*
 * open dir handle prevents directory deletion of php5 (and probably win)
 * thanks to cimangi <cimangi (at) yahoo (dot) it> for noticing and
 * giving a possible solution;
 *
 * paths are now cached and then deleted
 */
function fs_delete_recursive($path) {
	if (file_exists($path)) {

		$obj = new fs_pathlister($path);
		$list = ($obj->getList());

		unset($obj);

		$elem = null;
		while ($elem = array_pop($list)) {
			$elem;
			fs_delete($elem);
		}
	}

	return true;
}

function fs_copy($source, $dest) {
	if ($contents = io_load_file($source)) {
		return io_write_file($dest, $contents);
	}
	return false;
}

/**
 * Checks if the file with the given name is a directory component ('.' or '..').
 *
 * @param string $filename
 *        	the file name
 * @return boolean <code>true</code> if the file is a directory component; otherwise <code>false</code>
 */
function fs_is_directorycomponent($filename) {
	return $filename === '.' || $filename === '..';
}

/**
 * Checks if the file with the given name is a hidden file (i.e., starts with a '.').
 *
 * @param string $filename
 *        	the file name
 * @return boolean <code>true</code> if the file is a hidden file; otherwise <code>false</code>
 */
function fs_is_hidden_file($filename) {
	return strlen($filename) > 0 && substr($filename, 0, 1) === '.';
}