prism 2.0.0.rc1-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +870 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +145 -0
  8. data/README.md +81 -0
  9. data/config.yml +4752 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +69 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +81 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +133 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +147 -0
  23. data/docs/relocation.md +32 -0
  24. data/docs/ripper_translation.md +63 -0
  25. data/docs/ruby_api.md +45 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +230 -0
  28. data/docs/testing.md +72 -0
  29. data/ext/prism/api_node.c +7005 -0
  30. data/ext/prism/extconf.rb +126 -0
  31. data/ext/prism/extension.c +1783 -0
  32. data/ext/prism/extension.h +19 -0
  33. data/include/prism/arena.h +37 -0
  34. data/include/prism/ast.h +10371 -0
  35. data/include/prism/buffer.h +52 -0
  36. data/include/prism/comments.h +55 -0
  37. data/include/prism/compiler/accel.h +24 -0
  38. data/include/prism/compiler/align.h +36 -0
  39. data/include/prism/compiler/assume.h +25 -0
  40. data/include/prism/compiler/exported.h +24 -0
  41. data/include/prism/compiler/fallthrough.h +22 -0
  42. data/include/prism/compiler/filesystem.h +32 -0
  43. data/include/prism/compiler/flex_array.h +19 -0
  44. data/include/prism/compiler/force_inline.h +21 -0
  45. data/include/prism/compiler/format.h +25 -0
  46. data/include/prism/compiler/inline.h +17 -0
  47. data/include/prism/compiler/nodiscard.h +22 -0
  48. data/include/prism/compiler/nonnull.h +18 -0
  49. data/include/prism/compiler/unused.h +18 -0
  50. data/include/prism/constant_pool.h +81 -0
  51. data/include/prism/diagnostic.h +93 -0
  52. data/include/prism/errors_format.h +53 -0
  53. data/include/prism/excludes.h +29 -0
  54. data/include/prism/integer.h +41 -0
  55. data/include/prism/internal/allocator.h +68 -0
  56. data/include/prism/internal/allocator_debug.h +88 -0
  57. data/include/prism/internal/arena.h +108 -0
  58. data/include/prism/internal/bit.h +42 -0
  59. data/include/prism/internal/buffer.h +91 -0
  60. data/include/prism/internal/char.h +145 -0
  61. data/include/prism/internal/comments.h +53 -0
  62. data/include/prism/internal/constant_pool.h +152 -0
  63. data/include/prism/internal/diagnostic.h +389 -0
  64. data/include/prism/internal/encoding.h +242 -0
  65. data/include/prism/internal/integer.h +68 -0
  66. data/include/prism/internal/isinf.h +16 -0
  67. data/include/prism/internal/line_offset_list.h +34 -0
  68. data/include/prism/internal/list.h +62 -0
  69. data/include/prism/internal/magic_comments.h +23 -0
  70. data/include/prism/internal/memchr.h +15 -0
  71. data/include/prism/internal/node.h +38 -0
  72. data/include/prism/internal/options.h +232 -0
  73. data/include/prism/internal/parser.h +984 -0
  74. data/include/prism/internal/regexp.h +26 -0
  75. data/include/prism/internal/serialize.h +39 -0
  76. data/include/prism/internal/source.h +72 -0
  77. data/include/prism/internal/static_literals.h +100 -0
  78. data/include/prism/internal/stringy.h +35 -0
  79. data/include/prism/internal/strncasecmp.h +18 -0
  80. data/include/prism/internal/strpbrk.h +33 -0
  81. data/include/prism/internal/tokens.h +11 -0
  82. data/include/prism/json.h +32 -0
  83. data/include/prism/line_offset_list.h +61 -0
  84. data/include/prism/magic_comments.h +35 -0
  85. data/include/prism/node.h +94 -0
  86. data/include/prism/options.h +354 -0
  87. data/include/prism/parser.h +404 -0
  88. data/include/prism/prettyprint.h +31 -0
  89. data/include/prism/serialize.h +115 -0
  90. data/include/prism/source.h +155 -0
  91. data/include/prism/stream.h +28 -0
  92. data/include/prism/string_query.h +63 -0
  93. data/include/prism/stringy.h +72 -0
  94. data/include/prism/version.h +38 -0
  95. data/include/prism.h +131 -0
  96. data/lib/prism/3.3/prism.so +0 -0
  97. data/lib/prism/3.4/prism.so +0 -0
  98. data/lib/prism/4.0/prism.so +0 -0
  99. data/lib/prism/compiler.rb +815 -0
  100. data/lib/prism/desugar_compiler.rb +463 -0
  101. data/lib/prism/dispatcher.rb +2383 -0
  102. data/lib/prism/dot_visitor.rb +4848 -0
  103. data/lib/prism/dsl.rb +1369 -0
  104. data/lib/prism/ffi.rb +740 -0
  105. data/lib/prism/inspect_visitor.rb +2425 -0
  106. data/lib/prism/lex_compat.rb +953 -0
  107. data/lib/prism/mutation_compiler.rb +780 -0
  108. data/lib/prism/node.rb +33204 -0
  109. data/lib/prism/node_ext.rb +504 -0
  110. data/lib/prism/node_find.rb +215 -0
  111. data/lib/prism/parse_result/errors.rb +72 -0
  112. data/lib/prism/parse_result/newlines.rb +423 -0
  113. data/lib/prism/parse_result.rb +1221 -0
  114. data/lib/prism/pattern.rb +314 -0
  115. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  116. data/lib/prism/polyfill/byteindex.rb +13 -0
  117. data/lib/prism/polyfill/scan_byte.rb +14 -0
  118. data/lib/prism/polyfill/unpack1.rb +14 -0
  119. data/lib/prism/polyfill/warn.rb +36 -0
  120. data/lib/prism/reflection.rb +427 -0
  121. data/lib/prism/relocation.rb +567 -0
  122. data/lib/prism/serialize.rb +5308 -0
  123. data/lib/prism/string_query.rb +46 -0
  124. data/lib/prism/translation/parser/builder.rb +70 -0
  125. data/lib/prism/translation/parser/compiler.rb +2226 -0
  126. data/lib/prism/translation/parser/lexer.rb +769 -0
  127. data/lib/prism/translation/parser.rb +376 -0
  128. data/lib/prism/translation/parser_current.rb +26 -0
  129. data/lib/prism/translation/parser_versions.rb +36 -0
  130. data/lib/prism/translation/ripper/filter.rb +53 -0
  131. data/lib/prism/translation/ripper/lexer.rb +133 -0
  132. data/lib/prism/translation/ripper/sexp.rb +118 -0
  133. data/lib/prism/translation/ripper/shim.rb +7 -0
  134. data/lib/prism/translation/ripper.rb +4278 -0
  135. data/lib/prism/translation/ruby_parser.rb +1721 -0
  136. data/lib/prism/translation.rb +20 -0
  137. data/lib/prism/visitor.rb +1440 -0
  138. data/lib/prism.rb +168 -0
  139. data/prism.gemspec +234 -0
  140. data/rbi/generated/prism/compiler.rbi +489 -0
  141. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  142. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  143. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  144. data/rbi/generated/prism/dsl.rbi +751 -0
  145. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  146. data/rbi/generated/prism/lex_compat.rbi +162 -0
  147. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  148. data/rbi/generated/prism/node.rbi +13681 -0
  149. data/rbi/generated/prism/node_ext.rbi +259 -0
  150. data/rbi/generated/prism/node_find.rbi +79 -0
  151. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  152. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  153. data/rbi/generated/prism/parse_result.rbi +764 -0
  154. data/rbi/generated/prism/pattern.rbi +134 -0
  155. data/rbi/generated/prism/reflection.rbi +89 -0
  156. data/rbi/generated/prism/relocation.rbi +329 -0
  157. data/rbi/generated/prism/serialize.rbi +181 -0
  158. data/rbi/generated/prism/string_query.rbi +36 -0
  159. data/rbi/generated/prism/translation.rbi +7 -0
  160. data/rbi/generated/prism/visitor.rbi +650 -0
  161. data/rbi/generated/prism.rbi +86 -0
  162. data/rbi/prism/translation/parser.rbi +11 -0
  163. data/rbi/prism/translation/parser_versions.rbi +28 -0
  164. data/rbi/prism/translation/ripper.rbi +15 -0
  165. data/rbi/rubyvm/node_find.rbi +14 -0
  166. data/sig/generated/prism/compiler.rbs +492 -0
  167. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  168. data/sig/generated/prism/dispatcher.rbs +1292 -0
  169. data/sig/generated/prism/dot_visitor.rbs +635 -0
  170. data/sig/generated/prism/dsl.rbs +928 -0
  171. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  172. data/sig/generated/prism/lex_compat.rbs +166 -0
  173. data/sig/generated/prism/mutation_compiler.rbs +464 -0
  174. data/sig/generated/prism/node.rbs +20160 -0
  175. data/sig/generated/prism/node_ext.rbs +290 -0
  176. data/sig/generated/prism/node_find.rbs +90 -0
  177. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  178. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  179. data/sig/generated/prism/parse_result.rbs +859 -0
  180. data/sig/generated/prism/pattern.rbs +156 -0
  181. data/sig/generated/prism/reflection.rbs +101 -0
  182. data/sig/generated/prism/relocation.rbs +428 -0
  183. data/sig/generated/prism/serialize.rbs +195 -0
  184. data/sig/generated/prism/string_query.rbs +36 -0
  185. data/sig/generated/prism/translation.rbs +8 -0
  186. data/sig/generated/prism/visitor.rbs +1111 -0
  187. data/sig/generated/prism.rbs +84 -0
  188. data/sig/manifest.yaml +2 -0
  189. data/src/arena.c +117 -0
  190. data/src/buffer.c +380 -0
  191. data/src/char.c +289 -0
  192. data/src/comments.c +454 -0
  193. data/src/constant_pool.c +406 -0
  194. data/src/diagnostic.c +886 -0
  195. data/src/encoding.c +5345 -0
  196. data/src/errors_format.c +519 -0
  197. data/src/integer.c +681 -0
  198. data/src/json.c +5733 -0
  199. data/src/line_offset_list.c +100 -0
  200. data/src/list.c +24 -0
  201. data/src/memchr.c +37 -0
  202. data/src/node.c +4515 -0
  203. data/src/options.c +451 -0
  204. data/src/parser.c +349 -0
  205. data/src/prettyprint.c +9024 -0
  206. data/src/prism.c +23739 -0
  207. data/src/regexp.c +1706 -0
  208. data/src/serialize.c +2309 -0
  209. data/src/source.c +518 -0
  210. data/src/static_literals.c +685 -0
  211. data/src/string_query.c +166 -0
  212. data/src/stringy.c +91 -0
  213. data/src/strncasecmp.c +37 -0
  214. data/src/strpbrk.c +447 -0
  215. data/src/tokens.c +724 -0
  216. metadata +259 -0
data/src/source.c ADDED
@@ -0,0 +1,518 @@
1
+ #include "prism/internal/source.h"
2
+
3
+ #include "prism/internal/allocator.h"
4
+ #include "prism/internal/buffer.h"
5
+
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+
9
+ /* The following headers are necessary to read files using demand paging. */
10
+ #ifdef _WIN32
11
+ #include <windows.h>
12
+ #elif defined(_POSIX_MAPPED_FILES)
13
+ #include <fcntl.h>
14
+ #include <sys/mman.h>
15
+ #include <sys/stat.h>
16
+ #elif defined(PRISM_HAS_FILESYSTEM)
17
+ #include <fcntl.h>
18
+ #include <sys/stat.h>
19
+ #endif
20
+
21
+ static const uint8_t empty_source[] = "";
22
+
23
+ /**
24
+ * Allocate and initialize a pm_source_t with the given fields.
25
+ */
26
+ static pm_source_t *
27
+ pm_source_alloc(const uint8_t *source, size_t length, pm_source_type_t type) {
28
+ pm_source_t *result = xmalloc(sizeof(pm_source_t));
29
+ if (result == NULL) abort();
30
+
31
+ *result = (struct pm_source_t) {
32
+ .source = source,
33
+ .length = length,
34
+ .type = type
35
+ };
36
+
37
+ return result;
38
+ }
39
+
40
+ /**
41
+ * Create a new source that wraps existing constant memory.
42
+ */
43
+ pm_source_t *
44
+ pm_source_constant_new(const uint8_t *data, size_t length) {
45
+ return pm_source_alloc(data, length, PM_SOURCE_CONSTANT);
46
+ }
47
+
48
+ /**
49
+ * Create a new source that wraps existing shared memory.
50
+ */
51
+ pm_source_t *
52
+ pm_source_shared_new(const uint8_t *data, size_t length) {
53
+ return pm_source_alloc(data, length, PM_SOURCE_SHARED);
54
+ }
55
+
56
+ /**
57
+ * Create a new source that owns its memory.
58
+ */
59
+ pm_source_t *
60
+ pm_source_owned_new(uint8_t *data, size_t length) {
61
+ return pm_source_alloc(data, length, PM_SOURCE_OWNED);
62
+ }
63
+
64
+ #ifdef _WIN32
65
+ /**
66
+ * Represents a file handle on Windows, where the path will need to be freed
67
+ * when the file is closed.
68
+ */
69
+ typedef struct {
70
+ /** The path to the file, which will become allocated memory. */
71
+ WCHAR *path;
72
+
73
+ /** The size of the allocated path in bytes. */
74
+ size_t path_size;
75
+
76
+ /** The handle to the file, which will start as uninitialized memory. */
77
+ HANDLE file;
78
+ } pm_source_file_handle_t;
79
+
80
+ /**
81
+ * Open the file indicated by the filepath parameter for reading on Windows.
82
+ */
83
+ static pm_source_init_result_t
84
+ pm_source_file_handle_open(pm_source_file_handle_t *handle, const char *filepath) {
85
+ int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0);
86
+ if (length == 0) return PM_SOURCE_INIT_ERROR_GENERIC;
87
+
88
+ handle->path_size = sizeof(WCHAR) * ((size_t) length);
89
+ handle->path = xmalloc(handle->path_size);
90
+ if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) {
91
+ xfree_sized(handle->path, handle->path_size);
92
+ return PM_SOURCE_INIT_ERROR_GENERIC;
93
+ }
94
+
95
+ handle->file = CreateFileW(handle->path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
96
+ if (handle->file == INVALID_HANDLE_VALUE) {
97
+ pm_source_init_result_t result = PM_SOURCE_INIT_ERROR_GENERIC;
98
+
99
+ if (GetLastError() == ERROR_ACCESS_DENIED) {
100
+ DWORD attributes = GetFileAttributesW(handle->path);
101
+ if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
102
+ result = PM_SOURCE_INIT_ERROR_DIRECTORY;
103
+ }
104
+ }
105
+
106
+ xfree_sized(handle->path, handle->path_size);
107
+ return result;
108
+ }
109
+
110
+ return PM_SOURCE_INIT_SUCCESS;
111
+ }
112
+
113
+ /**
114
+ * Close the file handle and free the path.
115
+ */
116
+ static void
117
+ pm_source_file_handle_close(pm_source_file_handle_t *handle) {
118
+ xfree_sized(handle->path, handle->path_size);
119
+ CloseHandle(handle->file);
120
+ }
121
+ #endif
122
+
123
+ /**
124
+ * Create a new source by memory-mapping a file.
125
+ */
126
+ pm_source_t *
127
+ pm_source_mapped_new(const char *filepath, int open_flags, pm_source_init_result_t *result) {
128
+ #ifdef _WIN32
129
+ (void) open_flags;
130
+
131
+ /* Open the file for reading. */
132
+ pm_source_file_handle_t handle;
133
+ *result = pm_source_file_handle_open(&handle, filepath);
134
+ if (*result != PM_SOURCE_INIT_SUCCESS) return NULL;
135
+
136
+ /* Get the file size. */
137
+ DWORD file_size = GetFileSize(handle.file, NULL);
138
+ if (file_size == INVALID_FILE_SIZE) {
139
+ pm_source_file_handle_close(&handle);
140
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
141
+ return NULL;
142
+ }
143
+
144
+ /* If the file is empty, then return a constant source. */
145
+ if (file_size == 0) {
146
+ pm_source_file_handle_close(&handle);
147
+ *result = PM_SOURCE_INIT_SUCCESS;
148
+ return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT);
149
+ }
150
+
151
+ /* Create a mapping of the file. */
152
+ HANDLE mapping = CreateFileMapping(handle.file, NULL, PAGE_READONLY, 0, 0, NULL);
153
+ if (mapping == NULL) {
154
+ pm_source_file_handle_close(&handle);
155
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
156
+ return NULL;
157
+ }
158
+
159
+ /* Map the file into memory. */
160
+ uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
161
+ CloseHandle(mapping);
162
+ pm_source_file_handle_close(&handle);
163
+
164
+ if (source == NULL) {
165
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
166
+ return NULL;
167
+ }
168
+
169
+ *result = PM_SOURCE_INIT_SUCCESS;
170
+ return pm_source_alloc(source, (size_t) file_size, PM_SOURCE_MAPPED);
171
+ #elif defined(_POSIX_MAPPED_FILES)
172
+ /* Open the file for reading. */
173
+ int fd = open(filepath, O_RDONLY | open_flags);
174
+ if (fd == -1) {
175
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
176
+ return NULL;
177
+ }
178
+
179
+ /* Stat the file to get the file size. */
180
+ struct stat sb;
181
+ if (fstat(fd, &sb) == -1) {
182
+ close(fd);
183
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
184
+ return NULL;
185
+ }
186
+
187
+ /* Ensure it is a file and not a directory. */
188
+ if (S_ISDIR(sb.st_mode)) {
189
+ close(fd);
190
+ *result = PM_SOURCE_INIT_ERROR_DIRECTORY;
191
+ return NULL;
192
+ }
193
+
194
+ /*
195
+ * For non-regular files (pipes, character devices), return a specific
196
+ * error so the caller can handle reading through their own I/O layer.
197
+ */
198
+ if (!S_ISREG(sb.st_mode)) {
199
+ close(fd);
200
+ *result = PM_SOURCE_INIT_ERROR_NON_REGULAR;
201
+ return NULL;
202
+ }
203
+
204
+ /* mmap the file descriptor to virtually get the contents. */
205
+ size_t size = (size_t) sb.st_size;
206
+
207
+ if (size == 0) {
208
+ close(fd);
209
+ *result = PM_SOURCE_INIT_SUCCESS;
210
+ return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT);
211
+ }
212
+
213
+ uint8_t *source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
214
+ if (source == MAP_FAILED) {
215
+ close(fd);
216
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
217
+ return NULL;
218
+ }
219
+
220
+ close(fd);
221
+ *result = PM_SOURCE_INIT_SUCCESS;
222
+ return pm_source_alloc(source, size, PM_SOURCE_MAPPED);
223
+ #else
224
+ (void) open_flags;
225
+ return pm_source_file_new(filepath, result);
226
+ #endif
227
+ }
228
+
229
+ /**
230
+ * Create a new source by reading a file into a heap-allocated buffer.
231
+ */
232
+ pm_source_t *
233
+ pm_source_file_new(const char *filepath, pm_source_init_result_t *result) {
234
+ #ifdef _WIN32
235
+ /* Open the file for reading. */
236
+ pm_source_file_handle_t handle;
237
+ *result = pm_source_file_handle_open(&handle, filepath);
238
+ if (*result != PM_SOURCE_INIT_SUCCESS) return NULL;
239
+
240
+ /* Get the file size. */
241
+ const DWORD file_size = GetFileSize(handle.file, NULL);
242
+ if (file_size == INVALID_FILE_SIZE) {
243
+ pm_source_file_handle_close(&handle);
244
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
245
+ return NULL;
246
+ }
247
+
248
+ /* If the file is empty, return a constant source. */
249
+ if (file_size == 0) {
250
+ pm_source_file_handle_close(&handle);
251
+ *result = PM_SOURCE_INIT_SUCCESS;
252
+ return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT);
253
+ }
254
+
255
+ /* Create a buffer to read the file into. */
256
+ uint8_t *source = xmalloc(file_size);
257
+ if (source == NULL) {
258
+ pm_source_file_handle_close(&handle);
259
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
260
+ return NULL;
261
+ }
262
+
263
+ /* Read the contents of the file. */
264
+ DWORD bytes_read;
265
+ if (!ReadFile(handle.file, source, file_size, &bytes_read, NULL)) {
266
+ xfree_sized(source, file_size);
267
+ pm_source_file_handle_close(&handle);
268
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
269
+ return NULL;
270
+ }
271
+
272
+ /* Check the number of bytes read. */
273
+ if (bytes_read != file_size) {
274
+ xfree_sized(source, file_size);
275
+ pm_source_file_handle_close(&handle);
276
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
277
+ return NULL;
278
+ }
279
+
280
+ pm_source_file_handle_close(&handle);
281
+ *result = PM_SOURCE_INIT_SUCCESS;
282
+ return pm_source_alloc(source, (size_t) file_size, PM_SOURCE_OWNED);
283
+ #elif defined(PRISM_HAS_FILESYSTEM)
284
+ /* Open the file for reading. */
285
+ int fd = open(filepath, O_RDONLY);
286
+ if (fd == -1) {
287
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
288
+ return NULL;
289
+ }
290
+
291
+ /* Stat the file to get the file size. */
292
+ struct stat sb;
293
+ if (fstat(fd, &sb) == -1) {
294
+ close(fd);
295
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
296
+ return NULL;
297
+ }
298
+
299
+ /* Ensure it is a file and not a directory. */
300
+ if (S_ISDIR(sb.st_mode)) {
301
+ close(fd);
302
+ *result = PM_SOURCE_INIT_ERROR_DIRECTORY;
303
+ return NULL;
304
+ }
305
+
306
+ /* Check the size to see if it's empty. */
307
+ size_t size = (size_t) sb.st_size;
308
+ if (size == 0) {
309
+ close(fd);
310
+ *result = PM_SOURCE_INIT_SUCCESS;
311
+ return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT);
312
+ }
313
+
314
+ const size_t length = (size_t) size;
315
+ uint8_t *source = xmalloc(length);
316
+ if (source == NULL) {
317
+ close(fd);
318
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
319
+ return NULL;
320
+ }
321
+
322
+ ssize_t bytes_read = read(fd, source, length);
323
+ close(fd);
324
+
325
+ if (bytes_read == -1 || (size_t) bytes_read != length) {
326
+ xfree_sized(source, length);
327
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
328
+ return NULL;
329
+ }
330
+
331
+ *result = PM_SOURCE_INIT_SUCCESS;
332
+ return pm_source_alloc(source, length, PM_SOURCE_OWNED);
333
+ #else
334
+ (void) filepath;
335
+ *result = PM_SOURCE_INIT_ERROR_GENERIC;
336
+ perror("pm_source_file_new is not implemented for this platform");
337
+ return NULL;
338
+ #endif
339
+ }
340
+
341
+ /**
342
+ * Create a new source by reading from a stream. This allocates the source
343
+ * but does not read from the stream yet. Use pm_source_stream_read to read
344
+ * data.
345
+ */
346
+ pm_source_t *
347
+ pm_source_stream_new(void *stream, pm_source_stream_fgets_t *fgets, pm_source_stream_feof_t *feof) {
348
+ pm_source_t *source = pm_source_alloc(NULL, 0, PM_SOURCE_STREAM);
349
+ source->stream.buffer = pm_buffer_new();
350
+ source->stream.stream = stream;
351
+ source->stream.fgets = fgets;
352
+ source->stream.feof = feof;
353
+ source->stream.eof = false;
354
+
355
+ return source;
356
+ }
357
+
358
+ /**
359
+ * Read from the stream into the source's internal buffer until __END__ is
360
+ * encountered or EOF is reached. Updates the source pointer and length.
361
+ *
362
+ * Returns true if EOF was reached, false if __END__ was encountered.
363
+ */
364
+ bool
365
+ pm_source_stream_read(pm_source_t *source) {
366
+ pm_buffer_t *buffer = source->stream.buffer;
367
+
368
+ #define LINE_SIZE 4096
369
+ char line[LINE_SIZE];
370
+
371
+ /*
372
+ * A chunk read from the stream may legitimately contain embedded NUL bytes,
373
+ * so the NUL terminator written by fgets cannot be located with strlen.
374
+ * Instead we prefill the buffer with a non-NUL sentinel before each read;
375
+ * the terminator is then the only NUL at or after the data, so it can be
376
+ * found by scanning from the end. The value only needs to differ from '\0'.
377
+ */
378
+ #define LINE_FILL 0xff
379
+
380
+ while (memset(line, LINE_FILL, LINE_SIZE), source->stream.fgets(line, LINE_SIZE, source->stream.stream) != NULL) {
381
+ /*
382
+ * Locate the NUL terminator written by fgets (the last NUL in the
383
+ * buffer) to recover the length of the chunk, then drop the terminator.
384
+ */
385
+ size_t length = LINE_SIZE;
386
+ while (length > 0 && line[length - 1] != '\0') length--;
387
+ if (length > 0) length--;
388
+
389
+ /*
390
+ * An empty chunk means the stream returned an empty string instead of
391
+ * signaling EOF (a well-behaved stream never does this). We can't make
392
+ * progress on it, so stop reading rather than spinning forever.
393
+ */
394
+ if (length == 0) {
395
+ break;
396
+ }
397
+
398
+ /* Append the chunk to the buffer. */
399
+ pm_buffer_append_string(buffer, line, length);
400
+
401
+ bool newline = (line[length - 1] == '\n');
402
+
403
+ /*
404
+ * A chunk with no trailing newline is either a line longer than the
405
+ * read buffer (keep reading the rest of it) or the final line at EOF.
406
+ * This is the only place the stream's EOF state changes what we do
407
+ * next, so it is the only place we ask the stream whether it has hit
408
+ * EOF. A newline-terminated line needs no such check: if it happens to
409
+ * be the last line, the next fgets returns NULL and ends the loop.
410
+ */
411
+ if (!newline && !source->stream.feof(source->stream.stream)) {
412
+ continue;
413
+ }
414
+
415
+ /*
416
+ * Check if the line matches the __END__ marker. If it does, then stop
417
+ * reading and return false. In most circumstances, this means we should
418
+ * stop reading from the stream so that the DATA constant can pick it
419
+ * up.
420
+ */
421
+ switch (length) {
422
+ case 7:
423
+ if (strncmp(line, "__END__", 7) == 0) {
424
+ source->source = (const uint8_t *) pm_buffer_value(buffer);
425
+ source->length = pm_buffer_length(buffer);
426
+ return false;
427
+ }
428
+ break;
429
+ case 8:
430
+ if (strncmp(line, "__END__\n", 8) == 0) {
431
+ source->source = (const uint8_t *) pm_buffer_value(buffer);
432
+ source->length = pm_buffer_length(buffer);
433
+ return false;
434
+ }
435
+ break;
436
+ case 9:
437
+ if (strncmp(line, "__END__\r\n", 9) == 0) {
438
+ source->source = (const uint8_t *) pm_buffer_value(buffer);
439
+ source->length = pm_buffer_length(buffer);
440
+ return false;
441
+ }
442
+ break;
443
+ }
444
+
445
+ /*
446
+ * A chunk that reached here without a trailing newline is the final
447
+ * line at EOF (the check above would have continued otherwise), so
448
+ * there is nothing more to read.
449
+ */
450
+ if (!newline) {
451
+ break;
452
+ }
453
+ }
454
+
455
+ #undef LINE_FILL
456
+ #undef LINE_SIZE
457
+
458
+ source->stream.eof = true;
459
+ source->source = (const uint8_t *) pm_buffer_value(buffer);
460
+ source->length = pm_buffer_length(buffer);
461
+ return true;
462
+ }
463
+
464
+ /**
465
+ * Returns whether the stream source has reached EOF.
466
+ */
467
+ bool
468
+ pm_source_stream_eof(const pm_source_t *source) {
469
+ return source->stream.eof;
470
+ }
471
+
472
+ /**
473
+ * Free the given source and any memory it owns.
474
+ */
475
+ void
476
+ pm_source_free(pm_source_t *source) {
477
+ switch (source->type) {
478
+ case PM_SOURCE_CONSTANT:
479
+ case PM_SOURCE_SHARED:
480
+ /* No cleanup needed for the data. */
481
+ break;
482
+ case PM_SOURCE_OWNED:
483
+ xfree_sized((void *) source->source, source->length);
484
+ break;
485
+ case PM_SOURCE_MAPPED:
486
+ #if defined(_WIN32)
487
+ if (source->length > 0) {
488
+ UnmapViewOfFile((void *) source->source);
489
+ }
490
+ #elif defined(_POSIX_MAPPED_FILES)
491
+ if (source->length > 0) {
492
+ munmap((void *) source->source, source->length);
493
+ }
494
+ #endif
495
+ break;
496
+ case PM_SOURCE_STREAM:
497
+ pm_buffer_free(source->stream.buffer);
498
+ break;
499
+ }
500
+
501
+ xfree_sized(source, sizeof(pm_source_t));
502
+ }
503
+
504
+ /**
505
+ * Returns the length of the source data in bytes.
506
+ */
507
+ size_t
508
+ pm_source_length(const pm_source_t *source) {
509
+ return source->length;
510
+ }
511
+
512
+ /**
513
+ * Returns a pointer to the source data.
514
+ */
515
+ const uint8_t *
516
+ pm_source_source(const pm_source_t *source) {
517
+ return source->source;
518
+ }