cmd-parse.y revision 1.1 1 1.1 christos /* $OpenBSD$ */
2 1.1 christos
3 1.1 christos /*
4 1.1 christos * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott (at) gmail.com>
5 1.1 christos *
6 1.1 christos * Permission to use, copy, modify, and distribute this software for any
7 1.1 christos * purpose with or without fee is hereby granted, provided that the above
8 1.1 christos * copyright notice and this permission notice appear in all copies.
9 1.1 christos *
10 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 1.1 christos * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 1.1 christos * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 christos */
18 1.1 christos
19 1.1 christos %{
20 1.1 christos
21 1.1 christos #include <sys/types.h>
22 1.1 christos
23 1.1 christos #include <ctype.h>
24 1.1 christos #include <errno.h>
25 1.1 christos #include <pwd.h>
26 1.1 christos #include <stdlib.h>
27 1.1 christos #include <string.h>
28 1.1 christos #include <unistd.h>
29 1.1 christos
30 1.1 christos #include "tmux.h"
31 1.1 christos
32 1.1 christos static int yylex(void);
33 1.1 christos static int yyparse(void);
34 1.1 christos static int printflike(1,2) yyerror(const char *, ...);
35 1.1 christos
36 1.1 christos static char *yylex_token(int);
37 1.1 christos static char *yylex_format(void);
38 1.1 christos
39 1.1 christos struct cmd_parse_scope {
40 1.1 christos int flag;
41 1.1 christos TAILQ_ENTRY (cmd_parse_scope) entry;
42 1.1 christos };
43 1.1 christos
44 1.1 christos struct cmd_parse_command {
45 1.1 christos char *name;
46 1.1 christos u_int line;
47 1.1 christos
48 1.1 christos int argc;
49 1.1 christos char **argv;
50 1.1 christos
51 1.1 christos TAILQ_ENTRY(cmd_parse_command) entry;
52 1.1 christos };
53 1.1 christos TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
54 1.1 christos
55 1.1 christos struct cmd_parse_state {
56 1.1 christos FILE *f;
57 1.1 christos
58 1.1 christos const char *buf;
59 1.1 christos size_t len;
60 1.1 christos size_t off;
61 1.1 christos
62 1.1 christos int condition;
63 1.1 christos int eol;
64 1.1 christos int eof;
65 1.1 christos struct cmd_parse_input *input;
66 1.1 christos u_int escapes;
67 1.1 christos
68 1.1 christos char *error;
69 1.1 christos struct cmd_parse_commands *commands;
70 1.1 christos
71 1.1 christos struct cmd_parse_scope *scope;
72 1.1 christos TAILQ_HEAD(, cmd_parse_scope) stack;
73 1.1 christos };
74 1.1 christos static struct cmd_parse_state parse_state;
75 1.1 christos
76 1.1 christos static char *cmd_parse_get_error(const char *, u_int, const char *);
77 1.1 christos static void cmd_parse_free_command(struct cmd_parse_command *);
78 1.1 christos static struct cmd_parse_commands *cmd_parse_new_commands(void);
79 1.1 christos static void cmd_parse_free_commands(struct cmd_parse_commands *);
80 1.1 christos static void cmd_parse_print_commands(struct cmd_parse_input *, u_int,
81 1.1 christos struct cmd_list *);
82 1.1 christos
83 1.1 christos %}
84 1.1 christos
85 1.1 christos %union
86 1.1 christos {
87 1.1 christos char *token;
88 1.1 christos struct {
89 1.1 christos int argc;
90 1.1 christos char **argv;
91 1.1 christos } arguments;
92 1.1 christos int flag;
93 1.1 christos struct {
94 1.1 christos int flag;
95 1.1 christos struct cmd_parse_commands *commands;
96 1.1 christos } elif;
97 1.1 christos struct cmd_parse_commands *commands;
98 1.1 christos struct cmd_parse_command *command;
99 1.1 christos }
100 1.1 christos
101 1.1 christos %token ERROR
102 1.1 christos %token IF
103 1.1 christos %token ELSE
104 1.1 christos %token ELIF
105 1.1 christos %token ENDIF
106 1.1 christos %token <token> FORMAT TOKEN EQUALS
107 1.1 christos
108 1.1 christos %type <token> argument expanded format
109 1.1 christos %type <arguments> arguments
110 1.1 christos %type <flag> if_open if_elif
111 1.1 christos %type <elif> elif elif1
112 1.1 christos %type <commands> statements statement commands condition condition1
113 1.1 christos %type <command> command
114 1.1 christos
115 1.1 christos %%
116 1.1 christos
117 1.1 christos lines : /* empty */
118 1.1 christos | statements
119 1.1 christos {
120 1.1 christos struct cmd_parse_state *ps = &parse_state;
121 1.1 christos
122 1.1 christos ps->commands = $1;
123 1.1 christos }
124 1.1 christos
125 1.1 christos statements : statement '\n'
126 1.1 christos {
127 1.1 christos $$ = $1;
128 1.1 christos }
129 1.1 christos | statements statement '\n'
130 1.1 christos {
131 1.1 christos $$ = $1;
132 1.1 christos TAILQ_CONCAT($$, $2, entry);
133 1.1 christos free($2);
134 1.1 christos }
135 1.1 christos
136 1.1 christos statement : condition
137 1.1 christos {
138 1.1 christos struct cmd_parse_state *ps = &parse_state;
139 1.1 christos
140 1.1 christos if (ps->scope == NULL || ps->scope->flag)
141 1.1 christos $$ = $1;
142 1.1 christos else {
143 1.1 christos $$ = cmd_parse_new_commands();
144 1.1 christos cmd_parse_free_commands($1);
145 1.1 christos }
146 1.1 christos }
147 1.1 christos | assignment
148 1.1 christos {
149 1.1 christos $$ = xmalloc (sizeof *$$);
150 1.1 christos TAILQ_INIT($$);
151 1.1 christos }
152 1.1 christos | commands
153 1.1 christos {
154 1.1 christos struct cmd_parse_state *ps = &parse_state;
155 1.1 christos
156 1.1 christos if (ps->scope == NULL || ps->scope->flag)
157 1.1 christos $$ = $1;
158 1.1 christos else {
159 1.1 christos $$ = cmd_parse_new_commands();
160 1.1 christos cmd_parse_free_commands($1);
161 1.1 christos }
162 1.1 christos }
163 1.1 christos
164 1.1 christos format : FORMAT
165 1.1 christos {
166 1.1 christos $$ = $1;
167 1.1 christos }
168 1.1 christos | TOKEN
169 1.1 christos {
170 1.1 christos $$ = $1;
171 1.1 christos }
172 1.1 christos
173 1.1 christos expanded : format
174 1.1 christos {
175 1.1 christos struct cmd_parse_state *ps = &parse_state;
176 1.1 christos struct cmd_parse_input *pi = ps->input;
177 1.1 christos struct format_tree *ft;
178 1.1 christos struct client *c = pi->c;
179 1.1 christos struct cmd_find_state *fs;
180 1.1 christos int flags = FORMAT_NOJOBS;
181 1.1 christos
182 1.1 christos if (cmd_find_valid_state(&pi->fs))
183 1.1 christos fs = &pi->fs;
184 1.1 christos else
185 1.1 christos fs = NULL;
186 1.1 christos ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
187 1.1 christos if (fs != NULL)
188 1.1 christos format_defaults(ft, c, fs->s, fs->wl, fs->wp);
189 1.1 christos else
190 1.1 christos format_defaults(ft, c, NULL, NULL, NULL);
191 1.1 christos
192 1.1 christos $$ = format_expand(ft, $1);
193 1.1 christos format_free(ft);
194 1.1 christos free($1);
195 1.1 christos }
196 1.1 christos
197 1.1 christos assignment : /* empty */
198 1.1 christos | EQUALS
199 1.1 christos {
200 1.1 christos struct cmd_parse_state *ps = &parse_state;
201 1.1 christos int flags = ps->input->flags;
202 1.1 christos
203 1.1 christos if ((~flags & CMD_PARSE_PARSEONLY) &&
204 1.1 christos (ps->scope == NULL || ps->scope->flag))
205 1.1 christos environ_put(global_environ, $1);
206 1.1 christos free($1);
207 1.1 christos }
208 1.1 christos
209 1.1 christos if_open : IF expanded
210 1.1 christos {
211 1.1 christos struct cmd_parse_state *ps = &parse_state;
212 1.1 christos struct cmd_parse_scope *scope;
213 1.1 christos
214 1.1 christos scope = xmalloc(sizeof *scope);
215 1.1 christos $$ = scope->flag = format_true($2);
216 1.1 christos free($2);
217 1.1 christos
218 1.1 christos if (ps->scope != NULL)
219 1.1 christos TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
220 1.1 christos ps->scope = scope;
221 1.1 christos }
222 1.1 christos
223 1.1 christos if_else : ELSE
224 1.1 christos {
225 1.1 christos struct cmd_parse_state *ps = &parse_state;
226 1.1 christos struct cmd_parse_scope *scope;
227 1.1 christos
228 1.1 christos scope = xmalloc(sizeof *scope);
229 1.1 christos scope->flag = !ps->scope->flag;
230 1.1 christos
231 1.1 christos free(ps->scope);
232 1.1 christos ps->scope = scope;
233 1.1 christos }
234 1.1 christos
235 1.1 christos if_elif : ELIF expanded
236 1.1 christos {
237 1.1 christos struct cmd_parse_state *ps = &parse_state;
238 1.1 christos struct cmd_parse_scope *scope;
239 1.1 christos
240 1.1 christos scope = xmalloc(sizeof *scope);
241 1.1 christos $$ = scope->flag = format_true($2);
242 1.1 christos free($2);
243 1.1 christos
244 1.1 christos free(ps->scope);
245 1.1 christos ps->scope = scope;
246 1.1 christos }
247 1.1 christos
248 1.1 christos if_close : ENDIF
249 1.1 christos {
250 1.1 christos struct cmd_parse_state *ps = &parse_state;
251 1.1 christos
252 1.1 christos free(ps->scope);
253 1.1 christos ps->scope = TAILQ_FIRST(&ps->stack);
254 1.1 christos if (ps->scope != NULL)
255 1.1 christos TAILQ_REMOVE(&ps->stack, ps->scope, entry);
256 1.1 christos }
257 1.1 christos
258 1.1 christos condition : if_open '\n' statements if_close
259 1.1 christos {
260 1.1 christos if ($1)
261 1.1 christos $$ = $3;
262 1.1 christos else {
263 1.1 christos $$ = cmd_parse_new_commands();
264 1.1 christos cmd_parse_free_commands($3);
265 1.1 christos }
266 1.1 christos }
267 1.1 christos | if_open '\n' statements if_else '\n' statements if_close
268 1.1 christos {
269 1.1 christos if ($1) {
270 1.1 christos $$ = $3;
271 1.1 christos cmd_parse_free_commands($6);
272 1.1 christos } else {
273 1.1 christos $$ = $6;
274 1.1 christos cmd_parse_free_commands($3);
275 1.1 christos }
276 1.1 christos }
277 1.1 christos | if_open '\n' statements elif if_close
278 1.1 christos {
279 1.1 christos if ($1) {
280 1.1 christos $$ = $3;
281 1.1 christos cmd_parse_free_commands($4.commands);
282 1.1 christos } else if ($4.flag) {
283 1.1 christos $$ = $4.commands;
284 1.1 christos cmd_parse_free_commands($3);
285 1.1 christos } else {
286 1.1 christos $$ = cmd_parse_new_commands();
287 1.1 christos cmd_parse_free_commands($3);
288 1.1 christos cmd_parse_free_commands($4.commands);
289 1.1 christos }
290 1.1 christos }
291 1.1 christos | if_open '\n' statements elif if_else '\n' statements if_close
292 1.1 christos {
293 1.1 christos if ($1) {
294 1.1 christos $$ = $3;
295 1.1 christos cmd_parse_free_commands($4.commands);
296 1.1 christos cmd_parse_free_commands($7);
297 1.1 christos } else if ($4.flag) {
298 1.1 christos $$ = $4.commands;
299 1.1 christos cmd_parse_free_commands($3);
300 1.1 christos cmd_parse_free_commands($7);
301 1.1 christos } else {
302 1.1 christos $$ = $7;
303 1.1 christos cmd_parse_free_commands($3);
304 1.1 christos cmd_parse_free_commands($4.commands);
305 1.1 christos }
306 1.1 christos }
307 1.1 christos
308 1.1 christos elif : if_elif '\n' statements
309 1.1 christos {
310 1.1 christos if ($1) {
311 1.1 christos $$.flag = 1;
312 1.1 christos $$.commands = $3;
313 1.1 christos } else {
314 1.1 christos $$.flag = 0;
315 1.1 christos $$.commands = cmd_parse_new_commands();
316 1.1 christos cmd_parse_free_commands($3);
317 1.1 christos }
318 1.1 christos }
319 1.1 christos | if_elif '\n' statements elif
320 1.1 christos {
321 1.1 christos if ($1) {
322 1.1 christos $$.flag = 1;
323 1.1 christos $$.commands = $3;
324 1.1 christos cmd_parse_free_commands($4.commands);
325 1.1 christos } else if ($4.flag) {
326 1.1 christos $$.flag = 1;
327 1.1 christos $$.commands = $4.commands;
328 1.1 christos cmd_parse_free_commands($3);
329 1.1 christos } else {
330 1.1 christos $$.flag = 0;
331 1.1 christos $$.commands = cmd_parse_new_commands();
332 1.1 christos cmd_parse_free_commands($3);
333 1.1 christos cmd_parse_free_commands($4.commands);
334 1.1 christos }
335 1.1 christos }
336 1.1 christos
337 1.1 christos commands : command
338 1.1 christos {
339 1.1 christos struct cmd_parse_state *ps = &parse_state;
340 1.1 christos
341 1.1 christos $$ = cmd_parse_new_commands();
342 1.1 christos if (ps->scope == NULL || ps->scope->flag)
343 1.1 christos TAILQ_INSERT_TAIL($$, $1, entry);
344 1.1 christos else
345 1.1 christos cmd_parse_free_command($1);
346 1.1 christos }
347 1.1 christos | commands ';'
348 1.1 christos {
349 1.1 christos $$ = $1;
350 1.1 christos }
351 1.1 christos | commands ';' condition1
352 1.1 christos {
353 1.1 christos $$ = $1;
354 1.1 christos TAILQ_CONCAT($$, $3, entry);
355 1.1 christos free($3);
356 1.1 christos }
357 1.1 christos | commands ';' command
358 1.1 christos {
359 1.1 christos struct cmd_parse_state *ps = &parse_state;
360 1.1 christos
361 1.1 christos if (ps->scope == NULL || ps->scope->flag) {
362 1.1 christos $$ = $1;
363 1.1 christos TAILQ_INSERT_TAIL($$, $3, entry);
364 1.1 christos } else {
365 1.1 christos $$ = cmd_parse_new_commands();
366 1.1 christos cmd_parse_free_commands($1);
367 1.1 christos cmd_parse_free_command($3);
368 1.1 christos }
369 1.1 christos }
370 1.1 christos | condition1
371 1.1 christos {
372 1.1 christos $$ = $1;
373 1.1 christos }
374 1.1 christos
375 1.1 christos command : assignment TOKEN
376 1.1 christos {
377 1.1 christos struct cmd_parse_state *ps = &parse_state;
378 1.1 christos
379 1.1 christos $$ = xcalloc(1, sizeof *$$);
380 1.1 christos $$->name = $2;
381 1.1 christos $$->line = ps->input->line;
382 1.1 christos
383 1.1 christos }
384 1.1 christos | assignment TOKEN arguments
385 1.1 christos {
386 1.1 christos struct cmd_parse_state *ps = &parse_state;
387 1.1 christos
388 1.1 christos $$ = xcalloc(1, sizeof *$$);
389 1.1 christos $$->name = $2;
390 1.1 christos $$->line = ps->input->line;
391 1.1 christos
392 1.1 christos $$->argc = $3.argc;
393 1.1 christos $$->argv = $3.argv;
394 1.1 christos }
395 1.1 christos
396 1.1 christos condition1 : if_open commands if_close
397 1.1 christos {
398 1.1 christos if ($1)
399 1.1 christos $$ = $2;
400 1.1 christos else {
401 1.1 christos $$ = cmd_parse_new_commands();
402 1.1 christos cmd_parse_free_commands($2);
403 1.1 christos }
404 1.1 christos }
405 1.1 christos | if_open commands if_else commands if_close
406 1.1 christos {
407 1.1 christos if ($1) {
408 1.1 christos $$ = $2;
409 1.1 christos cmd_parse_free_commands($4);
410 1.1 christos } else {
411 1.1 christos $$ = $4;
412 1.1 christos cmd_parse_free_commands($2);
413 1.1 christos }
414 1.1 christos }
415 1.1 christos | if_open commands elif1 if_close
416 1.1 christos {
417 1.1 christos if ($1) {
418 1.1 christos $$ = $2;
419 1.1 christos cmd_parse_free_commands($3.commands);
420 1.1 christos } else if ($3.flag) {
421 1.1 christos $$ = $3.commands;
422 1.1 christos cmd_parse_free_commands($2);
423 1.1 christos } else {
424 1.1 christos $$ = cmd_parse_new_commands();
425 1.1 christos cmd_parse_free_commands($2);
426 1.1 christos cmd_parse_free_commands($3.commands);
427 1.1 christos }
428 1.1 christos }
429 1.1 christos | if_open commands elif1 if_else commands if_close
430 1.1 christos {
431 1.1 christos if ($1) {
432 1.1 christos $$ = $2;
433 1.1 christos cmd_parse_free_commands($3.commands);
434 1.1 christos cmd_parse_free_commands($5);
435 1.1 christos } else if ($3.flag) {
436 1.1 christos $$ = $3.commands;
437 1.1 christos cmd_parse_free_commands($2);
438 1.1 christos cmd_parse_free_commands($5);
439 1.1 christos } else {
440 1.1 christos $$ = $5;
441 1.1 christos cmd_parse_free_commands($2);
442 1.1 christos cmd_parse_free_commands($3.commands);
443 1.1 christos }
444 1.1 christos }
445 1.1 christos
446 1.1 christos elif1 : if_elif commands
447 1.1 christos {
448 1.1 christos if ($1) {
449 1.1 christos $$.flag = 1;
450 1.1 christos $$.commands = $2;
451 1.1 christos } else {
452 1.1 christos $$.flag = 0;
453 1.1 christos $$.commands = cmd_parse_new_commands();
454 1.1 christos cmd_parse_free_commands($2);
455 1.1 christos }
456 1.1 christos }
457 1.1 christos | if_elif commands elif1
458 1.1 christos {
459 1.1 christos if ($1) {
460 1.1 christos $$.flag = 1;
461 1.1 christos $$.commands = $2;
462 1.1 christos cmd_parse_free_commands($3.commands);
463 1.1 christos } else if ($3.flag) {
464 1.1 christos $$.flag = 1;
465 1.1 christos $$.commands = $3.commands;
466 1.1 christos cmd_parse_free_commands($2);
467 1.1 christos } else {
468 1.1 christos $$.flag = 0;
469 1.1 christos $$.commands = cmd_parse_new_commands();
470 1.1 christos cmd_parse_free_commands($2);
471 1.1 christos cmd_parse_free_commands($3.commands);
472 1.1 christos }
473 1.1 christos }
474 1.1 christos
475 1.1 christos arguments : argument
476 1.1 christos {
477 1.1 christos $$.argc = 1;
478 1.1 christos $$.argv = xreallocarray(NULL, 1, sizeof *$$.argv);
479 1.1 christos
480 1.1 christos $$.argv[0] = $1;
481 1.1 christos }
482 1.1 christos | argument arguments
483 1.1 christos {
484 1.1 christos cmd_prepend_argv(&$2.argc, &$2.argv, $1);
485 1.1 christos free($1);
486 1.1 christos $$ = $2;
487 1.1 christos }
488 1.1 christos
489 1.1 christos argument : TOKEN
490 1.1 christos {
491 1.1 christos $$ = $1;
492 1.1 christos }
493 1.1 christos | EQUALS
494 1.1 christos {
495 1.1 christos $$ = $1;
496 1.1 christos }
497 1.1 christos
498 1.1 christos %%
499 1.1 christos
500 1.1 christos static char *
501 1.1 christos cmd_parse_get_error(const char *file, u_int line, const char *error)
502 1.1 christos {
503 1.1 christos char *s;
504 1.1 christos
505 1.1 christos if (file == NULL)
506 1.1 christos s = xstrdup(error);
507 1.1 christos else
508 1.1 christos xasprintf (&s, "%s:%u: %s", file, line, error);
509 1.1 christos return (s);
510 1.1 christos }
511 1.1 christos
512 1.1 christos static void
513 1.1 christos cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
514 1.1 christos struct cmd_list *cmdlist)
515 1.1 christos {
516 1.1 christos char *s;
517 1.1 christos
518 1.1 christos if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
519 1.1 christos s = cmd_list_print(cmdlist, 0);
520 1.1 christos if (pi->file != NULL)
521 1.1 christos cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
522 1.1 christos else
523 1.1 christos cmdq_print(pi->item, "%u: %s", line, s);
524 1.1 christos free(s);
525 1.1 christos }
526 1.1 christos }
527 1.1 christos
528 1.1 christos static void
529 1.1 christos cmd_parse_free_command(struct cmd_parse_command *cmd)
530 1.1 christos {
531 1.1 christos free(cmd->name);
532 1.1 christos cmd_free_argv(cmd->argc, cmd->argv);
533 1.1 christos free(cmd);
534 1.1 christos }
535 1.1 christos
536 1.1 christos static struct cmd_parse_commands *
537 1.1 christos cmd_parse_new_commands(void)
538 1.1 christos {
539 1.1 christos struct cmd_parse_commands *cmds;
540 1.1 christos
541 1.1 christos cmds = xmalloc(sizeof *cmds);
542 1.1 christos TAILQ_INIT (cmds);
543 1.1 christos return (cmds);
544 1.1 christos }
545 1.1 christos
546 1.1 christos static void
547 1.1 christos cmd_parse_free_commands(struct cmd_parse_commands *cmds)
548 1.1 christos {
549 1.1 christos struct cmd_parse_command *cmd, *cmd1;
550 1.1 christos
551 1.1 christos TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
552 1.1 christos TAILQ_REMOVE(cmds, cmd, entry);
553 1.1 christos cmd_parse_free_command(cmd);
554 1.1 christos }
555 1.1 christos free(cmds);
556 1.1 christos }
557 1.1 christos
558 1.1 christos static struct cmd_parse_commands *
559 1.1 christos cmd_parse_run_parser(char **cause)
560 1.1 christos {
561 1.1 christos struct cmd_parse_state *ps = &parse_state;
562 1.1 christos struct cmd_parse_scope *scope, *scope1;
563 1.1 christos int retval;
564 1.1 christos
565 1.1 christos ps->commands = NULL;
566 1.1 christos TAILQ_INIT(&ps->stack);
567 1.1 christos
568 1.1 christos retval = yyparse();
569 1.1 christos TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
570 1.1 christos TAILQ_REMOVE(&ps->stack, scope, entry);
571 1.1 christos free(scope);
572 1.1 christos }
573 1.1 christos if (retval != 0) {
574 1.1 christos *cause = ps->error;
575 1.1 christos return (NULL);
576 1.1 christos }
577 1.1 christos
578 1.1 christos if (ps->commands == NULL)
579 1.1 christos return (cmd_parse_new_commands());
580 1.1 christos return (ps->commands);
581 1.1 christos }
582 1.1 christos
583 1.1 christos static struct cmd_parse_commands *
584 1.1 christos cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
585 1.1 christos {
586 1.1 christos struct cmd_parse_state *ps = &parse_state;
587 1.1 christos
588 1.1 christos memset(ps, 0, sizeof *ps);
589 1.1 christos ps->input = pi;
590 1.1 christos ps->f = f;
591 1.1 christos return (cmd_parse_run_parser(cause));
592 1.1 christos }
593 1.1 christos
594 1.1 christos static struct cmd_parse_commands *
595 1.1 christos cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
596 1.1 christos char **cause)
597 1.1 christos {
598 1.1 christos struct cmd_parse_state *ps = &parse_state;
599 1.1 christos
600 1.1 christos memset(ps, 0, sizeof *ps);
601 1.1 christos ps->input = pi;
602 1.1 christos ps->buf = buf;
603 1.1 christos ps->len = len;
604 1.1 christos return (cmd_parse_run_parser(cause));
605 1.1 christos }
606 1.1 christos
607 1.1 christos static struct cmd_parse_result *
608 1.1 christos cmd_parse_build_commands(struct cmd_parse_commands *cmds,
609 1.1 christos struct cmd_parse_input *pi)
610 1.1 christos {
611 1.1 christos static struct cmd_parse_result pr;
612 1.1 christos struct cmd_parse_commands *cmds2;
613 1.1 christos struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after;
614 1.1 christos u_int line = UINT_MAX;
615 1.1 christos int i;
616 1.1 christos struct cmd_list *cmdlist = NULL, *result;
617 1.1 christos struct cmd *add;
618 1.1 christos char *alias, *cause, *s;
619 1.1 christos
620 1.1 christos /* Check for an empty list. */
621 1.1 christos if (TAILQ_EMPTY(cmds)) {
622 1.1 christos cmd_parse_free_commands(cmds);
623 1.1 christos pr.status = CMD_PARSE_EMPTY;
624 1.1 christos return (&pr);
625 1.1 christos }
626 1.1 christos
627 1.1 christos /*
628 1.1 christos * Walk the commands and expand any aliases. Each alias is parsed
629 1.1 christos * individually to a new command list, any trailing arguments appended
630 1.1 christos * to the last command, and all commands inserted into the original
631 1.1 christos * command list.
632 1.1 christos */
633 1.1 christos TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
634 1.1 christos alias = cmd_get_alias(cmd->name);
635 1.1 christos if (alias == NULL)
636 1.1 christos continue;
637 1.1 christos
638 1.1 christos line = cmd->line;
639 1.1 christos log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias);
640 1.1 christos
641 1.1 christos pi->line = line;
642 1.1 christos cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
643 1.1 christos free(alias);
644 1.1 christos if (cmds2 == NULL) {
645 1.1 christos pr.status = CMD_PARSE_ERROR;
646 1.1 christos pr.error = cause;
647 1.1 christos goto out;
648 1.1 christos }
649 1.1 christos
650 1.1 christos cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands);
651 1.1 christos if (cmd2 == NULL) {
652 1.1 christos TAILQ_REMOVE(cmds, cmd, entry);
653 1.1 christos cmd_parse_free_command(cmd);
654 1.1 christos continue;
655 1.1 christos }
656 1.1 christos for (i = 0; i < cmd->argc; i++)
657 1.1 christos cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
658 1.1 christos
659 1.1 christos after = cmd;
660 1.1 christos TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) {
661 1.1 christos cmd2->line = line;
662 1.1 christos TAILQ_REMOVE(cmds2, cmd2, entry);
663 1.1 christos TAILQ_INSERT_AFTER(cmds, after, cmd2, entry);
664 1.1 christos after = cmd2;
665 1.1 christos }
666 1.1 christos cmd_parse_free_commands(cmds2);
667 1.1 christos
668 1.1 christos TAILQ_REMOVE(cmds, cmd, entry);
669 1.1 christos cmd_parse_free_command(cmd);
670 1.1 christos }
671 1.1 christos
672 1.1 christos /*
673 1.1 christos * Parse each command into a command list. Create a new command list
674 1.1 christos * for each line so they get a new group (so the queue knows which ones
675 1.1 christos * to remove if a command fails when executed).
676 1.1 christos */
677 1.1 christos result = cmd_list_new();
678 1.1 christos TAILQ_FOREACH(cmd, cmds, entry) {
679 1.1 christos log_debug("%s: %u %s", __func__, cmd->line, cmd->name);
680 1.1 christos cmd_log_argv(cmd->argc, cmd->argv, __func__);
681 1.1 christos
682 1.1 christos if (cmdlist == NULL || cmd->line != line) {
683 1.1 christos if (cmdlist != NULL) {
684 1.1 christos cmd_parse_print_commands(pi, line, cmdlist);
685 1.1 christos cmd_list_move(result, cmdlist);
686 1.1 christos cmd_list_free(cmdlist);
687 1.1 christos }
688 1.1 christos cmdlist = cmd_list_new();
689 1.1 christos }
690 1.1 christos line = cmd->line;
691 1.1 christos
692 1.1 christos cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name);
693 1.1 christos add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
694 1.1 christos if (add == NULL) {
695 1.1 christos cmd_list_free(result);
696 1.1 christos pr.status = CMD_PARSE_ERROR;
697 1.1 christos pr.error = cmd_parse_get_error(pi->file, line, cause);
698 1.1 christos free(cause);
699 1.1 christos goto out;
700 1.1 christos }
701 1.1 christos cmd_list_append(cmdlist, add);
702 1.1 christos }
703 1.1 christos if (cmdlist != NULL) {
704 1.1 christos cmd_parse_print_commands(pi, line, cmdlist);
705 1.1 christos cmd_list_move(result, cmdlist);
706 1.1 christos cmd_list_free(cmdlist);
707 1.1 christos }
708 1.1 christos
709 1.1 christos s = cmd_list_print(result, 0);
710 1.1 christos log_debug("%s: %s", __func__, s);
711 1.1 christos free(s);
712 1.1 christos
713 1.1 christos pr.status = CMD_PARSE_SUCCESS;
714 1.1 christos pr.cmdlist = result;
715 1.1 christos
716 1.1 christos out:
717 1.1 christos cmd_parse_free_commands(cmds);
718 1.1 christos
719 1.1 christos return (&pr);
720 1.1 christos }
721 1.1 christos
722 1.1 christos struct cmd_parse_result *
723 1.1 christos cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
724 1.1 christos {
725 1.1 christos static struct cmd_parse_result pr;
726 1.1 christos struct cmd_parse_input input;
727 1.1 christos struct cmd_parse_commands *cmds;
728 1.1 christos char *cause;
729 1.1 christos
730 1.1 christos if (pi == NULL) {
731 1.1 christos memset(&input, 0, sizeof input);
732 1.1 christos pi = &input;
733 1.1 christos }
734 1.1 christos memset(&pr, 0, sizeof pr);
735 1.1 christos
736 1.1 christos cmds = cmd_parse_do_file(f, pi, &cause);
737 1.1 christos if (cmds == NULL) {
738 1.1 christos pr.status = CMD_PARSE_ERROR;
739 1.1 christos pr.error = cause;
740 1.1 christos return (&pr);
741 1.1 christos }
742 1.1 christos return (cmd_parse_build_commands(cmds, pi));
743 1.1 christos }
744 1.1 christos
745 1.1 christos struct cmd_parse_result *
746 1.1 christos cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
747 1.1 christos {
748 1.1 christos static struct cmd_parse_result pr;
749 1.1 christos struct cmd_parse_input input;
750 1.1 christos struct cmd_parse_commands *cmds;
751 1.1 christos char *cause;
752 1.1 christos
753 1.1 christos if (pi == NULL) {
754 1.1 christos memset(&input, 0, sizeof input);
755 1.1 christos pi = &input;
756 1.1 christos }
757 1.1 christos memset(&pr, 0, sizeof pr);
758 1.1 christos
759 1.1 christos if (*s == '\0') {
760 1.1 christos pr.status = CMD_PARSE_EMPTY;
761 1.1 christos pr.cmdlist = NULL;
762 1.1 christos pr.error = NULL;
763 1.1 christos return (&pr);
764 1.1 christos }
765 1.1 christos
766 1.1 christos cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause);
767 1.1 christos if (cmds == NULL) {
768 1.1 christos pr.status = CMD_PARSE_ERROR;
769 1.1 christos pr.error = cause;
770 1.1 christos return (&pr);
771 1.1 christos }
772 1.1 christos return (cmd_parse_build_commands(cmds, pi));
773 1.1 christos }
774 1.1 christos
775 1.1 christos struct cmd_parse_result *
776 1.1 christos cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
777 1.1 christos {
778 1.1 christos struct cmd_parse_input input;
779 1.1 christos struct cmd_parse_commands *cmds;
780 1.1 christos struct cmd_parse_command *cmd;
781 1.1 christos char **copy, **new_argv;
782 1.1 christos size_t size;
783 1.1 christos int i, last, new_argc;
784 1.1 christos
785 1.1 christos /*
786 1.1 christos * The commands are already split up into arguments, so just separate
787 1.1 christos * into a set of commands by ';'.
788 1.1 christos */
789 1.1 christos
790 1.1 christos if (pi == NULL) {
791 1.1 christos memset(&input, 0, sizeof input);
792 1.1 christos pi = &input;
793 1.1 christos }
794 1.1 christos cmd_log_argv(argc, argv, "%s", __func__);
795 1.1 christos
796 1.1 christos cmds = cmd_parse_new_commands();
797 1.1 christos copy = cmd_copy_argv(argc, argv);
798 1.1 christos
799 1.1 christos last = 0;
800 1.1 christos for (i = 0; i < argc; i++) {
801 1.1 christos size = strlen(copy[i]);
802 1.1 christos if (size == 0 || copy[i][size - 1] != ';')
803 1.1 christos continue;
804 1.1 christos copy[i][--size] = '\0';
805 1.1 christos if (size > 0 && copy[i][size - 1] == '\\') {
806 1.1 christos copy[i][size - 1] = ';';
807 1.1 christos continue;
808 1.1 christos }
809 1.1 christos
810 1.1 christos new_argc = i - last;
811 1.1 christos new_argv = copy + last;
812 1.1 christos if (size != 0)
813 1.1 christos new_argc++;
814 1.1 christos
815 1.1 christos if (new_argc != 0) {
816 1.1 christos cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
817 1.1 christos i);
818 1.1 christos
819 1.1 christos cmd = xcalloc(1, sizeof *cmd);
820 1.1 christos cmd->name = xstrdup(new_argv[0]);
821 1.1 christos cmd->line = pi->line;
822 1.1 christos
823 1.1 christos cmd->argc = new_argc - 1;
824 1.1 christos cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
825 1.1 christos
826 1.1 christos TAILQ_INSERT_TAIL(cmds, cmd, entry);
827 1.1 christos }
828 1.1 christos
829 1.1 christos last = i + 1;
830 1.1 christos }
831 1.1 christos if (last != argc) {
832 1.1 christos new_argv = copy + last;
833 1.1 christos new_argc = argc - last;
834 1.1 christos
835 1.1 christos if (new_argc != 0) {
836 1.1 christos cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
837 1.1 christos last);
838 1.1 christos
839 1.1 christos cmd = xcalloc(1, sizeof *cmd);
840 1.1 christos cmd->name = xstrdup(new_argv[0]);
841 1.1 christos cmd->line = pi->line;
842 1.1 christos
843 1.1 christos cmd->argc = new_argc - 1;
844 1.1 christos cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
845 1.1 christos
846 1.1 christos TAILQ_INSERT_TAIL(cmds, cmd, entry);
847 1.1 christos }
848 1.1 christos }
849 1.1 christos
850 1.1 christos cmd_free_argv(argc, copy);
851 1.1 christos return (cmd_parse_build_commands(cmds, pi));
852 1.1 christos }
853 1.1 christos
854 1.1 christos static int printflike(1, 2)
855 1.1 christos yyerror(const char *fmt, ...)
856 1.1 christos {
857 1.1 christos struct cmd_parse_state *ps = &parse_state;
858 1.1 christos struct cmd_parse_input *pi = ps->input;
859 1.1 christos va_list ap;
860 1.1 christos char *error;
861 1.1 christos
862 1.1 christos if (ps->error != NULL)
863 1.1 christos return (0);
864 1.1 christos
865 1.1 christos va_start(ap, fmt);
866 1.1 christos xvasprintf(&error, fmt, ap);
867 1.1 christos va_end(ap);
868 1.1 christos
869 1.1 christos ps->error = cmd_parse_get_error(pi->file, pi->line, error);
870 1.1 christos free(error);
871 1.1 christos return (0);
872 1.1 christos }
873 1.1 christos
874 1.1 christos static int
875 1.1 christos yylex_is_var(char ch, int first)
876 1.1 christos {
877 1.1 christos if (ch == '=')
878 1.1 christos return (0);
879 1.1 christos if (first && isdigit((u_char)ch))
880 1.1 christos return (0);
881 1.1 christos return (isalnum((u_char)ch) || ch == '_');
882 1.1 christos }
883 1.1 christos
884 1.1 christos static void
885 1.1 christos yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
886 1.1 christos {
887 1.1 christos if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
888 1.1 christos fatalx("buffer is too big");
889 1.1 christos *buf = xrealloc(*buf, (*len) + 1 + addlen);
890 1.1 christos memcpy((*buf) + *len, add, addlen);
891 1.1 christos (*len) += addlen;
892 1.1 christos }
893 1.1 christos
894 1.1 christos static void
895 1.1 christos yylex_append1(char **buf, size_t *len, char add)
896 1.1 christos {
897 1.1 christos yylex_append(buf, len, &add, 1);
898 1.1 christos }
899 1.1 christos
900 1.1 christos static int
901 1.1 christos yylex_getc1(void)
902 1.1 christos {
903 1.1 christos struct cmd_parse_state *ps = &parse_state;
904 1.1 christos int ch;
905 1.1 christos
906 1.1 christos if (ps->f != NULL)
907 1.1 christos ch = getc(ps->f);
908 1.1 christos else {
909 1.1 christos if (ps->off == ps->len)
910 1.1 christos ch = EOF;
911 1.1 christos else
912 1.1 christos ch = ps->buf[ps->off++];
913 1.1 christos }
914 1.1 christos return (ch);
915 1.1 christos }
916 1.1 christos
917 1.1 christos static void
918 1.1 christos yylex_ungetc(int ch)
919 1.1 christos {
920 1.1 christos struct cmd_parse_state *ps = &parse_state;
921 1.1 christos
922 1.1 christos if (ps->f != NULL)
923 1.1 christos ungetc(ch, ps->f);
924 1.1 christos else if (ps->off > 0 && ch != EOF)
925 1.1 christos ps->off--;
926 1.1 christos }
927 1.1 christos
928 1.1 christos static int
929 1.1 christos yylex_getc(void)
930 1.1 christos {
931 1.1 christos struct cmd_parse_state *ps = &parse_state;
932 1.1 christos int ch;
933 1.1 christos
934 1.1 christos if (ps->escapes != 0) {
935 1.1 christos ps->escapes--;
936 1.1 christos return ('\\');
937 1.1 christos }
938 1.1 christos for (;;) {
939 1.1 christos ch = yylex_getc1();
940 1.1 christos if (ch == '\\') {
941 1.1 christos ps->escapes++;
942 1.1 christos continue;
943 1.1 christos }
944 1.1 christos if (ch == '\n' && (ps->escapes % 2) == 1) {
945 1.1 christos ps->input->line++;
946 1.1 christos ps->escapes--;
947 1.1 christos continue;
948 1.1 christos }
949 1.1 christos
950 1.1 christos if (ps->escapes != 0) {
951 1.1 christos yylex_ungetc(ch);
952 1.1 christos ps->escapes--;
953 1.1 christos return ('\\');
954 1.1 christos }
955 1.1 christos return (ch);
956 1.1 christos }
957 1.1 christos }
958 1.1 christos
959 1.1 christos static char *
960 1.1 christos yylex_get_word(int ch)
961 1.1 christos {
962 1.1 christos char *buf;
963 1.1 christos size_t len;
964 1.1 christos
965 1.1 christos len = 0;
966 1.1 christos buf = xmalloc(1);
967 1.1 christos
968 1.1 christos do
969 1.1 christos yylex_append1(&buf, &len, ch);
970 1.1 christos while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
971 1.1 christos yylex_ungetc(ch);
972 1.1 christos
973 1.1 christos buf[len] = '\0';
974 1.1 christos log_debug("%s: %s", __func__, buf);
975 1.1 christos return (buf);
976 1.1 christos }
977 1.1 christos
978 1.1 christos static int
979 1.1 christos yylex(void)
980 1.1 christos {
981 1.1 christos struct cmd_parse_state *ps = &parse_state;
982 1.1 christos char *token, *cp;
983 1.1 christos int ch, next, condition;
984 1.1 christos
985 1.1 christos if (ps->eol)
986 1.1 christos ps->input->line++;
987 1.1 christos ps->eol = 0;
988 1.1 christos
989 1.1 christos condition = ps->condition;
990 1.1 christos ps->condition = 0;
991 1.1 christos
992 1.1 christos for (;;) {
993 1.1 christos ch = yylex_getc();
994 1.1 christos
995 1.1 christos if (ch == EOF) {
996 1.1 christos /*
997 1.1 christos * Ensure every file or string is terminated by a
998 1.1 christos * newline. This keeps the parser simpler and avoids
999 1.1 christos * having to add a newline to each string.
1000 1.1 christos */
1001 1.1 christos if (ps->eof)
1002 1.1 christos break;
1003 1.1 christos ps->eof = 1;
1004 1.1 christos return ('\n');
1005 1.1 christos }
1006 1.1 christos
1007 1.1 christos if (ch == ' ' || ch == '\t') {
1008 1.1 christos /*
1009 1.1 christos * Ignore whitespace.
1010 1.1 christos */
1011 1.1 christos continue;
1012 1.1 christos }
1013 1.1 christos
1014 1.1 christos if (ch == '\n') {
1015 1.1 christos /*
1016 1.1 christos * End of line. Update the line number.
1017 1.1 christos */
1018 1.1 christos ps->eol = 1;
1019 1.1 christos return ('\n');
1020 1.1 christos }
1021 1.1 christos
1022 1.1 christos if (ch == ';') {
1023 1.1 christos /*
1024 1.1 christos * A semicolon is itself.
1025 1.1 christos */
1026 1.1 christos return (';');
1027 1.1 christos }
1028 1.1 christos
1029 1.1 christos if (ch == '#') {
1030 1.1 christos /*
1031 1.1 christos * #{ after a condition opens a format; anything else
1032 1.1 christos * is a comment, ignore up to the end of the line.
1033 1.1 christos */
1034 1.1 christos next = yylex_getc();
1035 1.1 christos if (condition && next == '{') {
1036 1.1 christos yylval.token = yylex_format();
1037 1.1 christos if (yylval.token == NULL)
1038 1.1 christos return (ERROR);
1039 1.1 christos return (FORMAT);
1040 1.1 christos }
1041 1.1 christos while (next != '\n' && next != EOF)
1042 1.1 christos next = yylex_getc();
1043 1.1 christos if (next == '\n') {
1044 1.1 christos ps->input->line++;
1045 1.1 christos return ('\n');
1046 1.1 christos }
1047 1.1 christos continue;
1048 1.1 christos }
1049 1.1 christos
1050 1.1 christos if (ch == '%') {
1051 1.1 christos /*
1052 1.1 christos * % is a condition unless it is all % or all numbers,
1053 1.1 christos * then it is a token.
1054 1.1 christos */
1055 1.1 christos yylval.token = yylex_get_word('%');
1056 1.1 christos for (cp = yylval.token; *cp != '\0'; cp++) {
1057 1.1 christos if (*cp != '%' && !isdigit((u_char)*cp))
1058 1.1 christos break;
1059 1.1 christos }
1060 1.1 christos if (*cp == '\0')
1061 1.1 christos return (TOKEN);
1062 1.1 christos ps->condition = 1;
1063 1.1 christos if (strcmp(yylval.token, "%if") == 0) {
1064 1.1 christos free(yylval.token);
1065 1.1 christos return (IF);
1066 1.1 christos }
1067 1.1 christos if (strcmp(yylval.token, "%else") == 0) {
1068 1.1 christos free(yylval.token);
1069 1.1 christos return (ELSE);
1070 1.1 christos }
1071 1.1 christos if (strcmp(yylval.token, "%elif") == 0) {
1072 1.1 christos free(yylval.token);
1073 1.1 christos return (ELIF);
1074 1.1 christos }
1075 1.1 christos if (strcmp(yylval.token, "%endif") == 0) {
1076 1.1 christos free(yylval.token);
1077 1.1 christos return (ENDIF);
1078 1.1 christos }
1079 1.1 christos free(yylval.token);
1080 1.1 christos return (ERROR);
1081 1.1 christos }
1082 1.1 christos
1083 1.1 christos /*
1084 1.1 christos * Otherwise this is a token.
1085 1.1 christos */
1086 1.1 christos token = yylex_token(ch);
1087 1.1 christos if (token == NULL)
1088 1.1 christos return (ERROR);
1089 1.1 christos yylval.token = token;
1090 1.1 christos
1091 1.1 christos if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1092 1.1 christos for (cp = token + 1; *cp != '='; cp++) {
1093 1.1 christos if (!yylex_is_var(*cp, 0))
1094 1.1 christos break;
1095 1.1 christos }
1096 1.1 christos if (*cp == '=')
1097 1.1 christos return (EQUALS);
1098 1.1 christos }
1099 1.1 christos return (TOKEN);
1100 1.1 christos }
1101 1.1 christos return (0);
1102 1.1 christos }
1103 1.1 christos
1104 1.1 christos static char *
1105 1.1 christos yylex_format(void)
1106 1.1 christos {
1107 1.1 christos char *buf;
1108 1.1 christos size_t len;
1109 1.1 christos int ch, brackets = 1;
1110 1.1 christos
1111 1.1 christos len = 0;
1112 1.1 christos buf = xmalloc(1);
1113 1.1 christos
1114 1.1 christos yylex_append(&buf, &len, "#{", 2);
1115 1.1 christos for (;;) {
1116 1.1 christos if ((ch = yylex_getc()) == EOF || ch == '\n')
1117 1.1 christos goto error;
1118 1.1 christos if (ch == '#') {
1119 1.1 christos if ((ch = yylex_getc()) == EOF || ch == '\n')
1120 1.1 christos goto error;
1121 1.1 christos if (ch == '{')
1122 1.1 christos brackets++;
1123 1.1 christos yylex_append1(&buf, &len, '#');
1124 1.1 christos } else if (ch == '}') {
1125 1.1 christos if (brackets != 0 && --brackets == 0) {
1126 1.1 christos yylex_append1(&buf, &len, ch);
1127 1.1 christos break;
1128 1.1 christos }
1129 1.1 christos }
1130 1.1 christos yylex_append1(&buf, &len, ch);
1131 1.1 christos }
1132 1.1 christos if (brackets != 0)
1133 1.1 christos goto error;
1134 1.1 christos
1135 1.1 christos buf[len] = '\0';
1136 1.1 christos log_debug("%s: %s", __func__, buf);
1137 1.1 christos return (buf);
1138 1.1 christos
1139 1.1 christos error:
1140 1.1 christos free(buf);
1141 1.1 christos return (NULL);
1142 1.1 christos }
1143 1.1 christos
1144 1.1 christos static int
1145 1.1 christos yylex_token_escape(char **buf, size_t *len)
1146 1.1 christos {
1147 1.1 christos int ch, type, o2, o3;
1148 1.1 christos u_int size, i, tmp;
1149 1.1 christos char s[9];
1150 1.1 christos struct utf8_data ud;
1151 1.1 christos
1152 1.1 christos ch = yylex_getc();
1153 1.1 christos
1154 1.1 christos if (ch >= '4' && ch <= '7') {
1155 1.1 christos yyerror("invalid octal escape");
1156 1.1 christos return (0);
1157 1.1 christos }
1158 1.1 christos if (ch >= '0' && ch <= '3') {
1159 1.1 christos o2 = yylex_getc();
1160 1.1 christos if (o2 >= '0' && o2 <= '7') {
1161 1.1 christos o3 = yylex_getc();
1162 1.1 christos if (o3 >= '0' && o3 <= '7') {
1163 1.1 christos ch = 64 * (ch - '0') +
1164 1.1 christos 8 * (o2 - '0') +
1165 1.1 christos (o3 - '0');
1166 1.1 christos yylex_append1(buf, len, ch);
1167 1.1 christos return (1);
1168 1.1 christos }
1169 1.1 christos }
1170 1.1 christos yyerror("invalid octal escape");
1171 1.1 christos return (0);
1172 1.1 christos }
1173 1.1 christos
1174 1.1 christos switch (ch) {
1175 1.1 christos case EOF:
1176 1.1 christos return (0);
1177 1.1 christos case 'a':
1178 1.1 christos ch = '\a';
1179 1.1 christos break;
1180 1.1 christos case 'b':
1181 1.1 christos ch = '\b';
1182 1.1 christos break;
1183 1.1 christos case 'e':
1184 1.1 christos ch = '\033';
1185 1.1 christos break;
1186 1.1 christos case 'f':
1187 1.1 christos ch = '\f';
1188 1.1 christos break;
1189 1.1 christos case 's':
1190 1.1 christos ch = ' ';
1191 1.1 christos break;
1192 1.1 christos case 'v':
1193 1.1 christos ch = '\v';
1194 1.1 christos break;
1195 1.1 christos case 'r':
1196 1.1 christos ch = '\r';
1197 1.1 christos break;
1198 1.1 christos case 'n':
1199 1.1 christos ch = '\n';
1200 1.1 christos break;
1201 1.1 christos case 't':
1202 1.1 christos ch = '\t';
1203 1.1 christos break;
1204 1.1 christos case 'u':
1205 1.1 christos type = 'u';
1206 1.1 christos size = 4;
1207 1.1 christos goto unicode;
1208 1.1 christos case 'U':
1209 1.1 christos type = 'U';
1210 1.1 christos size = 8;
1211 1.1 christos goto unicode;
1212 1.1 christos }
1213 1.1 christos
1214 1.1 christos yylex_append1(buf, len, ch);
1215 1.1 christos return (1);
1216 1.1 christos
1217 1.1 christos unicode:
1218 1.1 christos for (i = 0; i < size; i++) {
1219 1.1 christos ch = yylex_getc();
1220 1.1 christos if (ch == EOF || ch == '\n')
1221 1.1 christos return (0);
1222 1.1 christos if (!isxdigit((u_char)ch)) {
1223 1.1 christos yyerror("invalid \\%c argument", type);
1224 1.1 christos return (0);
1225 1.1 christos }
1226 1.1 christos s[i] = ch;
1227 1.1 christos }
1228 1.1 christos s[i] = '\0';
1229 1.1 christos
1230 1.1 christos if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1231 1.1 christos (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1232 1.1 christos yyerror("invalid \\%c argument", type);
1233 1.1 christos return (0);
1234 1.1 christos }
1235 1.1 christos if (utf8_split(tmp, &ud) != UTF8_DONE) {
1236 1.1 christos yyerror("invalid \\%c argument", type);
1237 1.1 christos return (0);
1238 1.1 christos }
1239 1.1 christos yylex_append(buf, len, ud.data, ud.size);
1240 1.1 christos return (1);
1241 1.1 christos }
1242 1.1 christos
1243 1.1 christos static int
1244 1.1 christos yylex_token_variable(char **buf, size_t *len)
1245 1.1 christos {
1246 1.1 christos struct environ_entry *envent;
1247 1.1 christos int ch, brackets = 0;
1248 1.1 christos char name[1024];
1249 1.1 christos size_t namelen = 0;
1250 1.1 christos const char *value;
1251 1.1 christos
1252 1.1 christos ch = yylex_getc();
1253 1.1 christos if (ch == EOF)
1254 1.1 christos return (0);
1255 1.1 christos if (ch == '{')
1256 1.1 christos brackets = 1;
1257 1.1 christos else {
1258 1.1 christos if (!yylex_is_var(ch, 1)) {
1259 1.1 christos yylex_append1(buf, len, '$');
1260 1.1 christos yylex_ungetc(ch);
1261 1.1 christos return (1);
1262 1.1 christos }
1263 1.1 christos name[namelen++] = ch;
1264 1.1 christos }
1265 1.1 christos
1266 1.1 christos for (;;) {
1267 1.1 christos ch = yylex_getc();
1268 1.1 christos if (brackets && ch == '}')
1269 1.1 christos break;
1270 1.1 christos if (ch == EOF || !yylex_is_var(ch, 0)) {
1271 1.1 christos if (!brackets) {
1272 1.1 christos yylex_ungetc(ch);
1273 1.1 christos break;
1274 1.1 christos }
1275 1.1 christos yyerror("invalid environment variable");
1276 1.1 christos return (0);
1277 1.1 christos }
1278 1.1 christos if (namelen == (sizeof name) - 2) {
1279 1.1 christos yyerror("environment variable is too long");
1280 1.1 christos return (0);
1281 1.1 christos }
1282 1.1 christos name[namelen++] = ch;
1283 1.1 christos }
1284 1.1 christos name[namelen] = '\0';
1285 1.1 christos
1286 1.1 christos envent = environ_find(global_environ, name);
1287 1.1 christos if (envent != NULL) {
1288 1.1 christos value = envent->value;
1289 1.1 christos log_debug("%s: %s -> %s", __func__, name, value);
1290 1.1 christos yylex_append(buf, len, value, strlen(value));
1291 1.1 christos }
1292 1.1 christos return (1);
1293 1.1 christos }
1294 1.1 christos
1295 1.1 christos static int
1296 1.1 christos yylex_token_tilde(char **buf, size_t *len)
1297 1.1 christos {
1298 1.1 christos struct environ_entry *envent;
1299 1.1 christos int ch;
1300 1.1 christos char name[1024];
1301 1.1 christos size_t namelen = 0;
1302 1.1 christos struct passwd *pw;
1303 1.1 christos const char *home = NULL;
1304 1.1 christos
1305 1.1 christos for (;;) {
1306 1.1 christos ch = yylex_getc();
1307 1.1 christos if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1308 1.1 christos yylex_ungetc(ch);
1309 1.1 christos break;
1310 1.1 christos }
1311 1.1 christos if (namelen == (sizeof name) - 2) {
1312 1.1 christos yyerror("user name is too long");
1313 1.1 christos return (0);
1314 1.1 christos }
1315 1.1 christos name[namelen++] = ch;
1316 1.1 christos }
1317 1.1 christos name[namelen] = '\0';
1318 1.1 christos
1319 1.1 christos if (*name == '\0') {
1320 1.1 christos envent = environ_find(global_environ, "HOME");
1321 1.1 christos if (envent != NULL && *envent->value != '\0')
1322 1.1 christos home = envent->value;
1323 1.1 christos else if ((pw = getpwuid(getuid())) != NULL)
1324 1.1 christos home = pw->pw_dir;
1325 1.1 christos } else {
1326 1.1 christos if ((pw = getpwnam(name)) != NULL)
1327 1.1 christos home = pw->pw_dir;
1328 1.1 christos }
1329 1.1 christos if (home == NULL)
1330 1.1 christos return (0);
1331 1.1 christos
1332 1.1 christos log_debug("%s: ~%s -> %s", __func__, name, home);
1333 1.1 christos yylex_append(buf, len, home, strlen(home));
1334 1.1 christos return (1);
1335 1.1 christos }
1336 1.1 christos
1337 1.1 christos static int
1338 1.1 christos yylex_token_brace(char **buf, size_t *len)
1339 1.1 christos {
1340 1.1 christos struct cmd_parse_state *ps = &parse_state;
1341 1.1 christos int ch, lines = 0, nesting = 1, escape = 0;
1342 1.1 christos int quote = '\0', token = 0;
1343 1.1 christos
1344 1.1 christos /*
1345 1.1 christos * Extract a string up to the matching unquoted '}', including newlines
1346 1.1 christos * and handling nested braces.
1347 1.1 christos *
1348 1.1 christos * To detect the final and intermediate braces which affect the nesting
1349 1.1 christos * depth, we scan the input as if it was a tmux config file, and ignore
1350 1.1 christos * braces which would be considered quoted, escaped, or in a comment.
1351 1.1 christos *
1352 1.1 christos * We update the token state after every character because '#' begins a
1353 1.1 christos * comment only when it begins a token. For simplicity, we treat an
1354 1.1 christos * unquoted directive format as comment.
1355 1.1 christos *
1356 1.1 christos * The result is verbatim copy of the input excluding the final brace.
1357 1.1 christos */
1358 1.1 christos
1359 1.1 christos for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) {
1360 1.1 christos yylex_append1(buf, len, ch);
1361 1.1 christos if (ch == '\n')
1362 1.1 christos lines++;
1363 1.1 christos
1364 1.1 christos /*
1365 1.1 christos * If the previous character was a backslash (escape is set),
1366 1.1 christos * escape anything if unquoted or in double quotes, otherwise
1367 1.1 christos * escape only '\n' and '\\'.
1368 1.1 christos */
1369 1.1 christos if (escape &&
1370 1.1 christos (quote == '\0' ||
1371 1.1 christos quote == '"' ||
1372 1.1 christos ch == '\n' ||
1373 1.1 christos ch == '\\')) {
1374 1.1 christos escape = 0;
1375 1.1 christos if (ch != '\n')
1376 1.1 christos token = 1;
1377 1.1 christos continue;
1378 1.1 christos }
1379 1.1 christos
1380 1.1 christos /*
1381 1.1 christos * The character is not escaped. If it is a backslash, set the
1382 1.1 christos * escape flag.
1383 1.1 christos */
1384 1.1 christos if (ch == '\\') {
1385 1.1 christos escape = 1;
1386 1.1 christos continue;
1387 1.1 christos }
1388 1.1 christos escape = 0;
1389 1.1 christos
1390 1.1 christos /* A newline always resets to unquoted. */
1391 1.1 christos if (ch == '\n') {
1392 1.1 christos quote = token = 0;
1393 1.1 christos continue;
1394 1.1 christos }
1395 1.1 christos
1396 1.1 christos if (quote) {
1397 1.1 christos /*
1398 1.1 christos * Inside quotes or comment. Check if this is the
1399 1.1 christos * closing quote.
1400 1.1 christos */
1401 1.1 christos if (ch == quote && quote != '#')
1402 1.1 christos quote = 0;
1403 1.1 christos token = 1; /* token continues regardless */
1404 1.1 christos } else {
1405 1.1 christos /* Not inside quotes or comment. */
1406 1.1 christos switch (ch) {
1407 1.1 christos case '"':
1408 1.1 christos case '\'':
1409 1.1 christos case '#':
1410 1.1 christos /* Beginning of quote or maybe comment. */
1411 1.1 christos if (ch != '#' || !token)
1412 1.1 christos quote = ch;
1413 1.1 christos token = 1;
1414 1.1 christos break;
1415 1.1 christos case ' ':
1416 1.1 christos case '\t':
1417 1.1 christos case ';':
1418 1.1 christos /* Delimiter - token resets. */
1419 1.1 christos token = 0;
1420 1.1 christos break;
1421 1.1 christos case '{':
1422 1.1 christos nesting++;
1423 1.1 christos token = 0; /* new commands set - token resets */
1424 1.1 christos break;
1425 1.1 christos case '}':
1426 1.1 christos nesting--;
1427 1.1 christos token = 1; /* same as after quotes */
1428 1.1 christos if (nesting == 0) {
1429 1.1 christos (*len)--; /* remove closing } */
1430 1.1 christos ps->input->line += lines;
1431 1.1 christos return (1);
1432 1.1 christos }
1433 1.1 christos break;
1434 1.1 christos default:
1435 1.1 christos token = 1;
1436 1.1 christos break;
1437 1.1 christos }
1438 1.1 christos }
1439 1.1 christos }
1440 1.1 christos
1441 1.1 christos /*
1442 1.1 christos * Update line count after error as reporting the opening line is more
1443 1.1 christos * useful than EOF.
1444 1.1 christos */
1445 1.1 christos yyerror("unterminated brace string");
1446 1.1 christos ps->input->line += lines;
1447 1.1 christos return (0);
1448 1.1 christos }
1449 1.1 christos
1450 1.1 christos static char *
1451 1.1 christos yylex_token(int ch)
1452 1.1 christos {
1453 1.1 christos char *buf;
1454 1.1 christos size_t len;
1455 1.1 christos enum { START,
1456 1.1 christos NONE,
1457 1.1 christos DOUBLE_QUOTES,
1458 1.1 christos SINGLE_QUOTES } state = NONE, last = START;
1459 1.1 christos
1460 1.1 christos len = 0;
1461 1.1 christos buf = xmalloc(1);
1462 1.1 christos
1463 1.1 christos for (;;) {
1464 1.1 christos /*
1465 1.1 christos * EOF or \n are always the end of the token. If inside quotes
1466 1.1 christos * they are an error.
1467 1.1 christos */
1468 1.1 christos if (ch == EOF || ch == '\n') {
1469 1.1 christos if (state != NONE)
1470 1.1 christos goto error;
1471 1.1 christos break;
1472 1.1 christos }
1473 1.1 christos
1474 1.1 christos /* Whitespace or ; ends a token unless inside quotes. */
1475 1.1 christos if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE)
1476 1.1 christos break;
1477 1.1 christos
1478 1.1 christos /*
1479 1.1 christos * \ ~ and $ are expanded except in single quotes.
1480 1.1 christos */
1481 1.1 christos if (ch == '\\' && state != SINGLE_QUOTES) {
1482 1.1 christos if (!yylex_token_escape(&buf, &len))
1483 1.1 christos goto error;
1484 1.1 christos goto skip;
1485 1.1 christos }
1486 1.1 christos if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1487 1.1 christos if (!yylex_token_tilde(&buf, &len))
1488 1.1 christos goto error;
1489 1.1 christos goto skip;
1490 1.1 christos }
1491 1.1 christos if (ch == '$' && state != SINGLE_QUOTES) {
1492 1.1 christos if (!yylex_token_variable(&buf, &len))
1493 1.1 christos goto error;
1494 1.1 christos goto skip;
1495 1.1 christos }
1496 1.1 christos if (ch == '{' && state == NONE) {
1497 1.1 christos if (!yylex_token_brace(&buf, &len))
1498 1.1 christos goto error;
1499 1.1 christos goto skip;
1500 1.1 christos }
1501 1.1 christos if (ch == '}' && state == NONE)
1502 1.1 christos goto error; /* unmatched (matched ones were handled) */
1503 1.1 christos
1504 1.1 christos /*
1505 1.1 christos * ' and " starts or end quotes (and is consumed).
1506 1.1 christos */
1507 1.1 christos if (ch == '\'') {
1508 1.1 christos if (state == NONE) {
1509 1.1 christos state = SINGLE_QUOTES;
1510 1.1 christos goto next;
1511 1.1 christos }
1512 1.1 christos if (state == SINGLE_QUOTES) {
1513 1.1 christos state = NONE;
1514 1.1 christos goto next;
1515 1.1 christos }
1516 1.1 christos }
1517 1.1 christos if (ch == '"') {
1518 1.1 christos if (state == NONE) {
1519 1.1 christos state = DOUBLE_QUOTES;
1520 1.1 christos goto next;
1521 1.1 christos }
1522 1.1 christos if (state == DOUBLE_QUOTES) {
1523 1.1 christos state = NONE;
1524 1.1 christos goto next;
1525 1.1 christos }
1526 1.1 christos }
1527 1.1 christos
1528 1.1 christos /*
1529 1.1 christos * Otherwise add the character to the buffer.
1530 1.1 christos */
1531 1.1 christos yylex_append1(&buf, &len, ch);
1532 1.1 christos
1533 1.1 christos skip:
1534 1.1 christos last = state;
1535 1.1 christos
1536 1.1 christos next:
1537 1.1 christos ch = yylex_getc();
1538 1.1 christos }
1539 1.1 christos yylex_ungetc(ch);
1540 1.1 christos
1541 1.1 christos buf[len] = '\0';
1542 1.1 christos log_debug("%s: %s", __func__, buf);
1543 1.1 christos return (buf);
1544 1.1 christos
1545 1.1 christos error:
1546 1.1 christos free(buf);
1547 1.1 christos return (NULL);
1548 1.1 christos }
1549