#include "m4.h"
#include <sys/stat.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
FILE *debug = NULL;
static struct obstack trace;
extern int expansion_level;
static void debug_set_file _((FILE *));
void
debug_init (void)
{
debug_set_file (stderr);
obstack_init (&trace);
}
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;
}
}
}
obstack_free (&trace, obstack_finish (&trace));
return level;
}
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;
}
}
}
void
debug_flush_files (void)
{
fflush (stdout);
fflush (stderr);
if (debug != NULL && debug != stdout && debug != stderr)
fflush (debug);
}
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;
}
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);
}
#ifdef __STDC__
static void
trace_format (const char *fmt, ...)
#else
static void
trace_format (...)
#endif
{
#ifndef __STDC__
const char *fmt;
#endif
va_list args;
char ch;
int d;
char nbuf[32];
const char *s;
int slen;
int maxlen;
#ifdef __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;
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);
}
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);
}
static void
trace_flush (void)
{
char *line;
obstack_1grow (&trace, '\0');
line = obstack_finish (&trace);
DEBUG_PRINT1 ("%s\n", line);
obstack_free (&trace, line);
}
void
trace_prepre (const char *name, int id)
{
trace_header (id);
trace_format ("%s ...", name);
trace_flush ();
}
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 ();
}
}
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 ();
}