Line | Count | Source |
1 | | #include <assert.h> |
2 | | #include <errno.h> |
3 | | #include <limits.h> |
4 | | #include <string.h> |
5 | | #include <stdio.h> |
6 | | #include <stdlib.h> |
7 | | #include <stdint.h> |
8 | | #include <sys/stat.h> |
9 | | #include <libgen.h> |
10 | | |
11 | | #ifdef WIN32 |
12 | | #include <shlwapi.h> |
13 | | #endif |
14 | | |
15 | | #include "jq_parser.h" |
16 | | #include "locfile.h" |
17 | | #include "jv.h" |
18 | | #include "jq.h" |
19 | | #include "util.h" |
20 | | #include "compile.h" |
21 | | #include "jv_alloc.h" |
22 | | |
23 | | struct lib_entry { |
24 | | char *name; |
25 | | block def; |
26 | | int loading; |
27 | | }; |
28 | | struct lib_loading_state { |
29 | | struct lib_entry *entries; |
30 | | uint64_t ct; |
31 | | }; |
32 | | static int load_library(jq_state *jq, jv lib_path, |
33 | | int is_data, int raw, int optional, |
34 | | const char *as, block *out_block, |
35 | | struct lib_loading_state *lib_state); |
36 | | |
37 | 0 | static int path_is_relative(jv p) { |
38 | 0 | const char *s = jv_string_value(p); |
39 | |
|
40 | | #ifdef WIN32 |
41 | | int res = PathIsRelativeA(s); |
42 | | #else |
43 | 0 | int res = *s != '/'; |
44 | 0 | #endif |
45 | 0 | jv_free(p); |
46 | 0 | return res; |
47 | 0 | } |
48 | | |
49 | | |
50 | | // Given a lib_path to search first, creates a chain of search paths |
51 | | // in the following order: |
52 | | // 1. lib_path |
53 | | // 2. -L paths passed in on the command line (from jq_state*) or builtin list |
54 | 5.75k | static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv lib_origin) { |
55 | 5.75k | assert(jv_get_kind(search_path) == JV_KIND_ARRAY); |
56 | 5.75k | jv expanded = jv_array(); |
57 | 5.75k | jv expanded_elt; |
58 | 5.75k | jv err = jv_null(); |
59 | 30.6k | jv_array_foreach(search_path, i, path) { |
60 | 30.6k | if (jv_get_kind(path) != JV_KIND_STRING) { |
61 | 0 | jv_free(path); |
62 | 0 | continue; |
63 | 0 | } |
64 | 30.6k | path = expand_path(path); |
65 | 30.6k | if (!jv_is_valid(path)) { |
66 | 0 | jv_free(err); |
67 | 0 | err = path; |
68 | 0 | path = jv_null(); |
69 | 0 | continue; |
70 | 0 | } |
71 | 30.6k | if (strcmp(".",jv_string_value(path)) == 0) { |
72 | 136 | expanded_elt = jv_copy(path); |
73 | 30.5k | } else if (strncmp("$ORIGIN/",jv_string_value(path),sizeof("$ORIGIN/") - 1) == 0) { |
74 | 38 | expanded_elt = jv_string_fmt("%s/%s", |
75 | 38 | jv_string_value(jq_origin), |
76 | 38 | jv_string_value(path) + sizeof ("$ORIGIN/") - 1); |
77 | 30.4k | } else if (jv_get_kind(lib_origin) == JV_KIND_STRING && |
78 | 0 | path_is_relative(jv_copy(path))) { |
79 | 0 | expanded_elt = jv_string_fmt("%s/%s", |
80 | 0 | jv_string_value(lib_origin), |
81 | 0 | jv_string_value(path)); |
82 | 30.4k | } else { |
83 | 30.4k | expanded_elt = path; |
84 | 30.4k | path = jv_invalid(); |
85 | 30.4k | } |
86 | 30.6k | expanded = jv_array_append(expanded, expanded_elt); |
87 | 30.6k | jv_free(path); |
88 | 30.6k | } |
89 | 5.75k | jv_free(jq_origin); |
90 | 5.75k | jv_free(lib_origin); |
91 | 5.75k | jv_free(search_path); |
92 | 5.75k | return JV_ARRAY(expanded, err); |
93 | 5.75k | } |
94 | | |
95 | | // Doesn't actually check that name not be an absolute path, and we |
96 | | // don't have to: we always append relative paths to others (with a '/' |
97 | | // in between). |
98 | 7.67k | static jv validate_relpath(jv name) { |
99 | 7.67k | const char *s = jv_string_value(name); |
100 | 7.67k | if (strlen(s) != (size_t)jv_string_length_bytes(jv_copy(name))) { |
101 | 120 | jv_free(name); |
102 | 120 | return jv_invalid_with_msg(jv_string("Module path contains a NUL byte")); |
103 | 120 | } |
104 | 7.55k | if (strchr(s, '\\')) { |
105 | 60 | jv res = jv_invalid_with_msg(jv_string_fmt("Modules must be named by relative paths using '/', not '\\' (%s)", s)); |
106 | 60 | jv_free(name); |
107 | 60 | return res; |
108 | 60 | } |
109 | 7.49k | jv components = jv_string_split(jv_copy(name), jv_string("/")); |
110 | 9.51k | jv_array_foreach(components, i, x) { |
111 | 9.51k | if (!strcmp(jv_string_value(x), "..")) { |
112 | 1.74k | jv_free(x); |
113 | 1.74k | jv_free(components); |
114 | 1.74k | jv res = jv_invalid_with_msg(jv_string_fmt("Relative paths to modules may not traverse to parent directories (%s)", s)); |
115 | 1.74k | jv_free(name); |
116 | 1.74k | return res; |
117 | 1.74k | } |
118 | 7.77k | if (i > 0 && jv_equal(jv_copy(x), jv_array_get(jv_copy(components), i - 1))) { |
119 | 0 | jv_free(x); |
120 | 0 | jv_free(components); |
121 | 0 | jv res = jv_invalid_with_msg(jv_string_fmt("module names must not have equal consecutive components: %s", |
122 | 0 | jv_string_value(name))); |
123 | 0 | jv_free(name); |
124 | 0 | return res; |
125 | 0 | } |
126 | 7.77k | jv_free(x); |
127 | 7.77k | } |
128 | 5.75k | jv_free(components); |
129 | 5.75k | return name; |
130 | 7.49k | } |
131 | | |
132 | | // Assumes name has been validated |
133 | 5.75k | static jv jv_basename(jv name) { |
134 | 5.75k | const char *s = jv_string_value(name); |
135 | 5.75k | const char *p = strrchr(s, '/'); |
136 | 5.75k | if (!p) |
137 | 5.66k | return name; |
138 | 91 | jv res = jv_string_fmt("%s", p); |
139 | 91 | jv_free(name); |
140 | 91 | return res; |
141 | 5.75k | } |
142 | | |
143 | | // Asummes validated relative path to module |
144 | 7.67k | static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) { |
145 | 7.67k | if (!jv_is_valid(rel_path)) { |
146 | 1.92k | jv_free(search); |
147 | 1.92k | jv_free(jq_origin); |
148 | 1.92k | jv_free(lib_origin); |
149 | 1.92k | return rel_path; |
150 | 1.92k | } |
151 | 5.75k | if (jv_get_kind(rel_path) != JV_KIND_STRING) { |
152 | 0 | jv_free(rel_path); |
153 | 0 | jv_free(search); |
154 | 0 | jv_free(jq_origin); |
155 | 0 | jv_free(lib_origin); |
156 | 0 | return jv_invalid_with_msg(jv_string_fmt("Module path must be a string")); |
157 | 0 | } |
158 | 5.75k | if (jv_get_kind(search) != JV_KIND_ARRAY) { |
159 | 0 | jv_free(rel_path); |
160 | 0 | jv_free(search); |
161 | 0 | jv_free(jq_origin); |
162 | 0 | jv_free(lib_origin); |
163 | 0 | return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array")); |
164 | 0 | } |
165 | | |
166 | 5.75k | struct stat st; |
167 | 5.75k | int ret; |
168 | | |
169 | | // Ideally we should cache this somewhere |
170 | 5.75k | search = build_lib_search_chain(jq, search, jq_origin, lib_origin); |
171 | 5.75k | jv err = jv_array_get(jv_copy(search), 1); |
172 | 5.75k | search = jv_array_get(search, 0); |
173 | | |
174 | 5.75k | jv bname = jv_basename(jv_copy(rel_path)); |
175 | | |
176 | 30.6k | jv_array_foreach(search, i, spath) { |
177 | 30.6k | if (jv_get_kind(spath) == JV_KIND_NULL) { |
178 | 0 | jv_free(spath); |
179 | 0 | break; |
180 | 0 | } |
181 | 30.6k | if (jv_get_kind(spath) != JV_KIND_STRING || |
182 | 30.6k | strcmp(jv_string_value(spath), "") == 0) { |
183 | 19.7k | jv_free(spath); |
184 | 19.7k | continue; /* XXX report non-strings in search path?? */ |
185 | 19.7k | } |
186 | | // Try ${search_dir}/${rel_path}.jq |
187 | 10.9k | jv testpath = jq_realpath(jv_string_fmt("%s/%s%s", |
188 | 10.9k | jv_string_value(spath), |
189 | 10.9k | jv_string_value(rel_path), |
190 | 10.9k | suffix)); |
191 | 10.9k | ret = stat(jv_string_value(testpath),&st); |
192 | 10.9k | if (ret == -1 && errno == ENOENT) { |
193 | 10.8k | jv_free(testpath); |
194 | | // Try ${search_dir}/$(dirname ${rel_path})/jq/main.jq |
195 | 10.8k | testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s", |
196 | 10.8k | jv_string_value(spath), |
197 | 10.8k | jv_string_value(rel_path), |
198 | 10.8k | "jq/main", |
199 | 10.8k | suffix)); |
200 | 10.8k | ret = stat(jv_string_value(testpath),&st); |
201 | 10.8k | } |
202 | 10.9k | if (ret == -1 && errno == ENOENT) { |
203 | 10.8k | jv_free(testpath); |
204 | | // Try ${search_dir}/${rel_path}/$(basename ${rel_path}).jq |
205 | 10.8k | testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s", |
206 | 10.8k | jv_string_value(spath), |
207 | 10.8k | jv_string_value(rel_path), |
208 | 10.8k | jv_string_value(bname), |
209 | 10.8k | suffix)); |
210 | 10.8k | ret = stat(jv_string_value(testpath),&st); |
211 | 10.8k | } |
212 | 10.9k | if (ret == 0) { |
213 | 0 | jv_free(err); |
214 | 0 | jv_free(rel_path); |
215 | 0 | jv_free(search); |
216 | 0 | jv_free(bname); |
217 | 0 | jv_free(spath); |
218 | 0 | return testpath; |
219 | 0 | } |
220 | 10.9k | jv_free(testpath); |
221 | 10.9k | jv_free(spath); |
222 | 10.9k | } |
223 | 5.75k | jv output; |
224 | 5.75k | if (!jv_is_valid(err)) { |
225 | 0 | err = jv_invalid_get_msg(err); |
226 | 0 | output = jv_invalid_with_msg(jv_string_fmt("module not found: %s (%s)", |
227 | 0 | jv_string_value(rel_path), |
228 | 0 | jv_string_value(err))); |
229 | 5.75k | } else { |
230 | 5.75k | output = jv_invalid_with_msg(jv_string_fmt("module not found: %s", |
231 | 5.75k | jv_string_value(rel_path))); |
232 | 5.75k | } |
233 | 5.75k | jv_free(err); |
234 | 5.75k | jv_free(rel_path); |
235 | 5.75k | jv_free(search); |
236 | 5.75k | jv_free(bname); |
237 | 5.75k | return output; |
238 | 5.75k | } |
239 | | |
240 | 3.25k | static jv default_search(jq_state *jq, jv value) { |
241 | 3.25k | if (!jv_is_valid(value)) { |
242 | | // dependent didn't say; prepend . to system search path listj |
243 | 4 | jv_free(value); |
244 | 4 | return jv_array_concat(JV_ARRAY(jv_string(".")), jq_get_lib_dirs(jq)); |
245 | 4 | } |
246 | 3.25k | if (jv_get_kind(value) != JV_KIND_ARRAY) |
247 | 3.23k | return JV_ARRAY(value); |
248 | 22 | return value; |
249 | 3.25k | } |
250 | | |
251 | | // XXX Split this into a util that takes a callback, and then... |
252 | 3.25k | static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) { |
253 | 3.25k | jv deps = block_take_imports(src_block); |
254 | 3.25k | block bk = *src_block; |
255 | 3.25k | int nerrors = 0; |
256 | | |
257 | | // XXX This is a backward jv_array_foreach because bindings go in reverse |
258 | 6.48k | for (int i = jv_array_length(jv_copy(deps)); i > 0; ) { |
259 | 3.25k | i--; |
260 | 3.25k | jv dep = jv_array_get(jv_copy(deps), i); |
261 | | |
262 | 3.25k | const char *as_str = NULL; |
263 | 3.25k | int is_data = jv_get_kind(jv_object_get(jv_copy(dep), jv_string("is_data"))) == JV_KIND_TRUE; |
264 | 3.25k | int raw = 0; |
265 | 3.25k | jv v = jv_object_get(jv_copy(dep), jv_string("raw")); |
266 | 3.25k | if (jv_get_kind(v) == JV_KIND_TRUE) |
267 | 0 | raw = 1; |
268 | 3.25k | int optional = 0; |
269 | 3.25k | if (jv_get_kind(jv_object_get(jv_copy(dep), jv_string("optional"))) == JV_KIND_TRUE) |
270 | 3.22k | optional = 1; |
271 | 3.25k | jv_free(v); |
272 | 3.25k | jv relpath = validate_relpath(jv_object_get(jv_copy(dep), jv_string("relpath"))); |
273 | 3.25k | jv as = jv_object_get(jv_copy(dep), jv_string("as")); |
274 | 3.25k | assert(!jv_is_valid(as) || jv_get_kind(as) == JV_KIND_STRING); |
275 | 3.25k | if (jv_get_kind(as) == JV_KIND_STRING) |
276 | 29 | as_str = jv_string_value(as); |
277 | 3.25k | jv search = default_search(jq, jv_object_get(dep, jv_string("search"))); |
278 | | // dep is now freed; do not reuse |
279 | | |
280 | | // find_lib does a lot of work that could be cached... |
281 | 3.25k | jv resolved = find_lib(jq, relpath, search, is_data ? ".json" : ".jq", jv_copy(jq_origin), jv_copy(lib_origin)); |
282 | | // XXX ...move the rest of this into a callback. |
283 | 3.25k | if (!jv_is_valid(resolved)) { |
284 | 3.25k | jv_free(as); |
285 | 3.25k | if (optional) { |
286 | 3.22k | jv_free(resolved); |
287 | 3.22k | continue; |
288 | 3.22k | } |
289 | 29 | jv emsg = jv_invalid_get_msg(resolved); |
290 | 29 | jq_report_error(jq, jv_string_fmt("jq: error: %s\n",jv_string_value(emsg))); |
291 | 29 | jv_free(emsg); |
292 | 29 | jv_free(deps); |
293 | 29 | jv_free(jq_origin); |
294 | 29 | jv_free(lib_origin); |
295 | 29 | return 1; |
296 | 3.25k | } |
297 | | |
298 | 0 | if (is_data) { |
299 | | // Can't reuse data libs because the wrong name is bound |
300 | 0 | block dep_def_block; |
301 | 0 | nerrors += load_library(jq, resolved, is_data, raw, optional, as_str, &dep_def_block, lib_state); |
302 | 0 | if (nerrors == 0) { |
303 | | // Bind as both $data::data and $data for backward compatibility vs common sense |
304 | 0 | bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, as_str); |
305 | 0 | bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, NULL); |
306 | 0 | } |
307 | 0 | } else { |
308 | 0 | uint64_t state_idx = 0; |
309 | 0 | for (; state_idx < lib_state->ct; ++state_idx) { |
310 | 0 | if (strcmp(lib_state->entries[state_idx].name, jv_string_value(resolved)) == 0) |
311 | 0 | break; |
312 | 0 | } |
313 | |
|
314 | 0 | if (state_idx < lib_state->ct) { // Found |
315 | 0 | if (lib_state->entries[state_idx].loading) { |
316 | 0 | jq_report_error(jq, jv_string_fmt("jq: error: circular import of %s\n", |
317 | 0 | jv_string_value(resolved))); |
318 | 0 | jv_free(resolved); |
319 | 0 | jv_free(as); |
320 | 0 | jv_free(deps); |
321 | 0 | jv_free(jq_origin); |
322 | 0 | jv_free(lib_origin); |
323 | 0 | return 1; |
324 | 0 | } |
325 | 0 | jv_free(resolved); |
326 | | // Bind the library to the program |
327 | 0 | bk = block_bind_library(lib_state->entries[state_idx].def, bk, OP_IS_CALL_PSEUDO, as_str); |
328 | 0 | } else { // Not found. Add it to the table before binding. |
329 | 0 | block dep_def_block = gen_noop(); |
330 | 0 | nerrors += load_library(jq, resolved, is_data, raw, optional, as_str, &dep_def_block, lib_state); |
331 | | // resolved has been freed |
332 | 0 | if (nerrors == 0) { |
333 | | // Bind the library to the program |
334 | 0 | bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, as_str); |
335 | 0 | } |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | 0 | jv_free(as); |
340 | 0 | } |
341 | 3.22k | jv_free(lib_origin); |
342 | 3.22k | jv_free(jq_origin); |
343 | 3.22k | jv_free(deps); |
344 | 3.22k | return nerrors; |
345 | 3.25k | } |
346 | | |
347 | | // Loads the library at lib_path into lib_state, putting the library's defs |
348 | | // into *out_block |
349 | 0 | static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, int optional, const char *as, block *out_block, struct lib_loading_state *lib_state) { |
350 | 0 | int nerrors = 0; |
351 | 0 | struct locfile* src = NULL; |
352 | 0 | block program; |
353 | 0 | jv data; |
354 | 0 | if (is_data && !raw) |
355 | 0 | data = jv_load_file(jv_string_value(lib_path), 0); |
356 | 0 | else |
357 | 0 | data = jv_load_file(jv_string_value(lib_path), 1); |
358 | 0 | int state_idx; |
359 | 0 | if (!jv_is_valid(data)) { |
360 | 0 | program = gen_noop(); |
361 | 0 | if (!optional) { |
362 | 0 | if (jv_invalid_has_msg(jv_copy(data))) |
363 | 0 | data = jv_invalid_get_msg(data); |
364 | 0 | else |
365 | 0 | data = jv_string("unknown error"); |
366 | 0 | jq_report_error(jq, jv_string_fmt("jq: error loading data file %s: %s\n", jv_string_value(lib_path), jv_string_value(data))); |
367 | 0 | nerrors++; |
368 | 0 | } |
369 | 0 | } else if (is_data) { |
370 | | // import "foo" as $bar; |
371 | 0 | program = gen_const_global(jv_copy(data), as); |
372 | 0 | state_idx = lib_state->ct++; |
373 | 0 | lib_state->entries = jv_mem_realloc(lib_state->entries, lib_state->ct * sizeof(struct lib_entry)); |
374 | 0 | lib_state->entries[state_idx].name = strdup(jv_string_value(lib_path)); |
375 | 0 | lib_state->entries[state_idx].def = program; |
376 | 0 | lib_state->entries[state_idx].loading = 0; |
377 | 0 | } else { |
378 | | // import "foo" as bar; |
379 | 0 | src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data))); |
380 | 0 | nerrors += jq_parse_library(src, &program); |
381 | 0 | locfile_free(src); |
382 | 0 | if (nerrors == 0) { |
383 | | // Register the library before processing its dependencies so that |
384 | | // circular imports can be detected. |
385 | 0 | state_idx = lib_state->ct++; |
386 | 0 | lib_state->entries = jv_mem_realloc(lib_state->entries, lib_state->ct * sizeof(struct lib_entry)); |
387 | 0 | lib_state->entries[state_idx].name = strdup(jv_string_value(lib_path)); |
388 | 0 | lib_state->entries[state_idx].def = gen_noop(); |
389 | 0 | lib_state->entries[state_idx].loading = 1; |
390 | |
|
391 | 0 | char *lib_origin = strdup(jv_string_value(lib_path)); |
392 | 0 | nerrors += process_dependencies(jq, jq_get_jq_origin(jq), |
393 | 0 | jv_string(dirname(lib_origin)), |
394 | 0 | &program, lib_state); |
395 | 0 | free(lib_origin); |
396 | 0 | program = block_bind_self(program, OP_IS_CALL_PSEUDO); |
397 | 0 | lib_state->entries[state_idx].def = program; |
398 | 0 | lib_state->entries[state_idx].loading = 0; |
399 | 0 | } |
400 | 0 | } |
401 | 0 | *out_block = program; |
402 | 0 | jv_free(lib_path); |
403 | 0 | jv_free(data); |
404 | 0 | return nerrors; |
405 | 0 | } |
406 | | |
407 | | // FIXME It'd be nice to have an option to search the same search path |
408 | | // as we do in process_dependencies. |
409 | 4.41k | jv load_module_meta(jq_state *jq, jv mod_relpath) { |
410 | | // We can't know the caller's origin; we could though, if it was passed in |
411 | 4.41k | jv lib_path = find_lib(jq, validate_relpath(mod_relpath), jq_get_lib_dirs(jq), ".jq", jq_get_jq_origin(jq), jv_null()); |
412 | 4.41k | if (!jv_is_valid(lib_path)) |
413 | 4.41k | return lib_path; |
414 | 0 | jv meta = jv_null(); |
415 | 0 | jv data = jv_load_file(jv_string_value(lib_path), 1); |
416 | 0 | if (jv_is_valid(data)) { |
417 | 0 | block program; |
418 | 0 | struct locfile* src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data))); |
419 | 0 | int nerrors = jq_parse_library(src, &program); |
420 | 0 | if (nerrors == 0) { |
421 | 0 | meta = block_module_meta(program); |
422 | 0 | if (jv_get_kind(meta) == JV_KIND_NULL) |
423 | 0 | meta = jv_object(); |
424 | 0 | meta = jv_object_set(meta, jv_string("deps"), block_take_imports(&program)); |
425 | 0 | meta = jv_object_set(meta, jv_string("defs"), block_list_funcs(program, 0)); |
426 | 0 | } |
427 | 0 | locfile_free(src); |
428 | 0 | block_free(program); |
429 | 0 | } |
430 | 0 | jv_free(lib_path); |
431 | 0 | jv_free(data); |
432 | 0 | return meta; |
433 | 4.41k | } |
434 | | |
435 | 3.81k | int load_program(jq_state *jq, struct locfile* src, block *out_block) { |
436 | 3.81k | int nerrors = 0; |
437 | 3.81k | block program; |
438 | 3.81k | struct lib_loading_state lib_state = {0,0}; |
439 | 3.81k | nerrors = jq_parse(src, &program); |
440 | 3.81k | if (nerrors) |
441 | 554 | return nerrors; |
442 | | |
443 | 3.26k | if (!block_has_main(program)) { |
444 | 5 | jq_report_error(jq, jv_string("jq: error: Top-level program not given (try \".\")")); |
445 | 5 | block_free(program); |
446 | 5 | return 1; |
447 | 5 | } |
448 | | |
449 | 3.25k | jv home = get_home(); |
450 | 3.25k | if (jv_is_valid(home)) { |
451 | | /* Import ~/.jq as a library named "" found in $HOME or %USERPROFILE% */ |
452 | 3.25k | block import = gen_import_meta(gen_import(jv_string(""), jv_invalid(), 0), |
453 | 3.25k | gen_const(JV_OBJECT( |
454 | 3.25k | jv_string("optional"), jv_true(), |
455 | 3.25k | jv_string("search"), home))); |
456 | 3.25k | program = BLOCK(import, program); |
457 | 3.25k | } else { // silently ignore if home dir not determined |
458 | 0 | jv_free(home); |
459 | 0 | } |
460 | | |
461 | 3.25k | nerrors = process_dependencies(jq, jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state); |
462 | 3.25k | block libs = gen_noop(); |
463 | 3.25k | for (uint64_t i = 0; i < lib_state.ct; ++i) { |
464 | 0 | free(lib_state.entries[i].name); |
465 | 0 | if (nerrors == 0 && !block_is_const(lib_state.entries[i].def)) |
466 | 0 | libs = block_join(libs, lib_state.entries[i].def); |
467 | 0 | else |
468 | 0 | block_free(lib_state.entries[i].def); |
469 | 0 | } |
470 | 3.25k | free(lib_state.entries); |
471 | 3.25k | if (nerrors) |
472 | 29 | block_free(program); |
473 | 3.22k | else |
474 | 3.22k | *out_block = block_drop_unreferenced(block_join(libs, program)); |
475 | | |
476 | 3.25k | return nerrors; |
477 | 3.26k | } |