<foo.h> -> <bits/foo.h>.
[kopensolaris-gnu/glibc.git] / stdlib / fmtmsg.c
1 /* Copyright (C) 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <fmtmsg.h>
21 #include <bits/libc-lock.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/syslog.h>
26
27
28 /* We have global data, protect the modification.  */
29 __libc_lock_define_initialized (static, lock)
30
31
32 enum
33 {
34   label_mask = 0x01,
35   severity_mask = 0x02,
36   text_mask = 0x04,
37   action_mask = 0x08,
38   tag_mask = 0x10,
39   all_mask = label_mask | severity_mask | text_mask | action_mask | tag_mask
40 };
41
42 static struct
43 {
44   const char *name;
45   size_t len;
46 } keywords[] =
47   {
48     { "label", 5 },
49     { "severity", 8 },
50     { "text", 4 },
51     { "action", 6},
52     { "tag", 3 }
53   };
54 #define NKEYWORDS (sizeof( keywords) / sizeof (keywords[0]))
55
56
57 struct severity_info
58 {
59   int severity;
60   const char *string;
61   struct severity_info *next;
62 };
63
64
65 /* List of known severities.  */
66 static const struct severity_info nosev =
67 {
68   MM_NOSEV, "", NULL
69 };
70 static const struct severity_info haltsev =
71 {
72   MM_HALT, "HALT", (struct severity_info *) &nosev
73 };
74 static const struct severity_info errorsev =
75 {
76   MM_ERROR, "ERROR", (struct severity_info *) &haltsev
77 };
78 static const struct severity_info warningsev =
79 {
80   MM_WARNING, "WARNING", (struct severity_info *) &errorsev
81 };
82 static const struct severity_info infosev =
83 {
84   MM_INFO, "INFO", (struct severity_info *) &warningsev
85 };
86
87 /* Start of the list.  */
88 static struct severity_info *severity_list = (struct severity_info *) &infosev;
89
90
91 /* Prototypes for local functions.  */
92 static int internal_addseverity (int severity, const char *string);
93
94
95 int
96 fmtmsg (long int classification, const char *label, int severity,
97         const char *text, const char *action, const char *tag)
98 {
99   static int print = -1;
100   int result = MM_OK;
101   struct severity_info *severity_rec;
102
103   if (print == -1)
104     {
105       __libc_lock_lock (lock);
106
107       if (print == -1)
108         {
109           const char *msgverb_var = getenv ("MSGVERB");
110           const char *sevlevel_var = getenv ("SEV_LEVEL");
111
112           if (msgverb_var != NULL && msgverb_var[0] != '\0')
113             {
114               /* Using this extra variable allows us to work without
115                  locking.  */
116               print = 0;
117
118               do
119                 {
120                   size_t cnt;
121
122                   for (cnt = 0; cnt < NKEYWORDS; ++cnt)
123                     if (memcmp (msgverb_var,
124                                 keywords[cnt].name, keywords[cnt].len) == 0
125                         && (msgverb_var[keywords[cnt].len] == ':'
126                             || msgverb_var[keywords[cnt].len] == '\0'))
127                       break;
128
129                   if (cnt < NKEYWORDS)
130                     {
131                       print |= 1 << cnt;
132
133                       msgverb_var += keywords[cnt].len;
134                       if (msgverb_var[0] == ':')
135                         ++msgverb_var;
136                     }
137                   else
138                     {
139                      /* We found an illegal keyword in the
140                         environment variable.  The specifications say
141                         that we print all fields.  */
142                       print = all_mask;
143                       break;
144                     }
145                 }
146               while (msgverb_var[0] != '\0');
147             }
148           else
149             print = all_mask;
150
151
152           if (sevlevel_var != NULL)
153             while (sevlevel_var[0] != '\0')
154               {
155                 const char *end = strchr (sevlevel_var, ':');
156                 int level;
157
158                 if (end == NULL)
159                   end = strchr (sevlevel_var, '\0');
160
161                 /* First field: keyword.  This is not used here but it
162                    must be present.  */
163                 while (sevlevel_var < end)
164                   if (*sevlevel_var++ == ',')
165                     break;
166
167                 if (sevlevel_var < end)
168                   {
169                     /* Second field: severity level, a number.  */
170                     char *cp;
171
172                     level = strtol (sevlevel_var, &cp, 0);
173                     if (cp != sevlevel_var && cp < end && *cp++ == ','
174                         && level > MM_INFO)
175                       {
176                         const char *new_string;
177
178                         new_string = __strndup (cp, end - cp);
179
180                         if (new_string != NULL
181                             && (internal_addseverity (level, new_string)
182                                 != MM_OK))
183                           free ((char *) new_string);
184                       }
185                   }
186
187                 sevlevel_var = end + (*end == ':' ? 1 : 0);
188               }
189         }
190
191       __libc_lock_unlock (lock);
192     }
193
194   /* Start the real work.  First check whether the input is ok.  */
195   if (label != MM_NULLLBL)
196     {
197       /* Must be two fields, separated by a colon.  */
198       const char *cp = strchr (label, ':');
199       if (cp == NULL)
200         return MM_NOTOK;
201
202       /* The first field must not contain more then 10 bytes.  */
203       if (cp - label > 10
204           /* The second field must not have more then 14 bytes.  */
205           || strlen (cp + 1) > 14)
206         return MM_NOTOK;
207     }
208
209   for (severity_rec = severity_list; severity_rec != NULL;
210        severity_rec = severity_rec->next)
211     if (severity == severity_rec->severity)
212       /* Bingo.  */
213       break;
214
215   /* If we don't know anything about the severity level return an error.  */
216   if (severity_rec == NULL)
217     return MM_NOTOK;
218
219
220   /* Now we can print.  */
221   if (classification & MM_PRINT)
222     {
223       int do_label = (print & label_mask) && label != MM_NULLLBL;
224       int do_severity = (print & severity_mask) && severity != MM_NULLSEV;
225       int do_text = (print & text_mask) && text != MM_NULLTXT;
226       int do_action = (print & action_mask) && action != MM_NULLACT;
227       int do_tag = (print & tag_mask) && tag != MM_NULLTAG;
228
229       if (fprintf (stderr, "%s%s%s%s%s%s%s%s%s%s\n",
230                    do_label ? label : "",
231                    do_label && (do_severity | do_text) ? ": " : "",
232                    do_severity ? severity_rec->string : "",
233                    do_severity && do_text ? ": " : "",
234                    do_text ? text : "",
235                    (do_label | do_severity | do_text) && (do_action | do_tag)
236                    ? "\n" : "",
237                    do_action ? "TO FIX: " : "",
238                    do_action ? action : "",
239                    do_action && do_tag ? " " : "",
240                    do_tag ? tag : "") == EOF)
241         /* Oh, oh.  An error occured during the output.  */
242         result = MM_NOMSG;
243     }
244
245   if (classification & MM_CONSOLE)
246     {
247       int do_label = label != MM_NULLLBL;
248       int do_severity = severity != MM_NULLSEV;
249       int do_text = text != MM_NULLTXT;
250       int do_action = action != MM_NULLACT;
251       int do_tag = tag != MM_NULLTAG;
252
253       syslog (LOG_ERR, "%s%s%s%s%s%s%s%s%s%s\n",
254               do_label ? label : "",
255               do_label && (do_severity | do_text) ? ": " : "",
256               do_severity ? severity_rec->string : "",
257               do_severity && do_text ? ": " : "",
258               do_text ? text : "",
259               (do_label | do_severity | do_text) && (do_action | do_tag)
260               ? "\n" : "",
261               do_action ? "TO FIX: " : "",
262               do_action ? action : "",
263               do_action && do_tag ? " " : "",
264               do_tag ? tag : "");
265     }
266
267   return result;
268 }
269
270
271 /* Add the new entry to the list.  */
272 static int
273 internal_addseverity (int severity, const char *string)
274 {
275   struct severity_info *runp, *lastp;
276   int result = MM_OK;
277
278   /* First see if there is already a record for the severity level.  */
279   for (runp = severity_list, lastp = NULL; runp != NULL; runp = runp-> next)
280     if (runp->severity == severity)
281       break;
282     else
283       lastp = runp;
284
285   if (runp != NULL)
286     {
287       /* Release old string.  */
288       free ((char *) runp->string);
289
290       if (string != NULL)
291         /* Change the string.  */
292         runp->string = string;
293       else
294         {
295           /* Remove the severity class.  */
296           if (lastp == NULL)
297             severity_list = runp->next;
298           else
299             lastp->next = runp->next;
300
301           free (runp);
302         }
303     }
304   else if (string != NULL)
305     {
306       runp = malloc (sizeof (*runp));
307       if (runp == NULL)
308         result = MM_NOTOK;
309       else
310         {
311           runp->severity = severity;
312           runp->next = severity_list;
313           runp->string = string;
314           severity_list = runp;
315         }
316     }
317   else
318     /* We tried to remove a non-existing severity class.  */
319     result = MM_NOTOK;
320
321   return result;
322 }
323
324
325 /* Add new severity level or remove old one.  */
326 int
327 addseverity (int severity, const char *string)
328 {
329   int result;
330   const char *new_string;
331
332   if (string == NULL)
333     /* We want to remove the severity class.  */
334     new_string = NULL;
335   else
336     {
337       new_string = __strdup (string);
338
339       if (new_string == NULL || severity <= MM_INFO)
340         /* Allocation failed or illegal value.  */
341         return MM_NOTOK;
342     }
343
344   /* Protect the global data.  */
345   __libc_lock_lock (lock);
346
347   /* Do the real work.  */
348   result = internal_addseverity (severity, string);
349
350   if (result != MM_OK)
351     /* Free the allocated string.  */
352     free ((char *) new_string);
353
354   /* Release the lock.  */
355   __libc_lock_unlock (lock);
356
357   return result;
358 }