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