home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / TEXT / UTILITY / CAWF2.ZIP / PASS2.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-06-04  |  28.3 KB  |  1,266 lines

  1. /*
  2.  *    pass2.c - cawf(1) pass 2 function
  3.  */
  4.  
  5. /*
  6.  *    Copyright (c) 1991 Purdue University Research Foundation,
  7.  *    West Lafayette, Indiana 47907.  All rights reserved.
  8.  *
  9.  *    Written by Victor A. Abell <abe@mace.cc.purdue.edu>,  Purdue
  10.  *    University Computing Center.  Not derived from licensed software;
  11.  *    derived from awf(1) by Henry Spencer of the University of Toronto.
  12.  *
  13.  *    Permission is granted to anyone to use this software for any
  14.  *    purpose on any computer system, and to alter it and redistribute
  15.  *    it freely, subject to the following restrictions:
  16.  *
  17.  *    1. The author is not responsible for any consequences of use of
  18.  *       this software, even if they arise from flaws in it.
  19.  *
  20.  *    2. The origin of this software must not be misrepresented, either
  21.  *       by explicit claim or by omission.  Credits must appear in the
  22.  *       documentation.
  23.  *
  24.  *    3. Altered versions must be plainly marked as such, and must not
  25.  *       be misrepresented as being the original software.  Credits must
  26.  *       appear in the documentation.
  27.  *
  28.  *    4. This notice may not be removed or altered.
  29.  */
  30.  
  31. #include "cawf.h"
  32. #ifdef    UNIX
  33. #ifdef    USG
  34. #include <string.h>
  35. #else
  36. #include <strings.h>
  37. #endif
  38. #else
  39. #include <string.h>
  40. #endif
  41. #include <ctype.h>
  42.  
  43. /*
  44.  * Pass2(line) - process the nroff commands in a line and break
  45.  *         text into words for pass 3
  46.  */
  47.  
  48. void
  49. Pass2(line)
  50.     char *line;
  51. {
  52.     char buf[MAXLINE];        /* working buffer */
  53.     char c;                /* character buffer */
  54.     double d;            /* temporary double */
  55.     double exscale;            /* expression scaling factor */
  56.     double expr[MAXEXP];            /* expressions */
  57.     char exsign[MAXEXP];            /* expression signs */
  58.     int i, j, k;            /* temporary indexes */
  59.     int inword;            /* word processing status */
  60.     int nexpr;            /* number of expressions */
  61.     char nm[4];            /* name */
  62.     int nsp;            /* number of spaces */
  63.     char op;            /* expression term operator */
  64.     char opstack[MAXSP];        /* expression operation stack */
  65.     char period;            /* end of word status */
  66.     char *s1, *s2, *s3, *s4, *s5;    /* temporary string pointers */
  67.     double sexpr[MAXEXP];           /* signed expressions */
  68.     int sp;                /* expression stack pointer */
  69.     char ssign;            /* expression's starting sign */
  70.     int tabpos;            /* tab position */
  71.     double tscale;            /* term scaling factor */
  72.     double tval;            /* term value */
  73.     double val;            /* term value */
  74.     double valstack[MAXSP];        /* expression value stack */
  75.     char xbuf[MAXLINE];        /* expansion buffer */
  76.  
  77.     if (line == NULL) {
  78.     /*
  79.      * End of macro expansion.
  80.      */
  81.         Pass3(DOBREAK, "need", NULL, 999);    /* flush page */
  82.         return;
  83.     }
  84.     /*
  85.      * Adjust line number.
  86.      */
  87.     if (Lockil == 0)
  88.         P2il++;
  89.     /*
  90.      * Empty line - "^[ \t]*$".
  91.      */
  92.     if (regexec(Pat[6].pat, line)) {
  93.         Pass3(DOBREAK, "space", NULL, 0);
  94.         return;
  95.     }
  96.     /*
  97.      * Line begins with white space.
  98.      */
  99.     if (*line == ' ' || *line == '\t') {
  100.         Pass3(DOBREAK, "flush", NULL, 0);
  101.         Pass3(0, "", NULL, 0);
  102.     }
  103.     /*
  104.      * Line contains text (not an nroff command).
  105.      */
  106.     if (*line != '.') {
  107.         if (Font[0] == 'R' && Backc == 0 && Aftnxt == NULL
  108.         &&  regexec(Pat[7].pat, line) == 0) {
  109.             /*
  110.              * The font is Roman, there is no "\\c" or "after next"
  111.              * trap pending and and the line has no '\\', '\t', '-',
  112.              * or "  "  (regular expression "\\|\t|-|  ").
  113.              *
  114.              * Output each word of the line as "<length> <word>".
  115.              */
  116.             for (s1 = line;;) {
  117.                 while (*s1 && *s1 == ' ')
  118.                     s1++;
  119.                 if (*s1 == '\0')
  120.                     break;
  121.                 for (s2 = s1, s3 = buf; *s2 && *s2 != ' ';)
  122.                     *s3++ = Trtbl[(int)*s2++];
  123.                 *s3 = '\0';
  124.                 Pass3((s2 - s1), buf, NULL, 0);
  125.                 s1 = *s2 ? ++s2 : s2;
  126.             }
  127.             /*
  128.              * Line terminates with punctuation and optional
  129.              * bracketing (regular expression "[.!?:][\])'\"*]*$").
  130.              */
  131.             if (regexec(Pat[8].pat, line))
  132.                 Pass3(NOBREAK, "gap", NULL, 2);
  133.             if (Centering > 0) {
  134.                 Pass3(DOBREAK,"center", NULL, 0);
  135.                 Centering--;
  136.             } else if (Fill == 0)
  137.                 Pass3(DOBREAK, "flush", NULL, 0);
  138.             return;
  139.         }
  140.         /*
  141.          * Line must be scanned a character at a time.
  142.          */
  143.         inword = nsp = tabpos = 0;
  144.         period = '\0';
  145.         for (s1 = line;; s1++) {
  146.             /*
  147.              * Space or TAB causes state transition.
  148.              */
  149.             if (*s1 == '\0' || *s1 == ' ' || *s1 == '\t') {
  150.                 if (inword) {
  151.                     if (!Backc) {
  152.                         Word[Wordx] = '\0';
  153.                         Pass3(Wordl, Word, NULL, 0);
  154.                         if (Uhyph) {
  155.                             Pass3(NOBREAK, "nohyphen",
  156.                             NULL, 0);
  157.                         }
  158.                     }
  159.                     inword = 0;
  160.                     nsp = 0;
  161.                 }
  162.                 if (*s1 == '\0')
  163.                     break;
  164.             } else {
  165.                 if (inword == 0) {
  166.                     if (Backc == 0) {
  167.                         Wordl = Wordx = 0;
  168.                         Uhyph = 0;
  169.                     }
  170.                     Backc = 0;
  171.                     inword = 1;
  172.                     if (nsp > 1) {
  173.                         Pass3(NOBREAK, "gap", NULL,
  174.                             nsp);
  175.                     }
  176.                 }
  177.             }
  178.             /*
  179.              * Process a character.
  180.              */
  181.             switch (*s1) {
  182.             /*
  183.              * Space
  184.              */
  185.                  case ' ':
  186.                 nsp++;
  187.                 period = '\0';
  188.                 break;
  189.             /*
  190.              * TAB
  191.              */
  192.                  case '\t':
  193.                 tabpos++;
  194.                 if (tabpos <= Ntabs) {
  195.                     Pass3(NOBREAK, "tabto", NULL,
  196.                         Tabs[tabpos-1]);
  197.                 }
  198.                 nsp = 0;
  199.                 period = '\0';
  200.                 break;
  201.             /*
  202.              * Hyphen if word is being assembled
  203.              */
  204.             case '-':
  205.                 if (Wordl <= 0)
  206.                     goto ordinary_char;
  207.                 if ((i = Findhy(NULL, 0, 0)) < 0) {
  208.                     Error(WARN, LINE, " no hyphen for font ",
  209.                     Font);
  210.                     return;
  211.                 }
  212.                 Word[Wordx] = '\0';
  213.                 Pass3(Wordl, Word, NULL, Hychar[i].len);
  214.                 Pass3(NOBREAK, "userhyphen", Hychar[i].str,
  215.                     Hychar[i].len);
  216.                 Wordl = Wordx = 0;
  217.                 period = '\0';
  218.                 Uhyph = 1;
  219.                 break;
  220.             /*
  221.              * Backslash
  222.              */
  223.             case '\\':
  224.                 s1++;
  225.                 switch(*s1) {
  226.                 /*
  227.                  * Change font - "\\fN"
  228.                  */
  229.                 case 'f':
  230.                     s1 = Asmcode(&s1, nm);
  231.                     if (nm[0] == 'P') {
  232.                         Font[0] = Prevfont;
  233.                         break;
  234.                     }
  235.                     for (i = 0; Fcode[i].nm; i++) {
  236.                         if (Fcode[i].nm == nm[0])
  237.                         break;
  238.                     }
  239.                     if (Fcode[i].nm == '\0'
  240.                     ||  nm[1] != '\0') {
  241.                         Error(WARN, LINE, " unknown font ",
  242.                             nm);
  243.                         break;
  244.                     }
  245.                     if (Fcode[i].status != '1') {
  246.                         Error(WARN, LINE,
  247.                         " font undefined ", nm);
  248.                         break;
  249.                     } else {
  250.                         Prevfont = Font[0];
  251.                         Font[0] = nm[0];
  252.                     }
  253.                     break;
  254.                 /*
  255.                  * Positive horizontal motion - "\\h\\n(NN" or
  256.                  * "\\h\\nN"
  257.                  */
  258.                 case 'h':
  259.                     if (s1[1] != '\\' || s1[2] != 'n') {
  260.                         Error(WARN, LINE,
  261.                         " no \\n after \\h", NULL);
  262.                         break;
  263.                     }
  264.                     s1 +=2;
  265.                     s1 = Asmcode(&s1, nm);
  266.                     if ((i = Findnum(nm, 0, 0)) < 0)
  267.                         goto unknown_num;
  268.                     if ((j = Numb[i].val) < 0) {
  269.                         Error(WARN, LINE, " \\h < 0 ",
  270.                         NULL);
  271.                         break;
  272.                     }
  273.                     if (j == 0)
  274.                         break;
  275.                     if ((strlen(s1+1) + j + 1) >=  MAXLINE)
  276.                         goto line_too_long;
  277.                     for (s2 = &xbuf[1]; j; j--)
  278.                         *s2++ = ' ';
  279.                     (void) strcpy(s2, s1+1);
  280.                     s1 = xbuf;
  281.                     break;
  282.                 /*
  283.                  * Save current position in register if "\\k<reg>"
  284.                  */
  285.                     case 'k':
  286.                     s1 = Asmcode(&s1, nm);
  287.                     if ((i = Findnum(nm, 0, 0)) < 0)
  288.                         i = Findnum(nm, 0, 1);
  289.                     Numb[i].val =
  290.                         (int)((double)Outll * Scalen);
  291.                     break;
  292.                 /*
  293.                  * Interpolate number - "\\n(NN" or "\\nN"
  294.                  */
  295.                 case 'n':
  296.                     s1 = Asmcode(&s1, nm);
  297.                     if ((i = Findnum(nm, 0, 0)) < 0) {
  298. unknown_num:
  299.                         Error(WARN, LINE,
  300.                             " unknown number register ",
  301.                         nm);
  302.                         break;
  303.                     }
  304.                     (void) sprintf(buf, "%d",
  305.                         Numb[i].val);
  306.                     if ((strlen(buf) + strlen(s1+1) + 1)
  307.                     >=  MAXLINE) {
  308. line_too_long:
  309.                         Error(WARN, LINE, " line too long",
  310.                             NULL);
  311.                         break;
  312.                     }
  313.                     (void) sprintf(buf, "%d%s",
  314.                         Numb[i].val, s1+1);
  315.                     (void) strcpy(&xbuf[1], buf);
  316.                         s1 = xbuf;
  317.                     break;
  318.                 /*
  319.                  * Change size - "\\s[+-][0-9]" - NOP
  320.                  */
  321.                 case 's':
  322.                     s1++;
  323.                     if (*s1 == '+' || *s1 == '-')
  324.                         s1++;
  325.                     while (*s1 && isdigit(*s1))
  326.                         s1++;
  327.                     s1--;
  328.                     break;
  329.                 /*
  330.                  * Continue - "\\c"
  331.                  */
  332.                 case 'c':
  333.                     Backc = 1;
  334.                     break;
  335.                 /*
  336.                  * Interpolate string - "\\*(NN" or "\\*N"
  337.                  */
  338.                 case '*':
  339.                     s1 = Asmcode(&s1, nm);
  340.                     s2 = Findstr(nm, NULL, 0);
  341.                     if (*s2 != '\0') {
  342.                         if ((strlen(s2) + strlen(s1+1) + 1)
  343.                         >=  MAXLINE)
  344.                         goto line_too_long;
  345.                         (void) sprintf(buf, "%s%s",
  346.                         s2, s1+1);
  347.                         (void) strcpy(&xbuf[1], buf);
  348.                         s1 = xbuf;
  349.                     }
  350.                     break;
  351.                 /*
  352.                  * Discretionary hyphen - "\\%"
  353.                  */
  354.                 case '%':
  355.                     if (Wordl <= 0)
  356.                         break;
  357.                     if ((i = Findhy(NULL, 0, 0)) < 0) {
  358.                         Error(WARN, LINE,
  359.                             " np hyphen for font ", Font);
  360.                         break;
  361.                     }
  362.                     Word[Wordx] = '\0';
  363.                     Pass3(Wordl, Word, NULL, Hychar[i].len);
  364.                     Pass3(NOBREAK, "hyphen", Hychar[i].str,
  365.                         Hychar[i].len);
  366.                     Wordl = Wordx = 0;
  367.                     Uhyph = 1;
  368.                     break;
  369.                 /*
  370.                  * None of the above - may be special character
  371.                  * name.
  372.                  */
  373.                 default:
  374.                     s2 = --s1;
  375.                     s1 = Asmcode(&s1, nm);
  376.                     if ((i = Findchar(nm, 0, 0, 0)) < 0) {
  377.                         s1 = s2;
  378.                         goto ordinary_char;
  379.                     }
  380.                     if (strcmp(nm, "em") == 0
  381.                     && Wordx > 0) {
  382.                     /*
  383.                      * "\\(em" is a special case when a word
  384.                      * has been assembled, because of
  385.                      * hyphenation.
  386.                      */
  387.                         Word[Wordx] = '\0';
  388.                         Pass3(Wordl, Word, NULL,
  389.                             Schar[i].len);
  390.                         Pass3(NOBREAK, "userhyphen",
  391.                             Schar[i].str, Schar[i].len);
  392.                             Wordl = Wordx = 0;
  393.                         period = '\0';
  394.                         Uhyph = 1;
  395.                      }
  396.                     /*
  397.                      * Interpolate a special character
  398.                      */
  399.                     if (Str2word(Schar[i].str,
  400.                         strlen(Schar[i].str)) != 0)
  401.                         return;
  402.                         Wordl += Schar[i].len;
  403.                     period = '\0';
  404.                 }
  405.                 break;
  406.             /*
  407.              * Ordinary character
  408.              */
  409.             default:
  410. ordinary_char:
  411.                 if (Str2word(s1, 1) != 0)
  412.                     return;
  413.                 Wordl++;
  414.                 if (*s1 == '.' || *s1 == '!'
  415.                 ||  *s1 == '?' || *s1 == ':')
  416.                     period = '.';
  417.                 else if (period == '.') {
  418.                     nm[0] = *s1;
  419.                     nm[1] = '\0';
  420.                     if (regexec(Pat[13].pat, nm) == 0)
  421.                      period = '\0';
  422.                 }
  423.             }
  424.         }
  425.         /*
  426.          * End of line processing
  427.          */
  428.              if (!Backc) {
  429.             if (period == '.')
  430.                 Pass3(NOBREAK, "gap", NULL, 2);
  431.             if (Centering > 0) {
  432.                 Pass3(DOBREAK, "center", NULL, 0);
  433.                 Centering--;
  434.             } else if (!Fill)
  435.                 Pass3(DOBREAK, "flush", NULL, 0);
  436.         }
  437.         if (Aftnxt == NULL)
  438.             return;
  439.         /* else fall through to process an "after next trap */
  440.     }
  441.     /*
  442.      * Special -man macro handling.
  443.      */
  444.     if (Marg == MANMACROS) {
  445.         /*
  446.          * A text line - "^[^.]" - is only processed when there is an
  447.          * "after next" directive.
  448.          */
  449.         if (*line != '.') {
  450.             if (Aftnxt != NULL) {
  451.                 if (regexec(Pat[9].pat, Aftnxt))  /* ",fP" */
  452.                     Font[0] = Prevfont;
  453.                 if (regexec(Pat[10].pat, Aftnxt))  /* ",tP" */
  454.                     Pass3(DOBREAK, "toindent", NULL, 0);
  455.                 Free(&Aftnxt);
  456.             }
  457.             return;
  458.         }
  459.         /*
  460.          * Special footer handling - "^.lF"
  461.          */
  462.         if (line[1] == 'l' && line[2] == 'F') {
  463.             s1 = Findstr("by", NULL, 0);
  464.             s2 = Findstr("nb", NULL, 0);
  465.             if (*s1 == '\0' || *s2 == '\0')
  466.                 (void) sprintf(buf, "%s%s", s1, s2);
  467.             else
  468.                 (void) sprintf(buf, "%s; %s", s1, s2);
  469.             Pass3(NOBREAK, "LF", buf, 0);
  470.             return;
  471.         }
  472.     }
  473.     /*
  474.      * Special -ms macro handling.
  475.      */
  476.     if (Marg == MSMACROS) {
  477.         /*
  478.          * A text line - "^[^.]" - is only processed when there is an
  479.          * "after next" directive.
  480.          */
  481.         if (*line != '.') {
  482.             if (Aftnxt != NULL) {
  483.                 if (regexec(Pat[10].pat, Aftnxt))  /* ",tP" */
  484.                     Pass3(DOBREAK, "toindent", NULL, 0);
  485.                 Free(&Aftnxt);
  486.             }
  487.             return;
  488.         }
  489.         /*
  490.          * Numbered headings - "^\.nH"
  491.          */
  492.         if (line[1] == 'n' && line[2] == 'H') {
  493.             s1 = Field(2, line, 0);
  494.             if (s1 != NULL) {
  495.                 i = atoi(s1) - 1;    
  496.                 if (i < 0) {
  497.                     for (j = 0; j < MAXNHNR; j++) {
  498.                         Nhnr[j] = 0;
  499.                     }
  500.                     i = 0;
  501.                 } else if (i >= MAXNHNR) {
  502.                     (void) sprintf(buf, " over NH limit (%d)",
  503.                     MAXNHNR);
  504.                     Error(WARN, LINE, buf, NULL);
  505.                 }
  506.             } else
  507.                 i = 0;
  508.             Nhnr[i]++;
  509.             for (j = i + 1; j < MAXNHNR; j++) {
  510.                 Nhnr[j] = 0;
  511.             }
  512.             s1 = buf;
  513.             for (j = 0; j <= i; j++) {
  514.                 (void) sprintf(s1, "%d.", Nhnr[j]);
  515.                 s1 = buf + strlen(buf);
  516.             }
  517.             (void) Findstr("Nh", buf, 1);
  518.             return;
  519.         }
  520.     }
  521.     /*
  522.      * Remaining lines should begin with a '.' unless an "after next"
  523.      * trap has failed.
  524.      */
  525.     if (line[0] != '.') {
  526.         if (Aftnxt != NULL)
  527.             Error(WARN, LINE, " failed .it: ", Aftnxt);
  528.         else
  529.             Error(WARN, LINE, " unrecognized line ", NULL);
  530.         return;
  531.     }
  532.     /*
  533.      * Evaluate expressions for "^\.(ta|ll|ls|in|ti|po|ne|sp|pl|nr)"
  534.      * Then process the commands.
  535.      */
  536.     if (regexec(Pat[11].pat, &line[1])) {
  537.         /*
  538.          * Establish default scale factor.
  539.          */
  540.         if ((line[1] == 'n' && line[2] == 'e')
  541.         ||  (line[1] == 's' && line[2] == 'p')
  542.         ||  (line[1] == 'p' && line[2] == 'l'))
  543.             exscale = Scalev;
  544.         else if (line[1] == 'n' && line[2] == 'r')
  545.             exscale = Scaleu;
  546.         else
  547.             exscale = Scalen;
  548.         /*
  549.          * Determine starting argument.
  550.          */
  551.         if (line[1] == 'n' && line[2] == 'r')
  552.             s1 = Field(2, &line[3], 0);
  553.         else
  554.             s1 = Field(1, &line[3], 0);
  555.         /*
  556.          * Evaluate expressions.
  557.          */
  558.         for (nexpr = 0; s1 != NULL &&*s1 != '\0'; ) {
  559.             while (*s1 == ' ' || *s1 == '\t')
  560.                 s1++;
  561.             if (*s1 == '+' || *s1 == '-')
  562.                 ssign = *s1++;
  563.             else
  564.                 ssign = '\0';
  565.             /*
  566.              * Process terms.
  567.              */
  568.             val = 0.0;
  569.             sp = -1;
  570.             c = '+';
  571.             s1--;
  572.             while (c == '+' || c == '*' || c == '%'
  573.             ||  c == ')' || c == '-' || c == '/') {
  574.                 op = c;
  575.                 s1++;
  576.                 tscale = exscale;
  577.                 tval = 0.0;
  578.             /*
  579.              * Pop stack on right parenthesis.
  580.              */
  581.                 if (op == ')') {
  582.                 tval = val;
  583.                 if (sp >= 0) {
  584.                     val = valstack[sp];
  585.                     op = opstack[sp];
  586.                     sp--;
  587.                 } else {
  588.                     Error(WARN, LINE,
  589.                     " expression stack underflow", NULL);
  590.                     return;
  591.                 }
  592.                 tscale = Scaleu;
  593.             /*
  594.              * Push stack on left parenthesis.
  595.              */
  596.                 } else if (*s1 == '(') {
  597.                 sp++;
  598.                 if (sp >= MAXSP) {
  599.                     Error(WARN, LINE,
  600.                        " expression stack overflow", NULL);
  601.                     return;
  602.                 }
  603.                 valstack[sp] = val;
  604.                 opstack[sp] = op;
  605.                 val = 0.0;
  606.                 c = '+';
  607.                 continue;
  608.                 } else if (*s1 == '\\') {
  609.                   s1++;
  610.                   switch(*s1) {
  611.             /*
  612.              * "\\"" begins a comment.
  613.              */
  614.                   case '"':
  615.                 while (*s1)
  616.                     s1++;
  617.                 break;
  618.             /*
  619.              * Crude width calculation for "\\w"
  620.              */
  621.                   case 'w':
  622.                 s2 = ++s1;
  623.                 if (*s1) {
  624.                     s1++;
  625.                     while (*s1 && *s1 != *s2)
  626.                     s1++;
  627.                     tval = (double) (s1 - s2 - 1) * Scalen;
  628.                     if (*s1)
  629.                     s1++;
  630.                 }
  631.                 break;
  632.             /*
  633.              * Interpolate number register if "\\n".
  634.              */
  635.                   case 'n':
  636.                 s1 = Asmcode(&s1, nm);
  637.                 if ((i = Findnum(nm, 0, 0)) >= 0)
  638.                     tval = Numb[i].val;
  639.                     s1++;
  640.                  }
  641.             /*
  642.              * Assemble numeric value.
  643.              */
  644.                 } else if (*s1 == '.' || isdigit(*s1)) {
  645.                 for (i = 0; isdigit(*s1) || *s1 == '.'; s1++) {
  646.                     if (*s1 == '.') {
  647.                     i = 10;
  648.                     continue;
  649.                     }
  650.                     d = (double) (*s1 - '0');
  651.                     if (i) {
  652.                     tval = tval + (d / (double) i);
  653.                     i = i * 10;
  654.                     } else
  655.                     tval = (tval * 10.0) + d;
  656.                 }
  657.                 } else {
  658.             /*
  659.              * It's not an expression.  Ignore extra scale.
  660.              */
  661.                 if ((i = Findscale(*s1, 0.0, 0)) < 0) {
  662.                     (void) sprintf(buf,
  663.                     " \"%s\" isn't an expression", s1);
  664.                     Error(WARN, LINE, buf, NULL);
  665.                 }
  666.                 s1++;
  667.                 }
  668.             /*
  669.              * Add term to expression value.
  670.              */
  671.                 if ((i = Findscale(*s1, 0.0, 0)) >= 0) {
  672.                 tval *= Scale[i].val;
  673.                 s1++;
  674.                 } else
  675.                 tval *= tscale;
  676.                 switch (op) {
  677.                 case '+':
  678.                 val += tval;
  679.                 break;
  680.                 case '-':
  681.                 val -= tval;
  682.                 break;
  683.                 case '*':
  684.                 val *= tval;
  685.                 break;
  686.                 case '/':
  687.                 case '%':
  688.                 i = (int) val;
  689.                 j = (int) tval;
  690.                 if (j == 0) {
  691.                     Error(WARN, LINE,
  692.                     (*s1 == '/') ? "div" : "mod",
  693.                         " by 0");
  694.                     return;
  695.                 }
  696.                 if (op == '/')
  697.                     val = (double) (i / j);
  698.                 else
  699.                     val = (double) (i % j);
  700.                 break;
  701.                 }
  702.                 c = *s1;
  703.             }
  704.             /*
  705.              * Save expression value and sign.
  706.              */
  707.             if (nexpr >= MAXEXP) {
  708.                 (void) sprintf(buf,
  709.                     " at expression limit of %d", MAXEXP);
  710.                 Error(WARN, LINE, buf, NULL);
  711.                 return;
  712.             }
  713.             exsign[nexpr] = ssign;
  714.             expr[nexpr] = val;
  715.             if (ssign == '-')
  716.                 sexpr[nexpr] = -1.0 * val;
  717.             else
  718.                 sexpr[nexpr] = val;
  719.             nexpr++;
  720.             while (*s1 == ' ' || *s1 == '\t')
  721.                 s1++;
  722.         }
  723.         /*
  724.          * Set parameters "(ll|ls|in|ti|po|pl)"
  725.          */
  726.         if (regexec(Pat[12].pat, &line[1])) {
  727.             nm[0] = line[1];
  728.             nm[1] = line[2];
  729.             if ((i = Findparms(nm)) < 0) {
  730.                 Error(WARN, LINE,
  731.                     " can't find parameter register ", nm);
  732.                 return;
  733.             }
  734.             if (nexpr == 0 || exscale == 0.0)
  735.                 j = Parms[i].prev;
  736.             else if (exsign[0] == '\0'
  737.                  ||  (nm[0] == 't' && nm[1] == 'i'))
  738.                  j = (int)(sexpr[0] / exscale);
  739.             else
  740.                 j = Parms[i].val + (int)(sexpr[0] / exscale);
  741.             Parms[i].prev = Parms[i].val;
  742.             Parms[i].val = j;
  743.             nm[0] = (nexpr) ? exsign[0] : '\0';     /* for .ti */
  744.             nm[1] = '\0';
  745.             Pass3(DOBREAK, Parms[i].cmd, nm, j);
  746.             return;
  747.         }
  748.         if (line[1] == 'n') {
  749.             switch(line[2]) {
  750.         /*
  751.          * Need - "^\.ne <expression>"
  752.          */
  753.             case 'e':
  754.                 if (nexpr && Scalev > 0.0)
  755.                     i = (int) ((expr[0]/Scalev) + 0.99);
  756.                 else
  757.                     i = 0;
  758.                 Pass3(DOBREAK, "need", NULL, i);
  759.                 return;
  760.         /*
  761.          * Number - "^\.nr <name> <expression>"
  762.          */
  763.             case 'r':
  764.                 if ((s1 = Field(2, line, 0)) == NULL) {
  765.                     Error(WARN, LINE, " bad number register",
  766.                         NULL);
  767.                     return;
  768.                 }
  769.                 if ((i = Findnum(s1, 0, 0)) < 0)
  770.                     i = Findnum(s1, 0, 1);
  771.                 if (nexpr < 1) {
  772.                     Numb[i].val = 0;
  773.                     return;
  774.                 }
  775.                 if (exsign[0] == '\0')
  776.                     Numb[i].val = (int) expr[0];
  777.                 else
  778.                     Numb[i].val += (int) sexpr[0];
  779.                 return;
  780.             }
  781.         }
  782.         /*
  783.          * Space - "^\.sp <expression>"
  784.          */
  785.         if (line[1] == 's' && line[2] == 'p') {
  786.             if (nexpr == 0)
  787.                 i = 1;
  788.             else
  789.                 i = (int)((expr[0] / Scalev) + 0.99);
  790.             while (i--)
  791.                 Pass3(DOBREAK, "space", NULL, 0);
  792.             return;
  793.         }
  794.         /*
  795.          * Tab positions - "^\.ta <pos1> <pos2> . . ."
  796.          */
  797.              if (line[1] == 't' && line[2] == 'a') {
  798.             tval = 0.0;
  799.             for (j = 0; j < nexpr; j++) {
  800.                 if (exsign[j] == '\0')
  801.                     tval = expr[j];
  802.                 else
  803.                     tval += sexpr[j];
  804.                 Tabs[j] = (int) (tval / Scalen);
  805.             }
  806.             Ntabs = nexpr;
  807.             return;
  808.         }
  809.     }
  810.     /*
  811.      * Remaining lines begin with a '.'.
  812.      */
  813.  
  814.     /*
  815.      * Adjust - "^\.ad"
  816.      */
  817.     if (line[1] == 'a' && line[2] == 'd') {
  818.         Pass3(NOBREAK, "both", NULL, 0);
  819.         return;
  820.     }
  821.     if (line[1] == 'b') {
  822.         switch (line[2]) {
  823.     /*
  824.      * Break - "^\.br"
  825.      */
  826.         case 'r':
  827.             Pass3(DOBREAK, "flush", NULL, 0);
  828.             return;
  829.     /*
  830.      * Begin new page - "^\.bp"
  831.      */
  832.         case 'p':
  833.             Pass3(DOBREAK, "need", NULL, 999);
  834.             return;
  835.         }
  836.     }
  837.     /*
  838.      * Center - "^\.ce"
  839.      */
  840.     if (line[1] == 'c' && line[2] == 'e') {
  841.         if ((s2 = Field(2, line, 0)) != NULL)
  842.             Centering = atoi(s2);
  843.         else
  844.             Centering = 1;
  845.         return;
  846.     }
  847.     if (line[1] == 'd') {
  848.         switch (line[2]) {
  849.     /*
  850.      * Diversion on and off - "^\.di"
  851.      */
  852.         case 'i':
  853.             Pass3(DOBREAK, "flush", NULL, 0);
  854.             Divert ^= 1;
  855.             return;
  856.     /*
  857.      * Define string - "^\.ds"
  858.      */
  859.         case 's':
  860.  
  861. common_ds:
  862.  
  863.             if (Asmname(&line[3], nm) == 0) {
  864. no_name:
  865.                 Error(WARN, LINE, " no name", NULL);
  866.                 return;
  867.             }
  868.             s3 = Field(3, line, 0);
  869.             s4 = Findstr(nm, s3, 1);
  870.             if (Hdft) {
  871.                 /*
  872.                  * Look for names LH, LF, CH, CF, RH, RF.
  873.                  */
  874.                 if ((nm[0]=='L' || nm[0]=='C' || nm[0]=='R')
  875.                 &&  (nm[1]=='F' || nm[1]=='H')) {
  876.                     (void) sprintf(buf, "%s", nm);
  877.                     Pass3(NOBREAK, buf, s4, 0);
  878.                 }
  879.             }
  880.             return;
  881.         }
  882.     }
  883.     if (line[1] == 'f') {
  884.     /*
  885.      * Fill - "^\.fi"
  886.      */
  887.         if (line[2] == 'i') {
  888.             Fill = 1;
  889.             Pass3(DOBREAK, "flush", NULL, 0);
  890.             return;
  891.         }
  892.     /*
  893.      * Font - "^\.ft <font_name>"
  894.      */
  895.         if (line[2] == 't') {
  896.             if (line[3] == '\0' || line[4] == '\0')
  897.                 line[4] = 'P';
  898.             if (line[4] == 'P') {
  899.                 Font[0] = Prevfont;
  900.                 return;
  901.             }
  902.             for (i = 0; Fcode[i].nm; i++) {
  903.                 if (Fcode[i].nm == line[4])
  904.                     break;
  905.             }
  906.             if (Fcode[i].status == '\0') {
  907.                 Error(WARN, LINE, " bad font code", NULL);
  908.                 return;
  909.             }
  910.             Prevfont = Font[0];
  911.             Font[0] = line[4];
  912.             return;
  913.         }
  914.     }
  915.     /*
  916.      * Input trap - "^\.it [1 <command>]"
  917.      */
  918.     if (line[1] == 'i' && line[2] == 't') {
  919.         if ((s2 = Field(2, line, 0)) == NULL) {
  920.             Free(&Aftnxt);
  921.             return;
  922.         }
  923.         if ((i = atoi(s2)) != 1) {
  924.             Error(WARN, LINE, " first .it arg must be 1", NULL);
  925.             return;
  926.         }
  927.         if ((s3 = Field(3, line, 0)) == NULL)
  928.             Free(&Aftnxt);
  929.         else {
  930.             (void) sprintf(buf, "%s,%s",
  931.                 (Aftnxt == NULL) ? "" : Aftnxt, s3);
  932.             Free(&Aftnxt);
  933.             Aftnxt = Newstr(buf);
  934.         }
  935.         return;
  936.     }
  937.     /*
  938.      * "^\.i0", "^\.lg" and "^\.li" - do nothing
  939.      */
  940.     if ((line[1] == 'i' && line[2] == '0')
  941.     ||  (line[1] == 'l' && line[2] == 'g')
  942.     ||  (line[1] == 'l' && line[2] == 'i'))
  943.         return;
  944.     if (line[1] == 'n') {
  945.         switch (line[2]) {
  946.     /*
  947.      * No adjust "^\.na"
  948.      */
  949.         case 'a':
  950.             Pass3(NOBREAK, "left", NULL, 0);
  951.             return;
  952.     /*
  953.      * No fill - "^\.nf"
  954.      */
  955.         case 'f':
  956.             Fill = 0;
  957.             Pass3(DOBREAK, "flush", NULL, 0);
  958.             return;
  959.     /*
  960.      * No space - "^\.ns"
  961.      */
  962.         case 's':
  963.             Pass3(NOBREAK, "nospace", NULL, 0);
  964.             return;
  965.         }
  966.     }
  967.     /*
  968.      * Point size - "^\.ps"
  969.      */
  970.     if (line[1] == 'p' && line[2] == 's')
  971.         return;
  972.     if (line[1] == 'r') {
  973.         switch (line[2]) {
  974.     /*
  975.      * Remove macro or string - "^\.rm"
  976.      */
  977.         case 'm':
  978.             if (Asmname(&line[3], nm) == 0)
  979.                 goto no_name;
  980.             if ((i = Findmacro(nm, 0)) >= 0) {
  981.                 Delmacro(i);
  982.                 return;
  983.             }
  984.             (void) Findstr(nm, NULL, 0);
  985.             if (Sx < 0) {
  986.                 Error(WARN, LINE, " no macro/string", NULL);
  987.                 return;
  988.             }
  989.             Delstr(Sx);
  990.             return;
  991.     /*
  992.      * Remove register - "^\.rr"
  993.      */
  994.         case 'r':
  995.             if (Asmname(&line[3], nm) == 0)
  996.                 goto no_name;
  997.             if ((i = Findnum(nm, 0, 0)) < 0) {
  998.                 Error(WARN, LINE, " no register", NULL);
  999.                 return;
  1000.             }
  1001.             Delnum(i);
  1002.             return;
  1003.     /*
  1004.      * Resume space - "^\.rs"
  1005.      */
  1006.         case 's':
  1007.             Pass3(NOBREAK, "yesspace", NULL, 0);
  1008.             return;
  1009.         }
  1010.     }
  1011.     if (line[1] == 't') {
  1012.         switch (line[2]) {
  1013.     /*
  1014.      * Message - "^\.tm"
  1015.      */
  1016.         case 'm':
  1017.             Pass3(MESSAGE, Inname,
  1018.                 (line[3] == ' ') ? &line[4] : &line[3], NR);
  1019.             return;
  1020.     /*
  1021.      * Translate - "^\.tr abcd..."
  1022.      */
  1023.         case 'r':
  1024.             if (line[3] != ' ') {
  1025.                 Error(WARN, LINE, " unknown translation",
  1026.                     NULL);
  1027.                 return;
  1028.             }
  1029.             for (s1 = &line[4]; *s1; s1 += 2)
  1030.                 Trtbl[(int)*s1] = (*(s1+1)) ? *(s1+1) : ' ';
  1031.             return;
  1032.         }
  1033.     }
  1034.     /*
  1035.      * Vertical spacing - "^\.vs" (do nothing)
  1036.      */
  1037.     if (line[1] == 'v' && line[2] == 's')
  1038.         return;
  1039.     if (line[1] == '^') {
  1040.         switch(line[2]) {
  1041.     /*
  1042.      * Initialization - "^\.\^b (fh|HF|NH) [01]"
  1043.      *
  1044.      * fh = first page header status
  1045.      * HF = header/footer status
  1046.      * NH = initialize number headers
  1047.      */
  1048.         case 'b':
  1049.             if ((s1 = Field(2, line, 0)) == NULL)
  1050.                 return;
  1051.             if ((s2 = Field(3, line, 0)) == NULL)
  1052.                 i = 0;
  1053.             else
  1054.                 i = atoi(s2);
  1055.             if (s1[0] == 'f' && s1[1] == 'h')
  1056.                 Pass3(NOBREAK, "fph", NULL, i);
  1057.             else if (s1[0] == 'H' && s1[1] == 'F')
  1058.                 Hdft = i;
  1059.             else if (s1[0] == 'N' && s1[1] == 'H') {
  1060.                 for (i = 0; i < MAXNHNR; i++)
  1061.                     Nhnr[i] = 0;
  1062.             } else
  1063.                 Error(WARN, LINE, " unknown initialization",
  1064.                     NULL);
  1065.             return;
  1066.     /*
  1067.      * Character definitions - "^\.\^c"
  1068.      */
  1069.         case 'c':
  1070.  
  1071.             s2 = Field(2, line, 0);
  1072.             i = atoi(Field(3, line, 0));
  1073.             s4 = Field(4, line, 0);
  1074.             if (i < 0 || i > MAXLINE/2 || *s2 == '\0') {
  1075.                 Error(WARN, LINE, " bad character definition",
  1076.                     NULL);
  1077.                 return;
  1078.             }
  1079.             if (s4 == NULL)
  1080.                 s4 = "";
  1081.             else if (*s4 == '"')
  1082.                 s4++;
  1083.             s1 = buf;
  1084.             while ((s5 = strchr(s4, '\\')) != NULL) {
  1085.                 while (s5 > s4)
  1086.                     *s1++ = *s4++;
  1087.                 s4 = ++s5;
  1088.                 if (*s5 == '\\')
  1089.                     *s1++ = '\\';
  1090.                 else if (*s5 == 'b')
  1091.                     *s1++ = '\b';
  1092.                 if (*s4)
  1093.                     s4++;
  1094.             }
  1095.             while (*s1++ = *s4++)
  1096.                 ;
  1097.             if (*s2 == 'h' && *(s2+1) == 'y')
  1098.                 (void) Findhy(buf, i, 1);
  1099.             else
  1100.                 (void) Findchar(s2, i, buf, 1);
  1101.             return;
  1102.     /*
  1103.      * Debug - "^\.\^d"
  1104.      */
  1105.         case 'd':
  1106.             return;
  1107.     /*
  1108.      * Finalization - "\.\^e"
  1109.      */
  1110.         case 'e':
  1111.             return;
  1112.     /*
  1113.      * Font is OK - "\.\^f <font_name_character>"
  1114.      */
  1115.         case 'f':
  1116.             if (line[3] != '\0' && line[4] != '\0') {
  1117.                 for (i = 0; Fcode[i].nm; i++) {
  1118.                     if (line[4] == Fcode[i].nm) {
  1119.                         Fcode[i].status = '1';
  1120.                         return;
  1121.                     }
  1122.                 }
  1123.             }
  1124.             Error(WARN, LINE, " unknown font", NULL);
  1125.             return;
  1126.     /*
  1127.      * Resolutions - "\.\^r cpi horizontal vertical"
  1128.      */
  1129.         case 'r':
  1130.             if ((i = atoi(Field(3, line, 0))) <= 0
  1131.             ||  (j = atoi(Field(4, line, 0))) <= 0) {
  1132.                 Error(WARN, LINE, " bad cpi resolutions",
  1133.                     NULL);
  1134.                 return;
  1135.             }
  1136.             tval = (double) (240.0 / (double) i);
  1137.             if (Findscale('m', tval, 1) < 0)
  1138.                 Error(FATAL, LINE, " missing Scal['m']",
  1139.                     NULL);
  1140.             Scalen = tval;
  1141.             if (Scalen <= 0.0) {
  1142.                 (void) sprintf(buf, " bad Scale['n'] (%f)",
  1143.                     Scalen);
  1144.                 Error(FATAL, LINE, buf, NULL);
  1145.             }
  1146.             if (Findscale('n', tval, 1) < 0)
  1147.                 Error(FATAL, LINE, " missing Scale['n']",
  1148.                     NULL);
  1149.             Scalev = (double) (240.0 / (double) j);
  1150.             if (Scalev <= 0.0) {
  1151.                 (void) sprintf(buf, " bad Scale['v'] (%f)",
  1152.                     Scalen);
  1153.                 Error(FATAL, LINE, buf, NULL);
  1154.             }
  1155.             if (Findscale('v', Scalev, 1) < 0)
  1156.                 Error(FATAL, LINE, " missing Scale['v']",
  1157.                     NULL);
  1158.             return;
  1159.     /*
  1160.      * Error file - "^\.\^x <name>"
  1161.      */
  1162.         case 'x':
  1163.             return;
  1164.     /*
  1165.      * Set line number and file name - "^\.\^# <number> <file>"
  1166.      *
  1167.      * Lock line number and file name - "^\.\^= <number> <file>"
  1168.      */
  1169.         case '#':
  1170.         case '=':
  1171.             if ((s1 = Field(2, line, 0)) != NULL)
  1172.                 P2il = atoi(s1) - 1;
  1173.             else
  1174.                 P2il = 0;
  1175.             Lockil = (line[2] == '#') ? 0 : 1;
  1176.             Free(&P2name);
  1177.             if (Field(3, line, 1) != NULL) {
  1178.                 P2name = F;
  1179.                 F = NULL;
  1180.             } else
  1181.                 P2name = NULL;
  1182.             return;
  1183.         }
  1184.     }
  1185.     /*
  1186.      * Comment - "^\.\\"
  1187.      */
  1188.     if (line[1] == '\\')
  1189.         return;
  1190.     /*
  1191.      * Unknown command starting with a '.'.
  1192.      */
  1193.     Error(WARN, LINE, " unknown command", NULL);
  1194.     return;
  1195. }
  1196.  
  1197.  
  1198. /*
  1199.  * Str2word(s, len) - copy len characters from string to Word[]
  1200.  *
  1201.  * entry:
  1202.  *    s   = pointer to string
  1203.  *    len = number of characters to copy
  1204.  *
  1205.  * exit:
  1206.  *    return = 0 if characters copied
  1207.  *         1 if error detected (warning message issued)
  1208.  */
  1209.  
  1210. Str2word(s, len)
  1211.     char *s;
  1212.     int len;
  1213. {
  1214.     for (; len > 0; len--, s++) {
  1215.         switch (Font[0]) {
  1216.         case 'B':
  1217.             if (Fontout == 'e') {
  1218.                 if ((Wordx + 2) >= MAXLINE) {
  1219. word_too_long:
  1220.                     Error(WARN, LINE, " word too long",
  1221.                         NULL);
  1222.                     return(1);
  1223.                 }
  1224.                 Word[Wordx++] = ESC;
  1225.                 Word[Wordx++] = 'B';
  1226.             }
  1227.             if ((Wordx + 1) >= MAXLINE)
  1228.                 goto word_too_long;
  1229.             Word[Wordx++] = Trtbl[(int)*s];
  1230.             if (Fontout == 'b') {
  1231.                 if ((Wordx + 4) >= MAXLINE)
  1232.                     goto word_too_long;
  1233.                 Word[Wordx++] = '\b';
  1234.                 Word[Wordx++] = Trtbl[(int)*s];
  1235.                 Word[Wordx++] = '\b';
  1236.                 Word[Wordx++] = Trtbl[(int)*s];
  1237.             }
  1238.             break;
  1239.         case 'I':
  1240.             if (isalnum(*s)) {
  1241.                 if (Fontout == 'e') {
  1242.                     if ((Wordx + 2) >= MAXLINE)
  1243.                         goto word_too_long;
  1244.                     Word[Wordx++] = ESC;
  1245.                     Word[Wordx++] = 'I';
  1246.                 } else if (Fontout == 'b') {
  1247.                     if ((Wordx + 2) >= MAXLINE)
  1248.                         goto word_too_long;
  1249.                     Word[Wordx++] = '_';
  1250.                     Word[Wordx++] = '\b';
  1251.                 }
  1252.                 if ((Wordx + 1) >= MAXLINE)
  1253.                     goto word_too_long;
  1254.                 Word[Wordx++] = Trtbl[(int)*s];
  1255.                 break;
  1256.             }
  1257.             /* else fall through */
  1258.         default:
  1259.             if ((Wordx + 1) >= MAXLINE)
  1260.                 goto word_too_long;
  1261.             Word[Wordx++] = Trtbl[(int)*s];
  1262.         }
  1263.     }
  1264.     return(0);
  1265. }
  1266.