xref: /netbsd-src/lib/libc/gen/fmtmsg.c (revision 9a513d96431ccbcc88e47c95deb796c0a8049327)
1*9a513d96Schristos /*	$NetBSD: fmtmsg.c,v 1.6 2014/09/18 13:58:20 christos Exp $	*/
2734b2b9bSkleink 
3734b2b9bSkleink /*-
4734b2b9bSkleink  * Copyright (c) 1999 The NetBSD Foundation, Inc.
5734b2b9bSkleink  * All rights reserved.
6734b2b9bSkleink  *
7734b2b9bSkleink  * This code is derived from software contributed to The NetBSD Foundation
8734b2b9bSkleink  * by Klaus Klein.
9734b2b9bSkleink  *
10734b2b9bSkleink  * Redistribution and use in source and binary forms, with or without
11734b2b9bSkleink  * modification, are permitted provided that the following conditions
12734b2b9bSkleink  * are met:
13734b2b9bSkleink  * 1. Redistributions of source code must retain the above copyright
14734b2b9bSkleink  *    notice, this list of conditions and the following disclaimer.
15734b2b9bSkleink  * 2. Redistributions in binary form must reproduce the above copyright
16734b2b9bSkleink  *    notice, this list of conditions and the following disclaimer in the
17734b2b9bSkleink  *    documentation and/or other materials provided with the distribution.
18734b2b9bSkleink  *
19734b2b9bSkleink  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20734b2b9bSkleink  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21734b2b9bSkleink  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22734b2b9bSkleink  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23734b2b9bSkleink  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24734b2b9bSkleink  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25734b2b9bSkleink  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26734b2b9bSkleink  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27734b2b9bSkleink  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28734b2b9bSkleink  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29734b2b9bSkleink  * POSSIBILITY OF SUCH DAMAGE.
30734b2b9bSkleink  */
31734b2b9bSkleink 
32734b2b9bSkleink #include <sys/cdefs.h>
33d5cfef41Skleink #if defined(LIBC_SCCS) && !defined(lint)
34*9a513d96Schristos __RCSID("$NetBSD: fmtmsg.c,v 1.6 2014/09/18 13:58:20 christos Exp $");
35d5cfef41Skleink #endif /* LIBC_SCCS and not lint */
36d5cfef41Skleink 
37734b2b9bSkleink #include <fmtmsg.h>
38734b2b9bSkleink #include <paths.h>
39734b2b9bSkleink #include <stdio.h>
40734b2b9bSkleink #include <stdlib.h>
41734b2b9bSkleink #include <string.h>
42734b2b9bSkleink 
43cf884af3Smatt static unsigned int	msgverb(const char *);
44cf884af3Smatt static const char *	severity2str(int);
45cf884af3Smatt static int		writeit(FILE *, unsigned int, const char *,
46734b2b9bSkleink 			    const char *, const char *, const char *,
47cf884af3Smatt 			    const char *);
48734b2b9bSkleink 
49734b2b9bSkleink #define MM_VERBLABEL		0x01U
50734b2b9bSkleink #define MM_VERBSEVERITY		0x02U
51734b2b9bSkleink #define MM_VERBTEXT		0x04U
52734b2b9bSkleink #define MM_VERBACTION		0x08U
53734b2b9bSkleink #define MM_VERBTAG		0x10U
54734b2b9bSkleink #define MM_VERBALL	\
55734b2b9bSkleink     (MM_VERBLABEL | MM_VERBSEVERITY | MM_VERBTEXT | MM_VERBACTION | \
56734b2b9bSkleink      MM_VERBTAG)
57734b2b9bSkleink 
58734b2b9bSkleink static const struct keyword {
59734b2b9bSkleink 	size_t			len;	/* strlen(keyword) */
60734b2b9bSkleink 	const char * const	keyword;
61734b2b9bSkleink } keywords[] = {
62734b2b9bSkleink 	{ 5,	"label"		},	/* log2(MM_VERBLABEL) */
63734b2b9bSkleink 	{ 8,	"severity"	},	/* ... */
64734b2b9bSkleink 	{ 4,	"text"		},
65734b2b9bSkleink 	{ 6,	"action"	},
66734b2b9bSkleink 	{ 3,	"tag"		}	/* log2(MM_VERBTAG) */
67734b2b9bSkleink };
68734b2b9bSkleink 
69734b2b9bSkleink static const size_t nkeywords = sizeof (keywords) / sizeof (keywords[0]);
70734b2b9bSkleink 
71734b2b9bSkleink /*
72734b2b9bSkleink  * Convert a colon-separated list of known keywords to a set of MM_VERB*
73734b2b9bSkleink  * flags, defaulting to `all' if not set, empty, or in presence of unknown
74734b2b9bSkleink  * keywords.
75734b2b9bSkleink  */
76734b2b9bSkleink static unsigned int
msgverb(const char * str)77cf884af3Smatt msgverb(const char *str)
78734b2b9bSkleink {
7937dfab8aSthorpej 	u_int i;
80734b2b9bSkleink 	unsigned int result;
81734b2b9bSkleink 
82734b2b9bSkleink 	if (str == NULL)
83734b2b9bSkleink 		return (MM_VERBALL);
84734b2b9bSkleink 
85734b2b9bSkleink 	result = 0;
86734b2b9bSkleink 	while (*str != '\0') {
87734b2b9bSkleink 		for (i = 0; i < nkeywords; i++) {
88734b2b9bSkleink 			if (memcmp(str, keywords[i].keyword, keywords[i].len)
89734b2b9bSkleink 			    == 0 &&
90734b2b9bSkleink 			    (*(str + keywords[i].len) == ':' ||
91734b2b9bSkleink 			     *(str + keywords[i].len) == '\0'))
92734b2b9bSkleink 				break;
93734b2b9bSkleink 		}
94734b2b9bSkleink 		if (i == nkeywords) {
95734b2b9bSkleink 			result = MM_VERBALL;
96734b2b9bSkleink 			break;
97734b2b9bSkleink 		}
98734b2b9bSkleink 
99734b2b9bSkleink 		result |= (1 << i);
100734b2b9bSkleink 		if (*(str += keywords[i].len) == ':')
101734b2b9bSkleink 			str++;	/* Advance */
102734b2b9bSkleink 	}
103734b2b9bSkleink 	if (result == 0)
104734b2b9bSkleink 		result = MM_VERBALL;
105734b2b9bSkleink 
106734b2b9bSkleink 	return (result);
107734b2b9bSkleink }
108734b2b9bSkleink 
109cf884af3Smatt static const char severities[][8] = {
110734b2b9bSkleink 	"",		/* MM_NONE */
111734b2b9bSkleink 	"HALT",
112734b2b9bSkleink 	"ERROR",
113734b2b9bSkleink 	"WARNING",
114734b2b9bSkleink 	"INFO"
115734b2b9bSkleink };
116734b2b9bSkleink 
117734b2b9bSkleink static const size_t nseverities = sizeof (severities) / sizeof (severities[0]);
118734b2b9bSkleink 
119734b2b9bSkleink /*
120734b2b9bSkleink  * Returns the string representation associated with the numerical severity
121734b2b9bSkleink  * value, defaulting to NULL for an unknown value.
122734b2b9bSkleink  */
123734b2b9bSkleink static const char *
severity2str(int severity)124cf884af3Smatt severity2str(int severity)
125734b2b9bSkleink {
126734b2b9bSkleink 	const char *result;
127734b2b9bSkleink 
12837dfab8aSthorpej 	if (severity >= 0 &&
12937dfab8aSthorpej 	    (u_int) severity < nseverities)
130734b2b9bSkleink 		result = severities[severity];
131734b2b9bSkleink 	else
132734b2b9bSkleink 		result = NULL;
133734b2b9bSkleink 
134734b2b9bSkleink 	return (result);
135734b2b9bSkleink }
136734b2b9bSkleink 
137734b2b9bSkleink /*
138734b2b9bSkleink  * Format and write the message to the given stream, selecting those
139734b2b9bSkleink  * components displayed from msgverb, returning the number of characters
140734b2b9bSkleink  * written, or a negative value in case of an error.
141734b2b9bSkleink  */
142734b2b9bSkleink static int
writeit(FILE * stream,unsigned int which,const char * label,const char * sevstr,const char * text,const char * action,const char * tag)143cf884af3Smatt writeit(FILE *stream, unsigned int which, const char *label,
144cf884af3Smatt 	const char *sevstr, const char *text, const char *action,
145cf884af3Smatt 	const char *tag)
146734b2b9bSkleink {
147734b2b9bSkleink 	int nwritten;
148734b2b9bSkleink 
149734b2b9bSkleink 	nwritten = fprintf(stream, "%s%s%s%s%s%s%s%s%s%s%s",
150734b2b9bSkleink 	    ((which & MM_VERBLABEL) && label != MM_NULLLBL) ?
151734b2b9bSkleink 	    label : "",
152734b2b9bSkleink 	    ((which & MM_VERBLABEL) && label != MM_NULLLBL) ?
153734b2b9bSkleink 	    ": " : "",
154734b2b9bSkleink 	    (which & MM_VERBSEVERITY) ?
155734b2b9bSkleink 	    sevstr : "",
156734b2b9bSkleink 	    (which & MM_VERBSEVERITY) ?
157734b2b9bSkleink 	    ": " : "",
158734b2b9bSkleink 	    ((which & MM_VERBTEXT) && text != MM_NULLTXT) ?
159734b2b9bSkleink 	    text : "",
160734b2b9bSkleink 	    ((which & MM_VERBLABEL) && label != MM_NULLLBL) ||
161734b2b9bSkleink 	    ((which & MM_VERBSEVERITY)) ||
162734b2b9bSkleink 	    ((which & MM_VERBTEXT) && text != MM_NULLTXT) ?
163734b2b9bSkleink 	    "\n" : "",
164734b2b9bSkleink 	    ((which & MM_VERBACTION) && action != MM_NULLACT) ?
165734b2b9bSkleink 	    "TO FIX: " : "",
166734b2b9bSkleink 	    ((which & MM_VERBACTION) && action != MM_NULLACT) ?
167734b2b9bSkleink 	    action : "",
168734b2b9bSkleink 	    ((which & MM_VERBACTION) && label != MM_NULLACT) ?
169734b2b9bSkleink 	    " " : "",
170734b2b9bSkleink 	    ((which & MM_VERBTAG) && tag != MM_NULLTAG) ?
171734b2b9bSkleink 	    tag : "",
172734b2b9bSkleink 	    ((which & MM_VERBACTION) && action != MM_NULLACT) ||
173734b2b9bSkleink 	    ((which & MM_VERBTAG) && tag != MM_NULLTAG) ?
174734b2b9bSkleink 	    "\n" : "");
175734b2b9bSkleink 
176734b2b9bSkleink 	return (nwritten);
177734b2b9bSkleink }
178734b2b9bSkleink 
179734b2b9bSkleink int
fmtmsg(long classification,const char * label,int severity,const char * text,const char * action,const char * tag)180cf884af3Smatt fmtmsg(long classification, const char *label, int severity,
181cf884af3Smatt 	const char *text, const char *action, const char *tag)
182734b2b9bSkleink {
183734b2b9bSkleink 	FILE *console;
184734b2b9bSkleink 	const char *p, *sevstr;
185734b2b9bSkleink 	int result;
186734b2b9bSkleink 
187734b2b9bSkleink 	/* Validate label constraints, if not null. */
188734b2b9bSkleink 	if (label != MM_NULLLBL) {
189734b2b9bSkleink 		/*
190734b2b9bSkleink 		 * Two fields, separated by a colon.  The first field is up to
191734b2b9bSkleink 		 * 10 bytes, the second is up to 14 bytes.
192734b2b9bSkleink 		 */
193734b2b9bSkleink 		p = strchr(label, ':');
194734b2b9bSkleink 		if (p ==  NULL || p - label > 10 || strlen(p + 1) > 14)
195734b2b9bSkleink 			return (MM_NOTOK);
196734b2b9bSkleink 	}
197734b2b9bSkleink 	/* Validate severity argument. */
198734b2b9bSkleink 	if ((sevstr = severity2str(severity)) == NULL)
199734b2b9bSkleink 		return (MM_NOTOK);
200734b2b9bSkleink 
201734b2b9bSkleink 	/*
202734b2b9bSkleink 	 * Fact in search for a better place: XSH5 does not define any
203734b2b9bSkleink 	 * functionality for `classification' bits other than the display
204734b2b9bSkleink 	 * subclassification.
205734b2b9bSkleink 	 */
206734b2b9bSkleink 
207734b2b9bSkleink 	result = 0;
208734b2b9bSkleink 
209734b2b9bSkleink 	if (classification & MM_PRINT) {
210734b2b9bSkleink 		if (writeit(stderr, msgverb(getenv("MSGVERB")),
211734b2b9bSkleink 		    label, sevstr, text, action, tag) < 0)
212734b2b9bSkleink 			result |= MM_NOMSG;
213734b2b9bSkleink 	}
214734b2b9bSkleink 	/* Similar to MM_PRINT but ignoring $MSGVERB. */
215734b2b9bSkleink 	if (classification & MM_CONSOLE) {
216*9a513d96Schristos 		if ((console = fopen(_PATH_CONSOLE, "we")) != NULL) {
217734b2b9bSkleink 			if (writeit(console, MM_VERBALL,
218734b2b9bSkleink 			    label, sevstr, text, action, tag) < 0)
219734b2b9bSkleink 				result |= MM_NOCON;
220734b2b9bSkleink 			/*
221734b2b9bSkleink 			 * Ignore result: does not constitute ``generate a
222734b2b9bSkleink 			 * console message.''
223734b2b9bSkleink 			 */
224734b2b9bSkleink 			(void)fclose(console);
225734b2b9bSkleink 		} else {
226734b2b9bSkleink 			result |= MM_NOCON;
227734b2b9bSkleink 		}
228734b2b9bSkleink 	}
229734b2b9bSkleink 
230734b2b9bSkleink 	if (result == (MM_NOMSG | MM_NOCON))
231734b2b9bSkleink 		result = MM_NOTOK;
232734b2b9bSkleink 
233734b2b9bSkleink 	return (result == 0 ? MM_OK : result);
234734b2b9bSkleink }
235