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