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 40 static char *printfmt(char *, long, const char *, int, const char *, 41 const char *, const char *); 42 static char *nextcomp(const char *); 43 static const char 44 *sevinfo(int); 45 static int validmsgverb(const char *); 46 47 int 48 fmtmsg(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 { 64 def: 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 */ 111 static char * 112 printfmt(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 */ 162 static char * 163 nextcomp(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 179 static const char * 180 sevinfo(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 */ 200 static int 201 validmsgverb(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