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