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
48int
49fmtmsg(long class, const char *label, int sev, const char *text,
50    const char *action, const char *tag)
51{
52	FILE *fp;
53	char *env, *msgverb, *output;
54
55	if (class & MM_PRINT) {
56		if ((env = getenv("MSGVERB")) != NULL && *env != '\0' &&
57		    strlen(env) <= strlen(DFLT_MSGVERB)) {
58			if ((msgverb = strdup(env)) == NULL)
59				return (MM_NOTOK);
60			else if (validmsgverb(msgverb) == 0) {
61				free(msgverb);
62				goto def;
63			}
64		} else {
65def:
66			if ((msgverb = strdup(DFLT_MSGVERB)) == NULL)
67				return (MM_NOTOK);
68		}
69		output = printfmt(msgverb, class, label, sev, text, action,
70		    tag);
71		if (output == NULL) {
72			free(msgverb);
73			return (MM_NOTOK);
74		}
75		if (*output != '\0')
76			fprintf(stderr, "%s", output);
77		free(msgverb);
78		free(output);
79	}
80	if (class & MM_CONSOLE) {
81		output = printfmt(DFLT_MSGVERB, class, label, sev, text,
82		    action, tag);
83		if (output == NULL)
84			return (MM_NOCON);
85		if (*output != '\0') {
86			if ((fp = fopen("/dev/console", "ae")) == NULL) {
87				free(output);
88				return (MM_NOCON);
89			}
90			fprintf(fp, "%s", output);
91			fclose(fp);
92		}
93		free(output);
94	}
95	return (MM_OK);
96}
97
98#define INSERT_COLON							\
99	if (*output != '\0')						\
100		strlcat(output, ": ", size)
101#define INSERT_NEWLINE							\
102	if (*output != '\0')						\
103		strlcat(output, "\n", size)
104#define INSERT_SPACE							\
105	if (*output != '\0')						\
106		strlcat(output, " ", size)
107
108/*
109 * Returns NULL on memory allocation failure, otherwise returns a pointer to
110 * a newly malloc()'d output buffer.
111 */
112static char *
113printfmt(char *msgverb, long class, const char *label, int sev,
114    const char *text, const char *act, const char *tag)
115{
116	size_t size;
117	char *comp, *output;
118	const char *sevname;
119
120	size = 32;
121	if (label != MM_NULLLBL)
122		size += strlen(label);
123	if ((sevname = sevinfo(sev)) != NULL)
124		size += strlen(sevname);
125	if (text != MM_NULLTXT)
126		size += strlen(text);
127	if (act != MM_NULLACT)
128		size += strlen(act);
129	if (tag != MM_NULLTAG)
130		size += strlen(tag);
131
132	if ((output = malloc(size)) == NULL)
133		return (NULL);
134	*output = '\0';
135	while ((comp = nextcomp(msgverb)) != NULL) {
136		if (strcmp(comp, "label") == 0 && label != MM_NULLLBL) {
137			INSERT_COLON;
138			strlcat(output, label, size);
139		} else if (strcmp(comp, "severity") == 0 && sevname != NULL) {
140			INSERT_COLON;
141			strlcat(output, sevinfo(sev), size);
142		} else if (strcmp(comp, "text") == 0 && text != MM_NULLTXT) {
143			INSERT_COLON;
144			strlcat(output, text, size);
145		} else if (strcmp(comp, "action") == 0 && act != MM_NULLACT) {
146			INSERT_NEWLINE;
147			strlcat(output, "TO FIX: ", size);
148			strlcat(output, act, size);
149		} else if (strcmp(comp, "tag") == 0 && tag != MM_NULLTAG) {
150			INSERT_SPACE;
151			strlcat(output, tag, size);
152		}
153	}
154	INSERT_NEWLINE;
155	return (output);
156}
157
158/*
159 * Returns a component of a colon delimited string.  NULL is returned to
160 * indicate that there are no remaining components.  This function must be
161 * called until it returns NULL in order for the local state to be cleared.
162 */
163static char *
164nextcomp(const char *msgverb)
165{
166	static char lmsgverb[MAX_MSGVERB], *state;
167	char *retval;
168
169	if (*lmsgverb == '\0') {
170		strlcpy(lmsgverb, msgverb, sizeof(lmsgverb));
171		retval = strtok_r(lmsgverb, ":", &state);
172	} else {
173		retval = strtok_r(NULL, ":", &state);
174	}
175	if (retval == NULL)
176		*lmsgverb = '\0';
177	return (retval);
178}
179
180static const char *
181sevinfo(int sev)
182{
183
184	switch (sev) {
185	case MM_HALT:
186		return ("HALT");
187	case MM_ERROR:
188		return ("ERROR");
189	case MM_WARNING:
190		return ("WARNING");
191	case MM_INFO:
192		return ("INFO");
193	default:
194		return (NULL);
195	}
196}
197
198/*
199 * Returns 1 if the msgverb list is valid, otherwise 0.
200 */
201static int
202validmsgverb(const char *msgverb)
203{
204	const char *validlist = "label\0severity\0text\0action\0tag\0";
205	char *msgcomp;
206	size_t len1, len2;
207	const char *p;
208	int equality;
209
210	equality = 0;
211	while ((msgcomp = nextcomp(msgverb)) != NULL) {
212		equality--;
213		len1 = strlen(msgcomp);
214		for (p = validlist; (len2 = strlen(p)) != 0; p += len2 + 1) {
215			if (len1 == len2 && memcmp(msgcomp, p, len1) == 0)
216				equality++;
217		}
218	}
219	return (!equality);
220}
221