diff options
Diffstat (limited to 'src/debug.c')
-rw-r--r-- | src/debug.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 00000000..ad1988d1 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,421 @@ +/* GNU m4 -- A simple macro processor + Copyright (C) 1991, 1992, 1993, 1994 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 2, 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "m4.h" + +#include <sys/stat.h> + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +/* File for debugging output. */ +FILE *debug = NULL; + +/* Obstack for trace messages. */ +static struct obstack trace; + +extern int expansion_level; + +static void debug_set_file _((FILE *)); + +/*----------------------------------. +| Initialise the debugging module. | +`----------------------------------*/ + +void +debug_init (void) +{ + debug_set_file (stderr); + obstack_init (&trace); +} + +/*-----------------------------------------------------------------. +| Function to decode the debugging flags OPTS. Used by main while | +| processing option -d, and by the builtin debugmode (). | +`-----------------------------------------------------------------*/ + +int +debug_decode (const char *opts) +{ + int level; + + if (opts == NULL || *opts == '\0') + level = DEBUG_TRACE_DEFAULT; + else + { + for (level = 0; *opts; opts++) + { + switch (*opts) + { + case 'a': + level |= DEBUG_TRACE_ARGS; + break; + + case 'e': + level |= DEBUG_TRACE_EXPANSION; + break; + + case 'q': + level |= DEBUG_TRACE_QUOTE; + break; + + case 't': + level |= DEBUG_TRACE_ALL; + break; + + case 'l': + level |= DEBUG_TRACE_LINE; + break; + + case 'f': + level |= DEBUG_TRACE_FILE; + break; + + case 'p': + level |= DEBUG_TRACE_PATH; + break; + + case 'c': + level |= DEBUG_TRACE_CALL; + break; + + case 'i': + level |= DEBUG_TRACE_INPUT; + break; + + case 'x': + level |= DEBUG_TRACE_CALLID; + break; + + case 'V': + level |= DEBUG_TRACE_VERBOSE; + break; + + default: + return -1; + } + } + } + + /* This is to avoid screwing up the trace output due to changes in the + debug_level. */ + + obstack_free (&trace, obstack_finish (&trace)); + + return level; +} + +/*------------------------------------------------------------------------. +| Change the debug output stream to FP. If the underlying file is the | +| same as stdout, use stdout instead so that debug messages appear in the | +| correct relative position. | +`------------------------------------------------------------------------*/ + +static void +debug_set_file (FILE *fp) +{ + struct stat stdout_stat, debug_stat; + + if (debug != NULL && debug != stderr && debug != stdout) + fclose (debug); + debug = fp; + + if (debug != NULL && debug != stdout) + { + if (fstat (fileno (stdout), &stdout_stat) < 0) + return; + if (fstat (fileno (debug), &debug_stat) < 0) + return; + + if (stdout_stat.st_ino == debug_stat.st_ino + && stdout_stat.st_dev == debug_stat.st_dev) + { + if (debug != stderr) + fclose (debug); + debug = stdout; + } + } +} + +/*-----------------------------------------------------------. +| Serialize files. Used before executing a system command. | +`-----------------------------------------------------------*/ + +void +debug_flush_files (void) +{ + fflush (stdout); + fflush (stderr); + if (debug != NULL && debug != stdout && debug != stderr) + fflush (debug); +} + +/*-------------------------------------------------------------------------. +| Change the debug output to file NAME. If NAME is NULL, debug output is | +| reverted to stderr, and if empty debug output is discarded. Return TRUE | +| iff the output stream was changed. | +`-------------------------------------------------------------------------*/ + +boolean +debug_set_output (const char *name) +{ + FILE *fp; + + if (name == NULL) + debug_set_file (stderr); + else if (*name == '\0') + debug_set_file (NULL); + else + { + fp = fopen (name, "a"); + if (fp == NULL) + return FALSE; + + debug_set_file (fp); + } + return TRUE; +} + +/*-----------------------------------------------------------------------. +| Print the header of a one-line debug message, starting by "m4 debug". | +`-----------------------------------------------------------------------*/ + +void +debug_message_prefix (void) +{ + fprintf (debug, "m4 debug: "); + if (debug_level & DEBUG_TRACE_FILE) + fprintf (debug, "%s: ", current_file); + if (debug_level & DEBUG_TRACE_LINE) + fprintf (debug, "%d: ", current_line); +} + +/* The rest of this file contains the functions for macro tracing output. + All tracing output for a macro call is collected on an obstack TRACE, + and printed whenever the line is complete. This prevents tracing + output from interfering with other debug messages generated by the + various builtins. */ + +/*---------------------------------------------------------------------. +| Tracing output is formatted here, by a simplified printf-to-obstack | +| function trace_format (). Understands only %S, %s, %d, %l (optional | +| left quote) and %r (optional right quote). | +`---------------------------------------------------------------------*/ + +#if __STDC__ +static void +trace_format (const char *fmt, ...) +#else +static void +trace_format (...) +#endif +{ +#if ! __STDC__ + const char *fmt; +#endif + va_list args; + char ch; + + int d; + char nbuf[32]; + const char *s; + int slen; + int maxlen; + +#if __STDC__ + va_start (args, fmt); +#else + va_start (args); + fmt = va_arg (args, const char *); +#endif + + while (TRUE) + { + while ((ch = *fmt++) != '\0' && ch != '%') + obstack_1grow (&trace, ch); + + if (ch == '\0') + break; + + maxlen = 0; + switch (*fmt++) + { + case 'S': + maxlen = max_debug_argument_length; + /* fall through */ + + case 's': + s = va_arg (args, const char *); + break; + + case 'l': + s = (debug_level & DEBUG_TRACE_QUOTE) ? lquote.string : ""; + break; + + case 'r': + s = (debug_level & DEBUG_TRACE_QUOTE) ? rquote.string : ""; + break; + + case 'd': + d = va_arg (args, int); + sprintf (nbuf, "%d", d); + s = nbuf; + break; + + default: + s = ""; + break; + } + + slen = strlen (s); + if (maxlen == 0 || maxlen > slen) + obstack_grow (&trace, s, slen); + else + { + obstack_grow (&trace, s, maxlen); + obstack_grow (&trace, "...", 3); + } + } + + va_end (args); +} + +/*------------------------------------------------------------------. +| Format the standard header attached to all tracing output lines. | +`------------------------------------------------------------------*/ + +static void +trace_header (int id) +{ + trace_format ("m4trace:"); + if (debug_level & DEBUG_TRACE_FILE) + trace_format ("%s:", current_file); + if (debug_level & DEBUG_TRACE_LINE) + trace_format ("%d:", current_line); + trace_format (" -%d- ", expansion_level); + if (debug_level & DEBUG_TRACE_CALLID) + trace_format ("id %d: ", id); +} + +/*----------------------------------------------------. +| Print current tracing line, and clear the obstack. | +`----------------------------------------------------*/ + +static void +trace_flush (void) +{ + char *line; + + obstack_1grow (&trace, '\0'); + line = obstack_finish (&trace); + DEBUG_PRINT1 ("%s\n", line); + obstack_free (&trace, line); +} + +/*-------------------------------------------------------------. +| Do pre-argument-collction tracing for macro NAME. Used from | +| expand_macro (). | +`-------------------------------------------------------------*/ + +void +trace_prepre (const char *name, int id) +{ + trace_header (id); + trace_format ("%s ...", name); + trace_flush (); +} + +/*-----------------------------------------------------------------------. +| Format the parts of a trace line, that can be made before the macro is | +| actually expanded. Used from expand_macro (). | +`-----------------------------------------------------------------------*/ + +void +trace_pre (const char *name, int id, int argc, token_data **argv) +{ + int i; + const builtin *bp; + + trace_header (id); + trace_format ("%s", name); + + if (argc > 1 && (debug_level & DEBUG_TRACE_ARGS)) + { + trace_format ("("); + + for (i = 1; i < argc; i++) + { + if (i != 1) + trace_format (", "); + + switch (TOKEN_DATA_TYPE (argv[i])) + { + case TOKEN_TEXT: + trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv[i])); + break; + + case TOKEN_FUNC: + bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[i])); + if (bp == NULL) + { + M4ERROR ((warning_status, 0, "\ +INTERNAL ERROR: Builtin not found in builtin table! (trace_pre ())")); + abort (); + } + trace_format ("<%s>", bp->name); + break; + + default: + M4ERROR ((warning_status, 0, + "INTERNAL ERROR: Bad token data type (trace_pre ())")); + abort (); + } + + } + trace_format (")"); + } + + if (debug_level & DEBUG_TRACE_CALL) + { + trace_format (" -> ???"); + trace_flush (); + } +} + +/*-------------------------------------------------------------------. +| Format the final part of a trace line and print it all. Used from | +| expand_macro (). | +`-------------------------------------------------------------------*/ + +void +trace_post (const char *name, int id, int argc, token_data **argv, + const char *expanded) +{ + if (debug_level & DEBUG_TRACE_CALL) + { + trace_header (id); + trace_format ("%s%s", name, (argc > 1) ? "(...)" : ""); + } + + if (expanded && (debug_level & DEBUG_TRACE_EXPANSION)) + trace_format (" -> %l%S%r", expanded); + trace_flush (); +} |