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