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
217struct 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
244static mutex_t fmt_lock = DEFAULTMUTEX;
245
246static	int		msgverb		= 0;
247static	int		sevlook		= TRUE;
248
249static	struct sevstr  *paugsevs	= (struct sevstr *)NULL;
250static	struct sevstr  *penvsevs	= (struct sevstr *)NULL;
251
252static	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};
258static	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
288static char *
289exttok(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
344static char *
345noesc(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++) {
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 */
394static	char		*leftoff = (char *)NULL;
395
396static	struct sevstr *
397getauxsevs(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
527static void
528msgverbset(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
620static char *sevspace = (char *)NULL;
621
622static void
623sevstrset(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)) != (char *)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 = (struct sevstr *)NULL;
640			if (psev != (struct sevstr *)NULL) {
641				penvsevs = psev;
642				plast = psev;
643				while (psev = getauxsevs((char *)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
675int
676addseverity(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 */
761static void
762itoa(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
808static void
809writemsg(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
1085int
1086fmtmsg(long class, const char *label, int severity,
1087const 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