source: vendor/m4/1.4.8/src/debug.c

Last change on this file was 3090, checked in by bird, 18 years ago

m4 1.4.8

File size: 11.3 KB
Line 
1/* GNU m4 -- A simple macro processor
2
3 Copyright (C) 1991, 1992, 1993, 1994, 2004, 2006 Free Software
4 Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA
20*/
21
22#include "m4.h"
23
24#include <stdarg.h>
25#include <sys/stat.h>
26
27/* File for debugging output. */
28FILE *debug = NULL;
29
30/* Obstack for trace messages. */
31static struct obstack trace;
32
33extern int expansion_level;
34
35static void debug_set_file (FILE *);
36
37/*----------------------------------.
38| Initialise the debugging module. |
39`----------------------------------*/
40
41void
42debug_init (void)
43{
44 debug_set_file (stderr);
45 obstack_init (&trace);
46}
47
48
49/*-----------------------------------------------------------------.
50| Function to decode the debugging flags OPTS. Used by main while |
51| processing option -d, and by the builtin debugmode (). |
52`-----------------------------------------------------------------*/
53
54int
55debug_decode (const char *opts)
56{
57 int level;
58
59 if (opts == NULL || *opts == '\0')
60 level = DEBUG_TRACE_DEFAULT;
61 else
62 {
63 for (level = 0; *opts; opts++)
64 {
65 switch (*opts)
66 {
67 case 'a':
68 level |= DEBUG_TRACE_ARGS;
69 break;
70
71 case 'e':
72 level |= DEBUG_TRACE_EXPANSION;
73 break;
74
75 case 'q':
76 level |= DEBUG_TRACE_QUOTE;
77 break;
78
79 case 't':
80 level |= DEBUG_TRACE_ALL;
81 break;
82
83 case 'l':
84 level |= DEBUG_TRACE_LINE;
85 break;
86
87 case 'f':
88 level |= DEBUG_TRACE_FILE;
89 break;
90
91 case 'p':
92 level |= DEBUG_TRACE_PATH;
93 break;
94
95 case 'c':
96 level |= DEBUG_TRACE_CALL;
97 break;
98
99 case 'i':
100 level |= DEBUG_TRACE_INPUT;
101 break;
102
103 case 'x':
104 level |= DEBUG_TRACE_CALLID;
105 break;
106
107 case 'V':
108 level |= DEBUG_TRACE_VERBOSE;
109 break;
110
111 default:
112 return -1;
113 }
114 }
115 }
116
117 /* This is to avoid screwing up the trace output due to changes in the
118 debug_level. */
119
120 obstack_free (&trace, obstack_finish (&trace));
121
122 return level;
123}
124
125/*------------------------------------------------------------------------.
126| Change the debug output stream to FP. If the underlying file is the |
127| same as stdout, use stdout instead so that debug messages appear in the |
128| correct relative position. |
129`------------------------------------------------------------------------*/
130
131static void
132debug_set_file (FILE *fp)
133{
134 struct stat stdout_stat, debug_stat;
135
136 if (debug != NULL && debug != stderr && debug != stdout
137 && close_stream (debug) != 0)
138 {
139 M4ERROR ((warning_status, errno, "error writing to debug stream"));
140 retcode = EXIT_FAILURE;
141 }
142 debug = fp;
143
144 if (debug != NULL && debug != stdout)
145 {
146 if (fstat (STDOUT_FILENO, &stdout_stat) < 0)
147 return;
148 if (fstat (fileno (debug), &debug_stat) < 0)
149 return;
150
151 /* mingw has a bug where fstat on a regular file reports st_ino
152 of 0. On normal system, st_ino should never be 0. */
153 if (stdout_stat.st_ino == debug_stat.st_ino
154 && stdout_stat.st_dev == debug_stat.st_dev
155 && stdout_stat.st_ino != 0)
156 {
157 if (debug != stderr && close_stream (debug) != 0)
158 {
159 M4ERROR ((warning_status, errno,
160 "error writing to debug stream"));
161 retcode = EXIT_FAILURE;
162 }
163 debug = stdout;
164 }
165 }
166}
167
168/*-----------------------------------------------------------.
169| Serialize files. Used before executing a system command. |
170`-----------------------------------------------------------*/
171
172void
173debug_flush_files (void)
174{
175 fflush (stdout);
176 fflush (stderr);
177 if (debug != NULL && debug != stdout && debug != stderr)
178 fflush (debug);
179 /* POSIX requires that if m4 doesn't consume all input, but stdin is
180 opened on a seekable file, that the file pointer be left at the
181 next character on exit (but places no restrictions on the file
182 pointer location on a non-seekable file). It also requires that
183 fflush() followed by fseek() on an input file set the underlying
184 file pointer. However, fflush() on a non-seekable file can lose
185 buffered data, which we might otherwise want to process after
186 syscmd. Hence, we must check whether stdin is seekable. We must
187 also be tolerant of operating with stdin closed, so we don't
188 report any failures in this attempt. The stdio-safer module and
189 friends are essential, so that if stdin was closed, this lseek is
190 not on some other file that we have since opened. Mingw has bugs
191 when using fseek on text files, so we only strive for POSIX
192 behavior when we detect a UNIX environment. */
193#if UNIX
194 if (lseek (STDIN_FILENO, 0, SEEK_CUR) >= 0
195 && fflush (stdin) == 0)
196 {
197 fseek (stdin, 0, SEEK_CUR);
198 }
199#endif /* UNIX */
200}
201
202/*-------------------------------------------------------------------------.
203| Change the debug output to file NAME. If NAME is NULL, debug output is |
204| reverted to stderr, and if empty debug output is discarded. Return true |
205| iff the output stream was changed. |
206`-------------------------------------------------------------------------*/
207
208bool
209debug_set_output (const char *name)
210{
211 FILE *fp;
212
213 if (name == NULL)
214 debug_set_file (stderr);
215 else if (*name == '\0')
216 debug_set_file (NULL);
217 else
218 {
219 fp = fopen (name, "a");
220 if (fp == NULL)
221 return false;
222
223 if (set_cloexec_flag (fileno (fp), true) != 0)
224 M4ERROR ((warning_status, errno,
225 "Warning: cannot protect debug file across forks"));
226 debug_set_file (fp);
227 }
228 return true;
229}
230
231/*-----------------------------------------------------------------------.
232| Print the header of a one-line debug message, starting by "m4 debug". |
233`-----------------------------------------------------------------------*/
234
235void
236debug_message_prefix (void)
237{
238 fprintf (debug, "m4debug:");
239 if (current_line)
240 {
241 if (debug_level & DEBUG_TRACE_FILE)
242 fprintf (debug, "%s:", current_file);
243 if (debug_level & DEBUG_TRACE_LINE)
244 fprintf (debug, "%d:", current_line);
245 }
246 putc (' ', debug);
247}
248
249
250/* The rest of this file contains the functions for macro tracing output.
251 All tracing output for a macro call is collected on an obstack TRACE,
252 and printed whenever the line is complete. This prevents tracing
253 output from interfering with other debug messages generated by the
254 various builtins. */
255
256/*---------------------------------------------------------------------.
257| Tracing output is formatted here, by a simplified printf-to-obstack |
258| function trace_format (). Understands only %S, %s, %d, %l (optional |
259| left quote) and %r (optional right quote). |
260`---------------------------------------------------------------------*/
261
262static void
263trace_format (const char *fmt, ...)
264{
265 va_list args;
266 char ch;
267
268 int d;
269 char nbuf[32];
270 const char *s;
271 int slen;
272 int maxlen;
273
274 va_start (args, fmt);
275
276 while (true)
277 {
278 while ((ch = *fmt++) != '\0' && ch != '%')
279 obstack_1grow (&trace, ch);
280
281 if (ch == '\0')
282 break;
283
284 maxlen = 0;
285 switch (*fmt++)
286 {
287 case 'S':
288 maxlen = max_debug_argument_length;
289 /* fall through */
290
291 case 's':
292 s = va_arg (args, const char *);
293 break;
294
295 case 'l':
296 s = (debug_level & DEBUG_TRACE_QUOTE) ? lquote.string : "";
297 break;
298
299 case 'r':
300 s = (debug_level & DEBUG_TRACE_QUOTE) ? rquote.string : "";
301 break;
302
303 case 'd':
304 d = va_arg (args, int);
305 sprintf (nbuf, "%d", d);
306 s = nbuf;
307 break;
308
309 default:
310 s = "";
311 break;
312 }
313
314 slen = strlen (s);
315 if (maxlen == 0 || maxlen > slen)
316 obstack_grow (&trace, s, slen);
317 else
318 {
319 obstack_grow (&trace, s, maxlen);
320 obstack_grow (&trace, "...", 3);
321 }
322 }
323
324 va_end (args);
325}
326
327/*------------------------------------------------------------------.
328| Format the standard header attached to all tracing output lines. |
329`------------------------------------------------------------------*/
330
331static void
332trace_header (int id)
333{
334 trace_format ("m4trace:");
335 if (current_line)
336 {
337 if (debug_level & DEBUG_TRACE_FILE)
338 trace_format ("%s:", current_file);
339 if (debug_level & DEBUG_TRACE_LINE)
340 trace_format ("%d:", current_line);
341 }
342 trace_format (" -%d- ", expansion_level);
343 if (debug_level & DEBUG_TRACE_CALLID)
344 trace_format ("id %d: ", id);
345}
346
347/*----------------------------------------------------.
348| Print current tracing line, and clear the obstack. |
349`----------------------------------------------------*/
350
351static void
352trace_flush (void)
353{
354 char *line;
355
356 obstack_1grow (&trace, '\0');
357 line = (char *) obstack_finish (&trace);
358 DEBUG_PRINT1 ("%s\n", line);
359 obstack_free (&trace, line);
360}
361
362/*-------------------------------------------------------------.
363| Do pre-argument-collction tracing for macro NAME. Used from |
364| expand_macro (). |
365`-------------------------------------------------------------*/
366
367void
368trace_prepre (const char *name, int id)
369{
370 trace_header (id);
371 trace_format ("%s ...", name);
372 trace_flush ();
373}
374
375/*-----------------------------------------------------------------------.
376| Format the parts of a trace line, that can be made before the macro is |
377| actually expanded. Used from expand_macro (). |
378`-----------------------------------------------------------------------*/
379
380void
381trace_pre (const char *name, int id, int argc, token_data **argv)
382{
383 int i;
384 const builtin *bp;
385
386 trace_header (id);
387 trace_format ("%s", name);
388
389 if (argc > 1 && (debug_level & DEBUG_TRACE_ARGS))
390 {
391 trace_format ("(");
392
393 for (i = 1; i < argc; i++)
394 {
395 if (i != 1)
396 trace_format (", ");
397
398 switch (TOKEN_DATA_TYPE (argv[i]))
399 {
400 case TOKEN_TEXT:
401 trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv[i]));
402 break;
403
404 case TOKEN_FUNC:
405 bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[i]));
406 if (bp == NULL)
407 {
408 M4ERROR ((warning_status, 0, "\
409INTERNAL ERROR: builtin not found in builtin table! (trace_pre ())"));
410 abort ();
411 }
412 trace_format ("<%s>", bp->name);
413 break;
414
415 default:
416 M4ERROR ((warning_status, 0,
417 "INTERNAL ERROR: bad token data type (trace_pre ())"));
418 abort ();
419 }
420
421 }
422 trace_format (")");
423 }
424
425 if (debug_level & DEBUG_TRACE_CALL)
426 {
427 trace_format (" -> ???");
428 trace_flush ();
429 }
430}
431
432/*-------------------------------------------------------------------.
433| Format the final part of a trace line and print it all. Used from |
434| expand_macro (). |
435`-------------------------------------------------------------------*/
436
437void
438trace_post (const char *name, int id, int argc, token_data **argv,
439 const char *expanded)
440{
441 if (debug_level & DEBUG_TRACE_CALL)
442 {
443 trace_header (id);
444 trace_format ("%s%s", name, (argc > 1) ? "(...)" : "");
445 }
446
447 if (expanded && (debug_level & DEBUG_TRACE_EXPANSION))
448 trace_format (" -> %l%S%r", expanded);
449 trace_flush ();
450}
Note: See TracBrowser for help on using the repository browser.