1 /* $NetBSD: fmtmsg.c,v 1.4 2008/04/28 20:22:59 martin 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.4 2008/04/28 20:22:59 martin 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 43 static unsigned int msgverb __P((const char *)); 44 static const char * severity2str __P((int)); 45 static int writeit __P((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 58 static 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 69 static 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 */ 76 static unsigned int 77 msgverb(str) 78 const char *str; 79 { 80 u_int i; 81 unsigned int result; 82 83 if (str == NULL) 84 return (MM_VERBALL); 85 86 result = 0; 87 while (*str != '\0') { 88 for (i = 0; i < nkeywords; i++) { 89 if (memcmp(str, keywords[i].keyword, keywords[i].len) 90 == 0 && 91 (*(str + keywords[i].len) == ':' || 92 *(str + keywords[i].len) == '\0')) 93 break; 94 } 95 if (i == nkeywords) { 96 result = MM_VERBALL; 97 break; 98 } 99 100 result |= (1 << i); 101 if (*(str += keywords[i].len) == ':') 102 str++; /* Advance */ 103 } 104 if (result == 0) 105 result = MM_VERBALL; 106 107 return (result); 108 } 109 110 static const char * const severities[] = { 111 "", /* MM_NONE */ 112 "HALT", 113 "ERROR", 114 "WARNING", 115 "INFO" 116 }; 117 118 static const size_t nseverities = sizeof (severities) / sizeof (severities[0]); 119 120 /* 121 * Returns the string representation associated with the numerical severity 122 * value, defaulting to NULL for an unknown value. 123 */ 124 static const char * 125 severity2str(severity) 126 int severity; 127 { 128 const char *result; 129 130 if (severity >= 0 && 131 (u_int) severity < nseverities) 132 result = severities[severity]; 133 else 134 result = NULL; 135 136 return (result); 137 } 138 139 /* 140 * Format and write the message to the given stream, selecting those 141 * components displayed from msgverb, returning the number of characters 142 * written, or a negative value in case of an error. 143 */ 144 static int 145 writeit(stream, which, label, sevstr, text, action, tag) 146 FILE *stream; 147 unsigned int which; 148 const char *label; 149 const char *sevstr; 150 const char *text; 151 const char *action; 152 const char *tag; 153 { 154 int nwritten; 155 156 nwritten = fprintf(stream, "%s%s%s%s%s%s%s%s%s%s%s", 157 ((which & MM_VERBLABEL) && label != MM_NULLLBL) ? 158 label : "", 159 ((which & MM_VERBLABEL) && label != MM_NULLLBL) ? 160 ": " : "", 161 (which & MM_VERBSEVERITY) ? 162 sevstr : "", 163 (which & MM_VERBSEVERITY) ? 164 ": " : "", 165 ((which & MM_VERBTEXT) && text != MM_NULLTXT) ? 166 text : "", 167 ((which & MM_VERBLABEL) && label != MM_NULLLBL) || 168 ((which & MM_VERBSEVERITY)) || 169 ((which & MM_VERBTEXT) && text != MM_NULLTXT) ? 170 "\n" : "", 171 ((which & MM_VERBACTION) && action != MM_NULLACT) ? 172 "TO FIX: " : "", 173 ((which & MM_VERBACTION) && action != MM_NULLACT) ? 174 action : "", 175 ((which & MM_VERBACTION) && label != MM_NULLACT) ? 176 " " : "", 177 ((which & MM_VERBTAG) && tag != MM_NULLTAG) ? 178 tag : "", 179 ((which & MM_VERBACTION) && action != MM_NULLACT) || 180 ((which & MM_VERBTAG) && tag != MM_NULLTAG) ? 181 "\n" : ""); 182 183 return (nwritten); 184 } 185 186 int 187 fmtmsg(classification, label, severity, text, action, tag) 188 long classification; 189 const char *label; 190 int severity; 191 const char *text; 192 const char *action; 193 const char *tag; 194 { 195 FILE *console; 196 const char *p, *sevstr; 197 int result; 198 199 /* Validate label constraints, if not null. */ 200 if (label != MM_NULLLBL) { 201 /* 202 * Two fields, separated by a colon. The first field is up to 203 * 10 bytes, the second is up to 14 bytes. 204 */ 205 p = strchr(label, ':'); 206 if (p == NULL || p - label > 10 || strlen(p + 1) > 14) 207 return (MM_NOTOK); 208 } 209 /* Validate severity argument. */ 210 if ((sevstr = severity2str(severity)) == NULL) 211 return (MM_NOTOK); 212 213 /* 214 * Fact in search for a better place: XSH5 does not define any 215 * functionality for `classification' bits other than the display 216 * subclassification. 217 */ 218 219 result = 0; 220 221 if (classification & MM_PRINT) { 222 if (writeit(stderr, msgverb(getenv("MSGVERB")), 223 label, sevstr, text, action, tag) < 0) 224 result |= MM_NOMSG; 225 } 226 /* Similar to MM_PRINT but ignoring $MSGVERB. */ 227 if (classification & MM_CONSOLE) { 228 if ((console = fopen(_PATH_CONSOLE, "w")) != NULL) { 229 if (writeit(console, MM_VERBALL, 230 label, sevstr, text, action, tag) < 0) 231 result |= MM_NOCON; 232 /* 233 * Ignore result: does not constitute ``generate a 234 * console message.'' 235 */ 236 (void)fclose(console); 237 } else { 238 result |= MM_NOCON; 239 } 240 } 241 242 if (result == (MM_NOMSG | MM_NOCON)) 243 result = MM_NOTOK; 244 245 return (result == 0 ? MM_OK : result); 246 } 247