3 * Explore and debug `runlisp' configration
5 * (c) 2020 Mark Wooding
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Runlisp, a tool for invoking Common Lisp scripts.
12 * Runlisp is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * Runlisp is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with Runlisp. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ------------------------------------------------------*/
40 /*----- Static data -------------------------------------------------------*/
42 /* Query operations. */
44 OP_LISTSEC
, /* list all sections */
45 OP_LISTVAR
, /* list variables in a section */
46 OP_RAW
, /* print a variable's value */
47 OP_SUBST
, /* print variable's expansion */
48 OP_SPLIT
, /* print word-split variable */
52 /* A node in the list of queued-up operations. */
54 struct op
*next
; /* link to next op in the list */
55 unsigned code
; /* operation code (`OP_...') */
56 const char *arg
; /* argument (from command-line) */
59 static struct op
*oplist
; /* list of queued-up operations */
61 static unsigned flags
= 0; /* flags for the application */
62 #define AF_BOGUS 0x0001u /* invalid command-line syntax */
63 #define AF_SETCONF 0x0002u /* explicit configuration */
65 /*----- Main code ---------------------------------------------------------*/
67 /* Append a new node to the list of delayed operations, with CODE and ARG.
69 * The address of the final link (initially, the list head) is in
70 * *TAIL_INOUT: make this point to the new node, and then update it to point
71 * to the link in the new node.
73 static void add_op(struct op
***tail_inout
, unsigned code
, const char *arg
)
75 struct op
*op
= xmalloc(sizeof(*op
));
76 op
->code
= code
; op
->arg
= arg
; **tail_inout
= op
; *tail_inout
= &op
->next
;
79 /* Given an ARG of the form `[SECT:]VAR', set *SECT_OUT and *VAR_OUT to the
80 * requested home section and variable. Leave these null if they can't be
83 static void find_var(const char *arg
,
84 struct config_section
**sect_out
,
85 struct config_var
**var_out
)
87 struct config_section
*sect
;
92 { sect
= toplevel
; p
= arg
; }
94 { sect
= config_find_section_n(&config
, 0, arg
, p
- arg
); p
++; }
96 if (!sect
) *var_out
= 0;
97 else *var_out
= config_find_var(&config
, sect
, CF_INHERIT
, p
);
100 /* Help and related functions. */
101 static void version(FILE *fp
)
102 { fprintf(fp
, "%s, runlisp version %s\n", progname
, PACKAGE_VERSION
); }
104 static void usage(FILE *fp
)
107 usage: %s [-Lqv] [-c CONF] [-o [SECT:]VAR=VAL]\n\
108 [-l SECT] [-p [SECT:]VAR] [-w [SECT:]VAR] [-x [SECT:]VAR]\n",
112 static void help(FILE *fp
)
114 version(fp
); fputc('\n', fp
); usage(fp
);
117 -h, --help Show this help text and exit successfully.\n\
118 -V, --version Show version number and exit successfully.\n\
121 -q, --quiet Don't print warning messages.\n\
122 -v, --verbose Print informational messages (repeatable).\n\
125 -c, --config-file=CONF Read configuration from CONF (repeatable).\n\
126 -o, --set-option=[SECT:]VAR=VAL Set configuration variable (repeatable).\n\
129 -L, --list-sections List all known section names in order.\n\
130 -l, --list-variables=SECTION List all defined variables in SECTION.\n\
131 -p, --print-variable=[SECT:]VAR Print the raw (unexpanded) value of VAR.\n\
132 -w, --split-variable=[SECT:]VAR Expand and word-split VAR and print.\n\
133 -x, --expand-variable=[SECT:]VAR Expand VAR and print the result.\n", fp
);
137 int main(int argc
, char *argv
[])
139 struct config_section_iter si
;
140 struct config_section
*sect
;
141 struct config_var_iter vi
;
142 struct config_var
*var
;
143 struct op
*op
, **tail
= &oplist
;
144 struct dstr d
= DSTR_INIT
;
145 struct argv av
= ARGV_INIT
;
148 /* Command-line options. */
149 static const struct option opts
[] = {
150 { "help", 0, 0, 'h' },
151 { "version", 0, 0, 'V' },
152 { "list-sections", 0, 0, 'L' },
153 { "config-file", OPTF_ARGREQ
, 0, 'c' },
154 { "list-variables", OPTF_ARGREQ
, 0, 'l' },
155 { "set-option", OPTF_ARGREQ
, 0, 'o' },
156 { "print-variable", OPTF_ARGREQ
, 0, 'p' },
157 { "quiet", 0, 0, 'q' },
158 { "verbose", 0, 0, 'v' },
159 { "split-variable", OPTF_ARGREQ
, 0, 'w' },
160 { "expand-variable", OPTF_ARGREQ
, 0, 'x' },
165 set_progname(argv
[0]);
168 /* Parse the options.
170 * We must delay the query operations until the configuration is loaded,
171 * but we won't know whether to load the default configuration until we're
172 * sure that that there are no `-c' options. So just stash the queries in
173 * a list until later.
175 optprog
= (/*unconst*/ char *)progname
;
177 i
= mdwopt(argc
- 1, argv
+ 1, "hVLc:l:o:p:qvw:x:", opts
, 0, 0,
181 case 'h': help(stdout
); exit(0);
182 case 'V': version(stdout
); exit(0);
183 case 'L': add_op(&tail
, OP_LISTSEC
, 0); break;
184 case 'c': read_config_path(optarg
, 0); flags
|= AF_SETCONF
; break;
185 case 'l': add_op(&tail
, OP_LISTVAR
, optarg
); break;
186 case 'o': if (set_config_var(optarg
)) flags
|= AF_BOGUS
; break;
187 case 'p': add_op(&tail
, OP_RAW
, optarg
); break;
188 case 'q': if (verbose
) verbose
--; break;
189 case 'v': verbose
++; break;
190 case 'w': add_op(&tail
, OP_SPLIT
, optarg
); break;
191 case 'x': add_op(&tail
, OP_SUBST
, optarg
); break;
192 default: flags
|= AF_BOGUS
; break;
196 /* Check that everything worked. */
198 if (optind
< argc
) flags
|= AF_BOGUS
;
199 if (flags
&AF_BOGUS
) { usage(stderr
); exit(127); }
200 *tail
= 0; if (!oplist
) lose("nothing to do");
202 /* Load default configuration if no explicit files were requested. */
203 if (!(flags
&AF_SETCONF
)) load_default_config();
205 /* Work through the operations we stashed earlier. */
206 for (op
= oplist
; op
; op
= op
->next
)
210 printf("sections:\n");
211 for (config_start_section_iter(&config
, &si
);
212 (sect
= config_next_section(&si
)); )
213 printf("\t%s\n", CONFIG_SECTION_NAME(sect
));
217 sect
= config_find_section(&config
, 0, op
->arg
);
219 printf("section `%s' not found\n", op
->arg
);
221 printf("section `%s' variables:\n", CONFIG_SECTION_NAME(sect
));
222 for (config_start_var_iter(&config
, sect
, &vi
);
223 (var
= config_next_var(&vi
)); )
224 printf("\t%s\n", CONFIG_VAR_NAME(var
));
229 find_var(op
->arg
, §
, &var
);
230 if (!var
) printf("%s not found\n", op
->arg
);
231 else printf("%s = %s\n", op
->arg
, var
->val
);
235 find_var(op
->arg
, §
, &var
);
237 printf("%s not found\n", op
->arg
);
239 dstr_reset(&d
); config_subst_var(&config
, sect
, var
, &d
);
240 printf("%s = %s\n", op
->arg
, d
.p
);
245 find_var(op
->arg
, §
, &var
);
247 printf("%s not found\n", op
->arg
);
249 argv_reset(&av
); config_subst_split_var(&config
, sect
, var
, &av
);
250 dstr_reset(&d
); argv_string(&d
, &av
);
251 printf("%s = %s\n", op
->arg
, d
.p
);
263 /*----- That's all, folks -------------------------------------------------*/