1/*-
2 * Copyright (c) 2002 Mike Barcroft <mike@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <fmtmsg.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
35/* Default value for MSGVERB. */
36#define	DFLT_MSGVERB	"label:severity:text:action:tag"
37
38/* Maximum valid size for a MSGVERB. */
39#define	MAX_MSGVERB	sizeof(DFLT_MSGVERB)
40
41static char	*printfmt(char *, long, const char *, int, const char *,
42		    const char *, const char *);
43static char	*nextcomp(const char *);
44static const char
45		*sevinfo(int);
46static int	 validmsgverb(const char *);
47
48static const char *validlist[] = {
49	"label", "severity", "text", "action", "tag", NULL
50};
51
52int
53fmtmsg(long class, const char *label, int sev, const char *text,
54    const char *action, const char *tag)
55{
56	FILE *fp;
57	char *env, *msgverb, *output;
58
59	if (class & MM_PRINT) {
60		if ((env = getenv("MSGVERB")) != NULL && *env != '\0' &&
61		    strlen(env) <= strlen(DFLT_MSGVERB)) {
62			if ((msgverb = strdup(env)) == NULL)
63				return (MM_NOTOK);
64			else if (validmsgverb(msgverb) == 0) {
65				free(msgverb);
66				goto def;
67			}
68		} else {
69def:
70			if ((msgverb = strdup(DFLT_MSGVERB)) == NULL)
71				return (MM_NOTOK);
72		}
73		output = printfmt(msgverb, class, label, sev, text, action,
74		    tag);
75		if (output == NULL) {
76			free(msgverb);
77			return (MM_NOTOK);
78		}
79		if (*output != '\0')
80			fprintf(stderr, "%s", output);
81		free(msgverb);
82		free(output);
83	}
84	if (class & MM_CONSOLE) {
85		output = printfmt(DFLT_MSGVERB, class, label, sev, text,
86		    action, tag);
87		if (output == NULL)
88			return (MM_NOCON);
89		if (*output != '\0') {
90			if ((fp = fopen("/dev/console", "a")) == NULL) {
91				free(output);
92				return (MM_NOCON);
93			}
94			fprintf(fp, "%s", output);
95			fclose(fp);
96		}
97		free(output);
98	}
99	return (MM_OK);
100}
101
102#define INSERT_COLON							\
103	if (*output != '\0')						\
104		strlcat(output, ": ", size)
105#define INSERT_NEWLINE							\
106	if (*output != '\0')						\
107		strlcat(output, "\n", size)
108#define INSERT_SPACE							\
109	if (*output != '\0')						\
110		strlcat(output, " ", size)
111
112/*
113 * Returns NULL on memory allocation failure, otherwise returns a pointer to
114 * a newly malloc()'d output buffer.
115 */
116static char *
117printfmt(char *msgverb, long class, const char *label, int sev,
118    const char *text, const char *act, const char *tag)
119{
120	size_t size;
121	char *comp, *output;
122	const char *sevname;
123
124	size = 32;
125	if (label != MM_NULLLBL)
126		size += strlen(label);
127	if ((sevname = sevinfo(sev)) != NULL)
128		size += strlen(sevname);
129	if (text != MM_NULLTXT)
130		size += strlen(text);
131	if (act != MM_NULLACT)
132		size += strlen(act);
133	if (tag != MM_NULLTAG)
134		size += strlen(tag);
135
136	if ((output = malloc(size)) == NULL)
137		return (NULL);
138	*output = '\0';
139	while ((comp = nextcomp(msgverb)) != NULL) {
140		if (strcmp(comp, "label") == 0 && label != MM_NULLLBL) {
141			INSERT_COLON;
142			strlcat(output, label, size);
143		} else if (strcmp(comp, "severity") == 0 && sevname != NULL) {
144			INSERT_COLON;
145			strlcat(output, sevinfo(sev), size);
146		} else if (strcmp(comp, "text") == 0 && text != MM_NULLTXT) {
147			INSERT_COLON;
148			strlcat(output, text, size);
149		} else if (strcmp(comp, "action") == 0 && act != MM_NULLACT) {
150			INSERT_NEWLINE;
151			strlcat(output, "TO FIX: ", size);
152			strlcat(output, act, size);
153		} else if (strcmp(comp, "tag") == 0 && tag != MM_NULLTAG) {
154			INSERT_SPACE;
155			strlcat(output, tag, size);
156		}
157	}
158	INSERT_NEWLINE;
159	return (output);
160}
161
162/*
163 * Returns a component of a colon delimited string.  NULL is returned to
164 * indicate that there are no remaining components.  This function must be
165 * called until it returns NULL in order for the local state to be cleared.
166 */
167static char *
168nextcomp(const char *msgverb)
169{
170	static char lmsgverb[MAX_MSGVERB], *state;
171	char *retval;
172
173	if (*lmsgverb == '\0') {
174		strlcpy(lmsgverb, msgverb, sizeof(lmsgverb));
175		retval = strtok_r(lmsgverb, ":", &state);
176	} else {
177		retval = strtok_r(NULL, ":", &state);
178	}
179	if (retval == NULL)
180		*lmsgverb = '\0';
181	return (retval);
182}
183
184static const char *
185sevinfo(int sev)
186{
187
188	switch (sev) {
189	case MM_HALT:
190		return ("HALT");
191	case MM_ERROR:
192		return ("ERROR");
193	case MM_WARNING:
194		return ("WARNING");
195	case MM_INFO:
196		return ("INFO");
197	default:
198		return (NULL);
199	}
200}
201
202/*
203 * Returns 1 if the msgverb list is valid, otherwise 0.
204 */
205static int
206validmsgverb(const char *msgverb)
207{
208	char *msgcomp;
209	int i, equality;
210
211	equality = 0;
212	while ((msgcomp = nextcomp(msgverb)) != NULL) {
213		equality--;
214		for (i = 0; validlist[i] != NULL; i++) {
215			if (strcmp(msgcomp, validlist[i]) == 0)
216				equality++;
217		}
218	}
219	return (!equality);
220}
221