2 * command.y - yacc/bison parser for debugger commands.
6 * Copyright (C) 2004, 2010, 2011, 2014 the Free Software Foundation, Inc.
8 * This file is part of GAWK, the GNU implementation of the
9 * AWK Programming Language.
11 * GAWK is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * GAWK is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
35 static int yylex(void);
36 static void yyerror(const char *mesg, ...);
38 static int find_command(const char *token, size_t toklen);
40 static bool want_nodeval = false;
42 static int cmd_idx = -1; /* index of current command in cmd table */
43 static int repeat_idx = -1; /* index of last repeatable command in command table */
44 static CMDARG *arg_list = NULL; /* list of arguments */
45 static long errcount = 0;
46 static char *lexptr_begin = NULL;
47 static bool in_commands = false;
50 static bool in_eval = false;
51 static const char start_EVAL[] = "function @eval(){";
52 static const char end_EVAL[] = "}";
53 static CMDARG *append_statement(CMDARG *stmt_list, char *stmt);
54 static NODE *concat_args(CMDARG *a, int count);
56 #ifdef HAVE_LIBREADLINE
57 static char *next_word(char *p, int len, char **endp);
58 static void history_expand_line(char **line);
59 static char *command_generator(const char *text, int state);
60 static char *srcfile_generator(const char *text, int state);
61 static char *argument_generator(const char *text, int state);
62 static char *variable_generator(const char *text, int state);
63 extern char *option_generator(const char *text, int state);
64 static int this_cmd = D_illegal;
66 #define history_expand_line(p) /* nothing */
67 static int rl_inhibit_completion; /* dummy variable */
73 enum nametypeval value;
77 * These two should be static, but there are some compilers that
78 * don't like the static keyword with an empty size. Therefore give
79 * them names that are less likely to conflict with the rest of gawk.
81 #define argtab zz_debug_argtab
82 #define cmdtab zz_debug_cmdtab
84 extern struct argtoken argtab[];
85 extern struct cmdtoken cmdtab[];
87 static CMDARG *mk_cmdarg(enum argtype type);
88 static void append_cmdarg(CMDARG *arg);
89 static int find_argument(CMDARG *arg);
90 #define YYSTYPE CMDARG *
93 %token D_BACKTRACE D_BREAK D_CLEAR D_CONTINUE D_DELETE D_DISABLE D_DOWN
94 %token D_ENABLE D_FINISH D_FRAME D_HELP D_IGNORE D_INFO D_LIST
95 %token D_NEXT D_NEXTI D_PRINT D_PRINTF D_QUIT D_RETURN D_RUN D_SET
96 %token D_STEP D_STEPI D_TBREAK D_UP D_UNTIL
97 %token D_DISPLAY D_UNDISPLAY D_WATCH D_UNWATCH
99 %token D_INT D_STRING D_NODE D_VARIABLE
100 %token D_OPTION D_COMMANDS D_END D_SILENT D_SOURCE
101 %token D_SAVE D_EVAL D_CONDITION
111 want_nodeval = false;
112 if (lexptr_begin != NULL) {
113 if (input_from_tty && lexptr_begin[0] != '\0')
114 add_history(lexptr_begin);
118 if (arg_list != NULL) {
119 free_cmdarg(arg_list);
129 if (errcount == 0 && cmd_idx >= 0) {
131 bool terminate = false;
135 ctype = cmdtab[cmd_idx].type;
137 /* a blank line repeats previous command
138 * (list, next, nexti, step, stepi and continue without arguments).
139 * save the index in the command table; used in yylex
146 || ctype == D_continue)
151 repeat_idx = cmd_idx;
155 /* call the command handler; reset the globals arg_list, cmd_idx,
156 * since this handler could invoke yyparse again.
157 * call do_commands for the list of commands in `commands';
158 * arg_list isn't freed on return.
161 cmdfunc = cmdtab[cmd_idx].cf_ptr;
163 cmdfunc = do_commands;
165 want_nodeval = false;
170 terminate = (*cmdfunc)(args, ctype);
171 if (! in_commands || ctype == D_commands)
210 /* mid-rule action buried in non-terminal to avoid conflict */
212 : { want_nodeval = true; }
216 : D_EVAL set_want_nodeval opt_param_list nls
219 /* don't free arg_list; passed on to statement_list
220 * non-terminal (empty rule action). See below.
222 if (input_from_tty) {
223 dbg_prompt = eval_prompt;
225 _("Type (g)awk statement(s). End with the command \"end\"\n"));
226 rl_inhibit_completion = 1;
237 $$ = append_statement(arg_list, (char *) start_EVAL);
238 if (read_a_line == read_commands_string) /* unserializing 'eval' in 'commands' */
239 $$->a_string[0] = '\0';
240 free_cmdarg(arg_list);
243 | statement_list D_STATEMENT { $$ = append_statement($1, lexptr_begin); } nls
250 : eval_prologue statement_list D_END
252 arg_list = append_statement($2, (char *) end_EVAL);
253 if (read_a_line == read_commands_string) { /* unserializing 'eval' in 'commands' */
254 char *str = arg_list->a_string;
255 size_t len = strlen(str);
256 assert(len > 2 && str[len - 2] == '}');
259 if (input_from_tty) {
260 dbg_prompt = in_commands ? commands_prompt : dgawk_prompt;
261 rl_inhibit_completion = 0;
263 cmd_idx = find_command("eval", 4);
266 | D_EVAL set_want_nodeval string_node
271 arg = append_statement(NULL, (char *) start_EVAL);
272 (void) append_statement(arg, n->stptr);
273 (void) append_statement(arg, (char *) end_EVAL);
274 free_cmdarg(arg_list);
284 | control_cmd opt_plus_integer
285 | frame_cmd opt_integer
287 if (cmdtab[cmd_idx].class == D_FRAME
288 && $2 != NULL && $2->a_int < 0)
289 yyerror(_("invalid frame number: %d"), $2->a_int);
293 int idx = find_argument($2);
295 yyerror(_("info: invalid option - \"%s\""), $2->a_string);
299 $2->type = D_argument;
300 $2->a_argument = argtab[idx].value;
303 | D_IGNORE plus_integer D_INT
304 | D_ENABLE enable_args
305 | D_PRINT { want_nodeval = true; } print_args
306 | D_PRINTF { want_nodeval = true; } printf_args
310 | break_cmd break_args
311 | D_SET { want_nodeval = true; } variable '=' node
312 | D_OPTION option_args
313 | D_RETURN { want_nodeval = true; } opt_node
314 | D_DISPLAY { want_nodeval = true; } opt_variable
315 | D_WATCH { want_nodeval = true; } variable condition_exp
316 | d_cmd opt_integer_list
320 if (in_cmd_src($2->a_string))
321 yyerror(_("source \"%s\": already sourced."), $2->a_string);
325 if (! input_from_tty)
326 yyerror(_("save \"%s\": command not permitted."), $2->a_string);
328 | D_COMMANDS commands_arg
338 else if (in_commands)
339 yyerror(_("Can't use command `commands' for breakpoint/watchpoint commands"));
340 else if ($2 == NULL && ! (type = has_break_or_watch_point(&num, true)))
341 yyerror(_("no breakpoint/watchpoint has been set yet"));
342 else if ($2 != NULL && ! (type = has_break_or_watch_point(&num, false)))
343 yyerror(_("invalid breakpoint/watchpoint number"));
346 if (input_from_tty) {
347 dbg_prompt = commands_prompt;
348 fprintf(out_fp, _("Type commands for when %s %d is hit, one per line.\n"),
349 (type == D_break) ? "breakpoint" : "watchpoint", num);
350 fprintf(out_fp, _("End with the command \"end\"\n"));
357 yyerror(_("`end' valid only in command `commands' or `eval'"));
360 dbg_prompt = dgawk_prompt;
367 yyerror(_("`silent' valid only in command `commands'"));
371 int idx = find_argument($2);
373 yyerror(_("trace: invalid option - \"%s\""), $2->a_string);
377 $2->type = D_argument;
378 $2->a_argument = argtab[idx].value;
381 | D_CONDITION plus_integer { want_nodeval = true; } condition_exp
385 type = has_break_or_watch_point(&num, false);
387 yyerror(_("condition: invalid breakpoint/watchpoint number"));
392 /* Prepend command 'eval' to argument list */
394 arg = mk_cmdarg(D_string);
395 arg->a_string = estrdup("eval", 4);
396 arg->next = arg_list;
406 NODE *n = $1->a_node;
408 $1->a_string = n->stptr;
429 | param_list D_VARIABLE
430 | param_list ',' D_VARIABLE
448 if ((n->flags & STRING) == 0)
449 yyerror(_("argument not a string"));
458 if (find_option($1->a_string) < 0)
459 yyerror(_("option: invalid parameter - \"%s\""), $1->a_string);
461 | D_STRING '=' D_STRING
463 if (find_option($1->a_string) < 0)
464 yyerror(_("option: invalid parameter - \"%s\""), $1->a_string);
472 n = lookup($1->a_string);
473 if (n == NULL || n->type != Node_func)
474 yyerror(_("no such function - \"%s\""), $1->a_string);
489 | D_STRING ':' plus_integer
490 | D_STRING ':' func_name
496 | plus_integer { want_nodeval = true; } condition_exp
498 | D_STRING ':' plus_integer { want_nodeval = true; } condition_exp
499 | D_STRING ':' func_name
527 | D_STRING opt_integer_list
529 int idx = find_argument($1);
531 yyerror(_("enable: invalid option - \"%s\""), $1->a_string);
535 $1->type = D_argument;
536 $1->a_argument = argtab[idx].value;
545 $2->type = D_array; /* dump all items */
548 | '@' D_VARIABLE subscript_list /* dump sub-array items*/
551 $2->a_count = num_dim;
557 | print_args print_exp
558 | print_args ',' print_exp
569 | printf_args ',' printf_exp
581 a = mk_cmdarg(D_int);
588 | D_STRING ':' plus_integer
589 | D_STRING ':' func_name
590 | D_STRING ':' integer_range
594 : plus_integer '-' plus_integer
596 if ($1->a_int > $3->a_int)
597 yyerror(_("invalid range specification: %d - %d"),
598 $1->a_int, $3->a_int);
615 | integer_list plus_integer
616 | integer_list integer_range
634 for (a = $2; a != NULL; a = a->next)
636 subs = concat_args($2, count);
637 free_cmdarg($2->next);
648 { $$ = $1; num_dim = 1; }
649 | subscript_list subscript
650 { $$ = $1; num_dim++; }
657 NODE *n = $2->a_node;
658 if ((n->flags & NUMBER) == 0)
659 yyerror(_("non-numeric value for field number"));
664 | D_VARIABLE subscript_list
666 /* a_string is array name, a_count is dimension count */
667 $1->type = D_subscript;
668 $1->a_count = num_dim;
678 NODE *n = $2->a_node;
679 if ((n->flags & NUMBER) == 0)
680 yyerror(_("non-numeric value found, numeric expected"));
685 NODE *n = $2->a_node;
686 if ((n->flags & NUMBER) == 0)
687 yyerror(_("non-numeric value found, numeric expected"));
712 yyerror(_("non-zero integer value"));
718 yyerror(_("non-zero integer value"));
730 $2->a_int = - $2->a_int;
738 if (lexptr_begin != NULL) {
739 if (input_from_tty && lexptr_begin[0] != '\0')
740 add_history(lexptr_begin);
750 /* append_statement --- append 'stmt' to the list of eval awk statements */
753 append_statement(CMDARG *stmt_list, char *stmt)
757 int len, slen, ssize;
761 if (stmt == start_EVAL) {
762 len = sizeof(start_EVAL);
763 for (a = stmt_list; a != NULL; a = a->next)
764 len += strlen(a->a_string) + 1; /* 1 for ',' */
767 emalloc(s, char *, (len + 2) * sizeof(char), "append_statement");
768 arg = mk_cmdarg(D_string);
770 arg->a_count = len; /* kludge */
772 slen = sizeof("function @eval(") - 1;
773 memcpy(s, start_EVAL, slen);
775 for (a = stmt_list; a != NULL; a = a->next) {
776 len = strlen(a->a_string);
777 memcpy(s + slen, a->a_string, len);
788 len = strlen(stmt) + 1; /* 1 for newline */
789 s = stmt_list->a_string;
791 ssize = stmt_list->a_count;
792 if (len > ssize - slen) {
793 ssize = slen + len + EVALSIZE;
794 erealloc(s, char *, (ssize + 2) * sizeof(char), "append_statement");
795 stmt_list->a_string = s;
796 stmt_list->a_count = ssize;
798 memcpy(s + slen, stmt, len);
800 if (slen >= 2 && s[slen - 2] != '\n') {
805 if (stmt == end_EVAL)
806 erealloc(stmt_list->a_string, char *, slen + 2, "append_statement");
813 /* command names sorted in ascending order */
815 struct cmdtoken cmdtab[] = {
816 { "backtrace", "bt", D_backtrace, D_BACKTRACE, do_backtrace,
817 gettext_noop("backtrace [N] - print trace of all or N innermost (outermost if N < 0) frames.") },
818 { "break", "b", D_break, D_BREAK, do_breakpoint,
819 gettext_noop("break [[filename:]N|function] - set breakpoint at the specified location.") },
820 { "clear", "", D_clear, D_CLEAR, do_clear,
821 gettext_noop("clear [[filename:]N|function] - delete breakpoints previously set.") },
822 { "commands", "", D_commands, D_COMMANDS, do_commands,
823 gettext_noop("commands [num] - starts a list of commands to be executed at a breakpoint(watchpoint) hit.") },
824 { "condition", "", D_condition, D_CONDITION, do_condition,
825 gettext_noop("condition num [expr] - set or clear breakpoint or watchpoint condition.") },
826 { "continue", "c", D_continue, D_CONTINUE, do_continue,
827 gettext_noop("continue [COUNT] - continue program being debugged.") },
828 { "delete", "d", D_delete, D_DELETE, do_delete_breakpoint,
829 gettext_noop("delete [breakpoints] [range] - delete specified breakpoints.") },
830 { "disable", "", D_disable, D_DISABLE, do_disable_breakpoint,
831 gettext_noop("disable [breakpoints] [range] - disable specified breakpoints.") },
832 { "display", "", D_display, D_DISPLAY, do_display,
833 gettext_noop("display [var] - print value of variable each time the program stops.") },
834 { "down", "", D_down, D_DOWN, do_down,
835 gettext_noop("down [N] - move N frames down the stack.") },
836 { "dump", "", D_dump, D_DUMP, do_dump_instructions,
837 gettext_noop("dump [filename] - dump instructions to file or stdout.") },
838 { "enable", "e", D_enable, D_ENABLE, do_enable_breakpoint,
839 gettext_noop("enable [once|del] [breakpoints] [range] - enable specified breakpoints.") },
840 { "end", "", D_end, D_END, do_commands,
841 gettext_noop("end - end a list of commands or awk statements.") },
842 { "eval", "", D_eval, D_EVAL, do_eval,
843 gettext_noop("eval stmt|[p1, p2, ...] - evaluate awk statement(s).") },
844 { "finish", "", D_finish, D_FINISH, do_finish,
845 gettext_noop("finish - execute until selected stack frame returns.") },
846 { "frame", "f", D_frame, D_FRAME, do_frame,
847 gettext_noop("frame [N] - select and print stack frame number N.") },
848 { "help", "h", D_help, D_HELP, do_help,
849 gettext_noop("help [command] - print list of commands or explanation of command.") },
850 { "ignore", "", D_ignore, D_IGNORE, do_ignore_breakpoint,
851 gettext_noop("ignore N COUNT - set ignore-count of breakpoint number N to COUNT.") },
852 { "info", "i", D_info, D_INFO, do_info,
853 gettext_noop("info topic - source|sources|variables|functions|break|frame|args|locals|display|watch.") },
854 { "list", "l", D_list, D_LIST, do_list,
855 gettext_noop("list [-|+|[filename:]lineno|function|range] - list specified line(s).") },
856 { "next", "n", D_next, D_NEXT, do_next,
857 gettext_noop("next [COUNT] - step program, proceeding through subroutine calls.") },
858 { "nexti", "ni", D_nexti, D_NEXTI, do_nexti,
859 gettext_noop("nexti [COUNT] - step one instruction, but proceed through subroutine calls.") },
860 { "option", "o", D_option, D_OPTION, do_option,
861 gettext_noop("option [name[=value]] - set or display debugger option(s).") },
862 { "print", "p", D_print, D_PRINT, do_print_var,
863 gettext_noop("print var [var] - print value of a variable or array.") },
864 { "printf", "", D_printf, D_PRINTF, do_print_f,
865 gettext_noop("printf format, [arg], ... - formatted output.") },
866 { "quit", "q", D_quit, D_QUIT, do_quit,
867 gettext_noop("quit - exit debugger.") },
868 { "return", "", D_return, D_RETURN, do_return,
869 gettext_noop("return [value] - make selected stack frame return to its caller.") },
870 { "run", "r", D_run, D_RUN, do_run,
871 gettext_noop("run - start or restart executing program.") },
872 #ifdef HAVE_LIBREADLINE
873 { "save", "", D_save, D_SAVE, do_save,
874 gettext_noop("save filename - save commands from the session to file.") },
876 { "set", "", D_set, D_SET, do_set_var,
877 gettext_noop("set var = value - assign value to a scalar variable.") },
878 { "silent", "", D_silent, D_SILENT, do_commands,
879 gettext_noop("silent - suspends usual message when stopped at a breakpoint/watchpoint.") },
880 { "source", "", D_source, D_SOURCE, do_source,
881 gettext_noop("source file - execute commands from file.") },
882 { "step", "s", D_step, D_STEP, do_step,
883 gettext_noop("step [COUNT] - step program until it reaches a different source line.") },
884 { "stepi", "si", D_stepi, D_STEPI, do_stepi,
885 gettext_noop("stepi [COUNT] - step one instruction exactly.") },
886 { "tbreak", "t", D_tbreak, D_TBREAK, do_tmp_breakpoint,
887 gettext_noop("tbreak [[filename:]N|function] - set a temporary breakpoint.") },
888 { "trace", "", D_trace, D_TRACE, do_trace_instruction,
889 gettext_noop("trace on|off - print instruction before executing.") },
890 { "undisplay", "", D_undisplay, D_UNDISPLAY, do_undisplay,
891 gettext_noop("undisplay [N] - remove variable(s) from automatic display list.") },
892 { "until", "u", D_until, D_UNTIL, do_until,
893 gettext_noop("until [[filename:]N|function] - execute until program reaches a different line or line N within current frame.") },
894 { "unwatch", "", D_unwatch, D_UNWATCH, do_unwatch,
895 gettext_noop("unwatch [N] - remove variable(s) from watch list.") },
896 { "up", "", D_up, D_UP, do_up,
897 gettext_noop("up [N] - move N frames up the stack.") },
898 { "watch", "w", D_watch, D_WATCH, do_watch,
899 gettext_noop("watch var - set a watchpoint for a variable.") },
900 { NULL, NULL, D_illegal, 0, (Func_cmd) 0,
904 struct argtoken argtab[] = {
905 { "args", D_info, A_ARGS },
906 { "break", D_info, A_BREAK },
907 { "del", D_enable, A_DEL },
908 { "display", D_info, A_DISPLAY },
909 { "frame", D_info, A_FRAME },
910 { "functions", D_info, A_FUNCTIONS },
911 { "locals", D_info, A_LOCALS },
912 { "off", D_trace, A_TRACE_OFF },
913 { "on", D_trace, A_TRACE_ON },
914 { "once", D_enable, A_ONCE },
915 { "source", D_info, A_SOURCE },
916 { "sources", D_info, A_SOURCES },
917 { "variables", D_info, A_VARIABLES },
918 { "watch", D_info, A_WATCH },
919 { NULL, D_illegal, A_NONE },
923 /* get_command --- return command handler function */
926 get_command(int ctype)
929 for (i = 0; cmdtab[i].name != NULL; i++) {
930 if (cmdtab[i].type == ctype)
931 return cmdtab[i].cf_ptr;
936 /* get_command_name --- return command name given it's type */
939 get_command_name(int ctype)
942 for (i = 0; cmdtab[i].name != NULL; i++) {
943 if (cmdtab[i].type == ctype)
944 return cmdtab[i].name;
949 /* mk_cmdarg --- make an argument for command */
952 mk_cmdarg(enum argtype type)
955 emalloc(arg, CMDARG *, sizeof(CMDARG), "mk_cmdarg");
956 memset(arg, 0, sizeof(CMDARG));
961 /* append_cmdarg --- append ARG to the list of arguments for the current command */
964 append_cmdarg(CMDARG *arg)
966 static CMDARG *savetail;
968 if (arg_list == NULL)
971 savetail->next = arg;
975 /* free_cmdarg --- free all arguments in LIST */
978 free_cmdarg(CMDARG *list)
982 for (arg = list; arg != NULL; arg = nexta) {
990 if (arg->a_string != NULL)
991 efree(arg->a_string);
1004 /* yyerror --- print a syntax error message */
1007 yyerror(const char *mesg, ...)
1010 va_start(args, mesg);
1011 fprintf(out_fp, _("error: "));
1012 vfprintf(out_fp, mesg, args);
1013 fprintf(out_fp, "\n");
1020 /* yylex --- read a command and turn it into tokens */
1025 static char *lexptr = NULL;
1026 static char *lexend;
1031 yylval = (CMDARG *) NULL;
1033 if (errcount > 0 && lexptr_begin == NULL) {
1034 /* fake a new line */
1039 if (lexptr_begin == NULL) {
1041 lexptr_begin = read_a_line(dbg_prompt);
1042 if (lexptr_begin == NULL) { /* EOF or error */
1043 if (get_eof_status() == EXIT_FATAL)
1045 if (get_eof_status() == EXIT_FAILURE) {
1046 static int seen_eof = 0;
1048 /* force a quit, and let do_quit (in debug.c) exit */
1051 fprintf(stderr, _("can't read command (%s)\n"), strerror(errno));
1052 exit_val = EXIT_FAILURE;
1054 exit_val = EXIT_SUCCESS; */
1057 return '\n'; /* end current command if any */
1058 } else if (seen_eof++ == 1) {
1059 cmd_idx = find_command("quit", 4);
1060 return D_QUIT; /* 'quit' token */
1062 return '\n'; /* end command 'quit' */
1065 d_error(_("can't read command (%s)"), strerror(errno));
1066 if (pop_cmd_src() == 0)
1068 exit(EXIT_FATAL); /* shouldn't happen */
1071 if (! in_commands && ! in_eval /* history expansion off in 'commands' and 'eval' */
1074 history_expand_line(&lexptr_begin);
1076 lexptr = lexptr_begin;
1077 lexend = lexptr + strlen(lexptr);
1078 if (*lexptr == '\0' /* blank line */
1083 #ifdef HAVE_LIBREADLINE
1085 h = previous_history();
1087 add_history(h->line);
1089 cmd_idx = repeat_idx;
1090 return cmdtab[cmd_idx].class; /* repeat last command */
1097 while (c == ' ' || c == '\t')
1100 if (! input_from_tty && c == '#')
1104 if (lexptr >= lexend)
1107 if (cmd_idx < 0) { /* need a command */
1108 if (c == '?' && tokstart[1] == '\0' && ! in_eval) {
1110 cmd_idx = find_command("help", 4);
1114 while (c != '\0' && c != ' ' && c != '\t') {
1115 if (! isalpha(c) && ! in_eval) {
1116 yyerror(_("invalid character in command"));
1122 toklen = lexptr - tokstart;
1126 && tokstart[3] == '\0'
1127 && tokstart[0] == 'e'
1128 && tokstart[1] == 'n'
1129 && tokstart[2] == 'd'
1131 cmd_idx = find_command(tokstart, toklen);
1138 cmd_idx = find_command(tokstart, toklen);
1140 if (in_commands && cmdtab[cmd_idx].type != D_eval) {
1141 /* add the actual command string (lexptr_begin) to
1142 * arg_list; command string for 'eval' prepended to the arg_list
1143 * in the grammer above (see eval_cmd non-terminal).
1146 arg = mk_cmdarg(D_string);
1147 arg->a_string = estrdup(lexptr_begin, lexend - lexptr_begin);
1150 return cmdtab[cmd_idx].class;
1152 yyerror(_("unknown command - \"%.*s\", try help"), toklen, tokstart);
1159 if (cmdtab[cmd_idx].type == D_option) {
1162 } else if (c == '-' || c == '+' || c == ':' || c == '|')
1167 int flags = ALREADY_MALLOCED;
1168 bool esc_seen = false;
1170 toklen = lexend - lexptr;
1171 emalloc(str, char *, toklen + 2, "yylex");
1174 while ((c = *++lexptr) != '"') {
1175 if (lexptr == lexend) {
1178 yyerror(_("unterminated string"));
1184 if (want_nodeval || c != '"')
1187 if (lexptr == lexend)
1194 if (! want_nodeval) {
1195 yylval = mk_cmdarg(D_string);
1196 yylval->a_string = str;
1197 append_cmdarg(yylval);
1199 } else { /* awk string */
1202 yylval = mk_cmdarg(D_node);
1203 yylval->a_node = make_str_node(str, p - str, flags);
1204 append_cmdarg(yylval);
1209 if (! want_nodeval) {
1210 while ((c = *++lexptr) != '\0' && c != ':' && c != '-'
1211 && c != ' ' && c != '\t' && c != '=')
1214 /* Is it an integer? */
1215 if (isdigit((unsigned char) tokstart[0]) && cmdtab[cmd_idx].type != D_option) {
1220 l = strtol(tokstart, &end, 0);
1222 yyerror(_("%s"), strerror(errno));
1227 if (lexptr == end) {
1228 yylval = mk_cmdarg(D_int);
1230 append_cmdarg(yylval);
1235 /* Must be string */
1236 yylval = mk_cmdarg(D_string);
1237 yylval->a_string = estrdup(tokstart, lexptr - tokstart);
1238 append_cmdarg(yylval);
1242 /* look for awk number */
1244 if (isdigit((unsigned char) tokstart[0])) {
1252 tval = mpfr_strtofr(r->mpg_numbr, tokstart, & lexptr, 0, ROUND_MODE);
1253 IEEE_FMT(r->mpg_numbr, tval);
1254 if (mpfr_integer_p(r->mpg_numbr)) {
1255 /* integral value, convert to a GMP type. */
1258 mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ);
1263 r = make_number(strtod(tokstart, & lexptr));
1266 yyerror(strerror(errno));
1271 yylval = mk_cmdarg(D_node);
1273 append_cmdarg(yylval);
1278 if (c == '$' || c == '@'
1279 || c == '[' || c == ']'
1280 || c == ',' || c == '=')
1283 if (c != '_' && ! isalpha(c)) {
1284 yyerror(_("invalid character"));
1288 while (isalnum(c) || c == '_')
1290 toklen = lexptr - tokstart;
1293 yylval = mk_cmdarg(D_variable);
1294 yylval->a_string = estrdup(tokstart, toklen);
1295 append_cmdarg(yylval);
1299 /* find_argument --- find index in 'argtab' for a command option */
1302 find_argument(CMDARG *arg)
1304 /* non-number argument */
1308 assert(cmd_idx >= 0);
1309 name = arg->a_string;
1311 for (idx = 0; (p = (char *) argtab[idx].name) != NULL; idx++) {
1312 if (cmdtab[cmd_idx].type == argtab[idx].cmd
1315 && strncmp(p, name, len) == 0
1319 return -1; /* invalid option */
1322 /* concat_args --- concatenate argument strings into a single string NODE */
1325 concat_args(CMDARG *arg, int count)
1329 char *str, *subsep, *p;
1330 long len, subseplen;
1334 n = force_string(arg->a_node);
1338 emalloc(tmp, NODE **, count * sizeof(NODE *), "concat_args");
1339 subseplen = SUBSEP_node->var_value->stlen;
1340 subsep = SUBSEP_node->var_value->stptr;
1343 for (i = 0; i < count; i++) {
1344 n = force_string(arg->a_node);
1345 len += n->stlen + subseplen;
1350 emalloc(str, char *, len + 2, "concat_args");
1352 memcpy(str, n->stptr, n->stlen);
1354 for (i = 1; i < count; i++) {
1357 else if (subseplen > 0) {
1358 memcpy(p, subsep, subseplen);
1363 memcpy(p, n->stptr, n->stlen);
1368 return make_str_node(str, len, ALREADY_MALLOCED);
1371 /* find_command --- find the index in 'cmdtab' using exact,
1372 * abbreviation or unique partial match
1376 find_command(const char *token, size_t toklen)
1380 bool try_exact = true;
1381 int abrv_match = -1;
1382 int partial_match = -1;
1384 #if 'a' == 0x81 /* it's EBCDIC */
1385 /* make sure all lower case characters in token (sorting
1386 * isn't the solution in this case)
1388 for (i = 0; i < toklen; i++) {
1389 if (token[i] != tolower(token[i]))
1394 k = sizeof(cmdtab)/sizeof(cmdtab[0]) - 1;
1395 for (i = 0; i < k; i++) {
1396 name = (char *) cmdtab[i].name;
1397 if (try_exact && *token == *name
1398 && toklen == strlen(name)
1399 && strncmp(name, token, toklen) == 0
1403 if (*name > *token || i == (k - 1))
1406 if (abrv_match < 0) {
1407 abrv = cmdtab[i].abbrvn;
1408 if (abrv[0] == token[0]) {
1409 if (toklen == 1 && ! abrv[1])
1411 else if (toklen == 2 && abrv[1] == token[1])
1415 if (! try_exact && abrv_match >= 0)
1417 if (partial_match < 0) {
1419 && toklen < strlen(name)
1420 && strncmp(name, token, toklen) == 0
1422 if ((i == k - 1 || strncmp(cmdtab[i + 1].name, token, toklen) != 0)
1423 && (i == 0 || strncmp(cmdtab[i - 1].name, token, toklen) != 0)
1429 return partial_match;
1432 /* do_help -- help command */
1435 do_help(CMDARG *arg, int cmd)
1439 initialize_pager(out_fp);
1440 if (setjmp(pager_quit_tag) == 0) {
1441 for (i = 0; cmdtab[i].name != NULL; i++) {
1442 gprintf(out_fp, "%s:\n", cmdtab[i].name);
1443 gprintf(out_fp, "\t%s\n", _(cmdtab[i].help_txt));
1446 } else if (arg->type == D_string) {
1448 name = arg->a_string;
1449 i = find_command(name, strlen(name));
1451 fprintf(out_fp, "%s\n", cmdtab[i].help_txt);
1452 if (strcmp(cmdtab[i].name, "option") == 0)
1455 fprintf(out_fp, _("undefined command: %s\n"), name);
1462 #ifdef HAVE_LIBREADLINE
1464 /* next_word --- find the next word in a line to complete
1465 * (word seperation characters are space and tab).
1469 next_word(char *p, int len, char **endp)
1474 if (p == NULL || len <= 0)
1476 for (i = 0; i < len; i++, p++)
1477 if (*p != ' ' && *p != '\t')
1482 for (i++, q = p + 1; i < len; i++, q++)
1483 if (*q == ' ' || *q == '\t')
1490 /* command_completion --- attempt to complete based on the word number in line;
1491 * try to complete on command names if this is the first word; for the next
1492 * word(s), the type of completion depends on the command name (first word).
1495 #ifndef RL_READLINE_VERSION /* < 4.2a */
1496 #define rl_completion_matches(x, y) completion_matches((char *) (x), (y))
1501 command_completion(const char *text, int start, int end)
1507 rl_attempted_completion_over = true; /* no default filename completion please */
1509 this_cmd = D_illegal;
1511 if ((cmdtok = next_word(rl_line_buffer, len, &e)) == NULL) /* no first word yet */
1512 return rl_completion_matches(text, command_generator);
1513 len -= (e - rl_line_buffer);
1515 idx = find_command(cmdtok, e - cmdtok);
1518 this_cmd = cmdtab[idx].type;
1520 if (! next_word(e, len, NULL)) {
1527 return rl_completion_matches(text, srcfile_generator);
1532 return rl_completion_matches(text, argument_generator);
1534 return rl_completion_matches(text, option_generator);
1540 return rl_completion_matches(text, variable_generator);
1546 if (this_cmd == D_print || this_cmd == D_printf)
1547 return rl_completion_matches(text, variable_generator);
1551 /* command_generator --- generator function for command completion */
1554 command_generator(const char *text, int state)
1556 static size_t textlen;
1560 if (! state) { /* first time */
1561 textlen = strlen(text);
1564 while ((name = (char *) cmdtab[idx].name) != NULL) {
1566 if (strncmp(name, text, textlen) == 0)
1567 return estrdup(name, strlen(name));
1572 /* srcfile_generator --- generator function for source file completion */
1575 srcfile_generator(const char *text, int state)
1577 static size_t textlen;
1580 extern SRCFILE *srcfiles;
1582 if (! state) { /* first time */
1583 textlen = strlen(text);
1586 while (s != srcfiles) {
1587 if (s->stype != SRC_FILE && s->stype != SRC_INC) {
1593 if (strncmp(name, text, textlen) == 0)
1594 return estrdup(name, strlen(name));
1599 /* argument_generator --- generator function for non-number argument completion */
1602 argument_generator(const char *text, int state)
1604 static size_t textlen;
1608 if (! state) { /* first time */
1609 textlen = strlen(text);
1613 if (this_cmd == D_help) {
1614 while ((name = cmdtab[idx++].name) != NULL) {
1615 if (strncmp(name, text, textlen) == 0)
1616 return estrdup(name, strlen(name));
1619 while ((name = argtab[idx].name) != NULL) {
1620 if (this_cmd != argtab[idx++].cmd)
1622 if (strncmp(name, text, textlen) == 0)
1623 return estrdup(name, strlen(name));
1629 /* variable_generator --- generator function for variable name completion */
1632 variable_generator(const char *text, int state)
1634 static size_t textlen;
1636 static NODE *func = NULL;
1637 static NODE **vars = NULL;
1641 if (! state) { /* first time */
1642 textlen = strlen(text);
1645 vars = variable_list();
1647 func = get_function(); /* function in current context */
1650 /* function params */
1651 while (func != NULL) {
1652 if (idx >= func->param_cnt) {
1653 func = NULL; /* don't try to match params again */
1657 name = func->fparms[idx++].param;
1658 if (strncmp(name, text, textlen) == 0)
1659 return estrdup(name, strlen(name));
1663 while ((r = vars[idx++]) != NULL) {
1665 if (strncmp(name, text, textlen) == 0)
1666 return estrdup(name, strlen(name));
1672 /* history_expand_line --- history expand the LINE */
1675 history_expand_line(char **line)
1680 if (! *line || input_fd != 0 || ! input_from_tty)
1683 ret = history_expand(*line, &expansion);
1684 if (ret < 0 || ret == 2)