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.
- checksums.yaml +7 -0
- data/BSDmakefile +58 -0
- data/CHANGELOG.md +870 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +58 -0
- data/LICENSE.md +7 -0
- data/Makefile +145 -0
- data/README.md +81 -0
- data/config.yml +4752 -0
- data/docs/build_system.md +119 -0
- data/docs/configuration.md +69 -0
- data/docs/cruby_compilation.md +27 -0
- data/docs/design.md +53 -0
- data/docs/encoding.md +121 -0
- data/docs/fuzzing.md +81 -0
- data/docs/heredocs.md +36 -0
- data/docs/javascript.md +133 -0
- data/docs/local_variable_depth.md +229 -0
- data/docs/mapping.md +117 -0
- data/docs/parser_translation.md +24 -0
- data/docs/parsing_rules.md +22 -0
- data/docs/releasing.md +147 -0
- data/docs/relocation.md +32 -0
- data/docs/ripper_translation.md +63 -0
- data/docs/ruby_api.md +45 -0
- data/docs/ruby_parser_translation.md +19 -0
- data/docs/serialization.md +230 -0
- data/docs/testing.md +72 -0
- data/ext/prism/api_node.c +7005 -0
- data/ext/prism/extconf.rb +126 -0
- data/ext/prism/extension.c +1783 -0
- data/ext/prism/extension.h +19 -0
- data/include/prism/arena.h +37 -0
- data/include/prism/ast.h +10371 -0
- data/include/prism/buffer.h +52 -0
- data/include/prism/comments.h +55 -0
- data/include/prism/compiler/accel.h +24 -0
- data/include/prism/compiler/align.h +36 -0
- data/include/prism/compiler/assume.h +25 -0
- data/include/prism/compiler/exported.h +24 -0
- data/include/prism/compiler/fallthrough.h +22 -0
- data/include/prism/compiler/filesystem.h +32 -0
- data/include/prism/compiler/flex_array.h +19 -0
- data/include/prism/compiler/force_inline.h +21 -0
- data/include/prism/compiler/format.h +25 -0
- data/include/prism/compiler/inline.h +17 -0
- data/include/prism/compiler/nodiscard.h +22 -0
- data/include/prism/compiler/nonnull.h +18 -0
- data/include/prism/compiler/unused.h +18 -0
- data/include/prism/constant_pool.h +81 -0
- data/include/prism/diagnostic.h +93 -0
- data/include/prism/errors_format.h +53 -0
- data/include/prism/excludes.h +29 -0
- data/include/prism/integer.h +41 -0
- data/include/prism/internal/allocator.h +68 -0
- data/include/prism/internal/allocator_debug.h +88 -0
- data/include/prism/internal/arena.h +108 -0
- data/include/prism/internal/bit.h +42 -0
- data/include/prism/internal/buffer.h +91 -0
- data/include/prism/internal/char.h +145 -0
- data/include/prism/internal/comments.h +53 -0
- data/include/prism/internal/constant_pool.h +152 -0
- data/include/prism/internal/diagnostic.h +389 -0
- data/include/prism/internal/encoding.h +242 -0
- data/include/prism/internal/integer.h +68 -0
- data/include/prism/internal/isinf.h +16 -0
- data/include/prism/internal/line_offset_list.h +34 -0
- data/include/prism/internal/list.h +62 -0
- data/include/prism/internal/magic_comments.h +23 -0
- data/include/prism/internal/memchr.h +15 -0
- data/include/prism/internal/node.h +38 -0
- data/include/prism/internal/options.h +232 -0
- data/include/prism/internal/parser.h +984 -0
- data/include/prism/internal/regexp.h +26 -0
- data/include/prism/internal/serialize.h +39 -0
- data/include/prism/internal/source.h +72 -0
- data/include/prism/internal/static_literals.h +100 -0
- data/include/prism/internal/stringy.h +35 -0
- data/include/prism/internal/strncasecmp.h +18 -0
- data/include/prism/internal/strpbrk.h +33 -0
- data/include/prism/internal/tokens.h +11 -0
- data/include/prism/json.h +32 -0
- data/include/prism/line_offset_list.h +61 -0
- data/include/prism/magic_comments.h +35 -0
- data/include/prism/node.h +94 -0
- data/include/prism/options.h +354 -0
- data/include/prism/parser.h +404 -0
- data/include/prism/prettyprint.h +31 -0
- data/include/prism/serialize.h +115 -0
- data/include/prism/source.h +155 -0
- data/include/prism/stream.h +28 -0
- data/include/prism/string_query.h +63 -0
- data/include/prism/stringy.h +72 -0
- data/include/prism/version.h +38 -0
- data/include/prism.h +131 -0
- data/lib/prism/3.3/prism.so +0 -0
- data/lib/prism/3.4/prism.so +0 -0
- data/lib/prism/4.0/prism.so +0 -0
- data/lib/prism/compiler.rb +815 -0
- data/lib/prism/desugar_compiler.rb +463 -0
- data/lib/prism/dispatcher.rb +2383 -0
- data/lib/prism/dot_visitor.rb +4848 -0
- data/lib/prism/dsl.rb +1369 -0
- data/lib/prism/ffi.rb +740 -0
- data/lib/prism/inspect_visitor.rb +2425 -0
- data/lib/prism/lex_compat.rb +953 -0
- data/lib/prism/mutation_compiler.rb +780 -0
- data/lib/prism/node.rb +33204 -0
- data/lib/prism/node_ext.rb +504 -0
- data/lib/prism/node_find.rb +215 -0
- data/lib/prism/parse_result/errors.rb +72 -0
- data/lib/prism/parse_result/newlines.rb +423 -0
- data/lib/prism/parse_result.rb +1221 -0
- data/lib/prism/pattern.rb +314 -0
- data/lib/prism/polyfill/append_as_bytes.rb +15 -0
- data/lib/prism/polyfill/byteindex.rb +13 -0
- data/lib/prism/polyfill/scan_byte.rb +14 -0
- data/lib/prism/polyfill/unpack1.rb +14 -0
- data/lib/prism/polyfill/warn.rb +36 -0
- data/lib/prism/reflection.rb +427 -0
- data/lib/prism/relocation.rb +567 -0
- data/lib/prism/serialize.rb +5308 -0
- data/lib/prism/string_query.rb +46 -0
- data/lib/prism/translation/parser/builder.rb +70 -0
- data/lib/prism/translation/parser/compiler.rb +2226 -0
- data/lib/prism/translation/parser/lexer.rb +769 -0
- data/lib/prism/translation/parser.rb +376 -0
- data/lib/prism/translation/parser_current.rb +26 -0
- data/lib/prism/translation/parser_versions.rb +36 -0
- data/lib/prism/translation/ripper/filter.rb +53 -0
- data/lib/prism/translation/ripper/lexer.rb +133 -0
- data/lib/prism/translation/ripper/sexp.rb +118 -0
- data/lib/prism/translation/ripper/shim.rb +7 -0
- data/lib/prism/translation/ripper.rb +4278 -0
- data/lib/prism/translation/ruby_parser.rb +1721 -0
- data/lib/prism/translation.rb +20 -0
- data/lib/prism/visitor.rb +1440 -0
- data/lib/prism.rb +168 -0
- data/prism.gemspec +234 -0
- data/rbi/generated/prism/compiler.rbi +489 -0
- data/rbi/generated/prism/desugar_compiler.rbi +306 -0
- data/rbi/generated/prism/dispatcher.rbi +1138 -0
- data/rbi/generated/prism/dot_visitor.rbi +618 -0
- data/rbi/generated/prism/dsl.rbi +751 -0
- data/rbi/generated/prism/inspect_visitor.rbi +503 -0
- data/rbi/generated/prism/lex_compat.rbi +162 -0
- data/rbi/generated/prism/mutation_compiler.rbi +464 -0
- data/rbi/generated/prism/node.rbi +13681 -0
- data/rbi/generated/prism/node_ext.rbi +259 -0
- data/rbi/generated/prism/node_find.rbi +79 -0
- data/rbi/generated/prism/parse_result/errors.rbi +21 -0
- data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
- data/rbi/generated/prism/parse_result.rbi +764 -0
- data/rbi/generated/prism/pattern.rbi +134 -0
- data/rbi/generated/prism/reflection.rbi +89 -0
- data/rbi/generated/prism/relocation.rbi +329 -0
- data/rbi/generated/prism/serialize.rbi +181 -0
- data/rbi/generated/prism/string_query.rbi +36 -0
- data/rbi/generated/prism/translation.rbi +7 -0
- data/rbi/generated/prism/visitor.rbi +650 -0
- data/rbi/generated/prism.rbi +86 -0
- data/rbi/prism/translation/parser.rbi +11 -0
- data/rbi/prism/translation/parser_versions.rbi +28 -0
- data/rbi/prism/translation/ripper.rbi +15 -0
- data/rbi/rubyvm/node_find.rbi +14 -0
- data/sig/generated/prism/compiler.rbs +492 -0
- data/sig/generated/prism/desugar_compiler.rbs +305 -0
- data/sig/generated/prism/dispatcher.rbs +1292 -0
- data/sig/generated/prism/dot_visitor.rbs +635 -0
- data/sig/generated/prism/dsl.rbs +928 -0
- data/sig/generated/prism/inspect_visitor.rbs +509 -0
- data/sig/generated/prism/lex_compat.rbs +166 -0
- data/sig/generated/prism/mutation_compiler.rbs +464 -0
- data/sig/generated/prism/node.rbs +20160 -0
- data/sig/generated/prism/node_ext.rbs +290 -0
- data/sig/generated/prism/node_find.rbs +90 -0
- data/sig/generated/prism/parse_result/errors.rbs +22 -0
- data/sig/generated/prism/parse_result/newlines.rbs +223 -0
- data/sig/generated/prism/parse_result.rbs +859 -0
- data/sig/generated/prism/pattern.rbs +156 -0
- data/sig/generated/prism/reflection.rbs +101 -0
- data/sig/generated/prism/relocation.rbs +428 -0
- data/sig/generated/prism/serialize.rbs +195 -0
- data/sig/generated/prism/string_query.rbs +36 -0
- data/sig/generated/prism/translation.rbs +8 -0
- data/sig/generated/prism/visitor.rbs +1111 -0
- data/sig/generated/prism.rbs +84 -0
- data/sig/manifest.yaml +2 -0
- data/src/arena.c +117 -0
- data/src/buffer.c +380 -0
- data/src/char.c +289 -0
- data/src/comments.c +454 -0
- data/src/constant_pool.c +406 -0
- data/src/diagnostic.c +886 -0
- data/src/encoding.c +5345 -0
- data/src/errors_format.c +519 -0
- data/src/integer.c +681 -0
- data/src/json.c +5733 -0
- data/src/line_offset_list.c +100 -0
- data/src/list.c +24 -0
- data/src/memchr.c +37 -0
- data/src/node.c +4515 -0
- data/src/options.c +451 -0
- data/src/parser.c +349 -0
- data/src/prettyprint.c +9024 -0
- data/src/prism.c +23739 -0
- data/src/regexp.c +1706 -0
- data/src/serialize.c +2309 -0
- data/src/source.c +518 -0
- data/src/static_literals.c +685 -0
- data/src/string_query.c +166 -0
- data/src/stringy.c +91 -0
- data/src/strncasecmp.c +37 -0
- data/src/strpbrk.c +447 -0
- data/src/tokens.c +724 -0
- 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
|
+
}
|