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, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 
  23 /*
  24  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  29 /*        All Rights Reserved   */
  30 
  31 #pragma ident   "%Z%%M% %I%     %E% SMI"
  32 
  33 
  34 /*
  35  * fmtmsg.c
  36  *
  37  * Contains:
  38  *      fmtmsg          Command that writes a message in the standard
  39  *                      message format.  May in future make these
  40  *                      messages available for logging.
  41  */
  42 
  43 
  44 /*
  45  * Header files used:
  46  *      <stdio.h> C Standard I/O function definitions
  47  *      <string.h>        C string-handling definitions
  48  *      <errno.h> UNIX error-code "errno" definitions
  49  *      <fmtmsg.h>        Standard Message definitions
  50  */
  51 
  52 #include        <stdio.h>
  53 #include        <string.h>
  54 #include        <errno.h>
  55 #include        <fmtmsg.h>
  56 
  57 
  58 /*
  59  * Externals referenced:
  60  *      strtol          Function that converts char strings to "long"
  61  *      fmtmsg          Function that writes a message in standard format
  62  *      getenv          Function that extracts an environment variable's
  63  *                      value
  64  *      malloc          Allocate memory from the memory pool
  65  *      free            Frees allocated memory
  66  *      getopt          Function that extracts arguments from the command-
  67  *      optarg          Points to option's argument (from getopt())
  68  *      optind          Option's argument index (from getopt())
  69  *      opterr          FLAG, write error if invalid option (for getopt())
  70  *                      line.
  71  *      exit            Exits the command
  72  */
  73 
  74 extern  long            strtol();
  75 extern  int             fmtmsg();
  76 extern  char           *getenv();
  77 extern  void           *malloc();
  78 extern  void            free();
  79 extern  int             getopt();
  80 extern  char           *optarg;
  81 extern  int             optind;
  82 extern  int             opterr;
  83 extern  void            exit();
  84 
  85 /*
  86  * Local definitions
  87  */
  88 
  89 /*
  90  * Local constants
  91  */
  92 
  93 
  94 /*
  95  * Boolean constants
  96  *      TRUE    Boolean value for "true" (any bits on)
  97  *      FALSE   Boolean value for "false" (all bits off)
  98  */
  99 
 100 #ifndef FALSE
 101 #define FALSE           (0)
 102 #endif
 103 
 104 #ifndef TRUE
 105 #define TRUE            (1)
 106 #endif
 107 
 108 
 109 #define CLASS           (MM_PRINT|MM_SOFT|MM_NRECOV|MM_UTIL)
 110 #define BIGUSAGE        "fmtmsg [-a action] [-c class] [-l label] [-s severity] [-t tag]\n       [-u subclass[,subclass[,...]]] [text]\n"
 111 
 112 
 113 /*
 114  * Local data-type definitions
 115  */
 116 
 117 /*
 118  * Structure used for tables containing keywords and integer values
 119  */
 120 
 121 struct sev_info {
 122         char   *keyword;
 123         int     value;
 124 };
 125 
 126 
 127 /*
 128  * Structure used for tables containing keywords, long values
 129  */
 130 
 131 struct class_info {
 132         char   *keyword;
 133         long    value;
 134         long    conflict;
 135 };
 136 
 137 
 138 /*
 139  * Severity string structure
 140  *
 141  *      struct sevstr
 142  *              sevvalue        Value of the severity-level being defined
 143  *              sevkywd         Keyword identifying the severity
 144  *              sevprptr        Pointer to the string associated with the value
 145  *              sevnext         Pointer to the next value in the list.
 146  */
 147 
 148 struct sevstr {
 149         int             sevvalue;
 150         char           *sevkywd;
 151         char           *sevprstr;
 152         struct sevstr  *sevnext;
 153 };
 154 
 155 
 156 /*
 157  * Local static data
 158  */
 159 
 160 
 161 /*
 162  * Table contains the keywords for the classes of a message
 163  */
 164 
 165 static  struct class_info       classes[] = {
 166 
 167         {"hard",        MM_HARD,        MM_SOFT|MM_FIRM},       /* hardware */
 168         {"soft",        MM_SOFT,        MM_HARD|MM_FIRM},       /* software */
 169         {"firm",        MM_FIRM,        MM_SOFT|MM_FIRM},       /* firmware */
 170 
 171         {(char *) NULL, 0L,             0L}                     /* end of list */
 172 
 173 };
 174 
 175 
 176 /*
 177  * Table contains the keywords for the subclasses for a message
 178  */
 179 
 180 static  struct class_info       subclasses[] =  {
 181 
 182         {"appl",        MM_APPL,        MM_UTIL|MM_OPSYS},      /* Application */
 183         {"util",        MM_UTIL,        MM_APPL|MM_OPSYS},      /* Utility */
 184         {"opsys",       MM_OPSYS,       MM_APPL|MM_UTIL},       /* Operating System */
 185 
 186         {"recov",       MM_RECOVER,     MM_NRECOV},             /* Recoverable */
 187         {"nrecov",      MM_NRECOV,      MM_RECOVER},            /* Non-recoverable */
 188 
 189         {"print",       MM_PRINT,       0L},                    /* Write message to stderr */
 190         {"console",     MM_CONSOLE,     0L},                    /* Write message on /dev/console */
 191         {(char *) NULL, 0L,             0L}                     /* End of list */
 192 
 193 };
 194 
 195 
 196 /*
 197  * Table contains the keywords for the standard severities of a message.
 198  * User may supply more through the SEV_LEVEL environment variable.
 199  */
 200 
 201 static  struct sev_info         severities[] =  {
 202         {"halt",        MM_HALT},       /* halt */
 203         {"error",       MM_ERROR},      /* error */
 204         {"warn",        MM_WARNING},    /* warn */
 205         {"info",        MM_INFO},       /* info */
 206         {(char *) NULL, 0}              /* end of list */
 207 };
 208 
 209 
 210 /*
 211  * Buffers used by the command
 212  */
 213 
 214 static  char    labelbuf[128];          /* Buf for message label */
 215 static  char    msgbuf[256];            /* Buf for messages */
 216 
 217 /*
 218  * static char *exttok(str, delims)
 219  *      char   *str
 220  *      char   *delims
 221  *
 222  *      This function examines the string pointed to by "str", looking
 223  *      for the first occurrence of any of the characters in the string
 224  *      whose address is "delims".  It returns the address of that
 225  *      character or (char *) NULL if there was nothing to search.
 226  *
 227  * Arguments:
 228  *      str     Address of the string to search
 229  *      delims  Address of the string containing delimiters
 230  *
 231  * Returns:  char *
 232  *      Returns the address of the first occurrence of any of the characters
 233  *      in "delim" in the string "str" (incl '\0').  If there was nothing
 234  *      to search, the function returns (char *) NULL.
 235  *
 236  * Notes:
 237  *    - This function is needed because strtok() can't be used inside a
 238  *      function.  Besides, strtok() is destructive in the string, which
 239  *      is undesirable in many circumstances.
 240  *    - This function understands escaped delimiters as non-delimiters.
 241  *      Delimiters are escaped by preceding them with '\' characters.
 242  *      The '\' character also must be escaped.
 243  */
 244 
 245 static char *
 246 exttok(tok, delims)
 247         char   *tok;            /* Ptr to the token we're parsing */
 248         char   *delims;         /* Ptr to string with delimiters */
 249 {
 250 
 251         /* Automatic Data */
 252         char   *tokend;         /* Ptr to the end of the token */
 253         char   *p, *q;          /* Temp pointers */
 254 
 255 
 256         /* Algorithm:
 257          *    1.  Get the starting address (new string or where we
 258          *        left off).  If nothing to search, return (char *) NULL
 259          *    2.  Find the end of the string
 260          *    3.  Look for the first unescaped delimiter closest to the 
 261          *        beginning of the string
 262          *    4.  Remember where we left off
 263          *    5.  Return a pointer to the delimiter we found
 264          */
 265 
 266         /* Begin at the beginning, if any */
 267         if (tok == (char *) NULL) {
 268             return ((char *) NULL);
 269         }
 270 
 271         /* Find end of the token string */
 272         tokend = tok + strlen(tok);
 273 
 274         /* Look for the 1st occurrence of any delimiter */
 275         for (p = delims ; *p != '\0' ; p++) {
 276             for (q = strchr(tok, *p) ; q && (q != tok) && (*(q-1) == '\\') ; q = strchr(q+1, *p)) ;
 277             if (q && (q < tokend)) tokend = q;
 278         }
 279 
 280         /* Done */
 281         return(tokend);
 282 }
 283 
 284 /*
 285  * char *noesc(str)
 286  *      
 287  *      This function squeezes out all of the escaped character sequences
 288  *      from the string <str>.  It returns a pointer to that string.
 289  *
 290  *  Arguments:
 291  *      str     char *
 292  *              The string that is to have its escaped characters removed.
 293  *
 294  *  Returns:  char *
 295  *      This function returns its argument <str> always.
 296  *
 297  *  Notes:
 298  *      This function potentially modifies the string it is given.
 299  */
 300 
 301 char *
 302 noesc(str) 
 303         char   *str;            /* String to remove escaped characters from */
 304 {
 305         char   *p;              /* Temp string pointer */
 306         char   *q;              /* Temp string pointer */
 307 
 308         /* Look for an escaped character */
 309         p = str;
 310         while (*p && (*p != '\\')) p++;
 311 
 312 
 313         /* 
 314          * If there was at least one, squeeze them out 
 315          * Otherwise, don't touch the argument string 
 316          */
 317 
 318         if (*p) {
 319             q = p++;
 320             while (*q++ = *p++) if (*p == '\\') p++;
 321         }
 322 
 323         /* Finished.  Return our argument */
 324         return(str);
 325 }
 326 
 327 /*
 328  * struct sevstr *getauxsevs(ptr)
 329  *
 330  *      Parses a string that is in the format of the severity definitions.
 331  *      Returns a pointer to a (malloc'd) structure that contains the
 332  *      definition, or (struct sevstr *) NULL if none was parsed.
 333  *
 334  * Arguments:
 335  *      ptr     char *
 336  *              References the string from which data is to be extracted.
 337  *              If (char *) NULL, continue where we left off.  Otherwise,
 338  *              start with the string referenced by ptr.
 339  *
 340  * Returns: struct sevstr *
 341  *      A pointer to a malloc'd structure containing the severity definition
 342  *      parsed from string, or (struct sevstr *) NULL if none.
 343  *
 344  * Notes:
 345  *    - This function is destructive to the string referenced by its argument.
 346  */
 347 
 348 
 349 /* Static data */
 350 static  char           *leftoff = (char *) NULL;
 351 
 352 static  struct sevstr *
 353 getauxsevs(ptr)
 354         char   *ptr;
 355 {
 356 
 357         /* Automatic data */
 358         char           *current;        /* Ptr to current sev def'n */
 359         char           *tokend;         /* Ptr to end of current sev def'n */
 360         char           *kywd;           /* Ptr to extracted kywd */
 361         char           *valstr;         /* Ptr to extracted sev value */
 362         char           *prstr;          /* Ptr to extracted print str */
 363         char           *p;              /* Temp pointer */
 364         int             val;            /* Converted severity value */
 365         int             done;           /* Flag, sev def'n found and ok? */
 366         struct sevstr  *rtnval;         /* Value to return */
 367 
 368 
 369         /* Start anew or start where we left off? */
 370         current = (ptr == (char *) NULL) ? leftoff : ptr;
 371 
 372 
 373         /* If nothing to parse, return (char *) NULL */
 374         if (current == (char *) NULL) {
 375             return ((struct sevstr *) NULL);
 376         }
 377 
 378 
 379         /*
 380          * Look through the string "current" for a token of the form
 381          * <kywd>,<sev>,<printstring> delimited by ':' or '\0'
 382          */
 383 
 384         /* Loop initializations */
 385         done = FALSE;
 386         rtnval = (struct sevstr *) NULL;
 387         while (!done) {
 388 
 389             /* Eat leading junk */
 390             while (*(tokend = exttok(current, ":,")) == ':') {
 391                 current = tokend + 1;
 392             }
 393 
 394             /* If we've found a <kywd>,... */
 395             if (*tokend == ',') {
 396                 kywd = current;
 397                 *tokend = '\0';
 398 
 399                 /* Look for <kywd>,<sev>,... */
 400                 current = tokend + 1;
 401                 if (*(tokend = exttok(current, ":,")) == ',') {
 402                     valstr = current;
 403                     *tokend = '\0';
 404                     current = tokend+1;
 405                     prstr = current;
 406 
 407                     /* Make sure <sev> > 4 */
 408                     val = (int) strtol(noesc(valstr), &p, 0);
 409                     if ((val > 4) && (p == tokend)) {
 410 
 411                         /*
 412                          * Found <kywd>,<sev>,<printstring>.
 413                          * remember where we left off
 414                          */
 415 
 416                         if (*(tokend = exttok(current, ":")) == ':') {
 417                             *tokend = '\0';
 418                             leftoff = tokend + 1;
 419                         } else leftoff = (char *) NULL;
 420 
 421                         /* Alloc structure to contain severity definition */
 422                         if (rtnval = (struct sevstr *) malloc(sizeof(struct sevstr))) {
 423 
 424                             /* Fill in structure */
 425                             rtnval->sevkywd = noesc(kywd);
 426                             rtnval->sevvalue = val;
 427                             rtnval->sevprstr = noesc(prstr);
 428                             rtnval->sevnext = (struct sevstr *) NULL;
 429                         }
 430 
 431                         done = TRUE;
 432 
 433                     } else {
 434 
 435                         /* Invalid severity value, eat thru end of token */
 436                         current = tokend;
 437                         if (*(tokend = exttok(prstr, ":")) == ':')
 438                             current++;
 439                     }
 440 
 441                 } else {
 442 
 443                     /* Invalid severity definition, eat thru end of token */
 444                     current = tokend;
 445                     if (*tokend == ':')
 446                         current++;
 447                 }
 448 
 449             } else {
 450 
 451                 /* End of string found */
 452                 done = TRUE;
 453                 leftoff = (char *) NULL;
 454             }
 455 
 456         } /* while (!done) */
 457 
 458         /* Finished */
 459         return(rtnval);
 460 }
 461 
 462 /*
 463  * fmtmsg [-a action] [-c classification] [-l label] [-s severity] [-t tag]
 464  *        [-u subclass[,subclass[,...]]] [text]
 465  *
 466  * Function:
 467  *      Writes a message in the standard format.  Typically used by shell
 468  *      scripts to write error messages to the user.
 469  *
 470  * Arguments:
 471  *      text            String that is the text of the message
 472  *
 473  * Options:
 474  *   -a action          String that describes user action to take to
 475  *                      correct the situation
 476  *   -c classification  Keyword that identifies the type of the message
 477  *   -l label           String that identifies the source of the message
 478  *   -s severity        Keyword that identifies the severity of the message
 479  *   -t tag             String that identifies the message (use unclear)
 480  *   -u sub_classes     Comma-list of keywords that refines the type of
 481  *                      the message
 482  *
 483  * Environment Variables Used:
 484  *      MSGVERB         Defines the pieces of a message the user expects
 485  *                      to see.  It is a list of keywords separated by
 486  *                      colons (':').
 487  *      SEV_LEVEL       Defines a list of auxiliary severity keywords, values,
 488  *                      and print-strings.  It is a list of fields separated
 489  *                      by colons (':').  Each field consists of three
 490  *                      elements, keyword, value (in octal, hex, or decimal),
 491  *                      and print-string, separated by commas (',').
 492  *
 493  * Needs:
 494  *
 495  * Open Issues:
 496  */
 497 
 498 int
 499 main(int argc, char **argv)
 500 {
 501 
 502         /* Local automatic data */
 503 
 504         long                    class;          /* Classification (built) */
 505 
 506         int                     severity;       /* User specified severity */
 507         int                     msgrtn;         /* Value returned by fmtmsg() */
 508         int                     optchar;        /* Opt char on cmdline */
 509         int                     exitval;        /* Value to return */
 510 
 511         int                     found;          /* FLAG, kywd found yet? */
 512         int                     errflg;         /* FLAG, error seen in cmd */
 513         int                     a_seen;         /* FLAG, -a option seen */
 514         int                     c_seen;         /* FLAG, -c option seen */
 515         int                     l_seen;         /* FLAG, -l option seen */
 516         int                     s_seen;         /* FLAG, -s option seen */
 517         int                     t_seen;         /* FLAG, -t option seen */
 518         int                     u_seen;         /* FLAG, -u option seen */
 519         int                     text_seen;      /* FLAG, text seen */
 520 
 521         char                   *text;           /* Ptr to user's text */
 522         char                   *label;          /* Ptr to user's label */
 523         char                   *tag;            /* Ptr to user's tag */
 524         char                   *action;         /* Ptr to user's action str */
 525         char                   *sstr;           /* Ptr to -s (severity) arg */
 526         char                   *ustr;           /* Ptr to -u (subclass) arg */
 527         char                   *cstr;           /* Ptr to -c (class) arg */
 528         char                   *sevstrval;      /* Ptr to SEV_LEVEL argument */
 529         char                   *sevval;         /* Ptr to temp SEV_LEVEL arg */
 530         char                   *tokenptr;       /* Ptr to current token */
 531         char                   *cmdname;        /* Ptr to base command name */
 532         char                   *p;              /* Multipurpose ptr */
 533 
 534         struct class_info      *class_info;     /* Ptr to class/subclass info structure */
 535         struct sev_info        *sev_info;       /* Ptr to severity info struct */
 536         struct sevstr          *penvsev;        /* Ptr to SEV_LEVEL values */
 537 
 538 
 539 
 540         /*
 541          * fmtmsg
 542          */
 543 
 544 
 545         /* Initializations */
 546 
 547 
 548         /* Extract the base command name from the command */
 549         if ((p = strrchr(argv[0], '/')) == (char *) NULL)
 550             cmdname = argv[0];
 551         else
 552             cmdname = p+1;
 553 
 554         /* Build the label for messages from "fmtmsg" */
 555         (void) snprintf(labelbuf, sizeof (labelbuf), "UX:%s", cmdname);
 556 
 557 
 558         /*
 559          * Extract arguments from the command line
 560          */
 561 
 562         /* Initializations */
 563 
 564         opterr = 0;                     /* Disable messages from getopt() */
 565         errflg = FALSE;                 /* No errors seen yet */
 566 
 567         a_seen = FALSE;                 /* No action (-a) text seen yet */
 568         c_seen = FALSE;                 /* No classification (-c) seen yet */
 569         l_seen = FALSE;                 /* No label (-l) seen yet */
 570         s_seen = FALSE;                 /* No severity (-s) seen yet */
 571         t_seen = FALSE;                 /* No tag (-t) seen yet */
 572         u_seen = FALSE;                 /* No subclass (-u) seen yet */
 573         text_seen = FALSE;              /* No text seen yet */
 574 
 575 
 576         /*
 577          * If only the command name was used, write out a usage string to
 578          * the standard output file.
 579          */
 580 
 581         if (argc == 1) {
 582             (void) fputs(BIGUSAGE, stderr);
 583             exit(0);
 584         }
 585 
 586 
 587         /* Parce command line */
 588         while (((optchar = getopt(argc, argv, "a:c:l:s:t:u:")) != EOF) &&
 589                !errflg) {
 590 
 591             switch(optchar) {
 592 
 593             case 'a':           /* -a actiontext */
 594                 if (!a_seen) {
 595                     action = optarg;
 596                     a_seen = TRUE;
 597                 } else errflg = TRUE;
 598                 break;
 599 
 600             case 'c':           /* -c classification */
 601                 if (!c_seen) {
 602                     cstr = optarg;
 603                     c_seen = TRUE;
 604                 } else errflg = TRUE;
 605                 break;
 606 
 607             case 'l':           /* -l label */
 608                 if (!l_seen) {
 609                     label = optarg;
 610                     l_seen = TRUE;
 611                 } else errflg = TRUE;
 612                 break;
 613 
 614             case 's':           /* -s severity */
 615                 if (!s_seen) {
 616                     sstr = optarg;
 617                     s_seen = TRUE;
 618                 } else errflg = TRUE;
 619                 break;
 620 
 621             case 't':           /* -t tag */
 622                 if (!t_seen) {
 623                     tag = optarg;
 624                     t_seen = TRUE;
 625                 } else errflg = TRUE;
 626                 break;
 627 
 628             case 'u':           /* -u subclasslist */
 629                 if (!u_seen) {
 630                     ustr = optarg;
 631                     u_seen = TRUE;
 632                 } else errflg = TRUE;
 633                 break;
 634 
 635             case '?':           /* -? or unknown option */
 636             default:
 637                 errflg = TRUE;
 638                 break;
 639 
 640             } /* esac */
 641         }
 642 
 643 
 644         /* Get the text */
 645         if (!errflg) {
 646             if (argc == (optind+1)) {
 647                 text = argv[optind];
 648                 text_seen = TRUE;
 649             }
 650             else if (argc != optind) {
 651                 errflg = TRUE;
 652             }
 653         }
 654 
 655 
 656         /* Report syntax errors */
 657         if (errflg) {
 658             (void) fputs(BIGUSAGE, stderr);
 659             exit(1);
 660         }
 661 
 662 
 663         /*
 664          * Classification.
 665          */
 666 
 667         class = 0L;
 668         if (c_seen) {
 669 
 670             /* Search for keyword in list */
 671             for (class_info = &classes[0] ;
 672                  (class_info->keyword != (char *) NULL) &&
 673                  (strcmp(cstr, class_info->keyword)) ;
 674                  class_info++) ;
 675 
 676             /* If invalid (keyword unknown), write a message and exit */
 677             if (class_info->keyword == (char *) NULL) {
 678                 (void) snprintf(msgbuf, sizeof (msgbuf),
 679                         "Invalid class: %s", cstr);
 680                 (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf,
 681                               MM_NULLACT, MM_NULLTAG);
 682                 exit(1);
 683             }
 684 
 685             /* Save classification */
 686             class = class_info->value;
 687 
 688         }
 689 
 690 
 691         /*
 692          * Subclassification.
 693          */
 694 
 695         if (u_seen) {
 696 
 697             errflg = FALSE;
 698             p = strcpy(malloc((unsigned int) strlen(ustr)+1), ustr);
 699             if ((tokenptr = strtok(p, ",")) == (char *) NULL) errflg = TRUE;
 700             else do {
 701 
 702                 /* Got a keyword.  Look for it in keyword list */
 703                 for (class_info = subclasses ;
 704                      (class_info->keyword != (char *) NULL) &&
 705                      (strcmp(tokenptr, class_info->keyword) != 0) ;
 706                      class_info++) ;
 707 
 708                 /* If found in list and no conflict, remember in class */
 709                 if ((class_info->keyword != (char *) NULL) && ((class & class_info->conflict) == 0L))
 710                     class |= class_info->value;
 711                 else 
 712                     errflg = TRUE;
 713 
 714             } while (!errflg && ((tokenptr = strtok((char *) NULL, ",")) != (char *) NULL)) ;
 715 
 716             if (errflg) {
 717                 (void) snprintf(msgbuf, sizeof (msgbuf),
 718                         "Invalid subclass: %s", ustr);
 719                 (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf,
 720                               MM_NULLACT, MM_NULLTAG);
 721                 exit(1);
 722             }
 723 
 724         }
 725 
 726         if (!c_seen & !u_seen) class = MM_NULLMC;
 727 
 728 
 729 
 730         /*
 731          * Severity.
 732          */
 733 
 734         if (s_seen) {
 735 
 736             /* If the severity is specified as a number, use that value */
 737             severity = strtol(sstr, &p, 10);
 738             if (*p || (strlen(sstr) == 0)) {
 739 
 740                 /* Look for the standard severities */
 741                 for (sev_info = severities ;
 742                      (sev_info->keyword != (char *) NULL) &&
 743                      (strcmp(sstr, sev_info->keyword)) ;
 744                      sev_info++) ;
 745 
 746                 /*
 747                  * If the "severity" argument is one of the standard keywords,
 748                  * remember it for fmtmsg().  Otherwise, look at the SEV_LEVEL
 749                  * environment variable for severity extensions.
 750                  */
 751 
 752                 /* If the keyword is one of the standard ones, save severity */
 753                 if (sev_info->keyword != (char *) NULL) severity = sev_info->value;
 754 
 755                 else {
 756 
 757                     /*
 758                      * Severity keyword may be one of the extended set, if any.
 759                      */
 760 
 761                     /* Get the value of the SEV_LEVEL environment variable */
 762                     found = FALSE;
 763                     if ((sevstrval = getenv(SEV_LEVEL)) != (char *) NULL) {
 764                         sevval = (char *) malloc((unsigned int) strlen(sevstrval)+1);
 765                         penvsev = getauxsevs(strcpy(sevval, sevstrval));
 766                         if (penvsev != (struct sevstr *) NULL) do {
 767                             if (strcmp(penvsev->sevkywd, sstr) == 0) {
 768                                 severity = penvsev->sevvalue;
 769                                 found = TRUE;
 770                             }
 771                             else {
 772                                 free(penvsev);
 773                                 penvsev = getauxsevs((char *) NULL);
 774                             }
 775                         } while (!found && (penvsev != (struct sevstr *) NULL));
 776 
 777                         if (found) free(penvsev);
 778                         free(sevval);
 779                     }
 780 
 781                     if (!found) {
 782                         (void) snprintf(msgbuf, sizeof (msgbuf),
 783                                 "Invalid severity: %s", sstr);
 784                         (void) fmtmsg(CLASS, labelbuf, MM_ERROR, msgbuf,
 785                                       MM_NULLACT, MM_NULLTAG);
 786                         exit(1);
 787                     }
 788 
 789                 }  /* <severity> is not one of the standard severities */
 790 
 791             }  /* <severity> is not numeric */
 792 
 793         }  /* if (s_seen) */
 794 
 795         else severity = MM_NULLSEV;
 796 
 797 
 798         /*
 799          * Other options
 800          */
 801 
 802         if (!a_seen) action = MM_NULLACT;
 803         if (!l_seen) label = MM_NULLLBL;
 804         if (!t_seen) tag = MM_NULLTAG;
 805         if (!text_seen) text = MM_NULLTXT;
 806 
 807 
 808         /*
 809          * Write the message
 810          */
 811 
 812         msgrtn = fmtmsg(class, label, severity, text, action ,tag);
 813 
 814 
 815         /*
 816          * Return appropriate value to the shell (or wherever)
 817          */
 818 
 819         exitval = 0;
 820         if (msgrtn == MM_NOTOK) exitval = 32;
 821         else {
 822             if (msgrtn & MM_NOMSG) exitval += 2;
 823             if (msgrtn & MM_NOCON) exitval += 4;
 824         }
 825 
 826         return(exitval);
 827 }