xref: /php-src/ext/standard/file.c (revision 41e3044f)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
14    |          Stig Bakken <ssb@php.net>                                   |
15    |          Andi Gutmans <andi@php.net>                                 |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    | PHP 4.0 patches by Thies C. Arntzen (thies@thieso.net)               |
18    | PHP streams by Wez Furlong (wez@thebrainroom.com)                    |
19    +----------------------------------------------------------------------+
20 */
21 
22 /* {{{ includes */
23 
24 #include "php.h"
25 #include "php_globals.h"
26 #include "ext/standard/flock_compat.h"
27 #include "ext/standard/exec.h"
28 #include "ext/standard/php_filestat.h"
29 #include "php_open_temporary_file.h"
30 #include "ext/standard/basic_functions.h"
31 #include "php_ini.h"
32 #include "zend_smart_str.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <wchar.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 
42 #ifdef PHP_WIN32
43 # include <io.h>
44 # define O_RDONLY _O_RDONLY
45 # include "win32/param.h"
46 # include "win32/winutil.h"
47 # include "win32/fnmatch.h"
48 # include "win32/ioutil.h"
49 #else
50 # if HAVE_SYS_PARAM_H
51 #  include <sys/param.h>
52 # endif
53 # if HAVE_SYS_SELECT_H
54 #  include <sys/select.h>
55 # endif
56 # include <sys/socket.h>
57 # include <netinet/in.h>
58 # include <netdb.h>
59 # if HAVE_ARPA_INET_H
60 #  include <arpa/inet.h>
61 # endif
62 #endif
63 
64 #include "ext/standard/head.h"
65 #include "php_string.h"
66 #include "file.h"
67 
68 #ifdef HAVE_PWD_H
69 # ifdef PHP_WIN32
70 #  include "win32/pwd.h"
71 # else
72 #  include <pwd.h>
73 # endif
74 #endif
75 
76 #ifdef HAVE_SYS_TIME_H
77 # include <sys/time.h>
78 #endif
79 
80 #include "fsock.h"
81 #include "fopen_wrappers.h"
82 #include "streamsfuncs.h"
83 #include "php_globals.h"
84 
85 #ifdef HAVE_SYS_FILE_H
86 # include <sys/file.h>
87 #endif
88 
89 #ifdef HAVE_SYS_MMAN_H
90 # include <sys/mman.h>
91 #endif
92 
93 #include "scanf.h"
94 #include "zend_API.h"
95 
96 #ifdef ZTS
97 int file_globals_id;
98 #else
99 php_file_globals file_globals;
100 #endif
101 
102 #if defined(HAVE_FNMATCH) && !defined(PHP_WIN32)
103 # ifndef _GNU_SOURCE
104 #  define _GNU_SOURCE
105 # endif
106 # include <fnmatch.h>
107 #endif
108 
109 #include "file_arginfo.h"
110 
111 /* }}} */
112 
113 #define PHP_STREAM_FROM_ZVAL(stream, arg) \
114 	ZEND_ASSERT(Z_TYPE_P(arg) == IS_RESOURCE); \
115 	php_stream_from_res(stream, Z_RES_P(arg));
116 
117 /* {{{ ZTS-stuff / Globals / Prototypes */
118 
119 /* sharing globals is *evil* */
120 static int le_stream_context = FAILURE;
121 
php_le_stream_context(void)122 PHPAPI int php_le_stream_context(void)
123 {
124 	return le_stream_context;
125 }
126 /* }}} */
127 
128 /* {{{ Module-Stuff */
ZEND_RSRC_DTOR_FUNC(file_context_dtor)129 static ZEND_RSRC_DTOR_FUNC(file_context_dtor)
130 {
131 	php_stream_context *context = (php_stream_context*)res->ptr;
132 	if (Z_TYPE(context->options) != IS_UNDEF) {
133 		zval_ptr_dtor(&context->options);
134 		ZVAL_UNDEF(&context->options);
135 	}
136 	php_stream_context_free(context);
137 }
138 
file_globals_ctor(php_file_globals * file_globals_p)139 static void file_globals_ctor(php_file_globals *file_globals_p)
140 {
141 	memset(file_globals_p, 0, sizeof(php_file_globals));
142 	file_globals_p->def_chunk_size = PHP_SOCK_CHUNK_SIZE;
143 }
144 
file_globals_dtor(php_file_globals * file_globals_p)145 static void file_globals_dtor(php_file_globals *file_globals_p)
146 {
147 #if defined(HAVE_GETHOSTBYNAME_R)
148 	if (file_globals_p->tmp_host_buf) {
149 		free(file_globals_p->tmp_host_buf);
150 	}
151 #endif
152 }
153 
PHP_INI_MH(OnUpdateAutoDetectLineEndings)154 static PHP_INI_MH(OnUpdateAutoDetectLineEndings)
155 {
156 	if (zend_ini_parse_bool(new_value)) {
157 		zend_error(E_DEPRECATED, "auto_detect_line_endings is deprecated");
158 	}
159 	return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
160 }
161 
162 PHP_INI_BEGIN()
163 	STD_PHP_INI_ENTRY("user_agent", NULL, PHP_INI_ALL, OnUpdateString, user_agent, php_file_globals, file_globals)
164 	STD_PHP_INI_ENTRY("from", NULL, PHP_INI_ALL, OnUpdateString, from_address, php_file_globals, file_globals)
165 	STD_PHP_INI_ENTRY("default_socket_timeout", "60", PHP_INI_ALL, OnUpdateLong, default_socket_timeout, php_file_globals, file_globals)
166 	STD_PHP_INI_BOOLEAN("auto_detect_line_endings", "0", PHP_INI_ALL, OnUpdateAutoDetectLineEndings, auto_detect_line_endings, php_file_globals, file_globals)
PHP_INI_END()167 PHP_INI_END()
168 
169 PHP_MINIT_FUNCTION(file)
170 {
171 	le_stream_context = zend_register_list_destructors_ex(file_context_dtor, NULL, "stream-context", module_number);
172 
173 #ifdef ZTS
174 	ts_allocate_id(&file_globals_id, sizeof(php_file_globals), (ts_allocate_ctor) file_globals_ctor, (ts_allocate_dtor) file_globals_dtor);
175 #else
176 	file_globals_ctor(&file_globals);
177 #endif
178 
179 	REGISTER_INI_ENTRIES();
180 
181 	register_file_symbols(module_number);
182 
183 	return SUCCESS;
184 }
185 /* }}} */
186 
PHP_MSHUTDOWN_FUNCTION(file)187 PHP_MSHUTDOWN_FUNCTION(file) /* {{{ */
188 {
189 #ifndef ZTS
190 	file_globals_dtor(&file_globals);
191 #endif
192 	return SUCCESS;
193 }
194 /* }}} */
195 
php_flock_common(php_stream * stream,zend_long operation,uint32_t operation_arg_num,zval * wouldblock,zval * return_value)196 PHPAPI void php_flock_common(php_stream *stream, zend_long operation,
197 	uint32_t operation_arg_num, zval *wouldblock, zval *return_value)
198 {
199 	int flock_values[] = { LOCK_SH, LOCK_EX, LOCK_UN };
200 	int act;
201 
202 	act = operation & PHP_LOCK_UN;
203 	if (act < 1 || act > 3) {
204 		zend_argument_value_error(operation_arg_num, "must be one of LOCK_SH, LOCK_EX, or LOCK_UN");
205 		RETURN_THROWS();
206 	}
207 
208 	if (wouldblock) {
209 		ZEND_TRY_ASSIGN_REF_LONG(wouldblock, 0);
210 	}
211 
212 	/* flock_values contains all possible actions if (operation & PHP_LOCK_NB) we won't block on the lock */
213 	act = flock_values[act - 1] | (operation & PHP_LOCK_NB ? LOCK_NB : 0);
214 	if (php_stream_lock(stream, act)) {
215 		if (operation && errno == EWOULDBLOCK && wouldblock) {
216 			ZEND_TRY_ASSIGN_REF_LONG(wouldblock, 1);
217 		}
218 		RETURN_FALSE;
219 	}
220 	RETURN_TRUE;
221 }
222 
223 /* {{{ Portable file locking */
PHP_FUNCTION(flock)224 PHP_FUNCTION(flock)
225 {
226 	zval *res, *wouldblock = NULL;
227 	php_stream *stream;
228 	zend_long operation = 0;
229 
230 	ZEND_PARSE_PARAMETERS_START(2, 3)
231 		Z_PARAM_RESOURCE(res)
232 		Z_PARAM_LONG(operation)
233 		Z_PARAM_OPTIONAL
234 		Z_PARAM_ZVAL(wouldblock)
235 	ZEND_PARSE_PARAMETERS_END();
236 
237 	PHP_STREAM_FROM_ZVAL(stream, res);
238 
239 	php_flock_common(stream, operation, 2, wouldblock, return_value);
240 }
241 /* }}} */
242 
243 #define PHP_META_UNSAFE ".\\+*?[^]$() "
244 
245 /* {{{ Extracts all meta tag content attributes from a file and returns an array */
PHP_FUNCTION(get_meta_tags)246 PHP_FUNCTION(get_meta_tags)
247 {
248 	char *filename;
249 	size_t filename_len;
250 	bool use_include_path = 0;
251 	int in_tag = 0, done = 0;
252 	int looking_for_val = 0, have_name = 0, have_content = 0;
253 	int saw_name = 0, saw_content = 0;
254 	char *name = NULL, *value = NULL, *temp = NULL;
255 	php_meta_tags_token tok, tok_last;
256 	php_meta_tags_data md;
257 
258 	/* Initialize our structure */
259 	memset(&md, 0, sizeof(md));
260 
261 	/* Parse arguments */
262 	ZEND_PARSE_PARAMETERS_START(1, 2)
263 		Z_PARAM_PATH(filename, filename_len)
264 		Z_PARAM_OPTIONAL
265 		Z_PARAM_BOOL(use_include_path)
266 	ZEND_PARSE_PARAMETERS_END();
267 
268 	md.stream = php_stream_open_wrapper(filename, "rb",
269 			(use_include_path ? USE_PATH : 0) | REPORT_ERRORS,
270 			NULL);
271 	if (!md.stream)	{
272 		RETURN_FALSE;
273 	}
274 
275 	array_init(return_value);
276 
277 	tok_last = TOK_EOF;
278 
279 	while (!done && (tok = php_next_meta_token(&md)) != TOK_EOF) {
280 		if (tok == TOK_ID) {
281 			if (tok_last == TOK_OPENTAG) {
282 				md.in_meta = !strcasecmp("meta", md.token_data);
283 			} else if (tok_last == TOK_SLASH && in_tag) {
284 				if (strcasecmp("head", md.token_data) == 0) {
285 					/* We are done here! */
286 					done = 1;
287 				}
288 			} else if (tok_last == TOK_EQUAL && looking_for_val) {
289 				if (saw_name) {
290 					if (name) efree(name);
291 					/* Get the NAME attr (Single word attr, non-quoted) */
292 					temp = name = estrndup(md.token_data, md.token_len);
293 
294 					while (temp && *temp) {
295 						if (strchr(PHP_META_UNSAFE, *temp)) {
296 							*temp = '_';
297 						}
298 						temp++;
299 					}
300 
301 					have_name = 1;
302 				} else if (saw_content) {
303 					if (value) efree(value);
304 					value = estrndup(md.token_data, md.token_len);
305 					have_content = 1;
306 				}
307 
308 				looking_for_val = 0;
309 			} else {
310 				if (md.in_meta) {
311 					if (strcasecmp("name", md.token_data) == 0) {
312 						saw_name = 1;
313 						saw_content = 0;
314 						looking_for_val = 1;
315 					} else if (strcasecmp("content", md.token_data) == 0) {
316 						saw_name = 0;
317 						saw_content = 1;
318 						looking_for_val = 1;
319 					}
320 				}
321 			}
322 		} else if (tok == TOK_STRING && tok_last == TOK_EQUAL && looking_for_val) {
323 			if (saw_name) {
324 				if (name) efree(name);
325 				/* Get the NAME attr (Quoted single/double) */
326 				temp = name = estrndup(md.token_data, md.token_len);
327 
328 				while (temp && *temp) {
329 					if (strchr(PHP_META_UNSAFE, *temp)) {
330 						*temp = '_';
331 					}
332 					temp++;
333 				}
334 
335 				have_name = 1;
336 			} else if (saw_content) {
337 				if (value) efree(value);
338 				value = estrndup(md.token_data, md.token_len);
339 				have_content = 1;
340 			}
341 
342 			looking_for_val = 0;
343 		} else if (tok == TOK_OPENTAG) {
344 			if (looking_for_val) {
345 				looking_for_val = 0;
346 				have_name = saw_name = 0;
347 				have_content = saw_content = 0;
348 			}
349 			in_tag = 1;
350 		} else if (tok == TOK_CLOSETAG) {
351 			if (have_name) {
352 				/* For BC */
353 				zend_str_tolower(name, strlen(name));
354 				if (have_content) {
355 					add_assoc_string(return_value, name, value);
356 				} else {
357 					add_assoc_string(return_value, name, "");
358 				}
359 
360 				efree(name);
361 				if (value) efree(value);
362 			} else if (have_content) {
363 				efree(value);
364 			}
365 
366 			name = value = NULL;
367 
368 			/* Reset all of our flags */
369 			in_tag = looking_for_val = 0;
370 			have_name = saw_name = 0;
371 			have_content = saw_content = 0;
372 			md.in_meta = 0;
373 		}
374 
375 		tok_last = tok;
376 
377 		if (md.token_data)
378 			efree(md.token_data);
379 
380 		md.token_data = NULL;
381 	}
382 
383 	if (value) efree(value);
384 	if (name) efree(name);
385 	php_stream_close(md.stream);
386 }
387 /* }}} */
388 
389 /* {{{ Read the entire file into a string */
PHP_FUNCTION(file_get_contents)390 PHP_FUNCTION(file_get_contents)
391 {
392 	char *filename;
393 	size_t filename_len;
394 	bool use_include_path = 0;
395 	php_stream *stream;
396 	zend_long offset = 0;
397 	zend_long maxlen;
398 	bool maxlen_is_null = 1;
399 	zval *zcontext = NULL;
400 	php_stream_context *context = NULL;
401 	zend_string *contents;
402 
403 	/* Parse arguments */
404 	ZEND_PARSE_PARAMETERS_START(1, 5)
405 		Z_PARAM_PATH(filename, filename_len)
406 		Z_PARAM_OPTIONAL
407 		Z_PARAM_BOOL(use_include_path)
408 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
409 		Z_PARAM_LONG(offset)
410 		Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null)
411 	ZEND_PARSE_PARAMETERS_END();
412 
413 	if (maxlen_is_null) {
414 		maxlen = (ssize_t) PHP_STREAM_COPY_ALL;
415 	} else if (maxlen < 0) {
416 		zend_argument_value_error(5, "must be greater than or equal to 0");
417 		RETURN_THROWS();
418 	}
419 
420 	context = php_stream_context_from_zval(zcontext, 0);
421 
422 	stream = php_stream_open_wrapper_ex(filename, "rb",
423 				(use_include_path ? USE_PATH : 0) | REPORT_ERRORS,
424 				NULL, context);
425 	if (!stream) {
426 		RETURN_FALSE;
427 	}
428 
429 	/* disabling the read buffer allows doing the whole transfer
430 	   in just one read() system call */
431 	if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
432 		php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
433 	}
434 
435 	if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) {
436 		php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset);
437 		php_stream_close(stream);
438 		RETURN_FALSE;
439 	}
440 
441 	if ((contents = php_stream_copy_to_mem(stream, maxlen, 0)) != NULL) {
442 		RETVAL_STR(contents);
443 	} else {
444 		RETVAL_EMPTY_STRING();
445 	}
446 
447 	php_stream_close(stream);
448 }
449 /* }}} */
450 
451 /* {{{ Write/Create a file with contents data and return the number of bytes written */
PHP_FUNCTION(file_put_contents)452 PHP_FUNCTION(file_put_contents)
453 {
454 	php_stream *stream;
455 	char *filename;
456 	size_t filename_len;
457 	zval *data;
458 	ssize_t numbytes = 0;
459 	zend_long flags = 0;
460 	zval *zcontext = NULL;
461 	php_stream_context *context = NULL;
462 	php_stream *srcstream = NULL;
463 	char mode[3] = "wb";
464 
465 	ZEND_PARSE_PARAMETERS_START(2, 4)
466 		Z_PARAM_PATH(filename, filename_len)
467 		Z_PARAM_ZVAL(data)
468 		Z_PARAM_OPTIONAL
469 		Z_PARAM_LONG(flags)
470 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
471 	ZEND_PARSE_PARAMETERS_END();
472 
473 	if (Z_TYPE_P(data) == IS_RESOURCE) {
474 		php_stream_from_zval(srcstream, data);
475 	}
476 
477 	context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
478 
479 	if (flags & PHP_FILE_APPEND) {
480 		mode[0] = 'a';
481 	} else if (flags & LOCK_EX) {
482 		/* check to make sure we are dealing with a regular file */
483 		if (php_memnstr(filename, "://", sizeof("://") - 1, filename + filename_len)) {
484 			if (strncasecmp(filename, "file://", sizeof("file://") - 1)) {
485 				php_error_docref(NULL, E_WARNING, "Exclusive locks may only be set for regular files");
486 				RETURN_FALSE;
487 			}
488 		}
489 		mode[0] = 'c';
490 	}
491 	mode[2] = '\0';
492 
493 	stream = php_stream_open_wrapper_ex(filename, mode, ((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
494 	if (stream == NULL) {
495 		RETURN_FALSE;
496 	}
497 
498 	if ((flags & LOCK_EX) && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) {
499 		php_stream_close(stream);
500 		php_error_docref(NULL, E_WARNING, "Exclusive locks are not supported for this stream");
501 		RETURN_FALSE;
502 	}
503 
504 	if (mode[0] == 'c') {
505 		php_stream_truncate_set_size(stream, 0);
506 	}
507 
508 	switch (Z_TYPE_P(data)) {
509 		case IS_RESOURCE: {
510 			size_t len;
511 			if (php_stream_copy_to_stream_ex(srcstream, stream, PHP_STREAM_COPY_ALL, &len) != SUCCESS) {
512 				numbytes = -1;
513 			} else {
514 				if (len > ZEND_LONG_MAX) {
515 					php_error_docref(NULL, E_WARNING, "content truncated from %zu to " ZEND_LONG_FMT " bytes", len, ZEND_LONG_MAX);
516 					len = ZEND_LONG_MAX;
517 				}
518 				numbytes = len;
519 			}
520 			break;
521 		}
522 		case IS_NULL:
523 		case IS_LONG:
524 		case IS_DOUBLE:
525 		case IS_FALSE:
526 		case IS_TRUE:
527 			convert_to_string(data);
528 			ZEND_FALLTHROUGH;
529 		case IS_STRING:
530 			if (Z_STRLEN_P(data)) {
531 				numbytes = php_stream_write(stream, Z_STRVAL_P(data), Z_STRLEN_P(data));
532 				if (numbytes != -1 && numbytes != Z_STRLEN_P(data)) {
533 					php_error_docref(NULL, E_WARNING, "Only %zd of %zd bytes written, possibly out of free disk space", numbytes, Z_STRLEN_P(data));
534 					numbytes = -1;
535 				}
536 			}
537 			break;
538 
539 		case IS_ARRAY:
540 			if (zend_hash_num_elements(Z_ARRVAL_P(data))) {
541 				ssize_t bytes_written;
542 				zval *tmp;
543 
544 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), tmp) {
545 					zend_string *t;
546 					zend_string *str = zval_get_tmp_string(tmp, &t);
547 					if (ZSTR_LEN(str)) {
548 						numbytes += ZSTR_LEN(str);
549 						bytes_written = php_stream_write(stream, ZSTR_VAL(str), ZSTR_LEN(str));
550 						if (bytes_written != ZSTR_LEN(str)) {
551 							php_error_docref(NULL, E_WARNING, "Failed to write %zd bytes to %s", ZSTR_LEN(str), filename);
552 							zend_tmp_string_release(t);
553 							numbytes = -1;
554 							break;
555 						}
556 					}
557 					zend_tmp_string_release(t);
558 				} ZEND_HASH_FOREACH_END();
559 			}
560 			break;
561 
562 		case IS_OBJECT:
563 			if (Z_OBJ_HT_P(data) != NULL) {
564 				zval out;
565 
566 				if (zend_std_cast_object_tostring(Z_OBJ_P(data), &out, IS_STRING) == SUCCESS) {
567 					numbytes = php_stream_write(stream, Z_STRVAL(out), Z_STRLEN(out));
568 					if (numbytes != -1 && numbytes != Z_STRLEN(out)) {
569 						php_error_docref(NULL, E_WARNING, "Only %zd of %zd bytes written, possibly out of free disk space", numbytes, Z_STRLEN(out));
570 						numbytes = -1;
571 					}
572 					zval_ptr_dtor_str(&out);
573 					break;
574 				}
575 			}
576 			ZEND_FALLTHROUGH;
577 		default:
578 			numbytes = -1;
579 			break;
580 	}
581 	php_stream_close(stream);
582 
583 	if (numbytes < 0) {
584 		RETURN_FALSE;
585 	}
586 
587 	RETURN_LONG(numbytes);
588 }
589 /* }}} */
590 
591 #define PHP_FILE_BUF_SIZE	80
592 
593 /* {{{ Read entire file into an array */
PHP_FUNCTION(file)594 PHP_FUNCTION(file)
595 {
596 	char *filename;
597 	size_t filename_len;
598 	char *p, *s, *e;
599 	int i = 0;
600 	char eol_marker = '\n';
601 	zend_long flags = 0;
602 	bool use_include_path;
603 	bool include_new_line;
604 	bool skip_blank_lines;
605 	php_stream *stream;
606 	zval *zcontext = NULL;
607 	php_stream_context *context = NULL;
608 	zend_string *target_buf;
609 
610 	/* Parse arguments */
611 	ZEND_PARSE_PARAMETERS_START(1, 3)
612 		Z_PARAM_PATH(filename, filename_len)
613 		Z_PARAM_OPTIONAL
614 		Z_PARAM_LONG(flags)
615 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
616 	ZEND_PARSE_PARAMETERS_END();
617 
618 	if ((flags & ~(PHP_FILE_USE_INCLUDE_PATH | PHP_FILE_IGNORE_NEW_LINES | PHP_FILE_SKIP_EMPTY_LINES | PHP_FILE_NO_DEFAULT_CONTEXT)) != 0) {
619 		zend_argument_value_error(2, "must be a valid flag value");
620 		RETURN_THROWS();
621 	}
622 
623 	use_include_path = flags & PHP_FILE_USE_INCLUDE_PATH;
624 	include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES);
625 	skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES;
626 
627 	context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
628 
629 	stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
630 	if (!stream) {
631 		RETURN_FALSE;
632 	}
633 
634 	/* Initialize return array */
635 	array_init(return_value);
636 
637 	if ((target_buf = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
638 		s = ZSTR_VAL(target_buf);
639 		e = ZSTR_VAL(target_buf) + ZSTR_LEN(target_buf);
640 
641 		if (!(p = (char*)php_stream_locate_eol(stream, target_buf))) {
642 			p = e;
643 			goto parse_eol;
644 		}
645 
646 		if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
647 			eol_marker = '\r';
648 		}
649 
650 		/* for performance reasons the code is duplicated, so that the if (include_new_line)
651 		 * will not need to be done for every single line in the file. */
652 		if (include_new_line) {
653 			do {
654 				p++;
655 parse_eol:
656 				add_index_stringl(return_value, i++, s, p-s);
657 				s = p;
658 			} while ((p = memchr(p, eol_marker, (e-p))));
659 		} else {
660 			do {
661 				int windows_eol = 0;
662 				if (p != ZSTR_VAL(target_buf) && eol_marker == '\n' && *(p - 1) == '\r') {
663 					windows_eol++;
664 				}
665 				if (skip_blank_lines && !(p-s-windows_eol)) {
666 					s = ++p;
667 					continue;
668 				}
669 				add_index_stringl(return_value, i++, s, p-s-windows_eol);
670 				s = ++p;
671 			} while ((p = memchr(p, eol_marker, (e-p))));
672 		}
673 
674 		/* handle any leftovers of files without new lines */
675 		if (s != e) {
676 			p = e;
677 			goto parse_eol;
678 		}
679 	}
680 
681 	if (target_buf) {
682 		zend_string_free(target_buf);
683 	}
684 	php_stream_close(stream);
685 }
686 /* }}} */
687 
688 /* {{{ Create a unique filename in a directory */
PHP_FUNCTION(tempnam)689 PHP_FUNCTION(tempnam)
690 {
691 	char *dir, *prefix;
692 	size_t dir_len, prefix_len;
693 	zend_string *opened_path;
694 	int fd;
695 	zend_string *p;
696 
697 	ZEND_PARSE_PARAMETERS_START(2, 2)
698 		Z_PARAM_PATH(dir, dir_len)
699 		Z_PARAM_PATH(prefix, prefix_len)
700 	ZEND_PARSE_PARAMETERS_END();
701 
702 	p = php_basename(prefix, prefix_len, NULL, 0);
703 	if (ZSTR_LEN(p) >= 64) {
704 		ZSTR_VAL(p)[63] = '\0';
705 	}
706 
707 	RETVAL_FALSE;
708 
709 	if ((fd = php_open_temporary_fd_ex(dir, ZSTR_VAL(p), &opened_path, PHP_TMP_FILE_OPEN_BASEDIR_CHECK_ALWAYS)) >= 0) {
710 		close(fd);
711 		RETVAL_STR(opened_path);
712 	}
713 	zend_string_release_ex(p, 0);
714 }
715 /* }}} */
716 
717 /* {{{ Create a temporary file that will be deleted automatically after use */
PHP_FUNCTION(tmpfile)718 PHP_FUNCTION(tmpfile)
719 {
720 	php_stream *stream;
721 
722 	ZEND_PARSE_PARAMETERS_NONE();
723 
724 	stream = php_stream_fopen_tmpfile();
725 
726 	if (stream) {
727 		php_stream_to_zval(stream, return_value);
728 	} else {
729 		RETURN_FALSE;
730 	}
731 }
732 /* }}} */
733 
734 /* {{{ Open a file or a URL and return a file pointer */
PHP_FUNCTION(fopen)735 PHP_FUNCTION(fopen)
736 {
737 	char *filename, *mode;
738 	size_t filename_len, mode_len;
739 	bool use_include_path = 0;
740 	zval *zcontext = NULL;
741 	php_stream *stream;
742 	php_stream_context *context = NULL;
743 
744 	ZEND_PARSE_PARAMETERS_START(2, 4)
745 		Z_PARAM_PATH(filename, filename_len)
746 		Z_PARAM_STRING(mode, mode_len)
747 		Z_PARAM_OPTIONAL
748 		Z_PARAM_BOOL(use_include_path)
749 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
750 	ZEND_PARSE_PARAMETERS_END();
751 
752 	context = php_stream_context_from_zval(zcontext, 0);
753 
754 	stream = php_stream_open_wrapper_ex(filename, mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
755 
756 	if (stream == NULL) {
757 		RETURN_FALSE;
758 	}
759 
760 	php_stream_to_zval(stream, return_value);
761 }
762 /* }}} */
763 
764 /* {{{ Close an open file pointer */
PHP_FUNCTION(fclose)765 PHPAPI PHP_FUNCTION(fclose)
766 {
767 	zval *res;
768 	php_stream *stream;
769 
770 	ZEND_PARSE_PARAMETERS_START(1, 1)
771 		Z_PARAM_RESOURCE(res)
772 	ZEND_PARSE_PARAMETERS_END();
773 
774 	PHP_STREAM_FROM_ZVAL(stream, res);
775 
776 	if ((stream->flags & PHP_STREAM_FLAG_NO_FCLOSE) != 0) {
777 		php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " is not a valid stream resource", stream->res->handle);
778 		RETURN_FALSE;
779 	}
780 
781 	php_stream_free(stream,
782 		PHP_STREAM_FREE_KEEP_RSRC |
783 		(stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : PHP_STREAM_FREE_CLOSE));
784 
785 	RETURN_TRUE;
786 }
787 /* }}} */
788 
789 /* {{{ Execute a command and open either a read or a write pipe to it */
PHP_FUNCTION(popen)790 PHP_FUNCTION(popen)
791 {
792 	char *command, *mode;
793 	size_t command_len, mode_len;
794 	FILE *fp;
795 	php_stream *stream;
796 	char *posix_mode;
797 
798 	ZEND_PARSE_PARAMETERS_START(2, 2)
799 		Z_PARAM_PATH(command, command_len)
800 		Z_PARAM_STRING(mode, mode_len)
801 	ZEND_PARSE_PARAMETERS_END();
802 
803 	posix_mode = estrndup(mode, mode_len);
804 #ifndef PHP_WIN32
805 	{
806 		char *z = memchr(posix_mode, 'b', mode_len);
807 		if (z) {
808 			memmove(z, z + 1, mode_len - (z - posix_mode));
809 			mode_len--;
810 		}
811 	}
812 #endif
813 
814 	/* Musl only partially validates the mode. Manually check it to ensure consistent behavior. */
815 	if (mode_len > 2 ||
816 		(mode_len == 1 && (*posix_mode != 'r' && *posix_mode != 'w')) ||
817 		(mode_len == 2 && (memcmp(posix_mode, "rb", 2) && memcmp(posix_mode, "wb", 2)))
818 	) {
819 		zend_argument_value_error(2, "must be one of \"r\", \"rb\", \"w\", or \"wb\"");
820 		efree(posix_mode);
821 		RETURN_THROWS();
822 	}
823 
824 	fp = VCWD_POPEN(command, posix_mode);
825 	if (!fp) {
826 		php_error_docref2(NULL, command, posix_mode, E_WARNING, "%s", strerror(errno));
827 		efree(posix_mode);
828 		RETURN_FALSE;
829 	}
830 
831 	stream = php_stream_fopen_from_pipe(fp, mode);
832 
833 	if (stream == NULL)	{
834 		php_error_docref2(NULL, command, mode, E_WARNING, "%s", strerror(errno));
835 		RETVAL_FALSE;
836 	} else {
837 		php_stream_to_zval(stream, return_value);
838 	}
839 
840 	efree(posix_mode);
841 }
842 /* }}} */
843 
844 /* {{{ Close a file pointer opened by popen() */
PHP_FUNCTION(pclose)845 PHP_FUNCTION(pclose)
846 {
847 	zval *res;
848 	php_stream *stream;
849 
850 	ZEND_PARSE_PARAMETERS_START(1, 1)
851 		Z_PARAM_RESOURCE(res)
852 	ZEND_PARSE_PARAMETERS_END();
853 
854 	PHP_STREAM_FROM_ZVAL(stream, res);
855 
856 	FG(pclose_wait) = 1;
857 	zend_list_close(stream->res);
858 	FG(pclose_wait) = 0;
859 	RETURN_LONG(FG(pclose_ret));
860 }
861 /* }}} */
862 
863 /* {{{ Test for end-of-file on a file pointer */
PHP_FUNCTION(feof)864 PHPAPI PHP_FUNCTION(feof)
865 {
866 	zval *res;
867 	php_stream *stream;
868 
869 	ZEND_PARSE_PARAMETERS_START(1, 1)
870 		Z_PARAM_RESOURCE(res)
871 	ZEND_PARSE_PARAMETERS_END();
872 
873 	PHP_STREAM_FROM_ZVAL(stream, res);
874 
875 	if (php_stream_eof(stream)) {
876 		RETURN_TRUE;
877 	} else {
878 		RETURN_FALSE;
879 	}
880 }
881 /* }}} */
882 
883 /* {{{ Get a line from file pointer */
PHP_FUNCTION(fgets)884 PHPAPI PHP_FUNCTION(fgets)
885 {
886 	zval *res;
887 	zend_long len = 1024;
888 	bool len_is_null = 1;
889 	char *buf = NULL;
890 	size_t line_len = 0;
891 	zend_string *str;
892 	php_stream *stream;
893 
894 	ZEND_PARSE_PARAMETERS_START(1, 2)
895 		Z_PARAM_RESOURCE(res)
896 		Z_PARAM_OPTIONAL
897 		Z_PARAM_LONG_OR_NULL(len, len_is_null)
898 	ZEND_PARSE_PARAMETERS_END();
899 
900 	PHP_STREAM_FROM_ZVAL(stream, res);
901 
902 	if (len_is_null) {
903 		/* ask streams to give us a buffer of an appropriate size */
904 		buf = php_stream_get_line(stream, NULL, 0, &line_len);
905 		if (buf == NULL) {
906 			RETURN_FALSE;
907 		}
908 		// TODO: avoid reallocation ???
909 		RETVAL_STRINGL(buf, line_len);
910 		efree(buf);
911 	} else {
912 		if (len <= 0) {
913 			zend_argument_value_error(2, "must be greater than 0");
914 			RETURN_THROWS();
915 		}
916 
917 		str = zend_string_alloc(len, 0);
918 		if (php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len) == NULL) {
919 			zend_string_efree(str);
920 			RETURN_FALSE;
921 		}
922 		/* resize buffer if it's much larger than the result.
923 		 * Only needed if the user requested a buffer size. */
924 		if (line_len < (size_t)len / 2) {
925 			str = zend_string_truncate(str, line_len, 0);
926 		} else {
927 			ZSTR_LEN(str) = line_len;
928 		}
929 		RETURN_NEW_STR(str);
930 	}
931 }
932 /* }}} */
933 
934 /* {{{ Get a character from file pointer */
PHP_FUNCTION(fgetc)935 PHPAPI PHP_FUNCTION(fgetc)
936 {
937 	zval *res;
938 	php_stream *stream;
939 
940 	ZEND_PARSE_PARAMETERS_START(1, 1)
941 		Z_PARAM_RESOURCE(res)
942 	ZEND_PARSE_PARAMETERS_END();
943 
944 	PHP_STREAM_FROM_ZVAL(stream, res);
945 
946 	int result = php_stream_getc(stream);
947 
948 	if (result == EOF) {
949 		RETVAL_FALSE;
950 	} else {
951 		RETURN_CHAR(result);
952 	}
953 }
954 /* }}} */
955 
956 /* {{{ Implements a mostly ANSI compatible fscanf() */
PHP_FUNCTION(fscanf)957 PHP_FUNCTION(fscanf)
958 {
959 	int result, argc = 0;
960 	size_t format_len;
961 	zval *args = NULL;
962 	zval *file_handle;
963 	char *buf, *format;
964 	size_t len;
965 	void *what;
966 
967 	ZEND_PARSE_PARAMETERS_START(2, -1)
968 		Z_PARAM_RESOURCE(file_handle)
969 		Z_PARAM_STRING(format, format_len)
970 		Z_PARAM_VARIADIC('*', args, argc)
971 	ZEND_PARSE_PARAMETERS_END();
972 
973 	what = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream());
974 
975 	/* we can't do a ZEND_VERIFY_RESOURCE(what), otherwise we end up
976 	 * with a leak if we have an invalid filehandle. This needs changing
977 	 * if the code behind ZEND_VERIFY_RESOURCE changed. - cc */
978 	if (!what) {
979 		RETURN_THROWS();
980 	}
981 
982 	buf = php_stream_get_line((php_stream *) what, NULL, 0, &len);
983 	if (buf == NULL) {
984 		RETURN_FALSE;
985 	}
986 
987 	result = php_sscanf_internal(buf, format, argc, args, 0, return_value);
988 
989 	efree(buf);
990 
991 	if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
992 		WRONG_PARAM_COUNT;
993 	}
994 }
995 /* }}} */
996 
997 /* {{{ Binary-safe file write */
PHP_FUNCTION(fwrite)998 PHPAPI PHP_FUNCTION(fwrite)
999 {
1000 	zval *res;
1001 	char *input;
1002 	size_t inputlen;
1003 	ssize_t ret;
1004 	size_t num_bytes;
1005 	zend_long maxlen = 0;
1006 	bool maxlen_is_null = 1;
1007 	php_stream *stream;
1008 
1009 	ZEND_PARSE_PARAMETERS_START(2, 3)
1010 		Z_PARAM_RESOURCE(res)
1011 		Z_PARAM_STRING(input, inputlen)
1012 		Z_PARAM_OPTIONAL
1013 		Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null)
1014 	ZEND_PARSE_PARAMETERS_END();
1015 
1016 	if (maxlen_is_null) {
1017 		num_bytes = inputlen;
1018 	} else if (maxlen <= 0) {
1019 		num_bytes = 0;
1020 	} else {
1021 		num_bytes = MIN((size_t) maxlen, inputlen);
1022 	}
1023 
1024 	if (!num_bytes) {
1025 		RETURN_LONG(0);
1026 	}
1027 
1028 	PHP_STREAM_FROM_ZVAL(stream, res);
1029 
1030 	ret = php_stream_write(stream, input, num_bytes);
1031 	if (ret < 0) {
1032 		RETURN_FALSE;
1033 	}
1034 
1035 	RETURN_LONG(ret);
1036 }
1037 /* }}} */
1038 
1039 /* {{{ Flushes output */
PHP_FUNCTION(fflush)1040 PHPAPI PHP_FUNCTION(fflush)
1041 {
1042 	zval *res;
1043 	int ret;
1044 	php_stream *stream;
1045 
1046 	ZEND_PARSE_PARAMETERS_START(1, 1)
1047 		Z_PARAM_RESOURCE(res)
1048 	ZEND_PARSE_PARAMETERS_END();
1049 
1050 	PHP_STREAM_FROM_ZVAL(stream, res);
1051 
1052 	ret = php_stream_flush(stream);
1053 	if (ret) {
1054 		RETURN_FALSE;
1055 	}
1056 	RETURN_TRUE;
1057 }
1058 /* }}} */
1059 
1060 /* {{{ Rewind the position of a file pointer */
PHP_FUNCTION(rewind)1061 PHPAPI PHP_FUNCTION(rewind)
1062 {
1063 	zval *res;
1064 	php_stream *stream;
1065 
1066 	ZEND_PARSE_PARAMETERS_START(1, 1)
1067 		Z_PARAM_RESOURCE(res)
1068 	ZEND_PARSE_PARAMETERS_END();
1069 
1070 	PHP_STREAM_FROM_ZVAL(stream, res);
1071 
1072 	if (-1 == php_stream_rewind(stream)) {
1073 		RETURN_FALSE;
1074 	}
1075 	RETURN_TRUE;
1076 }
1077 /* }}} */
1078 
1079 /* {{{ Get file pointer's read/write position */
PHP_FUNCTION(ftell)1080 PHPAPI PHP_FUNCTION(ftell)
1081 {
1082 	zval *res;
1083 	zend_long ret;
1084 	php_stream *stream;
1085 
1086 	ZEND_PARSE_PARAMETERS_START(1, 1)
1087 		Z_PARAM_RESOURCE(res)
1088 	ZEND_PARSE_PARAMETERS_END();
1089 
1090 	PHP_STREAM_FROM_ZVAL(stream, res);
1091 
1092 	ret = php_stream_tell(stream);
1093 	if (ret == -1)	{
1094 		RETURN_FALSE;
1095 	}
1096 	RETURN_LONG(ret);
1097 }
1098 /* }}} */
1099 
1100 /* {{{ Seek on a file pointer */
PHP_FUNCTION(fseek)1101 PHPAPI PHP_FUNCTION(fseek)
1102 {
1103 	zval *res;
1104 	zend_long offset, whence = SEEK_SET;
1105 	php_stream *stream;
1106 
1107 	ZEND_PARSE_PARAMETERS_START(2, 3)
1108 		Z_PARAM_RESOURCE(res)
1109 		Z_PARAM_LONG(offset)
1110 		Z_PARAM_OPTIONAL
1111 		Z_PARAM_LONG(whence)
1112 	ZEND_PARSE_PARAMETERS_END();
1113 
1114 	PHP_STREAM_FROM_ZVAL(stream, res);
1115 
1116 	RETURN_LONG(php_stream_seek(stream, offset, (int) whence));
1117 }
1118 /* }}} */
1119 
1120 /* {{{ php_mkdir */
1121 
1122 /* DEPRECATED APIs: Use php_stream_mkdir() instead */
php_mkdir_ex(const char * dir,zend_long mode,int options)1123 PHPAPI int php_mkdir_ex(const char *dir, zend_long mode, int options)
1124 {
1125 	int ret;
1126 
1127 	if (php_check_open_basedir(dir)) {
1128 		return -1;
1129 	}
1130 
1131 	if ((ret = VCWD_MKDIR(dir, (mode_t)mode)) < 0 && (options & REPORT_ERRORS)) {
1132 		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
1133 	}
1134 
1135 	return ret;
1136 }
1137 
php_mkdir(const char * dir,zend_long mode)1138 PHPAPI int php_mkdir(const char *dir, zend_long mode)
1139 {
1140 	return php_mkdir_ex(dir, mode, REPORT_ERRORS);
1141 }
1142 /* }}} */
1143 
1144 /* {{{ Create a directory */
PHP_FUNCTION(mkdir)1145 PHP_FUNCTION(mkdir)
1146 {
1147 	char *dir;
1148 	size_t dir_len;
1149 	zval *zcontext = NULL;
1150 	zend_long mode = 0777;
1151 	bool recursive = 0;
1152 	php_stream_context *context;
1153 
1154 	ZEND_PARSE_PARAMETERS_START(1, 4)
1155 		Z_PARAM_PATH(dir, dir_len)
1156 		Z_PARAM_OPTIONAL
1157 		Z_PARAM_LONG(mode)
1158 		Z_PARAM_BOOL(recursive)
1159 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
1160 	ZEND_PARSE_PARAMETERS_END();
1161 
1162 	context = php_stream_context_from_zval(zcontext, 0);
1163 
1164 	RETURN_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context));
1165 }
1166 /* }}} */
1167 
1168 /* {{{ Remove a directory */
PHP_FUNCTION(rmdir)1169 PHP_FUNCTION(rmdir)
1170 {
1171 	char *dir;
1172 	size_t dir_len;
1173 	zval *zcontext = NULL;
1174 	php_stream_context *context;
1175 
1176 	ZEND_PARSE_PARAMETERS_START(1, 2)
1177 		Z_PARAM_PATH(dir, dir_len)
1178 		Z_PARAM_OPTIONAL
1179 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
1180 	ZEND_PARSE_PARAMETERS_END();
1181 
1182 	context = php_stream_context_from_zval(zcontext, 0);
1183 
1184 	RETURN_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context));
1185 }
1186 /* }}} */
1187 
1188 /* {{{ Output a file or a URL */
PHP_FUNCTION(readfile)1189 PHP_FUNCTION(readfile)
1190 {
1191 	char *filename;
1192 	size_t filename_len;
1193 	size_t size = 0;
1194 	bool use_include_path = 0;
1195 	zval *zcontext = NULL;
1196 	php_stream *stream;
1197 	php_stream_context *context = NULL;
1198 
1199 	ZEND_PARSE_PARAMETERS_START(1, 3)
1200 		Z_PARAM_PATH(filename, filename_len)
1201 		Z_PARAM_OPTIONAL
1202 		Z_PARAM_BOOL(use_include_path)
1203 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
1204 	ZEND_PARSE_PARAMETERS_END();
1205 
1206 	context = php_stream_context_from_zval(zcontext, 0);
1207 
1208 	stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
1209 	if (stream) {
1210 		size = php_stream_passthru(stream);
1211 		php_stream_close(stream);
1212 		RETURN_LONG(size);
1213 	}
1214 
1215 	RETURN_FALSE;
1216 }
1217 /* }}} */
1218 
1219 /* {{{ Return or change the umask */
PHP_FUNCTION(umask)1220 PHP_FUNCTION(umask)
1221 {
1222 	zend_long mask = 0;
1223 	bool mask_is_null = 1;
1224 	int oldumask;
1225 
1226 	ZEND_PARSE_PARAMETERS_START(0, 1)
1227 		Z_PARAM_OPTIONAL
1228 		Z_PARAM_LONG_OR_NULL(mask, mask_is_null)
1229 	ZEND_PARSE_PARAMETERS_END();
1230 
1231 	oldumask = umask(077);
1232 
1233 	if (BG(umask) == -1) {
1234 		BG(umask) = oldumask;
1235 	}
1236 
1237 	if (mask_is_null) {
1238 		umask(oldumask);
1239 	} else {
1240 		umask((int) mask);
1241 	}
1242 
1243 	RETURN_LONG(oldumask);
1244 }
1245 /* }}} */
1246 
1247 /* {{{ Output all remaining data from a file pointer */
PHP_FUNCTION(fpassthru)1248 PHPAPI PHP_FUNCTION(fpassthru)
1249 {
1250 	zval *res;
1251 	size_t size;
1252 	php_stream *stream;
1253 
1254 	ZEND_PARSE_PARAMETERS_START(1, 1)
1255 		Z_PARAM_RESOURCE(res)
1256 	ZEND_PARSE_PARAMETERS_END();
1257 
1258 	PHP_STREAM_FROM_ZVAL(stream, res);
1259 
1260 	size = php_stream_passthru(stream);
1261 	RETURN_LONG(size);
1262 }
1263 /* }}} */
1264 
1265 /* {{{ Rename a file */
PHP_FUNCTION(rename)1266 PHP_FUNCTION(rename)
1267 {
1268 	char *old_name, *new_name;
1269 	size_t old_name_len, new_name_len;
1270 	zval *zcontext = NULL;
1271 	php_stream_wrapper *wrapper;
1272 	php_stream_context *context;
1273 
1274 	ZEND_PARSE_PARAMETERS_START(2, 3)
1275 		Z_PARAM_PATH(old_name, old_name_len)
1276 		Z_PARAM_PATH(new_name, new_name_len)
1277 		Z_PARAM_OPTIONAL
1278 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
1279 	ZEND_PARSE_PARAMETERS_END();
1280 
1281 	wrapper = php_stream_locate_url_wrapper(old_name, NULL, 0);
1282 
1283 	if (!wrapper || !wrapper->wops) {
1284 		php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper");
1285 		RETURN_FALSE;
1286 	}
1287 
1288 	if (!wrapper->wops->rename) {
1289 		php_error_docref(NULL, E_WARNING, "%s wrapper does not support renaming", wrapper->wops->label ? wrapper->wops->label : "Source");
1290 		RETURN_FALSE;
1291 	}
1292 
1293 	if (wrapper != php_stream_locate_url_wrapper(new_name, NULL, 0)) {
1294 		php_error_docref(NULL, E_WARNING, "Cannot rename a file across wrapper types");
1295 		RETURN_FALSE;
1296 	}
1297 
1298 	context = php_stream_context_from_zval(zcontext, 0);
1299 
1300 	RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, 0, context));
1301 }
1302 /* }}} */
1303 
1304 /* {{{ Delete a file */
PHP_FUNCTION(unlink)1305 PHP_FUNCTION(unlink)
1306 {
1307 	char *filename;
1308 	size_t filename_len;
1309 	php_stream_wrapper *wrapper;
1310 	zval *zcontext = NULL;
1311 	php_stream_context *context = NULL;
1312 
1313 	ZEND_PARSE_PARAMETERS_START(1, 2)
1314 		Z_PARAM_PATH(filename, filename_len)
1315 		Z_PARAM_OPTIONAL
1316 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
1317 	ZEND_PARSE_PARAMETERS_END();
1318 
1319 	context = php_stream_context_from_zval(zcontext, 0);
1320 
1321 	wrapper = php_stream_locate_url_wrapper(filename, NULL, 0);
1322 
1323 	if (!wrapper || !wrapper->wops) {
1324 		php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper");
1325 		RETURN_FALSE;
1326 	}
1327 
1328 	if (!wrapper->wops->unlink) {
1329 		php_error_docref(NULL, E_WARNING, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper");
1330 		RETURN_FALSE;
1331 	}
1332 	RETURN_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context));
1333 }
1334 /* }}} */
1335 
PHP_FUNCTION(fsync)1336 PHP_FUNCTION(fsync)
1337 {
1338 	zval *res;
1339 	php_stream *stream;
1340 
1341 	ZEND_PARSE_PARAMETERS_START(1, 1)
1342 		Z_PARAM_RESOURCE(res)
1343 	ZEND_PARSE_PARAMETERS_END();
1344 
1345 	PHP_STREAM_FROM_ZVAL(stream, res);
1346 
1347 	if (!php_stream_sync_supported(stream)) {
1348 		php_error_docref(NULL, E_WARNING, "Can't fsync this stream!");
1349 		RETURN_FALSE;
1350 	}
1351 
1352 	RETURN_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0);
1353 }
1354 
PHP_FUNCTION(fdatasync)1355 PHP_FUNCTION(fdatasync)
1356 {
1357 	zval *res;
1358 	php_stream *stream;
1359 
1360 	ZEND_PARSE_PARAMETERS_START(1, 1)
1361 		Z_PARAM_RESOURCE(res)
1362 	ZEND_PARSE_PARAMETERS_END();
1363 
1364 	PHP_STREAM_FROM_ZVAL(stream, res);
1365 
1366 	if (!php_stream_sync_supported(stream)) {
1367 		php_error_docref(NULL, E_WARNING, "Can't fsync this stream!");
1368 		RETURN_FALSE;
1369 	}
1370 
1371 	RETURN_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0);
1372 }
1373 
1374 /* {{{ Truncate file to 'size' length */
PHP_FUNCTION(ftruncate)1375 PHP_FUNCTION(ftruncate)
1376 {
1377 	zval *fp;
1378 	zend_long size;
1379 	php_stream *stream;
1380 
1381 	ZEND_PARSE_PARAMETERS_START(2, 2)
1382 		Z_PARAM_RESOURCE(fp)
1383 		Z_PARAM_LONG(size)
1384 	ZEND_PARSE_PARAMETERS_END();
1385 
1386 	if (size < 0) {
1387 		zend_argument_value_error(2, "must be greater than or equal to 0");
1388 		RETURN_THROWS();
1389 	}
1390 
1391 	PHP_STREAM_FROM_ZVAL(stream, fp);
1392 
1393 	if (!php_stream_truncate_supported(stream)) {
1394 		php_error_docref(NULL, E_WARNING, "Can't truncate this stream!");
1395 		RETURN_FALSE;
1396 	}
1397 
1398 	RETURN_BOOL(0 == php_stream_truncate_set_size(stream, size));
1399 }
1400 /* }}} */
php_fstat(php_stream * stream,zval * return_value)1401 PHPAPI void php_fstat(php_stream *stream, zval *return_value)
1402 {
1403 	php_stream_statbuf stat_ssb;
1404 	zval stat_dev, stat_ino, stat_mode, stat_nlink, stat_uid, stat_gid, stat_rdev,
1405 		 stat_size, stat_atime, stat_mtime, stat_ctime, stat_blksize, stat_blocks;
1406 	char *stat_sb_names[13] = {
1407 		"dev", "ino", "mode", "nlink", "uid", "gid", "rdev",
1408 		"size", "atime", "mtime", "ctime", "blksize", "blocks"
1409 	};
1410 
1411 	if (php_stream_stat(stream, &stat_ssb)) {
1412 		RETURN_FALSE;
1413 	}
1414 
1415 	array_init(return_value);
1416 
1417 	ZVAL_LONG(&stat_dev, stat_ssb.sb.st_dev);
1418 	ZVAL_LONG(&stat_ino, stat_ssb.sb.st_ino);
1419 	ZVAL_LONG(&stat_mode, stat_ssb.sb.st_mode);
1420 	ZVAL_LONG(&stat_nlink, stat_ssb.sb.st_nlink);
1421 	ZVAL_LONG(&stat_uid, stat_ssb.sb.st_uid);
1422 	ZVAL_LONG(&stat_gid, stat_ssb.sb.st_gid);
1423 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1424 	ZVAL_LONG(&stat_rdev, stat_ssb.sb.st_rdev);
1425 #else
1426 	ZVAL_LONG(&stat_rdev, -1);
1427 #endif
1428 	ZVAL_LONG(&stat_size, stat_ssb.sb.st_size);
1429 	ZVAL_LONG(&stat_atime, stat_ssb.sb.st_atime);
1430 	ZVAL_LONG(&stat_mtime, stat_ssb.sb.st_mtime);
1431 	ZVAL_LONG(&stat_ctime, stat_ssb.sb.st_ctime);
1432 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1433 	ZVAL_LONG(&stat_blksize, stat_ssb.sb.st_blksize);
1434 #else
1435 	ZVAL_LONG(&stat_blksize,-1);
1436 #endif
1437 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1438 	ZVAL_LONG(&stat_blocks, stat_ssb.sb.st_blocks);
1439 #else
1440 	ZVAL_LONG(&stat_blocks,-1);
1441 #endif
1442 	/* Store numeric indexes in proper order */
1443 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_dev);
1444 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ino);
1445 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mode);
1446 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_nlink);
1447 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_uid);
1448 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_gid);
1449 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_rdev);
1450 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_size);
1451 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_atime);
1452 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mtime);
1453 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ctime);
1454 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blksize);
1455 	zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blocks);
1456 
1457 	/* Store string indexes referencing the same zval*/
1458 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[0], strlen(stat_sb_names[0]), &stat_dev);
1459 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[1], strlen(stat_sb_names[1]), &stat_ino);
1460 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[2], strlen(stat_sb_names[2]), &stat_mode);
1461 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[3], strlen(stat_sb_names[3]), &stat_nlink);
1462 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[4], strlen(stat_sb_names[4]), &stat_uid);
1463 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[5], strlen(stat_sb_names[5]), &stat_gid);
1464 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[6], strlen(stat_sb_names[6]), &stat_rdev);
1465 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[7], strlen(stat_sb_names[7]), &stat_size);
1466 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[8], strlen(stat_sb_names[8]), &stat_atime);
1467 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[9], strlen(stat_sb_names[9]), &stat_mtime);
1468 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[10], strlen(stat_sb_names[10]), &stat_ctime);
1469 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[11], strlen(stat_sb_names[11]), &stat_blksize);
1470 	zend_hash_str_add_new(Z_ARRVAL_P(return_value), stat_sb_names[12], strlen(stat_sb_names[12]), &stat_blocks);
1471 }
1472 
1473 /* {{{ Stat() on a filehandle */
PHP_FUNCTION(fstat)1474 PHP_FUNCTION(fstat)
1475 {
1476 	zval *fp;
1477 	php_stream *stream;
1478 
1479 	ZEND_PARSE_PARAMETERS_START(1, 1)
1480 		Z_PARAM_RESOURCE(fp)
1481 	ZEND_PARSE_PARAMETERS_END();
1482 
1483 	PHP_STREAM_FROM_ZVAL(stream, fp);
1484 
1485 	php_fstat(stream, return_value);
1486 }
1487 /* }}} */
1488 
1489 /* {{{ Copy a file */
PHP_FUNCTION(copy)1490 PHP_FUNCTION(copy)
1491 {
1492 	char *source, *target;
1493 	size_t source_len, target_len;
1494 	zval *zcontext = NULL;
1495 	php_stream_context *context;
1496 
1497 	ZEND_PARSE_PARAMETERS_START(2, 3)
1498 		Z_PARAM_PATH(source, source_len)
1499 		Z_PARAM_PATH(target, target_len)
1500 		Z_PARAM_OPTIONAL
1501 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
1502 	ZEND_PARSE_PARAMETERS_END();
1503 
1504 	if (php_stream_locate_url_wrapper(source, NULL, 0) == &php_plain_files_wrapper && php_check_open_basedir(source)) {
1505 		RETURN_FALSE;
1506 	}
1507 
1508 	context = php_stream_context_from_zval(zcontext, 0);
1509 
1510 	if (php_copy_file_ctx(source, target, 0, context) == SUCCESS) {
1511 		RETURN_TRUE;
1512 	} else {
1513 		RETURN_FALSE;
1514 	}
1515 }
1516 /* }}} */
1517 
1518 /* {{{ php_copy_file */
php_copy_file(const char * src,const char * dest)1519 PHPAPI int php_copy_file(const char *src, const char *dest)
1520 {
1521 	return php_copy_file_ctx(src, dest, 0, NULL);
1522 }
1523 /* }}} */
1524 
1525 /* {{{ php_copy_file_ex */
php_copy_file_ex(const char * src,const char * dest,int src_flg)1526 PHPAPI int php_copy_file_ex(const char *src, const char *dest, int src_flg)
1527 {
1528 	return php_copy_file_ctx(src, dest, src_flg, NULL);
1529 }
1530 /* }}} */
1531 
1532 /* {{{ php_copy_file_ctx */
php_copy_file_ctx(const char * src,const char * dest,int src_flg,php_stream_context * ctx)1533 PHPAPI int php_copy_file_ctx(const char *src, const char *dest, int src_flg, php_stream_context *ctx)
1534 {
1535 	php_stream *srcstream = NULL, *deststream = NULL;
1536 	int ret = FAILURE;
1537 	php_stream_statbuf src_s, dest_s;
1538 	int src_stat_flags = (src_flg & STREAM_DISABLE_OPEN_BASEDIR) ? PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR : 0;
1539 
1540 	switch (php_stream_stat_path_ex(src, src_stat_flags, &src_s, ctx)) {
1541 		case -1:
1542 			/* non-statable stream */
1543 			goto safe_to_copy;
1544 			break;
1545 		case 0:
1546 			break;
1547 		default: /* failed to stat file, does not exist? */
1548 			return ret;
1549 	}
1550 	if (S_ISDIR(src_s.sb.st_mode)) {
1551 		php_error_docref(NULL, E_WARNING, "The first argument to copy() function cannot be a directory");
1552 		return FAILURE;
1553 	}
1554 
1555 	switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET, &dest_s, ctx)) {
1556 		case -1:
1557 			/* non-statable stream */
1558 			goto safe_to_copy;
1559 			break;
1560 		case 0:
1561 			break;
1562 		default: /* failed to stat file, does not exist? */
1563 			return ret;
1564 	}
1565 	if (S_ISDIR(dest_s.sb.st_mode)) {
1566 		php_error_docref(NULL, E_WARNING, "The second argument to copy() function cannot be a directory");
1567 		return FAILURE;
1568 	}
1569 	if (!src_s.sb.st_ino || !dest_s.sb.st_ino) {
1570 		goto no_stat;
1571 	}
1572 	if (src_s.sb.st_ino == dest_s.sb.st_ino && src_s.sb.st_dev == dest_s.sb.st_dev) {
1573 		return ret;
1574 	} else {
1575 		goto safe_to_copy;
1576 	}
1577 no_stat:
1578 	{
1579 		char *sp, *dp;
1580 		int res;
1581 
1582 		if ((sp = expand_filepath(src, NULL)) == NULL) {
1583 			return ret;
1584 		}
1585 		if ((dp = expand_filepath(dest, NULL)) == NULL) {
1586 			efree(sp);
1587 			goto safe_to_copy;
1588 		}
1589 
1590 		res =
1591 #ifndef PHP_WIN32
1592 			!strcmp(sp, dp);
1593 #else
1594 			!strcasecmp(sp, dp);
1595 #endif
1596 
1597 		efree(sp);
1598 		efree(dp);
1599 		if (res) {
1600 			return ret;
1601 		}
1602 	}
1603 safe_to_copy:
1604 
1605 	srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx);
1606 
1607 	if (!srcstream) {
1608 		return ret;
1609 	}
1610 
1611 	deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
1612 
1613 	if (deststream) {
1614 		ret = php_stream_copy_to_stream_ex(srcstream, deststream, PHP_STREAM_COPY_ALL, NULL);
1615 	}
1616 	php_stream_close(srcstream);
1617 	if (deststream) {
1618 		php_stream_close(deststream);
1619 	}
1620 	return ret;
1621 }
1622 /* }}} */
1623 
1624 /* {{{ Binary-safe file read */
PHP_FUNCTION(fread)1625 PHPAPI PHP_FUNCTION(fread)
1626 {
1627 	zval *res;
1628 	zend_long len;
1629 	php_stream *stream;
1630 	zend_string *str;
1631 
1632 	ZEND_PARSE_PARAMETERS_START(2, 2)
1633 		Z_PARAM_RESOURCE(res)
1634 		Z_PARAM_LONG(len)
1635 	ZEND_PARSE_PARAMETERS_END();
1636 
1637 	PHP_STREAM_FROM_ZVAL(stream, res);
1638 
1639 	if (len <= 0) {
1640 		zend_argument_value_error(2, "must be greater than 0");
1641 		RETURN_THROWS();
1642 	}
1643 
1644 	str = php_stream_read_to_str(stream, len);
1645 	if (!str) {
1646 		zval_ptr_dtor_str(return_value);
1647 		RETURN_FALSE;
1648 	}
1649 
1650 	RETURN_STR(str);
1651 }
1652 /* }}} */
1653 
php_fgetcsv_lookup_trailing_spaces(const char * ptr,size_t len)1654 static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t len) /* {{{ */
1655 {
1656 	int inc_len;
1657 	unsigned char last_chars[2] = { 0, 0 };
1658 
1659 	while (len > 0) {
1660 		inc_len = (*ptr == '\0' ? 1 : php_mblen(ptr, len));
1661 		switch (inc_len) {
1662 			case -2:
1663 			case -1:
1664 				inc_len = 1;
1665 				php_mb_reset();
1666 				break;
1667 			case 0:
1668 				goto quit_loop;
1669 			case 1:
1670 			default:
1671 				last_chars[0] = last_chars[1];
1672 				last_chars[1] = *ptr;
1673 				break;
1674 		}
1675 		ptr += inc_len;
1676 		len -= inc_len;
1677 	}
1678 quit_loop:
1679 	switch (last_chars[1]) {
1680 		case '\n':
1681 			if (last_chars[0] == '\r') {
1682 				return ptr - 2;
1683 			}
1684 			ZEND_FALLTHROUGH;
1685 		case '\r':
1686 			return ptr - 1;
1687 	}
1688 	return ptr;
1689 }
1690 /* }}} */
1691 
1692 #define FPUTCSV_FLD_CHK(c) memchr(ZSTR_VAL(field_str), c, ZSTR_LEN(field_str))
1693 
1694 /* {{{ Format line as CSV and write to file pointer */
PHP_FUNCTION(fputcsv)1695 PHP_FUNCTION(fputcsv)
1696 {
1697 	char delimiter = ',';					/* allow this to be set as parameter */
1698 	char enclosure = '"';					/* allow this to be set as parameter */
1699 	int escape_char = (unsigned char) '\\';	/* allow this to be set as parameter */
1700 	php_stream *stream;
1701 	zval *fp = NULL, *fields = NULL;
1702 	ssize_t ret;
1703 	char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
1704 	size_t delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;
1705 	zend_string *eol_str = NULL;
1706 
1707 	ZEND_PARSE_PARAMETERS_START(2, 6)
1708 		Z_PARAM_RESOURCE(fp)
1709 		Z_PARAM_ARRAY(fields)
1710 		Z_PARAM_OPTIONAL
1711 		Z_PARAM_STRING(delimiter_str, delimiter_str_len)
1712 		Z_PARAM_STRING(enclosure_str, enclosure_str_len)
1713 		Z_PARAM_STRING(escape_str, escape_str_len)
1714 		Z_PARAM_STR_OR_NULL(eol_str)
1715 	ZEND_PARSE_PARAMETERS_END();
1716 
1717 	if (delimiter_str != NULL) {
1718 		/* Make sure that there is at least one character in string */
1719 		if (delimiter_str_len != 1) {
1720 			zend_argument_value_error(3, "must be a single character");
1721 			RETURN_THROWS();
1722 		}
1723 
1724 		/* use first character from string */
1725 		delimiter = *delimiter_str;
1726 	}
1727 
1728 	if (enclosure_str != NULL) {
1729 		if (enclosure_str_len != 1) {
1730 			zend_argument_value_error(4, "must be a single character");
1731 			RETURN_THROWS();
1732 		}
1733 		/* use first character from string */
1734 		enclosure = *enclosure_str;
1735 	}
1736 
1737 	if (escape_str != NULL) {
1738 		if (escape_str_len > 1) {
1739 			zend_argument_value_error(5, "must be empty or a single character");
1740 			RETURN_THROWS();
1741 		}
1742 		if (escape_str_len < 1) {
1743 			escape_char = PHP_CSV_NO_ESCAPE;
1744 		} else {
1745 			/* use first character from string */
1746 			escape_char = (unsigned char) *escape_str;
1747 		}
1748 	}
1749 
1750 	PHP_STREAM_FROM_ZVAL(stream, fp);
1751 
1752 	ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str);
1753 	if (ret < 0) {
1754 		RETURN_FALSE;
1755 	}
1756 	RETURN_LONG(ret);
1757 }
1758 /* }}} */
1759 
1760 /* {{{ PHPAPI size_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, zend_string *eol_str) */
php_fputcsv(php_stream * stream,zval * fields,char delimiter,char enclosure,int escape_char,zend_string * eol_str)1761 PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, zend_string *eol_str)
1762 {
1763 	uint32_t count, i = 0;
1764 	size_t ret;
1765 	zval *field_tmp;
1766 	smart_str csvline = {0};
1767 
1768 	ZEND_ASSERT((escape_char >= 0 && escape_char <= UCHAR_MAX) || escape_char == PHP_CSV_NO_ESCAPE);
1769 	count = zend_hash_num_elements(Z_ARRVAL_P(fields));
1770 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(fields), field_tmp) {
1771 		zend_string *tmp_field_str;
1772 		zend_string *field_str = zval_get_tmp_string(field_tmp, &tmp_field_str);
1773 
1774 		/* enclose a field that contains a delimiter, an enclosure character, or a newline */
1775 		if (FPUTCSV_FLD_CHK(delimiter) ||
1776 			FPUTCSV_FLD_CHK(enclosure) ||
1777 			(escape_char != PHP_CSV_NO_ESCAPE && FPUTCSV_FLD_CHK(escape_char)) ||
1778 			FPUTCSV_FLD_CHK('\n') ||
1779 			FPUTCSV_FLD_CHK('\r') ||
1780 			FPUTCSV_FLD_CHK('\t') ||
1781 			FPUTCSV_FLD_CHK(' ')
1782 		) {
1783 			char *ch = ZSTR_VAL(field_str);
1784 			char *end = ch + ZSTR_LEN(field_str);
1785 			int escaped = 0;
1786 
1787 			smart_str_appendc(&csvline, enclosure);
1788 			while (ch < end) {
1789 				if (escape_char != PHP_CSV_NO_ESCAPE && *ch == escape_char) {
1790 					escaped = 1;
1791 				} else if (!escaped && *ch == enclosure) {
1792 					smart_str_appendc(&csvline, enclosure);
1793 				} else {
1794 					escaped = 0;
1795 				}
1796 				smart_str_appendc(&csvline, *ch);
1797 				ch++;
1798 			}
1799 			smart_str_appendc(&csvline, enclosure);
1800 		} else {
1801 			smart_str_append(&csvline, field_str);
1802 		}
1803 
1804 		if (++i != count) {
1805 			smart_str_appendl(&csvline, &delimiter, 1);
1806 		}
1807 		zend_tmp_string_release(tmp_field_str);
1808 	} ZEND_HASH_FOREACH_END();
1809 
1810 	if (eol_str) {
1811 		smart_str_append(&csvline, eol_str);
1812 	} else {
1813 		smart_str_appendc(&csvline, '\n');
1814 	}
1815 	smart_str_0(&csvline);
1816 
1817 	ret = php_stream_write(stream, ZSTR_VAL(csvline.s), ZSTR_LEN(csvline.s));
1818 
1819 	smart_str_free(&csvline);
1820 
1821 	return ret;
1822 }
1823 /* }}} */
1824 
1825 /* {{{ Get line from file pointer and parse for CSV fields */
PHP_FUNCTION(fgetcsv)1826 PHP_FUNCTION(fgetcsv)
1827 {
1828 	char delimiter = ',';	/* allow this to be set as parameter */
1829 	char enclosure = '"';	/* allow this to be set as parameter */
1830 	int escape = (unsigned char) '\\';
1831 
1832 	zend_long len = 0;
1833 	size_t buf_len;
1834 	char *buf;
1835 	php_stream *stream;
1836 
1837 	{
1838 		zval *fd;
1839 		bool len_is_null = 1;
1840 		char *delimiter_str = NULL;
1841 		size_t delimiter_str_len = 0;
1842 		char *enclosure_str = NULL;
1843 		size_t enclosure_str_len = 0;
1844 		char *escape_str = NULL;
1845 		size_t escape_str_len = 0;
1846 
1847 		ZEND_PARSE_PARAMETERS_START(1, 5)
1848 			Z_PARAM_RESOURCE(fd)
1849 			Z_PARAM_OPTIONAL
1850 			Z_PARAM_LONG_OR_NULL(len, len_is_null)
1851 			Z_PARAM_STRING(delimiter_str, delimiter_str_len)
1852 			Z_PARAM_STRING(enclosure_str, enclosure_str_len)
1853 			Z_PARAM_STRING(escape_str, escape_str_len)
1854 		ZEND_PARSE_PARAMETERS_END();
1855 
1856 		if (delimiter_str != NULL) {
1857 			/* Make sure that there is at least one character in string */
1858 			if (delimiter_str_len != 1) {
1859 				zend_argument_value_error(3, "must be a single character");
1860 				RETURN_THROWS();
1861 			}
1862 
1863 			/* use first character from string */
1864 			delimiter = delimiter_str[0];
1865 		}
1866 
1867 		if (enclosure_str != NULL) {
1868 			if (enclosure_str_len != 1) {
1869 				zend_argument_value_error(4, "must be a single character");
1870 				RETURN_THROWS();
1871 			}
1872 
1873 			/* use first character from string */
1874 			enclosure = enclosure_str[0];
1875 		}
1876 
1877 		if (escape_str != NULL) {
1878 			if (escape_str_len > 1) {
1879 				zend_argument_value_error(5, "must be empty or a single character");
1880 				RETURN_THROWS();
1881 			}
1882 
1883 			if (escape_str_len < 1) {
1884 				escape = PHP_CSV_NO_ESCAPE;
1885 			} else {
1886 				escape = (unsigned char) escape_str[0];
1887 			}
1888 		}
1889 
1890 		if (len_is_null || len == 0) {
1891 			len = -1;
1892 		} else if (len < 0) {
1893 			zend_argument_value_error(2, "must be a greater than or equal to 0");
1894 			RETURN_THROWS();
1895 		}
1896 
1897 		PHP_STREAM_FROM_ZVAL(stream, fd);
1898 	}
1899 
1900 	if (len < 0) {
1901 		if ((buf = php_stream_get_line(stream, NULL, 0, &buf_len)) == NULL) {
1902 			RETURN_FALSE;
1903 		}
1904 	} else {
1905 		buf = emalloc(len + 1);
1906 		if (php_stream_get_line(stream, buf, len + 1, &buf_len) == NULL) {
1907 			efree(buf);
1908 			RETURN_FALSE;
1909 		}
1910 	}
1911 
1912 	HashTable *values = php_fgetcsv(stream, delimiter, enclosure, escape, buf_len, buf);
1913 	if (values == NULL) {
1914 		values = php_bc_fgetcsv_empty_line();
1915 	}
1916 	RETURN_ARR(values);
1917 }
1918 /* }}} */
1919 
php_bc_fgetcsv_empty_line(void)1920 PHPAPI HashTable *php_bc_fgetcsv_empty_line(void)
1921 {
1922 	HashTable *values = zend_new_array(1);
1923 	zval tmp;
1924 	ZVAL_NULL(&tmp);
1925 	zend_hash_next_index_insert(values, &tmp);
1926 	return values;
1927 }
1928 
php_fgetcsv(php_stream * stream,char delimiter,char enclosure,int escape_char,size_t buf_len,char * buf)1929 PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure, int escape_char, size_t buf_len, char *buf) /* {{{ */
1930 {
1931 	char *temp, *bptr, *line_end, *limit;
1932 	size_t temp_len, line_end_len;
1933 	int inc_len;
1934 	bool first_field = true;
1935 
1936 	ZEND_ASSERT((escape_char >= 0 && escape_char <= UCHAR_MAX) || escape_char == PHP_CSV_NO_ESCAPE);
1937 
1938 	/* initialize internal state */
1939 	php_mb_reset();
1940 
1941 	/* Now into new section that parses buf for delimiter/enclosure fields */
1942 
1943 	/* Strip trailing space from buf, saving end of line in case required for enclosure field */
1944 
1945 	bptr = buf;
1946 	line_end = limit = (char *)php_fgetcsv_lookup_trailing_spaces(buf, buf_len);
1947 	line_end_len = buf_len - (size_t)(limit - buf);
1948 
1949 	/* reserve workspace for building each individual field */
1950 	temp_len = buf_len;
1951 	temp = emalloc(temp_len + line_end_len + 1);
1952 
1953 	/* Initialize values HashTable */
1954 	HashTable *values = zend_new_array(0);
1955 
1956 	/* Main loop to read CSV fields */
1957 	/* NB this routine will return NULL for a blank line */
1958 	do {
1959 		char *comp_end, *hunk_begin;
1960 		char *tptr = temp;
1961 
1962 		inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
1963 		if (inc_len == 1) {
1964 			char *tmp = bptr;
1965 			while ((*tmp != delimiter) && isspace((int)*(unsigned char *)tmp)) {
1966 				tmp++;
1967 			}
1968 			if (*tmp == enclosure && tmp < limit) {
1969 				bptr = tmp;
1970 			}
1971 		}
1972 
1973 		if (first_field && bptr == line_end) {
1974 			zend_array_destroy(values);
1975 			values = NULL;
1976 			break;
1977 		}
1978 		first_field = false;
1979 		/* 2. Read field, leaving bptr pointing at start of next field */
1980 		if (inc_len != 0 && *bptr == enclosure) {
1981 			int state = 0;
1982 
1983 			bptr++;	/* move on to first character in field */
1984 			hunk_begin = bptr;
1985 
1986 			/* 2A. handle enclosure delimited field */
1987 			for (;;) {
1988 				switch (inc_len) {
1989 					case 0:
1990 						switch (state) {
1991 							case 2:
1992 								tptr = zend_mempcpy(tptr, hunk_begin, (bptr - hunk_begin - 1));
1993 								hunk_begin = bptr;
1994 								goto quit_loop_2;
1995 
1996 							case 1:
1997 								tptr = zend_mempcpy(tptr, hunk_begin, (bptr - hunk_begin));
1998 								hunk_begin = bptr;
1999 								ZEND_FALLTHROUGH;
2000 
2001 							case 0: {
2002 								if (hunk_begin != line_end) {
2003 									tptr = zend_mempcpy(tptr, hunk_begin, (bptr - hunk_begin));
2004 									hunk_begin = bptr;
2005 								}
2006 
2007 								/* add the embedded line end to the field */
2008 								tptr = zend_mempcpy(tptr, line_end, line_end_len);
2009 
2010 								/* nothing can be fetched if stream is NULL (e.g. str_getcsv()) */
2011 								if (stream == NULL) {
2012 									/* the enclosure is unterminated */
2013 									if (bptr > limit) {
2014 										/* if the line ends with enclosure, we need to go back by
2015 										 * one character so the \0 character is not copied. */
2016 										if (hunk_begin == bptr) {
2017 											--hunk_begin;
2018 										}
2019 										--bptr;
2020 									}
2021 									goto quit_loop_2;
2022 								}
2023 
2024 								size_t new_len;
2025 								char *new_buf = php_stream_get_line(stream, NULL, 0, &new_len);
2026 								if (!new_buf) {
2027 									/* we've got an unterminated enclosure,
2028 									 * assign all the data from the start of
2029 									 * the enclosure to end of data to the
2030 									 * last element */
2031 									if (bptr > limit) {
2032 										/* if the line ends with enclosure, we need to go back by
2033 										 * one character so the \0 character is not copied. */
2034 										if (hunk_begin == bptr) {
2035 											--hunk_begin;
2036 										}
2037 										--bptr;
2038 									}
2039 									goto quit_loop_2;
2040 								}
2041 
2042 								temp_len += new_len;
2043 								char *new_temp = erealloc(temp, temp_len);
2044 								tptr = new_temp + (size_t)(tptr - temp);
2045 								temp = new_temp;
2046 
2047 								efree(buf);
2048 								buf_len = new_len;
2049 								bptr = buf = new_buf;
2050 								hunk_begin = buf;
2051 
2052 								line_end = limit = (char *)php_fgetcsv_lookup_trailing_spaces(buf, buf_len);
2053 								line_end_len = buf_len - (size_t)(limit - buf);
2054 
2055 								state = 0;
2056 							} break;
2057 						}
2058 						break;
2059 
2060 					case -2:
2061 					case -1:
2062 						php_mb_reset();
2063 						ZEND_FALLTHROUGH;
2064 					case 1:
2065 						/* we need to determine if the enclosure is
2066 						 * 'real' or is it escaped */
2067 						switch (state) {
2068 							case 1: /* escaped */
2069 								bptr++;
2070 								state = 0;
2071 								break;
2072 							case 2: /* embedded enclosure ? let's check it */
2073 								if (*bptr != enclosure) {
2074 									/* real enclosure */
2075 									tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin - 1);
2076 									hunk_begin = bptr;
2077 									goto quit_loop_2;
2078 								}
2079 								tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2080 								bptr++;
2081 								hunk_begin = bptr;
2082 								state = 0;
2083 								break;
2084 							default:
2085 								if (*bptr == enclosure) {
2086 									state = 2;
2087 								} else if (escape_char != PHP_CSV_NO_ESCAPE && *bptr == escape_char) {
2088 									state = 1;
2089 								}
2090 								bptr++;
2091 								break;
2092 						}
2093 						break;
2094 
2095 					default:
2096 						switch (state) {
2097 							case 2:
2098 								/* real enclosure */
2099 								tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin - 1);
2100 								hunk_begin = bptr;
2101 								goto quit_loop_2;
2102 							case 1:
2103 								bptr += inc_len;
2104 								tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2105 								hunk_begin = bptr;
2106 								state = 0;
2107 								break;
2108 							default:
2109 								bptr += inc_len;
2110 								break;
2111 						}
2112 						break;
2113 				}
2114 				inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2115 			}
2116 
2117 		quit_loop_2:
2118 			/* look up for a delimiter */
2119 			for (;;) {
2120 				switch (inc_len) {
2121 					case 0:
2122 						goto quit_loop_3;
2123 
2124 					case -2:
2125 					case -1:
2126 						inc_len = 1;
2127 						php_mb_reset();
2128 						ZEND_FALLTHROUGH;
2129 					case 1:
2130 						if (*bptr == delimiter) {
2131 							goto quit_loop_3;
2132 						}
2133 						break;
2134 					default:
2135 						break;
2136 				}
2137 				bptr += inc_len;
2138 				inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2139 			}
2140 
2141 		quit_loop_3:
2142 			tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2143 			bptr += inc_len;
2144 			comp_end = tptr;
2145 		} else {
2146 			/* 2B. Handle non-enclosure field */
2147 
2148 			hunk_begin = bptr;
2149 
2150 			for (;;) {
2151 				switch (inc_len) {
2152 					case 0:
2153 						goto quit_loop_4;
2154 					case -2:
2155 					case -1:
2156 						inc_len = 1;
2157 						php_mb_reset();
2158 						ZEND_FALLTHROUGH;
2159 					case 1:
2160 						if (*bptr == delimiter) {
2161 							goto quit_loop_4;
2162 						}
2163 						break;
2164 					default:
2165 						break;
2166 				}
2167 				bptr += inc_len;
2168 				inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2169 			}
2170 		quit_loop_4:
2171 			tptr = zend_mempcpy(tptr, hunk_begin, bptr - hunk_begin);
2172 
2173 			comp_end = (char *)php_fgetcsv_lookup_trailing_spaces(temp, tptr - temp);
2174 			if (*bptr == delimiter) {
2175 				bptr++;
2176 			}
2177 		}
2178 
2179 		/* 3. Now pass our field back to php */
2180 		*comp_end = '\0';
2181 
2182 		zval z_tmp;
2183 		ZVAL_STRINGL(&z_tmp, temp, comp_end - temp);
2184 		zend_hash_next_index_insert(values, &z_tmp);
2185 	} while (inc_len > 0);
2186 
2187 	efree(temp);
2188 	if (stream) {
2189 		efree(buf);
2190 	}
2191 
2192 	return values;
2193 }
2194 /* }}} */
2195 
2196 /* {{{ Return the resolved path */
PHP_FUNCTION(realpath)2197 PHP_FUNCTION(realpath)
2198 {
2199 	char *filename;
2200 	size_t filename_len;
2201 	char resolved_path_buff[MAXPATHLEN];
2202 
2203 	ZEND_PARSE_PARAMETERS_START(1, 1)
2204 		Z_PARAM_PATH(filename, filename_len)
2205 	ZEND_PARSE_PARAMETERS_END();
2206 
2207 	if (VCWD_REALPATH(filename, resolved_path_buff)) {
2208 		if (php_check_open_basedir(resolved_path_buff)) {
2209 			RETURN_FALSE;
2210 		}
2211 
2212 #ifdef ZTS
2213 		if (VCWD_ACCESS(resolved_path_buff, F_OK)) {
2214 			RETURN_FALSE;
2215 		}
2216 #endif
2217 		RETURN_STRING(resolved_path_buff);
2218 	} else {
2219 		RETURN_FALSE;
2220 	}
2221 }
2222 /* }}} */
2223 
2224 /* See http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.2 */
2225 #define PHP_META_HTML401_CHARS "-_.:"
2226 
2227 /* {{{ php_next_meta_token
2228    Tokenizes an HTML file for get_meta_tags */
php_next_meta_token(php_meta_tags_data * md)2229 php_meta_tags_token php_next_meta_token(php_meta_tags_data *md)
2230 {
2231 	int ch = 0, compliment;
2232 	char buff[META_DEF_BUFSIZE + 1];
2233 
2234 	memset((void *)buff, 0, META_DEF_BUFSIZE + 1);
2235 
2236 	while (md->ulc || (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)))) {
2237 		if (php_stream_eof(md->stream)) {
2238 			break;
2239 		}
2240 
2241 		if (md->ulc) {
2242 			ch = md->lc;
2243 			md->ulc = 0;
2244 		}
2245 
2246 		switch (ch) {
2247 			case '<':
2248 				return TOK_OPENTAG;
2249 				break;
2250 
2251 			case '>':
2252 				return TOK_CLOSETAG;
2253 				break;
2254 
2255 			case '=':
2256 				return TOK_EQUAL;
2257 				break;
2258 			case '/':
2259 				return TOK_SLASH;
2260 				break;
2261 
2262 			case '\'':
2263 			case '"':
2264 				compliment = ch;
2265 				md->token_len = 0;
2266 				while (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)) && ch != compliment && ch != '<' && ch != '>') {
2267 					buff[(md->token_len)++] = ch;
2268 
2269 					if (md->token_len == META_DEF_BUFSIZE) {
2270 						break;
2271 					}
2272 				}
2273 
2274 				if (ch == '<' || ch == '>') {
2275 					/* Was just an apostrophe */
2276 					md->ulc = 1;
2277 					md->lc = ch;
2278 				}
2279 
2280 				/* We don't need to alloc unless we are in a meta tag */
2281 				if (md->in_meta) {
2282 					md->token_data = (char *) emalloc(md->token_len + 1);
2283 					memcpy(md->token_data, buff, md->token_len+1);
2284 				}
2285 
2286 				return TOK_STRING;
2287 				break;
2288 
2289 			case '\n':
2290 			case '\r':
2291 			case '\t':
2292 				break;
2293 
2294 			case ' ':
2295 				return TOK_SPACE;
2296 				break;
2297 
2298 			default:
2299 				if (isalnum(ch)) {
2300 					md->token_len = 0;
2301 					buff[(md->token_len)++] = ch;
2302 					while (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)) && (isalnum(ch) || strchr(PHP_META_HTML401_CHARS, ch))) {
2303 						buff[(md->token_len)++] = ch;
2304 
2305 						if (md->token_len == META_DEF_BUFSIZE) {
2306 							break;
2307 						}
2308 					}
2309 
2310 					/* This is ugly, but we have to replace ungetc */
2311 					if (!isalpha(ch) && ch != '-') {
2312 						md->ulc = 1;
2313 						md->lc = ch;
2314 					}
2315 
2316 					md->token_data = (char *) emalloc(md->token_len + 1);
2317 					memcpy(md->token_data, buff, md->token_len+1);
2318 
2319 					return TOK_ID;
2320 				} else {
2321 					return TOK_OTHER;
2322 				}
2323 				break;
2324 		}
2325 	}
2326 
2327 	return TOK_EOF;
2328 }
2329 /* }}} */
2330 
2331 #ifdef HAVE_FNMATCH
2332 /* {{{ Match filename against pattern */
PHP_FUNCTION(fnmatch)2333 PHP_FUNCTION(fnmatch)
2334 {
2335 	char *pattern, *filename;
2336 	size_t pattern_len, filename_len;
2337 	zend_long flags = 0;
2338 
2339 	ZEND_PARSE_PARAMETERS_START(2, 3)
2340 		Z_PARAM_PATH(pattern, pattern_len)
2341 		Z_PARAM_PATH(filename, filename_len)
2342 		Z_PARAM_OPTIONAL
2343 		Z_PARAM_LONG(flags)
2344 	ZEND_PARSE_PARAMETERS_END();
2345 
2346 	if (filename_len >= MAXPATHLEN) {
2347 		php_error_docref(NULL, E_WARNING, "Filename exceeds the maximum allowed length of %d characters", MAXPATHLEN);
2348 		RETURN_FALSE;
2349 	}
2350 	if (pattern_len >= MAXPATHLEN) {
2351 		php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
2352 		RETURN_FALSE;
2353 	}
2354 
2355 	RETURN_BOOL( ! fnmatch( pattern, filename, (int)flags ));
2356 }
2357 /* }}} */
2358 #endif
2359 
2360 /* {{{ Returns directory path used for temporary files */
PHP_FUNCTION(sys_get_temp_dir)2361 PHP_FUNCTION(sys_get_temp_dir)
2362 {
2363 	ZEND_PARSE_PARAMETERS_NONE();
2364 
2365 	RETURN_STRING((char *)php_get_temporary_directory());
2366 }
2367 /* }}} */
2368