Home | History | Annotate | Download | only in gen
      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 (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 /*
     33  * fmtmsg.c
     34  *
     35  *  Contains:
     36  *	fmtmsg()	Writes a message in standard format.
     37  *	addseverity()	Adds a severity definition to the list of known
     38  *			severity definitions.
     39  *
     40  *	Notes:
     41  *	  - None of these functions can use strtok().
     42  */
     43 
     44 /*
     45  * Header Files Referenced:
     46  *	<stdio.h>		C Standard I/O Definitions
     47  *	<string.h>		C string handling definitions
     48  *	<fcntl.h>		UNIX file control definitions
     49  *	<errno.h>		UNIX error numbers and definitions
     50  *	<fmtmsg.h>		Global definitions for fmtmsg()
     51  *	<stdlib.h>		miscellaneous function declarations
     52  */
     53 
     54 #pragma weak _fmtmsg = fmtmsg
     55 #pragma weak _addseverity = addseverity
     56 
     57 #include "lint.h"
     58 #include "mtlib.h"
     59 #include "libc.h"
     60 #include <sys/types.h>
     61 #include <stddef.h>
     62 #include <stdio.h>
     63 #include <string.h>
     64 #include <fcntl.h>
     65 #include <errno.h>
     66 #include <fmtmsg.h>
     67 #include <stdlib.h>
     68 #include <thread.h>
     69 #include <synch.h>
     70 #include <alloca.h>
     71 
     72 /*
     73  * External functions referenced:
     74  *	(Others may be defined in header files above)
     75  *
     76  *	getenv		Extracts data from the environment
     77  *	libc_malloc	Allocates space from main memory
     78  *	libc_free	Frees space allocated via libc_malloc()
     79  *	strtol		Convert string to "long"
     80  *	clearerr	Clears an error on a stream (this is to make "lint"
     81  *			happy)
     82  */
     83 
     84 
     85 /*
     86  * Local Constant Definitions
     87  */
     88 
     89 /*
     90  * Boolean constants
     91  *	TRUE	Boolean value for "true" (any bits on)
     92  *	FALSE	Boolean value for "false" (all bits off)
     93  */
     94 
     95 #ifndef	FALSE
     96 #define	FALSE		(0)
     97 #endif
     98 
     99 #ifndef TRUE
    100 #define	TRUE		(1)
    101 #endif
    102 
    103 #define	MAX_MSG_SIZE	1024
    104 
    105 /*
    106  * Keywords for fields named in the MSGVERB environment variable.
    107  */
    108 
    109 #define	ST_LBL		"label"
    110 #define	ST_SEV		"severity"
    111 #define	ST_TXT		"text"
    112 #define	ST_TAG		"tag"
    113 #define	ST_ACT		"action"
    114 
    115 
    116 /*
    117  *	The following constants define the value of the "msgverb"
    118  *	variable.  This variable tells fmtmsg() which parts of the
    119  *	standard message it is to display.  If !(msgverb&MV_SET),
    120  *	fmtmsg() will interrogate the "MSGVERB" environment variable
    121  *	and set "msgverb" accordingly.
    122  *
    123  *	NOTE:  This means that if MSGVERB changes after the first call
    124  *	       to fmtmsg(), it will be ignored.
    125  *
    126  *	Constants:
    127  *		MV_INV	Check MSGVERB environment variable (invalidates value)
    128  *		MV_SET	MSGVERB checked, msgverb value valid
    129  *		MV_LBL	"label" selected
    130  *		MV_SEV	"severity" selected
    131  *		MV_TXT	"text" selected
    132  *		MV_TAG	"messageID" selected
    133  *		MV_ACT	"action" selected
    134  *
    135  *		MV_ALL	All components selected
    136  *		MV_DFLT	Default value for MSGVERB
    137  */
    138 
    139 #define	MV_INV		0
    140 #define	MV_SET		0x0001
    141 #define	MV_LBL		0x0002
    142 #define	MV_SEV		0x0004
    143 #define	MV_TXT		0x0008
    144 #define	MV_TAG		0x0010
    145 #define	MV_ACT		0x0020
    146 
    147 #define	MV_ALL		(MV_LBL|MV_SEV|MV_TXT|MV_TAG|MV_ACT)
    148 #define	MV_DFLT		(MV_LBL|MV_SEV|MV_TXT|MV_TAG|MV_ACT)
    149 
    150 
    151 
    152 /*
    153  * Strings defining the different severities of a message.
    154  * Internationalization may demand that these come from the message database
    155  */
    156 
    157 #define	SV_UNK		"UNKNOWN"
    158 #define	SV_HALT		"HALT"
    159 #define	SV_ERROR	"ERROR"
    160 #define	SV_WARN		"WARNING"
    161 #define	SV_INF		"INFO"
    162 
    163 
    164 /*
    165  * Text string if none is provided:
    166  */
    167 
    168 #define	DEFLT_TEXT	"No text provided with this message"
    169 
    170 
    171 /*
    172  * Text string introduction for "action".  This may have to come from
    173  * the message database because of internationalization.
    174  */
    175 
    176 #define	ACTINTRO	"TO FIX: "
    177 #define	ACTINTROLN	8
    178 
    179 
    180 /*
    181  * SEPSTR is the string that separates the "label" from what follows it,
    182  * and the severity from what follows it.
    183  */
    184 
    185 #define	SEPSTR		": "
    186 #define	SEPSTRLN	2
    187 
    188 
    189 /*
    190  * Miscellaneous constants:
    191  *	CONNAME		Filesystem entry name for the system console
    192  */
    193 
    194 #define	CONNAME		"/dev/console"
    195 
    196 /*
    197  * Local data type definitions
    198  */
    199 
    200 /*
    201  * Severity string structure
    202  *
    203  *	struct sevstr
    204  *		sevvalue	Value of the severity-level being defined
    205  *		sevkywd		Keyword identifying the severity
    206  *		sevprptr	Pointer to the string associated with the value
    207  *		sevnext		Pointer to the next value in the list.
    208  *
    209  *	Restrictions:
    210  *		sevvalue	Must be a non-negative integer (>=0)
    211  *
    212  *	There are three (possibly null) lists of these structures.
    213  *	  1)	is the list of standard severities
    214  *	  2)	is the list of severity-levels defined by SEV_LEVEL
    215  *	  3)	is the list of severity-levels defined by calls to
    216  *		addseverity()
    217  */
    218 
    219 struct sevstr {
    220 	int		sevvalue;
    221 	const char	*sevkywd;
    222 	const char	*sevprstr;
    223 	struct sevstr  *sevnext;
    224 };
    225 
    226 /*
    227  * Local Static Data
    228  *	msgverb		int
    229  *			Contains the internal representation or the
    230  *			MSGVERB environment variable.
    231  *	sevlook		TRUE if fmtmsg() has to look at SEV_LEVEL the
    232  *			next time it is called.
    233  *	paugsevs	struct sevstr *
    234  *			Head of the linked list of structures that define
    235  *			severities that augment the standard severities,
    236  *			as defined by addseverity().
    237  *	penvsevs	struct sevstrs *
    238  *			Head of the linked list of structures that define
    239  *			severities that augment the standard severities,
    240  *			as defined by SEV_LEVEL.
    241  *	pstdsevs	struct sevstrs *
    242  *			Head of the linked list of structures that define
    243  *			the standard severities.
    244  */
    245 
    246 static mutex_t fmt_lock = DEFAULTMUTEX;
    247 
    248 static	int		msgverb		= 0;
    249 static	int		sevlook		= TRUE;
    250 
    251 static	struct sevstr  *paugsevs	= (struct sevstr *)NULL;
    252 static	struct sevstr  *penvsevs	= (struct sevstr *)NULL;
    253 
    254 static	struct sevstr	sevstrs[]	= {
    255 	{ MM_HALT,	"", SV_HALT,	&sevstrs[1]},
    256 	{ MM_ERROR,    "", SV_ERROR,	&sevstrs[2]},
    257 	{ MM_WARNING,  "", SV_WARN, 	&sevstrs[3]},
    258 	{ MM_INFO,	"", SV_INF,  	(struct sevstr *)NULL},
    259 };
    260 static	struct sevstr  *pstdsevs	= &sevstrs[0];
    261 
    262 /*
    263  * static char *exttok(str, delims)
    264  *	const char   *str
    265  *	const char   *delims
    266  *
    267  *	This function examines the string pointed to by "str", looking
    268  *	for the first occurrence of any of the characters in the string
    269  *	whose address is "delims".  It returns the address of that
    270  *	character or (char *)NULL if there was nothing to search.
    271  *
    272  * Arguments:
    273  *	str	Address of the string to search
    274  *	delims	Address of the string containing delimiters
    275  *
    276  * Returns:  char *
    277  *	Returns the address of the first occurrence of any of the characters
    278  *	in "delim" in the string "str" (incl '\0').  If there was nothing
    279  *	to search, the function returns (char *)NULL.
    280  *
    281  * Notes:
    282  *    - This function is needed because strtok() can't be used inside a
    283  *	function.  Besides, strtok() is destructive in the string, which
    284  *	is undesirable in many circumstances.
    285  *    - This function understands escaped delimiters as non-delimiters.
    286  *	Delimiters are escaped by preceding them with '\' characters.
    287  *	The '\' character also must be escaped.
    288  */
    289 
    290 static char *
    291 exttok(const char *tok, const char *delims)
    292 {
    293 	char	*tokend;	/* Ptr to the end of the token */
    294 	char	*p, *q;		/* Temp pointers */
    295 
    296 	/*
    297 	 * Algorithm:
    298 	 *    1.  Get the starting address(new string or where we
    299 	 *	  left off).  If nothing to search, return(char *)NULL
    300 	 *    2.  Find the end of the string
    301 	 *    3.  Look for the first unescaped delimiter closest to the
    302 	 *	  beginning of the string
    303 	 *    4.  Remember where we left off
    304 	 *    5.  Return a pointer to the delimiter we found
    305 	 */
    306 
    307 	/* Begin at the beginning, if any */
    308 	if (tok == (char *)NULL) {
    309 		return ((char *)NULL);
    310 	}
    311 
    312 	/* Find end of the token string */
    313 	tokend = (char *)tok + (ptrdiff_t)strlen(tok);
    314 
    315 	/* Look for the 1st occurrence of any delimiter */
    316 	for (p = (char *)delims; *p != '\0'; p++) {
    317 		for (q = strchr(tok, (int)*p);
    318 		    (q != 0) && (q != tok) && (*(q - (ptrdiff_t)1) == '\\');
    319 		    q = strchr(q + (ptrdiff_t)1, (int)*p))
    320 			;
    321 		if ((q != 0) && (q < tokend))
    322 			tokend = q;
    323 	}
    324 
    325 	/* Done */
    326 	return (tokend);
    327 }
    328 
    329 /*
    330  * char *noesc(str)
    331  *
    332  *	This function squeezes out all of the escaped character sequences
    333  *	from the string <str>.  It returns a pointer to that string.
    334  *
    335  *  Arguments:
    336  *	str	char *
    337  *		The string that is to have its escaped characters removed.
    338  *
    339  *  Returns:  char *
    340  *	This function returns its argument <str> always.
    341  *
    342  *  Notes:
    343  *	This function potentially modifies the string it is given.
    344  */
    345 
    346 static char *
    347 noesc(char *str)
    348 {
    349 	char   *p;		/* Temp string pointer */
    350 	char   *q;		/* Temp string pointer */
    351 
    352 	/* Look for an escaped character */
    353 	p = str;
    354 	while (*p && (*p != '\\')) p++;
    355 
    356 
    357 	/*
    358 	 * If there was at least one, squeeze them out
    359 	 * Otherwise, don't touch the argument string
    360 	 */
    361 
    362 	if (*p) {
    363 		q = p++;
    364 		while (*q++ = *p++) {
    365 			if (*p == '\\')
    366 				p++;
    367 		}
    368 	}
    369 
    370 	/* Finished.  Return our argument */
    371 	return (str);
    372 }
    373 
    374 /*
    375  * struct sevstr *getauxsevs(ptr)
    376  *
    377  *	Parses a string that is in the format of the severity definitions.
    378  *	Returns a pointer to a (malloc'd) structure that contains the
    379  *	definition, or (struct sevstr *)NULL if none was parsed.
    380  *
    381  * Arguments:
    382  *	ptr	char *
    383  *		References the string from which data is to be extracted.
    384  *		If (char *)NULL, continue where we left off.  Otherwise,
    385  *		start with the string referenced by ptr.
    386  *
    387  * Returns: struct sevstr *
    388  *	A pointer to a malloc'd structure containing the severity definition
    389  *	parsed from string, or (struct sevstr *)NULL if none.
    390  *
    391  * Notes:
    392  *    - This function is destructive to the string referenced by its argument.
    393  */
    394 
    395 /* Static data */
    396 static	char		*leftoff = (char *)NULL;
    397 
    398 static	struct sevstr *
    399 getauxsevs(char *ptr)
    400 {
    401 	char		*current;	/* Ptr to current sev def'n */
    402 	char		*tokend;	/* Ptr to end of current sev def'n */
    403 	char		*kywd;		/* Ptr to extracted kywd */
    404 	char		*valstr;		/* Ptr to extracted sev value */
    405 	char		*prstr;		/* Ptr to extracted print str */
    406 	char		*p;		/* Temp pointer */
    407 	int		val;		/* Converted severity value */
    408 	int		done;		/* Flag, sev def'n found and ok? */
    409 	struct sevstr  *rtnval;		/* Value to return */
    410 
    411 
    412 	/* Start anew or start where we left off? */
    413 	current = (ptr == (char *)NULL) ? leftoff : ptr;
    414 
    415 
    416 	/* If nothing to parse, return (char *)NULL */
    417 	if (current == (char *)NULL) {
    418 		return ((struct sevstr *)NULL);
    419 	}
    420 
    421 
    422 	/*
    423 	 * Look through the string "current" for a token of the form
    424 	 * <kywd>,<sev>,<printstring> delimited by ':' or '\0'
    425 	 */
    426 
    427 	/* Loop initializations */
    428 	done = FALSE;
    429 	rtnval = (struct sevstr *)NULL;
    430 	while (!done) {
    431 		/* Eat leading junk */
    432 		while (*(tokend = exttok(current, ":,")) == ':') {
    433 			current = tokend + (ptrdiff_t)1;
    434 		}
    435 
    436 		/* If we've found a <kywd>,... */
    437 		if (*tokend == ',') {
    438 			kywd = current;
    439 			*tokend = '\0';
    440 
    441 			/* Look for <kywd>,<sev>,... */
    442 			current = tokend + (ptrdiff_t)1;
    443 			if (*(tokend = exttok(current, ":,")) == ',') {
    444 				valstr = current;
    445 				*tokend = '\0';
    446 
    447 				current = tokend + (ptrdiff_t)1;
    448 				prstr = current;
    449 
    450 				/* Make sure <sev> > 4 */
    451 				val = (int)strtol(noesc(valstr), &p, 0);
    452 				if ((val > 4) && (p == tokend)) {
    453 
    454 					/*
    455 					 * Found <kywd>,<sev>,<printstring>.
    456 					 * remember where we left off
    457 					 */
    458 
    459 					if (*(tokend =
    460 					    exttok(current, ":")) == ':') {
    461 						*tokend = '\0';
    462 						leftoff = tokend +
    463 						    (ptrdiff_t)1;
    464 					} else {
    465 						leftoff = (char *)NULL;
    466 					}
    467 
    468 					/*
    469 					 * Alloc structure to contain
    470 					 * severity definition
    471 					 */
    472 					rtnval = libc_malloc(
    473 					    sizeof (struct sevstr));
    474 					if (rtnval != NULL) {
    475 
    476 						/* Fill in structure */
    477 						rtnval->sevkywd = noesc(kywd);
    478 						rtnval->sevvalue = val;
    479 						rtnval->sevprstr = noesc(prstr);
    480 						rtnval->sevnext =
    481 						    (struct sevstr *)NULL;
    482 					}
    483 					done = TRUE;
    484 				} else {
    485 					/*
    486 					 * Invalid severity value,
    487 					 * eat thru end of token
    488 					 */
    489 					current = tokend;
    490 					if (*(tokend = exttok(prstr, ":")) ==
    491 					    ':') {
    492 						current++;
    493 					}
    494 				}
    495 			} else {
    496 				/*
    497 				 * Invalid severity definition,
    498 				 * eat thru end of token
    499 				 */
    500 				current = tokend;
    501 				if (*tokend == ':')
    502 					current++;
    503 			}
    504 		} else {
    505 			/* End of string found */
    506 			done = TRUE;
    507 			leftoff = (char *)NULL;
    508 		}
    509 	} /* while (!done) */
    510 
    511 	/* Finished */
    512 	return (rtnval);
    513 }
    514 
    515 /*
    516  * void msgverbset()
    517  *
    518  *	Parces the argument of the MSGVERB environment variable and places
    519  *	a representation of the value of that value in "msgverb"
    520  *
    521  * Arguments:
    522  *	None:
    523  *
    524  * Returns: void
    525  *
    526  * Notes:
    527  */
    528 
    529 static void
    530 msgverbset(void)
    531 {
    532 	char   *opts;			/* Pointer to MSGVERB's value */
    533 	char   *alloced;		/* Pointer to MSGVERB's value */
    534 	char   *tok;			/* Pointer to current token */
    535 	char   *tokend;			/* Pointer to end of current token */
    536 	char   *nexttok;		/* Pointer to next token */
    537 
    538 
    539 	/* Rid ourselves of junk in "msgverb" */
    540 	msgverb = 0;
    541 
    542 	/* Get the value of MSGVERB.  If none, use default value */
    543 	if ((opts = getenv(MSGVERB)) == (char *)NULL) {
    544 		msgverb = MV_DFLT;
    545 	} else { /* MSGVERB has a value.  Interpret it */
    546 		if ((alloced = libc_malloc(strlen(opts) + 1)) == NULL) {
    547 			msgverb = MV_DFLT;
    548 		} else {
    549 			/* Make a copy of the value of MSGVERB */
    550 			nexttok = strcpy(alloced, opts);
    551 
    552 			/* Parse the options given by the user */
    553 			while ((tok = nexttok) != (char *)NULL) {
    554 				/*
    555 				 * Find end of the next token and squeeze
    556 				 * out escaped characters
    557 				 */
    558 				tokend = exttok(tok, ":");
    559 				tok = noesc(tok);
    560 
    561 				/* Delimit token and mark next, if any */
    562 				if (*tokend == ':') {
    563 					nexttok = tokend + (ptrdiff_t)1;
    564 					*tokend = '\0';
    565 				} else {
    566 					nexttok = (char *)NULL;
    567 				}
    568 
    569 				/* Check for "text" */
    570 				if (strcmp(tok, ST_TXT) == 0) {
    571 					msgverb |= MV_TXT;
    572 
    573 					/* Check for "label" */
    574 				} else if (strcmp(tok, ST_LBL) == 0) {
    575 					msgverb |= MV_LBL;
    576 
    577 					/* Check for "action */
    578 				} else if (strcmp(tok, ST_ACT) == 0) {
    579 					msgverb |= MV_ACT;
    580 
    581 					/* Check for "severity" */
    582 				} else if (strcmp(tok, ST_SEV) == 0) {
    583 					msgverb |= MV_SEV;
    584 
    585 					/* Check for "tag" */
    586 				} else if (strcmp(tok, ST_TAG) == 0) {
    587 					msgverb |= MV_TAG;
    588 
    589 					/* Unknown, ignore MSGVERB value */
    590 				} else {
    591 					msgverb = MV_DFLT;
    592 					nexttok = (char *)NULL;
    593 				}
    594 			} /* do while */
    595 
    596 			/*
    597 			 * Use default if no keywords on MSGVERB
    598 			 * environment variable
    599 			 */
    600 			if (msgverb == 0)
    601 				msgverb = MV_DFLT;
    602 
    603 			/* Free allocated space */
    604 			libc_free(alloced);
    605 		}
    606 	}
    607 	/* Finished */
    608 	/* return; */
    609 }
    610 
    611 /*
    612  * void sevstrset()
    613  *
    614  *	This function builds a structure containing auxillary severity
    615  *	definitions.
    616  *
    617  *  Arguments:  None
    618  *
    619  *  Returns:  Void
    620  */
    621 
    622 static char *sevspace = (char *)NULL;
    623 
    624 static void
    625 sevstrset(void)
    626 {
    627 	struct sevstr  *plast;
    628 	struct sevstr  *psev;
    629 	char		*value;
    630 
    631 
    632 	/* Look for SEV_LEVEL definition */
    633 	if ((value = getenv(SEV_LEVEL)) != (char *)NULL) {
    634 
    635 		/* Allocate space and make a copy of the value of SEV_LEVEL */
    636 		if ((sevspace = libc_malloc(strlen(value) + 1)) != NULL) {
    637 			(void) strcpy(sevspace, value);
    638 
    639 			/* Continue for all severity descriptions */
    640 			psev = getauxsevs(sevspace);
    641 			plast = (struct sevstr *)NULL;
    642 			if (psev != (struct sevstr *)NULL) {
    643 				penvsevs = psev;
    644 				plast = psev;
    645 				while (psev = getauxsevs((char *)NULL)) {
    646 					plast->sevnext = psev;
    647 					plast = psev;
    648 				}
    649 			}
    650 		} /* if sevspace != (char *)NULL */
    651 	} /* if value != (char *)NULL */
    652 }
    653 
    654 /*
    655  * int addseverity(value, string)
    656  *	int	value		Value of the severity
    657  *	const char   *string	Print-string for the severity
    658  *
    659  *  Arguments:
    660  *	value		int
    661  *			The integer value of the severity being added
    662  *	string		char *
    663  *			A pointer to the character-string to be printed
    664  *			whenever a severity of "value" is printed
    665  *
    666  *  Returns:  int
    667  *	Zero if successful, -1 if failed. The function can fail under
    668  *	the following circumstances:
    669  *	  - libc_malloc() fails
    670  *	  - The "value" is one of the reserved values.
    671  *
    672  *	This function permits C applications to define severity-levels
    673  *	that augment the standard levels and those defined by the
    674  *	SEV_LEVEL environment variable.
    675  */
    676 
    677 int
    678 addseverity(int value, const char *string)
    679 {
    680 	struct sevstr  *p;		/* Temp ptr to severity structs */
    681 	struct sevstr  *q;		/* Temp ptr(follower) to severity */
    682 	int		found;		/* FLAG, element found in the list */
    683 	int		rtnval;		/* Value to return to the caller */
    684 
    685 	/* Make sure we're not trying to redefine one of the reserved values */
    686 	if (value <= 4) {
    687 		errno = EINVAL;
    688 		return (-1);
    689 	}
    690 
    691 	lmutex_lock(&fmt_lock);
    692 
    693 	/* Make sure we've interpreted SEV_LEVEL */
    694 
    695 	if (sevlook) {
    696 		sevstrset();
    697 		sevlook = FALSE;
    698 	}
    699 
    700 
    701 	/*
    702 	 * Leaf through the list.  We may be redefining or removing a
    703 	 * definition
    704 	 */
    705 	q = (struct sevstr *)NULL;
    706 	found = FALSE;
    707 	for (p = paugsevs; !found && (p != (struct sevstr *)NULL);
    708 	    p = p->sevnext) {
    709 		if (p->sevvalue == value) {
    710 			/* We've a match.  Remove or modify the entry */
    711 			if (string == (char *)NULL) {
    712 				if (q == (struct sevstr *)NULL) {
    713 					paugsevs = p->sevnext;
    714 				} else {
    715 					q->sevnext = p->sevnext;
    716 				}
    717 				libc_free(p);
    718 			} else {
    719 				p->sevprstr = string;
    720 			}
    721 			found = TRUE;
    722 		}
    723 		q = p;
    724 	}
    725 
    726 	/* Adding a definition */
    727 	if (!found && (string != (char *)NULL)) {
    728 		/* Allocate space for the severity structure */
    729 		if ((p = libc_malloc(sizeof (struct sevstr))) == NULL) {
    730 			lmutex_unlock(&fmt_lock);
    731 			return (-1);
    732 		}
    733 
    734 		/*
    735 		 * Fill in the new structure with the data supplied and add to
    736 		 * the head of the augmented severity list.
    737 		 */
    738 
    739 		p->sevkywd = (char *)NULL;
    740 		p->sevprstr = string;
    741 		p->sevvalue = value;
    742 		p->sevnext = paugsevs;
    743 		paugsevs = p;
    744 
    745 		/* Successfully added a new severity */
    746 		rtnval = 0;
    747 	} else if (string == (char *)NULL) {
    748 		/* Attempting to undefined a non-defined severity */
    749 		rtnval = -1;
    750 		errno = EINVAL;
    751 	} else {
    752 		/* Successfully redefined a severity */
    753 		rtnval = 0;
    754 	}
    755 	/* Finished, successful */
    756 	lmutex_unlock(&fmt_lock);
    757 	return (rtnval);
    758 }
    759 
    760 /*
    761  * Utility function for converting an integer to a string, avoiding stdio.
    762  */
    763 static void
    764 itoa(int n, char *s)
    765 {
    766 	char buf[12];		/* 32 bits fits in 10 decimal digits */
    767 	char *cp = buf;
    768 	uint_t un = (n < 0)? -n : n;
    769 
    770 	do {
    771 		*cp++ = "0123456789"[un % 10];
    772 		un /= 10;
    773 	} while (un);
    774 
    775 	if (n < 0)
    776 		*s++ = '-';
    777 
    778 	do {
    779 		*s++ = *--cp;
    780 	} while (cp > buf);
    781 
    782 	*s = '\0';
    783 }
    784 
    785 /*
    786  * void writemsg(buf, size, verbosity, label, severity, text, action, tag)
    787  *
    788  * Arguments:
    789  *	char	*buf		The buffer in which to format the message
    790  *	size_t	size		The size of the buffer
    791  * 	int	verbosity	A bit-string that indicates which components
    792  *				are to be written
    793  * 	const char   *label	The address of the label-component
    794  * 	int	severity	The severity value of the message
    795  * 	const char   *text	The address of the text-component
    796  * 	const char   *action	The address of the action-component
    797  * 	const char   *tag	The address of the tag-component
    798  *
    799  *	This function formats the message consisting of the label-component,
    800  *	severity-component, text-component, action-component, and tag-
    801  *	component into the provided buffer.  The "verbosity" argument
    802  *	tells which components can be selected.  Any or all of the
    803  *	components can be their null-values.
    804  *
    805  * Returns:  void
    806  *
    807  * Notes:
    808  */
    809 
    810 static void
    811 writemsg(char *buf, size_t size,
    812 	int verbosity, const char *label, int severity,
    813 	const char *text, const char *action, const char *tag)
    814 {
    815 	struct sevstr  *psev;		/* Ptr for severity str list */
    816 	char		*p;		/* General purpose pointer */
    817 	char		*sevpstr = NULL;  /* Pointer to severity string */
    818 	int		l1indent;	/* # chars to indent line 1 */
    819 	int		l2indent;	/* # chars to indent line 2 */
    820 	int		textindent;	/* # spaces to indent text */
    821 	int		actindent = 0;	/* # spaces to indent action */
    822 	int		i;		/* General purpose counter */
    823 	int		dolabel;	/* TRUE if label to be written */
    824 	int		dotext;		/* TRUE if text to be written */
    825 	int		dosev;		/* TRUE if severity to be written */
    826 	int		doaction;	/* TRUE if action to be written */
    827 	int		dotag;		/* TRUE if tag to be written */
    828 	char		c;		/* Temp, multiuse character */
    829 	char		sevpstrbuf[15];	/* Space for SV=%d */
    830 
    831 	char		lcllbl[MM_MXLABELLN+1];	/* Space for (possibly */
    832 						/* truncated) label */
    833 	char		lcltag[MM_MXTAGLN+1];	/* Space for (possibly */
    834 						/* truncated) tag */
    835 	char		*ebuf = buf + size - 2;
    836 
    837 	/*
    838 	 * initialize variables.
    839 	 */
    840 	sevpstrbuf[0] = (char)0;
    841 	lcllbl[0] = (char)0;
    842 	lcltag[0] = (char)0;
    843 
    844 	/*
    845 	 * Figure out what fields are to be written (all are optional)
    846 	 */
    847 
    848 	dolabel  = (verbosity & MV_LBL) && (label != MM_NULLLBL);
    849 	dosev    = (verbosity & MV_SEV) && (severity != MM_NULLSEV);
    850 	dotext   = (verbosity & MV_TXT) && (text != MM_NULLTXT);
    851 	doaction = (verbosity & MV_ACT) && (action != MM_NULLACT);
    852 	dotag    = (verbosity & MV_TAG) && (tag != MM_NULLTAG);
    853 
    854 	/*
    855 	 * Figure out how much we'll need to indent the text of the message
    856 	 */
    857 
    858 	/* Count the label of the message, if requested */
    859 	textindent = 0;
    860 	if (dolabel) {
    861 		(void) strncpy(lcllbl, label, (size_t)MM_MXLABELLN);
    862 		lcllbl[MM_MXLABELLN] = '\0';
    863 		textindent = (int)strlen(lcllbl) + SEPSTRLN;
    864 	}
    865 
    866 	/*
    867 	 * If severity req'd, determine the severity string and factor
    868 	 * into indent count.  Severity string generated by:
    869 	 *	1.  Search the standard list of severities.
    870 	 *	2.  Search the severities added by the application.
    871 	 *	3.  Search the severities added by the environment.
    872 	 *	4.  Use the default (SV=n where n is the value of the severity).
    873 	 */
    874 
    875 	if (dosev) {
    876 		/* Search the default severity definitions */
    877 		psev = pstdsevs;
    878 		while (psev != (struct sevstr *)NULL) {
    879 			if (psev->sevvalue == severity)
    880 				break;
    881 			psev = psev->sevnext;
    882 		}
    883 
    884 		if (psev == (struct sevstr *)NULL) {
    885 			/*
    886 			 * Search the severity definitions
    887 			 * added by the application
    888 			 */
    889 			psev = paugsevs;
    890 			while (psev != (struct sevstr *)NULL) {
    891 				if (psev->sevvalue == severity)
    892 					break;
    893 				psev = psev->sevnext;
    894 			}
    895 			if (psev == (struct sevstr *)NULL) {
    896 				/*
    897 				 * Search the severity definitions
    898 				 * added by the environment
    899 				 */
    900 				psev = penvsevs;
    901 				while (psev != (struct sevstr *)NULL) {
    902 					if (psev->sevvalue == severity)
    903 						break;
    904 					psev = psev->sevnext;
    905 				}
    906 				if (psev == (struct sevstr *)NULL) {
    907 					/* Use default string, SV=severity */
    908 					(void) strcpy(sevpstrbuf, "SV=");
    909 					itoa(severity, &sevpstrbuf[3]);
    910 					sevpstr = sevpstrbuf;
    911 				} else {
    912 					sevpstr = (char *)psev->sevprstr;
    913 				}
    914 			} else {
    915 				sevpstr = (char *)psev->sevprstr;
    916 			}
    917 		} else {
    918 			sevpstr = (char *)psev->sevprstr;
    919 		}
    920 		/* Factor into indent counts */
    921 		textindent += (int)strlen(sevpstr) + SEPSTRLN;
    922 	}
    923 
    924 	/*
    925 	 * Figure out the indents.
    926 	 */
    927 
    928 	if (doaction && dotext) {
    929 		if (textindent > ACTINTROLN) {
    930 			l1indent = 0;
    931 			l2indent = textindent - ACTINTROLN;
    932 			actindent = textindent;
    933 		} else {
    934 			l2indent = 0;
    935 			actindent = ACTINTROLN;
    936 			if (dosev || dolabel) {
    937 				l1indent = ACTINTROLN - textindent;
    938 				textindent = ACTINTROLN;
    939 			} else {
    940 				textindent = 0;
    941 				l1indent = 0;
    942 			}
    943 		}
    944 	} else {
    945 		l1indent = 0;
    946 		l2indent = 0;
    947 		if (doaction) {
    948 			actindent = textindent + ACTINTROLN;
    949 		} else if (dotext) {
    950 			actindent = 0;
    951 		}
    952 	}
    953 
    954 	/*
    955 	 * Write the message.
    956 	 */
    957 
    958 	/* Write the LABEL, if requested */
    959 	if (dolabel) {
    960 		/* Write spaces to align on the ':' char, if needed */
    961 		while (--l1indent >= 0 && buf < ebuf)
    962 			*buf++ = ' ';
    963 
    964 		/* Write the label */
    965 		buf += strlcpy(buf, lcllbl, ebuf - buf);
    966 
    967 		/*
    968 		 * Write the separator string
    969 		 * (if another component is to follow)
    970 		 */
    971 		if (dosev || dotext || doaction || dotag)
    972 			buf += strlcpy(buf, SEPSTR, ebuf - buf);
    973 	}
    974 
    975 	/* Write the SEVERITY, if requested */
    976 	if (dosev) {
    977 		/* Write spaces to align on the ':' char, if needed */
    978 		while (--l1indent >= 0 && buf < ebuf)
    979 			*buf++ = ' ';
    980 
    981 		/* Write the severity print-string */
    982 		buf += strlcpy(buf, sevpstr, ebuf - buf);
    983 
    984 		/*
    985 		 * Write the separator string
    986 		 * (if another component is to follow)
    987 		 */
    988 		if (dotext || doaction || dotag)
    989 			buf += strlcpy(buf, SEPSTR, ebuf - buf);
    990 	}
    991 
    992 	/* Write the TEXT, if requested */
    993 	if (dotext) {
    994 		p = (char *)text;
    995 		for (c = *p++; c != NULL && buf < ebuf; c = *p++) {
    996 			*buf++ = c;
    997 			if (c == '\n') {
    998 				for (i = 0; i < textindent && buf < ebuf; i++)
    999 					*buf++ = ' ';
   1000 			}
   1001 		}
   1002 	}
   1003 
   1004 	/*
   1005 	 * Write ACTION if requested.
   1006 	 */
   1007 
   1008 	if (doaction) {
   1009 		if (dotext && buf < ebuf) {
   1010 			*buf++ = '\n';
   1011 			while (--l2indent >= 0 && buf < ebuf)
   1012 				*buf++ = ' ';
   1013 		}
   1014 
   1015 		/* Write the action-string's introduction */
   1016 		buf += strlcpy(buf, ACTINTRO, ebuf - buf);
   1017 
   1018 		/* Write the "action" string */
   1019 		p = (char *)action;
   1020 		for (c = *p++; c != NULL && buf < ebuf; c = *p++) {
   1021 			*buf++ = c;
   1022 			if (c == '\n') {
   1023 				for (i = 0; i < actindent && buf < ebuf; i++)
   1024 					*buf++ = ' ';
   1025 			}
   1026 		}
   1027 	}
   1028 
   1029 	/*
   1030 	 * Write the TAG if requested
   1031 	 */
   1032 
   1033 	if (dotag) {
   1034 		if (doaction)
   1035 			buf += strlcpy(buf, "  ", ebuf - buf);
   1036 		else if (dotext && buf < ebuf)
   1037 			*buf++ = '\n';
   1038 		(void) strncpy(lcltag, tag, (size_t)MM_MXTAGLN);
   1039 		lcltag[MM_MXTAGLN] = '\0';
   1040 		buf += strlcpy(buf, lcltag, ebuf - buf);
   1041 	}
   1042 
   1043 	/*
   1044 	 * Write terminating newline and null byte.
   1045 	 * We reserved space for these at the start.
   1046 	 */
   1047 	*buf++ = '\n';
   1048 	*buf++ = '\0';
   1049 }
   1050 
   1051 /*
   1052  * int	fmtmsg(class, label, severity, text, action, tag)
   1053  *	long	class
   1054  *	const char   *label
   1055  *	int	severity
   1056  *	const char   *text
   1057  *	const char   *action
   1058  *	const char   *tag
   1059  *
   1060  *	If requested, the fmtmsg() function writes a message to the standard
   1061  *      error stream in the standard message format.  Also if requested, it
   1062  *	will write a message to the system console.
   1063  *
   1064  *	Arguments:
   1065  *	    class	Fields which classify the message for the system
   1066  *			logging facility
   1067  *	    label	A character-string that is printed as the "label"
   1068  *			of the message.  Typically identifies the source
   1069  *			of the message
   1070  *	    severity	Identifies the severity of the message.  Either one
   1071  *			of the standard severities, or possibly one of the
   1072  *			augmented severities
   1073  *	    text	Pointer to the text of the message
   1074  *	    action	Pointer to a char string that describes some type
   1075  *			of corrective action.
   1076  *	    tag		A character-string that is printed as the "tag" or
   1077  *			the message.  Typically a pointer to documentation
   1078  *
   1079  *	Returns:
   1080  *	    -1 if nothing was generated, 0 if everything requested was
   1081  *	    generated, or flags if partially generated.
   1082  *
   1083  *	Needs:
   1084  *	  - Nothing special for 4.0.
   1085  */
   1086 
   1087 int
   1088 fmtmsg(long class, const char *label, int severity,
   1089 const char *text, const char *action, const char *tag)
   1090 {
   1091 	int	rtnval;		/* Value to return */
   1092 	FILE	*console;	/* Ptr to "console" stream */
   1093 	char	*message1;
   1094 	char	*message2;
   1095 
   1096 	/*
   1097 	 * Determine the "verbosity" of the message.  If "msgverb" is
   1098 	 * already set, don't interrogate the "MSGVERB" environment vbl.
   1099 	 * If so, interrogate "MSGVERB" and do initialization stuff also.
   1100 	 */
   1101 
   1102 	lmutex_lock(&fmt_lock);
   1103 
   1104 	if (!(msgverb & MV_SET)) {
   1105 		msgverbset();
   1106 		msgverb |= MV_SET;
   1107 	}
   1108 
   1109 
   1110 	/*
   1111 	 * Extract the severity definitions from the SEV_LEVEL
   1112 	 * environment variable and save away for later.
   1113 	 */
   1114 
   1115 	if (sevlook) {
   1116 		sevstrset();
   1117 		sevlook = FALSE;
   1118 	}
   1119 
   1120 
   1121 	/* Set up the default text component [if text==(char *)NULL] */
   1122 	if (text == (char *)NULL)
   1123 		text = DEFLT_TEXT;
   1124 
   1125 	/* Prepare the message for stderr if requested */
   1126 	if (class & MM_PRINT) {
   1127 		message1 = alloca(MAX_MSG_SIZE);
   1128 		writemsg(message1, MAX_MSG_SIZE,
   1129 		    msgverb, label, severity, text, action, tag);
   1130 	}
   1131 
   1132 	/* Prepare the message for the console if requested */
   1133 	if (class & MM_CONSOLE) {
   1134 		message2 = alloca(MAX_MSG_SIZE);
   1135 		writemsg(message2, MAX_MSG_SIZE,
   1136 		    MV_ALL, label, severity, text, action, tag);
   1137 	}
   1138 
   1139 	lmutex_unlock(&fmt_lock);
   1140 
   1141 	rtnval = MM_OK;
   1142 
   1143 	/* Write the message to stderr if requested */
   1144 	if (class & MM_PRINT) {
   1145 		clearerr(stderr);
   1146 		(void) fputs(message1, stderr);
   1147 		if (ferror(stderr))
   1148 			rtnval |= MM_NOMSG;
   1149 	}
   1150 
   1151 	/* Write the message to the console if requested */
   1152 	if (class & MM_CONSOLE) {
   1153 		if ((console = fopen(CONNAME, "wF")) != NULL) {
   1154 			clearerr(console);
   1155 			(void) fputs(message2, console);
   1156 			if (ferror(console))
   1157 				rtnval |= MM_NOCON;
   1158 			(void) fclose(console);
   1159 		} else {
   1160 			rtnval |= MM_NOCON;
   1161 		}
   1162 	}
   1163 
   1164 	if ((rtnval & (MM_NOCON | MM_NOMSG)) == (MM_NOCON | MM_NOMSG))
   1165 		rtnval = MM_NOTOK;
   1166 	return (rtnval);
   1167 }
   1168