xref: /PHP-8.0/Zend/zend_stream.c (revision fa8d9b11)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend license,     |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
16    |          Scott MacVicar <scottmac@php.net>                           |
17    |          Nuno Lopes <nlopess@php.net>                                |
18    |          Marcus Boerger <helly@php.net>                              |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "zend.h"
23 #include "zend_compile.h"
24 #include "zend_stream.h"
25 
26 ZEND_DLIMPORT int isatty(int fd);
27 
zend_stream_stdio_reader(void * handle,char * buf,size_t len)28 static ssize_t zend_stream_stdio_reader(void *handle, char *buf, size_t len) /* {{{ */
29 {
30 	return fread(buf, 1, len, (FILE*)handle);
31 } /* }}} */
32 
zend_stream_stdio_closer(void * handle)33 static void zend_stream_stdio_closer(void *handle) /* {{{ */
34 {
35 	if (handle && (FILE*)handle != stdin) {
36 		fclose((FILE*)handle);
37 	}
38 } /* }}} */
39 
zend_stream_stdio_fsizer(void * handle)40 static size_t zend_stream_stdio_fsizer(void *handle) /* {{{ */
41 {
42 	zend_stat_t buf;
43 	if (handle && zend_fstat(fileno((FILE*)handle), &buf) == 0) {
44 #ifdef S_ISREG
45 		if (!S_ISREG(buf.st_mode)) {
46 			return 0;
47 		}
48 #endif
49 		return buf.st_size;
50 	}
51 	return -1;
52 } /* }}} */
53 
zend_stream_fsize(zend_file_handle * file_handle)54 static size_t zend_stream_fsize(zend_file_handle *file_handle) /* {{{ */
55 {
56 	ZEND_ASSERT(file_handle->type == ZEND_HANDLE_STREAM);
57 	if (file_handle->handle.stream.isatty) {
58 		return 0;
59 	}
60 	return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle);
61 } /* }}} */
62 
zend_stream_init_fp(zend_file_handle * handle,FILE * fp,const char * filename)63 ZEND_API void zend_stream_init_fp(zend_file_handle *handle, FILE *fp, const char *filename) {
64 	memset(handle, 0, sizeof(zend_file_handle));
65 	handle->type = ZEND_HANDLE_FP;
66 	handle->handle.fp = fp;
67 	handle->filename = filename;
68 }
69 
zend_stream_init_filename(zend_file_handle * handle,const char * filename)70 ZEND_API void zend_stream_init_filename(zend_file_handle *handle, const char *filename) {
71 	memset(handle, 0, sizeof(zend_file_handle));
72 	handle->type = ZEND_HANDLE_FILENAME;
73 	handle->filename = filename;
74 }
75 
zend_stream_open(const char * filename,zend_file_handle * handle)76 ZEND_API zend_result zend_stream_open(const char *filename, zend_file_handle *handle) /* {{{ */
77 {
78 	zend_string *opened_path;
79 	if (zend_stream_open_function) {
80 		return zend_stream_open_function(filename, handle);
81 	}
82 
83 	zend_stream_init_fp(handle, zend_fopen(filename, &opened_path), filename);
84 	handle->opened_path = opened_path;
85 	return handle->handle.fp ? SUCCESS : FAILURE;
86 } /* }}} */
87 
zend_stream_getc(zend_file_handle * file_handle)88 static int zend_stream_getc(zend_file_handle *file_handle) /* {{{ */
89 {
90 	char buf;
91 
92 	if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf))) {
93 		return (int)buf;
94 	}
95 	return EOF;
96 } /* }}} */
97 
zend_stream_read(zend_file_handle * file_handle,char * buf,size_t len)98 static ssize_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len) /* {{{ */
99 {
100 	if (file_handle->handle.stream.isatty) {
101 		int c = '*';
102 		size_t n;
103 
104 		for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n)  {
105 			buf[n] = (char)c;
106 		}
107 		if (c == '\n') {
108 			buf[n++] = (char)c;
109 		}
110 
111 		return n;
112 	}
113 	return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
114 } /* }}} */
115 
zend_stream_fixup(zend_file_handle * file_handle,char ** buf,size_t * len)116 ZEND_API zend_result zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
117 {
118 	size_t file_size;
119 
120 	if (file_handle->buf) {
121 		*buf = file_handle->buf;
122 		*len = file_handle->len;
123 		return SUCCESS;
124 	}
125 
126 	if (file_handle->type == ZEND_HANDLE_FILENAME) {
127 		if (zend_stream_open(file_handle->filename, file_handle) == FAILURE) {
128 			return FAILURE;
129 		}
130 	}
131 
132 	if (file_handle->type == ZEND_HANDLE_FP) {
133 		if (!file_handle->handle.fp) {
134 			return FAILURE;
135 		}
136 
137 		file_handle->type = ZEND_HANDLE_STREAM;
138 		file_handle->handle.stream.handle = file_handle->handle.fp;
139 		file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle));
140 		file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader;
141 		file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer;
142 		file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
143 	}
144 
145 	file_size = zend_stream_fsize(file_handle);
146 	if (file_size == (size_t)-1) {
147 		return FAILURE;
148 	}
149 
150 	if (file_size) {
151 		ssize_t read;
152 		size_t size = 0;
153 		*buf = safe_emalloc(1, file_size, ZEND_MMAP_AHEAD);
154 		while ((read = zend_stream_read(file_handle, *buf + size, file_size - size)) > 0) {
155 			size += read;
156 		}
157 		if (read < 0) {
158 			efree(*buf);
159 			return FAILURE;
160 		}
161 		file_handle->buf = *buf;
162 		file_handle->len = size;
163 	} else {
164 		size_t size = 0, remain = 4*1024;
165 		ssize_t read;
166 		*buf = emalloc(remain);
167 
168 		while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
169 			size   += read;
170 			remain -= read;
171 
172 			if (remain == 0) {
173 				*buf   = safe_erealloc(*buf, size, 2, 0);
174 				remain = size;
175 			}
176 		}
177 		if (read < 0) {
178 			efree(*buf);
179 			return FAILURE;
180 		}
181 
182 		file_handle->len = size;
183 		if (size && remain < ZEND_MMAP_AHEAD) {
184 			*buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
185 		}
186 		file_handle->buf = *buf;
187 	}
188 
189 	if (file_handle->len == 0) {
190 		*buf = erealloc(*buf, ZEND_MMAP_AHEAD);
191 		file_handle->buf = *buf;
192 	}
193 
194 	memset(file_handle->buf + file_handle->len, 0, ZEND_MMAP_AHEAD);
195 
196 	*buf = file_handle->buf;
197 	*len = file_handle->len;
198 
199 	return SUCCESS;
200 } /* }}} */
201 
zend_file_handle_dtor(zend_file_handle * fh)202 ZEND_API void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
203 {
204 	switch (fh->type) {
205 		case ZEND_HANDLE_FP:
206 			fclose(fh->handle.fp);
207 			break;
208 		case ZEND_HANDLE_STREAM:
209 			if (fh->handle.stream.closer && fh->handle.stream.handle) {
210 				fh->handle.stream.closer(fh->handle.stream.handle);
211 			}
212 			fh->handle.stream.handle = NULL;
213 			break;
214 		case ZEND_HANDLE_FILENAME:
215 			/* We're only supposed to get here when destructing the used_files hash,
216 			 * which doesn't really contain open files, but references to their names/paths
217 			 */
218 			break;
219 	}
220 	if (fh->opened_path) {
221 		zend_string_release_ex(fh->opened_path, 0);
222 		fh->opened_path = NULL;
223 	}
224 	if (fh->buf) {
225 		efree(fh->buf);
226 		fh->buf = NULL;
227 	}
228 	if (fh->free_filename && fh->filename) {
229 		efree((char*)fh->filename);
230 		fh->filename = NULL;
231 	}
232 }
233 /* }}} */
234 
235 /* return int to be compatible with Zend linked list API */
zend_compare_file_handles(zend_file_handle * fh1,zend_file_handle * fh2)236 ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
237 {
238 	if (fh1->type != fh2->type) {
239 		return 0;
240 	}
241 	switch (fh1->type) {
242 		case ZEND_HANDLE_FILENAME:
243 			return strcmp(fh1->filename, fh2->filename) == 0;
244 		case ZEND_HANDLE_FP:
245 			return fh1->handle.fp == fh2->handle.fp;
246 		case ZEND_HANDLE_STREAM:
247 			return fh1->handle.stream.handle == fh2->handle.stream.handle;
248 		default:
249 			return 0;
250 	}
251 	return 0;
252 } /* }}} */
253