xref: /illumos-gate/usr/src/lib/libc/port/gen/fmtmsg.c (revision 9a5d73e03cd3312ddb571a748c40a63c58bd66e5)
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