Skip to content

Commit 650a087

Browse files
committed
Replace $XDG_CONFIG_HOME with $JQ_CONFIG_HOME in search path
Introduce $JQ_CONFIG_HOME as a unified config home directory.
1 parent 299bb5a commit 650a087

7 files changed

Lines changed: 78 additions & 49 deletions

File tree

docs/content/manual/dev/manual.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3708,12 +3708,11 @@ 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.
3711+
For paths starting with `$JQ_CONFIG_HOME/`, `$JQ_CONFIG_HOME` is
3712+
resolved as follows: if the environment variable `$XDG_CONFIG_HOME`
3713+
is set and non-empty, `$XDG_CONFIG_HOME/jq` is used if the directory
3714+
exists; on non-Windows platforms, `~/.config/jq` is used if the
3715+
directory exists; otherwise, `~/.jq` is used as a fallback.
37173716
37183717
For paths starting with `$ORIGIN/`, the directory where the jq
37193718
executable is located is substituted for `$ORIGIN`.
@@ -3726,7 +3725,7 @@ sections:
37263725
the default is appended.
37273726
37283727
The default search path is the search path given to the `-L`
3729-
command-line option, else `["~/.jq", "$XDG_CONFIG_HOME/jq",
3728+
command-line option, else `["$JQ_CONFIG_HOME",
37303729
"$ORIGIN/../lib/jq", "$ORIGIN/../lib"]`.
37313730
37323731
Null and empty string path elements terminate search path

jq.1.prebuilt

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

src/linker.c

Lines changed: 26 additions & 21 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 xdg_config_home, jv jq_origin, jv lib_origin) {
51+
static jv build_lib_search_chain(jq_state *jq, jv search_path, jv 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,14 +66,15 @@ static jv build_lib_search_chain(jq_state *jq, jv search_path, jv xdg_config_hom
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)) {
69+
} else if (strcmp("$JQ_CONFIG_HOME",jv_string_value(path)) == 0) {
70+
expanded_elt = jv_copy(config_home);
71+
} else if (strncmp("$JQ_CONFIG_HOME/",jv_string_value(path),sizeof("$JQ_CONFIG_HOME/") - 1) == 0) {
72+
if (jv_is_valid(config_home)) {
7173
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+
jv_string_value(config_home),
75+
jv_string_value(path) + sizeof ("$JQ_CONFIG_HOME/") - 1);
7476
} else {
75-
// Remove $XDG_CONFIG_HOME/* from the search path if $XDG_CONFIG_HOME is not defined.
76-
expanded_elt = jv_null();
77+
expanded_elt = jv_invalid();
7778
}
7879
} else if (strncmp("$ORIGIN/",jv_string_value(path),sizeof("$ORIGIN/") - 1) == 0) {
7980
expanded_elt = jv_string_fmt("%s/%s",
@@ -88,10 +89,14 @@ static jv build_lib_search_chain(jq_state *jq, jv search_path, jv xdg_config_hom
8889
expanded_elt = path;
8990
path = jv_invalid();
9091
}
91-
expanded = jv_array_append(expanded, expanded_elt);
92+
if (jv_is_valid(expanded_elt)) {
93+
expanded = jv_array_append(expanded, expanded_elt);
94+
} else {
95+
jv_free(expanded_elt);
96+
}
9297
jv_free(path);
9398
}
94-
jv_free(xdg_config_home);
99+
jv_free(config_home);
95100
jv_free(jq_origin);
96101
jv_free(lib_origin);
97102
jv_free(search_path);
@@ -143,26 +148,26 @@ static jv jv_basename(jv name) {
143148
}
144149

145150
// Asummes validated relative path to module
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) {
151+
static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv config_home, jv jq_origin, jv lib_origin) {
147152
if (!jv_is_valid(rel_path)) {
148153
jv_free(search);
149-
jv_free(xdg_config_home);
154+
jv_free(config_home);
150155
jv_free(jq_origin);
151156
jv_free(lib_origin);
152157
return rel_path;
153158
}
154159
if (jv_get_kind(rel_path) != JV_KIND_STRING) {
155160
jv_free(rel_path);
156161
jv_free(search);
157-
jv_free(xdg_config_home);
162+
jv_free(config_home);
158163
jv_free(jq_origin);
159164
jv_free(lib_origin);
160165
return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
161166
}
162167
if (jv_get_kind(search) != JV_KIND_ARRAY) {
163168
jv_free(rel_path);
164169
jv_free(search);
165-
jv_free(xdg_config_home);
170+
jv_free(config_home);
166171
jv_free(jq_origin);
167172
jv_free(lib_origin);
168173
return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));
@@ -172,7 +177,7 @@ static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv
172177
int ret;
173178

174179
// Ideally we should cache this somewhere
175-
search = build_lib_search_chain(jq, search, xdg_config_home, jq_origin, lib_origin);
180+
search = build_lib_search_chain(jq, search, config_home, jq_origin, lib_origin);
176181
jv err = jv_array_get(jv_copy(search), 1);
177182
search = jv_array_get(search, 0);
178183

@@ -254,7 +259,7 @@ static jv default_search(jq_state *jq, jv value) {
254259
}
255260

256261
// XXX Split this into a util that takes a callback, and then...
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) {
262+
static int process_dependencies(jq_state *jq, jv config_home, jv jq_origin, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) {
258263
jv deps = block_take_imports(src_block);
259264
block bk = *src_block;
260265
int nerrors = 0;
@@ -283,7 +288,7 @@ static int process_dependencies(jq_state *jq, jv xdg_config_home, jv jq_origin,
283288
// dep is now freed; do not reuse
284289

285290
// find_lib does a lot of work that could be cached...
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));
291+
jv resolved = find_lib(jq, relpath, search, is_data ? ".json" : ".jq", jv_copy(config_home), jv_copy(jq_origin), jv_copy(lib_origin));
287292
// XXX ...move the rest of this into a callback.
288293
if (!jv_is_valid(resolved)) {
289294
jv_free(as);
@@ -295,7 +300,7 @@ static int process_dependencies(jq_state *jq, jv xdg_config_home, jv jq_origin,
295300
jq_report_error(jq, jv_string_fmt("jq: error: %s\n",jv_string_value(emsg)));
296301
jv_free(emsg);
297302
jv_free(deps);
298-
jv_free(xdg_config_home);
303+
jv_free(config_home);
299304
jv_free(jq_origin);
300305
jv_free(lib_origin);
301306
return 1;
@@ -335,7 +340,7 @@ static int process_dependencies(jq_state *jq, jv xdg_config_home, jv jq_origin,
335340
jv_free(as);
336341
}
337342
jv_free(lib_origin);
338-
jv_free(xdg_config_home);
343+
jv_free(config_home);
339344
jv_free(jq_origin);
340345
jv_free(deps);
341346
return nerrors;
@@ -374,7 +379,7 @@ static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, int opt
374379
locfile_free(src);
375380
if (nerrors == 0) {
376381
char *lib_origin = strdup(jv_string_value(lib_path));
377-
nerrors += process_dependencies(jq, get_xdg_config_home(),
382+
nerrors += process_dependencies(jq, get_config_home(),
378383
jq_get_jq_origin(jq),
379384
jv_string(dirname(lib_origin)),
380385
&program, lib_state);
@@ -398,7 +403,7 @@ static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, int opt
398403
// as we do in process_dependencies.
399404
jv load_module_meta(jq_state *jq, jv mod_relpath) {
400405
// We can't know the caller's origin; we could though, if it was passed in
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());
406+
jv lib_path = find_lib(jq, validate_relpath(mod_relpath), jq_get_lib_dirs(jq), ".jq", get_config_home(), jq_get_jq_origin(jq), jv_null());
402407
if (!jv_is_valid(lib_path))
403408
return lib_path;
404409
jv meta = jv_null();
@@ -448,7 +453,7 @@ int load_program(jq_state *jq, struct locfile* src, block *out_block) {
448453
jv_free(home);
449454
}
450455

451-
nerrors = process_dependencies(jq, get_xdg_config_home(), jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state);
456+
nerrors = process_dependencies(jq, get_config_home(), jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state);
452457
block libs = gen_noop();
453458
for (uint64_t i = 0; i < lib_state.ct; ++i) {
454459
free(lib_state.names[i]);

src/main.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,7 @@ int main(int argc, char* argv[]) {
570570

571571
if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) {
572572
// Default search path list
573-
lib_search_paths = JV_ARRAY(jv_string("~/.jq"),
574-
jv_string("$XDG_CONFIG_HOME/jq"),
573+
lib_search_paths = JV_ARRAY(jv_string("$JQ_CONFIG_HOME"),
575574
jv_string("$ORIGIN/../lib/jq"),
576575
jv_string("$ORIGIN/../lib"));
577576
}

src/util.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -126,25 +126,46 @@ 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) {
129+
static int is_directory(const char *path) {
130+
struct stat sb;
131+
return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);
132+
}
133+
134+
// Get the config home base directory. Resolved as follows:
135+
//
136+
// 1. $XDG_CONFIG_HOME/jq if set, non-empty, and the directory exists
137+
// 2. Non-Windows only: $HOME/.config/jq if the directory exists
138+
// 3. $HOME/.jq
139+
jv get_config_home(void) {
131140
char *xdg_config_home = getenv("XDG_CONFIG_HOME");
132141
if (xdg_config_home && xdg_config_home[0]) {
133-
return jv_string(xdg_config_home);
142+
jv xdg_jq = jv_string_fmt("%s/jq", xdg_config_home);
143+
if (is_directory(jv_string_value(xdg_jq))) {
144+
return xdg_jq;
145+
}
146+
jv_free(xdg_jq);
134147
}
135148

136-
#ifndef WIN32
137-
// Fallback to $HOME/.config on non-Windows platforms.
138149
jv home = get_home();
139-
if (jv_is_valid(home)) {
140-
jv ret = jv_string_fmt("%s/.config", jv_string_value(home));
150+
if (!jv_is_valid(home)) {
141151
jv_free(home);
142-
return ret;
152+
return jv_invalid_with_msg(jv_string("No config home directory available"));
143153
}
144-
jv_free(home);
154+
155+
#ifndef WIN32
156+
// Fallback to $HOME/.config/jq on non-Windows platforms.
157+
jv config_jq = jv_string_fmt("%s/.config/jq", jv_string_value(home));
158+
if (is_directory(jv_string_value(config_jq))) {
159+
jv_free(home);
160+
return config_jq;
161+
}
162+
jv_free(config_jq);
145163
#endif
164+
// Fallback to $HOME/.jq.
165+
jv dot_jq = jv_string_fmt("%s/.jq", jv_string_value(home));
166+
jv_free(home);
146167

147-
return jv_invalid_with_msg(jv_string("No $XDG_CONFIG_HOME available"));
168+
return dot_jq;
148169
}
149170

150171
jv jq_realpath(jv path) {

src/util.h

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

1616
jv expand_path(jv);
1717
jv get_home(void);
18-
jv get_xdg_config_home(void);
18+
jv get_config_home(void);
1919
jv jq_realpath(jv);
2020

2121
/*

tests/shtest

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -382,9 +382,9 @@ if [ "$(HOME=/nonexistent XDG_CONFIG_HOME="$mods/xdg1" $VALGRIND $Q $JQ -nr 'inc
382382
echo "Failed to load module from \$XDG_CONFIG_HOME/jq/" 1>&2
383383
exit 1
384384
fi
385-
## ~/.jq/ should be searched before $XDG_CONFIG_HOME/jq/
386-
if [ "$(HOME="$mods/home3" XDG_CONFIG_HOME="$mods/xdg2" $VALGRIND $Q $JQ -nr 'include "priority_test"; test')" != "qux" ]; then
387-
echo "~/.jq/ should take priority over \$XDG_CONFIG_HOME/jq/" 1>&2
385+
## $JQ_CONFIG_HOME/jq/ should be searched before ~/.jq/
386+
if [ "$(HOME="$mods/home3" XDG_CONFIG_HOME="$mods/xdg2" $VALGRIND $Q $JQ -nr 'include "priority_test"; test')" != "baz" ]; then
387+
echo "\$JQ_CONFIG_HOME/jq/ should take priority over ~/.jq/" 1>&2
388388
exit 1
389389
fi
390390
if $msys || $mingw; then
@@ -399,16 +399,21 @@ if $msys || $mingw; then
399399
exit 1
400400
fi
401401
else
402-
## If $XDG_CONFIG_HOME is unset, fallback to $HOME/.config/jq/
402+
## If $XDG_CONFIG_HOME is unset and $HOME/.config/jq exists, fallback to $HOME/.config/jq/
403403
if [ "$(unset XDG_CONFIG_HOME; HOME="$mods/home3" $VALGRIND $Q $JQ -nr 'include "cfg"; test')" != "bar" ]; then
404404
echo "Failed to fallback to \$HOME/.config/jq/ when \$XDG_CONFIG_HOME is unset" 1>&2
405405
exit 1
406406
fi
407-
## If $XDG_CONFIG_HOME is an empty string, fallback to $HOME/.config/jq/
407+
## If $XDG_CONFIG_HOME is an empty string and $HOME/.config/jq exists, fallback to $HOME/.config/jq/
408408
if [ "$(XDG_CONFIG_HOME="" HOME="$mods/home3" $VALGRIND $Q $JQ -nr 'include "cfg"; test')" != "bar" ]; then
409409
echo "Failed to fallback to \$HOME/.config/jq/ when \$XDG_CONFIG_HOME is empty" 1>&2
410410
exit 1
411411
fi
412+
## If $HOME/.config/jq does not exist, do not fallback to $HOME/.config/
413+
if unset XDG_CONFIG_HOME; HOME="$mods/home1" $VALGRIND $Q $JQ -nr 'include "cfg"; test' 2>/dev/null; then
414+
echo "Should not fallback to \$HOME/.config/ when \$HOME/.config/jq does not exist" 1>&2
415+
exit 1
416+
fi
412417
fi
413418

414419
cd "$JQBASEDIR" # so that relative library paths are guaranteed correct

0 commit comments

Comments
 (0)