maint: run update-copyright for 2014.
[m4/ericb.git] / m4 / debug.c
blob0eeece53164511ef4efce4b14654336dcc1dd10c
1 /* GNU m4 -- A simple macro processor
2 Copyright (C) 1991-1994, 2006-2010, 2013-2014 Free Software
3 Foundation, Inc.
5 This file is part of GNU M4.
7 GNU M4 is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU M4 is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <config.h>
23 #include <sys/stat.h>
24 #include <stdarg.h>
26 #include "m4private.h"
27 #include "close-stream.h"
29 static void set_debug_file (m4 *, const m4_call_info *, FILE *);
33 /* Function to decode the debugging flags OPTS of length LEN; or
34 SIZE_MAX if OPTS is NUL-terminated. If OPTS is NULL, use the
35 default flags. Used by main while processing option -d, and by the
36 builtin debugmode (). */
37 int
38 m4_debug_decode (m4 *context, const char *opts, size_t len)
40 int previous = context->debug_level;
41 int level;
42 char mode = '\0';
44 if (!opts)
45 opts = "";
46 if (len == SIZE_MAX)
47 len = strlen (opts);
48 if (!len)
49 level = M4_DEBUG_TRACE_DEFAULT | previous;
50 else
52 if (*opts == '-' || *opts == '+')
54 len--;
55 mode = *opts++;
57 for (level = 0; len--; opts++)
59 switch (*opts)
61 case 'a':
62 level |= M4_DEBUG_TRACE_ARGS;
63 break;
65 case 'e':
66 level |= M4_DEBUG_TRACE_EXPANSION;
67 break;
69 case 'q':
70 level |= M4_DEBUG_TRACE_QUOTE;
71 break;
73 case 't':
74 level |= M4_DEBUG_TRACE_ALL;
75 break;
77 case 'l':
78 level |= M4_DEBUG_TRACE_LINE;
79 break;
81 case 'f':
82 level |= M4_DEBUG_TRACE_FILE;
83 break;
85 case 'p':
86 level |= M4_DEBUG_TRACE_PATH;
87 break;
89 case 'c':
90 level |= M4_DEBUG_TRACE_CALL;
91 break;
93 case 'i':
94 level |= M4_DEBUG_TRACE_INPUT;
95 break;
97 case 'x':
98 level |= M4_DEBUG_TRACE_CALLID;
99 break;
101 case 'm':
102 level |= M4_DEBUG_TRACE_MODULE;
103 break;
105 case 's':
106 level |= M4_DEBUG_TRACE_STACK;
107 break;
109 case 'd':
110 level |= M4_DEBUG_TRACE_DEREF;
111 break;
113 case 'o':
114 level |= M4_DEBUG_TRACE_OUTPUT_DUMPDEF;
115 break;
117 case 'V':
118 level |= M4_DEBUG_TRACE_VERBOSE;
119 break;
121 default:
122 return -1;
127 switch (mode)
129 case '\0':
130 /* Replace old level. */
131 break;
133 case '-':
134 /* Subtract flags. */
135 level = previous & ~level;
136 break;
138 case '+':
139 /* Add flags. */
140 level |= previous;
141 break;
143 default:
144 assert (!"m4_debug_decode");
146 context->debug_level = level;
147 return level;
150 /* Change the debug output stream to FP. If the underlying file is the
151 same as stdout, use stdout instead so that debug messages appear in the
152 correct relative position. Report errors on behalf of CALLER. */
153 static void
154 set_debug_file (m4 *context, const m4_call_info *caller, FILE *fp)
156 FILE *debug_file;
157 struct stat stdout_stat, debug_stat;
159 assert (context);
161 debug_file = m4_get_debug_file (context);
162 if (debug_file != NULL && debug_file != stderr && debug_file != stdout
163 && close_stream (debug_file) != 0)
164 m4_error (context, 0, errno, caller, _("error writing to debug stream"));
166 debug_file = fp;
167 m4_set_debug_file (context, fp);
169 if (debug_file != NULL && debug_file != stdout)
171 if (fstat (fileno (stdout), &stdout_stat) < 0)
172 return;
173 if (fstat (fileno (debug_file), &debug_stat) < 0)
174 return;
176 /* mingw has a bug where fstat on a regular file reports st_ino
177 of 0. On normal system, st_ino should never be 0. */
178 if (stdout_stat.st_ino == debug_stat.st_ino
179 && stdout_stat.st_dev == debug_stat.st_dev
180 && stdout_stat.st_ino != 0)
182 if (debug_file != stderr && close_stream (debug_file) != 0)
183 m4_error (context, 0, errno, caller,
184 _("error writing to debug stream"));
185 m4_set_debug_file (context, stdout);
190 /* Change the debug output to file NAME. If NAME is NULL, debug
191 output is reverted to stderr, and if empty debug output is
192 discarded. Return true iff the output stream was changed. Report
193 errors on behalf of CALLER. */
194 bool
195 m4_debug_set_output (m4 *context, const m4_call_info *caller, const char *name)
197 FILE *fp;
199 assert (context);
201 if (name == NULL)
202 set_debug_file (context, caller, stderr);
203 else if (*name == '\0')
204 set_debug_file (context, caller, NULL);
205 else
207 fp = fopen (name, "a");
208 if (fp == NULL)
209 return false;
211 if (set_cloexec_flag (fileno (fp), true) != 0)
212 m4_warn (context, errno, caller,
213 _("cannot protect debug file across forks"));
214 set_debug_file (context, caller, fp);
216 return true;
219 /* Print the header of a one-line debug message, starting with "m4debug:". */
220 void
221 m4_debug_message_prefix (m4 *context)
223 FILE *debug_file;
225 assert (context);
227 debug_file = m4_get_debug_file (context);
228 fputs ("m4debug:", debug_file);
229 if (m4_get_current_line (context))
231 if (m4_is_debug_bit (context, M4_DEBUG_TRACE_FILE))
232 xfprintf (debug_file, "%s:", m4_get_current_file (context));
233 if (m4_is_debug_bit (context, M4_DEBUG_TRACE_LINE))
234 xfprintf (debug_file, "%d:", m4_get_current_line (context));
236 putc (' ', debug_file);
239 /* If the current debug mode includes MODE, and there is a current
240 debug file, then output a debug message described by FORMAT. A
241 message header is supplied, as well as a trailing newline. */
242 void
243 m4_debug_message (m4 *context, int mode, const char *format, ...)
245 /* Check that mode has exactly one bit set. */
246 assert (mode && (mode & (mode - 1)) == 0);
247 assert (format);
249 if (m4_get_debug_file (context) != NULL
250 && m4_is_debug_bit (context, mode))
252 va_list args;
254 m4_debug_message_prefix (context);
255 va_start (args, format);
256 xvfprintf (m4_get_debug_file (context), format, args);
257 va_end (args);
258 putc ('\n', m4_get_debug_file (context));