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