| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | #include <assert.h>
 | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  | #include <errno.h>
 | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | #include <limits.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <stdint.h>
 | 
					
						
							|  |  |  | #include <sys/stat.h>
 | 
					
						
							|  |  |  | #include <libgen.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "jq_parser.h"
 | 
					
						
							|  |  |  | #include "locfile.h"
 | 
					
						
							|  |  |  | #include "jv.h"
 | 
					
						
							|  |  |  | #include "jq.h"
 | 
					
						
							|  |  |  | #include "parser.h"
 | 
					
						
							|  |  |  | #include "util.h"
 | 
					
						
							|  |  |  | #include "compile.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct lib_loading_state { | 
					
						
							|  |  |  |   char **names; | 
					
						
							|  |  |  |   block *defs; | 
					
						
							|  |  |  |   uint64_t ct; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, | 
					
						
							|  |  |  |                         const char *as, block *out_block, | 
					
						
							|  |  |  |                         struct lib_loading_state *lib_state); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Given a lib_path to search first, creates a chain of search paths
 | 
					
						
							|  |  |  | // in the following order:
 | 
					
						
							|  |  |  | // 1. lib_path
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | // 2. -L paths passed in on the command line (from jq_state*) or builtin list
 | 
					
						
							|  |  |  | static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv lib_origin) { | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |   assert(jv_get_kind(search_path) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   jv expanded = jv_array(); | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   jv expanded_elt; | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |   jv_array_foreach(search_path, i, path) { | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     if (strcmp(".",jv_string_value(path)) == 0) { | 
					
						
							|  |  |  |       expanded_elt = jv_copy(path); | 
					
						
							|  |  |  |     } else if (jv_get_kind(lib_origin) == JV_KIND_STRING && | 
					
						
							|  |  |  |                strncmp("./", jv_string_value(path),sizeof("./")-1) == 0) { | 
					
						
							|  |  |  |       expanded_elt = jv_string_fmt("%s/%s", | 
					
						
							|  |  |  |                                jv_string_value(lib_origin), | 
					
						
							|  |  |  |                                jv_string_value(path) + sizeof ("./") - 1); | 
					
						
							|  |  |  |     } else if (strncmp("$ORIGIN/",jv_string_value(path),sizeof("$ORIGIN/")-1) == 0) { | 
					
						
							|  |  |  |       expanded_elt = jv_string_fmt("%s/%s", | 
					
						
							|  |  |  |                                jv_string_value(jq_origin), | 
					
						
							|  |  |  |                                jv_string_value(path) + sizeof ("$ORIGIN/") - 1); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       expanded_elt = expand_path(path); | 
					
						
							|  |  |  |       if (!jv_is_valid(expanded_elt)) { | 
					
						
							|  |  |  |         jv_free(search_path); | 
					
						
							|  |  |  |         jv_free(expanded); | 
					
						
							|  |  |  |         jv_free(path); | 
					
						
							|  |  |  |         return expanded_elt; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       path = jv_invalid(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expanded = jv_array_append(expanded, expanded_elt); | 
					
						
							|  |  |  |     jv_free(path); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   jv_free(jq_origin); | 
					
						
							|  |  |  |   jv_free(lib_origin); | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |   jv_free(search_path); | 
					
						
							|  |  |  |   return expanded; | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-02 19:16:43 -06:00
										 |  |  | // Doesn't actually check that name not be an absolute path, and we
 | 
					
						
							|  |  |  | // don't have to: we always append relative paths to others (with a '/'
 | 
					
						
							|  |  |  | // in between).
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | static jv validate_relpath(jv name) { | 
					
						
							|  |  |  |   const char *s = jv_string_value(name); | 
					
						
							|  |  |  |   if (strchr(s, '\\')) { | 
					
						
							|  |  |  |     jv res = jv_invalid_with_msg(jv_string_fmt("Modules must be named by relative paths using '/', not '\\' (%s)", s)); | 
					
						
							|  |  |  |     jv_free(name); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv components = jv_string_split(jv_copy(name), jv_string("/")); | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |   jv rp = jv_array_get(jv_copy(components), 0); | 
					
						
							|  |  |  |   components = jv_array_slice(components, 1, jv_array_length(jv_copy(components))); | 
					
						
							|  |  |  |   jv_array_foreach(components, i, x) { | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     if (!strcmp(jv_string_value(x), "..")) { | 
					
						
							|  |  |  |       jv_free(x); | 
					
						
							|  |  |  |       jv_free(rp); | 
					
						
							|  |  |  |       jv_free(components); | 
					
						
							|  |  |  |       jv res = jv_invalid_with_msg(jv_string_fmt("Relative paths to modules may not traverse to parent directories (%s)", s)); | 
					
						
							|  |  |  |       jv_free(name); | 
					
						
							|  |  |  |       return res; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |     if (i > 0 && jv_equal(jv_copy(x), jv_array_get(jv_copy(components), i - 1))) { | 
					
						
							|  |  |  |       jv_free(x); | 
					
						
							|  |  |  |       jv_free(rp); | 
					
						
							|  |  |  |       jv_free(components); | 
					
						
							|  |  |  |       jv res = jv_invalid_with_msg(jv_string_fmt("module names must not have equal consecutive components: %s", | 
					
						
							|  |  |  |                                                  jv_string_value(name))); | 
					
						
							|  |  |  |       jv_free(name); | 
					
						
							|  |  |  |       return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     rp = jv_string_concat(rp, jv_string_concat(jv_string("/"), x)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(components); | 
					
						
							|  |  |  |   jv_free(name); | 
					
						
							|  |  |  |   return rp; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | // Assumes name has been validated
 | 
					
						
							|  |  |  | static jv jv_basename(jv name) { | 
					
						
							|  |  |  |   const char *s = jv_string_value(name); | 
					
						
							|  |  |  |   const char *p = strrchr(s, '/'); | 
					
						
							|  |  |  |   if (!p) | 
					
						
							|  |  |  |     return name; | 
					
						
							|  |  |  |   jv res = jv_string_fmt("%s", p); | 
					
						
							|  |  |  |   jv_free(name); | 
					
						
							|  |  |  |   return res; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | // Asummes validated relative path to module
 | 
					
						
							|  |  |  | static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) { | 
					
						
							|  |  |  |   if (jv_get_kind(search) != JV_KIND_ARRAY) | 
					
						
							|  |  |  |     return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array")); | 
					
						
							|  |  |  |   if (jv_get_kind(rel_path) != JV_KIND_STRING) | 
					
						
							|  |  |  |     return jv_invalid_with_msg(jv_string_fmt("Module path must be a string")); | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   struct stat st; | 
					
						
							|  |  |  |   int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   // Ideally we should cache this somewhere
 | 
					
						
							|  |  |  |   search = build_lib_search_chain(jq, search, jq_origin, lib_origin); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   jv bname = jv_basename(jv_copy(rel_path)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   jv_array_foreach(search, i, spath) { | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |     if (jv_get_kind(spath) == JV_KIND_NULL) { | 
					
						
							|  |  |  |       jv_free(spath); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (jv_get_kind(spath) != JV_KIND_STRING || | 
					
						
							|  |  |  |         strcmp(jv_string_value(spath), "") == 0) { | 
					
						
							|  |  |  |       jv_free(spath); | 
					
						
							|  |  |  |       continue; /* XXX report non-strings in search path?? */ | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-02 19:16:43 -06:00
										 |  |  |     // Try ${search_dir}/${rel_path}.jq
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     jv testpath = jq_realpath(jv_string_fmt("%s/%s%s", | 
					
						
							| 
									
										
										
										
											2014-08-30 00:58:24 -05:00
										 |  |  |                                             jv_string_value(spath), | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |                                             jv_string_value(rel_path), | 
					
						
							|  |  |  |                                             suffix)); | 
					
						
							| 
									
										
										
										
											2014-08-30 00:58:24 -05:00
										 |  |  |     ret = stat(jv_string_value(testpath),&st); | 
					
						
							|  |  |  |     if (ret == -1 && errno == ENOENT) { | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |       jv_free(testpath); | 
					
						
							| 
									
										
										
										
											2015-01-02 19:16:43 -06:00
										 |  |  |       // Try ${search_dir}/$(dirname ${rel_path})/jq/main.jq
 | 
					
						
							|  |  |  |       testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s", | 
					
						
							|  |  |  |                                            jv_string_value(spath), | 
					
						
							|  |  |  |                                            jv_string_value(rel_path), | 
					
						
							|  |  |  |                                            "jq/main", | 
					
						
							|  |  |  |                                            suffix)); | 
					
						
							|  |  |  |       ret = stat(jv_string_value(testpath),&st); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ret == -1 && errno == ENOENT) { | 
					
						
							|  |  |  |       jv_free(testpath); | 
					
						
							|  |  |  |       // Try ${search_dir}/${rel_path}/$(basename ${rel_path}).jq
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |       testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s", | 
					
						
							| 
									
										
										
										
											2014-08-30 00:58:24 -05:00
										 |  |  |                                            jv_string_value(spath), | 
					
						
							|  |  |  |                                            jv_string_value(rel_path), | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |                                            jv_string_value(bname), | 
					
						
							|  |  |  |                                            suffix)); | 
					
						
							| 
									
										
										
										
											2014-08-30 00:58:24 -05:00
										 |  |  |       ret = stat(jv_string_value(testpath),&st); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ret == 0) { | 
					
						
							|  |  |  |       jv_free(rel_path); | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |       jv_free(search); | 
					
						
							|  |  |  |       jv_free(bname); | 
					
						
							|  |  |  |       jv_free(spath); | 
					
						
							| 
									
										
										
										
											2014-08-30 00:58:24 -05:00
										 |  |  |       return testpath; | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-08-30 00:58:24 -05:00
										 |  |  |     jv_free(testpath); | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |     jv_free(spath); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   jv output = jv_invalid_with_msg(jv_string_fmt("module not found: %s", jv_string_value(rel_path))); | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |   jv_free(rel_path); | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   jv_free(search); | 
					
						
							|  |  |  |   jv_free(bname); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   return output; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | static jv default_search(jq_state *jq, jv value) { | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |   if (!jv_is_valid(value)) { | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     // dependent didn't say; prepend . to system search path listj
 | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |     jv_free(value); | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     return jv_array_concat(JV_ARRAY(jv_string(".")), jq_get_lib_dirs(jq)); | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |   } | 
					
						
							|  |  |  |   if (jv_get_kind(value) != JV_KIND_ARRAY) | 
					
						
							|  |  |  |     return JV_ARRAY(value); | 
					
						
							|  |  |  |   return value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX Split this into a util that takes a callback, and then...
 | 
					
						
							|  |  |  | static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) { | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   jv deps = block_take_imports(src_block); | 
					
						
							|  |  |  |   block bk = *src_block; | 
					
						
							|  |  |  |   int nerrors = 0; | 
					
						
							| 
									
										
										
										
											2015-06-26 23:40:37 -05:00
										 |  |  |   const char *as_str = NULL; | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   jv_array_foreach(deps, i, dep) { | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     int is_data = jv_get_kind(jv_object_get(jv_copy(dep), jv_string("is_data"))) == JV_KIND_TRUE; | 
					
						
							|  |  |  |     int raw = 0; | 
					
						
							|  |  |  |     jv v = jv_object_get(jv_copy(dep), jv_string("raw")); | 
					
						
							|  |  |  |     if (jv_get_kind(v) == JV_KIND_TRUE) | 
					
						
							|  |  |  |       raw = 1; | 
					
						
							|  |  |  |     jv_free(v); | 
					
						
							|  |  |  |     jv relpath = validate_relpath(jv_object_get(jv_copy(dep), jv_string("relpath"))); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |     jv as = jv_object_get(jv_copy(dep), jv_string("as")); | 
					
						
							| 
									
										
										
										
											2015-06-26 23:40:37 -05:00
										 |  |  |     assert(!jv_is_valid(as) || jv_get_kind(as) == JV_KIND_STRING); | 
					
						
							|  |  |  |     if (jv_get_kind(as) == JV_KIND_STRING) | 
					
						
							|  |  |  |       as_str = jv_string_value(as); | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     jv search = default_search(jq, jv_object_get(dep, jv_string("search"))); | 
					
						
							|  |  |  |     // dep is now freed; do not reuse
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // find_lib does a lot of work that could be cached...
 | 
					
						
							|  |  |  |     jv resolved = find_lib(jq, relpath, search, is_data ? ".json" : ".jq", jv_copy(jq_origin), jv_copy(lib_origin)); | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |     // XXX ...move the rest of this into a callback.
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     if (!jv_is_valid(resolved)) { | 
					
						
							|  |  |  |       jv emsg = jv_invalid_get_msg(resolved); | 
					
						
							| 
									
										
										
										
											2014-08-11 17:25:09 -05:00
										 |  |  |       jq_report_error(jq, jv_string_fmt("jq: error: %s\n",jv_string_value(emsg))); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |       jv_free(emsg); | 
					
						
							|  |  |  |       jv_free(as); | 
					
						
							|  |  |  |       jv_free(deps); | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |       jv_free(jq_origin); | 
					
						
							|  |  |  |       jv_free(lib_origin); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |       return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     uint64_t state_idx = 0; | 
					
						
							|  |  |  |     for (; state_idx < lib_state->ct; ++state_idx) { | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |       if (strcmp(lib_state->names[state_idx],jv_string_value(resolved)) == 0) | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (state_idx < lib_state->ct) { // Found
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |       jv_free(resolved); | 
					
						
							|  |  |  |       // Bind the library to the program
 | 
					
						
							| 
									
										
										
										
											2015-06-26 23:40:37 -05:00
										 |  |  |       bk = block_bind_library(lib_state->defs[state_idx], bk, OP_IS_CALL_PSEUDO, as_str); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |     } else { // Not found.   Add it to the table before binding.
 | 
					
						
							|  |  |  |       block dep_def_block = gen_noop(); | 
					
						
							| 
									
										
										
										
											2015-06-26 23:40:37 -05:00
										 |  |  |       nerrors += load_library(jq, resolved, is_data, raw, as_str, &dep_def_block, lib_state); | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |       // resolved has been freed
 | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |       if (nerrors == 0) { | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |         // Bind the library to the program
 | 
					
						
							| 
									
										
										
										
											2015-06-26 23:40:37 -05:00
										 |  |  |         bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, as_str); | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     jv_free(as); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(lib_origin); | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |   jv_free(jq_origin); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   jv_free(deps); | 
					
						
							|  |  |  |   return nerrors; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Loads the library at lib_path into lib_state, putting the library's defs
 | 
					
						
							|  |  |  | // into *out_block
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, const char *as, block *out_block, struct lib_loading_state *lib_state) { | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   int nerrors = 0; | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   struct locfile* src = NULL; | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   block program; | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   jv data; | 
					
						
							|  |  |  |   if (is_data && !raw) | 
					
						
							|  |  |  |     data = jv_load_file(jv_string_value(lib_path), 0); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     data = jv_load_file(jv_string_value(lib_path), 1); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   int state_idx; | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   if (!jv_is_valid(data)) { | 
					
						
							|  |  |  |     if (jv_invalid_has_msg(jv_copy(data))) | 
					
						
							|  |  |  |       data = jv_invalid_get_msg(data); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       data = jv_string("unknown error"); | 
					
						
							|  |  |  |     jq_report_error(jq, jv_string_fmt("jq: error loading data file %s: %s\n", jv_string_value(lib_path), jv_string_value(data))); | 
					
						
							|  |  |  |     nerrors++; | 
					
						
							|  |  |  |     goto out; | 
					
						
							|  |  |  |   } else if (is_data) { | 
					
						
							|  |  |  |     // import "foo" as $bar;
 | 
					
						
							|  |  |  |     program = gen_const_global(jv_copy(data), as); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     // import "foo" as bar;
 | 
					
						
							| 
									
										
										
										
											2015-03-29 19:12:23 -05:00
										 |  |  |     src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data))); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |     nerrors += jq_parse_library(src, &program); | 
					
						
							|  |  |  |     if (nerrors == 0) { | 
					
						
							|  |  |  |       char *lib_origin = strdup(jv_string_value(lib_path)); | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |       nerrors += process_dependencies(jq, jq_get_jq_origin(jq), | 
					
						
							|  |  |  |                                       jv_string(dirname(lib_origin)), | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |                                       &program, lib_state); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |       free(lib_origin); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |   state_idx = lib_state->ct++; | 
					
						
							|  |  |  |   lib_state->names = realloc(lib_state->names, lib_state->ct * sizeof(const char *)); | 
					
						
							|  |  |  |   lib_state->defs = realloc(lib_state->defs, lib_state->ct * sizeof(block)); | 
					
						
							|  |  |  |   lib_state->names[state_idx] = strdup(jv_string_value(lib_path)); | 
					
						
							|  |  |  |   lib_state->defs[state_idx] = program; | 
					
						
							|  |  |  |   *out_block = program; | 
					
						
							|  |  |  |   if (src) | 
					
						
							|  |  |  |     locfile_free(src); | 
					
						
							|  |  |  | out: | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   jv_free(lib_path); | 
					
						
							|  |  |  |   jv_free(data); | 
					
						
							|  |  |  |   return nerrors; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  | // FIXME It'd be nice to have an option to search the same search path
 | 
					
						
							|  |  |  | // as we do in process_dependencies.
 | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  | jv load_module_meta(jq_state *jq, jv mod_relpath) { | 
					
						
							|  |  |  |   // We can't know the caller's origin; we could though, if it was passed in
 | 
					
						
							|  |  |  |   jv lib_path = find_lib(jq, validate_relpath(mod_relpath), jq_get_lib_dirs(jq), ".jq", jq_get_jq_origin(jq), jv_null()); | 
					
						
							|  |  |  |   if (!jv_is_valid(lib_path)) | 
					
						
							|  |  |  |     return lib_path; | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |   jv meta = jv_null(); | 
					
						
							|  |  |  |   jv data = jv_load_file(jv_string_value(lib_path), 1); | 
					
						
							|  |  |  |   if (jv_is_valid(data)) { | 
					
						
							|  |  |  |     block program; | 
					
						
							| 
									
										
										
										
											2015-03-29 19:12:23 -05:00
										 |  |  |     struct locfile* src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data))); | 
					
						
							| 
									
										
										
										
											2014-08-10 16:52:03 -05:00
										 |  |  |     int nerrors = jq_parse_library(src, &program); | 
					
						
							|  |  |  |     if (nerrors == 0) { | 
					
						
							|  |  |  |       meta = block_module_meta(program); | 
					
						
							|  |  |  |       if (jv_get_kind(meta) == JV_KIND_NULL) | 
					
						
							|  |  |  |         meta = jv_object(); | 
					
						
							|  |  |  |       meta = jv_object_set(meta, jv_string("deps"), block_take_imports(&program)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     locfile_free(src); | 
					
						
							|  |  |  |     block_free(program); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(lib_path); | 
					
						
							|  |  |  |   jv_free(data); | 
					
						
							|  |  |  |   return meta; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | int load_program(jq_state *jq, struct locfile* src, block *out_block) { | 
					
						
							|  |  |  |   int nerrors = 0; | 
					
						
							|  |  |  |   block program; | 
					
						
							|  |  |  |   struct lib_loading_state lib_state = {0,0,0}; | 
					
						
							|  |  |  |   nerrors = jq_parse(src, &program); | 
					
						
							|  |  |  |   if (nerrors) | 
					
						
							|  |  |  |     return nerrors; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-24 17:58:34 -06:00
										 |  |  |   nerrors = process_dependencies(jq, jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state); | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |   block libs = gen_noop(); | 
					
						
							|  |  |  |   for (uint64_t i = 0; i < lib_state.ct; ++i) { | 
					
						
							|  |  |  |     free(lib_state.names[i]); | 
					
						
							| 
									
										
										
										
											2014-12-30 13:13:30 -06:00
										 |  |  |     if (nerrors == 0 && !block_is_const(lib_state.defs[i])) | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  |       libs = block_join(libs, lib_state.defs[i]); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       block_free(lib_state.defs[i]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   free(lib_state.names); | 
					
						
							|  |  |  |   free(lib_state.defs); | 
					
						
							|  |  |  |   if (nerrors) | 
					
						
							|  |  |  |     block_free(program); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     *out_block = block_drop_unreferenced(block_join(libs, program)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return nerrors; | 
					
						
							|  |  |  | } |