4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
31 #pragma ident "%Z%%M% %I% %E% SMI"
38 * fmtmsg Command that writes a message in the standard
39 * message format. May in future make these
40 * messages available for logging.
46 * <stdio.h> C Standard I/O function definitions
47 * <string.h> C string-handling definitions
48 * <errno.h> UNIX error-code "errno" definitions
49 * <fmtmsg.h> Standard Message definitions
59 * Externals referenced:
60 * strtol Function that converts char strings to "long"
61 * fmtmsg Function that writes a message in standard format
62 * getenv Function that extracts an environment variable's
64 * malloc Allocate memory from the memory pool
65 * free Frees allocated memory
66 * getopt Function that extracts arguments from the command-
67 * optarg Points to option's argument (from getopt())
68 * optind Option's argument index (from getopt())
69 * opterr FLAG, write error if invalid option (for getopt())
71 * exit Exits the command
76 extern char *getenv();
77 extern void *malloc();
96 * TRUE Boolean value for "true" (any bits on)
97 * FALSE Boolean value for "false" (all bits off)
109 #define CLASS (MM_PRINT|MM_SOFT|MM_NRECOV|MM_UTIL)
110 #define BIGUSAGE "fmtmsg [-a action] [-c class] [-l label] [-s severity] [-t tag]\n [-u subclass[,subclass[,...]]] [text]\n"
114 * Local data-type definitions
118 * Structure used for tables containing keywords and integer values
128 * Structure used for tables containing keywords, long values
139 * Severity string structure
142 * sevvalue Value of the severity-level being defined
143 * sevkywd Keyword identifying the severity
144 * sevprptr Pointer to the string associated with the value
145 * sevnext Pointer to the next value in the list.
152 struct sevstr
*sevnext
;
162 * Table contains the keywords for the classes of a message
165 static struct class_info classes
[] = {
167 {"hard", MM_HARD
, MM_SOFT
|MM_FIRM
}, /* hardware */
168 {"soft", MM_SOFT
, MM_HARD
|MM_FIRM
}, /* software */
169 {"firm", MM_FIRM
, MM_SOFT
|MM_FIRM
}, /* firmware */
171 {(char *) NULL
, 0L, 0L} /* end of list */
177 * Table contains the keywords for the subclasses for a message
180 static struct class_info subclasses
[] = {
182 {"appl", MM_APPL
, MM_UTIL
|MM_OPSYS
}, /* Application */
183 {"util", MM_UTIL
, MM_APPL
|MM_OPSYS
}, /* Utility */
184 {"opsys", MM_OPSYS
, MM_APPL
|MM_UTIL
}, /* Operating System */
186 {"recov", MM_RECOVER
, MM_NRECOV
}, /* Recoverable */
187 {"nrecov", MM_NRECOV
, MM_RECOVER
}, /* Non-recoverable */
189 {"print", MM_PRINT
, 0L}, /* Write message to stderr */
190 {"console", MM_CONSOLE
, 0L}, /* Write message on /dev/console */
191 {(char *) NULL
, 0L, 0L} /* End of list */
197 * Table contains the keywords for the standard severities of a message.
198 * User may supply more through the SEV_LEVEL environment variable.
201 static struct sev_info severities
[] = {
202 {"halt", MM_HALT
}, /* halt */
203 {"error", MM_ERROR
}, /* error */
204 {"warn", MM_WARNING
}, /* warn */
205 {"info", MM_INFO
}, /* info */
206 {(char *) NULL
, 0} /* end of list */
211 * Buffers used by the command
214 static char labelbuf
[128]; /* Buf for message label */
215 static char msgbuf
[256]; /* Buf for messages */
218 * static char *exttok(str, delims)
222 * This function examines the string pointed to by "str", looking
223 * for the first occurrence of any of the characters in the string
224 * whose address is "delims". It returns the address of that
225 * character or (char *) NULL if there was nothing to search.
228 * str Address of the string to search
229 * delims Address of the string containing delimiters
232 * Returns the address of the first occurrence of any of the characters
233 * in "delim" in the string "str" (incl '\0'). If there was nothing
234 * to search, the function returns (char *) NULL.
237 * - This function is needed because strtok() can't be used inside a
238 * function. Besides, strtok() is destructive in the string, which
239 * is undesirable in many circumstances.
240 * - This function understands escaped delimiters as non-delimiters.
241 * Delimiters are escaped by preceding them with '\' characters.
242 * The '\' character also must be escaped.
247 char *tok
; /* Ptr to the token we're parsing */
248 char *delims
; /* Ptr to string with delimiters */
252 char *tokend
; /* Ptr to the end of the token */
253 char *p
, *q
; /* Temp pointers */
257 * 1. Get the starting address (new string or where we
258 * left off). If nothing to search, return (char *) NULL
259 * 2. Find the end of the string
260 * 3. Look for the first unescaped delimiter closest to the
261 * beginning of the string
262 * 4. Remember where we left off
263 * 5. Return a pointer to the delimiter we found
266 /* Begin at the beginning, if any */
267 if (tok
== (char *) NULL
) {
268 return ((char *) NULL
);
271 /* Find end of the token string */
272 tokend
= tok
+ strlen(tok
);
274 /* Look for the 1st occurrence of any delimiter */
275 for (p
= delims
; *p
!= '\0' ; p
++) {
276 for (q
= strchr(tok
, *p
) ; q
&& (q
!= tok
) && (*(q
-1) == '\\') ; q
= strchr(q
+1, *p
)) ;
277 if (q
&& (q
< tokend
)) tokend
= q
;
287 * This function squeezes out all of the escaped character sequences
288 * from the string <str>. It returns a pointer to that string.
292 * The string that is to have its escaped characters removed.
295 * This function returns its argument <str> always.
298 * This function potentially modifies the string it is given.
303 char *str
; /* String to remove escaped characters from */
305 char *p
; /* Temp string pointer */
306 char *q
; /* Temp string pointer */
308 /* Look for an escaped character */
310 while (*p
&& (*p
!= '\\')) p
++;
314 * If there was at least one, squeeze them out
315 * Otherwise, don't touch the argument string
320 while (*q
++ = *p
++) if (*p
== '\\') p
++;
323 /* Finished. Return our argument */
328 * struct sevstr *getauxsevs(ptr)
330 * Parses a string that is in the format of the severity definitions.
331 * Returns a pointer to a (malloc'd) structure that contains the
332 * definition, or (struct sevstr *) NULL if none was parsed.
336 * References the string from which data is to be extracted.
337 * If (char *) NULL, continue where we left off. Otherwise,
338 * start with the string referenced by ptr.
340 * Returns: struct sevstr *
341 * A pointer to a malloc'd structure containing the severity definition
342 * parsed from string, or (struct sevstr *) NULL if none.
345 * - This function is destructive to the string referenced by its argument.
350 static char *leftoff
= (char *) NULL
;
352 static struct sevstr
*
358 char *current
; /* Ptr to current sev def'n */
359 char *tokend
; /* Ptr to end of current sev def'n */
360 char *kywd
; /* Ptr to extracted kywd */
361 char *valstr
; /* Ptr to extracted sev value */
362 char *prstr
; /* Ptr to extracted print str */
363 char *p
; /* Temp pointer */
364 int val
; /* Converted severity value */
365 int done
; /* Flag, sev def'n found and ok? */
366 struct sevstr
*rtnval
; /* Value to return */
369 /* Start anew or start where we left off? */
370 current
= (ptr
== (char *) NULL
) ? leftoff
: ptr
;
373 /* If nothing to parse, return (char *) NULL */
374 if (current
== (char *) NULL
) {
375 return ((struct sevstr
*) NULL
);
380 * Look through the string "current" for a token of the form
381 * <kywd>,<sev>,<printstring> delimited by ':' or '\0'
384 /* Loop initializations */
386 rtnval
= (struct sevstr
*) NULL
;
389 /* Eat leading junk */
390 while (*(tokend
= exttok(current
, ":,")) == ':') {
391 current
= tokend
+ 1;
394 /* If we've found a <kywd>,... */
395 if (*tokend
== ',') {
399 /* Look for <kywd>,<sev>,... */
400 current
= tokend
+ 1;
401 if (*(tokend
= exttok(current
, ":,")) == ',') {
407 /* Make sure <sev> > 4 */
408 val
= (int) strtol(noesc(valstr
), &p
, 0);
409 if ((val
> 4) && (p
== tokend
)) {
412 * Found <kywd>,<sev>,<printstring>.
413 * remember where we left off
416 if (*(tokend
= exttok(current
, ":")) == ':') {
418 leftoff
= tokend
+ 1;
419 } else leftoff
= (char *) NULL
;
421 /* Alloc structure to contain severity definition */
422 if (rtnval
= (struct sevstr
*) malloc(sizeof(struct sevstr
))) {
424 /* Fill in structure */
425 rtnval
->sevkywd
= noesc(kywd
);
426 rtnval
->sevvalue
= val
;
427 rtnval
->sevprstr
= noesc(prstr
);
428 rtnval
->sevnext
= (struct sevstr
*) NULL
;
435 /* Invalid severity value, eat thru end of token */
437 if (*(tokend
= exttok(prstr
, ":")) == ':')
443 /* Invalid severity definition, eat thru end of token */
451 /* End of string found */
453 leftoff
= (char *) NULL
;
456 } /* while (!done) */
463 * fmtmsg [-a action] [-c classification] [-l label] [-s severity] [-t tag]
464 * [-u subclass[,subclass[,...]]] [text]
467 * Writes a message in the standard format. Typically used by shell
468 * scripts to write error messages to the user.
471 * text String that is the text of the message
474 * -a action String that describes user action to take to
475 * correct the situation
476 * -c classification Keyword that identifies the type of the message
477 * -l label String that identifies the source of the message
478 * -s severity Keyword that identifies the severity of the message
479 * -t tag String that identifies the message (use unclear)
480 * -u sub_classes Comma-list of keywords that refines the type of
483 * Environment Variables Used:
484 * MSGVERB Defines the pieces of a message the user expects
485 * to see. It is a list of keywords separated by
487 * SEV_LEVEL Defines a list of auxiliary severity keywords, values,
488 * and print-strings. It is a list of fields separated
489 * by colons (':'). Each field consists of three
490 * elements, keyword, value (in octal, hex, or decimal),
491 * and print-string, separated by commas (',').
499 main(int argc
, char **argv
)
502 /* Local automatic data */
504 long class; /* Classification (built) */
506 int severity
; /* User specified severity */
507 int msgrtn
; /* Value returned by fmtmsg() */
508 int optchar
; /* Opt char on cmdline */
509 int exitval
; /* Value to return */
511 int found
; /* FLAG, kywd found yet? */
512 int errflg
; /* FLAG, error seen in cmd */
513 int a_seen
; /* FLAG, -a option seen */
514 int c_seen
; /* FLAG, -c option seen */
515 int l_seen
; /* FLAG, -l option seen */
516 int s_seen
; /* FLAG, -s option seen */
517 int t_seen
; /* FLAG, -t option seen */
518 int u_seen
; /* FLAG, -u option seen */
519 int text_seen
; /* FLAG, text seen */
521 char *text
; /* Ptr to user's text */
522 char *label
; /* Ptr to user's label */
523 char *tag
; /* Ptr to user's tag */
524 char *action
; /* Ptr to user's action str */
525 char *sstr
; /* Ptr to -s (severity) arg */
526 char *ustr
; /* Ptr to -u (subclass) arg */
527 char *cstr
; /* Ptr to -c (class) arg */
528 char *sevstrval
; /* Ptr to SEV_LEVEL argument */
529 char *sevval
; /* Ptr to temp SEV_LEVEL arg */
530 char *tokenptr
; /* Ptr to current token */
531 char *cmdname
; /* Ptr to base command name */
532 char *p
; /* Multipurpose ptr */
534 struct class_info
*class_info
; /* Ptr to class/subclass info structure */
535 struct sev_info
*sev_info
; /* Ptr to severity info struct */
536 struct sevstr
*penvsev
; /* Ptr to SEV_LEVEL values */
545 /* Initializations */
548 /* Extract the base command name from the command */
549 if ((p
= strrchr(argv
[0], '/')) == (char *) NULL
)
554 /* Build the label for messages from "fmtmsg" */
555 (void) snprintf(labelbuf
, sizeof (labelbuf
), "UX:%s", cmdname
);
559 * Extract arguments from the command line
562 /* Initializations */
564 opterr
= 0; /* Disable messages from getopt() */
565 errflg
= FALSE
; /* No errors seen yet */
567 a_seen
= FALSE
; /* No action (-a) text seen yet */
568 c_seen
= FALSE
; /* No classification (-c) seen yet */
569 l_seen
= FALSE
; /* No label (-l) seen yet */
570 s_seen
= FALSE
; /* No severity (-s) seen yet */
571 t_seen
= FALSE
; /* No tag (-t) seen yet */
572 u_seen
= FALSE
; /* No subclass (-u) seen yet */
573 text_seen
= FALSE
; /* No text seen yet */
577 * If only the command name was used, write out a usage string to
578 * the standard output file.
582 (void) fputs(BIGUSAGE
, stderr
);
587 /* Parce command line */
588 while (((optchar
= getopt(argc
, argv
, "a:c:l:s:t:u:")) != EOF
) &&
593 case 'a': /* -a actiontext */
597 } else errflg
= TRUE
;
600 case 'c': /* -c classification */
604 } else errflg
= TRUE
;
607 case 'l': /* -l label */
611 } else errflg
= TRUE
;
614 case 's': /* -s severity */
618 } else errflg
= TRUE
;
621 case 't': /* -t tag */
625 } else errflg
= TRUE
;
628 case 'u': /* -u subclasslist */
632 } else errflg
= TRUE
;
635 case '?': /* -? or unknown option */
646 if (argc
== (optind
+1)) {
650 else if (argc
!= optind
) {
656 /* Report syntax errors */
658 (void) fputs(BIGUSAGE
, stderr
);
670 /* Search for keyword in list */
671 for (class_info
= &classes
[0] ;
672 (class_info
->keyword
!= (char *) NULL
) &&
673 (strcmp(cstr
, class_info
->keyword
)) ;
676 /* If invalid (keyword unknown), write a message and exit */
677 if (class_info
->keyword
== (char *) NULL
) {
678 (void) snprintf(msgbuf
, sizeof (msgbuf
),
679 "Invalid class: %s", cstr
);
680 (void) fmtmsg(CLASS
, labelbuf
, MM_ERROR
, msgbuf
,
681 MM_NULLACT
, MM_NULLTAG
);
685 /* Save classification */
686 class = class_info
->value
;
698 p
= strcpy(malloc((unsigned int) strlen(ustr
)+1), ustr
);
699 if ((tokenptr
= strtok(p
, ",")) == (char *) NULL
) errflg
= TRUE
;
702 /* Got a keyword. Look for it in keyword list */
703 for (class_info
= subclasses
;
704 (class_info
->keyword
!= (char *) NULL
) &&
705 (strcmp(tokenptr
, class_info
->keyword
) != 0) ;
708 /* If found in list and no conflict, remember in class */
709 if ((class_info
->keyword
!= (char *) NULL
) && ((class & class_info
->conflict
) == 0L))
710 class |= class_info
->value
;
714 } while (!errflg
&& ((tokenptr
= strtok((char *) NULL
, ",")) != (char *) NULL
)) ;
717 (void) snprintf(msgbuf
, sizeof (msgbuf
),
718 "Invalid subclass: %s", ustr
);
719 (void) fmtmsg(CLASS
, labelbuf
, MM_ERROR
, msgbuf
,
720 MM_NULLACT
, MM_NULLTAG
);
726 if (!c_seen
& !u_seen
) class = MM_NULLMC
;
736 /* If the severity is specified as a number, use that value */
737 severity
= strtol(sstr
, &p
, 10);
738 if (*p
|| (strlen(sstr
) == 0)) {
740 /* Look for the standard severities */
741 for (sev_info
= severities
;
742 (sev_info
->keyword
!= (char *) NULL
) &&
743 (strcmp(sstr
, sev_info
->keyword
)) ;
747 * If the "severity" argument is one of the standard keywords,
748 * remember it for fmtmsg(). Otherwise, look at the SEV_LEVEL
749 * environment variable for severity extensions.
752 /* If the keyword is one of the standard ones, save severity */
753 if (sev_info
->keyword
!= (char *) NULL
) severity
= sev_info
->value
;
758 * Severity keyword may be one of the extended set, if any.
761 /* Get the value of the SEV_LEVEL environment variable */
763 if ((sevstrval
= getenv(SEV_LEVEL
)) != (char *) NULL
) {
764 sevval
= (char *) malloc((unsigned int) strlen(sevstrval
)+1);
765 penvsev
= getauxsevs(strcpy(sevval
, sevstrval
));
766 if (penvsev
!= (struct sevstr
*) NULL
) do {
767 if (strcmp(penvsev
->sevkywd
, sstr
) == 0) {
768 severity
= penvsev
->sevvalue
;
773 penvsev
= getauxsevs((char *) NULL
);
775 } while (!found
&& (penvsev
!= (struct sevstr
*) NULL
));
777 if (found
) free(penvsev
);
782 (void) snprintf(msgbuf
, sizeof (msgbuf
),
783 "Invalid severity: %s", sstr
);
784 (void) fmtmsg(CLASS
, labelbuf
, MM_ERROR
, msgbuf
,
785 MM_NULLACT
, MM_NULLTAG
);
789 } /* <severity> is not one of the standard severities */
791 } /* <severity> is not numeric */
795 else severity
= MM_NULLSEV
;
802 if (!a_seen
) action
= MM_NULLACT
;
803 if (!l_seen
) label
= MM_NULLLBL
;
804 if (!t_seen
) tag
= MM_NULLTAG
;
805 if (!text_seen
) text
= MM_NULLTXT
;
812 msgrtn
= fmtmsg(class, label
, severity
, text
, action
,tag
);
816 * Return appropriate value to the shell (or wherever)
820 if (msgrtn
== MM_NOTOK
) exitval
= 32;
822 if (msgrtn
& MM_NOMSG
) exitval
+= 2;
823 if (msgrtn
& MM_NOCON
) exitval
+= 4;