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