2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * fmtmsg.c
2N/A *
2N/A * Contains:
2N/A * fmtmsg() Writes a message in standard format.
2N/A * addseverity() Adds a severity definition to the list of known
2N/A * severity definitions.
2N/A *
2N/A * Notes:
2N/A * - None of these functions can use strtok().
2N/A */
2N/A
2N/A/*
2N/A * Header Files Referenced:
2N/A * <stdio.h> C Standard I/O Definitions
2N/A * <string.h> C string handling definitions
2N/A * <fcntl.h> UNIX file control definitions
2N/A * <errno.h> UNIX error numbers and definitions
2N/A * <fmtmsg.h> Global definitions for fmtmsg()
2N/A * <stdlib.h> miscellaneous function declarations
2N/A */
2N/A
2N/A#pragma weak _fmtmsg = fmtmsg
2N/A#pragma weak _addseverity = addseverity
2N/A
2N/A#include "lint.h"
2N/A#include "mtlib.h"
2N/A#include "libc.h"
2N/A#include <sys/types.h>
2N/A#include <stddef.h>
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <fcntl.h>
2N/A#include <errno.h>
2N/A#include <fmtmsg.h>
2N/A#include <stdlib.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A#include <alloca.h>
2N/A
2N/A/*
2N/A * External functions referenced:
2N/A * (Others may be defined in header files above)
2N/A *
2N/A * getenv Extracts data from the environment
2N/A * libc_malloc Allocates space from main memory
2N/A * libc_free Frees space allocated via libc_malloc()
2N/A * strtol Convert string to "long"
2N/A * clearerr Clears an error on a stream (this is to make "lint"
2N/A * happy)
2N/A */
2N/A
2N/A
2N/A/*
2N/A * Local Constant Definitions
2N/A */
2N/A
2N/A/*
2N/A * Boolean constants
2N/A * TRUE Boolean value for "true" (any bits on)
2N/A * FALSE Boolean value for "false" (all bits off)
2N/A */
2N/A
2N/A#ifndef FALSE
2N/A#define FALSE (0)
2N/A#endif
2N/A
2N/A#ifndef TRUE
2N/A#define TRUE (1)
2N/A#endif
2N/A
2N/A#define MAX_MSG_SIZE 1024
2N/A
2N/A/*
2N/A * Keywords for fields named in the MSGVERB environment variable.
2N/A */
2N/A
2N/A#define ST_LBL "label"
2N/A#define ST_SEV "severity"
2N/A#define ST_TXT "text"
2N/A#define ST_TAG "tag"
2N/A#define ST_ACT "action"
2N/A
2N/A
2N/A/*
2N/A * The following constants define the value of the "msgverb"
2N/A * variable. This variable tells fmtmsg() which parts of the
2N/A * standard message it is to display. If !(msgverb&MV_SET),
2N/A * fmtmsg() will interrogate the "MSGVERB" environment variable
2N/A * and set "msgverb" accordingly.
2N/A *
2N/A * NOTE: This means that if MSGVERB changes after the first call
2N/A * to fmtmsg(), it will be ignored.
2N/A *
2N/A * Constants:
2N/A * MV_INV Check MSGVERB environment variable (invalidates value)
2N/A * MV_SET MSGVERB checked, msgverb value valid
2N/A * MV_LBL "label" selected
2N/A * MV_SEV "severity" selected
2N/A * MV_TXT "text" selected
2N/A * MV_TAG "messageID" selected
2N/A * MV_ACT "action" selected
2N/A *
2N/A * MV_ALL All components selected
2N/A * MV_DFLT Default value for MSGVERB
2N/A */
2N/A
2N/A#define MV_INV 0
2N/A#define MV_SET 0x0001
2N/A#define MV_LBL 0x0002
2N/A#define MV_SEV 0x0004
2N/A#define MV_TXT 0x0008
2N/A#define MV_TAG 0x0010
2N/A#define MV_ACT 0x0020
2N/A
2N/A#define MV_ALL (MV_LBL|MV_SEV|MV_TXT|MV_TAG|MV_ACT)
2N/A#define MV_DFLT (MV_LBL|MV_SEV|MV_TXT|MV_TAG|MV_ACT)
2N/A
2N/A
2N/A
2N/A/*
2N/A * Strings defining the different severities of a message.
2N/A * Internationalization may demand that these come from the message database
2N/A */
2N/A
2N/A#define SV_UNK "UNKNOWN"
2N/A#define SV_HALT "HALT"
2N/A#define SV_ERROR "ERROR"
2N/A#define SV_WARN "WARNING"
2N/A#define SV_INF "INFO"
2N/A
2N/A
2N/A/*
2N/A * Text string if none is provided:
2N/A */
2N/A
2N/A#define DEFLT_TEXT "No text provided with this message"
2N/A
2N/A
2N/A/*
2N/A * Text string introduction for "action". This may have to come from
2N/A * the message database because of internationalization.
2N/A */
2N/A
2N/A#define ACTINTRO "TO FIX: "
2N/A#define ACTINTROLN 8
2N/A
2N/A
2N/A/*
2N/A * SEPSTR is the string that separates the "label" from what follows it,
2N/A * and the severity from what follows it.
2N/A */
2N/A
2N/A#define SEPSTR ": "
2N/A#define SEPSTRLN 2
2N/A
2N/A
2N/A/*
2N/A * Miscellaneous constants:
2N/A * CONNAME Filesystem entry name for the system console
2N/A */
2N/A
2N/A#define CONNAME "/dev/console"
2N/A
2N/A/*
2N/A * Local data type definitions
2N/A */
2N/A
2N/A/*
2N/A * Severity string structure
2N/A *
2N/A * struct sevstr
2N/A * sevvalue Value of the severity-level being defined
2N/A * sevkywd Keyword identifying the severity
2N/A * sevprptr Pointer to the string associated with the value
2N/A * sevnext Pointer to the next value in the list.
2N/A *
2N/A * Restrictions:
2N/A * sevvalue Must be a non-negative integer (>=0)
2N/A *
2N/A * There are three (possibly null) lists of these structures.
2N/A * 1) is the list of standard severities
2N/A * 2) is the list of severity-levels defined by SEV_LEVEL
2N/A * 3) is the list of severity-levels defined by calls to
2N/A * addseverity()
2N/A */
2N/A
2N/Astruct sevstr {
2N/A int sevvalue;
2N/A const char *sevkywd;
2N/A const char *sevprstr;
2N/A struct sevstr *sevnext;
2N/A};
2N/A
2N/A/*
2N/A * Local Static Data
2N/A * msgverb int
2N/A * Contains the internal representation or the
2N/A * MSGVERB environment variable.
2N/A * sevlook TRUE if fmtmsg() has to look at SEV_LEVEL the
2N/A * next time it is called.
2N/A * paugsevs struct sevstr *
2N/A * Head of the linked list of structures that define
2N/A * severities that augment the standard severities,
2N/A * as defined by addseverity().
2N/A * penvsevs struct sevstrs *
2N/A * Head of the linked list of structures that define
2N/A * severities that augment the standard severities,
2N/A * as defined by SEV_LEVEL.
2N/A * pstdsevs struct sevstrs *
2N/A * Head of the linked list of structures that define
2N/A * the standard severities.
2N/A */
2N/A
2N/Astatic mutex_t fmt_lock = DEFAULTMUTEX;
2N/A
2N/Astatic int msgverb = 0;
2N/Astatic int sevlook = TRUE;
2N/A
2N/Astatic struct sevstr *paugsevs = (struct sevstr *)NULL;
2N/Astatic struct sevstr *penvsevs = (struct sevstr *)NULL;
2N/A
2N/Astatic struct sevstr sevstrs[] = {
2N/A { MM_HALT, "", SV_HALT, &sevstrs[1]},
2N/A { MM_ERROR, "", SV_ERROR, &sevstrs[2]},
2N/A { MM_WARNING, "", SV_WARN, &sevstrs[3]},
2N/A { MM_INFO, "", SV_INF, (struct sevstr *)NULL},
2N/A};
2N/Astatic struct sevstr *pstdsevs = &sevstrs[0];
2N/A
2N/A/*
2N/A * static char *exttok(str, delims)
2N/A * const char *str
2N/A * const char *delims
2N/A *
2N/A * This function examines the string pointed to by "str", looking
2N/A * for the first occurrence of any of the characters in the string
2N/A * whose address is "delims". It returns the address of that
2N/A * character or (char *)NULL if there was nothing to search.
2N/A *
2N/A * Arguments:
2N/A * str Address of the string to search
2N/A * delims Address of the string containing delimiters
2N/A *
2N/A * Returns: char *
2N/A * Returns the address of the first occurrence of any of the characters
2N/A * in "delim" in the string "str" (incl '\0'). If there was nothing
2N/A * to search, the function returns (char *)NULL.
2N/A *
2N/A * Notes:
2N/A * - This function is needed because strtok() can't be used inside a
2N/A * function. Besides, strtok() is destructive in the string, which
2N/A * is undesirable in many circumstances.
2N/A * - This function understands escaped delimiters as non-delimiters.
2N/A * Delimiters are escaped by preceding them with '\' characters.
2N/A * The '\' character also must be escaped.
2N/A */
2N/A
2N/Astatic char *
2N/Aexttok(const char *tok, const char *delims)
2N/A{
2N/A char *tokend; /* Ptr to the end of the token */
2N/A char *p, *q; /* Temp pointers */
2N/A
2N/A /*
2N/A * Algorithm:
2N/A * 1. Get the starting address(new string or where we
2N/A * left off). If nothing to search, return(char *)NULL
2N/A * 2. Find the end of the string
2N/A * 3. Look for the first unescaped delimiter closest to the
2N/A * beginning of the string
2N/A * 4. Remember where we left off
2N/A * 5. Return a pointer to the delimiter we found
2N/A */
2N/A
2N/A /* Begin at the beginning, if any */
2N/A if (tok == (char *)NULL) {
2N/A return ((char *)NULL);
2N/A }
2N/A
2N/A /* Find end of the token string */
2N/A tokend = (char *)tok + (ptrdiff_t)strlen(tok);
2N/A
2N/A /* Look for the 1st occurrence of any delimiter */
2N/A for (p = (char *)delims; *p != '\0'; p++) {
2N/A for (q = strchr(tok, (int)*p);
2N/A (q != 0) && (q != tok) && (*(q - (ptrdiff_t)1) == '\\');
2N/A q = strchr(q + (ptrdiff_t)1, (int)*p))
2N/A ;
2N/A if ((q != 0) && (q < tokend))
2N/A tokend = q;
2N/A }
2N/A
2N/A /* Done */
2N/A return (tokend);
2N/A}
2N/A
2N/A/*
2N/A * char *noesc(str)
2N/A *
2N/A * This function squeezes out all of the escaped character sequences
2N/A * from the string <str>. It returns a pointer to that string.
2N/A *
2N/A * Arguments:
2N/A * str char *
2N/A * The string that is to have its escaped characters removed.
2N/A *
2N/A * Returns: char *
2N/A * This function returns its argument <str> always.
2N/A *
2N/A * Notes:
2N/A * This function potentially modifies the string it is given.
2N/A */
2N/A
2N/Astatic char *
2N/Anoesc(char *str)
2N/A{
2N/A char *p; /* Temp string pointer */
2N/A char *q; /* Temp string pointer */
2N/A
2N/A /* Look for an escaped character */
2N/A p = str;
2N/A while (*p && (*p != '\\')) p++;
2N/A
2N/A
2N/A /*
2N/A * If there was at least one, squeeze them out
2N/A * Otherwise, don't touch the argument string
2N/A */
2N/A
2N/A if (*p) {
2N/A q = p++;
2N/A while (*q++ = *p++) {
2N/A if (*p == '\\')
2N/A p++;
2N/A }
2N/A }
2N/A
2N/A /* Finished. Return our argument */
2N/A return (str);
2N/A}
2N/A
2N/A/*
2N/A * struct sevstr *getauxsevs(ptr)
2N/A *
2N/A * Parses a string that is in the format of the severity definitions.
2N/A * Returns a pointer to a (malloc'd) structure that contains the
2N/A * definition, or (struct sevstr *)NULL if none was parsed.
2N/A *
2N/A * Arguments:
2N/A * ptr char *
2N/A * References the string from which data is to be extracted.
2N/A * If (char *)NULL, continue where we left off. Otherwise,
2N/A * start with the string referenced by ptr.
2N/A *
2N/A * Returns: struct sevstr *
2N/A * A pointer to a malloc'd structure containing the severity definition
2N/A * parsed from string, or (struct sevstr *)NULL if none.
2N/A *
2N/A * Notes:
2N/A * - This function is destructive to the string referenced by its argument.
2N/A */
2N/A
2N/A/* Static data */
2N/Astatic char *leftoff = (char *)NULL;
2N/A
2N/Astatic struct sevstr *
2N/Agetauxsevs(char *ptr)
2N/A{
2N/A char *current; /* Ptr to current sev def'n */
2N/A char *tokend; /* Ptr to end of current sev def'n */
2N/A char *kywd; /* Ptr to extracted kywd */
2N/A char *valstr; /* Ptr to extracted sev value */
2N/A char *prstr; /* Ptr to extracted print str */
2N/A char *p; /* Temp pointer */
2N/A int val; /* Converted severity value */
2N/A int done; /* Flag, sev def'n found and ok? */
2N/A struct sevstr *rtnval; /* Value to return */
2N/A
2N/A
2N/A /* Start anew or start where we left off? */
2N/A current = (ptr == (char *)NULL) ? leftoff : ptr;
2N/A
2N/A
2N/A /* If nothing to parse, return (char *)NULL */
2N/A if (current == (char *)NULL) {
2N/A return ((struct sevstr *)NULL);
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Look through the string "current" for a token of the form
2N/A * <kywd>,<sev>,<printstring> delimited by ':' or '\0'
2N/A */
2N/A
2N/A /* Loop initializations */
2N/A done = FALSE;
2N/A rtnval = (struct sevstr *)NULL;
2N/A while (!done) {
2N/A /* Eat leading junk */
2N/A while (*(tokend = exttok(current, ":,")) == ':') {
2N/A current = tokend + (ptrdiff_t)1;
2N/A }
2N/A
2N/A /* If we've found a <kywd>,... */
2N/A if (*tokend == ',') {
2N/A kywd = current;
2N/A *tokend = '\0';
2N/A
2N/A /* Look for <kywd>,<sev>,... */
2N/A current = tokend + (ptrdiff_t)1;
2N/A if (*(tokend = exttok(current, ":,")) == ',') {
2N/A valstr = current;
2N/A *tokend = '\0';
2N/A
2N/A current = tokend + (ptrdiff_t)1;
2N/A prstr = current;
2N/A
2N/A /* Make sure <sev> > 4 */
2N/A val = (int)strtol(noesc(valstr), &p, 0);
2N/A if ((val > 4) && (p == tokend)) {
2N/A
2N/A /*
2N/A * Found <kywd>,<sev>,<printstring>.
2N/A * remember where we left off
2N/A */
2N/A
2N/A if (*(tokend =
2N/A exttok(current, ":")) == ':') {
2N/A *tokend = '\0';
2N/A leftoff = tokend +
2N/A (ptrdiff_t)1;
2N/A } else {
2N/A leftoff = (char *)NULL;
2N/A }
2N/A
2N/A /*
2N/A * Alloc structure to contain
2N/A * severity definition
2N/A */
2N/A rtnval = libc_malloc(
2N/A sizeof (struct sevstr));
2N/A if (rtnval != NULL) {
2N/A
2N/A /* Fill in structure */
2N/A rtnval->sevkywd = noesc(kywd);
2N/A rtnval->sevvalue = val;
2N/A rtnval->sevprstr = noesc(prstr);
2N/A rtnval->sevnext =
2N/A (struct sevstr *)NULL;
2N/A }
2N/A done = TRUE;
2N/A } else {
2N/A /*
2N/A * Invalid severity value,
2N/A * eat thru end of token
2N/A */
2N/A current = tokend;
2N/A if (*(tokend = exttok(prstr, ":")) ==
2N/A ':') {
2N/A current++;
2N/A }
2N/A }
2N/A } else {
2N/A /*
2N/A * Invalid severity definition,
2N/A * eat thru end of token
2N/A */
2N/A current = tokend;
2N/A if (*tokend == ':')
2N/A current++;
2N/A }
2N/A } else {
2N/A /* End of string found */
2N/A done = TRUE;
2N/A leftoff = (char *)NULL;
2N/A }
2N/A } /* while (!done) */
2N/A
2N/A /* Finished */
2N/A return (rtnval);
2N/A}
2N/A
2N/A/*
2N/A * void msgverbset()
2N/A *
2N/A * Parces the argument of the MSGVERB environment variable and places
2N/A * a representation of the value of that value in "msgverb"
2N/A *
2N/A * Arguments:
2N/A * None:
2N/A *
2N/A * Returns: void
2N/A *
2N/A * Notes:
2N/A */
2N/A
2N/Astatic void
2N/Amsgverbset(void)
2N/A{
2N/A char *opts; /* Pointer to MSGVERB's value */
2N/A char *alloced; /* Pointer to MSGVERB's value */
2N/A char *tok; /* Pointer to current token */
2N/A char *tokend; /* Pointer to end of current token */
2N/A char *nexttok; /* Pointer to next token */
2N/A
2N/A
2N/A /* Rid ourselves of junk in "msgverb" */
2N/A msgverb = 0;
2N/A
2N/A /* Get the value of MSGVERB. If none, use default value */
2N/A if ((opts = getenv(MSGVERB)) == (char *)NULL) {
2N/A msgverb = MV_DFLT;
2N/A } else { /* MSGVERB has a value. Interpret it */
2N/A if ((alloced = libc_malloc(strlen(opts) + 1)) == NULL) {
2N/A msgverb = MV_DFLT;
2N/A } else {
2N/A /* Make a copy of the value of MSGVERB */
2N/A nexttok = strcpy(alloced, opts);
2N/A
2N/A /* Parse the options given by the user */
2N/A while ((tok = nexttok) != (char *)NULL) {
2N/A /*
2N/A * Find end of the next token and squeeze
2N/A * out escaped characters
2N/A */
2N/A tokend = exttok(tok, ":");
2N/A tok = noesc(tok);
2N/A
2N/A /* Delimit token and mark next, if any */
2N/A if (*tokend == ':') {
2N/A nexttok = tokend + (ptrdiff_t)1;
2N/A *tokend = '\0';
2N/A } else {
2N/A nexttok = (char *)NULL;
2N/A }
2N/A
2N/A /* Check for "text" */
2N/A if (strcmp(tok, ST_TXT) == 0) {
2N/A msgverb |= MV_TXT;
2N/A
2N/A /* Check for "label" */
2N/A } else if (strcmp(tok, ST_LBL) == 0) {
2N/A msgverb |= MV_LBL;
2N/A
2N/A /* Check for "action */
2N/A } else if (strcmp(tok, ST_ACT) == 0) {
2N/A msgverb |= MV_ACT;
2N/A
2N/A /* Check for "severity" */
2N/A } else if (strcmp(tok, ST_SEV) == 0) {
2N/A msgverb |= MV_SEV;
2N/A
2N/A /* Check for "tag" */
2N/A } else if (strcmp(tok, ST_TAG) == 0) {
2N/A msgverb |= MV_TAG;
2N/A
2N/A /* Unknown, ignore MSGVERB value */
2N/A } else {
2N/A msgverb = MV_DFLT;
2N/A nexttok = (char *)NULL;
2N/A }
2N/A } /* do while */
2N/A
2N/A /*
2N/A * Use default if no keywords on MSGVERB
2N/A * environment variable
2N/A */
2N/A if (msgverb == 0)
2N/A msgverb = MV_DFLT;
2N/A
2N/A /* Free allocated space */
2N/A libc_free(alloced);
2N/A }
2N/A }
2N/A /* Finished */
2N/A /* return; */
2N/A}
2N/A
2N/A/*
2N/A * void sevstrset()
2N/A *
2N/A * This function builds a structure containing auxillary severity
2N/A * definitions.
2N/A *
2N/A * Arguments: None
2N/A *
2N/A * Returns: Void
2N/A */
2N/A
2N/Astatic char *sevspace = (char *)NULL;
2N/A
2N/Astatic void
2N/Asevstrset(void)
2N/A{
2N/A struct sevstr *plast;
2N/A struct sevstr *psev;
2N/A char *value;
2N/A
2N/A
2N/A /* Look for SEV_LEVEL definition */
2N/A if ((value = getenv(SEV_LEVEL)) != (char *)NULL) {
2N/A
2N/A /* Allocate space and make a copy of the value of SEV_LEVEL */
2N/A if ((sevspace = libc_malloc(strlen(value) + 1)) != NULL) {
2N/A (void) strcpy(sevspace, value);
2N/A
2N/A /* Continue for all severity descriptions */
2N/A psev = getauxsevs(sevspace);
2N/A plast = (struct sevstr *)NULL;
2N/A if (psev != (struct sevstr *)NULL) {
2N/A penvsevs = psev;
2N/A plast = psev;
2N/A while (psev = getauxsevs((char *)NULL)) {
2N/A plast->sevnext = psev;
2N/A plast = psev;
2N/A }
2N/A }
2N/A } /* if sevspace != (char *)NULL */
2N/A } /* if value != (char *)NULL */
2N/A}
2N/A
2N/A/*
2N/A * int addseverity(value, string)
2N/A * int value Value of the severity
2N/A * const char *string Print-string for the severity
2N/A *
2N/A * Arguments:
2N/A * value int
2N/A * The integer value of the severity being added
2N/A * string char *
2N/A * A pointer to the character-string to be printed
2N/A * whenever a severity of "value" is printed
2N/A *
2N/A * Returns: int
2N/A * Zero if successful, -1 if failed. The function can fail under
2N/A * the following circumstances:
2N/A * - libc_malloc() fails
2N/A * - The "value" is one of the reserved values.
2N/A *
2N/A * This function permits C applications to define severity-levels
2N/A * that augment the standard levels and those defined by the
2N/A * SEV_LEVEL environment variable.
2N/A */
2N/A
2N/Aint
2N/Aaddseverity(int value, const char *string)
2N/A{
2N/A struct sevstr *p; /* Temp ptr to severity structs */
2N/A struct sevstr *q; /* Temp ptr(follower) to severity */
2N/A int found; /* FLAG, element found in the list */
2N/A int rtnval; /* Value to return to the caller */
2N/A
2N/A /* Make sure we're not trying to redefine one of the reserved values */
2N/A if (value <= 4) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A lmutex_lock(&fmt_lock);
2N/A
2N/A /* Make sure we've interpreted SEV_LEVEL */
2N/A
2N/A if (sevlook) {
2N/A sevstrset();
2N/A sevlook = FALSE;
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Leaf through the list. We may be redefining or removing a
2N/A * definition
2N/A */
2N/A q = (struct sevstr *)NULL;
2N/A found = FALSE;
2N/A for (p = paugsevs; !found && (p != (struct sevstr *)NULL);
2N/A p = p->sevnext) {
2N/A if (p->sevvalue == value) {
2N/A /* We've a match. Remove or modify the entry */
2N/A if (string == (char *)NULL) {
2N/A if (q == (struct sevstr *)NULL) {
2N/A paugsevs = p->sevnext;
2N/A } else {
2N/A q->sevnext = p->sevnext;
2N/A }
2N/A libc_free(p);
2N/A } else {
2N/A p->sevprstr = string;
2N/A }
2N/A found = TRUE;
2N/A }
2N/A q = p;
2N/A }
2N/A
2N/A /* Adding a definition */
2N/A if (!found && (string != (char *)NULL)) {
2N/A /* Allocate space for the severity structure */
2N/A if ((p = libc_malloc(sizeof (struct sevstr))) == NULL) {
2N/A lmutex_unlock(&fmt_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Fill in the new structure with the data supplied and add to
2N/A * the head of the augmented severity list.
2N/A */
2N/A
2N/A p->sevkywd = (char *)NULL;
2N/A p->sevprstr = string;
2N/A p->sevvalue = value;
2N/A p->sevnext = paugsevs;
2N/A paugsevs = p;
2N/A
2N/A /* Successfully added a new severity */
2N/A rtnval = 0;
2N/A } else if (string == (char *)NULL) {
2N/A /* Attempting to undefined a non-defined severity */
2N/A rtnval = -1;
2N/A errno = EINVAL;
2N/A } else {
2N/A /* Successfully redefined a severity */
2N/A rtnval = 0;
2N/A }
2N/A /* Finished, successful */
2N/A lmutex_unlock(&fmt_lock);
2N/A return (rtnval);
2N/A}
2N/A
2N/A/*
2N/A * Utility function for converting an integer to a string, avoiding stdio.
2N/A */
2N/Astatic void
2N/Aitoa(int n, char *s)
2N/A{
2N/A char buf[12]; /* 32 bits fits in 10 decimal digits */
2N/A char *cp = buf;
2N/A uint_t un = (n < 0)? -n : n;
2N/A
2N/A do {
2N/A *cp++ = "0123456789"[un % 10];
2N/A un /= 10;
2N/A } while (un);
2N/A
2N/A if (n < 0)
2N/A *s++ = '-';
2N/A
2N/A do {
2N/A *s++ = *--cp;
2N/A } while (cp > buf);
2N/A
2N/A *s = '\0';
2N/A}
2N/A
2N/A/*
2N/A * void writemsg(buf, size, verbosity, label, severity, text, action, tag)
2N/A *
2N/A * Arguments:
2N/A * char *buf The buffer in which to format the message
2N/A * size_t size The size of the buffer
2N/A * int verbosity A bit-string that indicates which components
2N/A * are to be written
2N/A * const char *label The address of the label-component
2N/A * int severity The severity value of the message
2N/A * const char *text The address of the text-component
2N/A * const char *action The address of the action-component
2N/A * const char *tag The address of the tag-component
2N/A *
2N/A * This function formats the message consisting of the label-component,
2N/A * severity-component, text-component, action-component, and tag-
2N/A * component into the provided buffer. The "verbosity" argument
2N/A * tells which components can be selected. Any or all of the
2N/A * components can be their null-values.
2N/A *
2N/A * Returns: void
2N/A *
2N/A * Notes:
2N/A */
2N/A
2N/Astatic void
2N/Awritemsg(char *buf, size_t size,
2N/A int verbosity, const char *label, int severity,
2N/A const char *text, const char *action, const char *tag)
2N/A{
2N/A struct sevstr *psev; /* Ptr for severity str list */
2N/A char *p; /* General purpose pointer */
2N/A char *sevpstr = NULL; /* Pointer to severity string */
2N/A int l1indent; /* # chars to indent line 1 */
2N/A int l2indent; /* # chars to indent line 2 */
2N/A int textindent; /* # spaces to indent text */
2N/A int actindent = 0; /* # spaces to indent action */
2N/A int i; /* General purpose counter */
2N/A int dolabel; /* TRUE if label to be written */
2N/A int dotext; /* TRUE if text to be written */
2N/A int dosev; /* TRUE if severity to be written */
2N/A int doaction; /* TRUE if action to be written */
2N/A int dotag; /* TRUE if tag to be written */
2N/A char c; /* Temp, multiuse character */
2N/A char sevpstrbuf[15]; /* Space for SV=%d */
2N/A
2N/A char lcllbl[MM_MXLABELLN+1]; /* Space for (possibly */
2N/A /* truncated) label */
2N/A char lcltag[MM_MXTAGLN+1]; /* Space for (possibly */
2N/A /* truncated) tag */
2N/A char *ebuf = buf + size - 2;
2N/A
2N/A /*
2N/A * initialize variables.
2N/A */
2N/A sevpstrbuf[0] = (char)0;
2N/A lcllbl[0] = (char)0;
2N/A lcltag[0] = (char)0;
2N/A
2N/A /*
2N/A * Figure out what fields are to be written (all are optional)
2N/A */
2N/A
2N/A dolabel = (verbosity & MV_LBL) && (label != MM_NULLLBL);
2N/A dosev = (verbosity & MV_SEV) && (severity != MM_NULLSEV);
2N/A dotext = (verbosity & MV_TXT) && (text != MM_NULLTXT);
2N/A doaction = (verbosity & MV_ACT) && (action != MM_NULLACT);
2N/A dotag = (verbosity & MV_TAG) && (tag != MM_NULLTAG);
2N/A
2N/A /*
2N/A * Figure out how much we'll need to indent the text of the message
2N/A */
2N/A
2N/A /* Count the label of the message, if requested */
2N/A textindent = 0;
2N/A if (dolabel) {
2N/A (void) strncpy(lcllbl, label, (size_t)MM_MXLABELLN);
2N/A lcllbl[MM_MXLABELLN] = '\0';
2N/A textindent = (int)strlen(lcllbl) + SEPSTRLN;
2N/A }
2N/A
2N/A /*
2N/A * If severity req'd, determine the severity string and factor
2N/A * into indent count. Severity string generated by:
2N/A * 1. Search the standard list of severities.
2N/A * 2. Search the severities added by the application.
2N/A * 3. Search the severities added by the environment.
2N/A * 4. Use the default (SV=n where n is the value of the severity).
2N/A */
2N/A
2N/A if (dosev) {
2N/A /* Search the default severity definitions */
2N/A psev = pstdsevs;
2N/A while (psev != (struct sevstr *)NULL) {
2N/A if (psev->sevvalue == severity)
2N/A break;
2N/A psev = psev->sevnext;
2N/A }
2N/A
2N/A if (psev == (struct sevstr *)NULL) {
2N/A /*
2N/A * Search the severity definitions
2N/A * added by the application
2N/A */
2N/A psev = paugsevs;
2N/A while (psev != (struct sevstr *)NULL) {
2N/A if (psev->sevvalue == severity)
2N/A break;
2N/A psev = psev->sevnext;
2N/A }
2N/A if (psev == (struct sevstr *)NULL) {
2N/A /*
2N/A * Search the severity definitions
2N/A * added by the environment
2N/A */
2N/A psev = penvsevs;
2N/A while (psev != (struct sevstr *)NULL) {
2N/A if (psev->sevvalue == severity)
2N/A break;
2N/A psev = psev->sevnext;
2N/A }
2N/A if (psev == (struct sevstr *)NULL) {
2N/A /* Use default string, SV=severity */
2N/A (void) strcpy(sevpstrbuf, "SV=");
2N/A itoa(severity, &sevpstrbuf[3]);
2N/A sevpstr = sevpstrbuf;
2N/A } else {
2N/A sevpstr = (char *)psev->sevprstr;
2N/A }
2N/A } else {
2N/A sevpstr = (char *)psev->sevprstr;
2N/A }
2N/A } else {
2N/A sevpstr = (char *)psev->sevprstr;
2N/A }
2N/A /* Factor into indent counts */
2N/A textindent += (int)strlen(sevpstr) + SEPSTRLN;
2N/A }
2N/A
2N/A /*
2N/A * Figure out the indents.
2N/A */
2N/A
2N/A if (doaction && dotext) {
2N/A if (textindent > ACTINTROLN) {
2N/A l1indent = 0;
2N/A l2indent = textindent - ACTINTROLN;
2N/A actindent = textindent;
2N/A } else {
2N/A l2indent = 0;
2N/A actindent = ACTINTROLN;
2N/A if (dosev || dolabel) {
2N/A l1indent = ACTINTROLN - textindent;
2N/A textindent = ACTINTROLN;
2N/A } else {
2N/A textindent = 0;
2N/A l1indent = 0;
2N/A }
2N/A }
2N/A } else {
2N/A l1indent = 0;
2N/A l2indent = 0;
2N/A if (doaction) {
2N/A actindent = textindent + ACTINTROLN;
2N/A } else if (dotext) {
2N/A actindent = 0;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Write the message.
2N/A */
2N/A
2N/A /* Write the LABEL, if requested */
2N/A if (dolabel) {
2N/A /* Write spaces to align on the ':' char, if needed */
2N/A while (--l1indent >= 0 && buf < ebuf)
2N/A *buf++ = ' ';
2N/A
2N/A /* Write the label */
2N/A buf += strlcpy(buf, lcllbl, ebuf - buf);
2N/A
2N/A /*
2N/A * Write the separator string
2N/A * (if another component is to follow)
2N/A */
2N/A if (dosev || dotext || doaction || dotag)
2N/A buf += strlcpy(buf, SEPSTR, ebuf - buf);
2N/A }
2N/A
2N/A /* Write the SEVERITY, if requested */
2N/A if (dosev) {
2N/A /* Write spaces to align on the ':' char, if needed */
2N/A while (--l1indent >= 0 && buf < ebuf)
2N/A *buf++ = ' ';
2N/A
2N/A /* Write the severity print-string */
2N/A buf += strlcpy(buf, sevpstr, ebuf - buf);
2N/A
2N/A /*
2N/A * Write the separator string
2N/A * (if another component is to follow)
2N/A */
2N/A if (dotext || doaction || dotag)
2N/A buf += strlcpy(buf, SEPSTR, ebuf - buf);
2N/A }
2N/A
2N/A /* Write the TEXT, if requested */
2N/A if (dotext) {
2N/A p = (char *)text;
2N/A for (c = *p++; c != NULL && buf < ebuf; c = *p++) {
2N/A *buf++ = c;
2N/A if (c == '\n') {
2N/A for (i = 0; i < textindent && buf < ebuf; i++)
2N/A *buf++ = ' ';
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Write ACTION if requested.
2N/A */
2N/A
2N/A if (doaction) {
2N/A if (dotext && buf < ebuf) {
2N/A *buf++ = '\n';
2N/A while (--l2indent >= 0 && buf < ebuf)
2N/A *buf++ = ' ';
2N/A }
2N/A
2N/A /* Write the action-string's introduction */
2N/A buf += strlcpy(buf, ACTINTRO, ebuf - buf);
2N/A
2N/A /* Write the "action" string */
2N/A p = (char *)action;
2N/A for (c = *p++; c != NULL && buf < ebuf; c = *p++) {
2N/A *buf++ = c;
2N/A if (c == '\n') {
2N/A for (i = 0; i < actindent && buf < ebuf; i++)
2N/A *buf++ = ' ';
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Write the TAG if requested
2N/A */
2N/A
2N/A if (dotag) {
2N/A if (doaction)
2N/A buf += strlcpy(buf, " ", ebuf - buf);
2N/A else if (dotext && buf < ebuf)
2N/A *buf++ = '\n';
2N/A (void) strncpy(lcltag, tag, (size_t)MM_MXTAGLN);
2N/A lcltag[MM_MXTAGLN] = '\0';
2N/A buf += strlcpy(buf, lcltag, ebuf - buf);
2N/A }
2N/A
2N/A /*
2N/A * Write terminating newline and null byte.
2N/A * We reserved space for these at the start.
2N/A */
2N/A *buf++ = '\n';
2N/A *buf++ = '\0';
2N/A}
2N/A
2N/A/*
2N/A * int fmtmsg(class, label, severity, text, action, tag)
2N/A * long class
2N/A * const char *label
2N/A * int severity
2N/A * const char *text
2N/A * const char *action
2N/A * const char *tag
2N/A *
2N/A * If requested, the fmtmsg() function writes a message to the standard
2N/A * error stream in the standard message format. Also if requested, it
2N/A * will write a message to the system console.
2N/A *
2N/A * Arguments:
2N/A * class Fields which classify the message for the system
2N/A * logging facility
2N/A * label A character-string that is printed as the "label"
2N/A * of the message. Typically identifies the source
2N/A * of the message
2N/A * severity Identifies the severity of the message. Either one
2N/A * of the standard severities, or possibly one of the
2N/A * augmented severities
2N/A * text Pointer to the text of the message
2N/A * action Pointer to a char string that describes some type
2N/A * of corrective action.
2N/A * tag A character-string that is printed as the "tag" or
2N/A * the message. Typically a pointer to documentation
2N/A *
2N/A * Returns:
2N/A * -1 if nothing was generated, 0 if everything requested was
2N/A * generated, or flags if partially generated.
2N/A *
2N/A * Needs:
2N/A * - Nothing special for 4.0.
2N/A */
2N/A
2N/Aint
2N/Afmtmsg(long class, const char *label, int severity,
2N/Aconst char *text, const char *action, const char *tag)
2N/A{
2N/A int rtnval; /* Value to return */
2N/A FILE *console; /* Ptr to "console" stream */
2N/A char *message1;
2N/A char *message2;
2N/A
2N/A /*
2N/A * Determine the "verbosity" of the message. If "msgverb" is
2N/A * already set, don't interrogate the "MSGVERB" environment vbl.
2N/A * If so, interrogate "MSGVERB" and do initialization stuff also.
2N/A */
2N/A
2N/A lmutex_lock(&fmt_lock);
2N/A
2N/A if (!(msgverb & MV_SET)) {
2N/A msgverbset();
2N/A msgverb |= MV_SET;
2N/A }
2N/A
2N/A
2N/A /*
2N/A * Extract the severity definitions from the SEV_LEVEL
2N/A * environment variable and save away for later.
2N/A */
2N/A
2N/A if (sevlook) {
2N/A sevstrset();
2N/A sevlook = FALSE;
2N/A }
2N/A
2N/A
2N/A /* Set up the default text component [if text==(char *)NULL] */
2N/A if (text == (char *)NULL)
2N/A text = DEFLT_TEXT;
2N/A
2N/A /* Prepare the message for stderr if requested */
2N/A if (class & MM_PRINT) {
2N/A message1 = alloca(MAX_MSG_SIZE);
2N/A writemsg(message1, MAX_MSG_SIZE,
2N/A msgverb, label, severity, text, action, tag);
2N/A }
2N/A
2N/A /* Prepare the message for the console if requested */
2N/A if (class & MM_CONSOLE) {
2N/A message2 = alloca(MAX_MSG_SIZE);
2N/A writemsg(message2, MAX_MSG_SIZE,
2N/A MV_ALL, label, severity, text, action, tag);
2N/A }
2N/A
2N/A lmutex_unlock(&fmt_lock);
2N/A
2N/A rtnval = MM_OK;
2N/A
2N/A /* Write the message to stderr if requested */
2N/A if (class & MM_PRINT) {
2N/A clearerr(stderr);
2N/A (void) fputs(message1, stderr);
2N/A if (ferror(stderr))
2N/A rtnval |= MM_NOMSG;
2N/A }
2N/A
2N/A /* Write the message to the console if requested */
2N/A if (class & MM_CONSOLE) {
2N/A if ((console = fopen(CONNAME, "wF")) != NULL) {
2N/A clearerr(console);
2N/A (void) fputs(message2, console);
2N/A if (ferror(console))
2N/A rtnval |= MM_NOCON;
2N/A (void) fclose(console);
2N/A } else {
2N/A rtnval |= MM_NOCON;
2N/A }
2N/A }
2N/A
2N/A if ((rtnval & (MM_NOCON | MM_NOMSG)) == (MM_NOCON | MM_NOMSG))
2N/A rtnval = MM_NOTOK;
2N/A return (rtnval);
2N/A}