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