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 *
exttok(const char * tok,const char * delims)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 *
noesc(char * str)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 *
getauxsevs(char * ptr)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
msgverbset(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
sevstrset(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
addseverity(int value,const char * string)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
itoa(int n,char * s)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
writemsg(char * buf,size_t size,int verbosity,const char * label,int severity,const char * text,const char * action,const char * tag)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
fmtmsg(long class,const char * label,int severity,const char * text,const char * action,const char * tag)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