1/* $NetBSD: fmtmsg.c,v 1.5 2012/03/20 16:36:05 matt Exp $ */ 2 3/*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Klaus Klein. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#if defined(LIBC_SCCS) && !defined(lint) 34__RCSID("$NetBSD: fmtmsg.c,v 1.5 2012/03/20 16:36:05 matt Exp $"); 35#endif /* LIBC_SCCS and not lint */ 36 37#include <fmtmsg.h> 38#include <paths.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42 43static unsigned int msgverb(const char *); 44static const char * severity2str(int); 45static int writeit(FILE *, unsigned int, const char *, 46 const char *, const char *, const char *, 47 const char *); 48 49#define MM_VERBLABEL 0x01U 50#define MM_VERBSEVERITY 0x02U 51#define MM_VERBTEXT 0x04U 52#define MM_VERBACTION 0x08U 53#define MM_VERBTAG 0x10U 54#define MM_VERBALL \ 55 (MM_VERBLABEL | MM_VERBSEVERITY | MM_VERBTEXT | MM_VERBACTION | \ 56 MM_VERBTAG) 57 58static const struct keyword { 59 size_t len; /* strlen(keyword) */ 60 const char * const keyword; 61} keywords[] = { 62 { 5, "label" }, /* log2(MM_VERBLABEL) */ 63 { 8, "severity" }, /* ... */ 64 { 4, "text" }, 65 { 6, "action" }, 66 { 3, "tag" } /* log2(MM_VERBTAG) */ 67}; 68 69static const size_t nkeywords = sizeof (keywords) / sizeof (keywords[0]); 70 71/* 72 * Convert a colon-separated list of known keywords to a set of MM_VERB* 73 * flags, defaulting to `all' if not set, empty, or in presence of unknown 74 * keywords. 75 */ 76static unsigned int 77msgverb(const char *str) 78{ 79 u_int i; 80 unsigned int result; 81 82 if (str == NULL) 83 return (MM_VERBALL); 84 85 result = 0; 86 while (*str != '\0') { 87 for (i = 0; i < nkeywords; i++) { 88 if (memcmp(str, keywords[i].keyword, keywords[i].len) 89 == 0 && 90 (*(str + keywords[i].len) == ':' || 91 *(str + keywords[i].len) == '\0')) 92 break; 93 } 94 if (i == nkeywords) { 95 result = MM_VERBALL; 96 break; 97 } 98 99 result |= (1 << i); 100 if (*(str += keywords[i].len) == ':') 101 str++; /* Advance */ 102 } 103 if (result == 0) 104 result = MM_VERBALL; 105 106 return (result); 107} 108 109static const char severities[][8] = { 110 "", /* MM_NONE */ 111 "HALT", 112 "ERROR", 113 "WARNING", 114 "INFO" 115}; 116 117static const size_t nseverities = sizeof (severities) / sizeof (severities[0]); 118 119/* 120 * Returns the string representation associated with the numerical severity 121 * value, defaulting to NULL for an unknown value. 122 */ 123static const char * 124severity2str(int severity) 125{ 126 const char *result; 127 128 if (severity >= 0 && 129 (u_int) severity < nseverities) 130 result = severities[severity]; 131 else 132 result = NULL; 133 134 return (result); 135} 136 137/* 138 * Format and write the message to the given stream, selecting those 139 * components displayed from msgverb, returning the number of characters 140 * written, or a negative value in case of an error. 141 */ 142static int 143writeit(FILE *stream, unsigned int which, const char *label, 144 const char *sevstr, const char *text, const char *action, 145 const char *tag) 146{ 147 int nwritten; 148 149 nwritten = fprintf(stream, "%s%s%s%s%s%s%s%s%s%s%s", 150 ((which & MM_VERBLABEL) && label != MM_NULLLBL) ? 151 label : "", 152 ((which & MM_VERBLABEL) && label != MM_NULLLBL) ? 153 ": " : "", 154 (which & MM_VERBSEVERITY) ? 155 sevstr : "", 156 (which & MM_VERBSEVERITY) ? 157 ": " : "", 158 ((which & MM_VERBTEXT) && text != MM_NULLTXT) ? 159 text : "", 160 ((which & MM_VERBLABEL) && label != MM_NULLLBL) || 161 ((which & MM_VERBSEVERITY)) || 162 ((which & MM_VERBTEXT) && text != MM_NULLTXT) ? 163 "\n" : "", 164 ((which & MM_VERBACTION) && action != MM_NULLACT) ? 165 "TO FIX: " : "", 166 ((which & MM_VERBACTION) && action != MM_NULLACT) ? 167 action : "", 168 ((which & MM_VERBACTION) && label != MM_NULLACT) ? 169 " " : "", 170 ((which & MM_VERBTAG) && tag != MM_NULLTAG) ? 171 tag : "", 172 ((which & MM_VERBACTION) && action != MM_NULLACT) || 173 ((which & MM_VERBTAG) && tag != MM_NULLTAG) ? 174 "\n" : ""); 175 176 return (nwritten); 177} 178 179int 180fmtmsg(long classification, const char *label, int severity, 181 const char *text, const char *action, const char *tag) 182{ 183 FILE *console; 184 const char *p, *sevstr; 185 int result; 186 187 /* Validate label constraints, if not null. */ 188 if (label != MM_NULLLBL) { 189 /* 190 * Two fields, separated by a colon. The first field is up to 191 * 10 bytes, the second is up to 14 bytes. 192 */ 193 p = strchr(label, ':'); 194 if (p == NULL || p - label > 10 || strlen(p + 1) > 14) 195 return (MM_NOTOK); 196 } 197 /* Validate severity argument. */ 198 if ((sevstr = severity2str(severity)) == NULL) 199 return (MM_NOTOK); 200 201 /* 202 * Fact in search for a better place: XSH5 does not define any 203 * functionality for `classification' bits other than the display 204 * subclassification. 205 */ 206 207 result = 0; 208 209 if (classification & MM_PRINT) { 210 if (writeit(stderr, msgverb(getenv("MSGVERB")), 211 label, sevstr, text, action, tag) < 0) 212 result |= MM_NOMSG; 213 } 214 /* Similar to MM_PRINT but ignoring $MSGVERB. */ 215 if (classification & MM_CONSOLE) { 216 if ((console = fopen(_PATH_CONSOLE, "we")) != NULL) { 217 if (writeit(console, MM_VERBALL, 218 label, sevstr, text, action, tag) < 0) 219 result |= MM_NOCON; 220 /* 221 * Ignore result: does not constitute ``generate a 222 * console message.'' 223 */ 224 (void)fclose(console); 225 } else { 226 result |= MM_NOCON; 227 } 228 } 229 230 if (result == (MM_NOMSG | MM_NOCON)) 231 result = MM_NOTOK; 232 233 return (result == 0 ? MM_OK : result); 234} 235