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