comparison anagram/support/log.cpp @ 0:13d2b8934445

Import AnaGram (near-)release tree into Mercurial.
author David A. Holland
date Sat, 22 Dec 2007 17:52:45 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:13d2b8934445
1 /*
2 * AnaGram, A System for Syntax Directed Programming
3 * Copyright 1993-2002 Parsifal Software. All Rights Reserved.
4 * Copyright 2006 David A. Holland. All Rights Reserved.
5 * See the file COPYING for license and usage terms.
6 *
7 * log.cpp - logging module.
8 *
9 * To use the logging stuff, turn on INCLUDE_LOGGING here and in
10 * the source files you want to log from. Then create a file called
11 * "aglog.ctl" contaning lines of the form
12 * +foo
13 * -foo
14 * to turn logging on or off (respectively) for LOGSECTION names
15 * matching "foo". With Unix builds, "foo" can be a shell glob (like
16 * "check_*") but owing to the lack of library support this is not
17 * supported in Windows. The control settings are applied in order.
18 *
19 * The log entries land in the file "ag.log", which is truncated with
20 * every new execution.
21 */
22
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <time.h>
27
28 #ifdef AG_ON_UNIX
29 #include <fnmatch.h>
30 #endif
31
32 #ifdef VACLGUI
33 //#include <icritsec.hpp>
34 //#include "resource.h"
35 #endif
36
37 #include "port.h"
38
39 #include "agstring.h"
40 #include "agstack.h"
41 #include "textfile.h"
42
43 //#define INCLUDE_LOGGING
44 #include "log.h"
45
46 #define PATH_LOGFILE "ag.log"
47 #define PATH_CTLFILE "aglog.ctl"
48 #define INDENT 3
49
50 #ifdef INCLUDE_LOGGING
51
52 static int initialized = 0;
53 static FILE *logfile = NULL;
54 static LogSwitch masterswitch = LOG_OFF;
55 static int indent = 0;
56 static LogSection *sectionstack = NULL;
57
58 // This must be a pointer, not a global object, in case someone wants to
59 // log from a global constructor somewhere.
60 static AgStack<AgString> *matches;
61
62
63 ////////////////////////////////////////////////////////////
64
65 static int readcontrol(void) {
66 LOGSECTION_ON("readcontrol");
67
68 text_file control(PATH_CTLFILE);
69 if ((char *)control == 0) {
70 //printf("Log: failed to open control file %s\n", PATH_CTLFILE);
71 return -1;
72 }
73 //printf("Log: control file read\n");
74
75 unsigned num_control_lines = control.size().y;
76
77 //printf("Log: trying to log something\n");
78 LOGV(num_control_lines);
79
80 //printf("Log: processing control file\n");
81 for (unsigned i = 0; i < num_control_lines; i++) {
82 char *line = control.line(i);
83 while (*line==' ') {
84 line++;
85 }
86
87 char *p = strchr(line, '\n');
88 if (p) {
89 *p = 0;
90 if (p != line && p[-1]=='\r') {
91 *(--p) = 0;
92 }
93 }
94 else {
95 p = line + strlen(line);
96 }
97 while (p != line && p[-1]==' ') {
98 *(--p) = 0;
99 }
100
101 if (*line==0 || *line=='#') {
102 continue;
103 }
104
105 LOGV(line);
106
107 if ((*line!='+' && *line!='-') || strlen(line)==1) {
108 LOGS("...line is invalid");
109 continue;
110 }
111
112 matches->push(AgString(line));
113 }
114
115 return 0;
116 }
117
118 static LogSwitch checkcontrol(const char *name) {
119 unsigned i, n = matches->size();
120 for (i=0; i<n; i++) {
121 const char *ctl = (*matches)[i].pointer();
122 assert(ctl[0] == '+' || ctl[0] == '-');
123
124 int matched;
125 #ifdef AG_ON_UNIX
126 matched = !fnmatch(ctl+1, name, 0);
127 #else
128 if (!strcmp(ctl+1, "*")) {
129 matched = 1;
130 }
131 else {
132 matched = !strcmp(ctl+1, name);
133 }
134 #endif
135
136 if (matched) {
137 return ctl[0] == '+' ? LOG_ON : LOG_OFF;
138 }
139 }
140
141 return LOG_MAYBE;
142 }
143
144 ////////////////////////////////////////////////////////////
145
146 static int loginit(void) {
147 if (initialized) {
148 return 0;
149 }
150
151 //printf("Log: initializing\n");
152 logfile = fopen(PATH_LOGFILE, "w");
153 if (!logfile) {
154 //printf("Log: failed to open output file %s\n", PATH_LOGFILE);
155 return -1;
156 }
157 //printf("Log: opened output file\n");
158 fprintf(logfile, "*** AnaGram log file ***\n");
159
160 masterswitch = LOG_ON;
161 initialized = 1;
162 matches = new AgStack<AgString>;
163
164 if (readcontrol()) {
165 fprintf(logfile, "\nReading control file %s failed\n", PATH_CTLFILE);
166 fclose(logfile);
167 logfile = NULL;
168 initialized = 0;
169 delete matches;
170 matches = NULL;
171 masterswitch = LOG_OFF;
172 return -1;
173 }
174
175 masterswitch = LOG_OFF;
176
177 //printf("Log: done initializing\n");
178 return 0;
179 }
180
181 #if 0 /* no way to reach this */
182 static void logclose(void) {
183 if (initialized) {
184
185 assert(logfile != NULL);
186 fclose(logfile);
187 logfile = NULL;
188
189 delete matches;
190 matches = NULL;
191
192 masterswitch = LOG_OFF;
193 initialized = 0;
194 }
195 }
196 #endif /* 0 */
197
198 LogSection::LogSection(const char *name_, LogSwitch caller_requested) {
199 if (loginit()) return;
200
201 previous = sectionstack;
202 sectionstack = this;
203 name = name_;
204 saved_switch = masterswitch;
205
206 // get setting instructed via the control file
207 masterswitch = LOG_ON;
208 LogSwitch ctl_setting = checkcontrol(name);
209 masterswitch = LOG_OFF;
210
211 // the control file takes precedence, unless it says "maybe"
212 LogSwitch new_setting;
213 if (ctl_setting != LOG_MAYBE) {
214 new_setting = ctl_setting;
215 }
216 else {
217 new_setting = caller_requested;
218 }
219
220 if (new_setting == LOG_ON) {
221 masterswitch = LOG_ON;
222 logeol(), dolog("BEGIN "), dolog(name), dolog(" "), logtime();
223 indent += INDENT;
224 }
225 }
226
227 LogSection::~LogSection() {
228 if (!initialized) return;
229
230 if (masterswitch == LOG_ON) {
231 indent -= INDENT;
232 logeol(), dolog("END "), dolog(name), dolog(" "), logtime();
233 }
234 masterswitch = saved_switch;
235 sectionstack = previous;
236 }
237
238 void LogSection::logstack(void) {
239 LOGSECTION("logsectionstack");
240 LogSection *s = sectionstack;
241 while (s) {
242 logeol(), dolog(s->name);
243 s = s->previous;
244 }
245 }
246
247 void logeol(void) {
248 if (loginit() || masterswitch == LOG_OFF) return;
249
250 assert(logfile != NULL);
251 fprintf(logfile, "\n%*s", indent, "");
252 fflush(logfile);
253 }
254
255 static void logf(const char *fmt, ...) {
256 if (loginit() || masterswitch == LOG_OFF) return;
257
258 assert(logfile != NULL);
259 va_list ap;
260 va_start(ap, fmt);
261 vfprintf(logfile, fmt, ap);
262 va_end(ap);
263 fflush(logfile);
264 }
265
266 void dolog(const char *s) {
267 logf("%s", s);
268 }
269
270 void dolog(const AgString &s) {
271 logf("%s", s.pointer());
272 }
273
274 void dolog(void *p) {
275 logf("%p", p);
276 }
277
278 void dolog(int n) {
279 logf("%d", n);
280 }
281
282 void dolog(cint p) {
283 logf("(%d, %d)", p.x, p.y);
284 }
285
286 void logtime(void) {
287 char buf[64];
288 time_t timer = time(NULL);
289 struct tm *t = gmtime(&timer);
290 strftime(buf, sizeof(buf), "%Y%m%d %T", t);
291 logf("%s", buf);
292 }
293
294 #ifdef VACLGUI
295
296 void dolog(const IString &s) {
297 logf("%s", (const char *) s);
298 }
299
300 void dolog(const IPair &p) {
301 logf("%s", p.asString());
302 }
303
304 void dolog(const IRectangle &r) {
305 logf("%s", r.asString());
306 }
307
308 void logexception(IException &ie) {
309 if (loginit() || masterswitch == LOG_OFF) return;
310
311 LOGSECTION_ON("Exception");
312 logeol(), dolog(ie.name()), dolog(ie.text());
313 int n = ie.locationCount();
314 for (int i = 0; i < n; i++) {
315 const IExceptionLocation *loc = ie.locationAtIndex(i);
316 logeol(), dolog("File: "), dolog(loc->fileName());
317 logeol(), dolog("Line: "), dolog(loc->lineNumber());
318 logeol(), dolog("Function: "), dolog(loc->functionName());
319 }
320 }
321
322 #endif /* VACLGUI */
323
324 #else /* not INCLUDE_LOGGING */
325
326 #ifdef __IBMCPP__
327 /* without this, the librarian bombs */
328 void ibmcpp_is_stupid_in_log_cpp(void) {}
329 #endif
330
331 #endif /* INCLUDE_LOGGING */
332
333