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