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