source: trunk/MagicSoft/Simulation/Corsika/Mmcs614/warning.c

Last change on this file was 1444, checked in by blanch, 22 years ago
*** empty log message ***
File size: 16.6 KB
Line 
1/* ================================================================ */
2/**
3 @file warning.c
4 @brief Pass warning messages to the screen or a usr function as set up.
5
6 One of the most import parameter for setting up the bevaviour
7 is the warning level:
8
9@verbatim
10 ------------------------------------------------------------
11 Warning level: The lowest level of messages to be displayed
12 ------------------------------------------------------------
13 Warning mode:
14 bit 0: display on screen (stderr),
15 bit 1: write to file,
16 bit 2: write with user-defined logging function.
17 bit 3: display origin if supplied.
18 bit 4: open log file for appending.
19 bit 5: call auxilliary function for time/date etc.
20 bit 6: use the auxilliary function output as origin string
21 if no explicit origin was supplied.
22 bit 7: use syslog().
23 ------------------------------------------------------------
24@endverbatim
25*/
26
27#include "initial.h"
28#define __WARNING_MODULE 1
29#include "warning.h"
30#include <errno.h>
31
32struct warn_specific_data
33{
34 /* Warning level: The lowest level of messages to be displayed */
35 int warninglevel;
36/*
37 * -----------------------------------------------------------
38 * Warning mode:
39 * bit 0: display on screen (stderr),
40 * bit 1: write to file,
41 * bit 2: write with user-defined logging function.
42 * bit 3: display origin if supplied.
43 * bit 4: open log file for appending.
44 * bit 5: call auxilliary function for time/date etc.
45 * bit 6: use the auxilliary function output as origin string
46 * if no explicit origin was supplied.
47 * bit 7: use syslog().
48 * -----------------------------------------------------------
49 */
50 int warningmode;
51 char output_buffer[2048];
52 /** The name of the log file. Used only when opening the file. */
53 const char *logfname;
54 char saved_logfname[256];
55 int buffered;
56 FILE *logfile;
57 void (*log_function) ARGLIST((const char *, const char *, int, int));
58 void (*output_function) ARGLIST((const char *));
59 char *(*aux_function) ARGLIST((void));
60 int recursive;
61};
62
63static struct warn_specific_data warn_defaults =
64{
65 0, /* warninglevel */
66 1+8, /* warningmode */
67 "", /* output_buffer */
68 "warning.log", /* logfname */
69 "", /* saved_logfname */
70 0, /* buffered */
71 NULL, /* logfile */
72 NULL, /* log_function */
73 NULL, /* output_function */
74 NULL, /* aux_function */
75 0 /* recursive */
76};
77
78#ifdef _REENTRANT
79
80#include <pthread.h>
81
82#ifndef PTHREAD_ONCE_INIT
83#define PTHREAD_ONCE_INIT pthread_once_init
84#define PTHREAD_MUTEX_INITIALIZER 0
85#endif
86
87/** Global key for thread-specific data. */
88static pthread_key_t warn_tsd_key;
89static pthread_once_t warn_key_once = PTHREAD_ONCE_INIT;
90
91static int delete_warn_specific(void);
92static void warn_destructor (void *whatever);
93static void warn_func_once(void);
94static int create_warn_specific(void);
95static void *get_warn_specific(void);
96
97static int delete_warn_specific()
98{
99 void *specific;
100
101 if ( (specific = pthread_getspecific(warn_tsd_key)) == NULL )
102 return 0;
103 free(specific);
104 if ( pthread_setspecific(warn_tsd_key,NULL) < 0 )
105 return -1;
106 return 0;
107}
108
109static void warn_destructor (void *whatever)
110{
111 fprintf(stderr,"delete_warn_specific() called\n");
112 delete_warn_specific();
113}
114
115static void warn_func_once()
116{
117 fprintf(stderr,"Doing one-time thread-specific warning data initialization.\n");
118#ifdef OS_LYNX
119 pthread_keycreate(&warn_tsd_key,warn_destructor);
120#else
121 pthread_key_create(&warn_tsd_key,warn_destructor);
122#endif
123}
124
125static int create_warn_specific()
126{
127 void *specific;
128 /* Make sure that the key has been set up */
129 if ( pthread_once(&warn_key_once,warn_func_once) != 0 )
130 {
131 fprintf(stderr,"Thread specific one-time initialization failed.\n");
132 return -1;
133 }
134 /* Any prior data ? This would be a memory leak. */
135 if ( (specific = pthread_getspecific(warn_tsd_key)) != NULL )
136 {
137 fprintf(stderr,"Prior thread-specific warning being deleted.\n");
138 free(specific);
139 pthread_setspecific(warn_tsd_key,NULL);
140 }
141
142 /* New data allocated and initialized with defaults */
143 if ( (specific = calloc(1,sizeof(struct warn_specific_data))) == NULL )
144 return -1;
145 memcpy(specific,&warn_defaults,sizeof(struct warn_specific_data));
146 if ( pthread_setspecific(warn_tsd_key,specific) != 0 )
147 return -1;
148 return 0;
149}
150
151static void *get_warn_specific()
152{
153 void *specific;
154
155 if ( (specific = pthread_getspecific(warn_tsd_key)) == NULL )
156 {
157#if 0
158 fprintf(stderr,"Dynamically creating warning thread-specific data.\n");
159 fprintf(stderr,"Failure to call warn_delete_specific() at thread termination may result in memory leaks.\n");
160#endif
161 create_warn_specific();
162 specific = pthread_getspecific(warn_tsd_key);
163 }
164
165 return specific;
166}
167
168#else
169#define get_warn_specific() (&warn_defaults)
170#endif
171
172/* ------------------------- warn_f_warning ------------------------- */
173/**
174 * @short Issue a warning to screen or other configured target.
175 *
176 * Issue a warning to screen and/or file if the warning
177 * has a sufficiently large message 'level' (high enough severity).
178 * This function should best be called through the macros
179 * 'Information', 'Warning', and 'Error'.
180 * The name of this function has been changed from 'warning'
181 * to '_warning' to avoid trouble if you call 'warning' instead
182 * of 'Warning'. Now such a typo causes an error in the link step.
183 *
184 * @param msgtext Warning or error text.
185 * @param msgorigin Optional origin (e.g. function name) or NULL.
186 * @param msglevel Level of message importance:
187 * negative: debugging if needed,
188 * 0-9: informative,
189 * 10-19: warning,
190 * 20-29: error.
191 * @param msgno Number of message or 0.
192 *
193 * @return (none)
194 *
195 */
196
197void warn_f_warning (const char *msgtext, const char *msgorigin,
198 int msglevel, int msgno)
199{
200 char enumtext[32];
201 struct warn_specific_data *wt = get_warn_specific();
202
203 if ( wt == NULL )
204 return;
205
206 if ( msgtext == (char *) NULL || wt == NULL )
207 return;
208
209 /* Don't record or print message text if not severe enough. */
210 if ( msglevel < wt->warninglevel )
211 return;
212
213 /* Make sure that warning() is not executed recursively, */
214 /* for example due to errors in a user-defined log function. */
215 if ( wt->recursive )
216 {
217 fputs("E0003: Recursive call to warning function!\n",stderr);
218 fputs("Text of last call: ",stderr);
219 fputs(msgtext,stderr);
220 fputs("\n",stderr);
221 if ( (wt->warningmode & 0x02) && wt->logfile != (FILE *) NULL )
222 {
223 fputs("E0003: Recursive call to warning function!\n",wt->logfile);
224 fputs("Text of last call: ",wt->logfile);
225 fputs(msgtext,wt->logfile);
226 fputs("\n",wt->logfile);
227 fflush(wt->logfile);
228 }
229 return; /* We could do more sophisticated things here */
230 }
231 wt->recursive++;
232
233 if ( msgorigin == (char *) NULL && (wt->warningmode & 0x40) &&
234 wt->aux_function != NULL )
235 msgorigin = wt->aux_function();
236
237 /* Sould the message be printed on the screen (stderr) ? */
238 if ( wt->warningmode & 0x01 )
239 {
240 if ( msgno > 0 )
241 {
242 (void) sprintf(enumtext,"%s%d: ",((msglevel>=20)?"E":"W"),msgno);
243 fputs(enumtext,stderr);
244 }
245 if ( msgorigin != (char *) NULL && ( wt->warningmode & 0x08 ) )
246 {
247 fputs(msgorigin,stderr);
248 fputs(": ",stderr);
249 }
250 if ( (wt->warningmode & 0x20) && wt->aux_function != NULL )
251 fprintf(stderr,"%s: ",wt->aux_function());
252 fputs(msgtext,stderr);
253 fputs("\n",stderr);
254 }
255
256 /* Should the warning be written to a file ? */
257 if ( wt->warningmode & 0x02 )
258 {
259 if ( wt->logfile == (FILE *) NULL )
260 {
261 if ( wt->logfname == (char *) NULL )
262 {
263 fputs("E0004: No error log file name specified\n",stderr);
264 wt->warningmode &= ~((int)0x02);
265 { wt->recursive--; return; }
266 }
267 if ( (wt->logfile = fopen(wt->logfname,(wt->warningmode&0x10)?"a":"w")) ==
268 (FILE *) NULL )
269 {
270 fputs("E0001: Error opening log file '",stderr);
271 fputs(wt->logfname,stderr);
272 fputs("'.\n",stderr);
273 /* Disable writing to the logfile */
274 wt->warningmode &= ~((int)0x02);
275 { wt->recursive--; return; }
276 }
277 }
278
279 if ( msgno > 0 )
280 (void) fprintf(wt->logfile,"%s%04d: ",((msglevel>=20)?"E":"W"),msgno);
281 if ( msgorigin != (char *) NULL && ( wt->warningmode & 8 ) )
282 {
283 (void) fputs(msgorigin,wt->logfile);
284 (void) fputs(": ",wt->logfile);
285 }
286 if ( (wt->warningmode & 0x20) && wt->aux_function != NULL )
287 fprintf(wt->logfile,"%s: ",wt->aux_function());
288 (void) fputs(msgtext,wt->logfile);
289 (void) fputs("\n",wt->logfile);
290 fflush(wt->logfile);
291
292 if ( ferror(wt->logfile) )
293 {
294 clearerr(wt->logfile);
295 fputs("E0002: Error writing to log file '",stderr);
296 fputs(wt->logfname,stderr);
297 fputs("'.\n",stderr);
298 /* Disable writing to the logfile if the disk is full. */
299 if ( errno == ENOSPC )
300 {
301 fputs("E0005: File system is full. Disabling logging now.\n",
302 stderr);
303 wt->warningmode &= ~((int)0x02);
304 }
305 }
306 }
307
308 if ( wt->log_function != NULL && (wt->warningmode & 0x04) )
309 (*wt->log_function)(msgtext,msgorigin,msglevel,msgno);
310
311 wt->recursive--;
312 return;
313}
314
315/* ------------------------ set_warning ------------------------- */
316/**
317 * @short Set a specific warning level and mode.
318 *
319 * @param level Warnings with level below this are ignored.
320 * @param mode To screen, to file, with user function ...
321 *
322 * @return 0 if ok, -1 if level and/or mode could not be set.
323 *
324 */
325
326int set_warning (int level, int mode)
327{
328 struct warn_specific_data *wt = get_warn_specific();
329
330 if ( wt == NULL )
331 return -1;
332
333 if ( level != -1 ) /* -1 means: keep the old value */
334 wt->warninglevel = level;
335 if ( mode != -1 )
336 wt->warningmode = mode;
337 return 0; /* So far, always ok */
338}
339
340int set_default_warning (int level, int mode)
341{
342 struct warn_specific_data *wt = &warn_defaults;
343
344 if ( wt == NULL )
345 return -1;
346
347 if ( level != -1 ) /* -1 means: keep the old value */
348 wt->warninglevel = level;
349 if ( mode != -1 )
350 wt->warningmode = mode;
351 return 0; /* So far, always ok */
352}
353
354/* --------------------- warning_status ---------------------- */
355/**
356 * Inquire status of warning settings.
357 *
358 * @param plevel Pointer to variable for storing current level.
359 * @param pmode Pointer to store the current warning mode.
360 *
361 * @return (none)
362 *
363 */
364
365void warning_status (int *plevel, int *pmode)
366{
367 struct warn_specific_data *wt = get_warn_specific();
368 if ( wt == NULL )
369 return;
370 if ( plevel )
371 *plevel = wt->warninglevel;
372 if ( pmode )
373 *pmode = wt->warningmode;
374}
375
376/* --------------- set_logging_function ------------- */
377/**
378 * @short Set user-defined function for logging warnings and errors.
379 *
380 * Set a user-defined function as the function to be
381 * used for logging warnings and errors.
382 * To enable usage of this function, bit 2 of the
383 * warning mode must be set and other bits reset, if
384 * logging to screen and/or disk file is no longer wanted.
385 *
386 * @param userfunc Pointer to a function taking two strings
387 * (the message text and the origin text,
388 * which may be NULL) and two integers
389 * (message level and message number).
390 *
391 * @return (none)
392 *
393*/
394
395void set_logging_function ( void (*user_function) ARGLIST((
396 const char *, const char *, int, int)) )
397{
398 struct warn_specific_data *wt = get_warn_specific();
399 if ( wt == NULL )
400 {
401 Warning("Cannot set logging function");
402 return;
403 }
404 wt->log_function = user_function;
405}
406
407void set_default_logging_function ( void (*user_function) ARGLIST((
408 const char *, const char *, int, int)) )
409{
410 struct warn_specific_data *wt = &warn_defaults;
411 if ( wt == NULL )
412 {
413 Warning("Cannot set logging function");
414 return;
415 }
416 wt->log_function = user_function;
417}
418
419/* --------------------- set_log_file -------------------- */
420/**
421 * @short Set a new log file name and save it in local storage.
422 *
423 * If there was a log file with a different name opened
424 * previously, close it.
425 *
426 * @param fname New name of log file for warnings
427 *
428 * @return 0 (o.k.), -1 (error)
429 *
430 */
431
432int set_log_file (const char *fname)
433{
434 struct warn_specific_data *wt = get_warn_specific();
435 if ( wt == NULL )
436 return -1;
437 /* No log file? */
438 if ( fname == (char *) NULL )
439 {
440 if ( wt->logfile != (FILE *) NULL )
441 {
442 wt->logfname = (char *) NULL;
443 fclose(wt->logfile);
444 wt->logfile = (FILE *) NULL;
445 }
446 return 0;
447 }
448
449 /* Is length of name o.k.? */
450 if ( strlen(fname) > sizeof(wt->saved_logfname)-1 )
451 {
452 Warning("Log file name is too long.");
453 return -1;
454 }
455
456 /* Is this the old log file name again? */
457 if ( wt->logfname != (char *) NULL )
458 if ( strcmp(fname,wt->logfname) == 0 )
459 return 0;
460
461 /* If there was a different log file before, close it. */
462 if ( wt->logfile != (FILE *) NULL )
463 {
464 fclose(wt->logfile);
465 wt->logfile = (FILE *) NULL;
466 }
467
468 strcpy(wt->saved_logfname,fname);
469 wt->logfname = wt->saved_logfname;
470
471 return 0;
472}
473
474/* ---------------------- output_text -------------------- */
475/**
476 * Print a text string (without appending a newline etc.)
477 * on the screen or send it to a controlling process, depending
478 * on the setting of the output function.
479 *
480 * @param text A text string to be displayed.
481 *
482 * @return (none)
483 *
484 */
485
486void warn_f_output_text (const char *text)
487{
488 int len;
489 struct warn_specific_data *wt = get_warn_specific();
490
491 if ( wt == NULL )
492 return ;
493
494 if ( *text == '\0' )
495 return;
496
497 if ( wt->output_function == NULL )
498 fputs(text,stdout);
499 else
500 {
501 len = strlen(text);
502 if ( wt->buffered + len + 1 >= sizeof(wt->output_buffer) )
503 {
504 if ( wt->buffered )
505 (*wt->output_function)(wt->output_buffer);
506 wt->output_buffer[0] = '\0';
507 wt->buffered = 0;
508 }
509 if ( len + 1 >= sizeof(wt->output_buffer) )
510 {
511 (*wt->output_function)(text);
512 }
513 else if ( len > 0 )
514 {
515 memcpy((wt->output_buffer+wt->buffered),text,(size_t)len);
516 wt->buffered += len;
517 wt->output_buffer[wt->buffered] = '\0';
518 }
519 }
520}
521
522/* ------------------ flush_output ----------------- */
523/**
524 * @short Flush buffered output.
525 * Output is flushed, no matter if it is standard output
526 * or a special output function;
527 *
528 * @return (none)
529 *
530 */
531
532void flush_output ()
533{
534 struct warn_specific_data *wt = get_warn_specific();
535
536 if ( wt == NULL )
537 return ;
538
539 if ( wt->output_function == NULL )
540 fflush(stdout);
541 else if ( wt->buffered )
542 (*wt->output_function)(wt->output_buffer);
543 wt->buffered = 0;
544}
545
546/* --------------- set_output_function ------------- */
547/**
548 * Set a user-defined function as the function to be used
549 * for normal text output. Such a function may be used to
550 * send output back to a remote control process via network.
551 *
552 * @param userfunc Pointer to a function taking a string
553 * (the text to be displayed) as argument.
554 *
555 * @return (none)
556 *
557*/
558
559void set_output_function ( void (*user_function) ARGLIST((const char *)) )
560{
561 struct warn_specific_data *wt = get_warn_specific();
562
563 if ( wt == NULL )
564 return ;
565
566 if ( wt->buffered )
567 flush_output();
568 wt->output_function = user_function;
569}
570
571void set_default_output_function ( void (*user_function) ARGLIST((const char *)) )
572{
573 struct warn_specific_data *wt = &warn_defaults;
574
575 if ( wt == NULL )
576 return ;
577
578 if ( wt->buffered )
579 flush_output();
580 wt->output_function = user_function;
581}
582
583/* --------------------- set_aux_warning_function --------------- */
584/**
585 * @short Set an auxilliary function for warnings.
586 * This function may be
587 * used to insert time and date or origin etc. at the beginning
588 * of the warning text.
589 *
590 * @param auxfunc -- Pointer to a function taking no argument
591 * and returning a character string.
592 *
593 * @return (none)
594 *
595 */
596
597void set_aux_warning_function ( char *(*auxfunc) ARGLIST((void)) )
598{
599 struct warn_specific_data *wt = get_warn_specific();
600
601 if ( wt == NULL )
602 return ;
603
604 wt->aux_function = auxfunc;
605}
606
607void set_default_aux_warning_function ( char *(*auxfunc) ARGLIST((void)) )
608{
609 struct warn_specific_data *wt = &warn_defaults;
610
611 if ( wt == NULL )
612 return ;
613
614 wt->aux_function = auxfunc;
615}
Note: See TracBrowser for help on using the repository browser.