2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
21 /* {{{ includes/startup/misc */
24 #include "fopen_wrappers.h"
27 #include "php_string.h"
28 #include "php_scandir.h"
29 #include "basic_functions.h"
42 #include "win32/readdir.h"
50 #include "win32/glob.h"
59 #define DIRG(v) TSRMG(dir_globals_id, php_dir_globals *, v)
62 #define DIRG(v) (dir_globals.v)
63 php_dir_globals dir_globals
;
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) { \
81 if (ZEND_NUM_ARGS() == 0) { \
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"); \
88 ZEND_FETCH_RESOURCE(dirp, php_stream *, tmp, -1, "Directory", php_file_le_stream()); \
90 ZEND_FETCH_RESOURCE(dirp, php_stream *, 0, DIRG(default_dir), "Directory", php_file_le_stream()); \
93 dirp = (php_stream *) zend_fetch_resource(&id TSRMLS_CC, -1, "Directory", NULL, 1, php_file_le_stream()); \
99 ZEND_BEGIN_ARG_INFO_EX(arginfo_dir
, 0, 0, 0)
100 ZEND_ARG_INFO(0, dir_handle
)
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
)
112 static void php_set_default_dir(int id TSRMLS_DC
)
114 if (DIRG(default_dir
)!=-1) {
115 zend_list_delete(DIRG(default_dir
));
119 zend_list_addref(id
);
122 DIRG(default_dir
) = id
;
125 PHP_RINIT_FUNCTION(dir
)
127 DIRG(default_dir
) = -1;
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
);
140 ts_allocate_id(&dir_globals_id
, sizeof(php_dir_globals
), NULL
, NULL
);
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
);
158 REGISTER_LONG_CONSTANT("GLOB_BRACE", GLOB_BRACE
, CONST_CS
| CONST_PERSISTENT
);
160 # define GLOB_BRACE 0
164 REGISTER_LONG_CONSTANT("GLOB_MARK", GLOB_MARK
, CONST_CS
| CONST_PERSISTENT
);
170 REGISTER_LONG_CONSTANT("GLOB_NOSORT", GLOB_NOSORT
, CONST_CS
| CONST_PERSISTENT
);
172 # define GLOB_NOSORT 0
176 REGISTER_LONG_CONSTANT("GLOB_NOCHECK", GLOB_NOCHECK
, CONST_CS
| CONST_PERSISTENT
);
178 # define GLOB_NOCHECK 0
182 REGISTER_LONG_CONSTANT("GLOB_NOESCAPE", GLOB_NOESCAPE
, CONST_CS
| CONST_PERSISTENT
);
184 # define GLOB_NOESCAPE 0
188 REGISTER_LONG_CONSTANT("GLOB_ERR", GLOB_ERR
, CONST_CS
| CONST_PERSISTENT
);
194 # define GLOB_ONLYDIR (1<<30)
195 # define GLOB_EMULATE_ONLYDIR
196 # define GLOB_FLAGMASK (~GLOB_ONLYDIR)
198 # define GLOB_FLAGMASK (~0)
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 */
213 /* {{{ internal functions */
214 static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS
, int createobject
)
218 zval
*zcontext
= NULL
;
219 php_stream_context
*context
= NULL
;
222 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s|r", &dirname
, &dir_len
, &zcontext
) == FAILURE
) {
226 context
= php_stream_context_from_zval(zcontext
, 0);
228 dirp
= php_stream_opendir(dirname
, REPORT_ERRORS
, context
);
234 dirp
->flags
|= PHP_STREAM_FLAG_NO_FCLOSE
;
236 php_set_default_dir(dirp
->rsrc_id TSRMLS_CC
);
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 */
244 php_stream_to_zval(dirp
, return_value
);
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);
257 /* {{{ proto object dir(string directory[, resource context])
258 Directory class with properties, handle and class and methods read, rewind and close */
261 _php_do_opendir(INTERNAL_FUNCTION_PARAM_PASSTHRU
, 1);
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
;
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
);
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
);
289 #if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC
290 /* {{{ proto bool chroot(string directory)
291 Change root directory */
297 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &str
, &str_len
) == FAILURE
) {
303 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "%s (errno %d)", strerror(errno
), errno
);
307 php_clear_stat_cache(1, NULL
, 0 TSRMLS_CC
);
312 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "%s (errno %d)", strerror(errno
), errno
);
321 /* {{{ proto bool chdir(string directory)
322 Change the current directory */
328 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "p", &str
, &str_len
) == FAILURE
) {
332 if (php_check_open_basedir(str TSRMLS_CC
)) {
335 ret
= VCWD_CHDIR(str
);
338 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "%s (errno %d)", strerror(errno
), errno
);
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
;
355 /* {{{ proto mixed getcwd(void)
356 Gets the current directory */
359 char path
[MAXPATHLEN
];
362 if (zend_parse_parameters_none() == FAILURE
) {
367 ret
= VCWD_GETCWD(path
, MAXPATHLEN
);
369 ret
= VCWD_GETWD(path
);
373 RETURN_STRING(path
, 1);
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
;
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
);
394 php_stream_rewinddir(dirp
);
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
;
404 php_stream_dirent entry
;
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
);
413 if (php_stream_readdir(dirp
, &entry
)) {
414 RETURN_STRINGL(entry
.d_name
, strlen(entry
.d_name
), 1);
421 /* {{{ proto array glob(string pattern [, int flags])
422 Find pathnames matching a pattern */
427 char cwd
[MAXPATHLEN
];
428 char work_pattern
[MAXPATHLEN
];
431 char *pattern
= NULL
;
437 zend_bool basedir_limit
= 0;
439 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "p|l", &pattern
, &pattern_len
, &flags
) == FAILURE
) {
443 if (pattern_len
>= MAXPATHLEN
) {
444 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN
);
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");
454 if (!IS_ABSOLUTE_PATH(pattern
, pattern_len
)) {
455 result
= VCWD_GETCWD(cwd
, MAXPATHLEN
);
460 if (IS_SLASH(*pattern
)) {
464 cwd_skip
= strlen(cwd
)+1;
466 snprintf(work_pattern
, MAXPATHLEN
, "%s%c%s", cwd
, DEFAULT_SLASH
, pattern
);
467 pattern
= work_pattern
;
472 memset(&globbuf
, 0, sizeof(glob_t
));
474 if (0 != (ret
= glob(pattern
, flags
& GLOB_FLAGMASK
, NULL
, &globbuf
))) {
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
491 /* now catch the FreeBSD style of "no matches" */
492 if (!globbuf
.gl_pathc
|| !globbuf
.gl_pathv
) {
494 if (PG(open_basedir
) && *PG(open_basedir
)) {
497 if (0 != VCWD_STAT(pattern
, &s
) || S_IFDIR
!= (s
.st_mode
& S_IFMT
)) {
501 array_init(return_value
);
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
)) {
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
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
) {
524 if (0 != VCWD_STAT(globbuf
.gl_pathv
[n
], &s
)) {
528 if (S_IFDIR
!= (s
.st_mode
& S_IFMT
)) {
532 add_next_index_string(return_value
, globbuf
.gl_pathv
[n
]+cwd_skip
, 1);
537 if (basedir_limit
&& !zend_hash_num_elements(Z_ARRVAL_P(return_value
))) {
538 zval_dtor(return_value
);
545 /* {{{ proto array scandir(string dir [, int sorting_order [, resource context]])
546 List files & directories inside the specified path */
547 PHP_FUNCTION(scandir
)
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
) {
562 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Directory name cannot be empty");
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
);
575 n
= php_stream_scandir(dirn
, &namelist
, context
, (void *) php_stream_dirent_alphasortr
);
578 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "(errno %d): %s", errno
, strerror(errno
));
582 array_init(return_value
);
584 for (i
= 0; i
< n
; i
++) {
585 add_next_index_string(return_value
, namelist
[i
], 0);
599 * vim600: sw=4 ts=4 fdm=marker