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