xref: /titanic_44/usr/src/cmd/fmtmsg/main.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
1 /*
2  * CDDL HEADER START
3  *
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
7  * with the License.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 
34 /*
35  * fmtmsg.c
36  *
37  * Contains:
38  *	fmtmsg		Command that writes a message in the standard
39  *			message format.  May in future make these
40  *			messages available for logging.
41  */
42 
43 
44 /*
45  * Header files used:
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
50  */
51 
52 #include	<stdio.h>
53 #include	<string.h>
54 #include	<errno.h>
55 #include	<fmtmsg.h>
56 
57 
58 /*
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
63  *			value
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())
70  *			line.
71  *	exit		Exits the command
72  */
73 
74 extern	long		strtol();
75 extern	int		fmtmsg();
76 extern	char	       *getenv();
77 extern	void	       *malloc();
78 extern	void		free();
79 extern	int		getopt();
80 extern	char	       *optarg;
81 extern	int		optind;
82 extern	int		opterr;
83 extern	void		exit();
84 
85 /*
86  * Local definitions
87  */
88 
89 /*
90  * Local constants
91  */
92 
93 
94 /*
95  * Boolean constants
96  *	TRUE	Boolean value for "true" (any bits on)
97  *	FALSE	Boolean value for "false" (all bits off)
98  */
99 
100 #ifndef	FALSE
101 #define	FALSE		(0)
102 #endif
103 
104 #ifndef TRUE
105 #define	TRUE		(1)
106 #endif
107 
108 
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"
111 
112 
113 /*
114  * Local data-type definitions
115  */
116 
117 /*
118  * Structure used for tables containing keywords and integer values
119  */
120 
121 struct sev_info {
122 	char   *keyword;
123 	int	value;
124 };
125 
126 
127 /*
128  * Structure used for tables containing keywords, long values
129  */
130 
131 struct class_info {
132 	char   *keyword;
133 	long	value;
134 	long	conflict;
135 };
136 
137 
138 /*
139  * Severity string structure
140  *
141  *	struct sevstr
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.
146  */
147 
148 struct sevstr {
149 	int		sevvalue;
150 	char           *sevkywd;
151 	char	       *sevprstr;
152 	struct sevstr  *sevnext;
153 };
154 
155 
156 /*
157  * Local static data
158  */
159 
160 
161 /*
162  * Table contains the keywords for the classes of a message
163  */
164 
165 static	struct class_info	classes[] = {
166 
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 */
170 
171 	{(char *) NULL,	0L,		0L}			/* end of list */
172 
173 };
174 
175 
176 /*
177  * Table contains the keywords for the subclasses for a message
178  */
179 
180 static	struct class_info	subclasses[] = 	{
181 
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 */
185 
186 	{"recov",    	MM_RECOVER,	MM_NRECOV},		/* Recoverable */
187 	{"nrecov",   	MM_NRECOV,	MM_RECOVER},		/* Non-recoverable */
188 
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 */
192 
193 };
194 
195 
196 /*
197  * Table contains the keywords for the standard severities of a message.
198  * User may supply more through the SEV_LEVEL environment variable.
199  */
200 
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 */
207 };
208 
209 
210 /*
211  * Buffers used by the command
212  */
213 
214 static	char	labelbuf[128];		/* Buf for message label */
215 static	char	msgbuf[256];		/* Buf for messages */
216 
217 /*
218  * static char *exttok(str, delims)
219  *	char   *str
220  *	char   *delims
221  *
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.
226  *
227  * Arguments:
228  *	str	Address of the string to search
229  *	delims	Address of the string containing delimiters
230  *
231  * Returns:  char *
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.
235  *
236  * Notes:
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.
243  */
244 
245 static char *
246 exttok(tok, delims)
247 	char   *tok;		/* Ptr to the token we're parsing */
248 	char   *delims;		/* Ptr to string with delimiters */
249 {
250 
251 	/* Automatic Data */
252 	char   *tokend;		/* Ptr to the end of the token */
253 	char   *p, *q;	 	/* Temp pointers */
254 
255 
256 	/* Algorithm:
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
264 	 */
265 
266 	/* Begin at the beginning, if any */
267 	if (tok == (char *) NULL) {
268 	    return ((char *) NULL);
269 	}
270 
271 	/* Find end of the token string */
272 	tokend = tok + strlen(tok);
273 
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;
278 	}
279 
280 	/* Done */
281 	return(tokend);
282 }
283 
284 /*
285  * char *noesc(str)
286  *
287  *	This function squeezes out all of the escaped character sequences
288  *	from the string <str>.  It returns a pointer to that string.
289  *
290  *  Arguments:
291  *	str	char *
292  *		The string that is to have its escaped characters removed.
293  *
294  *  Returns:  char *
295  *	This function returns its argument <str> always.
296  *
297  *  Notes:
298  *	This function potentially modifies the string it is given.
299  */
300 
301 char *
302 noesc(str)
303 	char   *str;		/* String to remove escaped characters from */
304 {
305 	char   *p;		/* Temp string pointer */
306 	char   *q;		/* Temp string pointer */
307 
308 	/* Look for an escaped character */
309 	p = str;
310 	while (*p && (*p != '\\')) p++;
311 
312 
313 	/*
314 	 * If there was at least one, squeeze them out
315 	 * Otherwise, don't touch the argument string
316 	 */
317 
318 	if (*p) {
319 	    q = p++;
320 	    while (*q++ = *p++) if (*p == '\\') p++;
321 	}
322 
323 	/* Finished.  Return our argument */
324 	return(str);
325 }
326 
327 /*
328  * struct sevstr *getauxsevs(ptr)
329  *
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.
333  *
334  * Arguments:
335  *	ptr	char *
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.
339  *
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.
343  *
344  * Notes:
345  *    - This function is destructive to the string referenced by its argument.
346  */
347 
348 
349 /* Static data */
350 static	char	       *leftoff = (char *) NULL;
351 
352 static	struct sevstr *
353 getauxsevs(ptr)
354 	char   *ptr;
355 {
356 
357 	/* Automatic data */
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 */
367 
368 
369 	/* Start anew or start where we left off? */
370 	current = (ptr == (char *) NULL) ? leftoff : ptr;
371 
372 
373 	/* If nothing to parse, return (char *) NULL */
374 	if (current == (char *) NULL) {
375 	    return ((struct sevstr *) NULL);
376 	}
377 
378 
379 	/*
380 	 * Look through the string "current" for a token of the form
381 	 * <kywd>,<sev>,<printstring> delimited by ':' or '\0'
382 	 */
383 
384 	/* Loop initializations */
385 	done = FALSE;
386 	rtnval = (struct sevstr *) NULL;
387 	while (!done) {
388 
389 	    /* Eat leading junk */
390 	    while (*(tokend = exttok(current, ":,")) == ':') {
391 		current = tokend + 1;
392 	    }
393 
394 	    /* If we've found a <kywd>,... */
395 	    if (*tokend == ',') {
396 		kywd = current;
397 		*tokend = '\0';
398 
399 		/* Look for <kywd>,<sev>,... */
400 		current = tokend + 1;
401 		if (*(tokend = exttok(current, ":,")) == ',') {
402 		    valstr = current;
403 		    *tokend = '\0';
404 		    current = tokend+1;
405 		    prstr = current;
406 
407 		    /* Make sure <sev> > 4 */
408 		    val = (int) strtol(noesc(valstr), &p, 0);
409 		    if ((val > 4) && (p == tokend)) {
410 
411 			/*
412 			 * Found <kywd>,<sev>,<printstring>.
413 			 * remember where we left off
414 			 */
415 
416 		        if (*(tokend = exttok(current, ":")) == ':') {
417 			    *tokend = '\0';
418 			    leftoff = tokend + 1;
419 			} else leftoff = (char *) NULL;
420 
421 			/* Alloc structure to contain severity definition */
422 			if (rtnval = (struct sevstr *) malloc(sizeof(struct sevstr))) {
423 
424 			    /* Fill in structure */
425 			    rtnval->sevkywd = noesc(kywd);
426 			    rtnval->sevvalue = val;
427 			    rtnval->sevprstr = noesc(prstr);
428 			    rtnval->sevnext = (struct sevstr *) NULL;
429 			}
430 
431 			done = TRUE;
432 
433 		    } else {
434 
435 			/* Invalid severity value, eat thru end of token */
436 			current = tokend;
437 			if (*(tokend = exttok(prstr, ":")) == ':')
438 			    current++;
439 		    }
440 
441 		} else {
442 
443 		    /* Invalid severity definition, eat thru end of token */
444 		    current = tokend;
445 		    if (*tokend == ':')
446 			current++;
447 		}
448 
449 	    } else {
450 
451 		/* End of string found */
452 		done = TRUE;
453 		leftoff = (char *) NULL;
454 	    }
455 
456 	} /* while (!done) */
457 
458 	/* Finished */
459 	return(rtnval);
460 }
461 
462 /*
463  * fmtmsg [-a action] [-c classification] [-l label] [-s severity] [-t tag]
464  *        [-u subclass[,subclass[,...]]] [text]
465  *
466  * Function:
467  *	Writes a message in the standard format.  Typically used by shell
468  *	scripts to write error messages to the user.
469  *
470  * Arguments:
471  *	text		String that is the text of the message
472  *
473  * Options:
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
481  *			the message
482  *
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
486  *			colons (':').
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 (',').
492  *
493  * Needs:
494  *
495  * Open Issues:
496  */
497 
498 main(argc, argv)
499 	int	argc;			/* Argument count */
500 	char   *argv[];			/* Pointers to arguments */
501 {
502 
503 	/* Local automatic data */
504 
505 	long			class;		/* Classification (built) */
506 
507 	int			severity;	/* User specified severity */
508 	int			msgrtn;		/* Value returned by fmtmsg() */
509 	int			optchar;	/* Opt char on cmdline */
510 	int			exitval;	/* Value to return */
511 
512 	int			found;		/* FLAG, kywd found yet? */
513 	int			errflg;		/* FLAG, error seen in cmd */
514 	int			a_seen;		/* FLAG, -a option seen */
515 	int			c_seen;		/* FLAG, -c option seen */
516 	int			l_seen;		/* FLAG, -l option seen */
517 	int			s_seen;		/* FLAG, -s option seen */
518 	int			t_seen;		/* FLAG, -t option seen */
519 	int			u_seen;		/* FLAG, -u option seen */
520 	int			text_seen;	/* FLAG, text seen */
521 
522 	char		       *text;		/* Ptr to user's text */
523 	char		       *label;		/* Ptr to user's label */
524 	char		       *tag;		/* Ptr to user's tag */
525 	char		       *action;		/* Ptr to user's action str */
526 	char		       *sstr;		/* Ptr to -s (severity) arg */
527 	char		       *ustr;		/* Ptr to -u (subclass) arg */
528 	char		       *cstr;		/* Ptr to -c (class) arg */
529 	char		       *sevstrval;	/* Ptr to SEV_LEVEL argument */
530 	char		       *sevval;		/* Ptr to temp SEV_LEVEL arg */
531 	char		       *tokenptr;	/* Ptr to current token */
532 	char		       *cmdname;	/* Ptr to base command name */
533 	char		       *p;		/* Multipurpose ptr */
534 
535 	struct class_info      *class_info;	/* Ptr to class/subclass info structure */
536 	struct sev_info	       *sev_info;	/* Ptr to severity info struct */
537 	struct sevstr	       *penvsev;	/* Ptr to SEV_LEVEL values */
538 
539 
540 
541 	/*
542 	 * fmtmsg
543 	 */
544 
545 
546 	/* Initializations */
547 
548 
549 	/* Extract the base command name from the command */
550 	if ((p = strrchr(argv[0], '/')) == (char *) NULL)
551 	    cmdname = argv[0];
552 	else
553 	    cmdname = p+1;
554 
555 	/* Build the label for messages from "fmtmsg" */
556 	(void) snprintf(labelbuf, sizeof (labelbuf), "UX:%s", cmdname);
557 
558 
559 	/*
560 	 * Extract arguments from the command line
561 	 */
562 
563 	/* Initializations */
564 
565 	opterr = 0;			/* Disable messages from getopt() */
566 	errflg = FALSE;			/* No errors seen yet */
567 
568 	a_seen = FALSE;			/* No action (-a) text seen yet */
569 	c_seen = FALSE;			/* No classification (-c) seen yet */
570 	l_seen = FALSE;			/* No label (-l) seen yet */
571 	s_seen = FALSE;			/* No severity (-s) seen yet */
572 	t_seen = FALSE;			/* No tag (-t) seen yet */
573 	u_seen = FALSE;			/* No subclass (-u) seen yet */
574 	text_seen = FALSE;		/* No text seen yet */
575 
576 
577 	/*
578 	 * If only the command name was used, write out a usage string to
579 	 * the standard output file.
580 	 */
581 
582 	if (argc == 1) {
583 	    (void) fputs(BIGUSAGE, stderr);
584 	    exit(0);
585 	}
586 
587 
588 	/* Parce command line */
589 	while (((optchar = getopt(argc, argv, "a:c:l:s:t:u:")) != EOF) &&
590 	       !errflg) {
591 
592 	    switch(optchar) {
593 
594 	    case 'a':		/* -a actiontext */
595 		if (!a_seen) {
596 		    action = optarg;
597 		    a_seen = TRUE;
598 		} else errflg = TRUE;
599 		break;
600 
601 	    case 'c':		/* -c classification */
602 		if (!c_seen) {
603 		    cstr = optarg;
604 		    c_seen = TRUE;
605 		} else errflg = TRUE;
606 		break;
607 
608 	    case 'l':		/* -l label */
609 		if (!l_seen) {
610 		    label = optarg;
611 		    l_seen = TRUE;
612 		} else errflg = TRUE;
613 		break;
614 
615 	    case 's':		/* -s severity */
616 		if (!s_seen) {
617 		    sstr = optarg;
618 		    s_seen = TRUE;
619 		} else errflg = TRUE;
620 		break;
621 
622 	    case 't':		/* -t tag */
623 		if (!t_seen) {
624 		    tag = optarg;
625 		    t_seen = TRUE;
626 		} else errflg = TRUE;
627 		break;
628 
629 	    case 'u':		/* -u subclasslist */
630 		if (!u_seen) {
631 		    ustr = optarg;
632 		    u_seen = TRUE;
633 		} else errflg = TRUE;
634 		break;
635 
636 	    case '?':		/* -? or unknown option */
637 	    default:
638 		errflg = TRUE;
639 		break;
640 
641 	    } /* esac */
642 	}
643 
644 
645 	/* Get the text */
646 	if (!errflg) {
647 	    if (argc == (optind+1)) {
648 		text = argv[optind];
649 		text_seen = TRUE;
650 	    }
651 	    else if (argc != optind) {
652 		errflg = TRUE;
653 	    }
654 	}
655 
656 
657 	/* Report syntax errors */
658 	if (errflg) {
659 	    (void) fputs(BIGUSAGE, stderr);
660 	    exit(1);
661 	}
662 
663 
664 	/*
665 	 * Classification.
666 	 */
667 
668 	class = 0L;
669 	if (c_seen) {
670 
671 	    /* Search for keyword in list */
672 	    for (class_info = &classes[0] ;
673 		 (class_info->keyword != (char *) NULL) &&
674 		 (strcmp(cstr, class_info->keyword)) ;
675 		 class_info++) ;
676 
677 	    /* If invalid (keyword unknown), write a message and exit */
678 	    if (class_info->keyword == (char *) NULL) {
679 		(void) snprintf(msgbuf, sizeof (msgbuf),
680 			"Invalid class: %s", cstr);
681 		(void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf,
682 		              MM_NULLACT, MM_NULLTAG);
683 		exit(1);
684 	    }
685 
686 	    /* Save classification */
687 	    class = class_info->value;
688 
689 	}
690 
691 
692 	/*
693 	 * Subclassification.
694 	 */
695 
696 	if (u_seen) {
697 
698 	    errflg = FALSE;
699 	    p = strcpy(malloc((unsigned int) strlen(ustr)+1), ustr);
700 	    if ((tokenptr = strtok(p, ",")) == (char *) NULL) errflg = TRUE;
701 	    else do {
702 
703 		/* Got a keyword.  Look for it in keyword list */
704 		for (class_info = subclasses ;
705 		     (class_info->keyword != (char *) NULL) &&
706 		     (strcmp(tokenptr, class_info->keyword) != 0) ;
707 		     class_info++) ;
708 
709 		/* If found in list and no conflict, remember in class */
710 		if ((class_info->keyword != (char *) NULL) && ((class & class_info->conflict) == 0L))
711 		    class |= class_info->value;
712 		else
713 		    errflg = TRUE;
714 
715 	    } while (!errflg && ((tokenptr = strtok((char *) NULL, ",")) != (char *) NULL)) ;
716 
717 	    if (errflg) {
718 		(void) snprintf(msgbuf, sizeof (msgbuf),
719 			"Invalid subclass: %s", ustr);
720 		(void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf,
721 		              MM_NULLACT, MM_NULLTAG);
722 		exit(1);
723 	    }
724 
725 	}
726 
727 	if (!c_seen & !u_seen) class = MM_NULLMC;
728 
729 
730 
731 	/*
732 	 * Severity.
733 	 */
734 
735 	if (s_seen) {
736 
737 	    /* If the severity is specified as a number, use that value */
738 	    severity = strtol(sstr, &p, 10);
739 	    if (*p || (strlen(sstr) == 0)) {
740 
741 		/* Look for the standard severities */
742 		for (sev_info = severities ;
743 		     (sev_info->keyword != (char *) NULL) &&
744 		     (strcmp(sstr, sev_info->keyword)) ;
745 		     sev_info++) ;
746 
747 		/*
748 		 * If the "severity" argument is one of the standard keywords,
749 		 * remember it for fmtmsg().  Otherwise, look at the SEV_LEVEL
750 		 * environment variable for severity extensions.
751 		 */
752 
753 		/* If the keyword is one of the standard ones, save severity */
754 		if (sev_info->keyword != (char *) NULL) severity = sev_info->value;
755 
756 		else {
757 
758 		    /*
759 		     * Severity keyword may be one of the extended set, if any.
760 		     */
761 
762 		    /* Get the value of the SEV_LEVEL environment variable */
763 		    found = FALSE;
764 		    if ((sevstrval = getenv(SEV_LEVEL)) != (char *) NULL) {
765 			sevval = (char *) malloc((unsigned int) strlen(sevstrval)+1);
766 			penvsev = getauxsevs(strcpy(sevval, sevstrval));
767 			if (penvsev != (struct sevstr *) NULL) do {
768 			    if (strcmp(penvsev->sevkywd, sstr) == 0) {
769 				severity = penvsev->sevvalue;
770 				found = TRUE;
771 			    }
772 			    else {
773 				free(penvsev);
774 				penvsev = getauxsevs((char *) NULL);
775 			    }
776 			} while (!found && (penvsev != (struct sevstr *) NULL));
777 
778 			if (found) free(penvsev);
779 			free(sevval);
780 		    }
781 
782 		    if (!found) {
783 			(void) snprintf(msgbuf, sizeof (msgbuf),
784 				"Invalid severity: %s", sstr);
785 			(void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf,
786 				      MM_NULLACT, MM_NULLTAG);
787 			exit(1);
788 		    }
789 
790 		}  /* <severity> is not one of the standard severities */
791 
792 	    }  /* <severity> is not numeric */
793 
794 	}  /* if (s_seen) */
795 
796 	else severity = MM_NULLSEV;
797 
798 
799 	/*
800 	 * Other options
801 	 */
802 
803 	if (!a_seen) action = MM_NULLACT;
804 	if (!l_seen) label = MM_NULLLBL;
805 	if (!t_seen) tag = MM_NULLTAG;
806 	if (!text_seen) text = MM_NULLTXT;
807 
808 
809 	/*
810 	 * Write the message
811 	 */
812 
813 	msgrtn = fmtmsg(class, label, severity, text, action ,tag);
814 
815 
816 	/*
817 	 * Return appropriate value to the shell (or wherever)
818 	 */
819 
820 	exitval = 0;
821 	if (msgrtn == MM_NOTOK) exitval = 32;
822 	else {
823 	    if (msgrtn & MM_NOMSG) exitval += 2;
824 	    if (msgrtn & MM_NOCON) exitval += 4;
825 	}
826 
827 	exit(exitval);
828 	return(exitval);
829 }
830