"Fossies" - the Fresh Open Source Software Archive

Member "gawk-5.1.0/debug.c" (20 Mar 2020, 142235 Bytes) of package /linux/misc/gawk-5.1.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "debug.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.0.1_vs_5.1.0.

    1 /*
    2  * debug.c - gawk debugger
    3  */
    4 
    5 /*
    6  * Copyright (C) 2004, 2010-2013, 2016-2020 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
   24  */
   25 
   26 #include "awk.h"
   27 #include "cmd.h"
   28 
   29 #ifndef O_RDONLY
   30 #include <fcntl.h>  /* open() */
   31 #endif
   32 
   33 #ifdef __MINGW32__
   34 #define execvp(p,a) w32_execvp(p,a)
   35 int w32_execvp(const char *, char **);
   36 #endif
   37 
   38 extern bool exiting;
   39 extern SRCFILE *srcfiles;
   40 extern INSTRUCTION *rule_list;
   41 extern INSTRUCTION *code_block;
   42 extern NODE **fcall_list;
   43 extern long fcall_count;
   44 extern FILE *output_fp;
   45 extern IOBUF *curfile;
   46 extern const char *command_file;
   47 extern const char *get_spec_varname(Func_ptr fptr);
   48 extern int zzparse(void);
   49 #define read_command()      (void) zzparse()
   50 
   51 extern const char *redir2str(int redirtype);
   52 
   53 static char *linebuf = NULL;    /* used to print a single line of source */
   54 static size_t linebuf_len;
   55 
   56 FILE *out_fp;
   57 char *dbg_prompt;
   58 char *commands_prompt = "> ";   /* breakpoint or watchpoint commands list */
   59 char *eval_prompt = "@> ";  /* awk statement(s) */
   60 
   61 bool input_from_tty = false;
   62 int input_fd;
   63 
   64 static SRCFILE *cur_srcfile;
   65 static long cur_frame = 0;
   66 static INSTRUCTION *cur_pc;
   67 int cur_rule = 0;
   68 
   69 static bool prog_running = false;
   70 
   71 struct condition {
   72     INSTRUCTION *code;
   73     AWK_CONTEXT *ctxt;
   74     char *expr;
   75 };
   76 
   77 struct commands_item {
   78     struct commands_item *next;
   79     struct commands_item *prev;
   80     int cmd;
   81     char *cmd_string;
   82     CMDARG *arg;
   83 };
   84 
   85 /* breakpoint structure */
   86 typedef struct break_point {
   87     struct break_point *next;
   88     struct break_point *prev;
   89     int number;
   90 
   91     long ignore_count;
   92     long hit_count;
   93     char *src;
   94     INSTRUCTION *bpi;   /* Op_breakpoint */
   95 
   96     struct commands_item commands;  /* list of commands to run */
   97     bool silent;
   98 
   99     struct condition cndn;
  100 
  101     short flags;
  102 #define BP_ENABLE       1
  103 #define BP_ENABLE_ONCE  2       /* enable once */
  104 #define BP_TEMP         4
  105 #define BP_IGNORE       8
  106 
  107 } BREAKPOINT;
  108 
  109 static BREAKPOINT breakpoints = { &breakpoints, &breakpoints, 0 };
  110 
  111 #ifdef HAVE_LIBREADLINE
  112 /* do_save -- save command */
  113 static int sess_history_base = 0;
  114 #endif
  115 
  116 #ifndef HAVE_HISTORY_LIST
  117 #define HIST_ENTRY void
  118 #define history_list()  NULL
  119 #endif
  120 
  121 
  122 /* 'list' command */
  123 static int last_printed_line = 0;
  124 static int last_print_count;    /* # of lines printed */
  125 
  126 /* watch or display item */
  127 struct list_item {
  128     struct list_item *next;
  129     struct list_item *prev;
  130     int number;     /* item number */
  131 
  132     NODE *symbol;   /* variable or function param */
  133     NODE **subs;    /* subscripts */
  134     int num_subs;   /* subscript(dimension) count */
  135     char *sname;    /* symbol or param name */
  136 
  137     long fcall_count;
  138 
  139     struct commands_item commands;
  140     int silent;
  141     struct condition cndn;
  142 
  143     /* This is for the value of the watched item */
  144     union {
  145         NODE *n;
  146         long l;
  147     } value[2];
  148 #define cur_value value[0].n
  149 #define cur_size  value[0].l
  150 #define old_value value[1].n
  151 #define old_size  value[1].l
  152 
  153     int flags;
  154 #define PARAM           1
  155 #define SUBSCRIPT       2
  156 #define FIELD_NUM       4
  157 #define OLD_IS_ARRAY    8    /* old item is array */
  158 #define CUR_IS_ARRAY    16   /* current item is array */
  159 };
  160 
  161 #define IS_PARAM(d) (((d)->flags & PARAM) != 0)
  162 #define IS_SUBSCRIPT(d) (((d)->flags & SUBSCRIPT) != 0)
  163 #define IS_FIELD(d) (((d)->flags & FIELD_NUM) != 0)
  164 #define WATCHING_ARRAY(d)   (((d)->flags & CUR_IS_ARRAY) != 0)
  165 
  166 static struct list_item display_list = { &display_list, &display_list, 0 };
  167 static struct list_item watch_list = { &watch_list, &watch_list, 0 };
  168 
  169 
  170 /* Structure to maintain data for processing debugger commands */
  171 
  172 static struct {
  173     long fcall_count;    /* 'finish', 'until', 'next', 'step', 'nexti' commands */
  174     int sourceline;      /* source line number last
  175                           * time we stopped execution,
  176                           * used by next, until and step commands
  177                           */
  178     char *source;        /* next, until and step */
  179 
  180     INSTRUCTION *pc;     /* 'until' and 'return' commands */
  181     int repeat_count;    /* 'step', 'next', 'stepi', 'nexti' commands */
  182     bool print_frame;    /* print frame info,  'finish' and 'until' */
  183     bool print_ret;      /* print returned value, 'finish' */
  184     int break_point;     /* non-zero (breakpoint number) if stopped at break point */
  185     int watch_point;     /* non-zero (watchpoint number) if stopped at watch point */
  186 
  187     int (*check_func)(INSTRUCTION **);      /* function to decide when to suspend
  188                                              * awk interpreter and return control
  189                                              * to debugger command interpreter.
  190                                              */
  191 
  192     enum argtype command;        /* command type */
  193 } stop;
  194 
  195 
  196 /* restart related stuff */
  197 extern char **d_argv;   /* copy of argv array */
  198 static bool need_restart = false;
  199 enum { BREAK=1, WATCH, DISPLAY, HISTORY, OPTION };
  200 static const char *const env_variable[] = {
  201 "",
  202 "DGAWK_BREAK",
  203 "DGAWK_WATCH",
  204 "DGAWK_DISPLAY",
  205 "DGAWK_HISTORY",
  206 "DGAWK_OPTION",
  207 };
  208 static void serialize_list(int type);
  209 static void unserialize_list(int type);
  210 static const char *commands_string = NULL;
  211 static int commands_string_len = 0;
  212 static char line_sep;
  213 #define FSEP    (char)'\037'
  214 #define RSEP    (char)'\036'
  215 #define CSEP    (char)'\035'
  216 
  217 
  218 /* debugger option */
  219 struct dbg_option {
  220     const char *name;
  221     int *num_val;
  222     char **str_val;
  223     void (*assign)(const char *);
  224     const char *help_txt;
  225 };
  226 
  227 #define DEFAULT_HISTFILE    "./.gawk_history"
  228 #define DEFAULT_OPTFILE     "./.gawkrc"
  229 #define DEFAULT_PROMPT      "gawk> "
  230 #define DEFAULT_LISTSIZE    15
  231 #define DEFAULT_HISTSIZE    100
  232 
  233 static void set_gawk_output(const char *file);
  234 static void set_prompt(const char *value);
  235 static void set_listsize(const char *value);
  236 static void set_trace(const char *value);
  237 static void set_save_history(const char *value);
  238 static void set_save_options(const char *value);
  239 static void set_history_size(const char *value);
  240 static const char *options_file = DEFAULT_OPTFILE;
  241 #ifdef HAVE_LIBREADLINE
  242 static const char *history_file = DEFAULT_HISTFILE;
  243 #endif
  244 
  245 /* debugger option related variables */
  246 
  247 static char *output_file = "/dev/stdout";  /* gawk output redirection */
  248 char *dgawk_prompt = NULL;                 /* initialized in interpret */
  249 static int list_size = DEFAULT_LISTSIZE;   /* # of lines that 'list' prints */
  250 static int do_trace = false;
  251 static int do_save_history = true;
  252 static int do_save_options = true;
  253 static int history_size = DEFAULT_HISTSIZE;  /* max # of lines in history file */
  254 
  255 static const struct dbg_option option_list[] = {
  256 {"history_size", &history_size, NULL, &set_history_size,
  257     gettext_noop("set or show the number of lines to keep in history file.") },
  258 {"listsize", &list_size, NULL, &set_listsize,
  259     gettext_noop("set or show the list command window size.") },
  260 {"outfile", NULL, &output_file, &set_gawk_output,
  261     gettext_noop("set or show gawk output file.") },
  262 {"prompt", NULL, &dgawk_prompt, &set_prompt,
  263     gettext_noop("set or show debugger prompt."), },
  264 {"save_history", &do_save_history, NULL, &set_save_history,
  265     gettext_noop("(un)set or show saving of command history (value=on|off).") },
  266 {"save_options", &do_save_options, NULL, &set_save_options,
  267     gettext_noop("(un)set or show saving of options (value=on|off).") },
  268 {"trace", &do_trace, NULL, &set_trace,
  269     gettext_noop("(un)set or show instruction tracing (value=on|off).") },
  270 {0, NULL, NULL, NULL, 0},
  271 };
  272 
  273 static void save_options(const char *file);
  274 
  275 
  276 /* pager */
  277 jmp_buf pager_quit_tag;
  278 int pager_quit_tag_valid = 0;
  279 static int screen_width = INT_MAX;  /* no of columns */
  280 static int screen_height = INT_MAX; /* no of rows */
  281 static int pager_lines_printed = 0; /* no of lines printed so far */
  282 
  283 static void restart(bool run) ATTRIBUTE_NORETURN;
  284 static void close_all(void);
  285 static int open_readfd(const char *file);
  286 static int find_lines(SRCFILE *s);
  287 static SRCFILE *source_find(char *src);
  288 static int print_lines(char *src, int start_line, int nlines);
  289 static void print_symbol(NODE *r, bool isparam);
  290 static NODE *find_frame(long num);
  291 static NODE *find_param(const char *name, long num, char **pname);
  292 static NODE *find_symbol(const char *name, char **pname);
  293 static NODE *find_array(const char *name);
  294 static void print_field(long field_num);
  295 static int print_function(INSTRUCTION *pc, void *);
  296 static void print_frame(NODE *func, char *src, int srcline);
  297 static void print_numbered_frame(long num);
  298 static void print_cur_frame_and_sourceline(void);
  299 static INSTRUCTION *find_rule(char *src, long lineno);
  300 static INSTRUCTION *mk_breakpoint(char *src, int srcline);
  301 static int execute_commands(struct commands_item *commands);
  302 static void delete_commands_item(struct commands_item *c);
  303 static NODE *execute_code(volatile INSTRUCTION *code);
  304 static int pre_execute_code(INSTRUCTION **pi);
  305 static int parse_condition(int type, int num, char *expr);
  306 static BREAKPOINT *add_breakpoint(INSTRUCTION *prevp, INSTRUCTION *ip, char *src, bool silent);
  307 static BREAKPOINT *set_breakpoint_next(INSTRUCTION *rp, INSTRUCTION *ip);
  308 static BREAKPOINT *set_breakpoint_at(INSTRUCTION *rp, int lineno, bool silent);
  309 static void delete_breakpoint(BREAKPOINT *b);
  310 static BREAKPOINT *find_breakpoint(long num);
  311 static void display(struct list_item *d);
  312 static struct list_item *find_item(struct list_item *list, long num);
  313 static struct list_item *add_item(struct list_item *list, int type, NODE *symbol, char *pname);
  314 static void delete_item(struct list_item *d);
  315 static int breakpoint_triggered(BREAKPOINT *b);
  316 static int watchpoint_triggered(struct list_item *w);
  317 static void print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump);
  318 static void print_ns_list(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump);
  319 static int print_code(INSTRUCTION *pc, void *x);
  320 static void next_command();
  321 static void debug_post_execute(INSTRUCTION *pc);
  322 static int debug_pre_execute(INSTRUCTION **pi);
  323 static char *g_readline(const char *prompt);
  324 static int prompt_yes_no(const char *, char , int , FILE *);
  325 static struct pf_data {
  326     Func_print print_func;
  327     bool defn;
  328     FILE *fp;
  329 } pf_data;
  330 
  331 char * (*read_a_line)(const char *) = 0;    /* reads a line of input */
  332 
  333 struct command_source
  334 {
  335     int fd;
  336     int is_tty;
  337     char * (*read_func)(const char *);
  338     int (*close_func)(int);
  339     int eof_status;     /* see push_cmd_src */
  340     int cmd;        /* D_source or 0 */
  341     char *str;      /* sourced file */
  342     struct command_source *next;
  343 };
  344 
  345 static struct command_source *cmd_src = NULL;
  346 
  347 #define PUSH_BINDING(stack, tag, val)   \
  348 if (val++) \
  349     memcpy((char *) (stack), (const char *) tag, sizeof(jmp_buf))
  350 #define POP_BINDING(stack, tag, val)    \
  351 if (--val) \
  352     memcpy((char *) tag, (const char *) (stack), sizeof(jmp_buf))
  353 
  354 
  355 #define CHECK_PROG_RUNNING() \
  356     do { \
  357         if (! prog_running) { \
  358             d_error(_("program not running.")); \
  359             return false; \
  360         } \
  361     } while (false)
  362 
  363 
  364 /* g_readline --  read a line of text; the interface is like 'readline' but
  365  *      without any command-line editing; used when not compiled with
  366  *      readline support and/or input is not from terminal (prompt set to NULL).
  367  */
  368 
  369 static char *
  370 g_readline(const char *prompt)
  371 {
  372     char *line;
  373     size_t line_size = 100;
  374     static char buf[2];
  375     char *p, *end;
  376     int n;
  377 
  378     if (input_from_tty && prompt && *prompt)
  379         fprintf(out_fp, "%s", prompt);
  380 
  381     emalloc(line, char *, line_size + 1, "g_readline");
  382     p = line;
  383     end = line + line_size;
  384     while ((n = read(input_fd, buf, 1)) > 0) {
  385         if (buf[0] == '\n') {
  386             if (p > line && p[-1] == '\r')
  387                 p--;
  388             break;
  389         }
  390         if (p == end) {
  391             erealloc(line, char *, 2 * line_size + 1, "g_readline");
  392             p = line + line_size;
  393             line_size *= 2;
  394             end = line + line_size;
  395         }
  396         *p++ = buf[0];
  397     }
  398     if (n == -1 || (n == 0 && p == line)) {
  399         efree(line);
  400         return NULL;
  401     }
  402     *p = '\0';
  403     return line;
  404 }
  405 
  406 
  407 /* d_error --- print an error message */
  408 
  409 void
  410 d_error(const char *mesg, ...)
  411 {
  412     va_list args;
  413     va_start(args, mesg);
  414     fprintf(out_fp, _("error: "));
  415     vfprintf(out_fp, mesg, args);
  416     fprintf(out_fp, "\n");
  417     va_end(args);
  418 }
  419 
  420 /* find_lines --- find the positions of the lines in the source file. */
  421 
  422 static int
  423 find_lines(SRCFILE *s)
  424 {
  425     char *buf, *p, *end;
  426     int n;
  427     int ofs = 0;
  428     int *pos;
  429     int pos_size;
  430     int maxlen = 0;
  431     int numlines = 0;
  432     char lastchar = '\0';
  433 
  434     emalloc(buf, char *, s->bufsize, "find_lines");
  435     pos_size = s->srclines;
  436     emalloc(s->line_offset, int *, (pos_size + 2) * sizeof(int), "find_lines");
  437     pos = s->line_offset;
  438     pos[0] = 0;
  439 
  440     while ((n = read(s->fd, buf, s->bufsize)) > 0) {
  441         end = buf + n;
  442         lastchar = buf[n - 1];
  443         p = buf;
  444         while (p < end) {
  445             if (*p++ == '\n') {
  446                 if (++numlines > pos_size) {
  447                     erealloc(s->line_offset, int *, (2 * pos_size + 2) * sizeof(int), "find_lines");
  448                     pos = s->line_offset + pos_size;
  449                     pos_size *= 2;
  450                 }
  451                 *++pos = ofs + (p - buf);
  452                 if ((pos[0] - pos[-1]) > maxlen)
  453                     maxlen = pos[0] - pos[-1];  /* length including NEWLINE */
  454             }
  455         }
  456         ofs += n;
  457     }
  458     efree(buf);
  459 
  460     if (n == -1) {
  461         d_error(_("cannot read source file `%s': %s"),
  462                     s->src, strerror(errno));
  463         return -1;
  464     }
  465     if (ofs <= 0) {
  466         fprintf(out_fp, _("source file `%s' is empty.\n"), s->src);
  467         return -1;
  468     }
  469 
  470     if (lastchar != '\n') {
  471         /* fake a NEWLINE at end */
  472         *++pos = ofs + 1;
  473         numlines++;
  474         if ((pos[0] - pos[-1]) > maxlen)
  475             maxlen = pos[0] - pos[-1];
  476     }
  477     s->maxlen = maxlen;
  478     s->srclines = numlines;
  479     return 0;
  480 }
  481 
  482 /* source_find --- return the SRCFILE struct for the source 'src' */
  483 
  484 static SRCFILE *
  485 source_find(char *src)
  486 {
  487     SRCFILE *s;
  488     struct stat sbuf;
  489     char *path;
  490     int errno_val = 0;
  491 
  492     if (src == NULL || *src == '\0') {
  493         d_error(_("no current source file."));
  494         return NULL;
  495     }
  496 
  497     if (cur_srcfile->src == src)  /* strcmp(cur_srcfile->src, src) == 0 */
  498         return cur_srcfile;
  499 
  500     for (s = srcfiles->next; s != srcfiles; s = s->next) {
  501         if ((s->stype == SRC_FILE || s->stype == SRC_INC)
  502                 && strcmp(s->src, src) == 0)
  503             return s;
  504     }
  505 
  506     path = find_source(src, & sbuf, & errno_val, false);
  507     if (path != NULL) {
  508         for (s = srcfiles->next; s != srcfiles; s = s->next) {
  509             if ((s->stype == SRC_FILE || s->stype == SRC_INC)
  510                         && files_are_same(path, s)) {
  511                 efree(path);
  512                 return s;
  513             }
  514         }
  515         efree(path);
  516     }
  517 
  518     d_error(_("cannot find source file named `%s': %s"), src, strerror(errno_val));
  519     return NULL;
  520 }
  521 
  522 /* print_lines --- print source lines, and update 'cur_srcfile' */
  523 
  524 static int
  525 print_lines(char *src, int start_line, int nlines)
  526 {
  527     SRCFILE *s;
  528     int *pos;
  529     int i;
  530     struct stat sbuf;
  531 
  532     s = source_find(src);
  533     if (s == NULL)
  534         return -1;
  535     if (s->fd <= INVALID_HANDLE && (s->fd = srcopen(s)) <= INVALID_HANDLE) {
  536         d_error(_("cannot open source file `%s' for reading: %s"),
  537                 src, strerror(errno));
  538         return -1;
  539     }
  540 
  541     if (fstat(s->fd, &sbuf) == 0 && s->mtime < sbuf.st_mtime) {
  542         fprintf(out_fp, _("WARNING: source file `%s' modified since program compilation.\n"),
  543                 src);
  544         efree(s->line_offset);
  545         s->line_offset = NULL;
  546         s->mtime = sbuf.st_mtime;
  547 
  548         /* reopen source file */
  549         close(s->fd);
  550         s->fd = INVALID_HANDLE;
  551         if ((s->fd = srcopen(s)) <= INVALID_HANDLE) {
  552             d_error(_("cannot open source file `%s' for reading: %s"),
  553                     src, strerror(errno));
  554             return -1;
  555         }
  556     }
  557 
  558     /* set binary mode so that byte offset calculations will be right */
  559     os_setbinmode(s->fd, O_BINARY);
  560 
  561     if (s->line_offset == NULL && find_lines(s) != 0)
  562         return -1;
  563     if (start_line < 1 || start_line > s->srclines) {
  564         d_error(_("line number %d out of range; `%s' has %d lines"),
  565                     start_line, src, s->srclines);
  566         return -1;
  567     }
  568 
  569     assert(nlines > 0);
  570     if ((start_line + nlines - 1) > s->srclines)
  571         nlines = s->srclines - start_line + 1;
  572 
  573     pos = s->line_offset;
  574     if (lseek(s->fd, (off_t) pos[start_line - 1], SEEK_SET) < 0) {
  575         d_error("%s: %s", src, strerror(errno));
  576         return -1;
  577     }
  578 
  579     if (linebuf == NULL) {
  580         emalloc(linebuf, char *, s->maxlen + 20, "print_lines"); /* 19 for line # */
  581         linebuf_len = s->maxlen;
  582     } else if (linebuf_len < s->maxlen) {
  583         erealloc(linebuf, char *, s->maxlen + 20, "print_lines");
  584         linebuf_len = s->maxlen;
  585     }
  586 
  587     for (i = start_line; i < start_line + nlines; i++) {
  588         int supposed_len, len;
  589         char *p;
  590 
  591         sprintf(linebuf, "%-8d", i);
  592 
  593         /* mark the line about to be executed with =>; nlines > 1
  594          * condition makes sure that we are in list command
  595          */
  596         if (nlines > 1) {
  597             BREAKPOINT *b;
  598             bool has_bpt = false;
  599             for (b = breakpoints.prev; b != &breakpoints; b = b->prev) {
  600                 if (src == b->src && i == b->bpi->source_line) {
  601                     has_bpt = true;
  602                     break;
  603                 }
  604             }
  605             if (prog_running && src == source && i == sourceline) {
  606                 if (has_bpt)
  607                     sprintf(linebuf, "%-4d:b=>", i);
  608                 else
  609                     sprintf(linebuf, "%-4d  =>", i);
  610             } else if (has_bpt)
  611                 sprintf(linebuf, "%-4d:b  ", i);
  612         }
  613 
  614         p = linebuf + strlen(linebuf);
  615         supposed_len = pos[i] - pos[i - 1];
  616         len = read(s->fd, p, supposed_len);
  617         switch (len) {
  618         case -1:
  619             d_error(_("cannot read source file `%s': %s"),
  620                         src, strerror(errno));
  621             return -1;
  622 
  623         case 0:
  624             d_error(_("unexpected eof while reading file `%s', line %d"),
  625                         src, i);
  626             return -1;
  627 
  628         default:
  629             if (i == s->srclines && p[len - 1] != '\n')
  630                 p[len++] = '\n';
  631 #if 0
  632             if (len != supposed_len || p[len - 1] != '\n') {
  633                 d_error(_("source file `%s' modified since start of program execution"),
  634                         src);
  635                 return -1;
  636             }
  637 #endif
  638             len += (p - linebuf);
  639             if (fwrite(linebuf, sizeof(char), len, out_fp) != len)
  640                 return -1;
  641         }
  642     }
  643 
  644     if (cur_srcfile != s) {
  645         if (cur_srcfile->fd != INVALID_HANDLE) {
  646             close(cur_srcfile->fd);
  647             cur_srcfile->fd = INVALID_HANDLE;
  648         }
  649         cur_srcfile = s;
  650     }
  651     return (i - 1);     /* no of lines printed */
  652 }
  653 
  654 /* do_list --- list command */
  655 
  656 int
  657 do_list(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
  658 {
  659     long line_first, line_last;
  660     long count = list_size;
  661     INSTRUCTION *rp;
  662     char *src = cur_srcfile->src;
  663 
  664     line_first = last_printed_line + 1;     /* default or no arg */
  665     if (arg == NULL)    /* list or list + */
  666         goto list;
  667 
  668     switch (arg->type) {
  669     case D_int:     /* list n or list - */
  670         if (arg->a_int < 0) {       /* list - */
  671             line_first = last_printed_line - last_print_count - list_size + 1;
  672             if (line_first < 1) {
  673                 if (last_printed_line != last_print_count)
  674                     line_first = 1;
  675                 else
  676                     return false;
  677             }
  678         } else {
  679 line:
  680             line_first = arg->a_int - list_size / 2;
  681             if (line_first < 1)
  682                 line_first = 1;
  683         }
  684         break;
  685 
  686     case D_range:   /* list m-n */
  687 range:
  688         line_first = arg->a_int;
  689         arg = arg->next;
  690         assert(arg != NULL);
  691         assert(arg->type == D_int);
  692         count = arg->a_int - line_first + 1;
  693         break;
  694 
  695     case D_string:
  696         src = arg->a_string;
  697         if (arg->next != NULL) {
  698             arg = arg->next;
  699             if (arg->type == D_int) /* list file:n */
  700                 goto line;
  701             else if (arg->type == D_range)  /* list file:m-n */
  702                 goto range;
  703             else if (arg->type == D_func)   /* list file:function */
  704                 goto func;
  705             else
  706                 line_first = 1;
  707         } else
  708             line_first = 1;
  709         break;
  710 
  711     case D_func:    /* list function */
  712 func:
  713         rp = arg->a_node->code_ptr;
  714         src = rp->source_file;
  715         line_first = rp->source_line - list_size / 2;
  716         if (line_first < 1)
  717             line_first = 1;
  718         break;
  719 
  720     default:
  721         break;
  722     }
  723 
  724 list:
  725     line_last = print_lines(src, line_first, count);
  726     if (line_last != -1) {
  727         last_printed_line = line_last;
  728         last_print_count = line_last - line_first + 1;
  729     }
  730     return false;
  731 }
  732 
  733 /* do_info --- info command */
  734 
  735 int
  736 do_info(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
  737 {
  738     NODE **table;
  739 
  740     if (arg == NULL || arg->type != D_argument)
  741         return false;
  742 
  743     switch (arg->a_argument) {
  744     case A_SOURCE:
  745         fprintf(out_fp, _("Current source file: %s\n"), cur_srcfile->src);
  746         fprintf(out_fp, _("Number of lines: %d\n"), cur_srcfile->srclines);
  747         break;
  748 
  749     case A_SOURCES:
  750     {
  751         SRCFILE *s;
  752         for (s = srcfiles->next; s != srcfiles; s = s->next) {
  753             fprintf(out_fp, _("Source file (lines): %s (%d)\n"),
  754                     (s->stype == SRC_FILE || s->stype == SRC_INC) ? s->src
  755                                                           : "cmd. line",
  756                     s->srclines);
  757         }
  758     }
  759         break;
  760 
  761     case A_BREAK:
  762         initialize_pager(out_fp);
  763         if (setjmp(pager_quit_tag) == 0) {
  764             BREAKPOINT *b;
  765             struct commands_item *c;
  766 
  767             gprintf(out_fp, _("Number  Disp  Enabled  Location\n\n"));
  768             for (b = breakpoints.prev; b != &breakpoints; b = b->prev) {
  769                 char *disp = "keep";
  770                 if ((b->flags & BP_ENABLE_ONCE) != 0)
  771                     disp = "dis";
  772                 else if ((b->flags & BP_TEMP) != 0)
  773                     disp = "del";
  774                 gprintf(out_fp, "%-6d  %-4.4s  %-7.7s  file %s, line #%d\n",
  775                         b->number, disp, (b->flags & BP_ENABLE) != 0 ? "yes" : "no",
  776                         b->src, b->bpi->source_line);
  777                 if (b->hit_count > 0)
  778                     gprintf(out_fp, _("\tno of hits = %ld\n"), b->hit_count);
  779                 if ((b->flags & BP_IGNORE) != 0)
  780                     gprintf(out_fp, _("\tignore next %ld hit(s)\n"), b->ignore_count);
  781                 if (b->cndn.code != NULL)
  782                     gprintf(out_fp, _("\tstop condition: %s\n"), b->cndn.expr);
  783                 if (b->commands.next != &b->commands)
  784                     gprintf(out_fp, _("\tcommands:\n"));
  785                 for (c = b->commands.next; c != &b->commands; c = c->next) {
  786                     gprintf(out_fp, "\t%s\n", c->cmd_string);
  787                     if (c->cmd == D_eval) {
  788                         char *start, *end;
  789                         CMDARG *a = c->arg;
  790                         start = strchr(a->a_string, '{');
  791                         end = strrchr(a->a_string, '}');
  792                         if (start == NULL || end == NULL)
  793                             continue;
  794                         start++;
  795                         *end = '\0';
  796                         gprintf(out_fp, "%s", start);   /* FIXME: translate ? */
  797                         *end = '}';
  798                     }
  799                 }
  800             }
  801         }
  802         break;
  803 
  804     case A_FRAME:
  805         CHECK_PROG_RUNNING();
  806         fprintf(out_fp, _("Current frame: "));
  807         print_numbered_frame(cur_frame);
  808         if (cur_frame < fcall_count) {
  809             fprintf(out_fp, _("Called by frame: "));
  810             print_numbered_frame(cur_frame + 1);
  811         }
  812         if (cur_frame > 0) {
  813             fprintf(out_fp, _("Caller of frame: "));
  814             print_numbered_frame(cur_frame - 1);
  815         }
  816         break;
  817 
  818     case A_ARGS:
  819     case A_LOCALS:
  820     {
  821         NODE *f, *func;
  822         INSTRUCTION *pc;
  823         int arg_count, pcount;
  824         int i, from, to;
  825 
  826         CHECK_PROG_RUNNING();
  827         f = find_frame(cur_frame);
  828         func = f->func_node;
  829         if (func == NULL) {
  830             /* print ARGV ? */
  831             fprintf(out_fp, _("None in main().\n"));
  832             return false;
  833         }
  834 
  835         pcount = func->param_cnt;              /* # of defined params */
  836 
  837         pc = (INSTRUCTION *) f->reti;          /* Op_func_call instruction */
  838         arg_count = (pc + 1)->expr_count;      /* # of arguments supplied */
  839 
  840         if (arg_count > pcount)                /* extra args */
  841             arg_count = pcount;
  842         if (arg->a_argument == A_ARGS) {
  843             from = 0;
  844             to = arg_count - 1;
  845         } else {
  846             from = arg_count;
  847             to = pcount - 1;
  848         }
  849 
  850         for (i = from; i <= to; i++) {
  851             NODE *r;
  852             r = f->stack[i];
  853             if (r->type == Node_array_ref)
  854                 r = r->orig_array;
  855             fprintf(out_fp, "%s = ", func->fparms[i].param);
  856             print_symbol(r, true);
  857         }
  858         if (to < from)
  859             fprintf(out_fp, "%s",
  860                 arg->a_argument == A_ARGS ?
  861                     _("No arguments.\n") :
  862                     _("No locals.\n"));
  863     }
  864         break;
  865 
  866     case A_VARIABLES:
  867         table = variable_list();
  868         initialize_pager(out_fp);
  869         if (setjmp(pager_quit_tag) == 0) {
  870             gprintf(out_fp, _("All defined variables:\n\n"));
  871             print_vars(table, gprintf, out_fp);
  872         }
  873         efree(table);
  874         break;
  875 
  876     case A_FUNCTIONS:
  877         table = function_list(true);
  878         initialize_pager(out_fp);
  879         if (setjmp(pager_quit_tag) == 0) {
  880             gprintf(out_fp, _("All defined functions:\n\n"));
  881             pf_data.print_func = gprintf;
  882             pf_data.fp = out_fp;
  883             pf_data.defn = true;
  884             (void) foreach_func(table,
  885                         (int (*)(INSTRUCTION *, void *)) print_function,
  886                         &pf_data);
  887         }
  888         efree(table);
  889         break;
  890 
  891     case A_DISPLAY:
  892     case A_WATCH:
  893         initialize_pager(out_fp);
  894         if (setjmp(pager_quit_tag) == 0) {
  895             struct list_item *d, *list;
  896 
  897             if (arg->a_argument == A_DISPLAY) {
  898                 list = &display_list;
  899                 gprintf(out_fp, _("Auto-display variables:\n\n"));
  900             } else {
  901                 list = &watch_list;
  902                 gprintf(out_fp, _("Watch variables:\n\n"));
  903             }
  904             for (d = list->prev; d != list; d = d->prev) {
  905                 int i;
  906                 struct commands_item *c;
  907                 NODE *symbol = d->symbol;
  908 
  909                 if (IS_SUBSCRIPT(d)) {
  910                     gprintf(out_fp, "%d:\t%s",  d->number, d->sname);
  911                     for (i = 0; i < d->num_subs; i++) {
  912                         NODE *sub;
  913                         sub = d->subs[i];
  914                         gprintf(out_fp, "[\"%.*s\"]", (int) sub->stlen, sub->stptr);
  915                     }
  916                     gprintf(out_fp, "\n");
  917                 } else if (IS_FIELD(d))
  918                     gprintf(out_fp, "%d:\t$%ld\n", d->number, get_number_si(symbol));
  919                 else
  920                     gprintf(out_fp, "%d:\t%s\n", d->number, d->sname);
  921                 if (d->cndn.code != NULL)
  922                     gprintf(out_fp, _("\tstop condition: %s\n"), d->cndn.expr);
  923                 if (d->commands.next != &d->commands)
  924                     gprintf(out_fp, _("\tcommands:\n"));
  925                 for (c = d->commands.next; c != &d->commands; c = c->next) {
  926                     gprintf(out_fp, "\t%s\n", c->cmd_string);
  927                     if (c->cmd == D_eval) {
  928                         char *start, *end;
  929                         CMDARG *a = c->arg;
  930                         start = strchr(a->a_string, '{');
  931                         end = strrchr(a->a_string, '}');
  932                         if (start == NULL || end == NULL)
  933                             continue;
  934                         start++;
  935                         *end = '\0';
  936                         gprintf(out_fp, "%s", start);   /* FIXME: translate ? */
  937                         *end = '}';
  938                     }
  939                 }
  940 
  941             }
  942         }
  943         break;
  944 
  945     default:
  946         break;
  947     }
  948 
  949     return false;
  950 }
  951 
  952 /* print_symbol --- print a symbol table entry */
  953 
  954 static void
  955 print_symbol(NODE *r, bool isparam)
  956 {
  957     switch (r->type) {
  958     case Node_var_new:
  959         fprintf(out_fp, "untyped variable\n");
  960         break;
  961     case Node_var:
  962         if (! isparam && r->var_update)
  963             r->var_update();
  964         valinfo(r->var_value, fprintf, out_fp);
  965         break;
  966     case Node_var_array:
  967         fprintf(out_fp, "array, %ld elements\n", assoc_length(r));
  968         break;
  969     case Node_func:
  970         fprintf(out_fp, "`function'\n");
  971         break;
  972     default:
  973         break;
  974     }
  975 }
  976 
  977 /* find_frame --- find frame given a frame number */
  978 
  979 static NODE *
  980 find_frame(long num)
  981 {
  982     assert(num >= 0);
  983     if (num == 0)
  984         return frame_ptr;
  985 
  986     assert(prog_running == true);
  987     assert(num <= fcall_count);
  988     assert(fcall_list[num] != NULL);
  989     return fcall_list[num];
  990 }
  991 
  992 /* find_param --- find a function parameter in a given frame number */
  993 
  994 static NODE *
  995 find_param(const char *name, long num, char **pname)
  996 {
  997     NODE *r = NULL;
  998     NODE *f;
  999     char *fparam;
 1000 
 1001     if (pname)
 1002         *pname = NULL;
 1003 
 1004     if (num < 0 || num > fcall_count || name == NULL)
 1005         return NULL;
 1006     f = find_frame(num);
 1007     if (f->func_node != NULL) {     /* in function */
 1008         NODE *func;
 1009         int i, pcount;
 1010 
 1011         func = f->func_node;
 1012         pcount = func->param_cnt;
 1013         for (i = 0; i < pcount; i++) {
 1014             fparam = func->fparms[i].param;
 1015             if (strcmp(name, fparam) == 0) {
 1016                 r = f->stack[i];
 1017                 if (r->type == Node_array_ref)
 1018                     r = r->orig_array;
 1019                 if (pname)
 1020                     *pname = fparam;
 1021                 break;
 1022             }
 1023         }
 1024     }
 1025     return r;
 1026 }
 1027 
 1028 /* find_symbol --- find a symbol in current context */
 1029 
 1030 static
 1031 NODE *find_symbol(const char *name, char **pname)
 1032 {
 1033     NODE *r = NULL;
 1034 
 1035     if (pname)
 1036         *pname = NULL;
 1037     if (prog_running)
 1038         r = find_param(name, cur_frame, pname);
 1039     if (r == NULL)
 1040         r = lookup(name); // for now, require fully qualified name
 1041     if (r == NULL)
 1042         fprintf(out_fp, _("no symbol `%s' in current context\n"), name);
 1043     return r;
 1044 }
 1045 
 1046 /* find_array -- find an array in current context */
 1047 
 1048 static NODE *
 1049 find_array(const char *name)
 1050 {
 1051     NODE *r;
 1052     r = find_symbol(name, NULL);
 1053     if (r != NULL && r->type != Node_var_array) {
 1054         fprintf(out_fp, _("`%s' is not an array\n"), name);
 1055         return NULL;
 1056     }
 1057     return r;
 1058 }
 1059 
 1060 /* print_field --- print the value of $n */
 1061 
 1062 static void
 1063 print_field(long field_num)
 1064 {
 1065     NODE **lhs;
 1066     lhs = get_field(field_num, NULL);
 1067     if (*lhs == Null_field || *lhs == Nnull_string)
 1068         fprintf(out_fp, _("$%ld = uninitialized field\n"), field_num);
 1069     else {
 1070         fprintf(out_fp, "$%ld = ", field_num);
 1071         valinfo(*lhs, fprintf, out_fp);
 1072     }
 1073 }
 1074 
 1075 /* print_array --- print the contents of an array */
 1076 
 1077 static int
 1078 print_array(volatile NODE *arr, char *arr_name)
 1079 {
 1080     NODE *subs;
 1081     NODE **list;
 1082     int i;
 1083     size_t num_elems = 0;
 1084     volatile NODE *r;
 1085     volatile int ret = 0;
 1086     volatile jmp_buf pager_quit_tag_stack;
 1087 
 1088     if (assoc_empty((NODE *) arr)) {
 1089         gprintf(out_fp, _("array `%s' is empty\n"), arr_name);
 1090         return 0;
 1091     }
 1092 
 1093     num_elems = assoc_length((NODE *) arr);
 1094 
 1095     /* sort indices, sub_arrays are also sorted! */
 1096     list = assoc_list((NODE *) arr, "@ind_str_asc", SORTED_IN);
 1097 
 1098     PUSH_BINDING(pager_quit_tag_stack, pager_quit_tag, pager_quit_tag_valid);
 1099     if (setjmp(pager_quit_tag) == 0) {
 1100         for (i = 0; ret == 0 && i < num_elems; i++) {
 1101             subs = list[i];
 1102             r = *assoc_lookup((NODE *) arr, subs);
 1103             if (r->type == Node_var_array)
 1104                 ret = print_array(r, r->vname);
 1105             else {
 1106                 gprintf(out_fp, "%s[\"%.*s\"] = ", arr_name, (int) subs->stlen, subs->stptr);
 1107                 valinfo((NODE *) r, gprintf, out_fp);
 1108             }
 1109         }
 1110     } else
 1111         ret = 1;
 1112 
 1113     POP_BINDING(pager_quit_tag_stack, pager_quit_tag, pager_quit_tag_valid);
 1114 
 1115     for (i = 0; i < num_elems; i++)
 1116         unref(list[i]);
 1117     efree(list);
 1118 
 1119     return ret;
 1120 }
 1121 
 1122 /* print_subscript --- print an array element */
 1123 
 1124 static void
 1125 print_subscript(NODE *arr, char *arr_name, CMDARG *a, int count)
 1126 {
 1127     NODE *r, *subs;
 1128 
 1129     subs = a->a_node;
 1130     r = in_array(arr, subs);
 1131     if (r == NULL)
 1132         fprintf(out_fp, _("[\"%.*s\"] not in array `%s'\n"), (int) subs->stlen, subs->stptr, arr_name);
 1133     else if (r->type == Node_var_array) {
 1134         if (count > 1)
 1135             print_subscript(r, r->vname, a->next, count - 1);
 1136         else {
 1137             /* print # of elements in array */
 1138             fprintf(out_fp, "%s = ", r->vname);
 1139             print_symbol(r, false);
 1140         }
 1141     } else {
 1142         fprintf(out_fp, "%s[\"%.*s\"] = ", arr_name, (int) subs->stlen, subs->stptr);
 1143         valinfo(r, fprintf, out_fp);
 1144     }
 1145 }
 1146 
 1147 /* do_print_var --- print command */
 1148 
 1149 int
 1150 do_print_var(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 1151 {
 1152     NODE *r;
 1153     CMDARG *a;
 1154     char *name, *pname;
 1155 
 1156     for (a = arg; a != NULL; a = a->next) {
 1157         switch (a->type) {
 1158         case D_variable:
 1159             name = a->a_string;
 1160             if ((r = find_symbol(name, &pname)) != NULL) {
 1161                 fprintf(out_fp, "%s = ", name);
 1162                 print_symbol(r, (pname != NULL));
 1163             }
 1164             break;
 1165 
 1166         case D_subscript:
 1167             assert(a->a_count > 0);
 1168             name = a->a_string;
 1169             r = find_array(name);
 1170             if (r != NULL)
 1171                 print_subscript(r, name, a->next, a->a_count);
 1172             break;
 1173 
 1174         case D_array:
 1175             name = a->a_string;
 1176             if ((r = find_array(name)) != NULL) {
 1177                 int count = a->a_count;
 1178                 for (; count > 0; count--) {
 1179                     NODE *value, *subs;
 1180                     a = a->next;
 1181                     subs = a->a_node;
 1182                     value = in_array(r, subs);
 1183                     if (value == NULL) {
 1184                         fprintf(out_fp, _("[\"%.*s\"] not in array `%s'\n"),
 1185                                     (int) subs->stlen, subs->stptr, name);
 1186                         break;
 1187                     } else if (value->type != Node_var_array) {
 1188                         fprintf(out_fp, _("`%s[\"%.*s\"]' is not an array\n"),
 1189                                     name, (int) subs->stlen, subs->stptr);
 1190                         break;
 1191                     } else {
 1192                         r = value;
 1193                         name = r->vname;
 1194                     }
 1195                 }
 1196                 if (count == 0) {
 1197                     initialize_pager(out_fp);
 1198                     print_array((volatile NODE *) r, name);
 1199                 }
 1200             }
 1201             break;
 1202 
 1203         case D_field:
 1204             print_field(get_number_si(a->a_node));
 1205             break;
 1206 
 1207         default:
 1208             /* notably D_node, subscript for invalid array name; skip */
 1209             break;
 1210         }
 1211     }
 1212     return false;
 1213 }
 1214 
 1215 /* do_set_var --- set command */
 1216 
 1217 int
 1218 do_set_var(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 1219 {
 1220     NODE *r, *val;
 1221     NODE **lhs;
 1222     char *name, *pname;
 1223 
 1224     switch (arg->type) {
 1225     case D_variable:
 1226         name = arg->a_string;
 1227         arg = arg->next;
 1228         val = arg->a_node;
 1229 
 1230         if ((r = find_symbol(name, &pname)) == NULL)
 1231             break;
 1232 
 1233         switch (r->type) {
 1234         case Node_var_new:
 1235             r->type = Node_var;
 1236             r->var_value = dupnode(Nnull_string);
 1237             /* fall through */
 1238         case Node_var:
 1239             lhs = &r->var_value;
 1240             unref(*lhs);
 1241             *lhs = dupnode(val);
 1242             if (pname == NULL && r->var_assign != NULL)
 1243                 r->var_assign();
 1244             fprintf(out_fp, "%s = ", name);
 1245             print_symbol(r, (pname != NULL));
 1246             break;
 1247 
 1248         default:
 1249             d_error(_("`%s' is not a scalar variable"), name);
 1250             break;
 1251         }
 1252         break;
 1253 
 1254     case D_subscript:
 1255     {
 1256         NODE *subs, *value;
 1257         int count = arg->a_count;
 1258         NODE *newval;
 1259 
 1260         assert(count > 0);
 1261         name = arg->a_string;
 1262         r = find_array(name);
 1263         if (r == NULL)
 1264             break;
 1265         for (; count > 0; count--) {
 1266             arg = arg->next;
 1267             subs = arg->a_node;
 1268             value = in_array(r, subs);
 1269 
 1270             if (count == 1) {
 1271                 if (value != NULL && value->type == Node_var_array)
 1272                     d_error(_("attempt to use array `%s[\"%.*s\"]' in a scalar context"),
 1273                                 name, (int) subs->stlen, subs->stptr);
 1274                 else {
 1275                     arg = arg->next;
 1276                     val = arg->a_node;
 1277                     newval = dupnode(val);
 1278                     // subs should not be freed, so
 1279                     // use dupnode in call to assoc_set.
 1280                     assoc_set(r, dupnode(subs), newval);
 1281                     fprintf(out_fp, "%s[\"%.*s\"] = ", name, (int) subs->stlen, subs->stptr);
 1282                     valinfo(newval, fprintf, out_fp);
 1283                 }
 1284             } else {
 1285                 if (value == NULL) {
 1286                     NODE *array;
 1287                     array = make_array();
 1288                     array->vname = estrdup(subs->stptr, subs->stlen);
 1289                     array->parent_array = r;
 1290                     // subs should not be freed, so
 1291                     // use dupnode in call to assoc_set.
 1292                     assoc_set(r, dupnode(subs), array);
 1293                     r = array;
 1294                 } else if (value->type != Node_var_array) {
 1295                     d_error(_("attempt to use scalar `%s[\"%.*s\"]' as array"),
 1296                             name, (int) subs->stlen, subs->stptr);
 1297                     break;
 1298                 } else {
 1299                     r = value;
 1300                     name = r->vname;
 1301                 }
 1302             }
 1303         }
 1304     }
 1305         break;
 1306 
 1307     case D_field:
 1308     {
 1309         long field_num;
 1310         Func_ptr assign = NULL;
 1311 
 1312         field_num = get_number_si(arg->a_node);
 1313         assert(field_num >= 0);
 1314         arg = arg->next;
 1315         val = arg->a_node;
 1316         lhs = get_field(field_num, &assign);
 1317         if (assign)
 1318             /* call assign to copy fields before unref frees $0 */
 1319             assign();
 1320         unref(*lhs);
 1321         *lhs = dupnode(val);
 1322         print_field(field_num);
 1323     }
 1324         break;
 1325 
 1326     default:
 1327         break;
 1328     }
 1329     return false;
 1330 }
 1331 
 1332 /* find_item --- find an item in the watch/display list */
 1333 
 1334 static struct list_item *
 1335 find_item(struct list_item *list, long num)
 1336 {
 1337     struct list_item *d;
 1338 
 1339     if (num <= 0)
 1340         return NULL;
 1341     for (d = list->next; d != list; d = d->next) {
 1342         if (d->number == num)
 1343             return d;
 1344     }
 1345     return NULL;
 1346 }
 1347 
 1348 /* delete_item --- delete an item from the watch/display list */
 1349 
 1350 static void
 1351 delete_item(struct list_item *d)
 1352 {
 1353     struct commands_item *c;
 1354     int i;
 1355 
 1356     if (IS_SUBSCRIPT(d)) {
 1357         for (i = 0; i < d->num_subs; i++)
 1358             unref(d->subs[i]);
 1359         efree(d->subs);
 1360     } else if (IS_FIELD(d))
 1361         unref(d->symbol);
 1362 
 1363     if ((d->flags & CUR_IS_ARRAY) == 0)
 1364         unref(d->cur_value);
 1365     if ((d->flags & OLD_IS_ARRAY) == 0)
 1366         unref(d->old_value);
 1367 
 1368     /* delete commands */
 1369     for (c = d->commands.next; c != &d->commands; c = c->next) {
 1370         c = c->prev;
 1371         delete_commands_item(c->next);
 1372     }
 1373 
 1374     free_context(d->cndn.ctxt, false);
 1375     if (d->cndn.expr != NULL)
 1376         efree(d->cndn.expr);
 1377 
 1378     d->next->prev = d->prev;
 1379     d->prev->next = d->next;
 1380     efree(d);
 1381 }
 1382 
 1383 /* add_item --- craete a watch/display item and add it to the list */
 1384 
 1385 static struct list_item *
 1386 add_item(struct list_item *list, int type, NODE *symbol, char *pname)
 1387 {
 1388     struct list_item *d;
 1389 
 1390     ezalloc(d, struct list_item *, sizeof(struct list_item), "add_item");
 1391     d->commands.next = d->commands.prev = &d->commands;
 1392 
 1393     d->number = ++list->number;
 1394     d->sname = symbol->vname;
 1395     if (pname != NULL) {    /* function param */
 1396         d->sname = pname;
 1397         d->flags |= PARAM;
 1398         d->fcall_count = fcall_count - cur_frame;
 1399     }
 1400 
 1401     if (type == D_field) {
 1402         /* field number */
 1403         d->symbol = symbol;
 1404         d->flags |= FIELD_NUM;
 1405     } else if (type == D_subscript) {
 1406         /* subscript */
 1407         d->symbol = symbol;
 1408         d->flags |= SUBSCRIPT;
 1409     } else {
 1410         /* array or variable */
 1411         d->symbol = symbol;
 1412     }
 1413 
 1414     /* add to list */
 1415     d->next = list->next;
 1416     d->prev = list;
 1417     list->next = d;
 1418     d->next->prev = d;
 1419     return d;
 1420 }
 1421 
 1422 /* do_add_item --- add an item to the watch/display list */
 1423 
 1424 static struct list_item *
 1425 do_add_item(struct list_item *list, CMDARG *arg)
 1426 {
 1427     NODE *symbol = NULL;
 1428     char *name, *pname = NULL;
 1429     struct list_item *item = NULL;
 1430 
 1431     switch (arg->type) {
 1432     case D_subscript:
 1433     case D_variable:
 1434         name = arg->a_string;
 1435         if ((symbol = find_symbol(name, &pname)) == NULL)
 1436             return NULL;
 1437         if (symbol->type == Node_func) {
 1438             d_error(_("`%s' is a function"), name);
 1439             return NULL;
 1440         }
 1441         if (arg->type == D_subscript && symbol->type != Node_var_array) {
 1442             d_error(_("`%s' is not an array\n"), name);
 1443             return NULL;
 1444         }
 1445 
 1446         item = add_item(list, arg->type, symbol, pname);
 1447         if (item != NULL && arg->type == D_subscript) {
 1448             NODE **subs;
 1449             int count = arg->a_count;
 1450             int i;
 1451 
 1452             assert(count > 0);
 1453             emalloc(subs, NODE **, count * sizeof(NODE *), "do_add_item");
 1454             for (i = 0; i < count; i++) {
 1455                 arg = arg->next;
 1456                 subs[i] = dupnode(arg->a_node);
 1457                 subs[i] = force_string(subs[i]);
 1458             }
 1459             item->subs = subs;
 1460             item->num_subs = count;
 1461         }
 1462         break;
 1463 
 1464     case D_field:
 1465         symbol = dupnode(arg->a_node);
 1466         item = add_item(list, D_field, symbol, NULL);
 1467         break;
 1468 
 1469     default:
 1470         break;
 1471     }
 1472 
 1473     /* watch condition if any */
 1474     if (list == &watch_list) {
 1475         arg = arg->next;
 1476         if (item != NULL && arg != NULL) {
 1477             if (parse_condition(D_watch, item->number, arg->a_string) == 0)
 1478                 arg->a_string = NULL;   /* don't let free_cmdarg free it */
 1479             else
 1480                 fprintf(out_fp, _("watchpoint %d is unconditional\n"), item->number);
 1481         }
 1482     }
 1483     return item;
 1484 }
 1485 
 1486 /* do_delete_item --- delete a watch/display item from list. */
 1487 
 1488 static void
 1489 do_delete_item(struct list_item *list, CMDARG *arg)
 1490 {
 1491     if (arg == NULL) {
 1492         while (list->next != list)
 1493             delete_item(list->next);
 1494     }
 1495 
 1496     for (; arg != NULL; arg = arg->next) {
 1497         struct list_item *d;
 1498         if (arg->type == D_range) {
 1499             long i, j;
 1500 
 1501             i = arg->a_int;
 1502             arg = arg->next;
 1503             j = arg->a_int;
 1504             if (j > list->number)
 1505                 j = list->number;
 1506             for (; i <= j; i++) {
 1507                 if ((d = find_item(list, i)) != NULL)
 1508                     delete_item(d);
 1509             }
 1510         } else {
 1511             if ((d = find_item(list, arg->a_int)) == NULL) {
 1512                 /* split into two for easier message translation */
 1513                 if (list == &display_list)
 1514                     d_error(_("No display item numbered %ld"),
 1515                         arg->a_int);
 1516                 else
 1517                     d_error(_("No watch item numbered %ld"),
 1518                         arg->a_int);
 1519             } else
 1520                 delete_item(d);
 1521         }
 1522     }
 1523 }
 1524 
 1525 /* display --- print an item from the auto-display list */
 1526 
 1527 static void
 1528 display(struct list_item *d)
 1529 {
 1530     NODE *symbol;
 1531 
 1532     symbol = d->symbol;
 1533     if (IS_PARAM(d) && (d->fcall_count != (fcall_count - cur_frame)))
 1534         return;
 1535 
 1536     if (IS_SUBSCRIPT(d)) {
 1537         NODE *sub, *r;
 1538         int i = 0, count = d->num_subs;
 1539         for (i = 0; i < count; i++) {
 1540             sub = d->subs[i];
 1541             r = in_array(symbol, sub);
 1542             if (r == NULL) {
 1543                 fprintf(out_fp, _("%d: [\"%.*s\"] not in array `%s'\n"),
 1544                             d->number, (int) sub->stlen, sub->stptr, d->sname);
 1545                 break;
 1546             }
 1547             if (r->type == Node_var_array) {
 1548                 symbol = r;
 1549                 if (i == count - 1) /* it's a sub-array */
 1550                     goto print_sym;     /* print # of elements in sub-array */
 1551             } else {
 1552                 if (i != count - 1)
 1553                     return;     /* FIXME msg and delete item ? */
 1554                 fprintf(out_fp, "%d: %s[\"%.*s\"] = ", d->number,
 1555                             d->sname, (int) sub->stlen, sub->stptr);
 1556                 valinfo(r, fprintf, out_fp);
 1557             }
 1558         }
 1559     } else if (IS_FIELD(d)) {
 1560         NODE *r = d->symbol;
 1561         fprintf(out_fp, "%d: ", d->number);
 1562         print_field(get_number_si(r));
 1563     } else {
 1564 print_sym:
 1565         fprintf(out_fp, "%d: %s = ", d->number, d->sname);
 1566         print_symbol(symbol, IS_PARAM(d));
 1567     }
 1568 }
 1569 
 1570 
 1571 /* do_display --- display command */
 1572 
 1573 int
 1574 do_display(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 1575 {
 1576     struct list_item *d;
 1577 
 1578     if (arg == NULL) {
 1579         /* display all items */
 1580         for (d = display_list.prev; d != &display_list; d = d->prev)
 1581             display(d);
 1582         return false;
 1583     }
 1584 
 1585     if ((d = do_add_item(&display_list, arg)) != NULL)
 1586         display(d);
 1587 
 1588     return false;
 1589 }
 1590 
 1591 /* do_undisplay --- undisplay command */
 1592 
 1593 int
 1594 do_undisplay(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 1595 {
 1596     do_delete_item(&display_list, arg);
 1597     return false;
 1598 }
 1599 
 1600 /* condition_triggered --- test if a condition expression is true */
 1601 
 1602 static int
 1603 condition_triggered(struct condition *cndn)
 1604 {
 1605     NODE *r;
 1606     int di;
 1607 
 1608     assert(cndn != NULL);
 1609     if (cndn->code == NULL)
 1610         return true;
 1611 
 1612     push_context(cndn->ctxt);
 1613     r = execute_code((volatile INSTRUCTION *) cndn->code);
 1614     pop_context();  /* switch to prev context */
 1615     if (r == NULL)      /* fatal error */
 1616         return false;   /* not triggered */
 1617 
 1618     force_number(r);
 1619     di = ! iszero(r);
 1620     DEREF(r);
 1621     return di;
 1622 }
 1623 
 1624 
 1625 static int
 1626 find_subscript(struct list_item *item, NODE **ptr)
 1627 {
 1628     NODE *symbol = item->symbol;
 1629     NODE *sub, *r;
 1630     int i = 0, count = item->num_subs;
 1631 
 1632     r = *ptr = NULL;
 1633     for (i = 0; i < count; i++) {
 1634         sub = item->subs[i];
 1635         r = in_array(symbol, sub);
 1636         if (r == NULL)
 1637             return 0;
 1638         if (r->type == Node_var_array)
 1639             symbol = r;
 1640         else if (i < count - 1)
 1641             return -1;
 1642     }
 1643     if (r != NULL)
 1644         *ptr = r;
 1645     return 0;
 1646 }
 1647 
 1648 /* cmp_val --- compare values of watched item, returns true if different; */
 1649 
 1650 static int
 1651 cmp_val(struct list_item *w, NODE *old, NODE *new)
 1652 {
 1653         /*
 1654          *  case    old     new     result
 1655          *  ------------------------------
 1656          *  1:      NULL    ARRAY   true
 1657          *  2:      NULL    SCALAR  true
 1658          *  3:      NULL    NULL    false
 1659          *  4:      SCALAR  SCALAR  cmp_node
 1660          *  5:      SCALAR  ARRAY   true
 1661          *  6:      SCALAR  NULL    true
 1662          *  7:      ARRAY   SCALAR  true
 1663          *  8:      ARRAY   ARRAY   compare size
 1664          *  9:      ARRAY   NULL    true
 1665          */
 1666 
 1667     if (WATCHING_ARRAY(w)) {
 1668         long size = 0;
 1669         if (! new)      /* 9 */
 1670             return true;
 1671         if (new->type == Node_val)  /* 7 */
 1672             return true;
 1673         /* new->type == Node_var_array */   /* 8 */
 1674         size = assoc_length(new);
 1675         if (w->cur_size == size)
 1676             return false;
 1677         return true;
 1678     }
 1679 
 1680     if (! old && ! new) /* 3 */
 1681         return false;
 1682     if ((! old && new)  /* 1, 2 */
 1683             || (old && ! new))  /* 6 */
 1684         return true;
 1685 
 1686     if (new->type == Node_var_array)    /* 5 */
 1687         return true;
 1688     return cmp_nodes(old, new, true);   /* 4 */
 1689 }
 1690 
 1691 /* watchpoint_triggered --- check if we should stop at this watchpoint;
 1692  *                          update old and current values accordingly.
 1693  */
 1694 
 1695 static int
 1696 watchpoint_triggered(struct list_item *w)
 1697 {
 1698     NODE *symbol;
 1699     NODE *t1, *t2;
 1700 
 1701     symbol = w->symbol;
 1702     if (IS_PARAM(w) && (w->fcall_count != (fcall_count - cur_frame)))
 1703         return 0; /* parameter with same name in a different function */
 1704     if (! condition_triggered(&w->cndn))
 1705         return 0;
 1706 
 1707     t1 = w->cur_value;
 1708     t2 = (NODE *) 0;
 1709     if (IS_SUBSCRIPT(w))
 1710         (void) find_subscript(w, &t2);
 1711     else if (IS_FIELD(w)) {
 1712         long field_num;
 1713         field_num = get_number_si(w->symbol);
 1714         t2 = *get_field(field_num, NULL);
 1715     } else {
 1716         switch (symbol->type) {
 1717         case Node_var:
 1718             t2 = symbol->var_value;
 1719             break;
 1720         case Node_var_array:
 1721             t2 = symbol;
 1722             break;
 1723         case Node_var_new:
 1724             break;
 1725         default:
 1726             cant_happen();
 1727         }
 1728     }
 1729 
 1730     if (! cmp_val(w, t1, t2))
 1731         return 0;
 1732 
 1733     /* update old and current values */
 1734 
 1735     if ((w->flags & OLD_IS_ARRAY) == 0)
 1736         unref(w->old_value);
 1737     w->flags &= ~OLD_IS_ARRAY;
 1738     if (WATCHING_ARRAY(w)) {    /* 7, 8, 9 */
 1739         w->old_size = w->cur_size;
 1740         w->flags |= OLD_IS_ARRAY;
 1741         if (! t2) {
 1742             w->flags &= ~CUR_IS_ARRAY;
 1743             w->cur_value = 0;
 1744         } else if (t2->type == Node_val) {
 1745             w->flags &= ~CUR_IS_ARRAY;
 1746             w->cur_value = dupnode(t2);
 1747         } else
 1748             w->cur_size = (t2->type == Node_var_array) ? assoc_length(t2) : 0;
 1749     } else if (! t1) { /* 1, 2 */
 1750         w->old_value = 0;
 1751         /* new != NULL */
 1752         if (t2->type == Node_val)
 1753             w->cur_value = dupnode(t2);
 1754         else {
 1755             w->flags |= CUR_IS_ARRAY;
 1756             w->cur_size = (t2->type == Node_var_array) ? assoc_length(t2) : 0;
 1757         }
 1758     } else /* if (t1->type == Node_val) */ {    /* 4, 5, 6 */
 1759         w->old_value = w->cur_value;
 1760         if (! t2)
 1761             w->cur_value = 0;
 1762         else if (t2->type == Node_var_array) {
 1763             w->flags |= CUR_IS_ARRAY;
 1764             w->cur_size = assoc_length(t2);
 1765         } else
 1766             w->cur_value = dupnode(t2);
 1767     }
 1768 
 1769     return w->number;
 1770 }
 1771 
 1772 /* initialize_watch_item --- initialize current value of a watched item */
 1773 
 1774 static int
 1775 initialize_watch_item(struct list_item *w)
 1776 {
 1777     NODE *t, *r;
 1778     NODE *symbol = w->symbol;
 1779 
 1780     if (IS_SUBSCRIPT(w)) {
 1781         if (find_subscript(w, &r) == -1) {
 1782             d_error(_("attempt to use scalar value as array"));
 1783             return -1;
 1784         }
 1785 
 1786         if (r == NULL)
 1787             w->cur_value = (NODE *) 0;
 1788         else if (r->type == Node_var_array) { /* it's a sub-array */
 1789             w->flags |= CUR_IS_ARRAY;
 1790             w->cur_size = assoc_length(r);
 1791         } else
 1792             w->cur_value = dupnode(r);
 1793     } else if (IS_FIELD(w)) {
 1794         long field_num;
 1795         t = w->symbol;
 1796         field_num = get_number_si(t);
 1797         r = *get_field(field_num, NULL);
 1798         w->cur_value = dupnode(r);
 1799     } else {
 1800         if (symbol->type == Node_var_new)
 1801             w->cur_value = (NODE *) 0;
 1802         else if (symbol->type == Node_var) {
 1803             r = symbol->var_value;
 1804             w->cur_value = dupnode(r);
 1805         } else if (symbol->type == Node_var_array) {
 1806             w->flags |= CUR_IS_ARRAY;
 1807             w->cur_size = assoc_length(symbol);
 1808         } else if (symbol->type == Node_val && (symbol->flags & REGEX) != 0) {
 1809             w->cur_value = dupnode(symbol);
 1810         } /* else
 1811             can't happen */
 1812     }
 1813     return 0;
 1814 }
 1815 
 1816 /* do_watch --- watch command */
 1817 
 1818 int
 1819 do_watch(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 1820 {
 1821     struct list_item *w;
 1822     NODE *symbol, *sub;
 1823     int i;
 1824 
 1825     w = do_add_item(&watch_list, arg);
 1826     if (w == NULL)
 1827         return false;
 1828 
 1829     if (initialize_watch_item(w) == -1) {
 1830         delete_item(w);
 1831         return false;
 1832     }
 1833 
 1834     fprintf(out_fp, "Watchpoint %d: ", w->number);
 1835     symbol = w->symbol;
 1836 
 1837     /* FIXME: common code also in print_watch_item */
 1838     if (IS_SUBSCRIPT(w)) {
 1839         fprintf(out_fp, "%s", w->sname);
 1840         for (i = 0; i < w->num_subs; i++) {
 1841             sub = w->subs[i];
 1842             fprintf(out_fp, "[\"%.*s\"]", (int) sub->stlen, sub->stptr);
 1843         }
 1844         fprintf(out_fp, "\n");
 1845     } else if (IS_FIELD(w))
 1846         fprintf(out_fp, "$%ld\n", get_number_si(symbol));
 1847     else
 1848         fprintf(out_fp, "%s\n", w->sname);
 1849 
 1850     return false;
 1851 }
 1852 
 1853 /* do_unwatch --- unwatch command */
 1854 
 1855 int
 1856 do_unwatch(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 1857 {
 1858     do_delete_item(&watch_list, arg);
 1859     return false;
 1860 }
 1861 
 1862 /* callback from pop_frame in eval.c */
 1863 
 1864 void
 1865 frame_popped()
 1866 {
 1867     struct list_item *item;
 1868 
 1869     /* delete all out of scope watchpoints */
 1870     for (item = watch_list.next; item != &watch_list; item = item->next) {
 1871         if (IS_PARAM(item) && (item->fcall_count > fcall_count)) {
 1872             fprintf(out_fp,
 1873                 _("Watchpoint %d deleted because parameter is out of scope.\n"),
 1874                 item->number);
 1875             item = item->prev;
 1876             delete_item(item->next);
 1877         }
 1878     }
 1879 
 1880     /* delete all out of scope display items */
 1881     for (item = display_list.next; item != &display_list; item = item->next) {
 1882         if (IS_PARAM(item) && (item->fcall_count > fcall_count)) {
 1883             fprintf(out_fp,
 1884                 _("Display %d deleted because parameter is out of scope.\n"),
 1885                 item->number);
 1886             item = item->prev;
 1887             delete_item(item->next);
 1888         }
 1889     }
 1890 }
 1891 
 1892 /* print_function --- print function name, parameters, and optionally
 1893  *                file and line number.
 1894  */
 1895 
 1896 static int
 1897 print_function(INSTRUCTION *pc, void *x)
 1898 {
 1899     NODE *func;
 1900     int i, pcount;
 1901     struct pf_data *data = (struct pf_data *) x;
 1902     int defn = data->defn;
 1903     Func_print print_func = data->print_func;
 1904     FILE *fp = data->fp;
 1905 
 1906     func = pc->func_body;
 1907     pcount = func->param_cnt;
 1908 
 1909     print_func(fp, "%s(", func->vname);
 1910     for (i = 0; i < pcount; i++) {
 1911         print_func(fp, "%s", func->fparms[i].param);
 1912         if (i < pcount - 1)
 1913             print_func(fp, ", ");
 1914     }
 1915     print_func(fp, ")");
 1916     if (defn)
 1917         print_func(fp, _(" in file `%s', line %d\n"),
 1918                 pc->source_file, pc->source_line);
 1919     return 0;
 1920 }
 1921 
 1922 /* print_frame --- print function name, parameters,
 1923  *                 source and line number of where it is
 1924  *                 executing.
 1925  */
 1926 
 1927 static void
 1928 print_frame(NODE *func, char *src, int srcline)
 1929 {
 1930     if (func == NULL)
 1931         fprintf(out_fp, "main()");
 1932     else {
 1933         pf_data.print_func = fprintf;
 1934         pf_data.fp = out_fp;
 1935         pf_data.defn = false;
 1936         (void) print_function(func->code_ptr, &pf_data);
 1937     }
 1938     fprintf(out_fp, _(" at `%s':%d"), src, srcline);
 1939 }
 1940 
 1941 /* print_numbered_frame --- print a frame given its number */
 1942 
 1943 static void
 1944 print_numbered_frame(long num)
 1945 {
 1946     NODE *f;
 1947 
 1948     assert(prog_running == true);
 1949     f = find_frame(num);
 1950     if (num == 0) {
 1951         fprintf(out_fp, "#%ld\t ", num);
 1952         print_frame(f->func_node, source, sourceline);
 1953     } else {
 1954         fprintf(out_fp, _("#%ld\tin "), num);
 1955         print_frame(f->func_node, f->vname,
 1956             ((INSTRUCTION *) find_frame(num - 1)->reti)->source_line);
 1957     }
 1958     fprintf(out_fp, "\n");
 1959 }
 1960 
 1961 /* do_backtrace --- backtrace command */
 1962 
 1963 int
 1964 do_backtrace(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 1965 {
 1966     long cur = 0;
 1967     long last = fcall_count;
 1968 
 1969     CHECK_PROG_RUNNING();
 1970     if (arg != NULL && arg->type == D_int) {
 1971         long count = arg->a_int;
 1972 
 1973         /* frame_ptr (frame #0),  fcall_list[1, 2, ... fcall_count] => total count */
 1974         if (count >= 0) {
 1975             /* toward outermost frame #fcall_count */
 1976             last = count - 1;
 1977             if (last > fcall_count)
 1978                  last = fcall_count;
 1979         } else {
 1980             /* toward innermost frame #0 */
 1981             cur = 1 + fcall_count + count;
 1982             if (cur < 0)
 1983                 cur = 0;
 1984         }
 1985     }
 1986 
 1987     for (; cur <= last; cur++) {
 1988         print_numbered_frame(cur);
 1989     }
 1990     if (cur <= fcall_count)
 1991         fprintf(out_fp, _("More stack frames follow ...\n"));
 1992     return false;
 1993 }
 1994 
 1995 /* print_cur_frame_and_sourceline --- print current frame, and
 1996  *                                    current source line.
 1997  */
 1998 
 1999 static void
 2000 print_cur_frame_and_sourceline()
 2001 {
 2002     NODE *f;
 2003     int srcline;
 2004     char *src;
 2005 
 2006     assert(prog_running == true);
 2007     f = find_frame(cur_frame);
 2008     if (cur_frame == 0) {
 2009         src = source;
 2010         srcline = sourceline;
 2011     } else {
 2012         f = find_frame(cur_frame);
 2013         src = f->vname;
 2014         srcline = ((INSTRUCTION *) find_frame(cur_frame - 1)->reti)->source_line;
 2015     }
 2016 
 2017     fprintf(out_fp, (cur_frame > 0 ? _("#%ld\tin ") : "#%ld\t "), cur_frame);
 2018     print_frame(f->func_node, src, srcline);
 2019     fprintf(out_fp, "\n");
 2020     print_lines(src, srcline, 1);
 2021     last_printed_line = srcline - list_size / 2;
 2022     if (last_printed_line < 0)
 2023         last_printed_line = 0;
 2024 }
 2025 
 2026 /* do_frame --- frame command */
 2027 
 2028 int
 2029 do_frame(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2030 {
 2031     CHECK_PROG_RUNNING();
 2032     if (arg && arg->type == D_int) {
 2033         if (arg->a_int < 0 || arg->a_int > fcall_count) {
 2034             d_error(_("invalid frame number"));
 2035             return false;
 2036         }
 2037         cur_frame = arg->a_int;
 2038     }
 2039     print_cur_frame_and_sourceline();
 2040     return false;
 2041 }
 2042 
 2043 /* do_up --- up command */
 2044 
 2045 int
 2046 do_up(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2047 {
 2048     CHECK_PROG_RUNNING();
 2049     if (arg != NULL && arg->type == D_int)
 2050         cur_frame += arg->a_int;
 2051     else
 2052         cur_frame++;
 2053     if (cur_frame < 0)
 2054         cur_frame = 0;
 2055     else if (cur_frame > fcall_count)
 2056         cur_frame = fcall_count;
 2057     print_cur_frame_and_sourceline();
 2058     return false;
 2059 }
 2060 
 2061 /* do_down --- down command */
 2062 
 2063 int
 2064 do_down(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2065 {
 2066     CHECK_PROG_RUNNING();
 2067     if (arg != NULL && arg->type == D_int)
 2068         cur_frame -= arg->a_int;
 2069     else
 2070         cur_frame--;
 2071     if (cur_frame < 0)
 2072         cur_frame = 0;
 2073     else if (cur_frame > fcall_count)
 2074         cur_frame = fcall_count;
 2075     print_cur_frame_and_sourceline();
 2076     return false;
 2077 }
 2078 
 2079 /* find_rule --- find a rule or function in file 'src' containing
 2080  *               source line 'lineno'
 2081  */
 2082 
 2083 static INSTRUCTION *
 2084 find_rule(char *src, long lineno)
 2085 {
 2086     INSTRUCTION *rp;
 2087 
 2088     /*
 2089      * FIXME: The check for zero and code that goes with it
 2090      * are probably fragile.  A break with no arguments can
 2091      * cause this in certain cases. Try to review how this works.
 2092      */
 2093     if (lineno == 0) {
 2094         for (rp = rule_list->nexti; rp != NULL; rp = rp->nexti) {
 2095             if ((rp - 1)->source_file == src && (rp - 1)->source_line > 0)
 2096                 return (rp - 1);
 2097         }
 2098     } else {
 2099         for (rp = rule_list->nexti; rp != NULL; rp = rp->nexti) {
 2100             if ((rp - 1)->source_file == src
 2101                     && lineno >= (rp + 1)->first_line
 2102                     && lineno <= (rp + 1)->last_line)
 2103                 return (rp - 1);
 2104         }
 2105     }
 2106     return NULL;
 2107 }
 2108 
 2109 /* mk_breakpoint --- create a breakpoint instruction and the corresponding
 2110  *                   breakpoint structure.
 2111  */
 2112 
 2113 static INSTRUCTION *
 2114 mk_breakpoint(char *src, int srcline)
 2115 {
 2116     INSTRUCTION *bp;
 2117     BREAKPOINT *b;
 2118 
 2119     bp = bcalloc(Op_breakpoint, 1, srcline);
 2120     emalloc(b, BREAKPOINT *, sizeof(BREAKPOINT), "mk_breakpoint");
 2121     memset(&b->cndn, 0, sizeof(struct condition));
 2122     b->commands.next = b->commands.prev = &b->commands;
 2123     b->silent = false;
 2124 
 2125 
 2126     b->number = ++watch_list.number;    /* breakpoints and watchpoints use same counter */
 2127     b->ignore_count = 0;
 2128     b->hit_count = 0;
 2129     b->flags = BP_ENABLE;
 2130     b->src = src;
 2131     bp->break_pt = b;
 2132     b->bpi = bp;
 2133 
 2134     /* prepend to list */
 2135     b->next = breakpoints.next;
 2136     b->prev = &breakpoints;
 2137     breakpoints.next = b;
 2138     b->next->prev = b;
 2139     return bp;
 2140 }
 2141 
 2142 /* delete_breakpoint --- delete a breakpoint structure and
 2143  *                       disable the breakpoint instruction.
 2144  */
 2145 
 2146 static void
 2147 delete_breakpoint(BREAKPOINT *b)
 2148 {
 2149     INSTRUCTION *pc = b->bpi;
 2150     struct commands_item *c;
 2151 
 2152     /* N.B.: easiest thing to do is to turn Op_breakpoint into a no-op;
 2153      * deleteing the instruction is not that simple,
 2154      * since could have reference to it somewhere else (e.g. cur_pc).
 2155      */
 2156 
 2157     pc->opcode = Op_no_op;
 2158     pc->source_line = 0;
 2159     pc->break_pt = NULL;
 2160 
 2161     /* delete commands */
 2162     for (c = b->commands.next; c != &b->commands; c = c->next) {
 2163         c = c->prev;
 2164         delete_commands_item(c->next);
 2165     }
 2166 
 2167     free_context(b->cndn.ctxt, false);
 2168     if (b->cndn.expr != NULL)
 2169         efree(b->cndn.expr);
 2170 
 2171     /* remove from list */
 2172     b->next->prev = b->prev;
 2173     b->prev->next = b->next;
 2174     efree(b);
 2175 }
 2176 
 2177 /* find_breakpoint --- find the breakpoint structure from a breakpoint number */
 2178 
 2179 static BREAKPOINT *
 2180 find_breakpoint(long num)
 2181 {
 2182     BREAKPOINT *b;
 2183 
 2184     if (num <= 0)
 2185         return NULL;
 2186 
 2187     for (b = breakpoints.next; b != &breakpoints; b = b->next) {
 2188         if (b->number == num)
 2189             return b;
 2190     }
 2191     return NULL;
 2192 }
 2193 
 2194 /* add_breakpoint --- add a breakpoint instruction between PREVP and IP */
 2195 
 2196 static BREAKPOINT *
 2197 add_breakpoint(INSTRUCTION *prevp, INSTRUCTION *ip, char *src, bool silent)
 2198 {
 2199     BREAKPOINT *b;
 2200     INSTRUCTION *bp;
 2201     int lineno = ip->source_line;
 2202 
 2203     /* add new breakpoint instruction at the end of
 2204      * already set breakpoints at this line number.
 2205      */
 2206 
 2207     while (ip->opcode == Op_breakpoint && ip->source_line == lineno) {
 2208         if (! silent) {
 2209             b = ip->break_pt;
 2210             /*
 2211              * This is more verbose that it might otherwise be,
 2212              * in order to provide easily translatable strings.
 2213              */
 2214             if ((b->flags & BP_ENABLE) != 0) {
 2215                 if ((b->flags & BP_IGNORE) != 0)
 2216                     fprintf(out_fp,
 2217             _("Note: breakpoint %d (enabled, ignore next %ld hits), also set at %s:%d"),
 2218                         b->number,
 2219                         b->ignore_count,
 2220                         b->src,
 2221                         lineno);
 2222                 else
 2223                     fprintf(out_fp,
 2224             _("Note: breakpoint %d (enabled), also set at %s:%d"),
 2225                         b->number,
 2226                         b->src,
 2227                         lineno);
 2228             } else {
 2229                 if ((b->flags & BP_IGNORE) != 0)
 2230                     fprintf(out_fp,
 2231             _("Note: breakpoint %d (disabled, ignore next %ld hits), also set at %s:%d"),
 2232                         b->number,
 2233                         b->ignore_count,
 2234                         b->src,
 2235                         lineno);
 2236                 else
 2237                     fprintf(out_fp,
 2238             _("Note: breakpoint %d (disabled), also set at %s:%d"),
 2239                         b->number,
 2240                         b->src,
 2241                         lineno);
 2242             }
 2243         }
 2244         prevp = ip;
 2245         ip = ip->nexti;
 2246     }
 2247 
 2248     assert(ip->source_line == lineno);
 2249 
 2250     bp = mk_breakpoint(src, lineno);
 2251     prevp->nexti = bp;
 2252     bp->nexti = ip;
 2253     b = bp->break_pt;
 2254     if (! silent)
 2255         fprintf(out_fp, _("Breakpoint %d set at file `%s', line %d\n"),
 2256                         b->number, src, lineno);
 2257     return b;
 2258 }
 2259 
 2260 /* set_breakpoint_at --- set a breakpoint at given line number*/
 2261 
 2262 static BREAKPOINT *
 2263 set_breakpoint_at(INSTRUCTION *rp, int lineno, bool silent)
 2264 {
 2265     INSTRUCTION *ip, *prevp;
 2266 
 2267     for (prevp = rp, ip = rp->nexti; ip; prevp = ip, ip = ip->nexti) {
 2268         if (ip->opcode == Op_K_case) {
 2269             INSTRUCTION *i1, *i2;
 2270 
 2271             /* Special case: the code line numbers for a switch do not form
 2272              * a monotonically increasing sequence. Check if the line # is between
 2273              * the first and last statements of the case block before continuing
 2274              * the search.
 2275              */
 2276             for (i2 = ip->stmt_start, i1 = i2->nexti; i2 != ip->stmt_end;
 2277                                 i2 = i1, i1 = i1->nexti) {
 2278                 if (i1->source_line >= lineno)
 2279                     return add_breakpoint(i2, i1, rp->source_file, silent);
 2280                 if (i1 == ip->stmt_end)
 2281                     break;
 2282             }
 2283         }
 2284 
 2285         if (ip->source_line >= lineno)
 2286             return add_breakpoint(prevp, ip, rp->source_file, silent);
 2287         if (ip == (rp + 1)->lasti)
 2288             break;
 2289     }
 2290     return NULL;
 2291 }
 2292 
 2293 /* set_breakpoint_next --- set a breakpoint at the next instruction */
 2294 
 2295 static BREAKPOINT *
 2296 set_breakpoint_next(INSTRUCTION *rp, INSTRUCTION *ip)
 2297 {
 2298     INSTRUCTION *prevp;
 2299 
 2300     if (ip == (rp + 1)->lasti)
 2301         return NULL;
 2302     prevp = ip;
 2303     if (ip->opcode != Op_breakpoint)
 2304         ip = ip->nexti;
 2305     for (; ip; prevp = ip, ip = ip->nexti) {
 2306         if (ip->source_line > 0)
 2307             return add_breakpoint(prevp, ip, rp->source_file, false);
 2308         if (ip == (rp + 1)->lasti)
 2309             break;
 2310     }
 2311     return NULL;
 2312 }
 2313 
 2314 /* set_breakpoint --- set a breakpoint */
 2315 
 2316 static int
 2317 set_breakpoint(CMDARG *arg, bool temporary)
 2318 {
 2319     int lineno;
 2320     BREAKPOINT *b = NULL;
 2321     INSTRUCTION *rp, *ip;
 2322     NODE *func;
 2323     SRCFILE *s = cur_srcfile;
 2324     char *src = cur_srcfile->src;
 2325 
 2326     if (arg == NULL) {
 2327 /*
 2328 * (From GDB Documentation):
 2329 *
 2330 * When called without any arguments, break sets a breakpoint at the next instruction
 2331 * to be executed in the selected stack frame (see section Examining the Stack).
 2332 * In any selected frame but the innermost, this makes your program stop as soon
 2333 * as control returns to that frame. This is similar to the effect of a finish command
 2334 * in the frame inside the selected frame--except that finish does not leave an
 2335 * active breakpoint. If you use break without an argument in the innermost frame,
 2336 * GDB stops the next time it reaches the current location; this may be useful
 2337 * inside loops.
 2338 * GDB normally ignores breakpoints when it resumes execution, until at least
 2339 * one instruction has been executed. If it did not do this,
 2340 * you would be unable to proceed past a breakpoint without first disabling the
 2341 * breakpoint. This rule applies whether or not the breakpoint already existed
 2342 * when your program stopped.
 2343 */
 2344         CHECK_PROG_RUNNING();
 2345         if (cur_frame == 0) {
 2346             src = source;
 2347             ip = cur_pc;
 2348         } else {
 2349             NODE *f;
 2350             f = find_frame(cur_frame);
 2351             src = f->vname;
 2352             ip = (INSTRUCTION *) find_frame(cur_frame - 1)->reti;  /* Op_func_call */
 2353         }
 2354         rp = find_rule(src, ip->source_line);
 2355         assert(rp != NULL);
 2356         if ((b = set_breakpoint_next(rp, ip)) == NULL)
 2357             fprintf(out_fp, _("cannot set breakpoint in file `%s'\n"), src);
 2358         else {
 2359             if (cur_frame == 0) {   /* stop next time */
 2360                 b->flags |= BP_IGNORE;
 2361                 b->ignore_count = 1;
 2362             }
 2363             if (temporary)
 2364                 b->flags |= BP_TEMP;
 2365         }
 2366         return false;
 2367     }
 2368 
 2369     /* arg != NULL */
 2370 
 2371     switch (arg->type) {
 2372     case D_string:  /* break filename:lineno|function */
 2373         s = source_find(arg->a_string);
 2374         arg = arg->next;
 2375         if (s == NULL || arg == NULL
 2376                 || (arg->type != D_int && arg->type != D_func))
 2377             return false;
 2378         src = s->src;
 2379         if (arg->type == D_func) /* break filename:function */
 2380             goto func;
 2381         else
 2382             /* fall through */
 2383     case D_int:     /* break lineno */
 2384         lineno = (int) arg->a_int;
 2385         if (lineno <= 0 || lineno > s->srclines)
 2386             d_error(_("line number %d in file `%s' out of range"), lineno, src);
 2387         else {
 2388             rp = find_rule(src, lineno);
 2389             if (rp == NULL)
 2390                 fprintf(out_fp, _("internal error: cannot find rule\n"));
 2391             if (rp == NULL || (b = set_breakpoint_at(rp, lineno, false)) == NULL)
 2392                 fprintf(out_fp, _("cannot set breakpoint at `%s':%d\n"),
 2393                         src, lineno);
 2394             if (b != NULL && temporary)
 2395                 b->flags |= BP_TEMP;
 2396         }
 2397         break;
 2398 
 2399     case D_func:    /* break function */
 2400 func:
 2401         func = arg->a_node;
 2402         rp = func->code_ptr;
 2403         if ((b = set_breakpoint_at(rp, rp->source_line, false)) == NULL)
 2404             fprintf(out_fp, _("cannot set breakpoint in function `%s'\n"),
 2405                         func->vname);
 2406         else if (temporary)
 2407             b->flags |= BP_TEMP;
 2408         lineno = b->bpi->source_line;
 2409         break;
 2410 
 2411     default:
 2412         return false;
 2413     }
 2414     /* condition if any */
 2415     arg = arg->next;
 2416     if (b != NULL && arg != NULL) {
 2417         if (parse_condition(D_break, b->number, arg->a_string) == 0)
 2418             arg->a_string = NULL;   /* don't let free_cmdarg free it */
 2419         else
 2420             fprintf(out_fp, _("breakpoint %d set at file `%s', line %d is unconditional\n"),
 2421                             b->number, src, lineno);
 2422     }
 2423     return false;
 2424 }
 2425 
 2426 
 2427 /* breakpoint_triggered --- check if we should stop at this breakpoint */
 2428 
 2429 static int
 2430 breakpoint_triggered(BREAKPOINT *b)
 2431 {
 2432     if ((b->flags & BP_ENABLE) == 0)
 2433         return 0;
 2434     if ((b->flags & BP_IGNORE) != 0) {
 2435         if (--b->ignore_count <= 0)
 2436             b->flags &= ~BP_IGNORE;
 2437         return 0;
 2438     }
 2439 
 2440     if (! condition_triggered(&b->cndn))
 2441         return 0;
 2442 
 2443     b->hit_count++;
 2444     if ((b->flags & BP_ENABLE_ONCE) != 0) {
 2445         b->flags &= ~BP_ENABLE_ONCE;
 2446         b->flags &= ~BP_ENABLE;
 2447     }
 2448     return b->number;
 2449 }
 2450 
 2451 /* do_breakpoint --- break command */
 2452 
 2453 int
 2454 do_breakpoint(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2455 {
 2456     return set_breakpoint(arg, false);
 2457 }
 2458 
 2459 /* do_tmp_breakpoint --- tbreak command */
 2460 
 2461 int
 2462 do_tmp_breakpoint(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2463 {
 2464     return set_breakpoint(arg, true);
 2465 }
 2466 
 2467 /* do_clear --- clear command */
 2468 
 2469 int
 2470 do_clear(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2471 {
 2472     int lineno;
 2473     BREAKPOINT *b;
 2474     INSTRUCTION *rp, *ip;
 2475     NODE *func;
 2476     SRCFILE *s = cur_srcfile;
 2477     char *src = cur_srcfile->src;
 2478     int bp_found = 0;
 2479 
 2480     if (arg == NULL) {  /* clear */
 2481         CHECK_PROG_RUNNING();
 2482         if (cur_frame == 0) {
 2483             lineno = sourceline;
 2484             src = source;
 2485         } else {
 2486             NODE *f;
 2487             f = find_frame(cur_frame);
 2488             src = f->vname;
 2489             lineno = ((INSTRUCTION *) find_frame(cur_frame - 1)->reti)->source_line;
 2490         }
 2491         goto delete_bp;
 2492     }
 2493 
 2494     switch (arg->type) {
 2495     case D_string:  /* clear filename:lineno|function */
 2496         s = source_find(arg->a_string);
 2497         arg = arg->next;
 2498         if (s == NULL || arg == NULL ||
 2499                 (arg->type != D_int && arg->type != D_func))
 2500             return false;
 2501         src = s->src;
 2502         if (arg->type == D_func)
 2503             goto func;
 2504         /* else
 2505             fall through */
 2506     case D_int: /* clear lineno */
 2507         lineno = (int) arg->a_int;
 2508         if (lineno <= 0 || lineno > s->srclines) {
 2509             d_error(_("line number %d in file `%s' out of range"), lineno, src);
 2510             return false;
 2511         }
 2512         break;
 2513 
 2514     case D_func:    /* clear function */
 2515 func:
 2516         func = arg->a_node;
 2517         rp = func->code_ptr;
 2518         for (ip = rp->nexti; ip; ip = ip->nexti) {
 2519             if (ip->source_line <= 0)
 2520                 continue;
 2521             if (ip->opcode != Op_breakpoint)
 2522                 break;
 2523             b = ip->break_pt;
 2524             if (++bp_found == 1)
 2525                 fprintf(out_fp, _("Deleted breakpoint %d"), b->number);
 2526             else
 2527                 fprintf(out_fp, ", %d", b->number);
 2528             delete_breakpoint(b);
 2529         }
 2530         if (bp_found == 0)
 2531             fprintf(out_fp, _("No breakpoint(s) at entry to function `%s'\n"),
 2532                     func->vname);
 2533         else
 2534             fprintf(out_fp, "\n");
 2535         /* fall through */
 2536     default:
 2537         return false;
 2538     }
 2539 
 2540 delete_bp:
 2541     rp = find_rule(src, lineno);
 2542     if (rp != NULL) {
 2543         for (ip = rp->nexti; ip; ip = ip->nexti) {
 2544             if (ip->opcode == Op_breakpoint && ip->source_line == lineno) {
 2545                 b = ip->break_pt;
 2546                 if (++bp_found == 1)
 2547                     fprintf(out_fp, _("Deleted breakpoint %d"), b->number);
 2548                 else
 2549                     fprintf(out_fp, ", %d", b->number);
 2550                 delete_breakpoint(b);
 2551             }
 2552             if (ip == (rp + 1)->lasti)
 2553                 break;
 2554         }
 2555     }
 2556 
 2557     if (bp_found == 0)
 2558         fprintf(out_fp, _("No breakpoint at file `%s', line #%d\n"),
 2559                     src, (int) lineno);
 2560     else
 2561         fprintf(out_fp, "\n");
 2562     return false;
 2563 }
 2564 
 2565 /* enable_breakpoint --- enable a breakpoint and set its disposition */
 2566 
 2567 static inline void
 2568 enable_breakpoint(BREAKPOINT *b, short disp)
 2569 {
 2570     b->flags &= ~(BP_ENABLE_ONCE|BP_TEMP);
 2571     b->flags |= BP_ENABLE;
 2572     if (disp)
 2573         b->flags |= disp;
 2574 }
 2575 
 2576 /* do_enable_breakpoint --- enable command */
 2577 
 2578 int
 2579 do_enable_breakpoint(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2580 {
 2581     BREAKPOINT *b;
 2582     short disp = 0;
 2583 
 2584     if (arg != NULL && arg->type == D_argument) {
 2585         if (arg->a_argument == A_DEL)   /* del */
 2586             disp = BP_TEMP;
 2587         else                        /* once */
 2588             disp = BP_ENABLE_ONCE;
 2589         arg = arg->next;
 2590     }
 2591 
 2592     if (arg == NULL) {  /* enable [once|del] */
 2593         for (b = breakpoints.next; b != &breakpoints; b = b->next)
 2594             enable_breakpoint(b, disp);
 2595     }
 2596 
 2597     for (; arg != NULL; arg = arg->next) {
 2598         if (arg->type == D_range) {
 2599             long i, j;
 2600 
 2601             i = arg->a_int;
 2602             arg = arg->next;
 2603             j = arg->a_int;
 2604             if (j > breakpoints.number)
 2605                 j = breakpoints.number;
 2606             for (; i <= j; i++) {
 2607                 if ((b = find_breakpoint(i)) != NULL)
 2608                     enable_breakpoint(b, disp);
 2609             }
 2610         } else {
 2611             assert(arg->type == D_int);
 2612             if ((b = find_breakpoint(arg->a_int)) == NULL)
 2613                 d_error(_("invalid breakpoint number"));
 2614             else
 2615                 enable_breakpoint(b, disp);
 2616         }
 2617     }
 2618     return false;
 2619 }
 2620 
 2621 /* do_delete_breakpoint --- delete command */
 2622 
 2623 int
 2624 do_delete_breakpoint(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2625 {
 2626     if (arg == NULL) {
 2627         bool delete_all = true;
 2628         delete_all = prompt_yes_no(
 2629                     _("Delete all breakpoints? (y or n) "),
 2630                     _("y")[0], true, out_fp);
 2631 
 2632         if (delete_all) {
 2633             while (breakpoints.next != &breakpoints)
 2634                 delete_breakpoint(breakpoints.next);
 2635         }
 2636     }
 2637 
 2638     for (; arg != NULL; arg = arg->next) {
 2639         BREAKPOINT *b;
 2640         if (arg->type == D_range) {
 2641             long i, j;
 2642 
 2643             i = arg->a_int;
 2644             arg = arg->next;
 2645             j = arg->a_int;
 2646             if (j > breakpoints.number)
 2647                 j = breakpoints.number;
 2648             for (; i <= j; i++) {
 2649                 if ((b = find_breakpoint(i)) != NULL)
 2650                     delete_breakpoint(b);
 2651             }
 2652         } else {
 2653             if ((b = find_breakpoint(arg->a_int)) == NULL)
 2654                 d_error(_("invalid breakpoint number"));
 2655             else
 2656                 delete_breakpoint(b);
 2657         }
 2658     }
 2659     return false;
 2660 }
 2661 
 2662 /* do_ignore_breakpoint --- ignore command */
 2663 
 2664 int
 2665 do_ignore_breakpoint(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2666 {
 2667     BREAKPOINT *b;
 2668 
 2669     if (arg == NULL || arg->type != D_int
 2670             || arg->next == NULL || arg->next->type != D_int)
 2671         return false;
 2672 
 2673     if ((b = find_breakpoint(arg->a_int)) == NULL)
 2674         d_error(_("invalid breakpoint number"));
 2675     else {
 2676         b->ignore_count = arg->next->a_int;
 2677         if (b->ignore_count > 0) {
 2678             b->flags |= BP_IGNORE;
 2679             fprintf(out_fp, _("Will ignore next %ld crossing(s) of breakpoint %d.\n"),
 2680                     b->ignore_count, b->number);
 2681         } else {
 2682             b->flags &= ~BP_IGNORE;
 2683             fprintf(out_fp, _("Will stop next time breakpoint %d is reached.\n"),
 2684                     b->number);
 2685         }
 2686     }
 2687     return false;
 2688 }
 2689 
 2690 /* do_disable_breakpoint --- disable command */
 2691 
 2692 int
 2693 do_disable_breakpoint(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 2694 {
 2695     BREAKPOINT *b;
 2696 
 2697     if (arg == NULL) {
 2698         /* disable all */
 2699         for (b = breakpoints.next; b != &breakpoints; b = b->next)
 2700             b->flags &= ~BP_ENABLE;
 2701     }
 2702 
 2703     for (; arg != NULL; arg = arg->next) {
 2704         if (arg->type == D_range) {
 2705             long i, j;
 2706 
 2707             i = arg->a_int;
 2708             arg = arg->next;
 2709             j = arg->a_int;
 2710             if (j > breakpoints.number)
 2711                 j = breakpoints.number;
 2712             for (; i <= j; i++)
 2713                 if ((b = find_breakpoint(i)) != NULL)
 2714                     b->flags &= ~BP_ENABLE;
 2715         } else {
 2716             if ((b = find_breakpoint(arg->a_int)) == NULL)
 2717                 d_error(_("invalid breakpoint number"));
 2718             else
 2719                 b->flags &= ~BP_ENABLE;
 2720         }
 2721     }
 2722     return false;
 2723 }
 2724 
 2725 #ifdef HAVE_LIBREADLINE
 2726 
 2727 /* get_function --- function definition in current context */
 2728 
 2729 NODE *
 2730 get_function()
 2731 {
 2732     NODE *func;
 2733 
 2734     if (! prog_running)
 2735         return NULL;
 2736     func = find_frame(cur_frame)->func_node;
 2737     return func;
 2738 }
 2739 
 2740 /* initialize_readline --- initialize readline */
 2741 
 2742 static void
 2743 initialize_readline()
 2744 {
 2745     /* tell readline which stream to use for output,
 2746      * default input stream is stdin.
 2747      */
 2748     rl_outstream = out_fp;
 2749 
 2750     /* allow conditional parsing of the ~/.inputrc file. */
 2751     rl_readline_name = "gawk";
 2752 
 2753     /* our completion function. */
 2754     rl_attempted_completion_function = command_completion;
 2755 
 2756     read_a_line = readline;
 2757 }
 2758 #else
 2759 #define initialize_readline()   /* nothing */
 2760 #endif
 2761 
 2762 
 2763 /* init_debug --- register debugger exec hooks */
 2764 
 2765 void
 2766 init_debug()
 2767 {
 2768     register_exec_hook(debug_pre_execute, debug_post_execute);
 2769 }
 2770 
 2771 
 2772 /* debug_prog --- debugger entry point */
 2773 
 2774 int
 2775 debug_prog(INSTRUCTION *pc)
 2776 {
 2777     char *run;
 2778 
 2779     input_fd = fileno(stdin);
 2780     out_fp = stdout;
 2781     if (os_isatty(input_fd))
 2782         input_from_tty = true;
 2783     if (input_fd == 0 && input_from_tty)
 2784         initialize_readline();
 2785 
 2786     if (! read_a_line)
 2787         read_a_line = g_readline;
 2788 
 2789     push_cmd_src(input_fd, input_from_tty, read_a_line, 0, 0, EXIT_FATAL);
 2790 
 2791     setbuf(out_fp, (char *) NULL);
 2792     for (cur_srcfile = srcfiles->prev; cur_srcfile != srcfiles;
 2793             cur_srcfile = cur_srcfile->prev) {
 2794         if (cur_srcfile->stype == SRC_FILE
 2795             || cur_srcfile->stype == SRC_INC)
 2796             break;
 2797     }
 2798 
 2799     if (cur_srcfile == srcfiles) {
 2800         fprintf(out_fp, _("Can only debug programs provided with the `-f' option.\n"));
 2801         exit(EXIT_FAILURE);
 2802     }
 2803 
 2804     dgawk_prompt = estrdup(DEFAULT_PROMPT, strlen(DEFAULT_PROMPT));
 2805     dbg_prompt = dgawk_prompt;
 2806 
 2807     memset(&stop, 0, sizeof(stop));
 2808     stop.command = D_illegal;
 2809 
 2810     if ((run = getenv("DGAWK_RESTART")) != NULL) {
 2811         /* We are restarting; restore state (breakpoints, history etc.)
 2812          * passed as environment variables and optionally execute the run command.
 2813          */
 2814         unserialize_list(BREAK);
 2815         unserialize_list(WATCH);
 2816         unserialize_list(DISPLAY);
 2817         unserialize_list(HISTORY);
 2818         unserialize_list(OPTION);
 2819         unsetenv("DGAWK_RESTART");
 2820         fprintf(out_fp, "Restarting ...\n");
 2821         if (strcasecmp(run, "true") == 0)
 2822             (void) do_run(NULL, 0);
 2823 
 2824     } else if (command_file != NULL) {
 2825         /* run commands from a file (--debug=file  or -D file) */
 2826         int fd;
 2827         fd = open_readfd(command_file);
 2828         if (fd == INVALID_HANDLE) {
 2829             fprintf(stderr, _("cannot open source file `%s' for reading: %s"),
 2830                         command_file, strerror(errno));
 2831             exit(EXIT_FAILURE);
 2832         }
 2833         push_cmd_src(fd, false, g_readline, close, 0, EXIT_FAILURE);
 2834         cmd_src->str = estrdup(command_file, strlen(command_file));
 2835 
 2836     } else {
 2837         int fd;
 2838 
 2839 #ifdef HAVE_LIBREADLINE
 2840         (void) read_history(history_file);
 2841         sess_history_base = history_length;
 2842 #endif
 2843 
 2844         /* read saved options */
 2845         fd = open_readfd(options_file);
 2846         if (fd > INVALID_HANDLE)
 2847             push_cmd_src(fd, false, g_readline, close, 0, EXIT_SUCCESS);
 2848     }
 2849 
 2850     /* start the command interpreter */
 2851     read_command(); /* yyparse */
 2852     return EXIT_SUCCESS;
 2853 }
 2854 
 2855 
 2856 /* N.B.: ignore breakpoints and watchpoints for return command */
 2857 
 2858 /* check_watchpoint --- check if any watchpoint triggered */
 2859 
 2860 static int
 2861 check_watchpoint()
 2862 {
 2863     struct list_item *w;
 2864 
 2865     if (stop.command == D_return)
 2866         return false;
 2867     for (w = watch_list.prev; w != &watch_list; w = w->prev) {
 2868         int wnum = watchpoint_triggered(w);
 2869         if (wnum > 0) {
 2870             stop.watch_point = wnum;
 2871             stop.print_frame = true;
 2872             return true;
 2873         }
 2874     }
 2875     return false;
 2876 }
 2877 
 2878 /* check_breakpoint --- check if breakpoint triggered */
 2879 
 2880 static int
 2881 check_breakpoint(INSTRUCTION **pi)
 2882 {
 2883     INSTRUCTION *pc;
 2884 
 2885     pc = *pi;
 2886     if (stop.command == D_return)
 2887         return false;
 2888     if (pc->opcode == Op_breakpoint) {
 2889         int bnum;
 2890         *pi = pc->nexti;    /* skip past the breakpoint instruction;
 2891                              * interpreter doesn't process Op_breakpoint.
 2892                              */
 2893         bnum = breakpoint_triggered(pc->break_pt);
 2894         if (bnum > 0) {
 2895             stop.break_point = bnum;
 2896             stop.print_frame = true;
 2897             return true;
 2898         }
 2899     }
 2900     return false;
 2901 }
 2902 
 2903 /* restart --- restart the debugger */
 2904 
 2905 static void
 2906 restart(bool run)
 2907 {
 2908     /* save state in the environment after serialization */
 2909     serialize_list(BREAK);
 2910     serialize_list(WATCH);
 2911     serialize_list(DISPLAY);
 2912     serialize_list(HISTORY);
 2913     serialize_list(OPTION);
 2914 
 2915     /* tell the new process to restore state from the environment */
 2916     setenv("DGAWK_RESTART", (run ? "true" : "false"), 1);
 2917 
 2918     /* close all open files */
 2919     close_all();
 2920 
 2921     /* start a new process replacing the current process */
 2922     execvp(d_argv[0], d_argv);
 2923 
 2924     /* execvp failed !!! */
 2925     fprintf(out_fp, _("Failed to restart debugger"));
 2926     exit(EXIT_FAILURE);
 2927 }
 2928 
 2929 /* do_run --- run command */
 2930 
 2931 int
 2932 do_run(CMDARG *arg ATTRIBUTE_UNUSED, int cmd ATTRIBUTE_UNUSED)
 2933 {
 2934     if (prog_running) {
 2935         if (! input_from_tty)
 2936             need_restart = true;    /* handled later */
 2937         else {
 2938             need_restart = prompt_yes_no(
 2939                      _("Program already running. Restart from beginning (y/n)? "),
 2940                      _("y")[0], false, out_fp);
 2941 
 2942             if (! need_restart) {
 2943                 fprintf(out_fp, _("Program not restarted\n"));
 2944                 return false;
 2945             }
 2946         }
 2947     }
 2948 
 2949     if (need_restart) {
 2950         /* avoid endless cycles of restarting */
 2951         if (command_file != NULL) {
 2952             /* input_from_tty = false */
 2953             fprintf(stderr, _("error: cannot restart, operation not allowed\n"));
 2954             exit(EXIT_FAILURE);
 2955         }
 2956 
 2957         if (cmd_src->cmd == D_source) {
 2958             /* input_from_tty = false */
 2959             fprintf(out_fp, _("error (%s): cannot restart, ignoring rest of the commands\n"), cmd_src->str);
 2960             pop_cmd_src();
 2961             return false;
 2962         }
 2963 
 2964         restart(true);  /* does not return */
 2965     }
 2966 
 2967     fprintf(out_fp, _("Starting program: \n"));
 2968 
 2969     prog_running = true;
 2970     fatal_tag_valid = 1;
 2971     if (setjmp(fatal_tag) == 0)
 2972         (void) interpret(code_block);
 2973 
 2974     fatal_tag_valid = 0;
 2975     prog_running = false;
 2976     fprintf(out_fp, (! exiting && exit_val != EXIT_SUCCESS)
 2977                 ? _("Program exited abnormally with exit value: %d\n")
 2978                 : _("Program exited normally with exit value: %d\n"),
 2979             exit_val);
 2980     need_restart = true;
 2981     return false;
 2982 }
 2983 
 2984 /* do_quit --- quit command */
 2985 
 2986 int
 2987 do_quit(CMDARG *arg ATTRIBUTE_UNUSED, int cmd ATTRIBUTE_UNUSED)
 2988 {
 2989     bool terminate = true;
 2990     if (prog_running)
 2991         terminate = prompt_yes_no(
 2992                     _("The program is running. Exit anyway (y/n)? "),
 2993                     _("y")[0], true, out_fp);
 2994     if (terminate) {
 2995         close_all();
 2996         do_trace = false;   /* don't save 'trace on' */
 2997 
 2998 #ifdef HAVE_LIBREADLINE
 2999         if (do_save_history && input_from_tty) {
 3000             int ret;
 3001             ret = write_history(history_file);
 3002             if (ret == 0 && history_length > history_size)
 3003                 (void) history_truncate_file(history_file, history_size);
 3004         }
 3005 #endif
 3006         if (do_save_options && input_from_tty)
 3007             save_options(options_file);
 3008 
 3009         exit(exit_val);
 3010     }
 3011     return false;
 3012 }
 3013 
 3014 /* do_continue --- continue command */
 3015 
 3016 int
 3017 do_continue(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 3018 {
 3019     BREAKPOINT *b;
 3020 
 3021     CHECK_PROG_RUNNING();
 3022     if (! arg || arg->type != D_int)
 3023         return true;
 3024 
 3025     /* arg is breakpoint ignore count if stopped at a breakpoint */
 3026     if (! stop.break_point) {
 3027         fprintf(out_fp, _("Not stopped at any breakpoint; argument ignored.\n"));
 3028         return true;
 3029     }
 3030     b = find_breakpoint(stop.break_point);
 3031     if (b == NULL) {
 3032         d_error(_("invalid breakpoint number %d."), stop.break_point);
 3033         return false;
 3034     }
 3035     b->flags |= BP_IGNORE;
 3036     b->ignore_count = arg->a_int;
 3037     fprintf(out_fp, _("Will ignore next %ld crossings of breakpoint %d.\n"),
 3038                 b->ignore_count, stop.break_point);
 3039     return true;
 3040 }
 3041 
 3042 /* next_step --- common code for next and step commands */
 3043 
 3044 static int
 3045 next_step(CMDARG *arg, int cmd)
 3046 {
 3047     CHECK_PROG_RUNNING();
 3048     if (arg != NULL && arg->type == D_int)
 3049         stop.repeat_count = arg->a_int;
 3050     else
 3051         stop.repeat_count = 1;
 3052     stop.command = cmd;
 3053     return true;
 3054 }
 3055 
 3056 /* check_step --- process step command, return true if stopping */
 3057 
 3058 static int
 3059 check_step(INSTRUCTION **pi)
 3060 {
 3061     if (fcall_count != stop.fcall_count) {
 3062         stop.fcall_count = fcall_count;
 3063         stop.sourceline = sourceline;
 3064         stop.source = source;
 3065         stop.print_frame = true;
 3066         return (--stop.repeat_count == 0);
 3067     }
 3068 
 3069     if (source != stop.source) {
 3070         stop.source = source;
 3071         stop.sourceline = sourceline;
 3072         return (--stop.repeat_count == 0);
 3073     }
 3074 
 3075     if (sourceline != stop.sourceline) {
 3076         stop.sourceline = sourceline;
 3077         return (--stop.repeat_count == 0);
 3078     }
 3079     return false;
 3080 }
 3081 
 3082 /* do_step -- process step command, return true if stopping */
 3083 
 3084 int
 3085 do_step(CMDARG *arg, int cmd)
 3086 {
 3087     int ret;
 3088     ret = next_step(arg, cmd);
 3089     if (ret) {
 3090         stop.fcall_count = fcall_count;
 3091         stop.source = source;
 3092         stop.sourceline = sourceline;
 3093         stop.check_func = check_step;
 3094     }
 3095     return ret;
 3096 }
 3097 
 3098 /* do_stepi -- process stepi command, return true if stopping */
 3099 
 3100 static int
 3101 check_stepi(INSTRUCTION **pi)
 3102 {
 3103     return (--stop.repeat_count == 0);
 3104 }
 3105 
 3106 /* do_stepi -- stepi command */
 3107 
 3108 int
 3109 do_stepi(CMDARG *arg, int cmd)
 3110 {
 3111     int ret;
 3112     ret = next_step(arg, cmd);
 3113     if (ret)
 3114         stop.check_func = check_stepi;
 3115     return ret;
 3116 }
 3117 
 3118 
 3119 /* check_next -- process next command returning true if stopping */
 3120 
 3121 static int
 3122 check_next(INSTRUCTION **pi)
 3123 {
 3124     /* make sure not to step inside function calls */
 3125 
 3126     if (fcall_count < stop.fcall_count) {
 3127         stop.fcall_count = fcall_count;
 3128         stop.sourceline = sourceline;
 3129         stop.source = source;
 3130         stop.print_frame = true;
 3131         return (--stop.repeat_count == 0);
 3132     }
 3133 
 3134     if (fcall_count == stop.fcall_count) {
 3135         if (source != stop.source) {
 3136             stop.source = source;
 3137             stop.sourceline = sourceline;
 3138             return (--stop.repeat_count == 0);
 3139         }
 3140         if (sourceline != stop.sourceline) {
 3141             stop.sourceline = sourceline;
 3142             return (--stop.repeat_count == 0);
 3143         }
 3144     }
 3145 
 3146 #if 0
 3147     /* redundant ? */
 3148     if (fcall_count > stop.fcall_count) {
 3149         stop.source = source;
 3150         stop.sourceline = sourceline;
 3151     }
 3152 #endif
 3153 
 3154     return false;
 3155 }
 3156 
 3157 /* do_next -- next command */
 3158 
 3159 int
 3160 do_next(CMDARG *arg, int cmd)
 3161 {
 3162     int ret;
 3163 
 3164     ret = next_step(arg, cmd);
 3165     if (ret) {
 3166         stop.source = source;
 3167         stop.sourceline = sourceline;
 3168         stop.fcall_count = fcall_count;
 3169         stop.check_func = check_next;
 3170     }
 3171     return ret;
 3172 }
 3173 
 3174 /* check_nexti --- process nexti command, returns true if stopping */
 3175 
 3176 static int
 3177 check_nexti(INSTRUCTION **pi)
 3178 {
 3179     /* make sure not to step inside function calls */
 3180 
 3181     if (fcall_count < stop.fcall_count) {
 3182         stop.print_frame = true;
 3183         stop.fcall_count = fcall_count;
 3184     }
 3185     return (fcall_count == stop.fcall_count
 3186             && --stop.repeat_count == 0);
 3187 }
 3188 
 3189 /* do_nexti -- nexti command */
 3190 
 3191 int
 3192 do_nexti(CMDARG *arg, int cmd)
 3193 {
 3194     int ret;
 3195 
 3196     ret = next_step(arg, cmd);
 3197     if (ret) {
 3198         stop.fcall_count = fcall_count;
 3199         stop.check_func = check_nexti;
 3200     }
 3201     return ret;
 3202 }
 3203 
 3204 /* check_finish --- process finish command, returns true if stopping */
 3205 
 3206 static int
 3207 check_finish(INSTRUCTION **pi)
 3208 {
 3209     if (fcall_count == stop.fcall_count) {
 3210         stop.print_frame = true;
 3211         return true;
 3212     }
 3213     return false;
 3214 }
 3215 
 3216 /* do_finish --- finish command */
 3217 
 3218 int
 3219 do_finish(CMDARG *arg ATTRIBUTE_UNUSED, int cmd)
 3220 {
 3221     CHECK_PROG_RUNNING();
 3222     if (cur_frame == fcall_count) {
 3223         fprintf(out_fp,
 3224             _("'finish' not meaningful in the outermost frame main()\n"));
 3225         return false;
 3226     }
 3227     stop.fcall_count = fcall_count - cur_frame - 1;
 3228     assert(stop.fcall_count >= 0);
 3229     fprintf(out_fp, _("Run till return from "));
 3230     print_numbered_frame(cur_frame);
 3231     stop.check_func = check_finish;
 3232     stop.command = cmd;
 3233     stop.print_ret = true;
 3234     return true;
 3235 }
 3236 
 3237 /* check_return --- process return, returns true if stopping */
 3238 
 3239 static int
 3240 check_return(INSTRUCTION **pi)
 3241 {
 3242     assert(fcall_count >= stop.fcall_count);
 3243 
 3244     if (fcall_count == stop.fcall_count) {
 3245         stop.print_frame = true;
 3246         return true;
 3247     }
 3248 
 3249     if (fcall_count > stop.fcall_count) {   /* innermost frame just returned */
 3250         /* force this one to return too */
 3251         NODE *func;
 3252 
 3253         func = find_frame(cur_frame)->func_node;
 3254         assert(func != NULL);
 3255         *pi = (func->code_ptr + 1)->lasti;
 3256         /* assert((*pi)->opcode == Op_K_return); */
 3257     }
 3258 
 3259     return false;
 3260 }
 3261 
 3262 /* do_return --- return command */
 3263 
 3264 int
 3265 do_return(CMDARG *arg, int cmd)
 3266 {
 3267     NODE *func, *n;
 3268 
 3269     CHECK_PROG_RUNNING();
 3270     func = find_frame(cur_frame)->func_node;
 3271     if (func == NULL) {
 3272         fprintf(out_fp, _("'return' not meaningful in the outermost frame main()\n"));
 3273         return false;
 3274     }
 3275 
 3276     stop.fcall_count = fcall_count - cur_frame - 1;
 3277     assert(stop.fcall_count >= 0);
 3278     stop.pc = (func->code_ptr + 1)->lasti;
 3279     assert(stop.pc->opcode == Op_K_return);
 3280     stop.command = cmd;
 3281 
 3282     stop.check_func = check_return;
 3283 
 3284     if (arg != NULL && arg->type == D_node) /* optional return value */
 3285         n = dupnode(arg->a_node);
 3286     else
 3287         n = dupnode(Nnull_string);
 3288     PUSH(n);
 3289 
 3290     return true;
 3291 }
 3292 
 3293 /* check_until --- process until, returns true if stopping */
 3294 
 3295 int
 3296 check_until(INSTRUCTION **pi)
 3297 {
 3298     if (fcall_count < stop.fcall_count) { /* current stack frame returned */
 3299         stop.print_frame = true;
 3300         return true;
 3301     } else if (fcall_count == stop.fcall_count) {
 3302         if (stop.pc && *pi == stop.pc)      /* until location */
 3303             return true;
 3304         if (stop.sourceline > 0     /* until */
 3305                 && source == stop.source
 3306                 && sourceline > stop.sourceline)
 3307             return true;
 3308     }
 3309     return false;
 3310 }
 3311 
 3312 /* do_until --- until command */
 3313 
 3314 int
 3315 do_until(CMDARG *arg, int cmd)
 3316 {
 3317     SRCFILE *s = cur_srcfile;
 3318     char *src = cur_srcfile->src;
 3319     int lineno;
 3320     INSTRUCTION *rp, *ip;
 3321     NODE *func;
 3322 
 3323     CHECK_PROG_RUNNING();
 3324     stop.pc = NULL;
 3325     stop.sourceline = 0;
 3326 
 3327     if (arg == NULL) {  /* until without argument */
 3328 
 3329     /* GDB doc.: continue running until a source line past the current line,
 3330      * in the current stack frame, is reached. Is used to avoid single
 3331      * stepping through a loop more than once. ...
 3332      * This means that when you reach the end of a loop after single
 3333      * stepping though it, until makes your program continue execution
 3334      * until it exits the loop. In contrast, a next command at the end
 3335      * of a loop simply steps back to the beginning of the loop, which
 3336      * forces you to step through the next iteration.
 3337      */
 3338 
 3339         stop.source = source;
 3340         stop.sourceline = sourceline;
 3341         stop.fcall_count = fcall_count - cur_frame;
 3342         stop.check_func = check_until;
 3343         stop.command = cmd;
 3344         return true;
 3345     }
 3346 
 3347     /* GDB: until location - continue running program until
 3348      * either the specified location is reached, or the
 3349      * current stack frame returns.
 3350      */
 3351 
 3352     switch (arg->type) {
 3353     case D_string:  /* until filename : lineno|function */
 3354         s = source_find(arg->a_string);
 3355         arg = arg->next;
 3356         if (s == NULL || arg == NULL
 3357                 || (arg->type != D_int && arg->type != D_func))
 3358             return false;
 3359         src = s->src;
 3360         if (arg->type == D_func)
 3361             goto func;
 3362         /* else
 3363             fall through */
 3364     case D_int: /* until lineno */
 3365         lineno = arg->a_int;
 3366         if (lineno <= 0 || lineno > s->srclines) {
 3367             d_error(_("line number %d in file `%s' out of range"),
 3368                         lineno, src);
 3369             return false;
 3370         }
 3371         break;
 3372 
 3373     case D_func:    /* until function */
 3374 func:
 3375         func = arg->a_node;
 3376         rp = func->code_ptr;
 3377         for (ip = rp->nexti; ip; ip = ip->nexti) {
 3378             if (ip->opcode != Op_breakpoint && ip->source_line > 0) {
 3379                 stop.pc = ip;
 3380                 stop.fcall_count = fcall_count - cur_frame;
 3381                 stop.check_func = check_until;
 3382                 stop.command = cmd;
 3383                 return true;
 3384             }
 3385         }
 3386         fprintf(out_fp, _("cannot find specified location in function `%s'\n"),
 3387                 func->vname);
 3388         /* fall through */
 3389     default:
 3390         return false;
 3391     }
 3392 
 3393     if ((rp = find_rule(src, lineno)) == NULL) {
 3394         d_error(_("invalid source line %d in file `%s'"), lineno, src);
 3395         return false;
 3396     }
 3397 
 3398     for (ip = rp->nexti; ip; ip = ip->nexti) {
 3399         if (ip->opcode != Op_breakpoint && ip->source_line >= lineno) {
 3400             stop.pc = ip;
 3401             stop.fcall_count = fcall_count - cur_frame;
 3402             stop.check_func = check_until;
 3403             stop.command = cmd;
 3404             return true;
 3405         }
 3406         if (ip == (rp + 1)->lasti)
 3407             break;
 3408     }
 3409     fprintf(out_fp, _("cannot find specified location %d in file `%s'\n"),
 3410                 lineno, src);
 3411     return false;
 3412 }
 3413 
 3414 /* print_watch_item --- print watched item name, old and current values */
 3415 
 3416 static void
 3417 print_watch_item(struct list_item *w)
 3418 {
 3419     NODE *symbol, *sub;
 3420     int i;
 3421 
 3422     symbol = w->symbol;
 3423     if (IS_SUBSCRIPT(w)) {
 3424         fprintf(out_fp, "%s", w->sname);
 3425         for (i = 0; i < w->num_subs; i++) {
 3426             sub = w->subs[i];
 3427             fprintf(out_fp, "[\"%.*s\"]", (int) sub->stlen, sub->stptr);
 3428         }
 3429         fprintf(out_fp, "\n");
 3430     } else if (IS_FIELD(w))
 3431         fprintf(out_fp, "$%ld\n", get_number_si(symbol));
 3432     else
 3433         fprintf(out_fp, "%s\n", w->sname);
 3434 
 3435 
 3436 #define print_value(X, S, V)                                        \
 3437 if (X)                                                              \
 3438     fprintf(out_fp, "array, %ld elements\n", w->S);                 \
 3439 else if (! w->V)                                                    \
 3440     fprintf(out_fp, IS_SUBSCRIPT(w) ?                               \
 3441             _("element not in array\n") : _("untyped variable\n")); \
 3442 else                                                                \
 3443     valinfo(w->V, fprintf, out_fp);
 3444 
 3445     fprintf(out_fp, "  Old value: ");
 3446     print_value((w->flags & OLD_IS_ARRAY) != 0, old_size, old_value);
 3447     fprintf(out_fp, "  New value: ");
 3448     print_value((w->flags & CUR_IS_ARRAY) != 0, cur_size, cur_value);
 3449 
 3450 #undef print_value
 3451 }
 3452 
 3453 /* next_command --- (optionally) print stoppage location and reason;
 3454  *                  also fetch next debug command from the user.
 3455  */
 3456 
 3457 static void
 3458 next_command()
 3459 {
 3460     static int last_rule = 0;
 3461     struct list_item *d = NULL, *w = NULL;
 3462     BREAKPOINT *b = NULL;
 3463     SRCFILE *s;
 3464 
 3465     if (source == NULL) {
 3466         stop.command = D_illegal;
 3467         stop.check_func = NULL;
 3468         return;
 3469     }
 3470 
 3471     if (stop.break_point) {
 3472         b = find_breakpoint(stop.break_point);
 3473         assert(b != NULL);
 3474         if (b->silent)
 3475             goto no_output;
 3476     } else if (stop.watch_point) {
 3477         w = find_item(&watch_list, stop.watch_point);
 3478         if (w->silent)
 3479             goto no_output;
 3480     }
 3481 
 3482     if (cur_rule != last_rule) {
 3483         fprintf(out_fp, _("Stopping in %s ...\n"), ruletab[cur_rule]);
 3484         last_rule = cur_rule;
 3485     }
 3486 
 3487     if (b != NULL)
 3488         fprintf(out_fp, "Breakpoint %d, ", b->number);
 3489     else if (w != NULL) {
 3490         fprintf(out_fp, "Watchpoint %d: ", w->number);
 3491         print_watch_item(w);
 3492     }
 3493 
 3494     /* frame info */
 3495     if (stop.print_frame) {
 3496         print_frame(frame_ptr->func_node, source, sourceline);
 3497         fprintf(out_fp, "\n");
 3498         stop.print_frame = false;
 3499     }
 3500 
 3501     (void) print_lines(source, sourceline, 1);
 3502 
 3503     /* automatic display of variables */
 3504     for (d = display_list.prev; d != &display_list; d = d->prev)
 3505         display(d);
 3506 
 3507 no_output:
 3508     /* update last_printed_line, so that  output of 'list' is
 3509      * centered around current sourceline
 3510      */
 3511 
 3512     last_printed_line = sourceline - list_size / 2;
 3513     if (last_printed_line < 0)
 3514         last_printed_line = 0;
 3515 
 3516     /* update current source file */
 3517     s = source_find(source);
 3518     if (cur_srcfile != s) {
 3519         if (cur_srcfile->fd != INVALID_HANDLE) {
 3520             close(cur_srcfile->fd);
 3521             cur_srcfile->fd = INVALID_HANDLE;
 3522         }
 3523         cur_srcfile = s;
 3524     }
 3525 
 3526     stop.command = D_illegal;
 3527     stop.check_func = NULL;
 3528 
 3529     if (b != NULL) {
 3530         int ret;
 3531         ret = execute_commands(&b->commands);
 3532         if ((b->flags & BP_TEMP) != 0)
 3533             delete_breakpoint(b);
 3534         if (ret)    /* resume execution */
 3535             return;
 3536     } else if (w != NULL && execute_commands(&w->commands))
 3537         return;
 3538 
 3539     read_command();     /* zzparse */
 3540 }
 3541 
 3542 /* debug_post_execute --- post_hook in the interpreter */
 3543 
 3544 static void
 3545 debug_post_execute(INSTRUCTION *pc)
 3546 {
 3547     if (! in_main_context())
 3548         return;
 3549 
 3550     switch (pc->opcode) {
 3551     case Op_K_next:
 3552     case Op_K_nextfile:
 3553     case Op_K_exit:
 3554         if (stop.command == D_finish) {
 3555             /* cancel finish command */
 3556             stop.print_ret = false;
 3557             stop.print_frame = false;
 3558             stop.command = D_illegal;
 3559             stop.check_func = NULL;
 3560             fprintf(out_fp, _("'finish' not meaningful with non-local jump '%s'\n"),
 3561                             op2str(pc->opcode));
 3562         } else if (stop.command == D_until) {
 3563             /* cancel until command */
 3564             stop.print_frame = false;
 3565             stop.command = D_illegal;
 3566             stop.check_func = NULL;
 3567             fprintf(out_fp, _("'until' not meaningful with non-local jump '%s'\n"),
 3568                             op2str(pc->opcode));
 3569         }
 3570         break;
 3571 
 3572     case Op_K_return:
 3573         if (stop.command == D_finish
 3574                 && fcall_count == stop.fcall_count
 3575                 && stop.print_ret
 3576         ) {
 3577             NODE *r;
 3578             /* print the returned value before it disappears. */
 3579             r = TOP();
 3580             fprintf(out_fp, "Returned value = ");
 3581             valinfo(r, fprintf, out_fp);
 3582             stop.print_ret = false;
 3583         }
 3584         break;
 3585 
 3586     case Op_newfile:
 3587     case Op_get_record:
 3588         return;
 3589 
 3590     default:
 3591         break;
 3592     }
 3593 }
 3594 
 3595 /* debug_pre_execute --- pre_hook, called by the interpreter before execution;
 3596  *                 checks if execution needs to be suspended and control
 3597  *                 transferred to the debugger.
 3598  */
 3599 
 3600 static int
 3601 debug_pre_execute(INSTRUCTION **pi)
 3602 {
 3603     static bool cant_stop = false;
 3604     NODE *m;
 3605 
 3606     if (! in_main_context())
 3607         return pre_execute_code(pi);
 3608 
 3609     cur_pc = *pi;
 3610     stop.break_point = 0;
 3611     stop.watch_point = 0;
 3612     cur_frame = 0;
 3613 
 3614     if (do_trace
 3615         && cur_pc->opcode != Op_breakpoint
 3616         && stop.command != D_return
 3617     )
 3618         print_instruction(cur_pc, fprintf, out_fp, false);
 3619 
 3620 /* N.B.: For Op_field_spec_lhs must execute instructions upto Op_field_assign
 3621  * as a group before stopping. Otherwise, watch/print of field variables
 3622  * yield surprising results. Ditto for Op_push_lhs for special variables
 3623  * (upto Op_var_assign, the set_FOO routine).
 3624  */
 3625 
 3626     switch (cur_pc->opcode) {
 3627     case Op_field_spec_lhs:
 3628         cant_stop = true;
 3629         break;
 3630 
 3631     case Op_field_assign:
 3632         cant_stop = false;
 3633         return true; /* may stop at next instruction */
 3634 
 3635     case Op_push_lhs:
 3636         m = cur_pc->memory;
 3637         if (m->type == Node_var && m->var_assign)
 3638             cant_stop = true;
 3639         break;
 3640 
 3641     case Op_arrayfor_incr:  /* can have special var as array variable !!! */
 3642         m = cur_pc->array_var;
 3643         if (m->type == Node_var && m->var_assign)
 3644             cant_stop = true;
 3645         break;
 3646 
 3647     case Op_var_assign:
 3648         cant_stop = false;
 3649         return true; /* may stop at next instruction */
 3650 
 3651     case Op_rule:
 3652         cur_rule = cur_pc->in_rule;
 3653         return true;
 3654 
 3655     case Op_func:
 3656     case Op_var_update:
 3657         return true;
 3658 
 3659     case Op_breakpoint:
 3660         break;  /* processed later in check_breakpoint() */
 3661 
 3662     default:
 3663         if (cur_pc->source_line <= 0)
 3664             return true;
 3665         break;
 3666     }
 3667 
 3668     if (cant_stop)
 3669         return true;
 3670 
 3671     assert(sourceline > 0);
 3672 
 3673     /*
 3674      * 11/2015: This used to check breakpoints first, but that could
 3675      * produce strange behavior, where a watchpoint doesn't print until
 3676      * some time after the data changed.  This reworks things so that
 3677      * watchpoints are checked first. It's a bit of a hack, but
 3678      * the behavior for the user is more logical.
 3679      */
 3680     if (check_watchpoint()) {
 3681         next_command(); /* return to debugger interface */
 3682         if (stop.command == D_return)
 3683             *pi = stop.pc;  /* jump to this instruction */
 3684         else if (cur_pc->opcode == Op_breakpoint)
 3685             cur_pc = cur_pc->nexti;    /* skip past the breakpoint instruction */
 3686     } else if (check_breakpoint(pi)
 3687             || (stop.check_func && stop.check_func(pi))) {
 3688         next_command(); /* return to debugger interface */
 3689         if (stop.command == D_return)
 3690             *pi = stop.pc;  /* jump to this instruction */
 3691     }
 3692 
 3693     /* if cur_pc == *pi, interpreter executes cur_pc;
 3694      * Otherwise, jumps to instruction *pi.
 3695      */
 3696     return (cur_pc == *pi);
 3697 }
 3698 
 3699 /* print_memory --- print a scalar value */
 3700 
 3701 static void
 3702 print_memory(NODE *m, NODE *func, Func_print print_func, FILE *fp)
 3703 {
 3704     switch (m->type) {
 3705     case Node_val:
 3706         if (m == Nnull_string)
 3707             print_func(fp, "Nnull_string");
 3708         else if ((m->flags & NUMBER) != 0) {
 3709 #ifdef HAVE_MPFR
 3710             if ((m->flags & MPFN) != 0)
 3711                 print_func(fp, "%s", mpg_fmt("%R*g", ROUND_MODE, m->mpg_numbr));
 3712             else if ((m->flags & MPZN) != 0)
 3713                 print_func(fp, "%s", mpg_fmt("%Zd", m->mpg_i));
 3714             else
 3715 #endif
 3716                 print_func(fp, "%g", m->numbr);
 3717         } else if ((m->flags & STRING) != 0)
 3718             pp_string_fp(print_func, fp, m->stptr, m->stlen, '"', false);
 3719         else if ((m->flags & REGEX) != 0) {
 3720             print_func(fp, "@");
 3721             pp_string_fp(print_func, fp, m->stptr, m->stlen, '/', false);
 3722         } else
 3723             print_func(fp, "-?-");
 3724         print_func(fp, " [%s]", flags2str(m->flags));
 3725         break;
 3726 
 3727     case Node_regex:
 3728         pp_string_fp(print_func, fp, m->re_exp->stptr, m->re_exp->stlen, '/', false);
 3729         break;
 3730 
 3731     case Node_dynregex:
 3732         break;
 3733 
 3734     case Node_param_list:
 3735         assert(func != NULL);
 3736         print_func(fp, "%s", func->fparms[m->param_cnt].param);
 3737         break;
 3738 
 3739     case Node_var:
 3740     case Node_var_new:
 3741     case Node_var_array:
 3742         print_func(fp, "%s", m->vname);
 3743         break;
 3744 
 3745     default:
 3746         print_func(fp, "?");  /* can't happen */
 3747     }
 3748 }
 3749 
 3750 /* print_instruction --- print a bytecode */
 3751 
 3752 static void
 3753 print_instruction(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump)
 3754 {
 3755     int pcount = 0;
 3756     static NODE *func = NULL;
 3757     static int noffset = 0;
 3758 
 3759     if (noffset == 0) {
 3760         static char buf[50];
 3761         /* offset for 2nd to last lines in a multi-line output */
 3762         noffset = sprintf(buf, "[      :%p] %-20.20s: ", (void *) pc,
 3763                 opcode2str(pc->opcode));
 3764     }
 3765 
 3766     if (pc->opcode == Op_func) {
 3767         func = pc->func_body;
 3768         pcount = func->param_cnt;
 3769         if (in_dump) {
 3770             int j;
 3771             print_func(fp, "\n\t# Function: %s (", func->vname);
 3772             for (j = 0; j < pcount; j++) {
 3773                 print_func(fp, "%s", func->fparms[j].param);
 3774                 if (j < pcount - 1)
 3775                     print_func(fp, ", ");
 3776             }
 3777             print_func(fp, ")\n\n");
 3778         }
 3779     } else if (pc->opcode == Op_rule) {
 3780         if (in_dump)
 3781             print_func(fp, "\n\t# %s\n\n", ruletab[pc->in_rule]);
 3782     }
 3783 
 3784     if (pc->opcode == Op_newfile)
 3785         print_func(fp, "\n");
 3786 
 3787     if (pc->source_line <= 0)
 3788         print_func(fp, "[      :%p] %-20.20s: ", pc, opcode2str(pc->opcode));
 3789     else
 3790         print_func(fp, "[%6d:%p] %-20.20s: ",
 3791                         pc->source_line, pc, opcode2str(pc->opcode));
 3792 
 3793     if (prog_running && ! in_dump) {
 3794         /* find Node_func if in function */
 3795         func = find_frame(0)->func_node;
 3796     }
 3797 
 3798 
 3799     switch (pc->opcode) {
 3800     case Op_K_if:
 3801         print_func(fp, "[branch_if = %p] [branch_else = %p] [branch_else->lasti = %p]\n",
 3802                 pc->branch_if, pc->branch_else, pc->branch_else->lasti);
 3803         break;
 3804 
 3805     case Op_K_else:
 3806         print_func(fp, "[branch_end = %p]\n", pc->branch_end);
 3807         break;
 3808 
 3809     case Op_K_while:
 3810         print_func(fp, "[while_body = %p] [target_break = %p]\n", (pc+1)->while_body, pc->target_break);
 3811         break;
 3812 
 3813     case Op_K_do:
 3814         print_func(fp, "[doloop_cond = %p] [target_break = %p]", (pc+1)->doloop_cond, pc->target_break);
 3815         if (pc->comment)
 3816             print_func(fp, " [comment = %p]", pc->comment);
 3817         print_func(fp, "\n");
 3818         if (pc->comment)
 3819             print_instruction(pc->comment, print_func, fp, in_dump);
 3820         break;
 3821 
 3822     case Op_K_for:
 3823         print_func(fp, "[forloop_cond = %p] ", (pc+1)->forloop_cond);
 3824         /* fall through */
 3825     case Op_K_arrayfor:
 3826         print_func(fp, "[forloop_body = %p] ", (pc+1)->forloop_body);
 3827         print_func(fp, "[target_break = %p] [target_continue = %p]", pc->target_break, pc->target_continue);
 3828         if (pc->comment != NULL) {
 3829             print_func(fp, " [comment = %p]\n", (pc)->comment);
 3830             print_instruction(pc->comment, print_func, fp, in_dump);
 3831         } else
 3832             print_func(fp, "\n");
 3833         break;
 3834 
 3835     case Op_K_switch:
 3836     {
 3837         bool need_newline = false;
 3838         print_func(fp, "[switch_start = %p] [switch_end = %p]\n", (pc+1)->switch_start, (pc+1)->switch_end);
 3839         if (pc->comment || (pc+1)->switch_end->comment)
 3840             print_func(fp, "%*s", noffset, "");
 3841         if (pc->comment) {
 3842             print_func(fp, "[start_comment = %p]", pc->comment);
 3843             need_newline = true;
 3844         }
 3845         if ((pc+1)->switch_end->comment) {
 3846             print_func(fp, "[end_comment = %p]", (pc + 1)->switch_end->comment);
 3847             need_newline = true;
 3848         }
 3849         if (need_newline)
 3850             print_func(fp, "\n");
 3851         if (pc->comment)
 3852             print_instruction(pc->comment, print_func, fp, in_dump);
 3853         if ((pc+1)->switch_end->comment)
 3854             print_instruction((pc+1)->switch_end->comment, print_func, fp, in_dump);
 3855     }
 3856         break;
 3857 
 3858     case Op_K_default:
 3859         print_func(fp, "[stmt_start = %p] [stmt_end = %p]", pc->stmt_start, pc->stmt_end);
 3860         if (pc->comment) {
 3861             print_func(fp, " [comment = %p]\n", pc->comment);
 3862             print_instruction(pc->comment, print_func, fp, in_dump);
 3863         } else
 3864             print_func(fp, "\n");
 3865         break;
 3866 
 3867     case Op_var_update:
 3868         print_func(fp, "[update_%s()]\n", get_spec_varname(pc->update_var));
 3869         break;
 3870 
 3871     case Op_var_assign:
 3872         print_func(fp, "[set_%s()]", get_spec_varname(pc->assign_var));
 3873         if (pc->assign_ctxt != 0)
 3874             print_func(fp, " [assign_ctxt = %s]", opcode2str(pc->assign_ctxt));
 3875         print_func(fp, "\n");
 3876         break;
 3877 
 3878     case Op_field_assign:
 3879         print_func(fp, "[%s]\n", pc->field_assign == reset_record ?
 3880                     "reset_record()" : "invalidate_field0()");
 3881         break;
 3882 
 3883     case Op_field_spec_lhs:
 3884         print_func(fp, "[target_assign = %p] [do_reference = %s]\n",
 3885                 pc->target_assign, pc->do_reference ? "true" : "false");
 3886         break;
 3887 
 3888     case Op_func:
 3889         print_func(fp, "[param_cnt = %d] [source_file = %s]", pcount,
 3890                 pc->source_file ? pc->source_file : "cmd. line");
 3891         if (pc[3].nexti != NULL) {
 3892             print_func(fp, "[ns_list = %p]\n", pc[3].nexti);
 3893             print_ns_list(pc[3].nexti, print_func, fp, in_dump);
 3894         } else
 3895             print_func(fp, "\n");
 3896         break;
 3897 
 3898     case Op_K_getline_redir:
 3899         print_func(fp, "[into_var = %s] [redir_type = \"%s\"]\n",
 3900                         pc->into_var ? "true" : "false",
 3901                         redir2str(pc->redir_type));
 3902         break;
 3903 
 3904     case Op_K_getline:
 3905         print_func(fp, "[into_var = %s]\n", pc->into_var ? "true" : "false");
 3906         print_func(fp, "%*s[target_beginfile = %p] [target_endfile = %p]\n",
 3907                         noffset, "",
 3908                         (pc + 1)->target_beginfile, (pc + 1)->target_endfile);
 3909         break;
 3910 
 3911     case Op_K_print_rec:
 3912         print_func(fp, "[redir_type = \"%s\"]\n", redir2str(pc->redir_type));
 3913         break;
 3914 
 3915     case Op_K_print:
 3916     case Op_K_printf:
 3917         print_func(fp, "[expr_count = %ld] [redir_type = \"%s\"]\n",
 3918                         pc->expr_count, redir2str(pc->redir_type));
 3919         break;
 3920 
 3921     case Op_indirect_func_call:
 3922     case Op_func_call:
 3923         print_func(fp, "[func_name = %s] [arg_count = %ld]\n",
 3924                         pc->func_name, (pc + 1)->expr_count);
 3925         break;
 3926 
 3927     case Op_K_nextfile:
 3928         print_func(fp, "[target_newfile = %p] [target_endfile = %p]\n",
 3929                         pc->target_newfile, pc->target_endfile);
 3930         break;
 3931 
 3932     case Op_newfile:
 3933         print_func(fp, "[target_jmp = %p] [target_endfile = %p]\n",
 3934                         pc->target_jmp, pc->target_endfile);
 3935         print_func(fp, "%*s[target_get_record = %p]\n",
 3936                         noffset, "", (pc + 1)->target_get_record);
 3937         break;
 3938 
 3939     case Op_get_record:
 3940         print_func(fp, "[target_newfile = %p]\n", pc->target_newfile);
 3941         break;
 3942 
 3943     case Op_jmp:
 3944     case Op_jmp_false:
 3945     case Op_jmp_true:
 3946     case Op_and:
 3947     case Op_or:
 3948     case Op_K_next:
 3949     case Op_arrayfor_init:
 3950     case Op_K_break:
 3951     case Op_K_continue:
 3952         print_func(fp, "[target_jmp = %p]\n", pc->target_jmp);
 3953         break;
 3954 
 3955     case Op_K_exit:
 3956         print_func(fp, "[target_end = %p] [target_atexit = %p]\n",
 3957                         pc->target_end, pc->target_atexit);
 3958         break;
 3959 
 3960     case Op_K_case:
 3961         print_func(fp, "[target_jmp = %p] [match_exp = %s]",
 3962                         pc->target_jmp, (pc + 1)->match_exp ? "true" : "false");
 3963         if (pc->comment) {
 3964             print_func(fp, " [comment = %p]\n", pc->comment);
 3965             print_instruction(pc->comment, print_func, fp, in_dump);
 3966         } else
 3967             print_func(fp, "\n");
 3968         break;
 3969 
 3970     case Op_K_namespace:
 3971         print_func(fp, "[namespace = %s]", pc->ns_name);
 3972         if (pc->nexti)
 3973             print_func(fp, "[nexti = %p]", pc->nexti);
 3974         if (pc->comment)
 3975             print_func(fp, "[comment = %p]", pc->comment);
 3976         print_func(fp, "\n");
 3977         break;
 3978 
 3979     case Op_arrayfor_incr:
 3980         print_func(fp, "[array_var = %s] [target_jmp = %p]\n",
 3981                         pc->array_var->type == Node_param_list ?
 3982                            func->fparms[pc->array_var->param_cnt].param : pc->array_var->vname,
 3983                         pc->target_jmp);
 3984         break;
 3985 
 3986     case Op_line_range:
 3987         print_func(fp, "[triggered = %ld] [target_jmp = %p]\n",
 3988                         pc->triggered, pc->target_jmp);
 3989         break;
 3990 
 3991     case Op_cond_pair:
 3992         print_func(fp, "[line_range = %p] [target_jmp = %p]\n",
 3993                         pc->line_range, pc->target_jmp);
 3994         break;
 3995 
 3996     case Op_sub_builtin:
 3997     {
 3998         const char *fname = "sub";
 3999         static const struct flagtab values[] = {
 4000             { GSUB, "GSUB" },
 4001             { GENSUB, "GENSUB" },
 4002             { LITERAL, "LITERAL" },
 4003             { 0, NULL }
 4004         };
 4005 
 4006         if ((pc->sub_flags & GSUB) != 0)
 4007             fname = "gsub";
 4008         else if ((pc->sub_flags & GENSUB) != 0)
 4009             fname = "gensub";
 4010         print_func(fp, "%s [arg_count = %ld] [sub_flags = %s]\n",
 4011                 fname, pc->expr_count,
 4012                 genflags2str(pc->sub_flags, values));
 4013     }
 4014         break;
 4015 
 4016     case Op_builtin:
 4017         print_func(fp, "%s [arg_count = %ld]\n", getfname(pc->builtin, false),
 4018                         pc->expr_count);
 4019         break;
 4020 
 4021     case Op_ext_builtin:
 4022         print_func(fp, "%s [arg_count = %ld]\n", (pc + 1)->func_name,
 4023                         pc->expr_count);
 4024         break;
 4025 
 4026     case Op_subscript:
 4027     case Op_sub_array:
 4028         print_func(fp, "[sub_count = %ld]\n", pc->sub_count);
 4029         break;
 4030 
 4031     case Op_store_sub:
 4032         print_memory(pc->memory, func, print_func, fp);
 4033         print_func(fp, " [sub_count = %ld]\n", pc->expr_count);
 4034         break;
 4035 
 4036     case Op_subscript_lhs:
 4037         print_func(fp, "[sub_count = %ld] [do_reference = %s]\n",
 4038                         pc->sub_count,
 4039                         pc->do_reference ? "true" : "false");
 4040         break;
 4041 
 4042     case Op_K_delete:
 4043     case Op_in_array:
 4044         print_func(fp, "[expr_count = %ld]\n", pc->expr_count);
 4045         break;
 4046 
 4047     case Op_concat:
 4048         /* NB: concat_flag CSVAR only used in grammar, don't display it */
 4049         print_func(fp, "[expr_count = %ld] [concat_flag = %s]\n",
 4050                         pc->expr_count,
 4051                         (pc->concat_flag & CSUBSEP) != 0 ? "CSUBSEP" : "0");
 4052         break;
 4053 
 4054     case Op_rule:
 4055         print_func(fp, "[in_rule = %s] [source_file = %s]",
 4056                         ruletab[pc->in_rule],
 4057                         pc->source_file ? pc->source_file : "cmd. line");
 4058         if (pc[3].nexti != NULL) {
 4059             print_func(fp, "[ns_list = %p]\n", pc[3].nexti);
 4060             print_ns_list(pc[3].nexti, print_func, fp, in_dump);
 4061         } else
 4062             print_func(fp, "\n");
 4063         break;
 4064 
 4065     case Op_lint:
 4066     {
 4067         static const char *const linttypetab[] = {
 4068             "LINT_illegal",
 4069             "LINT_assign_in_cond",
 4070             "LINT_no_effect"
 4071         };
 4072         print_func(fp, "[lint_type = %s]\n", linttypetab[pc->lint_type]);
 4073     }
 4074         break;
 4075 
 4076     case Op_exec_count:
 4077         print_func(fp, "[exec_count = %ld]\n", pc->exec_count);
 4078         break;
 4079 
 4080     case Op_store_var:
 4081         print_memory(pc->memory, func, print_func, fp);
 4082         if (pc->initval != NULL) {
 4083             print_func(fp, " = ");
 4084             print_memory(pc->initval, func, print_func, fp);
 4085         }
 4086         print_func(fp, "\n");
 4087         break;
 4088 
 4089     case Op_push_lhs:
 4090         print_memory(pc->memory, func, print_func, fp);
 4091         print_func(fp, " [do_reference = %s]\n",
 4092                         pc->do_reference ? "true" : "false");
 4093         break;
 4094 
 4095     case Op_comment:
 4096         print_memory(pc->memory, func, print_func, fp);
 4097         print_func(fp, " [comment_type = %s]",
 4098             pc->memory->comment_type == EOL_COMMENT ?
 4099                         "EOL" : "BLOCK");
 4100         if (pc->comment) {
 4101             print_func(fp, " [comment = %p]\n", pc->comment);
 4102             print_instruction(pc->comment, print_func, fp, in_dump);
 4103         } else
 4104             print_func(fp, "\n");
 4105         break;
 4106 
 4107     case Op_push_i:
 4108     case Op_push:
 4109     case Op_push_arg:
 4110     case Op_push_arg_untyped:
 4111     case Op_push_param:
 4112     case Op_push_array:
 4113     case Op_push_re:
 4114     case Op_match_rec:
 4115     case Op_match:
 4116     case Op_nomatch:
 4117     case Op_plus_i:
 4118     case Op_minus_i:
 4119     case Op_times_i:
 4120     case Op_exp_i:
 4121     case Op_quotient_i:
 4122     case Op_mod_i:
 4123     case Op_assign_concat:
 4124         print_memory(pc->memory, func, print_func, fp);
 4125         /* fall through */
 4126     default:
 4127         print_func(fp, "\n");
 4128         break;
 4129     }
 4130 }
 4131 
 4132 /* do_trace_instruction --- trace command */
 4133 
 4134 int
 4135 do_trace_instruction(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 4136 {
 4137     if (arg != NULL && arg->type == D_argument
 4138             && arg->a_argument == A_TRACE_ON)
 4139         do_trace = true;
 4140     else
 4141         do_trace = false;
 4142     return false;
 4143 }
 4144 
 4145 /* print_code --- print a list of instructions */
 4146 
 4147 static int
 4148 print_code(INSTRUCTION *pc, void *x)
 4149 {
 4150     struct pf_data *data = (struct pf_data *) x;
 4151     for (; pc != NULL; pc = pc->nexti)
 4152         print_instruction(pc, data->print_func, data->fp, data->defn /* in_dump */);
 4153     return 0;
 4154 }
 4155 
 4156 /* print_ns_list --- print the list of namespaces */
 4157 
 4158 static void
 4159 print_ns_list(INSTRUCTION *pc, Func_print print_func, FILE *fp, int in_dump)
 4160 {
 4161     for (; pc != NULL; pc = pc->nexti) {
 4162         print_instruction(pc, print_func, fp, in_dump);
 4163         if (pc->comment != NULL)
 4164             print_instruction(pc->comment, print_func, fp, in_dump);
 4165     }
 4166 }
 4167 
 4168 /* do_dump_instructions --- dump command */
 4169 
 4170 int
 4171 do_dump_instructions(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 4172 {
 4173     FILE *fp;
 4174     NODE **funcs;
 4175 
 4176     if (arg != NULL && arg->type == D_string) {
 4177         /* dump to a file */
 4178         if ((fp = fopen(arg->a_string, "w")) == NULL) {
 4179             d_error(_("could not open `%s' for writing: %s"),
 4180                     arg->a_string, strerror(errno));
 4181             return false;
 4182         }
 4183         pf_data.print_func = fprintf;
 4184         pf_data.fp = fp;
 4185         pf_data.defn = true;    /* in_dump = true */
 4186         (void) print_code(code_block, &pf_data);
 4187         funcs = function_list(true);
 4188         (void) foreach_func(funcs,
 4189                              (int (*)(INSTRUCTION *, void *)) print_code,
 4190                              &pf_data);
 4191         efree(funcs);
 4192         fclose(fp);
 4193         return false;
 4194     }
 4195 
 4196     funcs = function_list(true);
 4197     initialize_pager(out_fp);
 4198     if (setjmp(pager_quit_tag) == 0) {
 4199         pf_data.print_func = gprintf;
 4200         pf_data.fp = out_fp;
 4201         pf_data.defn = true;    /* in_dump = true */
 4202         (void) print_code(code_block, &pf_data);
 4203         (void) foreach_func(funcs,
 4204                             (int (*)(INSTRUCTION *, void *)) print_code,
 4205                              &pf_data);
 4206     }
 4207     efree(funcs);
 4208     return false;
 4209 }
 4210 
 4211 /* do_save --- save command */
 4212 
 4213 int
 4214 do_save(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 4215 {
 4216 #if defined(HAVE_LIBREADLINE) && defined(HAVE_HISTORY_LIST)
 4217     FILE *fp;
 4218     HIST_ENTRY **hist_list;
 4219     int i;
 4220 
 4221     if ((fp = fopen(arg->a_string, "w")) == NULL) {
 4222         d_error(_("could not open `%s' for writing: %s"),
 4223                 arg->a_string, strerror(errno));
 4224         return false;
 4225     }
 4226 
 4227     hist_list = history_list();
 4228     if (hist_list && history_length > sess_history_base) {
 4229         for (i = sess_history_base; hist_list[i] != NULL; i++) {
 4230             char *line;
 4231             line = hist_list[i]->line;
 4232 
 4233             /* exclude save commands;
 4234              * N.B.: this test may fail if there is another
 4235              * command with the same first 2 letters.
 4236              */
 4237 
 4238             if (strlen(line) > 1
 4239                 && strncmp(line, "sa", 2) == 0)
 4240                 continue;
 4241 
 4242             fprintf(fp, "%s\n", line);
 4243         }
 4244     }
 4245     fclose(fp);
 4246 #endif
 4247     return false;
 4248 }
 4249 
 4250 /* do_option --- option command */
 4251 
 4252 int
 4253 do_option(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 4254 {
 4255     const struct dbg_option *opt;
 4256     char *name, *value;
 4257 
 4258     if (arg == NULL) {  /* display all available options and corresponding values */
 4259         for (opt = option_list; opt->name; opt++) {
 4260             if (opt->str_val != NULL)
 4261                 fprintf(out_fp, "%s = \"%s\"\n", opt->name, *(opt->str_val));
 4262             else
 4263                 fprintf(out_fp, "%s = %d\n", opt->name, *(opt->num_val));
 4264         }
 4265         return false;
 4266     }
 4267 
 4268     name = arg->a_string;
 4269     arg = arg->next;
 4270     value = arg ? arg->a_string : NULL;
 4271 
 4272     for (opt = option_list; opt->name; opt++) { /* linear search */
 4273         if (strcmp(name, opt->name) == 0)
 4274             break;
 4275     }
 4276     if (! opt->name)
 4277         return false;
 4278 
 4279     if (value == NULL) {    /* display current setting */
 4280         if (opt->str_val != NULL)
 4281             fprintf(out_fp, "%s = \"%s\"\n", opt->name, *(opt->str_val));
 4282         else
 4283             fprintf(out_fp, "%s = %d\n", opt->name, *(opt->num_val));
 4284     } else
 4285         (*(opt->assign))(value);
 4286     return false;
 4287 }
 4288 
 4289 
 4290 #ifdef HAVE_LIBREADLINE
 4291 
 4292 /* initialize_pager --- initialize our idea of the terminal size */
 4293 
 4294 void
 4295 initialize_pager(FILE *fp)
 4296 {
 4297     if (! os_isatty(fileno(fp)) || ! input_from_tty || input_fd != 0) {
 4298         screen_width = INT_MAX;
 4299         screen_height = INT_MAX;
 4300     } else {
 4301         /* get the terminal size from readline. */
 4302 
 4303         rl_reset_terminal(NULL); /* N.B.: NULL argument means
 4304                                   * "use TERM env variable for terminal name".
 4305                                   */
 4306         rl_get_screen_size(&screen_height, &screen_width);
 4307         if (screen_height <= 1)
 4308             screen_height = INT_MAX;
 4309         if (screen_width <= 1)
 4310             screen_width = INT_MAX;
 4311     }
 4312     pager_lines_printed = 0;
 4313 }
 4314 #endif
 4315 
 4316 static void
 4317 prompt_continue(FILE *fp)
 4318 {
 4319     bool quit_pager = false;
 4320 
 4321     if (os_isatty(fileno(fp)) && input_fd == 0)
 4322         quit_pager = prompt_yes_no(
 4323             // TRANSLATORS: don't translate the 'q' inside the brackets.
 4324                     _("\t------[Enter] to continue or [q] + [Enter] to quit------"),
 4325                     'q', false, fp);
 4326     if (quit_pager)
 4327         longjmp(pager_quit_tag, 1);
 4328     pager_lines_printed = 0;
 4329 }
 4330 
 4331 /* gprintf --- like fprintf but allows paging */
 4332 
 4333 int
 4334 gprintf(FILE *fp, const char *format, ...)
 4335 {
 4336     va_list args;
 4337     static char *buf = NULL;
 4338     static size_t buflen = 0;
 4339     static int bl = 0;
 4340     char *p, *q;
 4341     int nchar;
 4342 
 4343 #define GPRINTF_BUFSIZ 512
 4344     if (buf == NULL) {
 4345         buflen = GPRINTF_BUFSIZ;
 4346         emalloc(buf, char *, buflen * sizeof(char), "gprintf");
 4347     } else if (buflen - bl < GPRINTF_BUFSIZ/2) {
 4348         buflen += GPRINTF_BUFSIZ;
 4349         erealloc(buf, char *, buflen * sizeof(char), "gprintf");
 4350     }
 4351 #undef GPRINTF_BUFSIZ
 4352 
 4353     while (true) {
 4354         va_start(args, format);
 4355         nchar = vsnprintf(buf + bl, buflen - bl, format, args);
 4356         va_end(args);
 4357         if (nchar == 0)
 4358             return 0;
 4359         if (nchar > 0 && nchar < buflen - bl) {
 4360             bl += nchar;
 4361             if (buf[bl-1] != '\n') /* buffer output until see a newline at end */
 4362                 return nchar;
 4363             break;
 4364         }
 4365 
 4366         /* enlarge buffer, and try again */
 4367         buflen *= 2;
 4368         erealloc(buf, char *, buflen * sizeof(char), "gprintf");
 4369     }
 4370 
 4371     bl = 0;
 4372     for (p = buf; (q = strchr(p, '\n')) != NULL; p = q + 1) {
 4373         int sz = (int) (q - p);
 4374 
 4375         while (sz > 0) {
 4376             int cnt;
 4377             cnt = sz > screen_width ? screen_width : sz;
 4378 
 4379             /* do not print partial line before scrolling */
 4380             if (cnt < sz && (pager_lines_printed == (screen_height - 2)))
 4381                 prompt_continue(fp);
 4382 
 4383             if (fwrite(p, sizeof(char), cnt, fp) != cnt)
 4384                 return -1;
 4385             if (cnt == sz)
 4386                 break;
 4387             else {
 4388                 if (++pager_lines_printed == (screen_height - 1))
 4389                     prompt_continue(fp);
 4390                 sz -= screen_width;
 4391                 assert(sz > 0);
 4392                 p += cnt;
 4393             }
 4394         }
 4395 
 4396         fprintf(fp, "\n");
 4397         if (++pager_lines_printed == (screen_height - 1))
 4398             prompt_continue(fp);
 4399         p++;
 4400     }
 4401     return nchar;
 4402 }
 4403 
 4404 
 4405 static int
 4406 serialize_subscript(char *buf, int buflen, struct list_item *item)
 4407 {
 4408     int bl, nchar, i;
 4409     NODE *sub;
 4410 
 4411     nchar = snprintf(buf, buflen, "%d%c%d%c%s%c%d%c",
 4412                 item->number, FSEP, D_subscript, FSEP, item->sname, FSEP,
 4413                 item->num_subs, FSEP);
 4414     if (nchar <= 0)
 4415         return 0;
 4416     else if (nchar >= buflen)   /* need larger buffer */
 4417         return nchar;
 4418     bl = nchar;
 4419     for (i = 0; i < item->num_subs; i++) {
 4420         sub = item->subs[i];
 4421         nchar = snprintf(buf + bl, buflen - bl, "%lu%c%.*s%c",
 4422                     (unsigned long) sub->stlen, FSEP,
 4423                     (int) sub->stlen, sub->stptr, FSEP);
 4424         if (nchar <= 0)
 4425             return 0;
 4426         bl += nchar;
 4427         if (bl >= buflen)   /* need larger buffer */
 4428             return bl;
 4429     }
 4430     return bl;
 4431 }
 4432 
 4433 
 4434 
 4435 /* serialize_list--- convert a list structure to a byte stream and
 4436  *               save in environment.
 4437  */
 4438 
 4439 static void
 4440 serialize_list(int type)
 4441 {
 4442     static char *buf = NULL;
 4443     static int buflen = 0;
 4444     int bl;
 4445     BREAKPOINT *b = NULL;
 4446     struct list_item *wd = NULL;
 4447     HIST_ENTRY **hist_list = NULL;
 4448     int hist_index = 0;
 4449     struct dbg_option *opt = NULL;
 4450     struct commands_item *commands = NULL, *c;
 4451     int cnum = 0;
 4452     struct condition *cndn = NULL;
 4453     void *ptr, *end_ptr;
 4454 #ifdef HAVE_LIBREADLINE
 4455     HIST_ENTRY *h = NULL;
 4456 #endif
 4457 
 4458     switch (type) {
 4459     case BREAK:
 4460         end_ptr = (void *) &breakpoints;
 4461         ptr = (void *) breakpoints.prev;
 4462         break;
 4463     case WATCH:
 4464         end_ptr = (void *) &watch_list;
 4465         ptr = (void *) watch_list.prev;
 4466         break;
 4467     case DISPLAY:
 4468         end_ptr = (void *) &display_list;
 4469         ptr = (void *) display_list.prev;
 4470         break;
 4471     case HISTORY:
 4472         hist_list = history_list();
 4473         if (hist_list == NULL) /* empty history list */
 4474             return;
 4475         end_ptr = NULL;
 4476         ptr = (void *) hist_list[0];
 4477         break;
 4478     case OPTION:
 4479     {
 4480         int n;
 4481         n = sizeof(option_list)/sizeof(option_list[0]);
 4482         end_ptr = (void *) &option_list[n - 1];
 4483         ptr = (void *) option_list;
 4484     }
 4485         break;
 4486 
 4487     default:
 4488         return;
 4489     }
 4490 
 4491     if (type != HISTORY && ptr == end_ptr)      /* empty list */
 4492         return;
 4493 
 4494 #define SERIALIZE_BUFSIZ 512
 4495 
 4496     if (buf == NULL) {  /* first time */
 4497         buflen = SERIALIZE_BUFSIZ;
 4498         emalloc(buf, char *, buflen + 1, "serialize");
 4499     }
 4500     bl = 0;
 4501 
 4502     while (ptr != end_ptr) {
 4503         int nchar = 0;
 4504         if (buflen - bl < SERIALIZE_BUFSIZ/2) {
 4505 enlarge_buffer:
 4506             buflen *= 2;
 4507             erealloc(buf, char *, buflen + 1, "serialize");
 4508         }
 4509 
 4510 #undef SERIALIZE_BUFSIZ
 4511 
 4512         /* field seperator is FSEP ('\037'), and the record separator is RSEP ('\036') */
 4513 
 4514         switch (type) {
 4515         case BREAK:
 4516             b = (BREAKPOINT *) ptr;
 4517 
 4518             /* src source_line flags ignore_count hit_count number;
 4519              * commands and condition processed later in the end switch
 4520              */
 4521 
 4522             nchar = snprintf(buf + bl, buflen - bl,
 4523                              "%s%c%d%c%d%c%d%c%d%c%d%c",
 4524                              b->src, FSEP, b->bpi->source_line, FSEP, b->flags, FSEP,
 4525                              (int) b->ignore_count, FSEP,
 4526                              (int) b->hit_count, FSEP, b->number, FSEP);
 4527             cnum = b->number;
 4528             commands = &b->commands;
 4529             cndn = &b->cndn;
 4530             break;
 4531         case DISPLAY:
 4532         case WATCH:
 4533             wd = (struct list_item *) ptr;
 4534 
 4535             /* subscript    -- number type sname num_subs subs(stlen + stptr) [commands [condition]]
 4536              * variable     -- number type sname [commands [condition]]
 4537              * field        -- number type symbol(numbr) [commands [condition]]
 4538              */
 4539 
 4540             if (IS_PARAM(wd))   /* exclude parameters */
 4541                 nchar = 0;
 4542             else if (IS_SUBSCRIPT(wd))
 4543                 nchar = serialize_subscript(buf + bl, buflen - bl, wd);
 4544             else if (IS_FIELD(wd))
 4545                 nchar = snprintf(buf + bl, buflen - bl, "%d%c%d%c%d%c",
 4546                             wd->number, FSEP, D_field, FSEP, (int) get_number_si(wd->symbol), FSEP);
 4547             else
 4548                 nchar = snprintf(buf + bl, buflen - bl, "%d%c%d%c%s%c",
 4549                             wd->number, FSEP, D_variable, FSEP, wd->sname, FSEP);
 4550             cnum = wd->number;
 4551             commands = &wd->commands;
 4552             cndn = &wd->cndn;
 4553             break;
 4554         case HISTORY:
 4555 #if defined(HAVE_LIBREADLINE) && defined(HAVE_HISTORY_LIST)
 4556             h = (HIST_ENTRY *) ptr;
 4557             nchar = strlen(h->line);
 4558             if (nchar >= buflen - bl)
 4559                 goto enlarge_buffer;
 4560             strcpy(buf + bl, h->line);
 4561 #endif
 4562             break;
 4563         case OPTION:
 4564             opt = (struct dbg_option *) ptr;
 4565             if (opt->num_val != NULL)
 4566                 nchar = snprintf(buf + bl, buflen - bl,
 4567                                 "%s%c%d%c", opt->name, FSEP, *(opt->num_val), FSEP);
 4568             else
 4569                 nchar = snprintf(buf + bl, buflen - bl,
 4570                                 "%s%c%s%c", opt->name, FSEP, *(opt->str_val), FSEP);
 4571             break;
 4572         default:
 4573             break;
 4574         }
 4575 
 4576         if (nchar == 0) /* skip empty history lines etc.*/
 4577             ;
 4578         else if (nchar > 0 && nchar  < buflen - bl) {
 4579             bl += nchar;
 4580             buf[bl] = RSEP; /* record */
 4581             buf[++bl] = '\0';
 4582         } else
 4583             goto enlarge_buffer;
 4584 
 4585         switch (type) {
 4586         case BREAK:
 4587         case WATCH:
 4588             /* recreate the `commands' command strings including the `commands'
 4589              * and `end' commands; command seperator is '\034'.
 4590              * re-parsed in unserialize_list to recover the commands list.
 4591              * Alternatively, one could encode(serialize) each command and it's arguments.
 4592              */
 4593 
 4594             bl--;   /* undo RSEP from above */
 4595 
 4596             /* compute required room in buffer */
 4597             nchar = 0;
 4598             for (c = commands->next; c != commands; c = c->next) {
 4599                 nchar += (strlen(c->cmd_string) + 1);
 4600                 if (c->cmd == D_eval) {
 4601                     CMDARG *a = c->arg;
 4602                     nchar += (strlen(a->a_string) + 1); /* awk statements */
 4603                     nchar += (strlen("end") + 1);
 4604                 }
 4605             }
 4606 
 4607             if (nchar > 0) {    /* non-empty commands list */
 4608                 nchar += (strlen("commands ") + 20 + strlen("end") + 1); /* 20 for cnum (an int) */
 4609                 if (nchar > buflen - bl) {
 4610                     buflen = bl + nchar;
 4611                     erealloc(buf, char *, buflen + 3, "serialize_list");
 4612                 }
 4613                 nchar = sprintf(buf + bl, "commands %d", cnum);
 4614                 bl += nchar;
 4615                 buf[bl++] = CSEP;
 4616                 for (c = commands->next; c != commands; c = c->next) {
 4617                     nchar = strlen(c->cmd_string);
 4618                     memcpy(buf + bl, c->cmd_string, nchar);
 4619                     bl += nchar;
 4620                     buf[bl++] = CSEP;
 4621 
 4622                     if (c->cmd == D_eval) {
 4623                         CMDARG *a = c->arg;
 4624                         nchar = strlen(a->a_string);    /* statements */
 4625                         memcpy(buf + bl, a->a_string, nchar);
 4626                         bl += nchar;
 4627                         buf[bl++] = CSEP;
 4628                         nchar = strlen("end");  /* end of 'eval' */
 4629                         memcpy(buf + bl, "end", nchar);
 4630                         bl += nchar;
 4631                         buf[bl++] = CSEP;
 4632                     }
 4633                 }
 4634                 nchar = strlen("end");  /* end of 'commands' */
 4635                 memcpy(buf + bl, "end", nchar);
 4636                 bl += nchar;
 4637             }
 4638             buf[bl++] = FSEP;       /* field */
 4639             buf[bl++] = RSEP;       /* record */
 4640             buf[bl] = '\0';
 4641 
 4642             /* condition expression */
 4643             if (cndn->expr) {
 4644                 bl--;   /* undo RSEP from above */
 4645                 nchar = strlen(cndn->expr);
 4646                 if (nchar > buflen - bl) {
 4647                     buflen = bl + nchar;
 4648                     erealloc(buf, char *, buflen + 3, "serialize_list");
 4649                 }
 4650                 memcpy(buf + bl, cndn->expr, nchar);
 4651                 bl += nchar;
 4652                 buf[bl++] = FSEP;       /* field */
 4653                 buf[bl++] = RSEP;       /* record */
 4654                 buf[bl] = '\0';
 4655             }
 4656 
 4657             ptr = (type == BREAK) ? (void *) b->prev : (void *) wd->prev;
 4658             break;
 4659         case DISPLAY:
 4660             ptr = (void *) wd->prev;
 4661             break;
 4662         case HISTORY:
 4663             ptr = (void *) hist_list[++hist_index];
 4664             break;
 4665         case OPTION:
 4666             ptr = (void *) (++opt);
 4667             break;
 4668         default:
 4669             break;
 4670         }
 4671     }
 4672 
 4673     if (bl > 0) /* non-empty list */
 4674         setenv(env_variable[type], buf, 1);
 4675 }
 4676 
 4677 
 4678 static void
 4679 unserialize_commands(char *str, int str_len)
 4680 {
 4681     if (str_len <= 0 || str == NULL)
 4682         return;
 4683     commands_string = str;
 4684     commands_string_len = str_len;
 4685     push_cmd_src(INVALID_HANDLE, false, read_commands_string, 0, 0, EXIT_FATAL);
 4686     line_sep = CSEP;
 4687     read_command();     /* forced to return in do_commands */
 4688     pop_cmd_src();
 4689 }
 4690 
 4691 
 4692 /* unserialize_list_item --- create a list_item structure from unserialized data */
 4693 
 4694 static struct list_item *
 4695 unserialize_list_item(struct list_item *list, char **pstr, int *pstr_len, int field_cnt)
 4696 {
 4697     int num, type, i;
 4698     struct list_item *l;
 4699     NODE *symbol = NULL;
 4700     int sub_cnt = 0, cnt;
 4701     NODE **subs = NULL;
 4702 
 4703     /* subscript    -- number type sname num_subs subs [commands [condition]]
 4704      * variable     -- number type sname [commands [condition]]
 4705      * field        -- number type symbol(numbr) commands [commands [condition]]
 4706      */
 4707 
 4708     num = strtol(pstr[0], NULL, 0);
 4709     type = strtol(pstr[1], NULL, 0);
 4710 
 4711     if (type == D_field) {
 4712         int field_num;
 4713         field_num = strtol(pstr[2], NULL, 0);
 4714         symbol = make_number((AWKNUM) field_num);
 4715         cnt = 3;
 4716     } else {
 4717         char *name;
 4718         name = estrdup(pstr[2], pstr_len[2]);
 4719         symbol = find_symbol(name, NULL);
 4720         efree(name);
 4721         if (symbol == NULL)
 4722             return NULL;
 4723         cnt = 3;
 4724         if (type == D_subscript) {
 4725             int sub_len;
 4726             sub_cnt = strtol(pstr[3], NULL, 0);
 4727             emalloc(subs, NODE **, sub_cnt * sizeof(NODE *), "unserialize_list_item");
 4728             cnt++;
 4729             for (i = 0; i < sub_cnt; i++) {
 4730                 sub_len = strtol(pstr[cnt], NULL, 0);
 4731                 subs[i] = make_string(pstr[cnt + 1], sub_len);
 4732                 cnt += 2;
 4733             }
 4734         }
 4735     }
 4736 
 4737     l = add_item(list, type, symbol, NULL);
 4738     if (type == D_subscript) {
 4739         l->num_subs = sub_cnt;
 4740         l->subs = subs;
 4741     }
 4742     l->number = num;    /* keep same item number across executions */
 4743 
 4744     if (list == &watch_list) {
 4745         initialize_watch_item(l);
 4746         /* unserialize watchpoint `commands' */
 4747         unserialize_commands(pstr[cnt], pstr_len[cnt]);
 4748         cnt++;
 4749         if (field_cnt > cnt) {
 4750             char *expr;
 4751             expr = estrdup(pstr[cnt], pstr_len[cnt]);
 4752             if (parse_condition(D_watch, l->number, expr) != 0)
 4753                 efree(expr);
 4754         }
 4755         if (num > list->number)   /* update list number counter */
 4756             list->number = num;
 4757     } else
 4758         list->number = num;
 4759 
 4760     return l;
 4761 }
 4762 
 4763 /* unserialize_breakpoint --- create a breakpoint structure from unserialized data */
 4764 
 4765 static BREAKPOINT *
 4766 unserialize_breakpoint(char **pstr, int *pstr_len, int field_cnt)
 4767 {
 4768     char *src;
 4769     int lineno;
 4770     BREAKPOINT *b = NULL;
 4771     INSTRUCTION *rp;
 4772     SRCFILE *s;
 4773 
 4774     /* src source_line flags ignore_count hit_count number commands [condition] */
 4775 
 4776     src = estrdup(pstr[0], pstr_len[0]);
 4777     s = source_find(src);
 4778     efree(src);
 4779     if (s == NULL)
 4780         return NULL;
 4781     src = s->src;
 4782     lineno = strtol(pstr[1], NULL, 0);
 4783     if (lineno <= 0 || lineno > s->srclines)
 4784         return NULL;
 4785     rp = find_rule(src, lineno);
 4786     if (rp == NULL
 4787             ||  (b = set_breakpoint_at(rp, lineno, true)) == NULL
 4788     )
 4789         return NULL;
 4790 
 4791     b->flags =  strtol(pstr[2], NULL, 0);
 4792     b->ignore_count = strtol(pstr[3], NULL, 0);
 4793     b->hit_count = strtol(pstr[4], NULL, 0);
 4794     b->number =  strtol(pstr[5], NULL, 0);  /* same number as previous run */
 4795 
 4796     if (field_cnt > 6)  /* unserialize breakpoint `commands' */
 4797         unserialize_commands(pstr[6], pstr_len[6]);
 4798 
 4799     if (field_cnt > 7) {    /* condition expression */
 4800         char *expr;
 4801         expr = estrdup(pstr[7], pstr_len[7]);
 4802         if (parse_condition(D_break, b->number, expr) != 0)
 4803             efree(expr);
 4804     }
 4805 
 4806     if (b->number > watch_list.number)  /* watch and break has same number counter */
 4807         watch_list.number = b->number;  /* update counter */
 4808     return b;
 4809 }
 4810 
 4811 /* unserialize_option --- set a debugger option from unserialized data. */
 4812 
 4813 static struct dbg_option *
 4814 unserialize_option(char **pstr, int *pstr_len, int field_cnt ATTRIBUTE_UNUSED)
 4815 {
 4816     const struct dbg_option *opt;
 4817 
 4818     for (opt = option_list; opt->name; opt++) {
 4819         if (strncmp(pstr[0], opt->name, pstr_len[0]) == 0) {
 4820             char *value;
 4821 
 4822             value = estrdup(pstr[1], pstr_len[1]);
 4823             (*(opt->assign))(value);
 4824             efree(value);
 4825             return ((struct dbg_option *) opt);
 4826         }
 4827     }
 4828     return NULL;
 4829 }
 4830 
 4831 /* unserialize_list -- reconstruct list from serialized data stored in
 4832  *                environment variable.
 4833  */
 4834 
 4835 static void
 4836 unserialize_list(int type)
 4837 {
 4838     char *val;
 4839     char *p, *q, *r, *s;
 4840 #define MAX_FIELD 30
 4841     static char *pstr[MAX_FIELD];
 4842     static int pstr_len[MAX_FIELD];
 4843 
 4844     val = getenv(env_variable[type]);
 4845     if (val == NULL)
 4846         return;
 4847 
 4848     for (p = val; (q = strchr(p, RSEP)) != NULL; p = q + 1) {
 4849         int field_cnt = 0;
 4850         if (type == HISTORY) {
 4851             *q = '\0';
 4852             add_history(p);
 4853             *q = RSEP;
 4854             continue;
 4855         }
 4856 
 4857         r = p;
 4858         while ((s = strchr(r, FSEP)) != NULL && s < q) {
 4859             pstr[field_cnt] = r;
 4860             pstr_len[field_cnt] = (int) (s - r);
 4861             r = s + 1;
 4862             field_cnt++;
 4863             if (field_cnt == MAX_FIELD)
 4864 #ifdef GAWKDEBUG
 4865                 fatal("Increase MAX_FIELD and recompile.");
 4866 #else
 4867                 return;
 4868 #endif
 4869         }
 4870 
 4871         switch (type) {
 4872         case BREAK:
 4873             (void) unserialize_breakpoint(pstr, pstr_len, field_cnt);
 4874             break;
 4875         case DISPLAY:
 4876             (void) unserialize_list_item(&display_list, pstr, pstr_len, field_cnt);
 4877             break;
 4878         case WATCH:
 4879             (void) unserialize_list_item(&watch_list, pstr, pstr_len, field_cnt);
 4880             break;
 4881         case OPTION:
 4882             (void) unserialize_option(pstr, pstr_len, field_cnt);
 4883             break;
 4884         case HISTORY:
 4885             /* processed at the beginning of for loop */
 4886             break;
 4887         default:
 4888             break;
 4889         }
 4890     }
 4891 
 4892 #ifdef HAVE_LIBREADLINE
 4893     if (type == HISTORY)
 4894         sess_history_base = history_length;
 4895 #endif
 4896 
 4897     unsetenv(env_variable[type]);
 4898 #undef MAX_FIELD
 4899 }
 4900 
 4901 static int
 4902 prompt_yes_no(const char *mesg, char res_true, int res_default, FILE *fp)
 4903 {
 4904     char *in_str;
 4905     int ret = res_default; /* default */
 4906 
 4907     if (input_from_tty) {
 4908         fprintf(fp, "%s", _(mesg));
 4909         in_str = read_a_line(NULL);
 4910         if (in_str == NULL) /* EOF */
 4911             exit(EXIT_FAILURE);
 4912         ret = (*in_str == res_true);
 4913         efree(in_str);
 4914     }
 4915     return ret;
 4916 }
 4917 
 4918 /* has_break_or_watch_point --- check if given breakpoint or watchpoint
 4919  *                              number exists. When flag any is true,
 4920  *                              check if any breakpoint/watchpoint
 4921  *                              has been set (ignores num). Returns
 4922  *                              type (breakpoint or watchpoint) or 0.
 4923  */
 4924 
 4925 int
 4926 has_break_or_watch_point(int *pnum, bool any)
 4927 {
 4928     BREAKPOINT *b = NULL;
 4929     struct list_item *w = NULL;
 4930 
 4931     if (any) {
 4932         if (breakpoints.next != &breakpoints)
 4933             b = breakpoints.next;
 4934         if (watch_list.next != &watch_list)
 4935             w = watch_list.next;
 4936 
 4937         if (! b && ! w)
 4938             return 0;
 4939         if (b && ! w) {
 4940             *pnum = b->number;
 4941             return D_break;
 4942         }
 4943         if (w && ! b) {
 4944             *pnum = w->number;
 4945             return D_watch;
 4946         }
 4947         if (w->number > b->number) {
 4948             *pnum = w->number;
 4949             return D_watch;
 4950         }
 4951         *pnum = b->number;
 4952         return D_break;
 4953     }
 4954 
 4955     /* N.B: breakpoints and watchpoints get numbers from a single
 4956      * counter/sequencer watch_list.number.
 4957      */
 4958 
 4959     for (b = breakpoints.next; b != &breakpoints; b = b->next) {
 4960         if (b->number == *pnum)
 4961             return D_break;
 4962     }
 4963     for (w = watch_list.next; w != &watch_list; w = w->next) {
 4964         if (w->number == *pnum)
 4965             return D_watch;
 4966     }
 4967 
 4968     return 0;
 4969 }
 4970 
 4971 /* delete_commands_item --- delete item(command) from `commands' list. */
 4972 
 4973 static void
 4974 delete_commands_item(struct commands_item *c)
 4975 {
 4976     efree(c->cmd_string);
 4977     free_cmdarg(c->arg);
 4978     c->next->prev = c->prev;
 4979     c->prev->next = c->next;
 4980     efree(c);
 4981 }
 4982 
 4983 /* do_commands --- commands command */
 4984 
 4985 int
 4986 do_commands(CMDARG *arg, int cmd)
 4987 {
 4988     static BREAKPOINT *b;
 4989     static struct list_item *w;
 4990     static struct commands_item *commands;
 4991     struct commands_item *c;
 4992 
 4993     if (cmd == D_commands) {
 4994         int num = -1, type;
 4995         if (arg == NULL)
 4996             type = has_break_or_watch_point(&num, true);
 4997         else {
 4998             num = arg->a_int;
 4999             type = has_break_or_watch_point(&num, false);
 5000         }
 5001         b = NULL;
 5002         w = NULL;
 5003         if (type == D_break)
 5004             b = find_breakpoint(num);
 5005         else if (type == D_watch)
 5006             w = find_item(&watch_list, num);
 5007         assert((b != NULL) || (w != NULL));
 5008         commands = (b != NULL) ? &b->commands : &w->commands;
 5009 
 5010         /* delete current commands */
 5011         for (c = commands->next; c != commands; c = c->next) {
 5012             c = c->prev;
 5013             delete_commands_item(c->next);
 5014         }
 5015         return false;
 5016 
 5017     } else if (cmd == D_end) {
 5018         commands = NULL;
 5019         if (read_a_line == read_commands_string) /* unserializig commands */
 5020             return true;    /* done unserializing, terminate zzparse() */
 5021         return false;
 5022 
 5023     } else if (cmd == D_silent) {
 5024         if (b != NULL)
 5025             b->silent = true;
 5026         else if (w != NULL)
 5027             w->silent = true;
 5028         /* we also append silent command to the list for use
 5029          * in `info break(watch)', and to simplify
 5030          * serialization/unserialization of commands.
 5031          */
 5032     }
 5033 
 5034     assert(commands != NULL);
 5035 
 5036     emalloc(c, struct commands_item *, sizeof(struct commands_item), "do_commands");
 5037     c->next = NULL;
 5038     c->cmd = cmd;
 5039 
 5040     /* N.B.: first arg is the command string, see command.y */
 5041     c->cmd_string = arg->a_string;
 5042     c->arg = arg->next; /* actual arguments to the command */
 5043     efree(arg);
 5044 
 5045     /* append to the list */
 5046     c->prev = commands->prev;
 5047     c->next = commands;
 5048     commands->prev = c;
 5049     c->prev->next = c;
 5050     return false;
 5051 }
 5052 
 5053 /* execute_commands --- execute breakpoint/watchpoint commands, the first
 5054  *                      command that resumes execution terminates
 5055  *                      commands processing.
 5056  */
 5057 
 5058 static int
 5059 execute_commands(struct commands_item *commands)
 5060 {
 5061     struct commands_item *c;
 5062     Func_cmd cmd_ptr;
 5063     bool ret = false;
 5064 
 5065     for (c = commands->next; c != commands; c = c->next) {
 5066         if (c->cmd == D_silent)
 5067             continue;
 5068         cmd_ptr = get_command(c->cmd);      /* command handler */
 5069         ret = (*cmd_ptr)(c->arg, c->cmd);
 5070         if (ret)    /* resume execution (continue, next etc.) */
 5071             break;
 5072     }
 5073     return ret;
 5074 }
 5075 
 5076 /* do_print_f --- printf command */
 5077 
 5078 int
 5079 do_print_f(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
 5080 {
 5081     int count = 0;
 5082     int i;
 5083     CMDARG *a;
 5084     NODE **tmp;
 5085     char *name;
 5086     NODE *r;
 5087     volatile jmp_buf fatal_tag_stack;
 5088 
 5089     /* count maximum required size for tmp */
 5090     for (a = arg; a != NULL ; a = a->next)
 5091         count++;
 5092     emalloc(tmp, NODE **, count * sizeof(NODE *), "do_print_f");
 5093 
 5094     for (i = 0, a = arg; a != NULL ; i++, a = a->next) {
 5095         switch (a->type) {
 5096         case D_variable:
 5097             name = a->a_string;
 5098             r = find_symbol(name, NULL);
 5099             if (r == NULL)
 5100                 goto done;
 5101             if (r->type == Node_var_new)
 5102                 tmp[i] = Nnull_string;
 5103             else if (r->type != Node_var) {
 5104                 d_error(_("`%s' is not a scalar variable"), name);
 5105                 goto done;
 5106             } else
 5107                 tmp[i] = r->var_value;
 5108             break;
 5109         case D_field:
 5110         {
 5111             long field_num;
 5112             r = a->a_node;
 5113             field_num = get_number_si(r);
 5114             tmp[i] = *get_field(field_num, NULL);
 5115         }
 5116             break;
 5117         case D_subscript:
 5118         {
 5119             int cnt = a->a_count;
 5120             name = a->a_string;
 5121             r = find_array(name);
 5122             if (r == NULL)
 5123                 goto done;
 5124 
 5125             for (; cnt > 0; cnt--) {
 5126                 NODE *value, *subs;
 5127                 a = a->next;
 5128                 subs = a->a_node;
 5129                 value = in_array(r, subs);
 5130                 if (cnt == 1) {
 5131                     if (value == NULL)
 5132                         tmp[i] = Nnull_string;      /* FIXME: goto done ? */
 5133                     else if (value->type == Node_var_array) {
 5134                         d_error(_("attempt to use array `%s[\"%.*s\"]' in a scalar context"),
 5135                                     name, (int) subs->stlen, subs->stptr);
 5136                         goto done;
 5137                     } else
 5138                         tmp[i] = value;
 5139                 } else {
 5140                     if (value == NULL) {
 5141                         d_error(_("[\"%.*s\"] not in array `%s'"),
 5142                                     (int) subs->stlen, subs->stptr, name);
 5143                         goto done;
 5144                     } else if (value->type != Node_var_array) {
 5145                         d_error(_("attempt to use scalar `%s[\"%.*s\"]' as array"),
 5146                                     name, (int) subs->stlen, subs->stptr);
 5147                         goto done;
 5148                     } else {
 5149                         r = value;
 5150                         name = r->vname;
 5151                     }
 5152                 }
 5153             }
 5154         }
 5155             break;
 5156         case D_node:
 5157             tmp[i] = a->a_node;
 5158             break;
 5159         default:
 5160             break;
 5161         }
 5162     }
 5163 
 5164     tmp[0] = force_string(tmp[0]);
 5165 
 5166     PUSH_BINDING(fatal_tag_stack, fatal_tag, fatal_tag_valid);
 5167     if (setjmp(fatal_tag) == 0)
 5168         r = format_tree(tmp[0]->stptr, tmp[0]->stlen, tmp, i);
 5169     else {
 5170         /* fatal error, restore exit_val of program */
 5171         exit_val = EXIT_SUCCESS;
 5172         r = NULL;
 5173     }
 5174     POP_BINDING(fatal_tag_stack, fatal_tag, fatal_tag_valid);
 5175 
 5176     if (r != NULL) {
 5177         (void) fwrite(r->stptr, sizeof(char), r->stlen, out_fp);
 5178         unref(r);
 5179     }
 5180 done:
 5181     efree(tmp);
 5182     return false;
 5183 }
 5184 
 5185 /* do_source --- source command */
 5186 
 5187 int