diff options
Diffstat (limited to 'sed/debug.c')
-rw-r--r-- | sed/debug.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/sed/debug.c b/sed/debug.c new file mode 100644 index 0000000..9ec37b6 --- /dev/null +++ b/sed/debug.c @@ -0,0 +1,456 @@ +/* GNU SED, a batch stream editor. + Copyright (C) 2018 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Assaf Gordon. */ + +/* debug.c: debugging functions */ + +#include "sed.h" +#include "basicdefs.h" +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <assert.h> + +/* indentation level when printing the program */ +static int block_level = 0; + + +void +debug_print_char (char c) +{ + if (ISPRINT (c) && c != '\\') + { + putchar (c); + return; + } + + putchar ('\\'); + switch (c) + { + case '\a': + putchar ('a'); + break; + case '\f': + putchar ('f'); + break; + case '\r': + putchar ('r'); + break; + case '\t': + putchar ('t'); + break; + case '\v': + putchar ('v'); + break; + case '\n': + putchar ('n'); + break; + case '\\': + putchar ('\\'); + break; + + default: + printf ("o%03o", (unsigned int) c); + } +} + +static void +debug_print_regex_pattern (const char *pat, size_t len) +{ + const char *p = pat; + while (len--) + { + if (*p == '/') + fputs ("\\/", stdout); + else + debug_print_char (*p); + ++p; + } +} + +static void +debug_print_regex_flags (const struct regex *r, bool addr) +{ + if (!r) + return; + +#ifdef REG_PERL + if (r->flags & REG_DOTALL) /* REG_PERL */ + putchar ('s'); + if (r->flags & REG_EXTENDED) /* REG_PERL */ + putchar ('x'); +#endif + + if (r->flags & REG_ICASE) + putchar (addr ? 'I' : 'i'); + if (r->flags & REG_NEWLINE) + putchar (addr ? 'M' : 'm'); +} + +static void +debug_print_regex (const struct regex *r) +{ + if (!r) + { + /* Previous Regex */ + fputs ("//", stdout); + return; + } + + putchar ('/'); + debug_print_regex_pattern (r->re, r->sz); + putchar ('/'); +} + +static void +debug_print_addr (const struct addr *a) +{ + if (!a) + return; + switch (a->addr_type) + { + case ADDR_IS_NULL: + fputs ("[ADDR-NULL]", stdout); + break; + case ADDR_IS_REGEX: + debug_print_regex (a->addr_regex); + debug_print_regex_flags (a->addr_regex, true); + break; + case ADDR_IS_NUM: + printf ("%lu", a->addr_number); + break; + case ADDR_IS_NUM_MOD: + printf ("%lu~%lu", a->addr_number, a->addr_step); + break; + case ADDR_IS_STEP: + printf ("+%lu", a->addr_step); + break; + case ADDR_IS_STEP_MOD: + printf ("~%lu", a->addr_step); + break; + case ADDR_IS_LAST: + putchar ('$'); + break; + } +} + +static void +debug_print_subst_replacement (const struct replacement *r) +{ + enum replacement_types last_repl_type = REPL_ASIS; + + if (!r) + return; + + const struct replacement *p = r; + while (p) + { + if (p->repl_type != last_repl_type) + { + /* Special GNU replacements \E\U\u\L\l should be printed + BEFORE the 'prefix' .... the 'prefix' refers to being + before the backreference. */ + putchar ('\\'); + if (p->repl_type == 0) + putchar ('E'); + else if (p->repl_type == REPL_UPPERCASE) + putchar ('U'); + else if (p->repl_type == REPL_LOWERCASE) + putchar ('L'); + else if ((p->repl_type & REPL_MODIFIERS) == REPL_UPPERCASE_FIRST) + putchar ('u'); + else if ((p->repl_type & REPL_MODIFIERS) == REPL_LOWERCASE_FIRST) + putchar ('l'); + + last_repl_type = p->repl_type; + } + + if (p->prefix_length) + fwrite (p->prefix, 1, p->prefix_length, stdout); + + if (p->subst_id != -1) + { + if (p->subst_id == 0) + putchar ('&'); + else + printf ("\\%d", p->subst_id); + } + + p = p->next; + } +} + +static void +debug_print_output_file (const struct output *o) +{ + if (!o) + return; + + fputs (o->name, stdout); +} + +static void +debug_print_subst (const struct subst *s) +{ + if (!s) + return; + + debug_print_regex (s->regx); + debug_print_subst_replacement (s->replacement); + putchar ('/'); + + debug_print_regex_flags (s->regx, false); + + if (s->global) + putchar ('g'); + if (s->eval) + putchar ('e'); + if (s->print) + putchar ('p'); + if (s->numb) + printf ("%lu", s->numb); + if (s->outf) + { + putchar ('w'); + debug_print_output_file (s->outf); + } +} + +static void +debug_print_translation (const struct sed_cmd *sc) +{ + unsigned int i; + + if (mb_cur_max > 1) + { + /* multibyte translation */ + putchar ('/'); + for (i = 0; sc->x.translatemb[2 * i] != NULL; i++) + fputs (sc->x.translatemb[2 * i], stdout); + putchar ('/'); + for (i = 0; sc->x.translatemb[2 * i] != NULL; i++) + fputs (sc->x.translatemb[2 * i + 1], stdout); + putchar ('/'); + } + else + { + /* unibyte translation */ + putchar ('/'); + for (i = 0; i < 256; ++i) + if (sc->x.translate[i] != (unsigned char) i) + putchar ((unsigned char) i); + putchar ('/'); + for (i = 0; i < 256; ++i) + if (sc->x.translate[i] != (unsigned char) i) + putchar (sc->x.translate[i]); + putchar ('/'); + } +} + +static void +debug_print_function (const struct vector *program, const struct sed_cmd *sc) +{ + if (!sc) + return; + + putchar (sc->cmd); + + switch (sc->cmd) /* LCOV_EXCL_BR */ + { + case '=': + break; + + case ':': + printf ("%s", sc->x.label_name); + break; + + case '{': + break; + + case '}': + break; + + case '#': /* LCOV_EXCL_LINE */ + /* should not happen - discarded during compilation. */ + assert (0); /* LCOV_EXCL_LINE */ + + case 'a': + case 'c': + case 'i': + fputs ("\\", stdout); + if (sc->x.cmd_txt.text_length) + fwrite (sc->x.cmd_txt.text, 1, sc->x.cmd_txt.text_length, stdout); + break; + + case 'b': + case 't': + case 'T': + { + if (sc->x.jump_index < program->v_length) + { + const char *label_name = program->v[sc->x.jump_index].x.label_name; + if (label_name) + printf (" %s", label_name); + } + } + break; + + case 'D': + break; + + case 'd': + break; + + case 'e': + putchar (' '); + fwrite (sc->x.cmd_txt.text, 1, sc->x.cmd_txt.text_length, stdout); + break; + + case 'F': + break; + + case 'g': + break; + + case 'G': + break; + + case 'h': + break; + + case 'H': + break; + + /* 'i' is lumped above with 'a' and 'c' */ + + case 'L': + case 'l': + case 'q': + case 'Q': + if (sc->x.int_arg != -1) + printf (" %d", sc->x.int_arg); + break; + + case 'n': + break; + + case 'N': + break; + + case 'P': + break; + + case 'p': + break; + + /* 'q','Q' are lumped above with 'L' and 'l' */ + + case 'r': + putchar (' '); + fputs (sc->x.fname, stdout); + break; + + case 'R': + putchar (' '); + fputs (sc->x.inf->name, stdout); + break; + + case 's': + debug_print_subst (sc->x.cmd_subst); + break; + + /* 't','T' are lumped above with 'b' */ + + case 'v': /* LCOV_EXCL_LINE */ + /* should not happen - handled during compilation then discarded. */ + assert (0); /* LCOV_EXCL_LINE */ + + case 'W': + debug_print_output_file (sc->x.outf); + break; + + case 'w': + debug_print_output_file (sc->x.outf); + break; + + case 'x': + break; + + case 'y': + debug_print_translation (sc); + break; + + case 'z': + break; + + default: /* LCOV_EXCL_LINE */ + /* should not happen - unless missed a sed command. */ + assert (0); /* LCOV_EXCL_LINE */ + } +} + +void +debug_print_command (const struct vector *program, const struct sed_cmd *sc) +{ + bool addr_bang; + if (!program) + return; + + if (sc->cmd == '}') + --block_level; + + for (int j = 0; j < block_level; ++j) + fputs (" ", stdout); + + debug_print_addr (sc->a1); + if (sc->a2) + putchar (','); + debug_print_addr (sc->a2); + + addr_bang = sc->addr_bang; + /* Implmentation detail: GNU Sed implements beginning of block + by negating the matched address and jumping if there's no match. */ + if (sc->cmd == '{') + addr_bang = !addr_bang; + if (addr_bang) + putchar ('!'); + + if (sc->a1 || sc->a2) + putchar (' '); + + debug_print_function (program, sc); + + putchar ('\n'); + + if (sc->cmd == '{') + ++block_level; +} + +void +debug_print_program (const struct vector *program) +{ + if (!program) + return; + + block_level = 1; + puts ("SED PROGRAM:"); + for (size_t i = 0; i < program->v_length; ++i) + debug_print_command (program, &program->v[i]); + block_level = 0; +} |