Annotation of src/usr.bin/tmux/cmd-parse.y, Revision 1.22
1.22 ! nicm 1: /* $OpenBSD: cmd-parse.y,v 1.21 2019/12/12 15:01:54 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.1 nicm 344: if (ps->scope == NULL || ps->scope->flag)
1.13 nicm 345: TAILQ_INSERT_TAIL($$, $1, entry);
1.1 nicm 346: else
347: cmd_parse_free_command($1);
348: }
349: | commands ';'
350: {
1.13 nicm 351: $$ = $1;
1.1 nicm 352: }
353: | commands ';' condition1
354: {
1.13 nicm 355: $$ = $1;
356: TAILQ_CONCAT($$, $3, entry);
357: free($3);
1.1 nicm 358: }
359: | commands ';' command
360: {
361: struct cmd_parse_state *ps = &parse_state;
362:
363: if (ps->scope == NULL || ps->scope->flag) {
1.13 nicm 364: $$ = $1;
365: TAILQ_INSERT_TAIL($$, $3, entry);
1.1 nicm 366: } else {
1.13 nicm 367: $$ = cmd_parse_new_commands();
368: cmd_parse_free_commands($1);
1.1 nicm 369: cmd_parse_free_command($3);
370: }
371: }
372: | condition1
373: {
1.13 nicm 374: $$ = $1;
1.1 nicm 375: }
376:
1.22 ! nicm 377: command : assignment
! 378: {
! 379: struct cmd_parse_state *ps = &parse_state;
! 380:
! 381: $$ = xcalloc(1, sizeof *$$);
! 382: $$->name = NULL;
! 383: $$->line = ps->input->line;
! 384: }
! 385: | optional_assignment TOKEN
1.1 nicm 386: {
387: struct cmd_parse_state *ps = &parse_state;
388:
389: $$ = xcalloc(1, sizeof *$$);
390: $$->name = $2;
1.10 nicm 391: $$->line = ps->input->line;
1.1 nicm 392:
393: }
1.22 ! nicm 394: | optional_assignment TOKEN arguments
1.1 nicm 395: {
396: struct cmd_parse_state *ps = &parse_state;
397:
398: $$ = xcalloc(1, sizeof *$$);
399: $$->name = $2;
1.10 nicm 400: $$->line = ps->input->line;
1.1 nicm 401:
402: $$->argc = $3.argc;
403: $$->argv = $3.argv;
404: }
405:
406: condition1 : if_open commands if_close
407: {
408: if ($1)
1.13 nicm 409: $$ = $2;
410: else {
411: $$ = cmd_parse_new_commands();
412: cmd_parse_free_commands($2);
413: }
1.1 nicm 414: }
415: | if_open commands if_else commands if_close
416: {
417: if ($1) {
1.13 nicm 418: $$ = $2;
419: cmd_parse_free_commands($4);
1.1 nicm 420: } else {
1.13 nicm 421: $$ = $4;
422: cmd_parse_free_commands($2);
1.1 nicm 423: }
424: }
425: | if_open commands elif1 if_close
426: {
427: if ($1) {
1.13 nicm 428: $$ = $2;
429: cmd_parse_free_commands($3.commands);
1.1 nicm 430: } else if ($3.flag) {
1.13 nicm 431: $$ = $3.commands;
432: cmd_parse_free_commands($2);
1.1 nicm 433: } else {
1.13 nicm 434: $$ = cmd_parse_new_commands();
435: cmd_parse_free_commands($2);
436: cmd_parse_free_commands($3.commands);
1.1 nicm 437: }
438: }
439: | if_open commands elif1 if_else commands if_close
440: {
441: if ($1) {
1.13 nicm 442: $$ = $2;
443: cmd_parse_free_commands($3.commands);
444: cmd_parse_free_commands($5);
1.1 nicm 445: } else if ($3.flag) {
1.13 nicm 446: $$ = $3.commands;
447: cmd_parse_free_commands($2);
448: cmd_parse_free_commands($5);
449: } else {
450: $$ = $5;
451: cmd_parse_free_commands($2);
452: cmd_parse_free_commands($3.commands);
1.1 nicm 453: }
454: }
455:
456: elif1 : if_elif commands
457: {
1.13 nicm 458: if ($1) {
459: $$.flag = 1;
460: $$.commands = $2;
461: } else {
462: $$.flag = 0;
463: $$.commands = cmd_parse_new_commands();
464: cmd_parse_free_commands($2);
465: }
1.1 nicm 466: }
467: | if_elif commands elif1
468: {
469: if ($1) {
470: $$.flag = 1;
1.13 nicm 471: $$.commands = $2;
472: cmd_parse_free_commands($3.commands);
473: } else if ($3.flag) {
474: $$.flag = 1;
475: $$.commands = $3.commands;
476: cmd_parse_free_commands($2);
1.1 nicm 477: } else {
1.13 nicm 478: $$.flag = 0;
479: $$.commands = cmd_parse_new_commands();
480: cmd_parse_free_commands($2);
481: cmd_parse_free_commands($3.commands);
1.1 nicm 482: }
483: }
484:
485: arguments : argument
486: {
487: $$.argc = 1;
488: $$.argv = xreallocarray(NULL, 1, sizeof *$$.argv);
489:
490: $$.argv[0] = $1;
491: }
492: | argument arguments
493: {
494: cmd_prepend_argv(&$2.argc, &$2.argv, $1);
495: free($1);
496: $$ = $2;
497: }
498:
499: argument : TOKEN
500: {
501: $$ = $1;
502: }
503: | EQUALS
504: {
505: $$ = $1;
506: }
507:
508: %%
509:
510: static char *
511: cmd_parse_get_error(const char *file, u_int line, const char *error)
512: {
513: char *s;
514:
515: if (file == NULL)
516: s = xstrdup(error);
517: else
518: xasprintf (&s, "%s:%u: %s", file, line, error);
519: return (s);
520: }
521:
522: static void
1.14 nicm 523: cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
524: struct cmd_list *cmdlist)
525: {
526: char *s;
527:
528: if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
529: s = cmd_list_print(cmdlist, 0);
1.16 nicm 530: if (pi->file != NULL)
531: cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
532: else
533: cmdq_print(pi->item, "%u: %s", line, s);
1.14 nicm 534: free(s);
535: }
536: }
537:
538: static void
1.1 nicm 539: cmd_parse_free_command(struct cmd_parse_command *cmd)
540: {
541: free(cmd->name);
542: cmd_free_argv(cmd->argc, cmd->argv);
543: free(cmd);
544: }
545:
1.13 nicm 546: static struct cmd_parse_commands *
547: cmd_parse_new_commands(void)
548: {
549: struct cmd_parse_commands *cmds;
550:
551: cmds = xmalloc(sizeof *cmds);
552: TAILQ_INIT (cmds);
553: return (cmds);
554: }
555:
1.1 nicm 556: static void
557: cmd_parse_free_commands(struct cmd_parse_commands *cmds)
558: {
559: struct cmd_parse_command *cmd, *cmd1;
560:
561: TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
562: TAILQ_REMOVE(cmds, cmd, entry);
563: cmd_parse_free_command(cmd);
564: }
1.13 nicm 565: free(cmds);
1.1 nicm 566: }
567:
568: static struct cmd_parse_commands *
1.5 nicm 569: cmd_parse_run_parser(char **cause)
1.1 nicm 570: {
1.13 nicm 571: struct cmd_parse_state *ps = &parse_state;
572: struct cmd_parse_scope *scope, *scope1;
573: int retval;
1.1 nicm 574:
1.13 nicm 575: ps->commands = NULL;
1.1 nicm 576: TAILQ_INIT(&ps->stack);
577:
578: retval = yyparse();
579: TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
580: TAILQ_REMOVE(&ps->stack, scope, entry);
581: free(scope);
582: }
583: if (retval != 0) {
584: *cause = ps->error;
585: return (NULL);
586: }
587:
1.13 nicm 588: if (ps->commands == NULL)
589: return (cmd_parse_new_commands());
590: return (ps->commands);
1.1 nicm 591: }
592:
1.5 nicm 593: static struct cmd_parse_commands *
594: cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
595: {
596: struct cmd_parse_state *ps = &parse_state;
597:
598: memset(ps, 0, sizeof *ps);
599: ps->input = pi;
600: ps->f = f;
601: return (cmd_parse_run_parser(cause));
602: }
603:
604: static struct cmd_parse_commands *
605: cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
606: char **cause)
607: {
608: struct cmd_parse_state *ps = &parse_state;
609:
610: memset(ps, 0, sizeof *ps);
611: ps->input = pi;
612: ps->buf = buf;
613: ps->len = len;
614: return (cmd_parse_run_parser(cause));
615: }
616:
1.4 nicm 617: static struct cmd_parse_result *
618: cmd_parse_build_commands(struct cmd_parse_commands *cmds,
619: struct cmd_parse_input *pi)
1.1 nicm 620: {
621: static struct cmd_parse_result pr;
1.4 nicm 622: struct cmd_parse_commands *cmds2;
1.1 nicm 623: struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after;
624: u_int line = UINT_MAX;
625: int i;
626: struct cmd_list *cmdlist = NULL, *result;
627: struct cmd *add;
628: char *alias, *cause, *s;
629:
1.4 nicm 630: /* Check for an empty list. */
1.1 nicm 631: if (TAILQ_EMPTY(cmds)) {
1.13 nicm 632: cmd_parse_free_commands(cmds);
1.1 nicm 633: pr.status = CMD_PARSE_EMPTY;
634: return (&pr);
635: }
636:
637: /*
638: * Walk the commands and expand any aliases. Each alias is parsed
639: * individually to a new command list, any trailing arguments appended
640: * to the last command, and all commands inserted into the original
641: * command list.
642: */
643: TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
1.22 ! nicm 644: if (cmd->name == NULL)
! 645: continue;
1.1 nicm 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) {
1.22 ! nicm 691: if (cmd->name == NULL)
! 692: continue;
1.1 nicm 693: log_debug("%s: %u %s", __func__, cmd->line, cmd->name);
694: cmd_log_argv(cmd->argc, cmd->argv, __func__);
695:
696: if (cmdlist == NULL || cmd->line != line) {
697: if (cmdlist != NULL) {
1.14 nicm 698: cmd_parse_print_commands(pi, line, cmdlist);
1.1 nicm 699: cmd_list_move(result, cmdlist);
700: cmd_list_free(cmdlist);
701: }
702: cmdlist = cmd_list_new();
703: }
704: line = cmd->line;
705:
706: cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name);
707: add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
708: if (add == NULL) {
709: cmd_list_free(result);
710: pr.status = CMD_PARSE_ERROR;
711: pr.error = cmd_parse_get_error(pi->file, line, cause);
712: free(cause);
1.20 nicm 713: cmd_list_free(cmdlist);
1.1 nicm 714: goto out;
715: }
716: cmd_list_append(cmdlist, add);
717: }
718: if (cmdlist != NULL) {
1.14 nicm 719: cmd_parse_print_commands(pi, line, cmdlist);
1.1 nicm 720: cmd_list_move(result, cmdlist);
721: cmd_list_free(cmdlist);
722: }
723:
1.2 nicm 724: s = cmd_list_print(result, 0);
1.1 nicm 725: log_debug("%s: %s", __func__, s);
726: free(s);
727:
728: pr.status = CMD_PARSE_SUCCESS;
729: pr.cmdlist = result;
730:
731: out:
732: cmd_parse_free_commands(cmds);
733:
734: return (&pr);
735: }
736:
737: struct cmd_parse_result *
1.4 nicm 738: cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
739: {
740: static struct cmd_parse_result pr;
741: struct cmd_parse_input input;
742: struct cmd_parse_commands *cmds;
743: char *cause;
744:
745: if (pi == NULL) {
746: memset(&input, 0, sizeof input);
747: pi = &input;
748: }
749: memset(&pr, 0, sizeof pr);
750:
1.5 nicm 751: cmds = cmd_parse_do_file(f, pi, &cause);
1.4 nicm 752: if (cmds == NULL) {
753: pr.status = CMD_PARSE_ERROR;
754: pr.error = cause;
755: return (&pr);
756: }
757: return (cmd_parse_build_commands(cmds, pi));
758: }
759:
760: struct cmd_parse_result *
1.1 nicm 761: cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
762: {
1.21 nicm 763: return (cmd_parse_from_buffer(s, strlen(s), pi));
764: }
765:
766: struct cmd_parse_result *
767: cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
768: {
1.1 nicm 769: static struct cmd_parse_result pr;
1.4 nicm 770: struct cmd_parse_input input;
1.5 nicm 771: struct cmd_parse_commands *cmds;
772: char *cause;
1.1 nicm 773:
1.4 nicm 774: if (pi == NULL) {
775: memset(&input, 0, sizeof input);
776: pi = &input;
777: }
778: memset(&pr, 0, sizeof pr);
779:
1.21 nicm 780: if (len == 0) {
1.1 nicm 781: pr.status = CMD_PARSE_EMPTY;
782: pr.cmdlist = NULL;
783: pr.error = NULL;
784: return (&pr);
785: }
786:
1.21 nicm 787: cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
1.5 nicm 788: if (cmds == NULL) {
1.1 nicm 789: pr.status = CMD_PARSE_ERROR;
1.5 nicm 790: pr.error = cause;
791: return (&pr);
1.1 nicm 792: }
1.5 nicm 793: return (cmd_parse_build_commands(cmds, pi));
1.4 nicm 794: }
795:
796: struct cmd_parse_result *
797: cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
798: {
799: struct cmd_parse_input input;
800: struct cmd_parse_commands *cmds;
801: struct cmd_parse_command *cmd;
802: char **copy, **new_argv;
803: size_t size;
804: int i, last, new_argc;
805:
806: /*
807: * The commands are already split up into arguments, so just separate
808: * into a set of commands by ';'.
809: */
810:
811: if (pi == NULL) {
812: memset(&input, 0, sizeof input);
813: pi = &input;
814: }
815: cmd_log_argv(argc, argv, "%s", __func__);
816:
1.13 nicm 817: cmds = cmd_parse_new_commands();
1.4 nicm 818: copy = cmd_copy_argv(argc, argv);
819:
820: last = 0;
821: for (i = 0; i < argc; i++) {
822: size = strlen(copy[i]);
823: if (size == 0 || copy[i][size - 1] != ';')
824: continue;
825: copy[i][--size] = '\0';
826: if (size > 0 && copy[i][size - 1] == '\\') {
827: copy[i][size - 1] = ';';
828: continue;
829: }
830:
831: new_argc = i - last;
832: new_argv = copy + last;
833: if (size != 0)
834: new_argc++;
835:
836: if (new_argc != 0) {
837: cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
838: i);
839:
840: cmd = xcalloc(1, sizeof *cmd);
841: cmd->name = xstrdup(new_argv[0]);
842: cmd->line = pi->line;
843:
844: cmd->argc = new_argc - 1;
845: cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
846:
847: TAILQ_INSERT_TAIL(cmds, cmd, entry);
848: }
849:
850: last = i + 1;
851: }
852: if (last != argc) {
853: new_argv = copy + last;
854: new_argc = argc - last;
855:
856: if (new_argc != 0) {
857: cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
858: last);
859:
860: cmd = xcalloc(1, sizeof *cmd);
861: cmd->name = xstrdup(new_argv[0]);
862: cmd->line = pi->line;
863:
864: cmd->argc = new_argc - 1;
865: cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
866:
867: TAILQ_INSERT_TAIL(cmds, cmd, entry);
868: }
869: }
1.13 nicm 870:
871: cmd_free_argv(argc, copy);
1.4 nicm 872: return (cmd_parse_build_commands(cmds, pi));
1.1 nicm 873: }
874:
875: static int printflike(1, 2)
876: yyerror(const char *fmt, ...)
877: {
878: struct cmd_parse_state *ps = &parse_state;
879: struct cmd_parse_input *pi = ps->input;
880: va_list ap;
881: char *error;
882:
883: if (ps->error != NULL)
884: return (0);
885:
886: va_start(ap, fmt);
887: xvasprintf(&error, fmt, ap);
888: va_end(ap);
889:
890: ps->error = cmd_parse_get_error(pi->file, pi->line, error);
891: free(error);
892: return (0);
893: }
894:
895: static int
896: yylex_is_var(char ch, int first)
897: {
898: if (ch == '=')
899: return (0);
900: if (first && isdigit((u_char)ch))
901: return (0);
902: return (isalnum((u_char)ch) || ch == '_');
903: }
904:
905: static void
906: yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
907: {
908: if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
909: fatalx("buffer is too big");
910: *buf = xrealloc(*buf, (*len) + 1 + addlen);
911: memcpy((*buf) + *len, add, addlen);
912: (*len) += addlen;
913: }
914:
915: static void
916: yylex_append1(char **buf, size_t *len, char add)
917: {
918: yylex_append(buf, len, &add, 1);
919: }
920:
921: static int
1.5 nicm 922: yylex_getc1(void)
923: {
924: struct cmd_parse_state *ps = &parse_state;
925: int ch;
926:
927: if (ps->f != NULL)
928: ch = getc(ps->f);
929: else {
930: if (ps->off == ps->len)
931: ch = EOF;
932: else
933: ch = ps->buf[ps->off++];
934: }
935: return (ch);
936: }
937:
938: static void
939: yylex_ungetc(int ch)
940: {
941: struct cmd_parse_state *ps = &parse_state;
942:
943: if (ps->f != NULL)
944: ungetc(ch, ps->f);
945: else if (ps->off > 0 && ch != EOF)
946: ps->off--;
947: }
948:
949: static int
1.1 nicm 950: yylex_getc(void)
951: {
952: struct cmd_parse_state *ps = &parse_state;
953: int ch;
954:
955: if (ps->escapes != 0) {
956: ps->escapes--;
957: return ('\\');
958: }
959: for (;;) {
1.5 nicm 960: ch = yylex_getc1();
1.1 nicm 961: if (ch == '\\') {
962: ps->escapes++;
963: continue;
964: }
965: if (ch == '\n' && (ps->escapes % 2) == 1) {
966: ps->input->line++;
967: ps->escapes--;
968: continue;
969: }
970:
971: if (ps->escapes != 0) {
1.5 nicm 972: yylex_ungetc(ch);
1.1 nicm 973: ps->escapes--;
974: return ('\\');
975: }
976: return (ch);
977: }
978: }
979:
980: static char *
981: yylex_get_word(int ch)
982: {
1.5 nicm 983: char *buf;
984: size_t len;
1.1 nicm 985:
986: len = 0;
987: buf = xmalloc(1);
988:
989: do
990: yylex_append1(&buf, &len, ch);
991: while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
1.5 nicm 992: yylex_ungetc(ch);
1.1 nicm 993:
994: buf[len] = '\0';
995: log_debug("%s: %s", __func__, buf);
996: return (buf);
997: }
998:
999: static int
1000: yylex(void)
1001: {
1002: struct cmd_parse_state *ps = &parse_state;
1003: char *token, *cp;
1.15 nicm 1004: int ch, next, condition;
1.1 nicm 1005:
1.8 nicm 1006: if (ps->eol)
1007: ps->input->line++;
1008: ps->eol = 0;
1009:
1.15 nicm 1010: condition = ps->condition;
1011: ps->condition = 0;
1012:
1.1 nicm 1013: for (;;) {
1014: ch = yylex_getc();
1015:
1016: if (ch == EOF) {
1017: /*
1018: * Ensure every file or string is terminated by a
1019: * newline. This keeps the parser simpler and avoids
1020: * having to add a newline to each string.
1021: */
1022: if (ps->eof)
1023: break;
1024: ps->eof = 1;
1025: return ('\n');
1026: }
1027:
1028: if (ch == ' ' || ch == '\t') {
1029: /*
1030: * Ignore whitespace.
1031: */
1032: continue;
1033: }
1034:
1035: if (ch == '\n') {
1036: /*
1037: * End of line. Update the line number.
1038: */
1.8 nicm 1039: ps->eol = 1;
1.1 nicm 1040: return ('\n');
1041: }
1042:
1043: if (ch == ';') {
1044: /*
1045: * A semicolon is itself.
1046: */
1047: return (';');
1048: }
1049:
1050: if (ch == '#') {
1051: /*
1.15 nicm 1052: * #{ after a condition opens a format; anything else
1053: * is a comment, ignore up to the end of the line.
1.1 nicm 1054: */
1055: next = yylex_getc();
1.15 nicm 1056: if (condition && next == '{') {
1.1 nicm 1057: yylval.token = yylex_format();
1058: if (yylval.token == NULL)
1059: return (ERROR);
1060: return (FORMAT);
1061: }
1062: while (next != '\n' && next != EOF)
1063: next = yylex_getc();
1064: if (next == '\n') {
1065: ps->input->line++;
1066: return ('\n');
1067: }
1068: continue;
1069: }
1070:
1071: if (ch == '%') {
1072: /*
1.11 nicm 1073: * % is a condition unless it is all % or all numbers,
1074: * then it is a token.
1.1 nicm 1075: */
1076: yylval.token = yylex_get_word('%');
1.11 nicm 1077: for (cp = yylval.token; *cp != '\0'; cp++) {
1078: if (*cp != '%' && !isdigit((u_char)*cp))
1079: break;
1080: }
1081: if (*cp == '\0')
1.1 nicm 1082: return (TOKEN);
1.15 nicm 1083: ps->condition = 1;
1.1 nicm 1084: if (strcmp(yylval.token, "%if") == 0) {
1085: free(yylval.token);
1086: return (IF);
1087: }
1088: if (strcmp(yylval.token, "%else") == 0) {
1089: free(yylval.token);
1090: return (ELSE);
1091: }
1092: if (strcmp(yylval.token, "%elif") == 0) {
1093: free(yylval.token);
1094: return (ELIF);
1095: }
1096: if (strcmp(yylval.token, "%endif") == 0) {
1097: free(yylval.token);
1098: return (ENDIF);
1099: }
1100: free(yylval.token);
1101: return (ERROR);
1102: }
1103:
1104: /*
1105: * Otherwise this is a token.
1106: */
1107: token = yylex_token(ch);
1108: if (token == NULL)
1109: return (ERROR);
1110: yylval.token = token;
1111:
1112: if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1113: for (cp = token + 1; *cp != '='; cp++) {
1114: if (!yylex_is_var(*cp, 0))
1115: break;
1116: }
1117: if (*cp == '=')
1118: return (EQUALS);
1119: }
1120: return (TOKEN);
1121: }
1122: return (0);
1123: }
1124:
1125: static char *
1126: yylex_format(void)
1127: {
1128: char *buf;
1129: size_t len;
1130: int ch, brackets = 1;
1131:
1132: len = 0;
1133: buf = xmalloc(1);
1134:
1135: yylex_append(&buf, &len, "#{", 2);
1136: for (;;) {
1137: if ((ch = yylex_getc()) == EOF || ch == '\n')
1138: goto error;
1139: if (ch == '#') {
1140: if ((ch = yylex_getc()) == EOF || ch == '\n')
1141: goto error;
1142: if (ch == '{')
1143: brackets++;
1144: yylex_append1(&buf, &len, '#');
1145: } else if (ch == '}') {
1146: if (brackets != 0 && --brackets == 0) {
1147: yylex_append1(&buf, &len, ch);
1148: break;
1149: }
1150: }
1151: yylex_append1(&buf, &len, ch);
1152: }
1153: if (brackets != 0)
1154: goto error;
1155:
1156: buf[len] = '\0';
1157: log_debug("%s: %s", __func__, buf);
1158: return (buf);
1159:
1160: error:
1161: free(buf);
1162: return (NULL);
1163: }
1164:
1165: static int
1166: yylex_token_escape(char **buf, size_t *len)
1167: {
1.7 nicm 1168: int ch, type, o2, o3;
1.1 nicm 1169: u_int size, i, tmp;
1170: char s[9];
1171: struct utf8_data ud;
1172:
1.7 nicm 1173: ch = yylex_getc();
1174:
1175: if (ch >= '4' && ch <= '7') {
1176: yyerror("invalid octal escape");
1177: return (0);
1178: }
1179: if (ch >= '0' && ch <= '3') {
1180: o2 = yylex_getc();
1181: if (o2 >= '0' && o2 <= '7') {
1182: o3 = yylex_getc();
1183: if (o3 >= '0' && o3 <= '7') {
1184: ch = 64 * (ch - '0') +
1185: 8 * (o2 - '0') +
1186: (o3 - '0');
1187: yylex_append1(buf, len, ch);
1188: return (1);
1189: }
1190: }
1191: yyerror("invalid octal escape");
1192: return (0);
1193: }
1194:
1195: switch (ch) {
1.1 nicm 1196: case EOF:
1197: return (0);
1.9 nicm 1198: case 'a':
1199: ch = '\a';
1200: break;
1201: case 'b':
1202: ch = '\b';
1203: break;
1.1 nicm 1204: case 'e':
1205: ch = '\033';
1.9 nicm 1206: break;
1207: case 'f':
1208: ch = '\f';
1209: break;
1210: case 's':
1211: ch = ' ';
1212: break;
1213: case 'v':
1214: ch = '\v';
1.1 nicm 1215: break;
1216: case 'r':
1217: ch = '\r';
1218: break;
1219: case 'n':
1220: ch = '\n';
1221: break;
1222: case 't':
1223: ch = '\t';
1224: break;
1225: case 'u':
1226: type = 'u';
1227: size = 4;
1228: goto unicode;
1229: case 'U':
1230: type = 'U';
1231: size = 8;
1232: goto unicode;
1233: }
1234:
1235: yylex_append1(buf, len, ch);
1236: return (1);
1237:
1238: unicode:
1239: for (i = 0; i < size; i++) {
1240: ch = yylex_getc();
1241: if (ch == EOF || ch == '\n')
1242: return (0);
1243: if (!isxdigit((u_char)ch)) {
1244: yyerror("invalid \\%c argument", type);
1245: return (0);
1246: }
1247: s[i] = ch;
1248: }
1249: s[i] = '\0';
1250:
1251: if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1252: (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1253: yyerror("invalid \\%c argument", type);
1254: return (0);
1255: }
1256: if (utf8_split(tmp, &ud) != UTF8_DONE) {
1257: yyerror("invalid \\%c argument", type);
1258: return (0);
1259: }
1260: yylex_append(buf, len, ud.data, ud.size);
1261: return (1);
1262: }
1263:
1264: static int
1265: yylex_token_variable(char **buf, size_t *len)
1266: {
1267: struct environ_entry *envent;
1268: int ch, brackets = 0;
1.19 nicm 1269: char name[1024];
1.1 nicm 1270: size_t namelen = 0;
1271: const char *value;
1272:
1273: ch = yylex_getc();
1274: if (ch == EOF)
1275: return (0);
1276: if (ch == '{')
1277: brackets = 1;
1278: else {
1279: if (!yylex_is_var(ch, 1)) {
1280: yylex_append1(buf, len, '$');
1.5 nicm 1281: yylex_ungetc(ch);
1.1 nicm 1282: return (1);
1283: }
1284: name[namelen++] = ch;
1285: }
1286:
1287: for (;;) {
1288: ch = yylex_getc();
1289: if (brackets && ch == '}')
1290: break;
1291: if (ch == EOF || !yylex_is_var(ch, 0)) {
1292: if (!brackets) {
1.5 nicm 1293: yylex_ungetc(ch);
1.1 nicm 1294: break;
1295: }
1296: yyerror("invalid environment variable");
1297: return (0);
1298: }
1299: if (namelen == (sizeof name) - 2) {
1300: yyerror("environment variable is too long");
1301: return (0);
1302: }
1303: name[namelen++] = ch;
1304: }
1305: name[namelen] = '\0';
1306:
1307: envent = environ_find(global_environ, name);
1308: if (envent != NULL) {
1309: value = envent->value;
1310: log_debug("%s: %s -> %s", __func__, name, value);
1311: yylex_append(buf, len, value, strlen(value));
1312: }
1313: return (1);
1314: }
1315:
1316: static int
1317: yylex_token_tilde(char **buf, size_t *len)
1318: {
1319: struct environ_entry *envent;
1320: int ch;
1.19 nicm 1321: char name[1024];
1.1 nicm 1322: size_t namelen = 0;
1323: struct passwd *pw;
1324: const char *home = NULL;
1325:
1326: for (;;) {
1327: ch = yylex_getc();
1328: if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1.5 nicm 1329: yylex_ungetc(ch);
1.1 nicm 1330: break;
1331: }
1332: if (namelen == (sizeof name) - 2) {
1333: yyerror("user name is too long");
1334: return (0);
1335: }
1336: name[namelen++] = ch;
1337: }
1338: name[namelen] = '\0';
1339:
1340: if (*name == '\0') {
1341: envent = environ_find(global_environ, "HOME");
1342: if (envent != NULL && *envent->value != '\0')
1343: home = envent->value;
1344: else if ((pw = getpwuid(getuid())) != NULL)
1345: home = pw->pw_dir;
1346: } else {
1347: if ((pw = getpwnam(name)) != NULL)
1348: home = pw->pw_dir;
1349: }
1350: if (home == NULL)
1351: return (0);
1352:
1353: log_debug("%s: ~%s -> %s", __func__, name, home);
1354: yylex_append(buf, len, home, strlen(home));
1355: return (1);
1356: }
1357:
1.6 nicm 1358: static int
1359: yylex_token_brace(char **buf, size_t *len)
1360: {
1361: struct cmd_parse_state *ps = &parse_state;
1.17 nicm 1362: int ch, lines = 0, nesting = 1, escape = 0;
1363: int quote = '\0', token = 0;
1.6 nicm 1364:
1365: /*
1366: * Extract a string up to the matching unquoted '}', including newlines
1367: * and handling nested braces.
1368: *
1369: * To detect the final and intermediate braces which affect the nesting
1370: * depth, we scan the input as if it was a tmux config file, and ignore
1371: * braces which would be considered quoted, escaped, or in a comment.
1372: *
1.17 nicm 1373: * We update the token state after every character because '#' begins a
1374: * comment only when it begins a token. For simplicity, we treat an
1375: * unquoted directive format as comment.
1376: *
1.6 nicm 1377: * The result is verbatim copy of the input excluding the final brace.
1378: */
1379:
1380: for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) {
1381: yylex_append1(buf, len, ch);
1382: if (ch == '\n')
1383: lines++;
1384:
1385: /*
1386: * If the previous character was a backslash (escape is set),
1387: * escape anything if unquoted or in double quotes, otherwise
1388: * escape only '\n' and '\\'.
1389: */
1390: if (escape &&
1391: (quote == '\0' ||
1392: quote == '"' ||
1393: ch == '\n' ||
1394: ch == '\\')) {
1395: escape = 0;
1.17 nicm 1396: if (ch != '\n')
1397: token = 1;
1.6 nicm 1398: continue;
1399: }
1400:
1401: /*
1402: * The character is not escaped. If it is a backslash, set the
1403: * escape flag.
1404: */
1405: if (ch == '\\') {
1406: escape = 1;
1407: continue;
1408: }
1409: escape = 0;
1410:
1411: /* A newline always resets to unquoted. */
1412: if (ch == '\n') {
1.17 nicm 1413: quote = token = 0;
1.6 nicm 1414: continue;
1415: }
1416:
1417: if (quote) {
1418: /*
1419: * Inside quotes or comment. Check if this is the
1420: * closing quote.
1421: */
1422: if (ch == quote && quote != '#')
1423: quote = 0;
1.17 nicm 1424: token = 1; /* token continues regardless */
1425: } else {
1.6 nicm 1426: /* Not inside quotes or comment. */
1427: switch (ch) {
1428: case '"':
1429: case '\'':
1430: case '#':
1.17 nicm 1431: /* Beginning of quote or maybe comment. */
1432: if (ch != '#' || !token)
1433: quote = ch;
1434: token = 1;
1435: break;
1436: case ' ':
1437: case '\t':
1438: case ';':
1439: /* Delimiter - token resets. */
1440: token = 0;
1.6 nicm 1441: break;
1442: case '{':
1443: nesting++;
1.17 nicm 1444: token = 0; /* new commands set - token resets */
1.6 nicm 1445: break;
1446: case '}':
1447: nesting--;
1.17 nicm 1448: token = 1; /* same as after quotes */
1.6 nicm 1449: if (nesting == 0) {
1450: (*len)--; /* remove closing } */
1451: ps->input->line += lines;
1452: return (1);
1453: }
1454: break;
1.17 nicm 1455: default:
1456: token = 1;
1457: break;
1.6 nicm 1458: }
1459: }
1460: }
1461:
1462: /*
1.17 nicm 1463: * Update line count after error as reporting the opening line is more
1464: * useful than EOF.
1.6 nicm 1465: */
1466: yyerror("unterminated brace string");
1467: ps->input->line += lines;
1468: return (0);
1469: }
1470:
1.1 nicm 1471: static char *
1472: yylex_token(int ch)
1473: {
1474: char *buf;
1475: size_t len;
1476: enum { START,
1477: NONE,
1478: DOUBLE_QUOTES,
1479: SINGLE_QUOTES } state = NONE, last = START;
1480:
1481: len = 0;
1482: buf = xmalloc(1);
1483:
1484: for (;;) {
1485: /*
1486: * EOF or \n are always the end of the token. If inside quotes
1487: * they are an error.
1488: */
1489: if (ch == EOF || ch == '\n') {
1490: if (state != NONE)
1491: goto error;
1492: break;
1493: }
1494:
1495: /* Whitespace or ; ends a token unless inside quotes. */
1496: if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE)
1497: break;
1498:
1499: /*
1500: * \ ~ and $ are expanded except in single quotes.
1501: */
1502: if (ch == '\\' && state != SINGLE_QUOTES) {
1503: if (!yylex_token_escape(&buf, &len))
1504: goto error;
1505: goto skip;
1506: }
1507: if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1508: if (!yylex_token_tilde(&buf, &len))
1509: goto error;
1510: goto skip;
1511: }
1512: if (ch == '$' && state != SINGLE_QUOTES) {
1513: if (!yylex_token_variable(&buf, &len))
1514: goto error;
1515: goto skip;
1516: }
1.6 nicm 1517: if (ch == '{' && state == NONE) {
1518: if (!yylex_token_brace(&buf, &len))
1519: goto error;
1520: goto skip;
1521: }
1522: if (ch == '}' && state == NONE)
1523: goto error; /* unmatched (matched ones were handled) */
1.1 nicm 1524:
1525: /*
1526: * ' and " starts or end quotes (and is consumed).
1527: */
1528: if (ch == '\'') {
1529: if (state == NONE) {
1530: state = SINGLE_QUOTES;
1531: goto next;
1532: }
1533: if (state == SINGLE_QUOTES) {
1534: state = NONE;
1535: goto next;
1536: }
1537: }
1538: if (ch == '"') {
1539: if (state == NONE) {
1540: state = DOUBLE_QUOTES;
1541: goto next;
1542: }
1543: if (state == DOUBLE_QUOTES) {
1544: state = NONE;
1545: goto next;
1546: }
1547: }
1548:
1549: /*
1550: * Otherwise add the character to the buffer.
1551: */
1552: yylex_append1(&buf, &len, ch);
1553:
1554: skip:
1555: last = state;
1556:
1557: next:
1558: ch = yylex_getc();
1559: }
1.5 nicm 1560: yylex_ungetc(ch);
1.1 nicm 1561:
1562: buf[len] = '\0';
1563: log_debug("%s: %s", __func__, buf);
1564: return (buf);
1565:
1566: error:
1567: free(buf);
1568: return (NULL);
1569: }