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