Mercurial > ~dholland > hg > ag > index.cgi
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 |