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 = {0};
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 ? zend_string_init(filename, strlen(filename), 0) : NULL;
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 ? zend_string_init(filename, strlen(filename), 0) : NULL;
74 }
75
zend_stream_init_filename_ex(zend_file_handle * handle,zend_string * filename)76 ZEND_API void zend_stream_init_filename_ex(zend_file_handle *handle, zend_string *filename) {
77 memset(handle, 0, sizeof(zend_file_handle));
78 handle->type = ZEND_HANDLE_FILENAME;
79 handle->filename = zend_string_copy(filename);
80 }
81
zend_stream_open(zend_file_handle * handle)82 ZEND_API zend_result zend_stream_open(zend_file_handle *handle) /* {{{ */
83 {
84 zend_string *opened_path;
85
86 ZEND_ASSERT(handle->type == ZEND_HANDLE_FILENAME);
87 if (zend_stream_open_function) {
88 return zend_stream_open_function(handle);
89 }
90
91 handle->handle.fp = zend_fopen(handle->filename, &opened_path);
92 if (!handle->handle.fp) {
93 return FAILURE;
94 }
95 handle->type = ZEND_HANDLE_FP;
96 return SUCCESS;
97 } /* }}} */
98
zend_stream_getc(zend_file_handle * file_handle)99 static int zend_stream_getc(zend_file_handle *file_handle) /* {{{ */
100 {
101 char buf;
102
103 if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf))) {
104 return (int)buf;
105 }
106 return EOF;
107 } /* }}} */
108
zend_stream_read(zend_file_handle * file_handle,char * buf,size_t len)109 static ssize_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len) /* {{{ */
110 {
111 if (file_handle->handle.stream.isatty) {
112 int c = '*';
113 size_t n;
114
115 for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n) {
116 buf[n] = (char)c;
117 }
118 if (c == '\n') {
119 buf[n++] = (char)c;
120 }
121
122 return n;
123 }
124 return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
125 } /* }}} */
126
zend_stream_fixup(zend_file_handle * file_handle,char ** buf,size_t * len)127 ZEND_API zend_result zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
128 {
129 size_t file_size;
130
131 if (file_handle->buf) {
132 *buf = file_handle->buf;
133 *len = file_handle->len;
134 return SUCCESS;
135 }
136
137 if (file_handle->type == ZEND_HANDLE_FILENAME) {
138 if (zend_stream_open(file_handle) == FAILURE) {
139 return FAILURE;
140 }
141 }
142
143 if (file_handle->type == ZEND_HANDLE_FP) {
144 if (!file_handle->handle.fp) {
145 return FAILURE;
146 }
147
148 file_handle->type = ZEND_HANDLE_STREAM;
149 file_handle->handle.stream.handle = file_handle->handle.fp;
150 file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle));
151 file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader;
152 file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer;
153 file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
154 }
155
156 file_size = zend_stream_fsize(file_handle);
157 if (file_size == (size_t)-1) {
158 return FAILURE;
159 }
160
161 if (file_size) {
162 ssize_t read;
163 size_t size = 0;
164 *buf = safe_emalloc(1, file_size, ZEND_MMAP_AHEAD);
165 while ((read = zend_stream_read(file_handle, *buf + size, file_size - size)) > 0) {
166 size += read;
167 }
168 if (read < 0) {
169 efree(*buf);
170 return FAILURE;
171 }
172 file_handle->buf = *buf;
173 file_handle->len = size;
174 } else {
175 size_t size = 0, remain = 4*1024;
176 ssize_t read;
177 *buf = emalloc(remain);
178
179 while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
180 size += read;
181 remain -= read;
182
183 if (remain == 0) {
184 *buf = safe_erealloc(*buf, size, 2, 0);
185 remain = size;
186 }
187 }
188 if (read < 0) {
189 efree(*buf);
190 return FAILURE;
191 }
192
193 file_handle->len = size;
194 if (size && remain < ZEND_MMAP_AHEAD) {
195 *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
196 }
197 file_handle->buf = *buf;
198 }
199
200 if (file_handle->len == 0) {
201 *buf = erealloc(*buf, ZEND_MMAP_AHEAD);
202 file_handle->buf = *buf;
203 }
204
205 memset(file_handle->buf + file_handle->len, 0, ZEND_MMAP_AHEAD);
206
207 *buf = file_handle->buf;
208 *len = file_handle->len;
209
210 return SUCCESS;
211 } /* }}} */
212
zend_file_handle_dtor(zend_file_handle * fh)213 static void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
214 {
215 switch (fh->type) {
216 case ZEND_HANDLE_FP:
217 if (fh->handle.fp) {
218 fclose(fh->handle.fp);
219 fh->handle.fp = NULL;
220 }
221 break;
222 case ZEND_HANDLE_STREAM:
223 if (fh->handle.stream.closer && fh->handle.stream.handle) {
224 fh->handle.stream.closer(fh->handle.stream.handle);
225 }
226 fh->handle.stream.handle = NULL;
227 break;
228 case ZEND_HANDLE_FILENAME:
229 /* We're only supposed to get here when destructing the used_files hash,
230 * which doesn't really contain open files, but references to their names/paths
231 */
232 break;
233 }
234 if (fh->opened_path) {
235 zend_string_release_ex(fh->opened_path, 0);
236 fh->opened_path = NULL;
237 }
238 if (fh->buf) {
239 efree(fh->buf);
240 fh->buf = NULL;
241 }
242 if (fh->filename) {
243 zend_string_release(fh->filename);
244 fh->filename = NULL;
245 }
246 }
247 /* }}} */
248
249 /* return int to be compatible with Zend linked list API */
zend_compare_file_handles(zend_file_handle * fh1,zend_file_handle * fh2)250 static int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
251 {
252 if (fh1->type != fh2->type) {
253 return 0;
254 }
255 switch (fh1->type) {
256 case ZEND_HANDLE_FILENAME:
257 return zend_string_equals(fh1->filename, fh2->filename);
258 case ZEND_HANDLE_FP:
259 return fh1->handle.fp == fh2->handle.fp;
260 case ZEND_HANDLE_STREAM:
261 return fh1->handle.stream.handle == fh2->handle.stream.handle;
262 default:
263 return 0;
264 }
265 return 0;
266 } /* }}} */
267
zend_destroy_file_handle(zend_file_handle * file_handle)268 ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle) /* {{{ */
269 {
270 if (file_handle->in_list) {
271 zend_llist_del_element(&CG(open_files), file_handle, (int (*)(void *, void *)) zend_compare_file_handles);
272 /* zend_file_handle_dtor() operates on the copy, so we have to NULLify the original here */
273 file_handle->opened_path = NULL;
274 file_handle->filename = NULL;
275 } else {
276 zend_file_handle_dtor(file_handle);
277 }
278 } /* }}} */
279
zend_stream_init(void)280 void zend_stream_init(void) /* {{{ */
281 {
282 zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) zend_file_handle_dtor, 0);
283 } /* }}} */
284
zend_stream_shutdown(void)285 void zend_stream_shutdown(void) /* {{{ */
286 {
287 zend_llist_destroy(&CG(open_files));
288 } /* }}} */
289