1 /* GNU m4 -- A simple macro processor
2
3 Copyright (C) 1991-1994, 2004, 2006-2007, 2009-2014, 2016-2017,
4 2020-2021 Free Software 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 <https://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
debug_init(void)40 debug_init (void)
41 {
42 debug_set_file (stderr);
43 obstack_init (&trace);
44 }
45
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
debug_decode(const char * opts)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
debug_set_file(FILE * fp)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
debug_flush_files(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
debug_set_output(const char * name)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, "ae");
214 if (fp == NULL)
215 return false;
216 debug_set_file (fp);
217 }
218 return true;
219 }
220
221 /*--------------------------------------------------------------.
222 | Print the header of a one-line debug message, starting by "m4 |
223 | debug". |
224 `--------------------------------------------------------------*/
225
226 void
debug_message_prefix(void)227 debug_message_prefix (void)
228 {
229 xfprintf (debug, "m4debug:");
230 if (current_line)
231 {
232 if (debug_level & DEBUG_TRACE_FILE)
233 xfprintf (debug, "%s:", current_file);
234 if (debug_level & DEBUG_TRACE_LINE)
235 xfprintf (debug, "%d:", current_line);
236 }
237 putc (' ', debug);
238 }
239
240 /* The rest of this file contains the functions for macro tracing output.
241 All tracing output for a macro call is collected on an obstack TRACE,
242 and printed whenever the line is complete. This prevents tracing
243 output from interfering with other debug messages generated by the
244 various builtins. */
245
246 /*------------------------------------------------------------------.
247 | Tracing output is formatted here, by a simplified |
248 | printf-to-obstack function trace_format (). Understands only %S, |
249 | %s, %d, %l (optional left quote) and %r (optional right quote). |
250 `------------------------------------------------------------------*/
251
252 static void
trace_format(const char * fmt,...)253 trace_format (const char *fmt, ...)
254 {
255 va_list args;
256 char ch;
257
258 int d;
259 const char *s;
260 int slen;
261 int maxlen;
262
263 va_start (args, fmt);
264
265 while (true)
266 {
267 while ((ch = *fmt++) != '\0' && ch != '%')
268 obstack_1grow (&trace, ch);
269
270 if (ch == '\0')
271 break;
272
273 maxlen = 0;
274 switch (*fmt++)
275 {
276 case 'S':
277 maxlen = max_debug_argument_length;
278 FALLTHROUGH;
279 case 's':
280 s = va_arg (args, const char *);
281 break;
282
283 case 'l':
284 s = (debug_level & DEBUG_TRACE_QUOTE) ? lquote.string : "";
285 break;
286
287 case 'r':
288 s = (debug_level & DEBUG_TRACE_QUOTE) ? rquote.string : "";
289 break;
290
291 case 'd':
292 d = va_arg (args, int);
293 s = ntoa (d, 10);
294 break;
295
296 default:
297 s = "";
298 break;
299 }
300
301 slen = strlen (s);
302 if (maxlen == 0 || maxlen > slen)
303 obstack_grow (&trace, s, slen);
304 else
305 {
306 obstack_grow (&trace, s, maxlen);
307 obstack_grow (&trace, "...", 3);
308 }
309 }
310
311 va_end (args);
312 }
313
314 /*------------------------------------------------------------------.
315 | Format the standard header attached to all tracing output lines. |
316 `------------------------------------------------------------------*/
317
318 static void
trace_header(int id)319 trace_header (int id)
320 {
321 trace_format ("m4trace:");
322 if (current_line)
323 {
324 if (debug_level & DEBUG_TRACE_FILE)
325 trace_format ("%s:", current_file);
326 if (debug_level & DEBUG_TRACE_LINE)
327 trace_format ("%d:", current_line);
328 }
329 trace_format (" -%d- ", expansion_level);
330 if (debug_level & DEBUG_TRACE_CALLID)
331 trace_format ("id %d: ", id);
332 }
333
334 /*----------------------------------------------------.
335 | Print current tracing line, and clear the obstack. |
336 `----------------------------------------------------*/
337
338 static void
trace_flush(void)339 trace_flush (void)
340 {
341 char *line;
342
343 obstack_1grow (&trace, '\0');
344 line = (char *) obstack_finish (&trace);
345 DEBUG_PRINT1 ("%s\n", line);
346 obstack_free (&trace, line);
347 }
348
349 /*-------------------------------------------------------------.
350 | Do pre-argument-collction tracing for macro NAME. Used from |
351 | expand_macro (). |
352 `-------------------------------------------------------------*/
353
354 void
trace_prepre(const char * name,int id)355 trace_prepre (const char *name, int id)
356 {
357 trace_header (id);
358 trace_format ("%s ...", name);
359 trace_flush ();
360 }
361
362 /*--------------------------------------------------------------.
363 | Format the parts of a trace line, that can be made before the |
364 | macro is actually expanded. Used from expand_macro (). |
365 `--------------------------------------------------------------*/
366
367 void
trace_pre(const char * name,int id,int argc,token_data ** argv)368 trace_pre (const char *name, int id, int argc, token_data **argv)
369 {
370 int i;
371 const builtin *bp;
372
373 trace_header (id);
374 trace_format ("%s", name);
375
376 if (argc > 1 && (debug_level & DEBUG_TRACE_ARGS))
377 {
378 trace_format ("(");
379
380 for (i = 1; i < argc; i++)
381 {
382 if (i != 1)
383 trace_format (", ");
384
385 switch (TOKEN_DATA_TYPE (argv[i]))
386 {
387 case TOKEN_TEXT:
388 trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv[i]));
389 break;
390
391 case TOKEN_FUNC:
392 bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[i]));
393 if (bp == NULL)
394 {
395 M4ERROR ((warning_status, 0, "\
396 INTERNAL ERROR: builtin not found in builtin table! (trace_pre ())"));
397 abort ();
398 }
399 trace_format ("<%s>", bp->name);
400 break;
401
402 case TOKEN_VOID:
403 default:
404 M4ERROR ((warning_status, 0,
405 "INTERNAL ERROR: bad token data type (trace_pre ())"));
406 abort ();
407 }
408
409 }
410 trace_format (")");
411 }
412
413 if (debug_level & DEBUG_TRACE_CALL)
414 {
415 trace_format (" -> ???");
416 trace_flush ();
417 }
418 }
419
420 /*-------------------------------------------------------------------.
421 | Format the final part of a trace line and print it all. Used from |
422 | expand_macro (). |
423 `-------------------------------------------------------------------*/
424
425 void
trace_post(const char * name,int id,int argc,const char * expanded)426 trace_post (const char *name, int id, int argc, const char *expanded)
427 {
428 if (debug_level & DEBUG_TRACE_CALL)
429 {
430 trace_header (id);
431 trace_format ("%s%s", name, (argc > 1) ? "(...)" : "");
432 }
433
434 if (expanded && (debug_level & DEBUG_TRACE_EXPANSION))
435 trace_format (" -> %l%S%r", expanded);
436 trace_flush ();
437 }
438