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