Skip to content

Commit dabb7dd

Browse files
committed
Support $XDG_CONFIG_HOME in module search path
1 parent d44baeb commit dabb7dd

File tree

12 files changed

+104
-12
lines changed

12 files changed

+104
-12
lines changed

Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,17 @@ EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \
220220
tests/modules/a.jq tests/modules/b/b.jq tests/modules/c/c.jq \
221221
tests/modules/c/d.jq tests/modules/data.json \
222222
tests/modules/home1/.jq tests/modules/home2/.jq/g.jq \
223+
tests/modules/home3/.config/jq/cfg.jq \
224+
tests/modules/home3/.jq/priority_test.jq \
223225
tests/modules/lib/jq/e/e.jq tests/modules/lib/jq/f.jq \
224226
tests/modules/shadow1.jq tests/modules/shadow2.jq \
225227
tests/modules/syntaxerror/syntaxerror.jq \
226228
tests/modules/test_bind_order.jq \
227229
tests/modules/test_bind_order0.jq \
228230
tests/modules/test_bind_order1.jq \
229231
tests/modules/test_bind_order2.jq \
232+
tests/modules/xdg1/jq/xdg.jq \
233+
tests/modules/xdg2/jq/priority_test.jq \
230234
tests/onig.supp tests/local.supp \
231235
tests/setup tests/torture/input0.json \
232236
tests/optional.test tests/man.test tests/manonig.test \

docs/content/manual/dev/manual.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3708,6 +3708,13 @@ sections:
37083708
For paths starting with `~/`, the user's home directory is
37093709
substituted for `~`.
37103710
3711+
For paths starting with `$XDG_CONFIG_HOME/`, the value of the
3712+
environment variable `$XDG_CONFIG_HOME` is substituted for
3713+
`$XDG_CONFIG_HOME`. If the variable is not defined, `$HOME/.config`
3714+
is used as the default on non-Windows platforms. On Windows, these
3715+
paths are removed from the search path if the variable is not
3716+
defined.
3717+
37113718
For paths starting with `$ORIGIN/`, the directory where the jq
37123719
executable is located is substituted for `$ORIGIN`.
37133720
@@ -3719,8 +3726,8 @@ sections:
37193726
the default is appended.
37203727
37213728
The default search path is the search path given to the `-L`
3722-
command-line option, else `["~/.jq", "$ORIGIN/../lib/jq",
3723-
"$ORIGIN/../lib"]`.
3729+
command-line option, else `["~/.jq", "$XDG_CONFIG_HOME/jq",
3730+
"$ORIGIN/../lib/jq", "$ORIGIN/../lib"]`.
37243731
37253732
Null and empty string path elements terminate search path
37263733
processing.

jq.1.prebuilt

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/linker.c

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static int path_is_relative(jv p) {
4848
// in the following order:
4949
// 1. lib_path
5050
// 2. -L paths passed in on the command line (from jq_state*) or builtin list
51-
static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv lib_origin) {
51+
static jv build_lib_search_chain(jq_state *jq, jv search_path, jv xdg_config_home, jv jq_origin, jv lib_origin) {
5252
assert(jv_get_kind(search_path) == JV_KIND_ARRAY);
5353
jv expanded = jv_array();
5454
jv expanded_elt;
@@ -66,6 +66,15 @@ static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv
6666
}
6767
if (strcmp(".",jv_string_value(path)) == 0) {
6868
expanded_elt = jv_copy(path);
69+
} else if (strncmp("$XDG_CONFIG_HOME/",jv_string_value(path),sizeof("$XDG_CONFIG_HOME/") - 1) == 0) {
70+
if (jv_is_valid(xdg_config_home)) {
71+
expanded_elt = jv_string_fmt("%s/%s",
72+
jv_string_value(xdg_config_home),
73+
jv_string_value(path) + sizeof ("$XDG_CONFIG_HOME/") - 1);
74+
} else {
75+
// Remove $XDG_CONFIG_HOME/* from the search path if $XDG_CONFIG_HOME is not defined.
76+
expanded_elt = jv_null();
77+
}
6978
} else if (strncmp("$ORIGIN/",jv_string_value(path),sizeof("$ORIGIN/") - 1) == 0) {
7079
expanded_elt = jv_string_fmt("%s/%s",
7180
jv_string_value(jq_origin),
@@ -82,6 +91,7 @@ static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv
8291
expanded = jv_array_append(expanded, expanded_elt);
8392
jv_free(path);
8493
}
94+
jv_free(xdg_config_home);
8595
jv_free(jq_origin);
8696
jv_free(lib_origin);
8797
jv_free(search_path);
@@ -133,23 +143,26 @@ static jv jv_basename(jv name) {
133143
}
134144

135145
// Asummes validated relative path to module
136-
static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) {
146+
static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv xdg_config_home, jv jq_origin, jv lib_origin) {
137147
if (!jv_is_valid(rel_path)) {
138148
jv_free(search);
149+
jv_free(xdg_config_home);
139150
jv_free(jq_origin);
140151
jv_free(lib_origin);
141152
return rel_path;
142153
}
143154
if (jv_get_kind(rel_path) != JV_KIND_STRING) {
144155
jv_free(rel_path);
145156
jv_free(search);
157+
jv_free(xdg_config_home);
146158
jv_free(jq_origin);
147159
jv_free(lib_origin);
148160
return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
149161
}
150162
if (jv_get_kind(search) != JV_KIND_ARRAY) {
151163
jv_free(rel_path);
152164
jv_free(search);
165+
jv_free(xdg_config_home);
153166
jv_free(jq_origin);
154167
jv_free(lib_origin);
155168
return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));
@@ -159,7 +172,7 @@ static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv
159172
int ret;
160173

161174
// Ideally we should cache this somewhere
162-
search = build_lib_search_chain(jq, search, jq_origin, lib_origin);
175+
search = build_lib_search_chain(jq, search, xdg_config_home, jq_origin, lib_origin);
163176
jv err = jv_array_get(jv_copy(search), 1);
164177
search = jv_array_get(search, 0);
165178

@@ -241,7 +254,7 @@ static jv default_search(jq_state *jq, jv value) {
241254
}
242255

243256
// XXX Split this into a util that takes a callback, and then...
244-
static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) {
257+
static int process_dependencies(jq_state *jq, jv xdg_config_home, jv jq_origin, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) {
245258
jv deps = block_take_imports(src_block);
246259
block bk = *src_block;
247260
int nerrors = 0;
@@ -270,7 +283,7 @@ static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block
270283
// dep is now freed; do not reuse
271284

272285
// find_lib does a lot of work that could be cached...
273-
jv resolved = find_lib(jq, relpath, search, is_data ? ".json" : ".jq", jv_copy(jq_origin), jv_copy(lib_origin));
286+
jv resolved = find_lib(jq, relpath, search, is_data ? ".json" : ".jq", jv_copy(xdg_config_home), jv_copy(jq_origin), jv_copy(lib_origin));
274287
// XXX ...move the rest of this into a callback.
275288
if (!jv_is_valid(resolved)) {
276289
jv_free(as);
@@ -282,6 +295,7 @@ static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block
282295
jq_report_error(jq, jv_string_fmt("jq: error: %s\n",jv_string_value(emsg)));
283296
jv_free(emsg);
284297
jv_free(deps);
298+
jv_free(xdg_config_home);
285299
jv_free(jq_origin);
286300
jv_free(lib_origin);
287301
return 1;
@@ -321,6 +335,7 @@ static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block
321335
jv_free(as);
322336
}
323337
jv_free(lib_origin);
338+
jv_free(xdg_config_home);
324339
jv_free(jq_origin);
325340
jv_free(deps);
326341
return nerrors;
@@ -359,7 +374,8 @@ static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, int opt
359374
locfile_free(src);
360375
if (nerrors == 0) {
361376
char *lib_origin = strdup(jv_string_value(lib_path));
362-
nerrors += process_dependencies(jq, jq_get_jq_origin(jq),
377+
nerrors += process_dependencies(jq, get_xdg_config_home(),
378+
jq_get_jq_origin(jq),
363379
jv_string(dirname(lib_origin)),
364380
&program, lib_state);
365381
free(lib_origin);
@@ -382,7 +398,7 @@ static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, int opt
382398
// as we do in process_dependencies.
383399
jv load_module_meta(jq_state *jq, jv mod_relpath) {
384400
// We can't know the caller's origin; we could though, if it was passed in
385-
jv lib_path = find_lib(jq, validate_relpath(mod_relpath), jq_get_lib_dirs(jq), ".jq", jq_get_jq_origin(jq), jv_null());
401+
jv lib_path = find_lib(jq, validate_relpath(mod_relpath), jq_get_lib_dirs(jq), ".jq", get_xdg_config_home(), jq_get_jq_origin(jq), jv_null());
386402
if (!jv_is_valid(lib_path))
387403
return lib_path;
388404
jv meta = jv_null();
@@ -432,7 +448,7 @@ int load_program(jq_state *jq, struct locfile* src, block *out_block) {
432448
jv_free(home);
433449
}
434450

435-
nerrors = process_dependencies(jq, jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state);
451+
nerrors = process_dependencies(jq, get_xdg_config_home(), jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state);
436452
block libs = gen_noop();
437453
for (uint64_t i = 0; i < lib_state.ct; ++i) {
438454
free(lib_state.names[i]);

src/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ int main(int argc, char* argv[]) {
571571
if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) {
572572
// Default search path list
573573
lib_search_paths = JV_ARRAY(jv_string("~/.jq"),
574+
jv_string("$XDG_CONFIG_HOME/jq"),
574575
jv_string("$ORIGIN/../lib/jq"),
575576
jv_string("$ORIGIN/../lib"));
576577
}

src/util.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,26 @@ jv get_home(void) {
126126
return ret;
127127
}
128128

129+
// Get $XDG_CONFIG_HOME, fallbacking to $HOME/.config on non-Windows platforms.
130+
jv get_xdg_config_home(void) {
131+
char *xdg_config_home = getenv("XDG_CONFIG_HOME");
132+
if (xdg_config_home && xdg_config_home[0]) {
133+
return jv_string(xdg_config_home);
134+
}
135+
136+
#ifndef WIN32
137+
// Fallback to $HOME/.config on non-Windows platforms.
138+
jv home = get_home();
139+
if (jv_is_valid(home)) {
140+
jv ret = jv_string_fmt("%s/.config", jv_string_value(home));
141+
jv_free(home);
142+
return ret;
143+
}
144+
jv_free(home);
145+
#endif
146+
147+
return jv_invalid_with_msg(jv_string("No $XDG_CONFIG_HOME available"));
148+
}
129149

130150
jv jq_realpath(jv path) {
131151
int path_max;

src/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
jv expand_path(jv);
1717
jv get_home(void);
18+
jv get_xdg_config_home(void);
1819
jv jq_realpath(jv);
1920

2021
/*
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def test: "bar";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def test: "qux";

tests/modules/xdg1/jq/xdg.jq

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def test: "foo";

0 commit comments

Comments
 (0)