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