de4fe3838dff783e5a6b81700095396cce9b020a
[atutor.git] / docs / include / classes / XML / XML_HTMLSax / System.php
1 <?php\r
2 /**\r
3  * File/Directory manipulation\r
4  *\r
5  * PHP versions 4 and 5\r
6  *\r
7  * LICENSE: This source file is subject to version 3.0 of the PHP license\r
8  * that is available through the world-wide-web at the following URI:\r
9  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of\r
10  * the PHP License and are unable to obtain it through the web, please\r
11  * send a note to license@php.net so we can mail you a copy immediately.\r
12  *\r
13  * @category   pear\r
14  * @package    System\r
15  * @author     Tomas V.V.Cox <cox@idecnet.com>\r
16  * @copyright  1997-2008 The PHP Group\r
17  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0\r
18  * @version    CVS: $Id$\r
19  * @link       http://pear.php.net/package/PEAR\r
20  * @since      File available since Release 0.1\r
21  */\r
22 \r
23 /**\r
24  * base class\r
25  */\r
26 require_once 'PEAR.php';\r
27 require_once 'Console/Getopt.php';\r
28 \r
29 $GLOBALS['_System_temp_files'] = array();\r
30 \r
31 /**\r
32 * System offers cross plattform compatible system functions\r
33 *\r
34 * Static functions for different operations. Should work under\r
35 * Unix and Windows. The names and usage has been taken from its respectively\r
36 * GNU commands. The functions will return (bool) false on error and will\r
37 * trigger the error with the PHP trigger_error() function (you can silence\r
38 * the error by prefixing a '@' sign after the function call).\r
39 *\r
40 * Documentation on this class you can find in:\r
41 * http://pear.php.net/manual/\r
42 *\r
43 * Example usage:\r
44 * if (!@System::rm('-r file1 dir1')) {\r
45 *    print "could not delete file1 or dir1";\r
46 * }\r
47 *\r
48 * In case you need to to pass file names with spaces,\r
49 * pass the params as an array:\r
50 *\r
51 * System::rm(array('-r', $file1, $dir1));\r
52 *\r
53 * @category   pear\r
54 * @package    System\r
55 * @author     Tomas V.V. Cox <cox@idecnet.com>\r
56 * @copyright  1997-2008 The PHP Group\r
57 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0\r
58 * @version    Release: 1.4.4\r
59 * @link       http://pear.php.net/package/PEAR\r
60 * @since      Class available since Release 0.1\r
61 */\r
62 class System\r
63 {\r
64     /**\r
65     * returns the commandline arguments of a function\r
66     *\r
67     * @param    string  $argv           the commandline\r
68     * @param    string  $short_options  the allowed option short-tags\r
69     * @param    string  $long_options   the allowed option long-tags\r
70     * @return   array   the given options and there values\r
71     * @access private\r
72     */\r
73     function _parseArgs($argv, $short_options, $long_options = null)\r
74     {\r
75         if (!is_array($argv) && $argv !== null) {\r
76             $argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY);\r
77         }\r
78         return Console_Getopt::getopt2($argv, $short_options);\r
79     }\r
80 \r
81     /**\r
82     * Output errors with PHP trigger_error(). You can silence the errors\r
83     * with prefixing a "@" sign to the function call: @System::mkdir(..);\r
84     *\r
85     * @param mixed $error a PEAR error or a string with the error message\r
86     * @return bool false\r
87     * @access private\r
88     */\r
89     function raiseError($error)\r
90     {\r
91         if (PEAR::isError($error)) {\r
92             $error = $error->getMessage();\r
93         }\r
94         trigger_error($error, E_USER_WARNING);\r
95         return false;\r
96     }\r
97 \r
98     /**\r
99     * Creates a nested array representing the structure of a directory\r
100     *\r
101     * System::_dirToStruct('dir1', 0) =>\r
102     *   Array\r
103     *    (\r
104     *    [dirs] => Array\r
105     *        (\r
106     *            [0] => dir1\r
107     *        )\r
108     *\r
109     *    [files] => Array\r
110     *        (\r
111     *            [0] => dir1/file2\r
112     *            [1] => dir1/file3\r
113     *        )\r
114     *    )\r
115     * @param    string  $sPath      Name of the directory\r
116     * @param    integer $maxinst    max. deep of the lookup\r
117     * @param    integer $aktinst    starting deep of the lookup\r
118     * @return   array   the structure of the dir\r
119     * @access   private\r
120     */\r
121 \r
122     function _dirToStruct($sPath, $maxinst, $aktinst = 0)\r
123     {\r
124         $struct = array('dirs' => array(), 'files' => array());\r
125         if (($dir = @opendir($sPath)) === false) {\r
126             System::raiseError("Could not open dir $sPath");\r
127             return $struct; // XXX could not open error\r
128         }\r
129         $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?\r
130         $list = array();\r
131         while (false !== ($file = readdir($dir))) {\r
132             if ($file != '.' && $file != '..') {\r
133                 $list[] = $file;\r
134             }\r
135         }\r
136         closedir($dir);\r
137         sort($list);\r
138         if ($aktinst < $maxinst || $maxinst == 0) {\r
139             foreach($list as $val) {\r
140                 $path = $sPath . DIRECTORY_SEPARATOR . $val;\r
141                 if (is_dir($path) && !is_link($path)) {\r
142                     $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1);\r
143                     $struct = array_merge_recursive($tmp, $struct);\r
144                 } else {\r
145                     $struct['files'][] = $path;\r
146                 }\r
147             }\r
148         }\r
149         return $struct;\r
150     }\r
151 \r
152     /**\r
153     * Creates a nested array representing the structure of a directory and files\r
154     *\r
155     * @param    array $files Array listing files and dirs\r
156     * @return   array\r
157     * @see System::_dirToStruct()\r
158     */\r
159     function _multipleToStruct($files)\r
160     {\r
161         $struct = array('dirs' => array(), 'files' => array());\r
162         settype($files, 'array');\r
163         foreach ($files as $file) {\r
164             if (is_dir($file) && !is_link($file)) {\r
165                 $tmp = System::_dirToStruct($file, 0);\r
166                 $struct = array_merge_recursive($tmp, $struct);\r
167             } else {\r
168                 $struct['files'][] = $file;\r
169             }\r
170         }\r
171         return $struct;\r
172     }\r
173 \r
174     /**\r
175     * The rm command for removing files.\r
176     * Supports multiple files and dirs and also recursive deletes\r
177     *\r
178     * @param    string  $args   the arguments for rm\r
179     * @return   mixed   PEAR_Error or true for success\r
180     * @access   public\r
181     */\r
182     function rm($args)\r
183     {\r
184         $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-)\r
185         if (PEAR::isError($opts)) {\r
186             return System::raiseError($opts);\r
187         }\r
188         foreach($opts[0] as $opt) {\r
189             if ($opt[0] == 'r') {\r
190                 $do_recursive = true;\r
191             }\r
192         }\r
193         $ret = true;\r
194         if (isset($do_recursive)) {\r
195             $struct = System::_multipleToStruct($opts[1]);\r
196             foreach($struct['files'] as $file) {\r
197                 if (!@unlink($file)) {\r
198                     $ret = false;\r
199                 }\r
200             }\r
201             foreach($struct['dirs'] as $dir) {\r
202                 if (!@rmdir($dir)) {\r
203                     $ret = false;\r
204                 }\r
205             }\r
206         } else {\r
207             foreach ($opts[1] as $file) {\r
208                 $delete = (is_dir($file)) ? 'rmdir' : 'unlink';\r
209                 if (!@$delete($file)) {\r
210                     $ret = false;\r
211                 }\r
212             }\r
213         }\r
214         return $ret;\r
215     }\r
216 \r
217     /**\r
218     * Make directories.\r
219     *\r
220     * The -p option will create parent directories\r
221     * @param    string  $args    the name of the director(y|ies) to create\r
222     * @return   bool    True for success\r
223     * @access   public\r
224     */\r
225     function mkDir($args)\r
226     {\r
227         $opts = System::_parseArgs($args, 'pm:');\r
228         if (PEAR::isError($opts)) {\r
229             return System::raiseError($opts);\r
230         }\r
231         $mode = 0777; // default mode\r
232         foreach($opts[0] as $opt) {\r
233             if ($opt[0] == 'p') {\r
234                 $create_parents = true;\r
235             } elseif($opt[0] == 'm') {\r
236                 // if the mode is clearly an octal number (starts with 0)\r
237                 // convert it to decimal\r
238                 if (strlen($opt[1]) && $opt[1]{0} == '0') {\r
239                     $opt[1] = octdec($opt[1]);\r
240                 } else {\r
241                     // convert to int\r
242                     $opt[1] += 0;\r
243                 }\r
244                 $mode = $opt[1];\r
245             }\r
246         }\r
247         $ret = true;\r
248         if (isset($create_parents)) {\r
249             foreach($opts[1] as $dir) {\r
250                 $dirstack = array();\r
251                 while (!@is_dir($dir) && $dir != DIRECTORY_SEPARATOR) {\r
252                     array_unshift($dirstack, $dir);\r
253                     $dir = dirname($dir);\r
254                 }\r
255                 while ($newdir = array_shift($dirstack)) {\r
256                     if (!is_writeable(dirname($newdir))) {\r
257                         $ret = false;\r
258                         break;\r
259                     }\r
260                     if (!mkdir($newdir, $mode)) {\r
261                         $ret = false;\r
262                     }\r
263                 }\r
264             }\r
265         } else {\r
266             foreach($opts[1] as $dir) {\r
267                 if (!@is_dir($dir) && !mkdir($dir, $mode)) {\r
268                     $ret = false;\r
269                 }\r
270             }\r
271         }\r
272         return $ret;\r
273     }\r
274 \r
275     /**\r
276     * Concatenate files\r
277     *\r
278     * Usage:\r
279     * 1) $var = System::cat('sample.txt test.txt');\r
280     * 2) System::cat('sample.txt test.txt > final.txt');\r
281     * 3) System::cat('sample.txt test.txt >> final.txt');\r
282     *\r
283     * Note: as the class use fopen, urls should work also (test that)\r
284     *\r
285     * @param    string  $args   the arguments\r
286     * @return   boolean true on success\r
287     * @access   public\r
288     */\r
289     function &cat($args)\r
290     {\r
291         $ret = null;\r
292         $files = array();\r
293         if (!is_array($args)) {\r
294             $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);\r
295         }\r
296         for($i=0; $i < count($args); $i++) {\r
297             if ($args[$i] == '>') {\r
298                 $mode = 'wb';\r
299                 $outputfile = $args[$i+1];\r
300                 break;\r
301             } elseif ($args[$i] == '>>') {\r
302                 $mode = 'ab+';\r
303                 $outputfile = $args[$i+1];\r
304                 break;\r
305             } else {\r
306                 $files[] = $args[$i];\r
307             }\r
308         }\r
309         if (isset($mode)) {\r
310             if (!$outputfd = fopen($outputfile, $mode)) {\r
311                 $err = System::raiseError("Could not open $outputfile");\r
312                 return $err;\r
313             }\r
314             $ret = true;\r
315         }\r
316         foreach ($files as $file) {\r
317             if (!$fd = fopen($file, 'r')) {\r
318                 System::raiseError("Could not open $file");\r
319                 continue;\r
320             }\r
321             while ($cont = fread($fd, 2048)) {\r
322                 if (isset($outputfd)) {\r
323                     fwrite($outputfd, $cont);\r
324                 } else {\r
325                     $ret .= $cont;\r
326                 }\r
327             }\r
328             fclose($fd);\r
329         }\r
330         if (@is_resource($outputfd)) {\r
331             fclose($outputfd);\r
332         }\r
333         return $ret;\r
334     }\r
335 \r
336     /**\r
337     * Creates temporary files or directories. This function will remove\r
338     * the created files when the scripts finish its execution.\r
339     *\r
340     * Usage:\r
341     *   1) $tempfile = System::mktemp("prefix");\r
342     *   2) $tempdir  = System::mktemp("-d prefix");\r
343     *   3) $tempfile = System::mktemp();\r
344     *   4) $tempfile = System::mktemp("-t /var/tmp prefix");\r
345     *\r
346     * prefix -> The string that will be prepended to the temp name\r
347     *           (defaults to "tmp").\r
348     * -d     -> A temporary dir will be created instead of a file.\r
349     * -t     -> The target dir where the temporary (file|dir) will be created. If\r
350     *           this param is missing by default the env vars TMP on Windows or\r
351     *           TMPDIR in Unix will be used. If these vars are also missing\r
352     *           c:\windows\temp or /tmp will be used.\r
353     *\r
354     * @param   string  $args  The arguments\r
355     * @return  mixed   the full path of the created (file|dir) or false\r
356     * @see System::tmpdir()\r
357     * @access  public\r
358     */\r
359     function mktemp($args = null)\r
360     {\r
361         static $first_time = true;\r
362         $opts = System::_parseArgs($args, 't:d');\r
363         if (PEAR::isError($opts)) {\r
364             return System::raiseError($opts);\r
365         }\r
366         foreach($opts[0] as $opt) {\r
367             if($opt[0] == 'd') {\r
368                 $tmp_is_dir = true;\r
369             } elseif($opt[0] == 't') {\r
370                 $tmpdir = $opt[1];\r
371             }\r
372         }\r
373         $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';\r
374         if (!isset($tmpdir)) {\r
375             $tmpdir = System::tmpdir();\r
376         }\r
377         if (!System::mkDir(array('-p', $tmpdir))) {\r
378             return false;\r
379         }\r
380         $tmp = tempnam($tmpdir, $prefix);\r
381         if (isset($tmp_is_dir)) {\r
382             unlink($tmp); // be careful possible race condition here\r
383             if (!mkdir($tmp, 0700)) {\r
384                 return System::raiseError("Unable to create temporary directory $tmpdir");\r
385             }\r
386         }\r
387         $GLOBALS['_System_temp_files'][] = $tmp;\r
388         if ($first_time) {\r
389             PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));\r
390             $first_time = false;\r
391         }\r
392         return $tmp;\r
393     }\r
394 \r
395     /**\r
396     * Remove temporary files created my mkTemp. This function is executed\r
397     * at script shutdown time\r
398     *\r
399     * @access private\r
400     */\r
401     function _removeTmpFiles()\r
402     {\r
403         if (count($GLOBALS['_System_temp_files'])) {\r
404             $delete = $GLOBALS['_System_temp_files'];\r
405             array_unshift($delete, '-r');\r
406             System::rm($delete);\r
407             $GLOBALS['_System_temp_files'] = array();\r
408         }\r
409     }\r
410 \r
411     /**\r
412     * Get the path of the temporal directory set in the system\r
413     * by looking in its environments variables.\r
414     * Note: php.ini-recommended removes the "E" from the variables_order setting,\r
415     * making unavaible the $_ENV array, that s why we do tests with _ENV\r
416     *\r
417     * @return string The temporal directory on the system\r
418     */\r
419     function tmpdir()\r
420     {\r
421         if (OS_WINDOWS) {\r
422             if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {\r
423                 return $var;\r
424             }\r
425             if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {\r
426                 return $var;\r
427             }\r
428             if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {\r
429                 return $var;\r
430             }\r
431             return getenv('SystemRoot') . '\temp';\r
432         }\r
433         if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {\r
434             return $var;\r
435         }\r
436         return '/tmp';\r
437     }\r
438 \r
439     /**\r
440     * The "which" command (show the full path of a command)\r
441     *\r
442     * @param string $program The command to search for\r
443     * @param mixed  $fallback Value to return if $program is not found\r
444     *\r
445     * @return mixed A string with the full path or false if not found\r
446     * @author Stig Bakken <ssb@php.net>\r
447     */\r
448     function which($program, $fallback = false)\r
449     {\r
450         // avaible since 4.3.0RC2\r
451         if (defined('PATH_SEPARATOR')) {\r
452             $path_delim = PATH_SEPARATOR;\r
453         } else {\r
454             $path_delim = OS_WINDOWS ? ';' : ':';\r
455         }\r
456         // full path given\r
457         if (basename($program) != $program) {\r
458             $path_elements[] = dirname($program);\r
459             $program = basename($program);\r
460         } else {\r
461             // Honor safe mode\r
462             if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {\r
463                 $path = getenv('PATH');\r
464                 if (!$path) {\r
465                     $path = getenv('Path'); // some OSes are just stupid enough to do this\r
466                 }\r
467             }\r
468             $path_elements = explode($path_delim, $path);\r
469         }\r
470 \r
471         if (OS_WINDOWS) {\r
472             $exe_suffixes = getenv('PATHEXT')\r
473                                 ? explode($path_delim, getenv('PATHEXT'))\r
474                                 : array('.exe','.bat','.cmd','.com');\r
475             // allow passing a command.exe param\r
476             if (strpos($program, '.') !== false) {\r
477                 array_unshift($exe_suffixes, '');\r
478             }\r
479             // is_executable() is not available on windows for PHP4\r
480             $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';\r
481         } else {\r
482             $exe_suffixes = array('');\r
483             $pear_is_executable = 'is_executable';\r
484         }\r
485 \r
486         foreach ($exe_suffixes as $suff) {\r
487             foreach ($path_elements as $dir) {\r
488                 $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;\r
489                 if ($pear_is_executable($file)) {\r
490                     return $file;\r
491                 }\r
492             }\r
493         }\r
494         return $fallback;\r
495     }\r
496 \r
497     /**\r
498     * The "find" command\r
499     *\r
500     * Usage:\r
501     *\r
502     * System::find($dir);\r
503     * System::find("$dir -type d");\r
504     * System::find("$dir -type f");\r
505     * System::find("$dir -name *.php");\r
506     * System::find("$dir -name *.php -name *.htm*");\r
507     * System::find("$dir -maxdepth 1");\r
508     *\r
509     * Params implmented:\r
510     * $dir            -> Start the search at this directory\r
511     * -type d         -> return only directories\r
512     * -type f         -> return only files\r
513     * -maxdepth <n>   -> max depth of recursion\r
514     * -name <pattern> -> search pattern (bash style). Multiple -name param allowed\r
515     *\r
516     * @param  mixed Either array or string with the command line\r
517     * @return array Array of found files\r
518     *\r
519     */\r
520     function find($args)\r
521     {\r
522         if (!is_array($args)) {\r
523             $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);\r
524         }\r
525         $dir = array_shift($args);\r
526         $patterns = array();\r
527         $depth = 0;\r
528         $do_files = $do_dirs = true;\r
529         for ($i = 0; $i < count($args); $i++) {\r
530             switch ($args[$i]) {\r
531                 case '-type':\r
532                     if (in_array($args[$i+1], array('d', 'f'))) {\r
533                         if ($args[$i+1] == 'd') {\r
534                             $do_files = false;\r
535                         } else {\r
536                             $do_dirs = false;\r
537                         }\r
538                     }\r
539                     $i++;\r
540                     break;\r
541                 case '-name':\r
542                     if (OS_WINDOWS) {\r
543                         if ($args[$i+1]{0} == '\\') {\r
544                             // prepend drive\r
545                             $args[$i+1] = addslashes(substr(getcwd(), 0, 2) . $args[$i + 1]);\r
546                         }\r
547                     }\r
548                     $patterns[] = "(" . preg_replace(array('/\./', '/\*/'),\r
549                                                      array('\.', '.*', ),\r
550                                                      $args[$i+1])\r
551                                       . ")";\r
552                     $i++;\r
553                     break;\r
554                 case '-maxdepth':\r
555                     $depth = $args[$i+1];\r
556                     break;\r
557             }\r
558         }\r
559         $path = System::_dirToStruct($dir, $depth);\r
560         if ($do_files && $do_dirs) {\r
561             $files = array_merge($path['files'], $path['dirs']);\r
562         } elseif ($do_dirs) {\r
563             $files = $path['dirs'];\r
564         } else {\r
565             $files = $path['files'];\r
566         }\r
567         if (count($patterns)) {\r
568             $patterns = implode('|', $patterns);\r
569             $ret = array();\r
570             for ($i = 0; $i < count($files); $i++) {\r
571                 if (preg_match("#^$patterns\$#", $files[$i])) {\r
572                     $ret[] = $files[$i];\r
573                 }\r
574             }\r
575             return $ret;\r
576         }\r
577         return $files;\r
578     }\r
579 }\r
580 ?>\r