1 /*
2  * $Header: /home/kline/devel/atom/RCS/msg.c,v 1.1 1996/09/04 04:56:19 kline Exp kline $
3  * msg.c - routines to handle debug and error messages
4  */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stddef.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <time.h>
12 #include "msg.h"
13 /*
14  * Default levels for masking out messages.
15  */
16 MSG_CONST msg_severity_t msg_level[MSG_CATEGORIES] = {
17     5, 5, 0, 0, 0,
18 };
19 
20 /* The string prepended to each message. You may use date directives
21  * like %T for time (see strftime(3)). Extension: You can use %s for
22  * the severity level.
23  */
24 static const char *msg_preface[MSG_CATEGORIES] = {
25     "Trace\t",
26     "Debug\t",
27     "Warning [%Y-%m-%d %H:%M.%S] level %s\n\t",
28     "Error [%Y-%m-%d %H:%M.%S] level %s\n\t",
29     "Fatal [%Y-%m-%d %H:%M.%S] level %s\n\t",
30 };
31 
32 #if defined(MSG_COUNT_CALLS)
33 /* How many messages to accept in a given class before calling
34  * MSG_EXIT.
35  * 0 means never exit.
36  */
37 int msg_warnings[MSG_CATEGORIES] = {
38     0, 0, 0, 0, 0,
39 };
40 #endif
41 
42 msg_category_t msg_category;	/* global used for communication from macro */
43 msg_severity_t msg_severity;	/* global used for communication from macro */
44 static FILE *msg_file;
45 static int msg_parse_preface[MSG_CATEGORIES];	/* really Boolean */
46 static int msg_init_done = 0;
47 
48 /* Prototypes
49  */
50 static void parse_envir(void);
51 static char *parse_preface(const char *);
52 static void msg_fatal(char *);
53 
54 
55 void
msg_init()56 msg_init()
57 {
58     const char *env;
59     register short int i;
60 
61     msg_init_done = 1;
62     env = getenv("MSG_FILE");
63     if (env && *env) {
64         msg_file = fopen(env, "a");
65         if (msg_file == NULL) {
66             perror(env);
67             msg_file = stderr;
68         }
69 	setbuf(msg_file, NULL);
70     } else
71         msg_file = stderr;
72 
73     for (i = MSG_CATEGORIES; i--;)
74 	msg_parse_preface[i] = strchr(msg_preface[i], '%') ? 1 : 0;
75 
76     parse_envir();
77 }
78 
79 
80 static void
parse_envir()81 parse_envir()
82 {
83 #if defined(MSG_PARSE_ENVIR)
84     register short int         i = 0;
85     const char *env;
86 
87     env = getenv("MSG_LEVEL");
88     /* Parse the environment variable:
89      *     "5,,-,"
90      * becomes
91      *     [ 5, OLD VALUE, MSG_MAX_SEVERITY + 1, OLD VALUE, OLD VALUE ]
92      */
93     if (env && *env) {
94         enum { fresh, number, junk } state = fresh;
95         register short int                          value = 0;
96 
97         do {
98             if (*env == ',') {
99 		if (state == number || state == junk)
100                     msg_level[i] = value;
101                 value = 0;
102                 state = fresh;
103                 if (++i >= MSG_CATEGORIES)
104                     break;
105             } else if (state != junk && isdigit(*env)) {
106                 value *= 10;
107                 value += *env - '0';
108                 if (value > MSG_MAX_SEVERITY + 1) {
109                     value = MSG_MAX_SEVERITY + 1;
110                     state = junk;
111                 } else
112                     state = number;
113 	    } else if (*env == '-') {
114 		value = MSG_MAX_SEVERITY + 1;
115 		state = junk;
116 	    } else if (*env == '+') {
117 		value = 0;
118 		state = junk;
119             } else if (state == number) /* non-digit, change state */
120                 state = junk; /* so "4k5, 7" becomes "4,7", not "45,7" */
121         } while (*++env);
122 
123         if (state == number || state == junk)
124             msg_level[i] = value;
125     }
126 #endif
127 }
128 
129 
130 void
msg_printf(const char * fmt,...)131 msg_printf(const char *fmt, ...)
132 {
133     char    buff[1024];
134     va_list args;
135     register short int     prelen, msglen;
136 
137     if (!msg_init_done)
138 	msg_init();
139 
140     if (msg_parse_preface[msg_category]) {
141         struct tm *tm;
142 	time_t     clock;
143 	char      *fmt;
144 
145         fmt = parse_preface(msg_preface[msg_category]);
146         time(&clock);
147         tm = localtime(&clock);
148         prelen = strftime(buff, sizeof(buff), fmt, tm);
149         if (prelen == 0)		/* Error condition from strftime */
150             msg_fatal("Buffer too small");
151         free(fmt);
152     } else {
153         strncpy(buff, msg_preface[msg_category], sizeof(buff));
154 	prelen = strlen(msg_preface[msg_category]);
155     }
156 
157     va_start(args, fmt);
158 #if defined(_POSIX_SOURCE)
159     msglen = vsprintf(buff + prelen, fmt, args);
160 
161     if (prelen + msglen > sizeof(buff))
162         msg_fatal("Buffer too small");
163 #else
164     /* BSD vsprintf() returns the first argument - can't be used
165      * for detecting buffer over-runs.
166      */
167     (void) vsprintf(buff + prelen, fmt, args);
168 #endif
169 
170     va_end(args);
171     fprintf(msg_file, "%s\n", buff);
172 }
173 
174 
175 static char *
parse_preface(const char * template)176 parse_preface(const char *template) {
177     char                    *fmt;
178     size_t                   fmt_size;
179     ptrdiff_t                p = 0;
180     enum { normal, percent } state = normal;
181 
182     fmt_size = strlen(template) + 1;
183     fmt = (char *) malloc(fmt_size);
184     if (fmt == NULL)
185         msg_fatal("Out of memory");
186 
187     for (; *template; template++) {
188         if (p > fmt_size) {
189             fmt_size += 16;
190             fmt = realloc(fmt, fmt_size);
191             if (fmt == NULL)
192                 msg_fatal("Out of memory");
193         }
194         fmt[p++] = *template;
195         if (state == percent) {
196             if (*template == 's')
197 #if defined(_POSIX_SOURCE)
198                 p += sprintf(fmt + p - 2, "%d", msg_severity) - 2;
199 #else
200                 p = strchr(sprintf(fmt + p - 2, "%d", msg_severity), 0) - fmt;
201 #endif
202             state = normal;
203         } else if (*template == '%')
204             state = percent;
205     }
206     fmt[p] = 0;
207     return fmt;
208 }
209 
210 static void
msg_fatal(char * msg)211 msg_fatal(char *msg)
212 {
213     fprintf(msg_file, "Fatal error: %s\n", msg);
214     fclose(msg_file);
215     exit(1);
216 }
217