"Fossies" - the Fresh Open Source Software Archive

Member "gawk-5.1.0/command.y" (10 Apr 2020, 39796 Bytes) of package /linux/misc/gawk-5.1.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Bison source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "command.y": 5.0.1_vs_5.1.0.

    1 /*
    2  * command.y - yacc/bison parser for debugger commands.
    3  */
    4 
    5 /*
    6  * Copyright (C) 2004, 2010, 2011, 2014, 2016, 2017, 2019, 2020,
    7  * the Free Software Foundation, Inc.
    8  *
    9  * This file is part of GAWK, the GNU implementation of the
   10  * AWK Programming Language.
   11  *
   12  * GAWK is free software; you can redistribute it and/or modify
   13  * it under the terms of the GNU General Public License as published by
   14  * the Free Software Foundation; either version 3 of the License, or
   15  * (at your option) any later version.
   16  *
   17  * GAWK is distributed in the hope that it will be useful,
   18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   20  * GNU General Public License for more details.
   21  *
   22  * You should have received a copy of the GNU General Public License
   23  * along with this program; if not, write to the Free Software
   24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335,
   25  * USA 
   26  */
   27 
   28 %{
   29 #include "awk.h"
   30 #include "cmd.h"
   31 
   32 #if 0
   33 #define YYDEBUG 12
   34 int yydebug = 2;
   35 #endif
   36 
   37 static int yylex(void);
   38 static void yyerror(const char *mesg, ...);
   39 
   40 static int find_command(const char *token, size_t toklen);
   41 
   42 static bool want_nodeval = false;
   43 
   44 static int cmd_idx = -1;        /* index of current command in cmd table */
   45 static int repeat_idx = -1;     /* index of last repeatable command in command table */
   46 static CMDARG *arg_list = NULL;     /* list of arguments */
   47 static long dbg_errcount = 0;
   48 static char *lexptr_begin = NULL;
   49 static bool in_commands = false;
   50 static int num_dim;
   51 
   52 static bool in_eval = false;
   53 static const char start_EVAL[] = "function @eval(){";
   54 static const char end_EVAL[] = "}";
   55 static CMDARG *append_statement(CMDARG *stmt_list, char *stmt);
   56 static NODE *concat_args(CMDARG *a, int count);
   57 
   58 #ifdef HAVE_LIBREADLINE
   59 static char *next_word(char *p, int len, char **endp);
   60 static void history_expand_line(char **line);
   61 static char *command_generator(const char *text, int state);
   62 static char *srcfile_generator(const char *text, int state);
   63 static char *argument_generator(const char *text, int state);
   64 static char *variable_generator(const char *text, int state);
   65 extern char *option_generator(const char *text, int state);
   66 static int this_cmd = D_illegal;
   67 #else
   68 #define history_expand_line(p)  /* nothing */
   69 static int rl_inhibit_completion;   /* dummy variable */
   70 #endif
   71 
   72 struct argtoken {
   73     const char *name;
   74     enum argtype cmd;
   75     enum nametypeval value;
   76 };
   77 
   78 /*
   79  * These two should be static, but there are some compilers that
   80  * don't like the static keyword with an empty size. Therefore give
   81  * them names that are less likely to conflict with the rest of gawk.
   82  */
   83 #define argtab zz_debug_argtab
   84 #define cmdtab zz_debug_cmdtab
   85 
   86 extern struct argtoken argtab[];
   87 extern struct cmdtoken cmdtab[];
   88 
   89 static CMDARG *mk_cmdarg(enum argtype type);
   90 static void append_cmdarg(CMDARG *arg);
   91 static int find_argument(CMDARG *arg);
   92 #define YYSTYPE CMDARG *
   93 %}
   94 
   95 %token D_BACKTRACE D_BREAK D_CLEAR D_CONTINUE D_DELETE D_DISABLE D_DOWN
   96 %token D_ENABLE D_FINISH D_FRAME D_HELP D_IGNORE D_INFO D_LIST
   97 %token D_NEXT D_NEXTI D_PRINT D_PRINTF D_QUIT D_RETURN D_RUN D_SET
   98 %token D_STEP D_STEPI D_TBREAK D_UP D_UNTIL
   99 %token D_DISPLAY D_UNDISPLAY D_WATCH D_UNWATCH
  100 %token D_DUMP D_TRACE
  101 %token D_INT D_STRING D_NODE D_VARIABLE
  102 %token D_OPTION D_COMMANDS D_END D_SILENT D_SOURCE
  103 %token D_SAVE D_EVAL D_CONDITION
  104 %token D_STATEMENT
  105 
  106 %%
  107 
  108 input
  109     : /* empty */
  110     | input line
  111       {
  112         cmd_idx = -1;
  113         want_nodeval = false;
  114         if (lexptr_begin != NULL) {
  115             if (input_from_tty && lexptr_begin[0] != '\0')
  116                 add_history(lexptr_begin);
  117             efree(lexptr_begin);
  118             lexptr_begin = NULL;
  119         }
  120         if (arg_list != NULL) {
  121             free_cmdarg(arg_list);
  122             arg_list = NULL;
  123         }
  124       }
  125     ;
  126 
  127 line
  128     : nls
  129     | command nls
  130       {
  131         if (dbg_errcount == 0 && cmd_idx >= 0) {
  132             Func_cmd cmdfunc;
  133             bool terminate = false;
  134             CMDARG *args;
  135             int ctype = 0;
  136 
  137             ctype = cmdtab[cmd_idx].type;
  138 
  139             /* a blank line repeats previous command
  140              * (list, next, nexti, step, stepi and continue without arguments).
  141              * save the index in the command table; used in yylex
  142              */
  143             if ((ctype == D_list
  144                     || ctype == D_next
  145                     || ctype == D_step
  146                     || ctype == D_nexti
  147                     || ctype == D_stepi
  148                     || ctype == D_continue)
  149                 && arg_list == NULL
  150                 && ! in_commands
  151                 && input_from_tty
  152             )
  153                 repeat_idx = cmd_idx;
  154             else
  155                 repeat_idx = -1;
  156 
  157             /* call the command handler; reset the globals arg_list, cmd_idx,
  158              * since this handler could invoke yyparse again.
  159              * call do_commands for the list of commands in `commands';
  160              * arg_list isn't freed on return.
  161              */
  162 
  163             cmdfunc = cmdtab[cmd_idx].cf_ptr;
  164             if (in_commands)
  165                 cmdfunc = do_commands;
  166             cmd_idx = -1;
  167             want_nodeval = false;
  168 
  169             args = arg_list;
  170             arg_list = NULL;
  171 
  172             terminate = (*cmdfunc)(args, ctype);
  173             if (! in_commands || ctype == D_commands)
  174                 free_cmdarg(args);
  175             if (terminate)
  176                 YYACCEPT;
  177         }
  178       }
  179     | error nls
  180       {
  181         yyerrok;
  182       }
  183     ;
  184 
  185 control_cmd
  186     : D_CONTINUE
  187     | D_NEXT
  188     | D_NEXTI
  189     | D_STEP
  190     | D_STEPI
  191     ;
  192 
  193 d_cmd
  194     : D_UNDISPLAY
  195     | D_UNWATCH
  196     | D_DISABLE
  197     | D_DELETE
  198     ;
  199 
  200 frame_cmd
  201     : D_UP
  202     | D_DOWN
  203     | D_BACKTRACE
  204     | D_FRAME
  205     ;
  206 
  207 break_cmd
  208     : D_BREAK
  209     | D_TBREAK
  210     ;
  211 
  212 /* mid-rule action buried in non-terminal to avoid conflict */
  213 set_want_nodeval
  214     : { want_nodeval = true; }
  215     ;
  216 
  217 eval_prologue
  218     : D_EVAL set_want_nodeval opt_param_list nls
  219       {
  220         if (dbg_errcount == 0) {
  221             /* don't free arg_list; passed on to statement_list
  222              * non-terminal (empty rule action). See below.
  223              */
  224             if (input_from_tty) {
  225                 dbg_prompt = eval_prompt;
  226                 fprintf(out_fp,
  227         _("Type (g)awk statement(s). End with the command `end'\n"));
  228                 rl_inhibit_completion = 1;
  229             }
  230             cmd_idx = -1;
  231             in_eval = true;
  232         }
  233       }
  234     ;
  235 
  236 statement_list
  237     : /* empty */
  238       {
  239         $$ = append_statement(arg_list, (char *) start_EVAL);
  240         if (read_a_line == read_commands_string)    /* unserializing 'eval' in 'commands' */
  241             $$->a_string[0] = '\0';
  242         free_cmdarg(arg_list);
  243         arg_list = NULL;
  244       }
  245     | statement_list D_STATEMENT { $$ = append_statement($1, lexptr_begin); } nls
  246       {
  247         $$ = $3;
  248       }
  249     ;
  250 
  251 eval_cmd
  252     : eval_prologue statement_list D_END
  253       {
  254         arg_list = append_statement($2, (char *) end_EVAL);
  255         if (read_a_line == read_commands_string) {  /* unserializing 'eval' in 'commands' */
  256             char *str = arg_list->a_string;
  257             size_t len = strlen(str);
  258             assert(len > 2 && str[len - 2] == '}');
  259             str[len - 2] = '\0';
  260         }
  261         if (input_from_tty) {
  262             dbg_prompt = in_commands ? commands_prompt : dgawk_prompt;
  263             rl_inhibit_completion = 0;
  264         }
  265         cmd_idx = find_command("eval", 4);
  266         in_eval = false;
  267       }
  268     | D_EVAL set_want_nodeval string_node
  269       {
  270         NODE *n;
  271         CMDARG *arg;
  272         n = $3->a_node;
  273         arg = append_statement(NULL, (char *) start_EVAL);
  274         (void) append_statement(arg, n->stptr);
  275         (void) append_statement(arg, (char *) end_EVAL);
  276         free_cmdarg(arg_list);
  277         arg_list = arg;
  278       }
  279     ;
  280 
  281 command
  282     : D_HELP help_args
  283     | D_QUIT
  284     | D_RUN
  285     | D_FINISH
  286     | control_cmd opt_plus_integer
  287     | frame_cmd opt_integer
  288       {
  289         if (cmdtab[cmd_idx].class == D_FRAME
  290                 && $2 != NULL && $2->a_int < 0)
  291             yyerror(_("invalid frame number: %d"), $2->a_int);
  292       }
  293     | D_INFO D_STRING
  294       {
  295         int idx = find_argument($2);
  296         if (idx < 0)
  297             yyerror(_("info: invalid option - `%s'"), $2->a_string);
  298         else {
  299             efree($2->a_string);
  300             $2->a_string = NULL;
  301             $2->type = D_argument;
  302             $2->a_argument = argtab[idx].value;
  303         }
  304       }
  305     | D_IGNORE plus_integer D_INT
  306     | D_ENABLE enable_args
  307     | D_PRINT { want_nodeval = true; } print_args
  308     | D_PRINTF { want_nodeval = true; } printf_args
  309     | D_LIST list_args
  310     | D_UNTIL location
  311     | D_CLEAR location
  312     | break_cmd break_args
  313     | D_SET { want_nodeval = true; } variable '=' node
  314     | D_OPTION option_args
  315     | D_RETURN { want_nodeval = true; } opt_node
  316     | D_DISPLAY { want_nodeval = true; } opt_variable
  317     | D_WATCH { want_nodeval = true; } variable condition_exp
  318     | d_cmd opt_integer_list
  319     | D_DUMP opt_string
  320     | D_SOURCE D_STRING
  321       {
  322         if (in_cmd_src($2->a_string))
  323             yyerror(_("source: `%s': already sourced."), $2->a_string);
  324       }
  325     | D_SAVE D_STRING
  326       {
  327         if (! input_from_tty)
  328             yyerror(_("save: `%s': command not permitted."), $2->a_string);
  329       }
  330     | D_COMMANDS commands_arg
  331       {
  332         int type = 0;
  333         int num;
  334 
  335         if ($2 != NULL)
  336             num = $2->a_int;
  337 
  338         if (dbg_errcount != 0)
  339             ;
  340         else if (in_commands)
  341             yyerror(_("cannot use command `commands' for breakpoint/watchpoint commands"));
  342         else if ($2 == NULL &&  ! (type = has_break_or_watch_point(&num, true)))
  343             yyerror(_("no breakpoint/watchpoint has been set yet"));
  344         else if ($2 != NULL && ! (type = has_break_or_watch_point(&num, false)))
  345             yyerror(_("invalid breakpoint/watchpoint number"));
  346         if (type) {
  347             in_commands = true;
  348             if (input_from_tty) {
  349                 dbg_prompt = commands_prompt;
  350                 fprintf(out_fp, _("Type commands for when %s %d is hit, one per line.\n"),
  351                                 (type == D_break) ? "breakpoint" : "watchpoint", num);
  352                 fprintf(out_fp, _("End with the command `end'\n"));
  353             }
  354         }
  355       }
  356     | D_END
  357       {
  358         if (! in_commands)
  359             yyerror(_("`end' valid only in command `commands' or `eval'"));
  360         else {
  361             if (input_from_tty)
  362                 dbg_prompt = dgawk_prompt;
  363             in_commands = false;
  364         }
  365       }
  366     | D_SILENT
  367       {
  368         if (! in_commands)
  369             yyerror(_("`silent' valid only in command `commands'"));
  370       }
  371     | D_TRACE D_STRING
  372       {
  373         int idx = find_argument($2);
  374         if (idx < 0)
  375             yyerror(_("trace: invalid option - `%s'"), $2->a_string);
  376         else {
  377             efree($2->a_string);
  378             $2->a_string = NULL;
  379             $2->type = D_argument;
  380             $2->a_argument = argtab[idx].value;
  381         }
  382       }
  383     | D_CONDITION plus_integer { want_nodeval = true; } condition_exp
  384       {
  385         int type;
  386         int num = $2->a_int;
  387         type = has_break_or_watch_point(&num, false);
  388         if (! type)
  389             yyerror(_("condition: invalid breakpoint/watchpoint number"));
  390       }
  391     | eval_cmd
  392       {
  393         if (in_commands) {
  394             /* Prepend command 'eval' to argument list */
  395             CMDARG *arg;
  396             arg = mk_cmdarg(D_string);
  397             arg->a_string = estrdup("eval", 4);
  398             arg->next = arg_list;
  399             arg_list = arg;
  400         }
  401       }
  402     ;
  403 
  404 condition_exp
  405     : opt_string_node
  406       {
  407         if ($1 != NULL) {
  408             NODE *n = $1->a_node;
  409             $1->type = D_string;
  410             $1->a_string = n->stptr;
  411             freenode(n);
  412         }
  413         $$ = $1;
  414       }
  415     ;
  416 
  417 commands_arg
  418     : opt_plus_integer
  419     | error
  420       { $$ = NULL; }
  421     ;
  422 
  423 opt_param_list
  424     : /* empty */
  425       { $$ = NULL; }
  426     | param_list
  427     ;
  428 
  429 param_list
  430     : D_VARIABLE
  431     | param_list D_VARIABLE
  432     | param_list ',' D_VARIABLE
  433     | error
  434       { $$ = NULL; }
  435     ;
  436 
  437 opt_string_node
  438     : /* empty */
  439       { $$ = NULL; }
  440     | string_node
  441     | error
  442       { $$ = NULL; }
  443     ;
  444 
  445 string_node
  446     : D_NODE
  447       {
  448         NODE *n;
  449         n = $1->a_node;
  450         if ((n->flags & STRING) == 0)
  451             yyerror(_("argument not a string"));
  452       }
  453     ;
  454 
  455 option_args
  456     : /* empty */
  457       { $$ = NULL; }
  458     | D_STRING
  459       {
  460         if (find_option($1->a_string) < 0)
  461             yyerror(_("option: invalid parameter - `%s'"), $1->a_string);
  462       }
  463     | D_STRING '=' D_STRING
  464       {
  465         if (find_option($1->a_string) < 0)
  466             yyerror(_("option: invalid parameter - `%s'"), $1->a_string);
  467       }
  468     ;
  469 
  470 func_name
  471     : D_STRING
  472       {
  473         NODE *n;
  474         n = lookup($1->a_string);
  475         if (n == NULL || n->type != Node_func)
  476             yyerror(_("no such function - `%s'"), $1->a_string);
  477         else {
  478             $1->type = D_func;
  479             efree($1->a_string);
  480             $1->a_string = NULL;
  481             $1->a_node = n;
  482         }
  483       }
  484     ;
  485 
  486 location
  487     : /* empty */
  488       { $$ = NULL; }
  489     | plus_integer
  490     | func_name
  491     | D_STRING ':' plus_integer
  492     | D_STRING ':' func_name
  493     ;
  494 
  495 break_args
  496     : /* empty */
  497       { $$ = NULL; }
  498     | plus_integer { want_nodeval = true; } condition_exp
  499     | func_name
  500     | D_STRING ':' plus_integer { want_nodeval = true; } condition_exp
  501     | D_STRING ':' func_name
  502     ;
  503 
  504 opt_variable
  505     : /* empty */
  506       { $$ = NULL; }
  507     | variable
  508     ;
  509 
  510 opt_string
  511     : /* empty */
  512       { $$ = NULL; }
  513     | D_STRING
  514     ;
  515 
  516 opt_node
  517     : /* empty */
  518       { $$ = NULL; }
  519     | node
  520     ;
  521 
  522 help_args
  523     : /* empty */
  524     | D_STRING
  525     ;
  526 
  527 enable_args
  528     : opt_integer_list
  529     | D_STRING opt_integer_list
  530       {
  531         int idx = find_argument($1);
  532         if (idx < 0)
  533             yyerror(_("enable: invalid option - `%s'"), $1->a_string);
  534         else {
  535             efree($1->a_string);
  536             $1->a_string = NULL;
  537             $1->type = D_argument;
  538             $1->a_argument = argtab[idx].value;
  539         }
  540       }
  541     ;
  542 
  543 print_exp
  544     : variable
  545     | '@' D_VARIABLE
  546       {
  547         $2->type = D_array; /* dump all items */
  548         $2->a_count = 0;
  549       }
  550     | '@' D_VARIABLE subscript_list /* dump sub-array items*/
  551       {
  552         $2->type = D_array;
  553         $2->a_count = num_dim;
  554       }
  555     ;
  556 
  557 print_args
  558     : print_exp
  559     | print_args print_exp
  560     | print_args ',' print_exp
  561     | error
  562     ;
  563 
  564 printf_exp
  565     : D_NODE
  566     | variable
  567     ;
  568 
  569 printf_args
  570     : printf_exp
  571     | printf_args ',' printf_exp
  572     | error
  573     ;
  574 
  575 list_args
  576     : /* empty */
  577       { $$ = NULL; }
  578     | '+'
  579       { $$ = NULL; }
  580     | '-'
  581       {
  582         CMDARG *a;
  583         a = mk_cmdarg(D_int);
  584         a->a_int = -1;
  585         append_cmdarg(a);
  586       }
  587     | plus_integer
  588     | func_name
  589     | integer_range
  590     | D_STRING ':' plus_integer
  591     | D_STRING ':' func_name
  592     | D_STRING ':' integer_range
  593     ;
  594 
  595 integer_range
  596     : plus_integer '-' plus_integer
  597       {
  598         if ($1->a_int > $3->a_int)
  599             yyerror(_("invalid range specification: %d - %d"),
  600                 $1->a_int, $3->a_int);
  601         else
  602             $1->type = D_range;
  603         $$ = $1;
  604       }
  605     ;
  606 
  607 opt_integer_list
  608     : /* empty */
  609       { $$ = NULL; }
  610     | integer_list
  611     | error
  612     ;
  613 
  614 integer_list
  615     : plus_integer
  616     | integer_range
  617     | integer_list plus_integer
  618     | integer_list integer_range
  619     ;
  620 
  621 exp_list
  622     : node
  623       { $$ = $1; }
  624     | exp_list ',' node
  625       { $$ = $1; }
  626     | error
  627     ;
  628 
  629 subscript
  630     : '[' exp_list ']'
  631       {
  632         CMDARG *a;
  633         NODE *subs;
  634         int count = 0;
  635 
  636         for (a = $2; a != NULL; a = a->next)
  637             count++;
  638         subs = concat_args($2, count);
  639         free_cmdarg($2->next);
  640         $2->next = NULL;
  641         $2->type = D_node;
  642         $2->a_node = subs;
  643         $$ = $2;
  644       }
  645     | '[' exp_list error
  646     ;
  647 
  648 subscript_list
  649     : subscript
  650       { $$ = $1; num_dim = 1; }
  651     | subscript_list subscript
  652       { $$ = $1; num_dim++; }
  653     ;
  654 
  655 variable
  656     : D_VARIABLE
  657     | '$' D_NODE
  658       {
  659         NODE *n = $2->a_node;
  660         if ((n->flags & NUMBER) == 0)
  661             yyerror(_("non-numeric value for field number"));
  662         else
  663             $2->type = D_field;
  664         $$ = $2;
  665       }
  666     | D_VARIABLE subscript_list
  667       {
  668         /* a_string is array name, a_count is dimension count */
  669         $1->type = D_subscript;
  670         $1->a_count = num_dim;
  671         $$ = $1;
  672       }
  673     ;
  674 
  675 node
  676     : D_NODE
  677       { $$ = $1; }
  678     | '+' D_NODE
  679       {
  680         NODE *n = $2->a_node;
  681         if ((n->flags & NUMBER) == 0)
  682             yyerror(_("non-numeric value found, numeric expected"));
  683         $$ = $2;
  684       }
  685     | '-' D_NODE
  686       {
  687         NODE *n = $2->a_node;
  688         if ((n->flags & NUMBER) == 0)
  689             yyerror(_("non-numeric value found, numeric expected"));
  690         else
  691             negate_num(n);
  692         $$ = $2;
  693       }
  694     ;
  695 
  696 opt_plus_integer
  697     : /* empty */
  698       { $$ = NULL; }
  699     | plus_integer
  700       { $$ = $1; }
  701     ;
  702 
  703 opt_integer
  704     : /* empty */
  705       { $$ = NULL; }
  706     | integer
  707       { $$ = $1; }
  708     ;
  709 
  710 plus_integer
  711     : D_INT
  712       {
  713         if ($1->a_int == 0)
  714             yyerror(_("non-zero integer value"));
  715         $$ = $1;
  716       }
  717     | '+' D_INT
  718       {
  719         if ($2->a_int == 0)
  720             yyerror(_("non-zero integer value"));
  721         $$ = $2;
  722       }
  723     ;
  724 
  725 integer
  726     : D_INT
  727       { $$ = $1; }
  728     | '+' D_INT
  729       { $$ = $2; }
  730     | '-' D_INT
  731       {
  732         $2->a_int = - $2->a_int;
  733         $$ = $2;
  734       }
  735     ;
  736 
  737 nls
  738     : '\n'
  739       {
  740         if (lexptr_begin != NULL) {
  741             if (input_from_tty && lexptr_begin[0] != '\0')
  742                 add_history(lexptr_begin);
  743             efree(lexptr_begin);
  744             lexptr_begin = NULL;
  745         }
  746       }
  747     ;
  748 
  749 %%
  750 
  751 
  752 /* append_statement --- append 'stmt' to the list of eval awk statements */
  753 
  754 static CMDARG *
  755 append_statement(CMDARG *stmt_list, char *stmt)
  756 {
  757     CMDARG *a, *arg;
  758     char *s;
  759     int len, slen, ssize;
  760 
  761 #define EVALSIZE    512
  762 
  763     if (stmt == start_EVAL) {
  764         len = sizeof(start_EVAL);
  765         for (a = stmt_list; a != NULL; a = a->next)
  766             len += strlen(a->a_string) + 1; /* 1 for ',' */
  767         len += EVALSIZE;
  768 
  769         emalloc(s, char *, (len + 1) * sizeof(char), "append_statement");
  770         arg = mk_cmdarg(D_string);
  771         arg->a_string = s;
  772         arg->a_count = len; /* kludge */
  773 
  774         slen = sizeof("function @eval(") - 1;
  775         memcpy(s, start_EVAL, slen);
  776 
  777         for (a = stmt_list; a != NULL; a = a->next) {
  778             len = strlen(a->a_string);
  779             memcpy(s + slen, a->a_string, len);
  780             slen += len;
  781             if (a->next != NULL)
  782                 s[slen++] = ',';
  783         }
  784         s[slen++] = ')';
  785         s[slen++] = '{';
  786         s[slen] = '\0';
  787         return arg;
  788     }
  789 
  790     len = strlen(stmt) + 1; /* 1 for newline */
  791     s = stmt_list->a_string;
  792     slen = strlen(s);
  793     ssize = stmt_list->a_count;
  794     if (len > ssize - slen) {
  795         ssize = slen + len + EVALSIZE;
  796         erealloc(s, char *, (ssize + 1) * sizeof(char), "append_statement");
  797         stmt_list->a_string = s;
  798         stmt_list->a_count = ssize;
  799     }
  800     memcpy(s + slen, stmt, len);
  801     slen += len;
  802     if (slen >= 2 && s[slen - 2] != '\n') {
  803         s[slen - 1] = '\n';
  804         s[slen] = '\0';
  805     }
  806 
  807     if (stmt == end_EVAL)
  808         erealloc(stmt_list->a_string, char *, slen + 1, "append_statement");
  809     return stmt_list;
  810 
  811 #undef EVALSIZE
  812 }
  813 
  814 
  815 /* command names sorted in ascending order */
  816 
  817 struct cmdtoken cmdtab[] = {
  818 { "backtrace", "bt", D_backtrace, D_BACKTRACE, do_backtrace,
  819     gettext_noop("backtrace [N] - print trace of all or N innermost (outermost if N < 0) frames.") },
  820 { "break", "b", D_break, D_BREAK, do_breakpoint,
  821     gettext_noop("break [[filename:]N|function] - set breakpoint at the specified location.") },
  822 { "clear", "", D_clear, D_CLEAR, do_clear,
  823     gettext_noop("clear [[filename:]N|function] - delete breakpoints previously set.") },
  824 { "commands", "", D_commands, D_COMMANDS, do_commands,
  825     gettext_noop("commands [num] - starts a list of commands to be executed at a breakpoint(watchpoint) hit.") },
  826 { "condition", "", D_condition, D_CONDITION, do_condition,
  827     gettext_noop("condition num [expr] - set or clear breakpoint or watchpoint condition.") },
  828 { "continue", "c", D_continue, D_CONTINUE, do_continue,
  829     gettext_noop("continue [COUNT] - continue program being debugged.") },
  830 { "delete", "d", D_delete, D_DELETE, do_delete_breakpoint,
  831     gettext_noop("delete [breakpoints] [range] - delete specified breakpoints.") },
  832 { "disable", "", D_disable, D_DISABLE, do_disable_breakpoint,
  833     gettext_noop("disable [breakpoints] [range] - disable specified breakpoints.") },
  834 { "display", "", D_display, D_DISPLAY, do_display,
  835     gettext_noop("display [var] - print value of variable each time the program stops.") },
  836 { "down", "", D_down, D_DOWN, do_down,
  837     gettext_noop("down [N] - move N frames down the stack.") },
  838 { "dump", "", D_dump, D_DUMP, do_dump_instructions,
  839     gettext_noop("dump [filename] - dump instructions to file or stdout.") },
  840 { "enable", "e", D_enable, D_ENABLE, do_enable_breakpoint,
  841     gettext_noop("enable [once|del] [breakpoints] [range] - enable specified breakpoints.") },
  842 { "end", "", D_end, D_END, do_commands,
  843     gettext_noop("end - end a list of commands or awk statements.") },
  844 { "eval", "", D_eval, D_EVAL, do_eval,
  845     gettext_noop("eval stmt|[p1, p2, ...] - evaluate awk statement(s).") },
  846 { "exit", "", D_quit, D_QUIT, do_quit,
  847     gettext_noop("exit - (same as quit) exit debugger.") },
  848 { "finish", "", D_finish, D_FINISH, do_finish,
  849     gettext_noop("finish - execute until selected stack frame returns.") },
  850 { "frame", "f", D_frame, D_FRAME, do_frame,
  851     gettext_noop("frame [N] - select and print stack frame number N.") },
  852 { "help", "h", D_help, D_HELP, do_help,
  853     gettext_noop("help [command] - print list of commands or explanation of command.") },
  854 { "ignore", "", D_ignore, D_IGNORE, do_ignore_breakpoint,
  855     gettext_noop("ignore N COUNT - set ignore-count of breakpoint number N to COUNT.") },
  856 { "info", "i", D_info, D_INFO, do_info,
  857     gettext_noop("info topic - source|sources|variables|functions|break|frame|args|locals|display|watch.") },
  858 { "list", "l", D_list, D_LIST, do_list,
  859     gettext_noop("list [-|+|[filename:]lineno|function|range] - list specified line(s).") },
  860 { "next", "n", D_next, D_NEXT, do_next,
  861     gettext_noop("next [COUNT] - step program, proceeding through subroutine calls.") },
  862 { "nexti", "ni", D_nexti, D_NEXTI, do_nexti,
  863     gettext_noop("nexti [COUNT] - step one instruction, but proceed through subroutine calls.") },
  864 { "option", "o", D_option, D_OPTION, do_option,
  865     gettext_noop("option [name[=value]] - set or display debugger option(s).") },
  866 { "print", "p", D_print, D_PRINT, do_print_var,
  867     gettext_noop("print var [var] - print value of a variable or array.") },
  868 { "printf", "", D_printf, D_PRINTF, do_print_f,
  869     gettext_noop("printf format, [arg], ... - formatted output.") },
  870 { "quit", "q", D_quit, D_QUIT, do_quit,
  871     gettext_noop("quit - exit debugger.") },
  872 { "return", "", D_return, D_RETURN, do_return,
  873     gettext_noop("return [value] - make selected stack frame return to its caller.") },
  874 { "run", "r", D_run, D_RUN, do_run,
  875     gettext_noop("run - start or restart executing program.") },
  876 #ifdef HAVE_LIBREADLINE
  877 { "save", "", D_save, D_SAVE, do_save,
  878     gettext_noop("save filename - save commands from the session to file.") },
  879 #endif
  880 { "set", "", D_set, D_SET, do_set_var,
  881     gettext_noop("set var = value - assign value to a scalar variable.") },
  882 { "silent", "", D_silent, D_SILENT, do_commands,
  883     gettext_noop("silent - suspends usual message when stopped at a breakpoint/watchpoint.") },
  884 { "source", "", D_source, D_SOURCE, do_source,
  885     gettext_noop("source file - execute commands from file.") },
  886 { "step", "s", D_step, D_STEP, do_step,
  887     gettext_noop("step [COUNT] - step program until it reaches a different source line.") },
  888 { "stepi", "si", D_stepi, D_STEPI, do_stepi,
  889     gettext_noop("stepi [COUNT] - step one instruction exactly.") },
  890 { "tbreak", "t", D_tbreak, D_TBREAK, do_tmp_breakpoint,
  891     gettext_noop("tbreak [[filename:]N|function] - set a temporary breakpoint.") },
  892 { "trace", "", D_trace, D_TRACE, do_trace_instruction,
  893     gettext_noop("trace on|off - print instruction before executing.") },
  894 { "undisplay",  "", D_undisplay, D_UNDISPLAY, do_undisplay,
  895     gettext_noop("undisplay [N] - remove variable(s) from automatic display list.") },
  896 { "until", "u", D_until, D_UNTIL, do_until,
  897     gettext_noop("until [[filename:]N|function] - execute until program reaches a different line or line N within current frame.") },
  898 { "unwatch", "", D_unwatch, D_UNWATCH, do_unwatch,
  899     gettext_noop("unwatch [N] - remove variable(s) from watch list.") },
  900 { "up", "", D_up, D_UP, do_up,
  901     gettext_noop("up [N] - move N frames up the stack.") },
  902 { "watch", "w", D_watch, D_WATCH, do_watch,
  903     gettext_noop("watch var - set a watchpoint for a variable.") },
  904 { "where", "", D_backtrace, D_BACKTRACE, do_backtrace,
  905     gettext_noop("where [N] - (same as backtrace) print trace of all or N innermost (outermost if N < 0) frames.") },
  906 { NULL, NULL, D_illegal, 0, (Func_cmd) 0,
  907      NULL },
  908 };
  909 
  910 struct argtoken argtab[] = {
  911     { "args", D_info, A_ARGS },
  912     { "break", D_info, A_BREAK },
  913     { "del", D_enable, A_DEL },
  914     { "display", D_info, A_DISPLAY },
  915     { "frame", D_info, A_FRAME },
  916     { "functions", D_info, A_FUNCTIONS },
  917     { "locals", D_info, A_LOCALS },
  918     { "off", D_trace, A_TRACE_OFF },
  919     { "on", D_trace, A_TRACE_ON },
  920     { "once", D_enable, A_ONCE },
  921     { "source", D_info, A_SOURCE },
  922     { "sources", D_info, A_SOURCES },
  923     { "variables", D_info, A_VARIABLES },
  924     { "watch", D_info, A_WATCH },
  925     { NULL, D_illegal, A_NONE },
  926 };
  927 
  928 
  929 /* get_command --- return command handler function */
  930 
  931 Func_cmd
  932 get_command(int ctype)
  933 {
  934     int i;
  935     for (i = 0; cmdtab[i].name != NULL; i++) {
  936         if (cmdtab[i].type == ctype)
  937             return cmdtab[i].cf_ptr;
  938     }
  939     return (Func_cmd) 0;
  940 }
  941 
  942 /* get_command_name --- return command name given it's type */
  943 
  944 const char *
  945 get_command_name(int ctype)
  946 {
  947     int i;
  948     for (i = 0; cmdtab[i].name != NULL; i++) {
  949         if (cmdtab[i].type == ctype)
  950             return cmdtab[i].name;
  951     }
  952     return NULL;
  953 }
  954 
  955 /* mk_cmdarg --- make an argument for command */
  956 
  957 static CMDARG *
  958 mk_cmdarg(enum argtype type)
  959 {
  960     CMDARG *arg;
  961     ezalloc(arg, CMDARG *, sizeof(CMDARG), "mk_cmdarg");
  962     arg->type = type;
  963     return arg;
  964 }
  965 
  966 /* append_cmdarg --- append ARG to the list of arguments for the current command */
  967 
  968 static void
  969 append_cmdarg(CMDARG *arg)
  970 {
  971     static CMDARG *savetail;
  972 
  973     if (arg_list == NULL)
  974         arg_list = arg;
  975     else
  976         savetail->next = arg;
  977     savetail = arg;
  978 }
  979 
  980 /* free_cmdarg --- free all arguments in LIST */
  981 
  982 void
  983 free_cmdarg(CMDARG *list)
  984 {
  985     CMDARG *arg, *nexta;
  986 
  987     for (arg = list; arg != NULL; arg = nexta) {
  988         nexta = arg->next;
  989 
  990         switch (arg->type) {
  991         case D_variable:
  992         case D_subscript:
  993         case D_array:
  994         case D_string:
  995             if (arg->a_string != NULL)
  996                 efree(arg->a_string);
  997             break;
  998         case D_node:
  999         case D_field:
 1000             unref(arg->a_node);
 1001             break;
 1002         default:
 1003             break;
 1004         }
 1005         efree(arg);
 1006     }
 1007 }
 1008 
 1009 /* yyerror --- print a syntax error message */
 1010 
 1011 static void
 1012 yyerror(const char *mesg, ...)
 1013 {
 1014     va_list args;
 1015     va_start(args, mesg);
 1016     fprintf(out_fp, _("error: "));
 1017     vfprintf(out_fp, mesg, args);
 1018     fprintf(out_fp, "\n");
 1019     va_end(args);
 1020     dbg_errcount++;
 1021     repeat_idx = -1;
 1022 }
 1023 
 1024 
 1025 /* yylex --- read a command and turn it into tokens */
 1026 
 1027 static int
 1028 #ifdef USE_EBCDIC
 1029 yylex_ebcdic(void)
 1030 #else
 1031 yylex(void)
 1032 #endif
 1033 {
 1034     static char *lexptr = NULL;
 1035     static char *lexend;
 1036     int c;
 1037     char *tokstart;
 1038     size_t toklen;
 1039 
 1040     yylval = (CMDARG *) NULL;
 1041 
 1042     if (dbg_errcount > 0 && lexptr_begin == NULL) {
 1043         /* fake a new line */
 1044         dbg_errcount = 0;
 1045         return '\n';
 1046     }
 1047 
 1048     if (lexptr_begin == NULL) {
 1049 again:
 1050         lexptr_begin = read_a_line(dbg_prompt);
 1051         if (lexptr_begin == NULL) { /* EOF or error */
 1052             if (get_eof_status() == EXIT_FATAL)
 1053                 exit(EXIT_FATAL);
 1054             if (get_eof_status() == EXIT_FAILURE) {
 1055                 static int seen_eof = 0;
 1056 
 1057                 /* force a quit, and let do_quit (in debug.c) exit */
 1058                 if (! seen_eof) {
 1059                     if (errno != 0) {
 1060                         fprintf(stderr, _("cannot read command: %s\n"), strerror(errno));
 1061                         exit_val = EXIT_FAILURE;
 1062                     } /* else
 1063                         exit_val = EXIT_SUCCESS; */
 1064 
 1065                     seen_eof = 1;
 1066                     return '\n';    /* end current command if any */
 1067                 } else if (seen_eof++ == 1) {
 1068                     cmd_idx = find_command("quit", 4);
 1069                     return D_QUIT;  /* 'quit' token */
 1070                 } else
 1071                     return '\n';    /* end command 'quit' */
 1072             }
 1073             if (errno != 0)
 1074                 d_error(_("cannot read command: %s"), strerror(errno));
 1075             if (pop_cmd_src() == 0)
 1076                 goto again;
 1077             exit(EXIT_FATAL);   /* shouldn't happen */
 1078         }
 1079 
 1080         if (! in_commands && ! in_eval  /* history expansion off in 'commands' and 'eval' */
 1081                 && input_from_tty
 1082         )
 1083             history_expand_line(&lexptr_begin);
 1084 
 1085         lexptr = lexptr_begin;
 1086         lexend = lexptr + strlen(lexptr);
 1087         if (*lexptr == '\0'     /* blank line */
 1088                 && repeat_idx >= 0
 1089                 && input_from_tty
 1090                 && ! in_eval
 1091         ) {
 1092 #ifdef HAVE_LIBREADLINE
 1093             HIST_ENTRY *h;
 1094             h = previous_history();
 1095             if (h != NULL)
 1096                 add_history(h->line);
 1097 #endif
 1098             cmd_idx = repeat_idx;
 1099             return cmdtab[cmd_idx].class;   /* repeat last command */
 1100         }
 1101         repeat_idx = -1;
 1102     }
 1103 
 1104     c = *lexptr;
 1105 
 1106     while (c == ' ' || c == '\t')
 1107         c = *++lexptr;
 1108 
 1109     if (! input_from_tty && c == '#')
 1110         return '\n';
 1111 
 1112     tokstart = lexptr;
 1113     if (lexptr >= lexend)
 1114         return '\n';
 1115 
 1116     if (cmd_idx < 0) {  /* need a command */
 1117         if (c == '?' && tokstart[1] == '\0' && ! in_eval) {
 1118             lexptr++;
 1119             cmd_idx = find_command("help", 4);
 1120             return D_HELP;
 1121         }
 1122 
 1123         while (c != '\0' && c != ' ' && c != '\t') {
 1124             if (! is_alpha(c) && ! in_eval) {
 1125                 yyerror(_("invalid character in command"));
 1126                 return '\n';
 1127             }
 1128             c = *++lexptr;
 1129         }
 1130 
 1131         toklen = lexptr - tokstart;
 1132 
 1133         if (in_eval) {
 1134             if (toklen == 3
 1135                     && tokstart[3] == '\0'
 1136                     && tokstart[0] == 'e'
 1137                     && tokstart[1] == 'n'
 1138                     && tokstart[2] == 'd'
 1139             ) {
 1140                 cmd_idx = find_command(tokstart, toklen);
 1141                 return D_END;
 1142             }
 1143             lexptr = lexend;
 1144             return D_STATEMENT;
 1145         }
 1146 
 1147         cmd_idx = find_command(tokstart, toklen);
 1148         if (cmd_idx >= 0) {
 1149             if (in_commands && cmdtab[cmd_idx].type != D_eval) {
 1150                 /* add the actual command string (lexptr_begin) to
 1151                  * arg_list; command string for 'eval' prepended to the arg_list
 1152                  * in the grammer above (see eval_cmd non-terminal).
 1153                  */
 1154                 CMDARG *arg;
 1155                 arg = mk_cmdarg(D_string);
 1156                 arg->a_string = estrdup(lexptr_begin, lexend - lexptr_begin);
 1157                 append_cmdarg(arg);
 1158             }
 1159             return cmdtab[cmd_idx].class;
 1160         } else {
 1161             yyerror(_("unknown command - `%.*s', try help"), toklen, tokstart);
 1162             return '\n';
 1163         }
 1164     }
 1165 
 1166     c = *lexptr;
 1167 
 1168     if (cmdtab[cmd_idx].type == D_option) {
 1169         if (c == '=')
 1170             return *lexptr++;
 1171     } else if (c == '-' || c == '+' || c == ':' || c == '|')
 1172         return *lexptr++;
 1173 
 1174     if (c == '"') {
 1175         char *str, *p;
 1176         int flags = ALREADY_MALLOCED;
 1177         bool esc_seen = false;
 1178 
 1179         toklen = lexend - lexptr;
 1180         emalloc(str, char *, toklen + 1, "yylex");
 1181         p = str;
 1182 
 1183         while ((c = *++lexptr) != '"') {
 1184             if (lexptr == lexend) {
 1185 err:
 1186                 efree(str);
 1187                 yyerror(_("unterminated string"));
 1188                 return '\n';
 1189             }
 1190             if (c == '\\') {
 1191                 c = *++lexptr;
 1192                 esc_seen = true;
 1193                 if (want_nodeval || c != '"')
 1194                     *p++ = '\\';
 1195             }
 1196             if (lexptr == lexend)
 1197                 goto err;
 1198             *p++ = c;
 1199         }
 1200         lexptr++;
 1201         *p = '\0';
 1202 
 1203         if (! want_nodeval) {
 1204             yylval = mk_cmdarg(D_string);
 1205             yylval->a_string = str;
 1206             append_cmdarg(yylval);
 1207             return D_STRING;
 1208         } else {    /* awk string */
 1209             if (esc_seen)
 1210                 flags |= SCAN;
 1211             yylval = mk_cmdarg(D_node);
 1212             yylval->a_node = make_str_node(str, p - str, flags);
 1213             append_cmdarg(yylval);
 1214             return D_NODE;
 1215         }
 1216     }
 1217 
 1218     if (! want_nodeval) {
 1219         while ((c = *++lexptr) != '\0' && c != ':' && c != '-'
 1220                     && c != ' ' && c != '\t' && c != '=')
 1221             ;
 1222 
 1223         /* Is it an integer? */
 1224         if (isdigit((unsigned char) tokstart[0]) && cmdtab[cmd_idx].type != D_option) {
 1225             char *end;
 1226             long l;
 1227 
 1228             errno = 0;
 1229             l = strtol(tokstart, &end, 0);
 1230             if (errno != 0) {
 1231                 yyerror(_("%s"), strerror(errno));
 1232                 errno = 0;
 1233                 return '\n';
 1234             }
 1235 
 1236             if (lexptr == end) {
 1237                 yylval = mk_cmdarg(D_int);
 1238                 yylval->a_int = l;
 1239                 append_cmdarg(yylval);
 1240                 return D_INT;
 1241             }
 1242         }
 1243 
 1244         /* Must be string */
 1245         yylval = mk_cmdarg(D_string);
 1246         yylval->a_string = estrdup(tokstart, lexptr - tokstart);
 1247         append_cmdarg(yylval);
 1248         return D_STRING;
 1249     }
 1250 
 1251     /* look for awk number */
 1252 
 1253     if (isdigit((unsigned char) tokstart[0])) {
 1254         NODE *r = NULL;
 1255 
 1256         errno = 0;
 1257 #ifdef HAVE_MPFR
 1258         if (do_mpfr) {
 1259             int tval;
 1260             r = mpg_float();
 1261             tval = mpfr_strtofr(r->mpg_numbr, tokstart, & lexptr, 0, ROUND_MODE);
 1262             IEEE_FMT(r->mpg_numbr, tval);
 1263             if (mpfr_integer_p(r->mpg_numbr)) {
 1264                 /* integral value, convert to a GMP type. */
 1265                 NODE *tmp = r;
 1266                 r = mpg_integer();
 1267                 mpfr_get_z(r->mpg_i, tmp->mpg_numbr, MPFR_RNDZ);
 1268                 unref(tmp);
 1269             }
 1270         } else
 1271 #endif
 1272             r = make_number(strtod(tokstart, & lexptr));
 1273 
 1274         if (errno != 0) {
 1275             yyerror(strerror(errno));
 1276             unref(r);
 1277             errno = 0;
 1278             return '\n';
 1279         }
 1280         yylval = mk_cmdarg(D_node);
 1281         yylval->a_node = r;
 1282         append_cmdarg(yylval);
 1283         return D_NODE;
 1284     }
 1285 
 1286     c = *lexptr;
 1287     if (c == '$' || c == '@'
 1288             || c == '[' || c == ']'
 1289             || c == ',' || c == '=')
 1290         return *lexptr++;
 1291 
 1292     if (! is_letter(c)) {
 1293         yyerror(_("invalid character"));
 1294         return '\n';
 1295     }
 1296 
 1297     while (is_identchar(c))
 1298         c = *++lexptr;
 1299     toklen = lexptr - tokstart;
 1300 
 1301     /* awk variable */
 1302     yylval = mk_cmdarg(D_variable);
 1303     yylval->a_string = estrdup(tokstart, toklen);
 1304     append_cmdarg(yylval);
 1305     return D_VARIABLE;
 1306 }
 1307 
 1308 /* Convert single-character tokens coming out of yylex() from EBCDIC to
 1309    ASCII values on-the-fly so that the parse tables need not be regenerated
 1310    for EBCDIC systems.  */
 1311 #ifdef USE_EBCDIC
 1312 static int
 1313 yylex(void)
 1314 {
 1315     static char etoa_xlate[256];
 1316     static int do_etoa_init = 1;
 1317     int tok;
 1318 
 1319     if (do_etoa_init)
 1320     {
 1321         for (tok = 0; tok < 256; tok++)
 1322             etoa_xlate[tok] = (char) tok;
 1323 #ifdef HAVE___ETOA_L
 1324         /* IBM helpfully provides this function.  */
 1325         __etoa_l(etoa_xlate, sizeof(etoa_xlate));
 1326 #else
 1327 # error "An EBCDIC-to-ASCII translation function is needed for this system"
 1328 #endif
 1329         do_etoa_init = 0;
 1330     }
 1331 
 1332     tok = yylex_ebcdic();
 1333 
 1334     if (tok >= 0 && tok <= 0xFF)
 1335         tok = etoa_xlate[tok];
 1336 
 1337     return tok;
 1338 }
 1339 #endif /* USE_EBCDIC */
 1340 
 1341 /* find_argument --- find index in 'argtab' for a command option */
 1342 
 1343 static int
 1344 find_argument(CMDARG *arg)
 1345 {
 1346     /* non-number argument */
 1347     int idx;
 1348     char *name, *p;
 1349     size_t len;
 1350     assert(cmd_idx >= 0);
 1351     name = arg->a_string;
 1352     len = strlen(name);
 1353     for (idx = 0; (p = (char *) argtab[idx].name) != NULL; idx++) {
 1354         if (cmdtab[cmd_idx].type == argtab[idx].cmd
 1355                 && *p == *name
 1356                 && strlen(p) == len
 1357                 && strncmp(p, name, len) == 0
 1358         )
 1359             return idx;
 1360     }
 1361     return -1;  /* invalid option */
 1362 }
 1363 
 1364 /* concat_args --- concatenate argument strings into a single string NODE */
 1365 
 1366 static NODE *
 1367 concat_args(CMDARG *arg, int count)
 1368 {
 1369     NODE *n;
 1370     NODE **tmp;
 1371     char *str, *subsep, *p;
 1372     long len, subseplen;
 1373     int i;
 1374 
 1375     if (count == 1) {
 1376         n = force_string(arg->a_node);
 1377         return dupnode(n);
 1378     }
 1379 
 1380     emalloc(tmp, NODE **, count * sizeof(NODE *), "concat_args");
 1381     subseplen = SUBSEP_node->var_value->stlen;
 1382     subsep = SUBSEP_node->var_value->stptr;
 1383     len = -subseplen;
 1384 
 1385     for (i = 0; i < count; i++) {
 1386         n = force_string(arg->a_node);
 1387         len += n->stlen + subseplen;
 1388         tmp[i] = n;
 1389         arg = arg->next;
 1390     }
 1391 
 1392     emalloc(str, char *, len + 1, "concat_args");
 1393     n = tmp[0];
 1394     memcpy(str, n->stptr, n->stlen);
 1395     p = str + n->stlen;
 1396     for (i = 1; i < count; i++) {
 1397         if (subseplen == 1)
 1398             *p++ = *subsep;
 1399         else if (subseplen > 0) {
 1400             memcpy(p, subsep, subseplen);
 1401             p += subseplen;
 1402         }
 1403 
 1404         n = tmp[i];
 1405         memcpy(p, n->stptr, n->stlen);
 1406         p += n->stlen;
 1407     }
 1408     str[len] = '\0';
 1409     efree(tmp);
 1410     return make_str_node(str, len, ALREADY_MALLOCED);
 1411 }
 1412 
 1413 /* find_command --- find the index in 'cmdtab' using exact,
 1414  *                  abbreviation or unique partial match
 1415  */
 1416 
 1417 static int
 1418 find_command(const char *token, size_t toklen)
 1419 {
 1420     char *name, *abrv;
 1421     int i, k;
 1422     bool try_exact = true;
 1423     int abrv_match = -1;
 1424     int partial_match = -1;
 1425 
 1426 #ifdef USE_EBCDIC
 1427     /* make sure all lower case characters in token (sorting
 1428      * isn't the solution in this case)
 1429      */
 1430     for (i = 0; i < toklen; i++) {
 1431         if (token[i] != tolower(token[i]))
 1432             return -1;
 1433     }
 1434 #endif
 1435 
 1436     k = sizeof(cmdtab)/sizeof(cmdtab[0]) - 1;
 1437     for (i = 0; i < k; i++) {
 1438         name = (char *) cmdtab[i].name;
 1439         if (try_exact && *token == *name
 1440                 && toklen == strlen(name)
 1441                 && strncmp(name, token, toklen) == 0
 1442         )
 1443             return i;
 1444 
 1445         if (*name > *token || i == (k - 1))
 1446             try_exact = false;
 1447 
 1448         if (abrv_match < 0) {
 1449             abrv = cmdtab[i].abbrvn;
 1450             if (abrv[0] == token[0]) {
 1451                 if (toklen == 1 && ! abrv[1])
 1452                     abrv_match = i;
 1453                 else if (toklen == 2 && abrv[1] == token[1])
 1454                     abrv_match = i;
 1455             }
 1456         }
 1457         if (! try_exact && abrv_match >= 0)
 1458             return abrv_match;
 1459         if (partial_match < 0) {
 1460             if (*token == *name
 1461                     && toklen < strlen(name)
 1462                     && strncmp(name, token, toklen) == 0
 1463             ) {
 1464                 if ((i == k - 1 || strncmp(cmdtab[i + 1].name, token, toklen) != 0)
 1465                     && (i == 0 || strncmp(cmdtab[i - 1].name, token, toklen) != 0)
 1466                 )
 1467                     partial_match = i;
 1468             }
 1469         }
 1470     }
 1471     return partial_match;
 1472 }
 1473 
 1474 /* do_help -- help command */
 1475 
 1476 int
 1477 do_help(CMDARG *arg, int cmd)
 1478 {
 1479     int i;
 1480     if (arg == NULL) {
 1481         initialize_pager(out_fp);
 1482         if (setjmp(pager_quit_tag) == 0) {
 1483             for (i = 0; cmdtab[i].name != NULL; i++) {
 1484                 gprintf(out_fp, "%s:\n", cmdtab[i].name);
 1485                 gprintf(out_fp, "\t%s\n", _(cmdtab[i].help_txt));
 1486             }
 1487         }
 1488     } else if (arg->type == D_string) {
 1489         char *name;
 1490         name = arg->a_string;
 1491         i = find_command(name, strlen(name));
 1492         if (i >= 0) {
 1493             fprintf(out_fp, "%s\n", cmdtab[i].help_txt);
 1494             if (strcmp(cmdtab[i].name, "option") == 0)
 1495                 option_help();
 1496         } else
 1497             fprintf(out_fp, _("undefined command: %s\n"), name);
 1498     }
 1499 
 1500     return false;
 1501 }
 1502 
 1503 
 1504 #ifdef HAVE_LIBREADLINE
 1505 
 1506 /* next_word --- find the next word in a line to complete
 1507  *               (word seperation characters are space and tab).
 1508  */
 1509 
 1510 static char *
 1511 next_word(char *p, int len, char **endp)
 1512 {
 1513     char *q;
 1514     int i;
 1515 
 1516     if (p == NULL || len <= 0)
 1517         return NULL;
 1518     for (i = 0; i < len; i++, p++)
 1519         if (*p != ' ' && *p != '\t')
 1520             break;
 1521     if (i == len)
 1522         return NULL;
 1523     if (endp != NULL) {
 1524         for (i++, q = p + 1; i < len; i++, q++)
 1525             if (*q == ' ' || *q == '\t')
 1526                 break;
 1527         *endp = q;
 1528     }
 1529     return p;
 1530 }
 1531 
 1532 /* command_completion --- attempt to complete based on the word number in line;
 1533  *    try to complete on command names if this is the first word; for the next
 1534  *    word(s), the type of completion depends on the command name (first word).
 1535  */
 1536 
 1537 #ifndef RL_READLINE_VERSION     /* < 4.2a */
 1538 #define rl_completion_matches(x, y) completion_matches((char *) (x), (y))
 1539 #endif
 1540 
 1541 
 1542 char **
 1543 command_completion(const char *text, int start, int end)
 1544 {
 1545     char *cmdtok, *e;
 1546     int idx;
 1547     int len;
 1548 
 1549     rl_attempted_completion_over = true;    /* no default filename completion please */
 1550 
 1551     this_cmd = D_illegal;
 1552     len = start;
 1553     if ((cmdtok = next_word(rl_line_buffer, len, &e)) == NULL)  /* no first word yet */
 1554         return  rl_completion_matches(text, command_generator);
 1555     len -= (e - rl_line_buffer);
 1556 
 1557     idx = find_command(cmdtok, e - cmdtok);
 1558     if (idx < 0)
 1559         return NULL;
 1560     this_cmd = cmdtab[idx].type;
 1561 
 1562     if (! next_word(e, len, NULL)) {
 1563         switch (this_cmd) {
 1564         case D_break:
 1565         case D_list:
 1566         case D_until:
 1567         case D_tbreak:
 1568         case D_clear:
 1569             return rl_completion_matches(text, srcfile_generator);
 1570         case D_info:
 1571         case D_enable:
 1572         case D_trace:
 1573         case D_help:
 1574             return rl_completion_matches(text, argument_generator);
 1575         case D_option:
 1576             return rl_completion_matches(text, option_generator);
 1577         case D_print:
 1578         case D_printf:
 1579         case D_set:
 1580         case D_display:
 1581         case D_watch:
 1582             return rl_completion_matches(text, variable_generator);
 1583         default:
 1584             return NULL;
 1585         }
 1586     }
 1587 
 1588     if (this_cmd == D_print || this_cmd == D_printf)
 1589         return rl_completion_matches(text, variable_generator);
 1590     return NULL;
 1591 }
 1592 
 1593 /* command_generator --- generator function for command completion */
 1594 
 1595 static char *
 1596 command_generator(const char *text, int state)
 1597 {
 1598     static size_t textlen;
 1599     static int idx = 0;
 1600     char *name;
 1601 
 1602     if (! state) {  /* first time */
 1603         textlen = strlen(text);
 1604         idx = 0;
 1605     }
 1606     while ((name = (char *) cmdtab[idx].name) != NULL) {
 1607         idx++;
 1608         if (strncmp(name, text, textlen) == 0)
 1609             return estrdup(name, strlen(name));
 1610     }
 1611     return NULL;
 1612 }
 1613 
 1614 /* srcfile_generator --- generator function for source file completion */
 1615 
 1616 static char *
 1617 srcfile_generator(const char *text, int state)
 1618 {
 1619     static size_t textlen;
 1620     static SRCFILE *s;
 1621     char *name;
 1622     extern SRCFILE *srcfiles;
 1623 
 1624     if (! state) {  /* first time */
 1625         textlen = strlen(text);
 1626         s = srcfiles->next;
 1627     }
 1628     while (s != srcfiles) {
 1629         if (s->stype != SRC_FILE && s->stype != SRC_INC) {
 1630             s = s->next;
 1631             continue;
 1632         }
 1633         name = s->src;
 1634         s = s->next;
 1635         if (strncmp(name, text, textlen) == 0)
 1636             return estrdup(name, strlen(name));
 1637     }
 1638     return NULL;
 1639 }
 1640 
 1641 /* argument_generator --- generator function for non-number argument completion */
 1642 
 1643 static char *
 1644 argument_generator(const char *text, int state)
 1645 {
 1646     static size_t textlen;
 1647     static int idx;
 1648     const char *name;
 1649 
 1650     if (! state) {  /* first time */
 1651         textlen = strlen(text);
 1652         idx = 0;
 1653     }
 1654 
 1655     if (this_cmd == D_help) {
 1656         while ((name = cmdtab[idx++].name) != NULL) {
 1657             if (strncmp(name, text, textlen) == 0)
 1658                 return estrdup(name, strlen(name));
 1659         }
 1660     } else {
 1661         while ((name = argtab[idx].name) != NULL) {
 1662             if (this_cmd != argtab[idx++].cmd)
 1663                 continue;
 1664             if (strncmp(name, text, textlen) == 0)
 1665                 return estrdup(name, strlen(name));
 1666         }
 1667     }
 1668     return NULL;
 1669 }
 1670 
 1671 /* variable_generator --- generator function for variable name completion */
 1672 
 1673 static char *
 1674 variable_generator(const char *text, int state)
 1675 {
 1676     static size_t textlen;
 1677     static int idx = 0;
 1678     static NODE *func = NULL;
 1679     static NODE **vars = NULL;
 1680     const char *name;
 1681     NODE *r;
 1682 
 1683     if (! state) {  /* first time */
 1684         textlen = strlen(text);
 1685         if (vars != NULL)
 1686             efree(vars);
 1687         vars = variable_list();
 1688         idx = 0;
 1689         func = get_function();  /* function in current context */
 1690     }
 1691 
 1692     /* function params */
 1693     while (func != NULL) {
 1694         if (idx >= func->param_cnt) {
 1695             func = NULL;    /* don't try to match params again */
 1696             idx = 0;
 1697             break;
 1698         }
 1699         name = func->fparms[idx++].param;
 1700         if (strncmp(name, text, textlen) == 0)
 1701             return estrdup(name, strlen(name));
 1702     }
 1703 
 1704     /* globals */
 1705     while ((r = vars[idx++]) != NULL) {
 1706         name = r->vname;
 1707         if (strncmp(name, text, textlen) == 0)
 1708             return estrdup(name, strlen(name));
 1709     }
 1710 
 1711     return NULL;
 1712 }
 1713 
 1714 /* history_expand_line ---  history expand the LINE */
 1715 
 1716 static void
 1717 history_expand_line(char **line)
 1718 {
 1719     int ret;
 1720     char *expansion;
 1721 
 1722     if (! *line || input_fd != 0 || ! input_from_tty)
 1723         return;
 1724     using_history();
 1725     ret = history_expand(*line, &expansion);
 1726     if (ret < 0 || ret == 2)
 1727         efree(expansion);
 1728     else {
 1729         efree(*line);
 1730         *line = expansion;
 1731     }
 1732 }
 1733 
 1734 #endif