Updates to Tomato RAF including NGINX && PHP
[tomato.git] / release / src / router / php / ext / standard / dir.c
blobef28e9feafbc657d15a2be6f6869d260bb53e8e3
1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Thies C. Arntzen <thies@thieso.net> |
16 +----------------------------------------------------------------------+
19 /* $Id$ */
21 /* {{{ includes/startup/misc */
23 #include "php.h"
24 #include "fopen_wrappers.h"
25 #include "file.h"
26 #include "php_dir.h"
27 #include "php_string.h"
28 #include "php_scandir.h"
29 #include "basic_functions.h"
31 #ifdef HAVE_DIRENT_H
32 #include <dirent.h>
33 #endif
35 #if HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
39 #include <errno.h>
41 #ifdef PHP_WIN32
42 #include "win32/readdir.h"
43 #endif
46 #ifdef HAVE_GLOB
47 #ifndef PHP_WIN32
48 #include <glob.h>
49 #else
50 #include "win32/glob.h"
51 #endif
52 #endif
54 typedef struct {
55 int default_dir;
56 } php_dir_globals;
58 #ifdef ZTS
59 #define DIRG(v) TSRMG(dir_globals_id, php_dir_globals *, v)
60 int dir_globals_id;
61 #else
62 #define DIRG(v) (dir_globals.v)
63 php_dir_globals dir_globals;
64 #endif
66 #if 0
67 typedef struct {
68 int id;
69 DIR *dir;
70 } php_dir;
72 static int le_dirp;
73 #endif
75 static zend_class_entry *dir_class_entry_ptr;
77 #define FETCH_DIRP() \
78 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &id) == FAILURE) { \
79 return; \
80 } \
81 if (ZEND_NUM_ARGS() == 0) { \
82 myself = getThis(); \
83 if (myself) { \
84 if (zend_hash_find(Z_OBJPROP_P(myself), "handle", sizeof("handle"), (void **)&tmp) == FAILURE) { \
85 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find my handle property"); \
86 RETURN_FALSE; \
87 } \
88 ZEND_FETCH_RESOURCE(dirp, php_stream *, tmp, -1, "Directory", php_file_le_stream()); \
89 } else { \
90 ZEND_FETCH_RESOURCE(dirp, php_stream *, 0, DIRG(default_dir), "Directory", php_file_le_stream()); \
91 } \
92 } else { \
93 dirp = (php_stream *) zend_fetch_resource(&id TSRMLS_CC, -1, "Directory", NULL, 1, php_file_le_stream()); \
94 if (!dirp) \
95 RETURN_FALSE; \
98 /* {{{ arginfo */
99 ZEND_BEGIN_ARG_INFO_EX(arginfo_dir, 0, 0, 0)
100 ZEND_ARG_INFO(0, dir_handle)
101 ZEND_END_ARG_INFO()
102 /* }}} */
104 static const zend_function_entry php_dir_class_functions[] = {
105 PHP_FALIAS(close, closedir, arginfo_dir)
106 PHP_FALIAS(rewind, rewinddir, arginfo_dir)
107 PHP_NAMED_FE(read, php_if_readdir, arginfo_dir)
108 {NULL, NULL, NULL}
112 static void php_set_default_dir(int id TSRMLS_DC)
114 if (DIRG(default_dir)!=-1) {
115 zend_list_delete(DIRG(default_dir));
118 if (id != -1) {
119 zend_list_addref(id);
122 DIRG(default_dir) = id;
125 PHP_RINIT_FUNCTION(dir)
127 DIRG(default_dir) = -1;
128 return SUCCESS;
131 PHP_MINIT_FUNCTION(dir)
133 static char dirsep_str[2], pathsep_str[2];
134 zend_class_entry dir_class_entry;
136 INIT_CLASS_ENTRY(dir_class_entry, "Directory", php_dir_class_functions);
137 dir_class_entry_ptr = zend_register_internal_class(&dir_class_entry TSRMLS_CC);
139 #ifdef ZTS
140 ts_allocate_id(&dir_globals_id, sizeof(php_dir_globals), NULL, NULL);
141 #endif
143 dirsep_str[0] = DEFAULT_SLASH;
144 dirsep_str[1] = '\0';
145 REGISTER_STRING_CONSTANT("DIRECTORY_SEPARATOR", dirsep_str, CONST_CS|CONST_PERSISTENT);
147 pathsep_str[0] = ZEND_PATHS_SEPARATOR;
148 pathsep_str[1] = '\0';
149 REGISTER_STRING_CONSTANT("PATH_SEPARATOR", pathsep_str, CONST_CS|CONST_PERSISTENT);
151 REGISTER_LONG_CONSTANT("SCANDIR_SORT_ASCENDING", PHP_SCANDIR_SORT_ASCENDING, CONST_CS | CONST_PERSISTENT);
152 REGISTER_LONG_CONSTANT("SCANDIR_SORT_DESCENDING", PHP_SCANDIR_SORT_DESCENDING, CONST_CS | CONST_PERSISTENT);
153 REGISTER_LONG_CONSTANT("SCANDIR_SORT_NONE", PHP_SCANDIR_SORT_NONE, CONST_CS | CONST_PERSISTENT);
155 #ifdef HAVE_GLOB
157 #ifdef GLOB_BRACE
158 REGISTER_LONG_CONSTANT("GLOB_BRACE", GLOB_BRACE, CONST_CS | CONST_PERSISTENT);
159 #else
160 # define GLOB_BRACE 0
161 #endif
163 #ifdef GLOB_MARK
164 REGISTER_LONG_CONSTANT("GLOB_MARK", GLOB_MARK, CONST_CS | CONST_PERSISTENT);
165 #else
166 # define GLOB_MARK 0
167 #endif
169 #ifdef GLOB_NOSORT
170 REGISTER_LONG_CONSTANT("GLOB_NOSORT", GLOB_NOSORT, CONST_CS | CONST_PERSISTENT);
171 #else
172 # define GLOB_NOSORT 0
173 #endif
175 #ifdef GLOB_NOCHECK
176 REGISTER_LONG_CONSTANT("GLOB_NOCHECK", GLOB_NOCHECK, CONST_CS | CONST_PERSISTENT);
177 #else
178 # define GLOB_NOCHECK 0
179 #endif
181 #ifdef GLOB_NOESCAPE
182 REGISTER_LONG_CONSTANT("GLOB_NOESCAPE", GLOB_NOESCAPE, CONST_CS | CONST_PERSISTENT);
183 #else
184 # define GLOB_NOESCAPE 0
185 #endif
187 #ifdef GLOB_ERR
188 REGISTER_LONG_CONSTANT("GLOB_ERR", GLOB_ERR, CONST_CS | CONST_PERSISTENT);
189 #else
190 # define GLOB_ERR 0
191 #endif
193 #ifndef GLOB_ONLYDIR
194 # define GLOB_ONLYDIR (1<<30)
195 # define GLOB_EMULATE_ONLYDIR
196 # define GLOB_FLAGMASK (~GLOB_ONLYDIR)
197 #else
198 # define GLOB_FLAGMASK (~0)
199 #endif
201 /* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */
202 #define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR)
204 REGISTER_LONG_CONSTANT("GLOB_ONLYDIR", GLOB_ONLYDIR, CONST_CS | CONST_PERSISTENT);
205 REGISTER_LONG_CONSTANT("GLOB_AVAILABLE_FLAGS", GLOB_AVAILABLE_FLAGS, CONST_CS | CONST_PERSISTENT);
207 #endif /* HAVE_GLOB */
209 return SUCCESS;
211 /* }}} */
213 /* {{{ internal functions */
214 static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
216 char *dirname;
217 int dir_len;
218 zval *zcontext = NULL;
219 php_stream_context *context = NULL;
220 php_stream *dirp;
222 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &dirname, &dir_len, &zcontext) == FAILURE) {
223 RETURN_NULL();
226 context = php_stream_context_from_zval(zcontext, 0);
228 dirp = php_stream_opendir(dirname, REPORT_ERRORS, context);
230 if (dirp == NULL) {
231 RETURN_FALSE;
234 dirp->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
236 php_set_default_dir(dirp->rsrc_id TSRMLS_CC);
238 if (createobject) {
239 object_init_ex(return_value, dir_class_entry_ptr);
240 add_property_stringl(return_value, "path", dirname, dir_len, 1);
241 add_property_resource(return_value, "handle", dirp->rsrc_id);
242 php_stream_auto_cleanup(dirp); /* so we don't get warnings under debug */
243 } else {
244 php_stream_to_zval(dirp, return_value);
247 /* }}} */
249 /* {{{ proto mixed opendir(string path[, resource context])
250 Open a directory and return a dir_handle */
251 PHP_FUNCTION(opendir)
253 _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
255 /* }}} */
257 /* {{{ proto object dir(string directory[, resource context])
258 Directory class with properties, handle and class and methods read, rewind and close */
259 PHP_FUNCTION(getdir)
261 _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
263 /* }}} */
265 /* {{{ proto void closedir([resource dir_handle])
266 Close directory connection identified by the dir_handle */
267 PHP_FUNCTION(closedir)
269 zval *id = NULL, **tmp, *myself;
270 php_stream *dirp;
271 int rsrc_id;
273 FETCH_DIRP();
275 if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
276 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d is not a valid Directory resource", dirp->rsrc_id);
277 RETURN_FALSE;
280 rsrc_id = dirp->rsrc_id;
281 zend_list_delete(dirp->rsrc_id);
283 if (rsrc_id == DIRG(default_dir)) {
284 php_set_default_dir(-1 TSRMLS_CC);
287 /* }}} */
289 #if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
290 /* {{{ proto bool chroot(string directory)
291 Change root directory */
292 PHP_FUNCTION(chroot)
294 char *str;
295 int ret, str_len;
297 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
298 RETURN_FALSE;
301 ret = chroot(str);
302 if (ret != 0) {
303 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
304 RETURN_FALSE;
307 php_clear_stat_cache(1, NULL, 0 TSRMLS_CC);
309 ret = chdir("/");
311 if (ret != 0) {
312 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
313 RETURN_FALSE;
316 RETURN_TRUE;
318 /* }}} */
319 #endif
321 /* {{{ proto bool chdir(string directory)
322 Change the current directory */
323 PHP_FUNCTION(chdir)
325 char *str;
326 int ret, str_len;
328 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &str, &str_len) == FAILURE) {
329 RETURN_FALSE;
332 if (php_check_open_basedir(str TSRMLS_CC)) {
333 RETURN_FALSE;
335 ret = VCWD_CHDIR(str);
337 if (ret != 0) {
338 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s (errno %d)", strerror(errno), errno);
339 RETURN_FALSE;
342 if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) {
343 efree(BG(CurrentStatFile));
344 BG(CurrentStatFile) = NULL;
346 if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) {
347 efree(BG(CurrentLStatFile));
348 BG(CurrentLStatFile) = NULL;
351 RETURN_TRUE;
353 /* }}} */
355 /* {{{ proto mixed getcwd(void)
356 Gets the current directory */
357 PHP_FUNCTION(getcwd)
359 char path[MAXPATHLEN];
360 char *ret=NULL;
362 if (zend_parse_parameters_none() == FAILURE) {
363 return;
366 #if HAVE_GETCWD
367 ret = VCWD_GETCWD(path, MAXPATHLEN);
368 #elif HAVE_GETWD
369 ret = VCWD_GETWD(path);
370 #endif
372 if (ret) {
373 RETURN_STRING(path, 1);
374 } else {
375 RETURN_FALSE;
378 /* }}} */
380 /* {{{ proto void rewinddir([resource dir_handle])
381 Rewind dir_handle back to the start */
382 PHP_FUNCTION(rewinddir)
384 zval *id = NULL, **tmp, *myself;
385 php_stream *dirp;
387 FETCH_DIRP();
389 if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
390 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d is not a valid Directory resource", dirp->rsrc_id);
391 RETURN_FALSE;
394 php_stream_rewinddir(dirp);
396 /* }}} */
398 /* {{{ proto string readdir([resource dir_handle])
399 Read directory entry from dir_handle */
400 PHP_NAMED_FUNCTION(php_if_readdir)
402 zval *id = NULL, **tmp, *myself;
403 php_stream *dirp;
404 php_stream_dirent entry;
406 FETCH_DIRP();
408 if (!(dirp->flags & PHP_STREAM_FLAG_IS_DIR)) {
409 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d is not a valid Directory resource", dirp->rsrc_id);
410 RETURN_FALSE;
413 if (php_stream_readdir(dirp, &entry)) {
414 RETURN_STRINGL(entry.d_name, strlen(entry.d_name), 1);
416 RETURN_FALSE;
418 /* }}} */
420 #ifdef HAVE_GLOB
421 /* {{{ proto array glob(string pattern [, int flags])
422 Find pathnames matching a pattern */
423 PHP_FUNCTION(glob)
425 int cwd_skip = 0;
426 #ifdef ZTS
427 char cwd[MAXPATHLEN];
428 char work_pattern[MAXPATHLEN];
429 char *result;
430 #endif
431 char *pattern = NULL;
432 int pattern_len;
433 long flags = 0;
434 glob_t globbuf;
435 int n;
436 int ret;
437 zend_bool basedir_limit = 0;
439 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l", &pattern, &pattern_len, &flags) == FAILURE) {
440 return;
443 if (pattern_len >= MAXPATHLEN) {
444 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
445 RETURN_FALSE;
448 if ((GLOB_AVAILABLE_FLAGS & flags) != flags) {
449 php_error_docref(NULL TSRMLS_CC, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform");
450 RETURN_FALSE;
453 #ifdef ZTS
454 if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) {
455 result = VCWD_GETCWD(cwd, MAXPATHLEN);
456 if (!result) {
457 cwd[0] = '\0';
459 #ifdef PHP_WIN32
460 if (IS_SLASH(*pattern)) {
461 cwd[2] = '\0';
463 #endif
464 cwd_skip = strlen(cwd)+1;
466 snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern);
467 pattern = work_pattern;
469 #endif
472 memset(&globbuf, 0, sizeof(glob_t));
473 globbuf.gl_offs = 0;
474 if (0 != (ret = glob(pattern, flags & GLOB_FLAGMASK, NULL, &globbuf))) {
475 #ifdef GLOB_NOMATCH
476 if (GLOB_NOMATCH == ret) {
477 /* Some glob implementation simply return no data if no matches
478 were found, others return the GLOB_NOMATCH error code.
479 We don't want to treat GLOB_NOMATCH as an error condition
480 so that PHP glob() behaves the same on both types of
481 implementations and so that 'foreach (glob() as ...'
482 can be used for simple glob() calls without further error
483 checking.
485 goto no_results;
487 #endif
488 RETURN_FALSE;
491 /* now catch the FreeBSD style of "no matches" */
492 if (!globbuf.gl_pathc || !globbuf.gl_pathv) {
493 no_results:
494 if (PG(open_basedir) && *PG(open_basedir)) {
495 struct stat s;
497 if (0 != VCWD_STAT(pattern, &s) || S_IFDIR != (s.st_mode & S_IFMT)) {
498 RETURN_FALSE;
501 array_init(return_value);
502 return;
505 array_init(return_value);
506 for (n = 0; n < globbuf.gl_pathc; n++) {
507 if (PG(open_basedir) && *PG(open_basedir)) {
508 if (php_check_open_basedir_ex(globbuf.gl_pathv[n], 0 TSRMLS_CC)) {
509 basedir_limit = 1;
510 continue;
513 /* we need to do this everytime since GLOB_ONLYDIR does not guarantee that
514 * all directories will be filtered. GNU libc documentation states the
515 * following:
516 * If the information about the type of the file is easily available
517 * non-directories will be rejected but no extra work will be done to
518 * determine the information for each file. I.e., the caller must still be
519 * able to filter directories out.
521 if (flags & GLOB_ONLYDIR) {
522 struct stat s;
524 if (0 != VCWD_STAT(globbuf.gl_pathv[n], &s)) {
525 continue;
528 if (S_IFDIR != (s.st_mode & S_IFMT)) {
529 continue;
532 add_next_index_string(return_value, globbuf.gl_pathv[n]+cwd_skip, 1);
535 globfree(&globbuf);
537 if (basedir_limit && !zend_hash_num_elements(Z_ARRVAL_P(return_value))) {
538 zval_dtor(return_value);
539 RETURN_FALSE;
542 /* }}} */
543 #endif
545 /* {{{ proto array scandir(string dir [, int sorting_order [, resource context]])
546 List files & directories inside the specified path */
547 PHP_FUNCTION(scandir)
549 char *dirn;
550 int dirn_len;
551 long flags = 0;
552 char **namelist;
553 int n, i;
554 zval *zcontext = NULL;
555 php_stream_context *context = NULL;
557 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|lr", &dirn, &dirn_len, &flags, &zcontext) == FAILURE) {
558 return;
561 if (dirn_len < 1) {
562 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Directory name cannot be empty");
563 RETURN_FALSE;
566 if (zcontext) {
567 context = php_stream_context_from_zval(zcontext, 0);
570 if (flags == PHP_SCANDIR_SORT_ASCENDING) {
571 n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasort);
572 } else if (flags == PHP_SCANDIR_SORT_NONE) {
573 n = php_stream_scandir(dirn, &namelist, context, NULL);
574 } else {
575 n = php_stream_scandir(dirn, &namelist, context, (void *) php_stream_dirent_alphasortr);
577 if (n < 0) {
578 php_error_docref(NULL TSRMLS_CC, E_WARNING, "(errno %d): %s", errno, strerror(errno));
579 RETURN_FALSE;
582 array_init(return_value);
584 for (i = 0; i < n; i++) {
585 add_next_index_string(return_value, namelist[i], 0);
588 if (n) {
589 efree(namelist);
592 /* }}} */
595 * Local variables:
596 * tab-width: 4
597 * c-basic-offset: 4
598 * End:
599 * vim600: sw=4 ts=4 fdm=marker
600 * vim<600: sw=4 ts=4