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