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