home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / sendmail / sendmail-5.65 / src / readcf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-05  |  18.3 KB  |  932 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted provided
  7.  * that: (1) source distributions retain this entire copyright notice and
  8.  * comment, and (2) distributions including binaries display the following
  9.  * acknowledgement:  ``This product includes software developed by the
  10.  * University of California, Berkeley and its contributors'' in the
  11.  * documentation or other materials provided with the distribution and in
  12.  * all advertising materials mentioning features or use of this software.
  13.  * Neither the name of the University nor the names of its contributors may
  14.  * be used to endorse or promote products derived from this software without
  15.  * specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  17.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  18.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  */
  20.  
  21. #ifndef lint
  22. static char sccsid[] = "@(#)readcf.c    5.21 (Berkeley) 6/1/90";
  23. #endif /* not lint */
  24.  
  25. # include "sendmail.h"
  26.  
  27. /*
  28. **  READCF -- read control file.
  29. **
  30. **    This routine reads the control file and builds the internal
  31. **    form.
  32. **
  33. **    The file is formatted as a sequence of lines, each taken
  34. **    atomically.  The first character of each line describes how
  35. **    the line is to be interpreted.  The lines are:
  36. **        Dxval        Define macro x to have value val.
  37. **        Cxword        Put word into class x.
  38. **        Fxfile [fmt]    Read file for lines to put into
  39. **                class x.  Use scanf string 'fmt'
  40. **                or "%s" if not present.  Fmt should
  41. **                only produce one string-valued result.
  42. **        Hname: value    Define header with field-name 'name'
  43. **                and value as specified; this will be
  44. **                macro expanded immediately before
  45. **                use.
  46. **        Sn        Use rewriting set n.
  47. **        Rlhs rhs    Rewrite addresses that match lhs to
  48. **                be rhs.
  49. **        Mn arg=val...    Define mailer.  n is the internal name.
  50. **                Args specify mailer parameters.
  51. **        Oxvalue        Set option x to value.
  52. **        Pname=value    Set precedence name to value.
  53. **
  54. **    Parameters:
  55. **        cfname -- control file name.
  56. **
  57. **    Returns:
  58. **        none.
  59. **
  60. **    Side Effects:
  61. **        Builds several internal tables.
  62. */
  63.  
  64. readcf(cfname)
  65.     char *cfname;
  66. {
  67.     FILE *cf;
  68.     int ruleset = 0;
  69.     char *q;
  70.     char **pv;
  71.     struct rewrite *rwp = NULL;
  72.     char buf[MAXLINE];
  73.     register char *p;
  74.     extern char **prescan();
  75.     extern char **copyplist();
  76.     char exbuf[MAXLINE];
  77.     char pvpbuf[PSBUFSIZE];
  78.     extern char *fgetfolded();
  79.     extern char *munchstring();
  80.  
  81.     cf = fopen(cfname, "r");
  82.     if (cf == NULL)
  83.     {
  84.         syserr("cannot open %s", cfname);
  85.         exit(EX_OSFILE);
  86.     }
  87.  
  88.     FileName = cfname;
  89.     LineNumber = 0;
  90.     while (fgetfolded(buf, sizeof buf, cf) != NULL)
  91.     {
  92.         /* map $ into \001 (ASCII SOH) for macro expansion */
  93.         for (p = buf; *p != '\0'; p++)
  94.         {
  95.             if (*p != '$')
  96.                 continue;
  97.  
  98.             if (p[1] == '$')
  99.             {
  100.                 /* actual dollar sign.... */
  101.                 (void) strcpy(p, p + 1);
  102.                 continue;
  103.             }
  104.  
  105.             /* convert to macro expansion character */
  106.             *p = '\001';
  107.         }
  108.  
  109.         /* interpret this line */
  110.         switch (buf[0])
  111.         {
  112.           case '\0':
  113.           case '#':        /* comment */
  114.             break;
  115.  
  116.           case 'R':        /* rewriting rule */
  117.             for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
  118.                 continue;
  119.  
  120.             if (*p == '\0')
  121.             {
  122.                 syserr("invalid rewrite line \"%s\"", buf);
  123.                 break;
  124.             }
  125.  
  126.             /* allocate space for the rule header */
  127.             if (rwp == NULL)
  128.             {
  129.                 RewriteRules[ruleset] = rwp =
  130.                     (struct rewrite *) xalloc(sizeof *rwp);
  131.             }
  132.             else
  133.             {
  134.                 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
  135.                 rwp = rwp->r_next;
  136.             }
  137.             rwp->r_next = NULL;
  138.  
  139.             /* expand and save the LHS */
  140.             *p = '\0';
  141.             expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
  142.             rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
  143.             if (rwp->r_lhs != NULL)
  144.                 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
  145.  
  146.             /* expand and save the RHS */
  147.             while (*++p == '\t')
  148.                 continue;
  149.             q = p;
  150.             while (*p != '\0' && *p != '\t')
  151.                 p++;
  152.             *p = '\0';
  153.             expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
  154.             rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
  155.             if (rwp->r_rhs != NULL)
  156.                 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
  157.             break;
  158.  
  159.           case 'S':        /* select rewriting set */
  160.             ruleset = atoi(&buf[1]);
  161.             if (ruleset >= MAXRWSETS || ruleset < 0)
  162.             {
  163.                 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
  164.                 ruleset = 0;
  165.             }
  166.             rwp = NULL;
  167.             break;
  168.  
  169.           case 'D':        /* macro definition */
  170.             define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
  171.             break;
  172.  
  173.           case 'H':        /* required header line */
  174.             (void) chompheader(&buf[1], TRUE);
  175.             break;
  176.  
  177.           case 'C':        /* word class */
  178.           case 'F':        /* word class from file */
  179.             /* read list of words from argument or file */
  180.             if (buf[0] == 'F')
  181.             {
  182.                 /* read from file */
  183.                 for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
  184.                     continue;
  185.                 if (*p == '\0')
  186.                     p = "%s";
  187.                 else
  188.                 {
  189.                     *p = '\0';
  190.                     while (isspace(*++p))
  191.                         continue;
  192.                 }
  193.                 fileclass(buf[1], &buf[2], p);
  194.                 break;
  195.             }
  196.  
  197.             /* scan the list of words and set class for all */
  198.             for (p = &buf[2]; *p != '\0'; )
  199.             {
  200.                 register char *wd;
  201.                 char delim;
  202.  
  203.                 while (*p != '\0' && isspace(*p))
  204.                     p++;
  205.                 wd = p;
  206.                 while (*p != '\0' && !isspace(*p))
  207.                     p++;
  208.                 delim = *p;
  209.                 *p = '\0';
  210.                 if (wd[0] != '\0')
  211.                     setclass(buf[1], wd);
  212.                 *p = delim;
  213.             }
  214.             break;
  215.  
  216.           case 'M':        /* define mailer */
  217.             makemailer(&buf[1]);
  218.             break;
  219.  
  220.           case 'O':        /* set option */
  221.             setoption(buf[1], &buf[2], TRUE, FALSE);
  222.             break;
  223.  
  224.           case 'P':        /* set precedence */
  225.             if (NumPriorities >= MAXPRIORITIES)
  226.             {
  227.                 toomany('P', MAXPRIORITIES);
  228.                 break;
  229.             }
  230.             for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
  231.                 continue;
  232.             if (*p == '\0')
  233.                 goto badline;
  234.             *p = '\0';
  235.             Priorities[NumPriorities].pri_name = newstr(&buf[1]);
  236.             Priorities[NumPriorities].pri_val = atoi(++p);
  237.             NumPriorities++;
  238.             break;
  239.  
  240.           case 'T':        /* trusted user(s) */
  241.             p = &buf[1];
  242.             while (*p != '\0')
  243.             {
  244.                 while (isspace(*p))
  245.                     p++;
  246.                 q = p;
  247.                 while (*p != '\0' && !isspace(*p))
  248.                     p++;
  249.                 if (*p != '\0')
  250.                     *p++ = '\0';
  251.                 if (*q == '\0')
  252.                     continue;
  253.                 for (pv = TrustedUsers; *pv != NULL; pv++)
  254.                     continue;
  255.                 if (pv >= &TrustedUsers[MAXTRUST])
  256.                 {
  257.                     toomany('T', MAXTRUST);
  258.                     break;
  259.                 }
  260.                 *pv = newstr(q);
  261.             }
  262.             break;
  263.  
  264.           default:
  265.           badline:
  266.             syserr("unknown control line \"%s\"", buf);
  267.         }
  268.     }
  269.     FileName = NULL;
  270. }
  271. /*
  272. **  TOOMANY -- signal too many of some option
  273. **
  274. **    Parameters:
  275. **        id -- the id of the error line
  276. **        maxcnt -- the maximum possible values
  277. **
  278. **    Returns:
  279. **        none.
  280. **
  281. **    Side Effects:
  282. **        gives a syserr.
  283. */
  284.  
  285. toomany(id, maxcnt)
  286.     char id;
  287.     int maxcnt;
  288. {
  289.     syserr("too many %c lines, %d max", id, maxcnt);
  290. }
  291. /*
  292. **  FILECLASS -- read members of a class from a file
  293. **
  294. **    Parameters:
  295. **        class -- class to define.
  296. **        filename -- name of file to read.
  297. **        fmt -- scanf string to use for match.
  298. **
  299. **    Returns:
  300. **        none
  301. **
  302. **    Side Effects:
  303. **
  304. **        puts all lines in filename that match a scanf into
  305. **            the named class.
  306. */
  307.  
  308. fileclass(class, filename, fmt)
  309.     int class;
  310.     char *filename;
  311.     char *fmt;
  312. {
  313.     FILE *f;
  314.     char buf[MAXLINE];
  315.  
  316.     f = fopen(filename, "r");
  317.     if (f == NULL)
  318.     {
  319.         syserr("cannot open %s", filename);
  320.         return;
  321.     }
  322.  
  323.     while (fgets(buf, sizeof buf, f) != NULL)
  324.     {
  325.         register STAB *s;
  326.         register char *p;
  327. # ifdef SCANF
  328.         char wordbuf[MAXNAME+1];
  329.  
  330.         if (sscanf(buf, fmt, wordbuf) != 1)
  331.             continue;
  332.         p = wordbuf;
  333. # else SCANF
  334.         p = buf;
  335. # endif SCANF
  336.  
  337.         /*
  338.         **  Break up the match into words.
  339.         */
  340.  
  341.         while (*p != '\0')
  342.         {
  343.             register char *q;
  344.  
  345.             /* strip leading spaces */
  346.             while (isspace(*p))
  347.                 p++;
  348.             if (*p == '\0')
  349.                 break;
  350.  
  351.             /* find the end of the word */
  352.             q = p;
  353.             while (*p != '\0' && !isspace(*p))
  354.                 p++;
  355.             if (*p != '\0')
  356.                 *p++ = '\0';
  357.  
  358.             /* enter the word in the symbol table */
  359.             s = stab(q, ST_CLASS, ST_ENTER);
  360.             setbitn(class, s->s_class);
  361.         }
  362.     }
  363.  
  364.     (void) fclose(f);
  365. }
  366. /*
  367. **  MAKEMAILER -- define a new mailer.
  368. **
  369. **    Parameters:
  370. **        line -- description of mailer.  This is in labeled
  371. **            fields.  The fields are:
  372. **               P -- the path to the mailer
  373. **               F -- the flags associated with the mailer
  374. **               A -- the argv for this mailer
  375. **               S -- the sender rewriting set
  376. **               R -- the recipient rewriting set
  377. **               E -- the eol string
  378. **            The first word is the canonical name of the mailer.
  379. **
  380. **    Returns:
  381. **        none.
  382. **
  383. **    Side Effects:
  384. **        enters the mailer into the mailer table.
  385. */
  386.  
  387. makemailer(line)
  388.     char *line;
  389. {
  390.     register char *p;
  391.     register struct mailer *m;
  392.     register STAB *s;
  393.     int i;
  394.     char fcode;
  395.     extern int NextMailer;
  396.     extern char **makeargv();
  397.     extern char *munchstring();
  398.     extern char *DelimChar;
  399.     extern long atol();
  400.  
  401.     /* allocate a mailer and set up defaults */
  402.     m = (struct mailer *) xalloc(sizeof *m);
  403.     bzero((char *) m, sizeof *m);
  404.     m->m_mno = NextMailer;
  405.     m->m_eol = "\n";
  406.  
  407.     /* collect the mailer name */
  408.     for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
  409.         continue;
  410.     if (*p != '\0')
  411.         *p++ = '\0';
  412.     m->m_name = newstr(line);
  413.  
  414.     /* now scan through and assign info from the fields */
  415.     while (*p != '\0')
  416.     {
  417.         while (*p != '\0' && (*p == ',' || isspace(*p)))
  418.             p++;
  419.  
  420.         /* p now points to field code */
  421.         fcode = *p;
  422.         while (*p != '\0' && *p != '=' && *p != ',')
  423.             p++;
  424.         if (*p++ != '=')
  425.         {
  426.             syserr("`=' expected");
  427.             return;
  428.         }
  429.         while (isspace(*p))
  430.             p++;
  431.  
  432.         /* p now points to the field body */
  433.         p = munchstring(p);
  434.  
  435.         /* install the field into the mailer struct */
  436.         switch (fcode)
  437.         {
  438.           case 'P':        /* pathname */
  439.             m->m_mailer = newstr(p);
  440.             break;
  441.  
  442.           case 'F':        /* flags */
  443.             for (; *p != '\0'; p++)
  444.                 setbitn(*p, m->m_flags);
  445.             break;
  446.  
  447.           case 'S':        /* sender rewriting ruleset */
  448.           case 'R':        /* recipient rewriting ruleset */
  449.             i = atoi(p);
  450.             if (i < 0 || i >= MAXRWSETS)
  451.             {
  452.                 syserr("invalid rewrite set, %d max", MAXRWSETS);
  453.                 return;
  454.             }
  455.             if (fcode == 'S')
  456.                 m->m_s_rwset = i;
  457.             else
  458.                 m->m_r_rwset = i;
  459.             break;
  460.  
  461.           case 'E':        /* end of line string */
  462.             m->m_eol = newstr(p);
  463.             break;
  464.  
  465.           case 'A':        /* argument vector */
  466.             m->m_argv = makeargv(p);
  467.             break;
  468.  
  469.           case 'M':        /* maximum message size */
  470.             m->m_maxsize = atol(p);
  471.             break;
  472.         }
  473.  
  474.         p = DelimChar;
  475.     }
  476.  
  477.     /* now store the mailer away */
  478.     if (NextMailer >= MAXMAILERS)
  479.     {
  480.         syserr("too many mailers defined (%d max)", MAXMAILERS);
  481.         return;
  482.     }
  483.     Mailer[NextMailer++] = m;
  484.     s = stab(m->m_name, ST_MAILER, ST_ENTER);
  485.     s->s_mailer = m;
  486. }
  487. /*
  488. **  MUNCHSTRING -- translate a string into internal form.
  489. **
  490. **    Parameters:
  491. **        p -- the string to munch.
  492. **
  493. **    Returns:
  494. **        the munched string.
  495. **
  496. **    Side Effects:
  497. **        Sets "DelimChar" to point to the string that caused us
  498. **        to stop.
  499. */
  500.  
  501. char *
  502. munchstring(p)
  503.     register char *p;
  504. {
  505.     register char *q;
  506.     bool backslash = FALSE;
  507.     bool quotemode = FALSE;
  508.     static char buf[MAXLINE];
  509.     extern char *DelimChar;
  510.  
  511.     for (q = buf; *p != '\0'; p++)
  512.     {
  513.         if (backslash)
  514.         {
  515.             /* everything is roughly literal */
  516.             backslash = FALSE;
  517.             switch (*p)
  518.             {
  519.               case 'r':        /* carriage return */
  520.                 *q++ = '\r';
  521.                 continue;
  522.  
  523.               case 'n':        /* newline */
  524.                 *q++ = '\n';
  525.                 continue;
  526.  
  527.               case 'f':        /* form feed */
  528.                 *q++ = '\f';
  529.                 continue;
  530.  
  531.               case 'b':        /* backspace */
  532.                 *q++ = '\b';
  533.                 continue;
  534.             }
  535.             *q++ = *p;
  536.         }
  537.         else
  538.         {
  539.             if (*p == '\\')
  540.                 backslash = TRUE;
  541.             else if (*p == '"')
  542.                 quotemode = !quotemode;
  543.             else if (quotemode || *p != ',')
  544.                 *q++ = *p;
  545.             else
  546.                 break;
  547.         }
  548.     }
  549.  
  550.     DelimChar = p;
  551.     *q++ = '\0';
  552.     return (buf);
  553. }
  554. /*
  555. **  MAKEARGV -- break up a string into words
  556. **
  557. **    Parameters:
  558. **        p -- the string to break up.
  559. **
  560. **    Returns:
  561. **        a char **argv (dynamically allocated)
  562. **
  563. **    Side Effects:
  564. **        munges p.
  565. */
  566.  
  567. char **
  568. makeargv(p)
  569.     register char *p;
  570. {
  571.     char *q;
  572.     int i;
  573.     char **avp;
  574.     char *argv[MAXPV + 1];
  575.  
  576.     /* take apart the words */
  577.     i = 0;
  578.     while (*p != '\0' && i < MAXPV)
  579.     {
  580.         q = p;
  581.         while (*p != '\0' && !isspace(*p))
  582.             p++;
  583.         while (isspace(*p))
  584.             *p++ = '\0';
  585.         argv[i++] = newstr(q);
  586.     }
  587.     argv[i++] = NULL;
  588.  
  589.     /* now make a copy of the argv */
  590.     avp = (char **) xalloc(sizeof *avp * i);
  591.     bcopy((char *) argv, (char *) avp, sizeof *avp * i);
  592.  
  593.     return (avp);
  594. }
  595. /*
  596. **  PRINTRULES -- print rewrite rules (for debugging)
  597. **
  598. **    Parameters:
  599. **        none.
  600. **
  601. **    Returns:
  602. **        none.
  603. **
  604. **    Side Effects:
  605. **        prints rewrite rules.
  606. */
  607.  
  608. printrules()
  609. {
  610.     register struct rewrite *rwp;
  611.     register int ruleset;
  612.  
  613.     for (ruleset = 0; ruleset < 10; ruleset++)
  614.     {
  615.         if (RewriteRules[ruleset] == NULL)
  616.             continue;
  617.         printf("\n----Rule Set %d:", ruleset);
  618.  
  619.         for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
  620.         {
  621.             printf("\nLHS:");
  622.             printav(rwp->r_lhs);
  623.             printf("RHS:");
  624.             printav(rwp->r_rhs);
  625.         }
  626.     }
  627. }
  628.  
  629. /*
  630. **  SETOPTION -- set global processing option
  631. **
  632. **    Parameters:
  633. **        opt -- option name.
  634. **        val -- option value (as a text string).
  635. **        safe -- set if this came from a configuration file.
  636. **            Some options (if set from the command line) will
  637. **            reset the user id to avoid security problems.
  638. **        sticky -- if set, don't let other setoptions override
  639. **            this value.
  640. **
  641. **    Returns:
  642. **        none.
  643. **
  644. **    Side Effects:
  645. **        Sets options as implied by the arguments.
  646. */
  647.  
  648. static BITMAP    StickyOpt;        /* set if option is stuck */
  649. extern char    *NetName;        /* name of home (local) network */
  650.  
  651. setoption(opt, val, safe, sticky)
  652.     char opt;
  653.     char *val;
  654.     bool safe;
  655.     bool sticky;
  656. {
  657.     extern bool atobool();
  658.     extern time_t convtime();
  659.     extern int QueueLA;
  660.     extern int RefuseLA;
  661.     extern bool trusteduser();
  662.     extern char *username();
  663.  
  664.     if (tTd(37, 1))
  665.         printf("setoption %c=%s", opt, val);
  666.  
  667.     /*
  668.     **  See if this option is preset for us.
  669.     */
  670.  
  671.     if (bitnset(opt, StickyOpt))
  672.     {
  673.         if (tTd(37, 1))
  674.             printf(" (ignored)\n");
  675.         return;
  676.     }
  677.  
  678.     /*
  679.     **  Check to see if this option can be specified by this user.
  680.     */
  681.  
  682.     if (!safe && getuid() == 0)
  683.         safe = TRUE;
  684.     if (!safe && index("deiLmorsv", opt) == NULL)
  685.     {
  686.         if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
  687.         {
  688.             if (tTd(37, 1))
  689.                 printf(" (unsafe)");
  690.             if (getuid() != geteuid())
  691.             {
  692.                 printf("(Resetting uid)\n");
  693.                 (void) setgid(getgid());
  694.                 (void) setuid(getuid());
  695.             }
  696.         }
  697.     }
  698.     else if (tTd(37, 1))
  699.         printf("\n");
  700.  
  701.     switch (opt)
  702.     {
  703.       case 'A':        /* set default alias file */
  704.         if (val[0] == '\0')
  705.             AliasFile = "aliases";
  706.         else
  707.             AliasFile = newstr(val);
  708.         break;
  709.  
  710.       case 'a':        /* look N minutes for "@:@" in alias file */
  711.         if (val[0] == '\0')
  712.             SafeAlias = 5;
  713.         else
  714.             SafeAlias = atoi(val);
  715.         break;
  716.  
  717.       case 'B':        /* substitution for blank character */
  718.         SpaceSub = val[0];
  719.         if (SpaceSub == '\0')
  720.             SpaceSub = ' ';
  721.         break;
  722.  
  723.       case 'c':        /* don't connect to "expensive" mailers */
  724.         NoConnect = atobool(val);
  725.         break;
  726.  
  727.       case 'C':        /* checkpoint after N connections */
  728.         CheckPointLimit = atoi(val);
  729.         break;
  730.  
  731.       case 'd':        /* delivery mode */
  732.         switch (*val)
  733.         {
  734.           case '\0':
  735.             SendMode = SM_DELIVER;
  736.             break;
  737.  
  738.           case SM_QUEUE:    /* queue only */
  739. #ifndef QUEUE
  740.             syserr("need QUEUE to set -odqueue");
  741. #endif QUEUE
  742.             /* fall through..... */
  743.  
  744.           case SM_DELIVER:    /* do everything */
  745.           case SM_FORK:        /* fork after verification */
  746.             SendMode = *val;
  747.             break;
  748.  
  749.           default:
  750.             syserr("Unknown delivery mode %c", *val);
  751.             exit(EX_USAGE);
  752.         }
  753.         break;
  754.  
  755.       case 'D':        /* rebuild alias database as needed */
  756.         AutoRebuild = atobool(val);
  757.         break;
  758.  
  759.       case 'e':        /* set error processing mode */
  760.         switch (*val)
  761.         {
  762.           case EM_QUIET:    /* be silent about it */
  763.           case EM_MAIL:        /* mail back */
  764.           case EM_BERKNET:    /* do berknet error processing */
  765.           case EM_WRITE:    /* write back (or mail) */
  766.             HoldErrs = TRUE;
  767.             /* fall through... */
  768.  
  769.           case EM_PRINT:    /* print errors normally (default) */
  770.             ErrorMode = *val;
  771.             break;
  772.         }
  773.         break;
  774.  
  775.       case 'F':        /* file mode */
  776.         FileMode = atooct(val) & 0777;
  777.         break;
  778.  
  779.       case 'f':        /* save Unix-style From lines on front */
  780.         SaveFrom = atobool(val);
  781.         break;
  782.  
  783.       case 'g':        /* default gid */
  784.         DefGid = atoi(val);
  785.         break;
  786.  
  787.       case 'H':        /* help file */
  788.         if (val[0] == '\0')
  789.             HelpFile = "sendmail.hf";
  790.         else
  791.             HelpFile = newstr(val);
  792.         break;
  793.  
  794.       case 'I':        /* use internet domain name server */
  795.         UseNameServer = atobool(val);
  796.         break;
  797.  
  798.       case 'i':        /* ignore dot lines in message */
  799.         IgnrDot = atobool(val);
  800.         break;
  801.  
  802.       case 'L':        /* log level */
  803.         LogLevel = atoi(val);
  804.         break;
  805.  
  806.       case 'M':        /* define macro */
  807.         define(val[0], newstr(&val[1]), CurEnv);
  808.         sticky = FALSE;
  809.         break;
  810.  
  811.       case 'm':        /* send to me too */
  812.         MeToo = atobool(val);
  813.         break;
  814.  
  815.       case 'n':        /* validate RHS in newaliases */
  816.         CheckAliases = atobool(val);
  817.         break;
  818.  
  819. # ifdef DAEMON
  820.       case 'N':        /* home (local?) network name */
  821.         NetName = newstr(val);
  822.         break;
  823. # endif DAEMON
  824.  
  825.       case 'o':        /* assume old style headers */
  826.         if (atobool(val))
  827.             CurEnv->e_flags |= EF_OLDSTYLE;
  828.         else
  829.             CurEnv->e_flags &= ~EF_OLDSTYLE;
  830.         break;
  831.  
  832.       case 'P':        /* postmaster copy address for returned mail */
  833.         PostMasterCopy = newstr(val);
  834.         break;
  835.  
  836.       case 'q':        /* slope of queue only function */
  837.         QueueFactor = atoi(val);
  838.         break;
  839.  
  840.       case 'Q':        /* queue directory */
  841.         if (val[0] == '\0')
  842.             QueueDir = "mqueue";
  843.         else
  844.             QueueDir = newstr(val);
  845.         break;
  846.  
  847.       case 'r':        /* read timeout */
  848.         ReadTimeout = convtime(val);
  849.         break;
  850.  
  851.       case 'S':        /* status file */
  852.         if (val[0] == '\0')
  853.             StatFile = "sendmail.st";
  854.         else
  855.             StatFile = newstr(val);
  856.         break;
  857.  
  858.       case 's':        /* be super safe, even if expensive */
  859.         SuperSafe = atobool(val);
  860.         break;
  861.  
  862.       case 'T':        /* queue timeout */
  863.         TimeOut = convtime(val);
  864.         /*FALLTHROUGH*/
  865.  
  866.       case 't':        /* time zone name */
  867.         break;
  868.  
  869.       case 'u':        /* set default uid */
  870.         DefUid = atoi(val);
  871.         setdefuser();
  872.         break;
  873.  
  874.       case 'v':        /* run in verbose mode */
  875.         Verbose = atobool(val);
  876.         break;
  877.  
  878.       case 'x':        /* load avg at which to auto-queue msgs */
  879.         QueueLA = atoi(val);
  880.         break;
  881.  
  882.       case 'X':        /* load avg at which to auto-reject connections */
  883.         RefuseLA = atoi(val);
  884.         break;
  885.  
  886.       case 'y':        /* work recipient factor */
  887.         WkRecipFact = atoi(val);
  888.         break;
  889.  
  890.       case 'Y':        /* fork jobs during queue runs */
  891.         ForkQueueRuns = atobool(val);
  892.         break;
  893.  
  894.       case 'z':        /* work message class factor */
  895.         WkClassFact = atoi(val);
  896.         break;
  897.  
  898.       case 'Z':        /* work time factor */
  899.         WkTimeFact = atoi(val);
  900.         break;
  901.  
  902.       default:
  903.         break;
  904.     }
  905.     if (sticky)
  906.         setbitn(opt, StickyOpt);
  907.     return;
  908. }
  909. /*
  910. **  SETCLASS -- set a word into a class
  911. **
  912. **    Parameters:
  913. **        class -- the class to put the word in.
  914. **        word -- the word to enter
  915. **
  916. **    Returns:
  917. **        none.
  918. **
  919. **    Side Effects:
  920. **        puts the word into the symbol table.
  921. */
  922.  
  923. setclass(class, word)
  924.     int class;
  925.     char *word;
  926. {
  927.     register STAB *s;
  928.  
  929.     s = stab(word, ST_CLASS, ST_ENTER);
  930.     setbitn(class, s->s_class);
  931. }
  932.