xref: /php-src/ext/standard/file.c (revision c573b822)
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_TO_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_TO_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 < 0 || flags > (PHP_FILE_USE_INCLUDE_PATH | PHP_FILE_IGNORE_NEW_LINES | PHP_FILE_SKIP_EMPTY_LINES | PHP_FILE_NO_DEFAULT_CONTEXT)) {
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 left overs 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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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_TO_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 
1543 	switch (php_stream_stat_path_ex(src, 0, &src_s, ctx)) {
1544 		case -1:
1545 			/* non-statable stream */
1546 			goto safe_to_copy;
1547 			break;
1548 		case 0:
1549 			break;
1550 		default: /* failed to stat file, does not exist? */
1551 			return ret;
1552 	}
1553 	if (S_ISDIR(src_s.sb.st_mode)) {
1554 		php_error_docref(NULL, E_WARNING, "The first argument to copy() function cannot be a directory");
1555 		return FAILURE;
1556 	}
1557 
1558 	switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET, &dest_s, ctx)) {
1559 		case -1:
1560 			/* non-statable stream */
1561 			goto safe_to_copy;
1562 			break;
1563 		case 0:
1564 			break;
1565 		default: /* failed to stat file, does not exist? */
1566 			return ret;
1567 	}
1568 	if (S_ISDIR(dest_s.sb.st_mode)) {
1569 		php_error_docref(NULL, E_WARNING, "The second argument to copy() function cannot be a directory");
1570 		return FAILURE;
1571 	}
1572 	if (!src_s.sb.st_ino || !dest_s.sb.st_ino) {
1573 		goto no_stat;
1574 	}
1575 	if (src_s.sb.st_ino == dest_s.sb.st_ino && src_s.sb.st_dev == dest_s.sb.st_dev) {
1576 		return ret;
1577 	} else {
1578 		goto safe_to_copy;
1579 	}
1580 no_stat:
1581 	{
1582 		char *sp, *dp;
1583 		int res;
1584 
1585 		if ((sp = expand_filepath(src, NULL)) == NULL) {
1586 			return ret;
1587 		}
1588 		if ((dp = expand_filepath(dest, NULL)) == NULL) {
1589 			efree(sp);
1590 			goto safe_to_copy;
1591 		}
1592 
1593 		res =
1594 #ifndef PHP_WIN32
1595 			!strcmp(sp, dp);
1596 #else
1597 			!strcasecmp(sp, dp);
1598 #endif
1599 
1600 		efree(sp);
1601 		efree(dp);
1602 		if (res) {
1603 			return ret;
1604 		}
1605 	}
1606 safe_to_copy:
1607 
1608 	srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx);
1609 
1610 	if (!srcstream) {
1611 		return ret;
1612 	}
1613 
1614 	deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx);
1615 
1616 	if (srcstream && deststream) {
1617 		ret = php_stream_copy_to_stream_ex(srcstream, deststream, PHP_STREAM_COPY_ALL, NULL);
1618 	}
1619 	if (srcstream) {
1620 		php_stream_close(srcstream);
1621 	}
1622 	if (deststream) {
1623 		php_stream_close(deststream);
1624 	}
1625 	return ret;
1626 }
1627 /* }}} */
1628 
1629 /* {{{ Binary-safe file read */
PHP_FUNCTION(fread)1630 PHPAPI PHP_FUNCTION(fread)
1631 {
1632 	zval *res;
1633 	zend_long len;
1634 	php_stream *stream;
1635 	zend_string *str;
1636 
1637 	ZEND_PARSE_PARAMETERS_START(2, 2)
1638 		Z_PARAM_RESOURCE(res)
1639 		Z_PARAM_LONG(len)
1640 	ZEND_PARSE_PARAMETERS_END();
1641 
1642 	PHP_STREAM_TO_ZVAL(stream, res);
1643 
1644 	if (len <= 0) {
1645 		zend_argument_value_error(2, "must be greater than 0");
1646 		RETURN_THROWS();
1647 	}
1648 
1649 	str = php_stream_read_to_str(stream, len);
1650 	if (!str) {
1651 		zval_ptr_dtor_str(return_value);
1652 		RETURN_FALSE;
1653 	}
1654 
1655 	RETURN_STR(str);
1656 }
1657 /* }}} */
1658 
php_fgetcsv_lookup_trailing_spaces(const char * ptr,size_t len)1659 static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t len) /* {{{ */
1660 {
1661 	int inc_len;
1662 	unsigned char last_chars[2] = { 0, 0 };
1663 
1664 	while (len > 0) {
1665 		inc_len = (*ptr == '\0' ? 1 : php_mblen(ptr, len));
1666 		switch (inc_len) {
1667 			case -2:
1668 			case -1:
1669 				inc_len = 1;
1670 				php_mb_reset();
1671 				break;
1672 			case 0:
1673 				goto quit_loop;
1674 			case 1:
1675 			default:
1676 				last_chars[0] = last_chars[1];
1677 				last_chars[1] = *ptr;
1678 				break;
1679 		}
1680 		ptr += inc_len;
1681 		len -= inc_len;
1682 	}
1683 quit_loop:
1684 	switch (last_chars[1]) {
1685 		case '\n':
1686 			if (last_chars[0] == '\r') {
1687 				return ptr - 2;
1688 			}
1689 			ZEND_FALLTHROUGH;
1690 		case '\r':
1691 			return ptr - 1;
1692 	}
1693 	return ptr;
1694 }
1695 /* }}} */
1696 
1697 #define FPUTCSV_FLD_CHK(c) memchr(ZSTR_VAL(field_str), c, ZSTR_LEN(field_str))
1698 
1699 /* {{{ Format line as CSV and write to file pointer */
PHP_FUNCTION(fputcsv)1700 PHP_FUNCTION(fputcsv)
1701 {
1702 	char delimiter = ',';					/* allow this to be set as parameter */
1703 	char enclosure = '"';					/* allow this to be set as parameter */
1704 	int escape_char = (unsigned char) '\\';	/* allow this to be set as parameter */
1705 	php_stream *stream;
1706 	zval *fp = NULL, *fields = NULL;
1707 	ssize_t ret;
1708 	char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
1709 	size_t delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;
1710 	zend_string *eol_str = NULL;
1711 
1712 	ZEND_PARSE_PARAMETERS_START(2, 6)
1713 		Z_PARAM_RESOURCE(fp)
1714 		Z_PARAM_ARRAY(fields)
1715 		Z_PARAM_OPTIONAL
1716 		Z_PARAM_STRING(delimiter_str, delimiter_str_len)
1717 		Z_PARAM_STRING(enclosure_str, enclosure_str_len)
1718 		Z_PARAM_STRING(escape_str, escape_str_len)
1719 		Z_PARAM_STR_OR_NULL(eol_str)
1720 	ZEND_PARSE_PARAMETERS_END();
1721 
1722 	if (delimiter_str != NULL) {
1723 		/* Make sure that there is at least one character in string */
1724 		if (delimiter_str_len != 1) {
1725 			zend_argument_value_error(3, "must be a single character");
1726 			RETURN_THROWS();
1727 		}
1728 
1729 		/* use first character from string */
1730 		delimiter = *delimiter_str;
1731 	}
1732 
1733 	if (enclosure_str != NULL) {
1734 		if (enclosure_str_len != 1) {
1735 			zend_argument_value_error(4, "must be a single character");
1736 			RETURN_THROWS();
1737 		}
1738 		/* use first character from string */
1739 		enclosure = *enclosure_str;
1740 	}
1741 
1742 	if (escape_str != NULL) {
1743 		if (escape_str_len > 1) {
1744 			zend_argument_value_error(5, "must be empty or a single character");
1745 			RETURN_THROWS();
1746 		}
1747 		if (escape_str_len < 1) {
1748 			escape_char = PHP_CSV_NO_ESCAPE;
1749 		} else {
1750 			/* use first character from string */
1751 			escape_char = (unsigned char) *escape_str;
1752 		}
1753 	}
1754 
1755 	PHP_STREAM_TO_ZVAL(stream, fp);
1756 
1757 	ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str);
1758 	if (ret < 0) {
1759 		RETURN_FALSE;
1760 	}
1761 	RETURN_LONG(ret);
1762 }
1763 /* }}} */
1764 
1765 /* {{{ 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)1766 PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, zend_string *eol_str)
1767 {
1768 	uint32_t count, i = 0;
1769 	size_t ret;
1770 	zval *field_tmp;
1771 	smart_str csvline = {0};
1772 
1773 	ZEND_ASSERT((escape_char >= 0 && escape_char <= UCHAR_MAX) || escape_char == PHP_CSV_NO_ESCAPE);
1774 	count = zend_hash_num_elements(Z_ARRVAL_P(fields));
1775 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(fields), field_tmp) {
1776 		zend_string *tmp_field_str;
1777 		zend_string *field_str = zval_get_tmp_string(field_tmp, &tmp_field_str);
1778 
1779 		/* enclose a field that contains a delimiter, an enclosure character, or a newline */
1780 		if (FPUTCSV_FLD_CHK(delimiter) ||
1781 			FPUTCSV_FLD_CHK(enclosure) ||
1782 			(escape_char != PHP_CSV_NO_ESCAPE && FPUTCSV_FLD_CHK(escape_char)) ||
1783 			FPUTCSV_FLD_CHK('\n') ||
1784 			FPUTCSV_FLD_CHK('\r') ||
1785 			FPUTCSV_FLD_CHK('\t') ||
1786 			FPUTCSV_FLD_CHK(' ')
1787 		) {
1788 			char *ch = ZSTR_VAL(field_str);
1789 			char *end = ch + ZSTR_LEN(field_str);
1790 			int escaped = 0;
1791 
1792 			smart_str_appendc(&csvline, enclosure);
1793 			while (ch < end) {
1794 				if (escape_char != PHP_CSV_NO_ESCAPE && *ch == escape_char) {
1795 					escaped = 1;
1796 				} else if (!escaped && *ch == enclosure) {
1797 					smart_str_appendc(&csvline, enclosure);
1798 				} else {
1799 					escaped = 0;
1800 				}
1801 				smart_str_appendc(&csvline, *ch);
1802 				ch++;
1803 			}
1804 			smart_str_appendc(&csvline, enclosure);
1805 		} else {
1806 			smart_str_append(&csvline, field_str);
1807 		}
1808 
1809 		if (++i != count) {
1810 			smart_str_appendl(&csvline, &delimiter, 1);
1811 		}
1812 		zend_tmp_string_release(tmp_field_str);
1813 	} ZEND_HASH_FOREACH_END();
1814 
1815 	if (eol_str) {
1816 		smart_str_append(&csvline, eol_str);
1817 	} else {
1818 		smart_str_appendc(&csvline, '\n');
1819 	}
1820 	smart_str_0(&csvline);
1821 
1822 	ret = php_stream_write(stream, ZSTR_VAL(csvline.s), ZSTR_LEN(csvline.s));
1823 
1824 	smart_str_free(&csvline);
1825 
1826 	return ret;
1827 }
1828 /* }}} */
1829 
1830 /* {{{ Get line from file pointer and parse for CSV fields */
PHP_FUNCTION(fgetcsv)1831 PHP_FUNCTION(fgetcsv)
1832 {
1833 	char delimiter = ',';	/* allow this to be set as parameter */
1834 	char enclosure = '"';	/* allow this to be set as parameter */
1835 	int escape = (unsigned char) '\\';
1836 
1837 	zend_long len = 0;
1838 	size_t buf_len;
1839 	char *buf;
1840 	php_stream *stream;
1841 
1842 	{
1843 		zval *fd;
1844 		bool len_is_null = 1;
1845 		char *delimiter_str = NULL;
1846 		size_t delimiter_str_len = 0;
1847 		char *enclosure_str = NULL;
1848 		size_t enclosure_str_len = 0;
1849 		char *escape_str = NULL;
1850 		size_t escape_str_len = 0;
1851 
1852 		ZEND_PARSE_PARAMETERS_START(1, 5)
1853 			Z_PARAM_RESOURCE(fd)
1854 			Z_PARAM_OPTIONAL
1855 			Z_PARAM_LONG_OR_NULL(len, len_is_null)
1856 			Z_PARAM_STRING(delimiter_str, delimiter_str_len)
1857 			Z_PARAM_STRING(enclosure_str, enclosure_str_len)
1858 			Z_PARAM_STRING(escape_str, escape_str_len)
1859 		ZEND_PARSE_PARAMETERS_END();
1860 
1861 		if (delimiter_str != NULL) {
1862 			/* Make sure that there is at least one character in string */
1863 			if (delimiter_str_len != 1) {
1864 				zend_argument_value_error(3, "must be a single character");
1865 				RETURN_THROWS();
1866 			}
1867 
1868 			/* use first character from string */
1869 			delimiter = delimiter_str[0];
1870 		}
1871 
1872 		if (enclosure_str != NULL) {
1873 			if (enclosure_str_len != 1) {
1874 				zend_argument_value_error(4, "must be a single character");
1875 				RETURN_THROWS();
1876 			}
1877 
1878 			/* use first character from string */
1879 			enclosure = enclosure_str[0];
1880 		}
1881 
1882 		if (escape_str != NULL) {
1883 			if (escape_str_len > 1) {
1884 				zend_argument_value_error(5, "must be empty or a single character");
1885 				RETURN_THROWS();
1886 			}
1887 
1888 			if (escape_str_len < 1) {
1889 				escape = PHP_CSV_NO_ESCAPE;
1890 			} else {
1891 				escape = (unsigned char) escape_str[0];
1892 			}
1893 		}
1894 
1895 		if (len_is_null || len == 0) {
1896 			len = -1;
1897 		} else if (len < 0) {
1898 			zend_argument_value_error(2, "must be a greater than or equal to 0");
1899 			RETURN_THROWS();
1900 		}
1901 
1902 		PHP_STREAM_TO_ZVAL(stream, fd);
1903 	}
1904 
1905 	if (len < 0) {
1906 		if ((buf = php_stream_get_line(stream, NULL, 0, &buf_len)) == NULL) {
1907 			RETURN_FALSE;
1908 		}
1909 	} else {
1910 		buf = emalloc(len + 1);
1911 		if (php_stream_get_line(stream, buf, len + 1, &buf_len) == NULL) {
1912 			efree(buf);
1913 			RETURN_FALSE;
1914 		}
1915 	}
1916 
1917 	HashTable *values = php_fgetcsv(stream, delimiter, enclosure, escape, buf_len, buf);
1918 	if (values == NULL) {
1919 		values = php_bc_fgetcsv_empty_line();
1920 	}
1921 	RETURN_ARR(values);
1922 }
1923 /* }}} */
1924 
php_bc_fgetcsv_empty_line(void)1925 PHPAPI HashTable *php_bc_fgetcsv_empty_line(void)
1926 {
1927 	HashTable *values = zend_new_array(1);
1928 	zval tmp;
1929 	ZVAL_NULL(&tmp);
1930 	zend_hash_next_index_insert(values, &tmp);
1931 	return values;
1932 }
1933 
php_fgetcsv(php_stream * stream,char delimiter,char enclosure,int escape_char,size_t buf_len,char * buf)1934 PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure, int escape_char, size_t buf_len, char *buf) /* {{{ */
1935 {
1936 	char *temp, *bptr, *line_end, *limit;
1937 	size_t temp_len, line_end_len;
1938 	int inc_len;
1939 	bool first_field = true;
1940 
1941 	ZEND_ASSERT((escape_char >= 0 && escape_char <= UCHAR_MAX) || escape_char == PHP_CSV_NO_ESCAPE);
1942 
1943 	/* initialize internal state */
1944 	php_mb_reset();
1945 
1946 	/* Now into new section that parses buf for delimiter/enclosure fields */
1947 
1948 	/* Strip trailing space from buf, saving end of line in case required for enclosure field */
1949 
1950 	bptr = buf;
1951 	line_end = limit = (char *)php_fgetcsv_lookup_trailing_spaces(buf, buf_len);
1952 	line_end_len = buf_len - (size_t)(limit - buf);
1953 
1954 	/* reserve workspace for building each individual field */
1955 	temp_len = buf_len;
1956 	temp = emalloc(temp_len + line_end_len + 1);
1957 
1958 	/* Initialize values HashTable */
1959 	HashTable *values = zend_new_array(0);
1960 
1961 	/* Main loop to read CSV fields */
1962 	/* NB this routine will return NULL for a blank line */
1963 	do {
1964 		char *comp_end, *hunk_begin;
1965 		char *tptr = temp;
1966 
1967 		inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
1968 		if (inc_len == 1) {
1969 			char *tmp = bptr;
1970 			while ((*tmp != delimiter) && isspace((int)*(unsigned char *)tmp)) {
1971 				tmp++;
1972 			}
1973 			if (*tmp == enclosure) {
1974 				bptr = tmp;
1975 			}
1976 		}
1977 
1978 		if (first_field && bptr == line_end) {
1979 			zend_array_destroy(values);
1980 			values = NULL;
1981 			break;
1982 		}
1983 		first_field = false;
1984 		/* 2. Read field, leaving bptr pointing at start of next field */
1985 		if (inc_len != 0 && *bptr == enclosure) {
1986 			int state = 0;
1987 
1988 			bptr++;	/* move on to first character in field */
1989 			hunk_begin = bptr;
1990 
1991 			/* 2A. handle enclosure delimited field */
1992 			for (;;) {
1993 				switch (inc_len) {
1994 					case 0:
1995 						switch (state) {
1996 							case 2:
1997 								memcpy(tptr, hunk_begin, bptr - hunk_begin - 1);
1998 								tptr += (bptr - hunk_begin - 1);
1999 								hunk_begin = bptr;
2000 								goto quit_loop_2;
2001 
2002 							case 1:
2003 								memcpy(tptr, hunk_begin, bptr - hunk_begin);
2004 								tptr += (bptr - hunk_begin);
2005 								hunk_begin = bptr;
2006 								ZEND_FALLTHROUGH;
2007 
2008 							case 0: {
2009 								if (hunk_begin != line_end) {
2010 									memcpy(tptr, hunk_begin, bptr - hunk_begin);
2011 									tptr += (bptr - hunk_begin);
2012 									hunk_begin = bptr;
2013 								}
2014 
2015 								/* add the embedded line end to the field */
2016 								memcpy(tptr, line_end, line_end_len);
2017 								tptr += line_end_len;
2018 
2019 								if (stream == NULL) {
2020 									goto quit_loop_2;
2021 								}
2022 
2023 								size_t new_len;
2024 								char *new_buf = php_stream_get_line(stream, NULL, 0, &new_len);
2025 								if (!new_buf) {
2026 									/* we've got an unterminated enclosure,
2027 									 * assign all the data from the start of
2028 									 * the enclosure to end of data to the
2029 									 * last element */
2030 									goto quit_loop_2;
2031 								}
2032 
2033 								temp_len += new_len;
2034 								char *new_temp = erealloc(temp, temp_len);
2035 								tptr = new_temp + (size_t)(tptr - temp);
2036 								temp = new_temp;
2037 
2038 								efree(buf);
2039 								buf_len = new_len;
2040 								bptr = buf = new_buf;
2041 								hunk_begin = buf;
2042 
2043 								line_end = limit = (char *)php_fgetcsv_lookup_trailing_spaces(buf, buf_len);
2044 								line_end_len = buf_len - (size_t)(limit - buf);
2045 
2046 								state = 0;
2047 							} break;
2048 						}
2049 						break;
2050 
2051 					case -2:
2052 					case -1:
2053 						php_mb_reset();
2054 						ZEND_FALLTHROUGH;
2055 					case 1:
2056 						/* we need to determine if the enclosure is
2057 						 * 'real' or is it escaped */
2058 						switch (state) {
2059 							case 1: /* escaped */
2060 								bptr++;
2061 								state = 0;
2062 								break;
2063 							case 2: /* embedded enclosure ? let's check it */
2064 								if (*bptr != enclosure) {
2065 									/* real enclosure */
2066 									memcpy(tptr, hunk_begin, bptr - hunk_begin - 1);
2067 									tptr += (bptr - hunk_begin - 1);
2068 									hunk_begin = bptr;
2069 									goto quit_loop_2;
2070 								}
2071 								memcpy(tptr, hunk_begin, bptr - hunk_begin);
2072 								tptr += (bptr - hunk_begin);
2073 								bptr++;
2074 								hunk_begin = bptr;
2075 								state = 0;
2076 								break;
2077 							default:
2078 								if (*bptr == enclosure) {
2079 									state = 2;
2080 								} else if (escape_char != PHP_CSV_NO_ESCAPE && *bptr == escape_char) {
2081 									state = 1;
2082 								}
2083 								bptr++;
2084 								break;
2085 						}
2086 						break;
2087 
2088 					default:
2089 						switch (state) {
2090 							case 2:
2091 								/* real enclosure */
2092 								memcpy(tptr, hunk_begin, bptr - hunk_begin - 1);
2093 								tptr += (bptr - hunk_begin - 1);
2094 								hunk_begin = bptr;
2095 								goto quit_loop_2;
2096 							case 1:
2097 								bptr += inc_len;
2098 								memcpy(tptr, hunk_begin, bptr - hunk_begin);
2099 								tptr += (bptr - hunk_begin);
2100 								hunk_begin = bptr;
2101 								state = 0;
2102 								break;
2103 							default:
2104 								bptr += inc_len;
2105 								break;
2106 						}
2107 						break;
2108 				}
2109 				inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2110 			}
2111 
2112 		quit_loop_2:
2113 			/* look up for a delimiter */
2114 			for (;;) {
2115 				switch (inc_len) {
2116 					case 0:
2117 						goto quit_loop_3;
2118 
2119 					case -2:
2120 					case -1:
2121 						inc_len = 1;
2122 						php_mb_reset();
2123 						ZEND_FALLTHROUGH;
2124 					case 1:
2125 						if (*bptr == delimiter) {
2126 							goto quit_loop_3;
2127 						}
2128 						break;
2129 					default:
2130 						break;
2131 				}
2132 				bptr += inc_len;
2133 				inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2134 			}
2135 
2136 		quit_loop_3:
2137 			memcpy(tptr, hunk_begin, bptr - hunk_begin);
2138 			tptr += (bptr - hunk_begin);
2139 			bptr += inc_len;
2140 			comp_end = tptr;
2141 		} else {
2142 			/* 2B. Handle non-enclosure field */
2143 
2144 			hunk_begin = bptr;
2145 
2146 			for (;;) {
2147 				switch (inc_len) {
2148 					case 0:
2149 						goto quit_loop_4;
2150 					case -2:
2151 					case -1:
2152 						inc_len = 1;
2153 						php_mb_reset();
2154 						ZEND_FALLTHROUGH;
2155 					case 1:
2156 						if (*bptr == delimiter) {
2157 							goto quit_loop_4;
2158 						}
2159 						break;
2160 					default:
2161 						break;
2162 				}
2163 				bptr += inc_len;
2164 				inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0);
2165 			}
2166 		quit_loop_4:
2167 			memcpy(tptr, hunk_begin, bptr - hunk_begin);
2168 			tptr += (bptr - hunk_begin);
2169 
2170 			comp_end = (char *)php_fgetcsv_lookup_trailing_spaces(temp, tptr - temp);
2171 			if (*bptr == delimiter) {
2172 				bptr++;
2173 			}
2174 		}
2175 
2176 		/* 3. Now pass our field back to php */
2177 		*comp_end = '\0';
2178 
2179 		zval z_tmp;
2180 		ZVAL_STRINGL(&z_tmp, temp, comp_end - temp);
2181 		zend_hash_next_index_insert(values, &z_tmp);
2182 	} while (inc_len > 0);
2183 
2184 	efree(temp);
2185 	if (stream) {
2186 		efree(buf);
2187 	}
2188 
2189 	return values;
2190 }
2191 /* }}} */
2192 
2193 /* {{{ Return the resolved path */
PHP_FUNCTION(realpath)2194 PHP_FUNCTION(realpath)
2195 {
2196 	char *filename;
2197 	size_t filename_len;
2198 	char resolved_path_buff[MAXPATHLEN];
2199 
2200 	ZEND_PARSE_PARAMETERS_START(1, 1)
2201 		Z_PARAM_PATH(filename, filename_len)
2202 	ZEND_PARSE_PARAMETERS_END();
2203 
2204 	if (VCWD_REALPATH(filename, resolved_path_buff)) {
2205 		if (php_check_open_basedir(resolved_path_buff)) {
2206 			RETURN_FALSE;
2207 		}
2208 
2209 #ifdef ZTS
2210 		if (VCWD_ACCESS(resolved_path_buff, F_OK)) {
2211 			RETURN_FALSE;
2212 		}
2213 #endif
2214 		RETURN_STRING(resolved_path_buff);
2215 	} else {
2216 		RETURN_FALSE;
2217 	}
2218 }
2219 /* }}} */
2220 
2221 /* See http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.2 */
2222 #define PHP_META_HTML401_CHARS "-_.:"
2223 
2224 /* {{{ php_next_meta_token
2225    Tokenizes an HTML file for get_meta_tags */
php_next_meta_token(php_meta_tags_data * md)2226 php_meta_tags_token php_next_meta_token(php_meta_tags_data *md)
2227 {
2228 	int ch = 0, compliment;
2229 	char buff[META_DEF_BUFSIZE + 1];
2230 
2231 	memset((void *)buff, 0, META_DEF_BUFSIZE + 1);
2232 
2233 	while (md->ulc || (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)))) {
2234 		if (php_stream_eof(md->stream)) {
2235 			break;
2236 		}
2237 
2238 		if (md->ulc) {
2239 			ch = md->lc;
2240 			md->ulc = 0;
2241 		}
2242 
2243 		switch (ch) {
2244 			case '<':
2245 				return TOK_OPENTAG;
2246 				break;
2247 
2248 			case '>':
2249 				return TOK_CLOSETAG;
2250 				break;
2251 
2252 			case '=':
2253 				return TOK_EQUAL;
2254 				break;
2255 			case '/':
2256 				return TOK_SLASH;
2257 				break;
2258 
2259 			case '\'':
2260 			case '"':
2261 				compliment = ch;
2262 				md->token_len = 0;
2263 				while (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)) && ch != compliment && ch != '<' && ch != '>') {
2264 					buff[(md->token_len)++] = ch;
2265 
2266 					if (md->token_len == META_DEF_BUFSIZE) {
2267 						break;
2268 					}
2269 				}
2270 
2271 				if (ch == '<' || ch == '>') {
2272 					/* Was just an apostrophe */
2273 					md->ulc = 1;
2274 					md->lc = ch;
2275 				}
2276 
2277 				/* We don't need to alloc unless we are in a meta tag */
2278 				if (md->in_meta) {
2279 					md->token_data = (char *) emalloc(md->token_len + 1);
2280 					memcpy(md->token_data, buff, md->token_len+1);
2281 				}
2282 
2283 				return TOK_STRING;
2284 				break;
2285 
2286 			case '\n':
2287 			case '\r':
2288 			case '\t':
2289 				break;
2290 
2291 			case ' ':
2292 				return TOK_SPACE;
2293 				break;
2294 
2295 			default:
2296 				if (isalnum(ch)) {
2297 					md->token_len = 0;
2298 					buff[(md->token_len)++] = ch;
2299 					while (!php_stream_eof(md->stream) && (ch = php_stream_getc(md->stream)) && (isalnum(ch) || strchr(PHP_META_HTML401_CHARS, ch))) {
2300 						buff[(md->token_len)++] = ch;
2301 
2302 						if (md->token_len == META_DEF_BUFSIZE) {
2303 							break;
2304 						}
2305 					}
2306 
2307 					/* This is ugly, but we have to replace ungetc */
2308 					if (!isalpha(ch) && ch != '-') {
2309 						md->ulc = 1;
2310 						md->lc = ch;
2311 					}
2312 
2313 					md->token_data = (char *) emalloc(md->token_len + 1);
2314 					memcpy(md->token_data, buff, md->token_len+1);
2315 
2316 					return TOK_ID;
2317 				} else {
2318 					return TOK_OTHER;
2319 				}
2320 				break;
2321 		}
2322 	}
2323 
2324 	return TOK_EOF;
2325 }
2326 /* }}} */
2327 
2328 #ifdef HAVE_FNMATCH
2329 /* {{{ Match filename against pattern */
PHP_FUNCTION(fnmatch)2330 PHP_FUNCTION(fnmatch)
2331 {
2332 	char *pattern, *filename;
2333 	size_t pattern_len, filename_len;
2334 	zend_long flags = 0;
2335 
2336 	ZEND_PARSE_PARAMETERS_START(2, 3)
2337 		Z_PARAM_PATH(pattern, pattern_len)
2338 		Z_PARAM_PATH(filename, filename_len)
2339 		Z_PARAM_OPTIONAL
2340 		Z_PARAM_LONG(flags)
2341 	ZEND_PARSE_PARAMETERS_END();
2342 
2343 	if (filename_len >= MAXPATHLEN) {
2344 		php_error_docref(NULL, E_WARNING, "Filename exceeds the maximum allowed length of %d characters", MAXPATHLEN);
2345 		RETURN_FALSE;
2346 	}
2347 	if (pattern_len >= MAXPATHLEN) {
2348 		php_error_docref(NULL, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN);
2349 		RETURN_FALSE;
2350 	}
2351 
2352 	RETURN_BOOL( ! fnmatch( pattern, filename, (int)flags ));
2353 }
2354 /* }}} */
2355 #endif
2356 
2357 /* {{{ Returns directory path used for temporary files */
PHP_FUNCTION(sys_get_temp_dir)2358 PHP_FUNCTION(sys_get_temp_dir)
2359 {
2360 	ZEND_PARSE_PARAMETERS_NONE();
2361 
2362 	RETURN_STRING((char *)php_get_temporary_directory());
2363 }
2364 /* }}} */
2365