zend_stream.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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. #include "zend.h"
  22. #include "zend_compile.h"
  23. #include "zend_stream.h"
  24. ZEND_DLIMPORT int isatty(int fd);
  25. static ssize_t zend_stream_stdio_reader(void *handle, char *buf, size_t len) /* {{{ */
  26. {
  27. return fread(buf, 1, len, (FILE*)handle);
  28. } /* }}} */
  29. static void zend_stream_stdio_closer(void *handle) /* {{{ */
  30. {
  31. if (handle && (FILE*)handle != stdin) {
  32. fclose((FILE*)handle);
  33. }
  34. } /* }}} */
  35. static size_t zend_stream_stdio_fsizer(void *handle) /* {{{ */
  36. {
  37. zend_stat_t buf;
  38. if (handle && zend_fstat(fileno((FILE*)handle), &buf) == 0) {
  39. #ifdef S_ISREG
  40. if (!S_ISREG(buf.st_mode)) {
  41. return 0;
  42. }
  43. #endif
  44. return buf.st_size;
  45. }
  46. return -1;
  47. } /* }}} */
  48. static size_t zend_stream_fsize(zend_file_handle *file_handle) /* {{{ */
  49. {
  50. ZEND_ASSERT(file_handle->type == ZEND_HANDLE_STREAM);
  51. if (file_handle->handle.stream.isatty) {
  52. return 0;
  53. }
  54. return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle);
  55. } /* }}} */
  56. ZEND_API void zend_stream_init_fp(zend_file_handle *handle, FILE *fp, const char *filename) {
  57. memset(handle, 0, sizeof(zend_file_handle));
  58. handle->type = ZEND_HANDLE_FP;
  59. handle->handle.fp = fp;
  60. handle->filename = filename ? zend_string_init(filename, strlen(filename), 0) : NULL;
  61. }
  62. ZEND_API void zend_stream_init_filename(zend_file_handle *handle, const char *filename) {
  63. memset(handle, 0, sizeof(zend_file_handle));
  64. handle->type = ZEND_HANDLE_FILENAME;
  65. handle->filename = filename ? zend_string_init(filename, strlen(filename), 0) : NULL;
  66. }
  67. ZEND_API void zend_stream_init_filename_ex(zend_file_handle *handle, zend_string *filename) {
  68. memset(handle, 0, sizeof(zend_file_handle));
  69. handle->type = ZEND_HANDLE_FILENAME;
  70. handle->filename = zend_string_copy(filename);
  71. }
  72. ZEND_API zend_result zend_stream_open(zend_file_handle *handle) /* {{{ */
  73. {
  74. zend_string *opened_path;
  75. ZEND_ASSERT(handle->type == ZEND_HANDLE_FILENAME);
  76. if (zend_stream_open_function) {
  77. return zend_stream_open_function(handle);
  78. }
  79. handle->handle.fp = zend_fopen(handle->filename, &opened_path);
  80. if (!handle->handle.fp) {
  81. return FAILURE;
  82. }
  83. handle->type = ZEND_HANDLE_FP;
  84. return SUCCESS;
  85. } /* }}} */
  86. static int zend_stream_getc(zend_file_handle *file_handle) /* {{{ */
  87. {
  88. char buf;
  89. if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf))) {
  90. return (int)buf;
  91. }
  92. return EOF;
  93. } /* }}} */
  94. static ssize_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len) /* {{{ */
  95. {
  96. if (file_handle->handle.stream.isatty) {
  97. int c = '*';
  98. size_t n;
  99. for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n) {
  100. buf[n] = (char)c;
  101. }
  102. if (c == '\n') {
  103. buf[n++] = (char)c;
  104. }
  105. return n;
  106. }
  107. return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
  108. } /* }}} */
  109. ZEND_API zend_result zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
  110. {
  111. size_t file_size;
  112. if (file_handle->buf) {
  113. *buf = file_handle->buf;
  114. *len = file_handle->len;
  115. return SUCCESS;
  116. }
  117. if (file_handle->type == ZEND_HANDLE_FILENAME) {
  118. if (zend_stream_open(file_handle) == FAILURE) {
  119. return FAILURE;
  120. }
  121. }
  122. if (file_handle->type == ZEND_HANDLE_FP) {
  123. if (!file_handle->handle.fp) {
  124. return FAILURE;
  125. }
  126. file_handle->type = ZEND_HANDLE_STREAM;
  127. file_handle->handle.stream.handle = file_handle->handle.fp;
  128. file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle));
  129. file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader;
  130. file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer;
  131. file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
  132. }
  133. file_size = zend_stream_fsize(file_handle);
  134. if (file_size == (size_t)-1) {
  135. return FAILURE;
  136. }
  137. if (file_size) {
  138. ssize_t read;
  139. size_t size = 0;
  140. *buf = safe_emalloc(1, file_size, ZEND_MMAP_AHEAD);
  141. while ((read = zend_stream_read(file_handle, *buf + size, file_size - size)) > 0) {
  142. size += read;
  143. }
  144. if (read < 0) {
  145. efree(*buf);
  146. return FAILURE;
  147. }
  148. file_handle->buf = *buf;
  149. file_handle->len = size;
  150. } else {
  151. size_t size = 0, remain = 4*1024;
  152. ssize_t read;
  153. *buf = emalloc(remain);
  154. while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
  155. size += read;
  156. remain -= read;
  157. if (remain == 0) {
  158. *buf = safe_erealloc(*buf, size, 2, 0);
  159. remain = size;
  160. }
  161. }
  162. if (read < 0) {
  163. efree(*buf);
  164. return FAILURE;
  165. }
  166. file_handle->len = size;
  167. if (size && remain < ZEND_MMAP_AHEAD) {
  168. *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
  169. }
  170. file_handle->buf = *buf;
  171. }
  172. if (file_handle->len == 0) {
  173. *buf = erealloc(*buf, ZEND_MMAP_AHEAD);
  174. file_handle->buf = *buf;
  175. }
  176. memset(file_handle->buf + file_handle->len, 0, ZEND_MMAP_AHEAD);
  177. *buf = file_handle->buf;
  178. *len = file_handle->len;
  179. return SUCCESS;
  180. } /* }}} */
  181. static void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
  182. {
  183. switch (fh->type) {
  184. case ZEND_HANDLE_FP:
  185. if (fh->handle.fp) {
  186. fclose(fh->handle.fp);
  187. fh->handle.fp = NULL;
  188. }
  189. break;
  190. case ZEND_HANDLE_STREAM:
  191. if (fh->handle.stream.closer && fh->handle.stream.handle) {
  192. fh->handle.stream.closer(fh->handle.stream.handle);
  193. }
  194. fh->handle.stream.handle = NULL;
  195. break;
  196. case ZEND_HANDLE_FILENAME:
  197. /* We're only supposed to get here when destructing the used_files hash,
  198. * which doesn't really contain open files, but references to their names/paths
  199. */
  200. break;
  201. }
  202. if (fh->opened_path) {
  203. zend_string_release_ex(fh->opened_path, 0);
  204. fh->opened_path = NULL;
  205. }
  206. if (fh->buf) {
  207. efree(fh->buf);
  208. fh->buf = NULL;
  209. }
  210. if (fh->filename) {
  211. zend_string_release(fh->filename);
  212. fh->filename = NULL;
  213. }
  214. }
  215. /* }}} */
  216. /* return int to be compatible with Zend linked list API */
  217. static int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
  218. {
  219. if (fh1->type != fh2->type) {
  220. return 0;
  221. }
  222. switch (fh1->type) {
  223. case ZEND_HANDLE_FILENAME:
  224. return zend_string_equals(fh1->filename, fh2->filename);
  225. case ZEND_HANDLE_FP:
  226. return fh1->handle.fp == fh2->handle.fp;
  227. case ZEND_HANDLE_STREAM:
  228. return fh1->handle.stream.handle == fh2->handle.stream.handle;
  229. default:
  230. return 0;
  231. }
  232. return 0;
  233. } /* }}} */
  234. ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle) /* {{{ */
  235. {
  236. if (file_handle->in_list) {
  237. zend_llist_del_element(&CG(open_files), file_handle, (int (*)(void *, void *)) zend_compare_file_handles);
  238. /* zend_file_handle_dtor() operates on the copy, so we have to NULLify the original here */
  239. file_handle->opened_path = NULL;
  240. file_handle->filename = NULL;
  241. } else {
  242. zend_file_handle_dtor(file_handle);
  243. }
  244. } /* }}} */
  245. void zend_stream_init(void) /* {{{ */
  246. {
  247. zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) zend_file_handle_dtor, 0);
  248. } /* }}} */
  249. void zend_stream_shutdown(void) /* {{{ */
  250. {
  251. zend_llist_destroy(&CG(open_files));
  252. } /* }}} */