home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / cawf407.zip / src / pass2.c < prev    next >
C/C++ Source or Header  |  1993-12-28  |  20KB  |  849 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. #include <ctype.h>
  33.  
  34. /*
  35.  * Pass2(line) - process the nroff requests in a line and break
  36.  *         text into words for pass 3
  37.  */
  38.  
  39. void
  40. Pass2(line)
  41.     unsigned char *line;
  42. {
  43.     int brk;            /* request break status */
  44.     unsigned char buf[MAXLINE];    /* working buffer */
  45.     unsigned char c;        /* character buffer */
  46.     double d;            /* temporary double */
  47.     double exscale;            /* expression scaling factor */
  48.     double expr[MAXEXP];            /* expressions */
  49.     unsigned char exsign[MAXEXP];    /* expression signs */
  50.     int i, j;            /* temporary indexes */
  51.     int inword;            /* word processing status */
  52.     int nexpr;            /* number of expressions */
  53.     unsigned char nm[4];        /* name */
  54.     int nsp;            /* number of spaces */
  55.     unsigned char op;        /* expression term operator */
  56.     unsigned char opstack[MAXSP];    /* expression operation stack */
  57.     unsigned char period;        /* end of word status */
  58.     unsigned char *s1, *s2, *s3;    /* temporary string pointers */
  59.     double sexpr[MAXEXP];           /* signed expressions */
  60.     int sp;                /* expression stack pointer */
  61.     unsigned char ssign;        /* expression's starting sign */
  62.     double tscale;            /* term scaling factor */
  63.     double tval;            /* term value */
  64.     double val;            /* term value */
  65.     double valstack[MAXSP];        /* expression value stack */
  66.     unsigned char xbuf[MAXLINE];    /* expansion buffer */
  67.  
  68.     if (line == NULL) {
  69.     /*
  70.      * End of macro expansion.
  71.      */
  72.         Pass3(DOBREAK, (unsigned char *)"need", 4, NULL, 999);
  73.         return;
  74.     }
  75.     /*
  76.      * Adjust line number.
  77.      */
  78.     if (Lockil == 0)
  79.         P2il++;
  80.     /*
  81.      * Empty line - "^[ \t]*$" or "^\\\"".
  82.      */
  83.     if (regexec(Pat[6].pat, line)
  84.     ||  strncmp((char *)line, "\\\"", 2) == 0) {
  85.         Pass3(DOBREAK, (unsigned char *)"space", 5, NULL, 0);
  86.         return;
  87.     }
  88.     /*
  89.      * Line begins with white space.
  90.      */
  91.     if (*line == ' ' || *line == '\t') {
  92.         Pass3(DOBREAK, (unsigned char *)"flush", 5, NULL, 0);
  93.         Pass3(0, (unsigned char *)"", 0, NULL, 0);
  94.     }
  95.     if (*line != '.' && *line != '\'') {
  96.     /*
  97.      * Line contains text (not an nroff request).
  98.      */
  99.         if (Font[0] == 'R' && Backc == 0 && Aftnxt == NULL
  100.         &&  regexec(Pat[7].pat, line) == 0) {
  101.             /*
  102.              * The font is Roman, there is no "\\c" or "after next"
  103.              * trap pending and and the line has no '\\', '\t', '-',
  104.              * or "  "  (regular expression "\\|\t|-|  ").
  105.              *
  106.              * Output each word of the line as "<length> <word>".
  107.              */
  108.             for (s1 = line;;) {
  109.                 while (*s1 && *s1 == ' ')
  110.                     s1++;
  111.                 if (*s1 == '\0')
  112.                     break;
  113.                 for (s2 = s1, s3 = buf; *s2 && *s2 != ' ';)
  114.                     *s3++ = Trtbl[(int)*s2++];
  115.                 *s3 = '\0';
  116.                 Pass3((s2 - s1), buf, (s2 - s1), NULL, 0);
  117.                 s1 = *s2 ? ++s2 : s2;
  118.             }
  119.             /*
  120.              * Line terminates with punctuation and optional
  121.              * bracketing (regular expression "[.!?:][\])'\"*]*$").
  122.              */
  123.             if (regexec(Pat[8].pat, line))
  124.                 Pass3(NOBREAK, (unsigned char *)"gap", 3,
  125.                     NULL, 2);
  126.             if (Centering > 0) {
  127.                 Pass3(DOBREAK,(unsigned char *)"center", 6,
  128.                     NULL, 0);
  129.                 Centering--;
  130.             } else if (Fill == 0)
  131.                 Pass3(DOBREAK, (unsigned char *)"flush", 5,
  132.                     NULL, 0);
  133.             return;
  134.         }
  135.         /*
  136.          * Line must be scanned a character at a time.
  137.          */
  138.         inword = nsp = 0;
  139.         period = '\0';
  140.         for (s1 = line;; s1++) {
  141.             /*
  142.              * Space or TAB causes state transition.
  143.              */
  144.             if (*s1 == '\0' || *s1 == ' ' || *s1 == '\t') {
  145.                 if (inword) {
  146.                     if (!Backc) {
  147.                         Endword();
  148.                         Pass3(Wordl, Word, Wordx,
  149.                             NULL, 0);
  150.                         if (Uhyph) {
  151.                           Pass3(NOBREAK,
  152.                             (unsigned char *)"nohyphen",
  153.                             8, NULL, 0);
  154.                         }
  155.                     }
  156.                     inword = 0;
  157.                     nsp = 0;
  158.                 }
  159.                 if (*s1 == '\0')
  160.                     break;
  161.             } else {
  162.                 if (inword == 0) {
  163.                     if (Backc == 0) {
  164.                         Wordl = Wordx = 0;
  165.                         Uhyph = 0;
  166.                     }
  167.                     Backc = 0;
  168.                     inword = 1;
  169.                     if (nsp > 1) {
  170.                         Pass3(NOBREAK,
  171.                             (unsigned char *)"gap",
  172.                             3, NULL, nsp);
  173.                     }
  174.                 }
  175.             }
  176.             /*
  177.              * Process a character.
  178.              */
  179.             switch (*s1) {
  180.             /*
  181.              * Space
  182.              */
  183.                  case ' ':
  184.                 nsp++;
  185.                 period = '\0';
  186.                 break;
  187.             /*
  188.              * TAB
  189.              */
  190.                  case '\t':
  191.                 Pass3(NOBREAK, (unsigned char *)"tabto", 5,
  192.                     NULL, 0);
  193.                 nsp = 0;
  194.                 period = '\0';
  195.                 break;
  196.             /*
  197.              * Hyphen if word is being assembled
  198.              */
  199.             case '-':
  200.                 if (Wordl <= 0
  201.                 || (Wordl == 1 && Word[Wordx - 1] == '-'))
  202.                     goto ordinary_char;
  203.                 if ((i = Findhy(NULL, 0, 0)) < 0) {
  204.                     Error(WARN, LINE, " no hyphen for font ",
  205.                     (char *)Font);
  206.                     return;
  207.                 }
  208.                 Endword();
  209.                 Pass3(Wordl, Word, Wordx, NULL, Hychar[i].len);
  210.                 Pass3(NOBREAK, (unsigned char *)"userhyphen",
  211.                     10, Hychar[i].str, Hychar[i].len);
  212.                 Wordl = Wordx = 0;
  213.                 period = '\0';
  214.                 Uhyph = 1;
  215.                 break;
  216.             /*
  217.              * Backslash
  218.              */
  219.             case '\\':
  220.                 s1++;
  221.                 switch(*s1) {
  222.                 /*
  223.                  * Hard space - "\\ "
  224.                  */
  225.                 case ' ':
  226.                     goto ordinary_char;
  227.                 /*
  228.                  * Comment - "\\\""
  229.                  */
  230.                 case '"':
  231.                     while (*(s1+1))
  232.                         s1++;
  233.                     break;
  234.                 /*
  235.                  * Change font - "\\fN"
  236.                  */
  237.                 case 'f':
  238.                     s1 = Asmcode(&s1, nm);
  239.                     if (nm[0] == 'P') {
  240.                         Font[0] = Prevfont;
  241.                         break;
  242.                     }
  243.                     for (i = 0; Fcode[i].nm; i++) {
  244.                         if (Fcode[i].nm == nm[0])
  245.                         break;
  246.                     }
  247.                     if (Fcode[i].nm == '\0'
  248.                     ||  nm[1] != '\0') {
  249.                         Error(WARN, LINE, " unknown font ",
  250.                             (char *)nm);
  251.                         break;
  252.                     }
  253.                     if (Fcode[i].status != '1') {
  254.                         Error(WARN, LINE,
  255.                         " font undefined ", (char *)nm);
  256.                         break;
  257.                     } else {
  258.                         Prevfont = Font[0];
  259.                         Font[0] = nm[0];
  260.                     }
  261.                     break;
  262.                 /*
  263.                  * Positive horizontal motion - "\\h\\n(NN" or
  264.                  * "\\h\\nN"
  265.                  */
  266.                 case 'h':
  267.                     if (s1[1] != '\\' || s1[2] != 'n') {
  268.                         Error(WARN, LINE,
  269.                         " no \\n after \\h", NULL);
  270.                         break;
  271.                     }
  272.                     s1 +=2;
  273.                     s1 = Asmcode(&s1, nm);
  274.                     if ((i = Findnum(nm, 0, 0)) < 0)
  275.                         goto unknown_num;
  276.                     if ((j = Numb[i].val) < 0) {
  277.                         Error(WARN, LINE, " \\h < 0 ",
  278.                         NULL);
  279.                         break;
  280.                     }
  281.                     if (j == 0)
  282.                         break;
  283.                     if ((strlen((char *)s1+1) + j + 1)
  284.                     >=  MAXLINE)
  285.                         goto line_too_long;
  286.                     for (s2 = &xbuf[1]; j; j--)
  287.                         *s2++ = ' ';
  288.                     (void) strcpy((char *)s2, (char *)s1+1);
  289.                     s1 = xbuf;
  290.                     break;
  291.                 /*
  292.                  * Save current position in register if "\\k<reg>"
  293.                  */
  294.                     case 'k':
  295.                     s1 = Asmcode(&s1, nm);
  296.                     if ((i = Findnum(nm, 0, 0)) < 0)
  297.                         i = Findnum(nm, 0, 1);
  298.                     Numb[i].val =
  299.                         (int)((double)Outll * Scalen);
  300.                     break;
  301.                 /*
  302.                  * Interpolate number - "\\n(NN" or "\\nN"
  303.                  */
  304.                 case 'n':
  305.                     s1 = Asmcode(&s1, nm);
  306.                     if ((i = Findnum(nm, 0, 0)) < 0) {
  307. unknown_num:
  308.                         Error(WARN, LINE,
  309.                             " unknown number register ",
  310.                         (char *)nm);
  311.                         break;
  312.                     }
  313.                     (void) sprintf((char *)buf, "%d",
  314.                         Numb[i].val);
  315.                     if ((strlen((char *)buf)
  316.                        + strlen((char *)s1+1) + 1)
  317.                     >=  MAXLINE) {
  318. line_too_long:
  319.                         Error(WARN, LINE, " line too long",
  320.                             NULL);
  321.                         break;
  322.                     }
  323.                     (void) sprintf((char *)buf, "%d%s",
  324.                         Numb[i].val, (char *)s1+1);
  325.                     (void) strcpy((char *)&xbuf[1],
  326.                         (char *)buf);
  327.                         s1 = xbuf;
  328.                     break;
  329.                 /*
  330.                  * Change size - "\\s[+-][0-9]" - NOP
  331.                  */
  332.                 case 's':
  333.                     s1++;
  334.                     if (*s1 == '+' || *s1 == '-')
  335.                         s1++;
  336.                     while (*s1 && isdigit(*s1))
  337.                         s1++;
  338.                     s1--;
  339.                     break;
  340.                 /*
  341.                  * Continue - "\\c"
  342.                  */
  343.                 case 'c':
  344.                     Backc = 1;
  345.                     break;
  346.                 /*
  347.                  * Interpolate string - "\\*(NN" or "\\*N"
  348.                  */
  349.                 case '*':
  350.                     s1 = Asmcode(&s1, nm);
  351.                     s2 = Findstr(nm, NULL, 0);
  352.                     if (*s2 != '\0') {
  353.                         if ((strlen((char *)s2)
  354.                            + strlen((char *)s1+1) + 1)
  355.                         >=  MAXLINE)
  356.                         goto line_too_long;
  357.                         (void) sprintf((char *)buf, "%s%s",
  358.                         (char *)s2, (char *)s1+1);
  359.                         (void) strcpy((char *)&xbuf[1],
  360.                         (char *)buf);
  361.                         s1 = xbuf;
  362.                     }
  363.                     break;
  364.                 /*
  365.                  * Discretionary hyphen - "\\%"
  366.                  */
  367.                 case '%':
  368.                     if (Wordl <= 0)
  369.                         break;
  370.                     if ((i = Findhy(NULL, 0, 0)) < 0) {
  371.                         Error(WARN, LINE,
  372.                             " no hyphen for font ",
  373.                         (char *)Font);
  374.                         break;
  375.                     }
  376.                     Endword();
  377.                     Pass3(Wordl, Word, Wordx, NULL,
  378.                         Hychar[i].len);
  379.                     Pass3(NOBREAK,
  380.                         (unsigned char *) "hyphen", 6,
  381.                         Hychar[i].str, Hychar[i].len);
  382.                     Wordl = Wordx = 0;
  383.                     Uhyph = 1;
  384.                     break;
  385.                 /*
  386.                  * None of the above - may be special character
  387.                  * name.
  388.                  */
  389.                 default:
  390.                     s2 = --s1;
  391.                     s1 = Asmcode(&s1, nm);
  392.                     if ((i = Findchar(nm, 0, NULL, 0)) < 0){
  393.                         s1 = s2;
  394.                         goto ordinary_char;
  395.                     }
  396.                     if (strcmp((char *)nm, "em") == 0
  397.                     && Wordx > 0) {
  398.                     /*
  399.                      * "\\(em" is a special case when a word
  400.                      * has been assembled, because of
  401.                      * hyphenation.
  402.                      */
  403.                         Endword();
  404.                         Pass3(Wordl, Word, Wordx, NULL,
  405.                             Schar[i].len);
  406.                         Pass3(NOBREAK,
  407.                         (unsigned char *)"userhyphen",
  408.                             10, Schar[i].str, Schar[i].len);
  409.                             Wordl = Wordx = 0;
  410.                         period = '\0';
  411.                         Uhyph = 1;
  412.                         break;
  413.                      }
  414.                     /*
  415.                      * Interpolate a special character
  416.                      */
  417.                     if (Str2word(Schar[i].str,
  418.                         strlen((char *)Schar[i].str)) != 0)
  419.                         return;
  420.                         Wordl += Schar[i].len;
  421.                     period = '\0';
  422.                 }
  423.                 break;
  424.             /*
  425.              * Ordinary character
  426.              */
  427.             default:
  428. ordinary_char:
  429.                 if (Str2word(s1, 1) != 0)
  430.                     return;
  431.                 Wordl++;
  432.                 if (*s1 == '.' || *s1 == '!'
  433.                 ||  *s1 == '?' || *s1 == ':')
  434.                     period = '.';
  435.                 else if (period == '.') {
  436.                     nm[0] = *s1;
  437.                     nm[1] = '\0';
  438.                     if (regexec(Pat[13].pat, nm) == 0)
  439.                      period = '\0';
  440.                 }
  441.             }
  442.         }
  443.         /*
  444.          * End of line processing
  445.          */
  446.              if (!Backc) {
  447.             if (period == '.')
  448.                 Pass3(NOBREAK, (unsigned char *)"gap", 3,
  449.                     NULL, 2);
  450.             if (Centering > 0) {
  451.                 Pass3(DOBREAK, (unsigned char *)"center", 6,
  452.                     NULL, 0);
  453.                 Centering--;
  454.             } else if (!Fill)
  455.                 Pass3(DOBREAK, (unsigned char *)"flush", 5,
  456.                     NULL, 0);
  457.         }
  458.         if (Aftnxt == NULL)
  459.             return;
  460.         /* else fall through to process an "after next trap */
  461.     }
  462.     /*
  463.      * Special -man macro handling.
  464.      */
  465.     if (Marg == MANMACROS) {
  466.         /*
  467.          * A text line - "^[^.]" - is only processed when there is an
  468.          * "after next" directive.
  469.          */
  470.         if (*line != '.' && *line != '\'') {
  471.             if (Aftnxt != NULL) {
  472.                 if (regexec(Pat[9].pat, Aftnxt))  /* ",fP" */
  473.                     Font[0] = Prevfont;
  474.                 if (regexec(Pat[10].pat, Aftnxt))  /* ",tP" */
  475.                     Pass3(DOBREAK,
  476.                         (unsigned char *)"toindent",
  477.                         8, NULL, 0);
  478.                 Free(&Aftnxt);
  479.             }
  480.             return;
  481.         }
  482.         /*
  483.          * Special footer handling - "^.lF"
  484.          */
  485.         if (line[1] == 'l' && line[2] == 'F') {
  486.             s1 = Findstr((unsigned char *)"by", NULL, 0);
  487.             s2 = Findstr((unsigned char *)"nb", NULL, 0);
  488.             if (*s1 == '\0' || *s2 == '\0')
  489.                 (void) sprintf((char *)buf, "%s%s",
  490.                     (char *)s1, (char *)s2);
  491.             else
  492.                 (void) sprintf((char *)buf, "%s; %s",
  493.                     (char *)s1, (char *)s2);
  494.             Pass3(NOBREAK, (unsigned char *)"LF", 2, buf, 0);
  495.             return;
  496.         }
  497.     }
  498.     /*
  499.      * Special -me and -ms macro handling.
  500.      */
  501.     if (Marg == MEMACROS || Marg == MSMACROS) {
  502.         /*
  503.          * A text line - "^[^.]" - is only processed when there is an
  504.          * "after next" directive.
  505.          */
  506.         if (*line != '.' && *line != '\'') {
  507.             if (Aftnxt != NULL) {
  508.                 if (regexec(Pat[10].pat, Aftnxt))  /* ",tP" */
  509.                     Pass3(DOBREAK,
  510.                         (unsigned char *)"toindent",
  511.                         8, NULL, 0);
  512.                 Free(&Aftnxt);
  513.             }
  514.             return;
  515.         }
  516.         /*
  517.          * Numbered headings - "^[.']nH"
  518.          */
  519.         if (line[1] == 'n' && line[2] == 'H') {
  520.             s1 = Field(2, line, 0);
  521.             if (s1 != NULL) {
  522.                 i = atoi((char *)s1) - 1;    
  523.                 if (i < 0) {
  524.                     for (j = 0; j < MAXNHNR; j++) {
  525.                         Nhnr[j] = 0;
  526.                     }
  527.                     i = 0;
  528.                 } else if (i >= MAXNHNR) {
  529.                     (void) sprintf((char *)buf,
  530.                     " over NH limit (%d)", MAXNHNR);
  531.                     Error(WARN, LINE, (char *)buf, NULL);
  532.                 }
  533.             } else
  534.                 i = 0;
  535.             Nhnr[i]++;
  536.             for (j = i + 1; j < MAXNHNR; j++) {
  537.                 Nhnr[j] = 0;
  538.             }
  539.             s1 = buf;
  540.             for (j = 0; j <= i; j++) {
  541.                 (void) sprintf((char *)s1, "%d.", Nhnr[j]);
  542.                 s1 = buf + strlen((char *)buf);
  543.             }
  544.             (void) Findstr((unsigned char *)"Nh", buf, 1);
  545.             return;
  546.         }
  547.     }
  548.     /*
  549.      * Remaining lines should begin with a '.' or '\'' unless an "after next"
  550.      * trap has failed.
  551.      */
  552.     if (*line != '.' && *line != '\'') {
  553.         if (Aftnxt != NULL)
  554.             Error(WARN, LINE, " failed .it: ", (char *)Aftnxt);
  555.         else
  556.             Error(WARN, LINE, " unrecognized line ", NULL);
  557.         return;
  558.     }
  559.     brk = (*line == '.') ? DOBREAK : NOBREAK;
  560.     /*
  561.      * Evaluate expressions for "^[.'](ta|ll|ls|lt|in|ti|po|ne|sp|pl|nr)"
  562.      * Then process the requests.
  563.      */
  564.     if (regexec(Pat[11].pat, &line[1])) {
  565.         /*
  566.          * Establish default scale factor.
  567.          */
  568.         if ((line[1] == 'n' && line[2] == 'e')
  569.         ||  (line[1] == 's' && line[2] == 'p')
  570.         ||  (line[1] == 'p' && line[2] == 'l'))
  571.             exscale = Scalev;
  572.         else if (line[1] == 'n' && line[2] == 'r')
  573.             exscale = Scaleu;
  574.         else
  575.             exscale = Scalen;
  576.         /*
  577.          * Determine starting argument.
  578.          */
  579.         if (line[1] == 'n' && line[2] == 'r')
  580.             s1 = Field(2, &line[3], 0);
  581.         else
  582.             s1 = Field(1, &line[3], 0);
  583.         /*
  584.          * Evaluate expressions.
  585.          */
  586.         for (nexpr = 0; s1 != NULL &&*s1 != '\0'; ) {
  587.             while (*s1 == ' ' || *s1 == '\t')
  588.                 s1++;
  589.             if (*s1 == '+' || *s1 == '-')
  590.                 ssign = *s1++;
  591.             else
  592.                 ssign = '\0';
  593.             /*
  594.              * Process terms.
  595.              */
  596.             val = 0.0;
  597.             sp = -1;
  598.             c = '+';
  599.             s1--;
  600.             while (c == '+' || c == '*' || c == '%'
  601.             ||  c == ')' || c == '-' || c == '/') {
  602.                 op = c;
  603.                 s1++;
  604.                 tscale = exscale;
  605.                 tval = 0.0;
  606.             /*
  607.              * Pop stack on right parenthesis.
  608.              */
  609.                 if (op == ')') {
  610.                 tval = val;
  611.                 if (sp >= 0) {
  612.                     val = valstack[sp];
  613.                     op = opstack[sp];
  614.                     sp--;
  615.                 } else {
  616.                     Error(WARN, LINE,
  617.                     " expression stack underflow", NULL);
  618.                     return;
  619.                 }
  620.                 tscale = Scaleu;
  621.             /*
  622.              * Push stack on left parenthesis.
  623.              */
  624.                 } else if (*s1 == '(') {
  625.                 sp++;
  626.                 if (sp >= MAXSP) {
  627.                     Error(WARN, LINE,
  628.                        " expression stack overflow", NULL);
  629.                     return;
  630.                 }
  631.                 valstack[sp] = val;
  632.                 opstack[sp] = op;
  633.                 val = 0.0;
  634.                 c = '+';
  635.                 continue;
  636.                 } else if (*s1 == '\\') {
  637.                   s1++;
  638.                   switch(*s1) {
  639.             /*
  640.              * "\\"" begins a comment.
  641.              */
  642.                   case '"':
  643.                 while (*s1)
  644.                     s1++;
  645.                 break;
  646.             /*
  647.              * Crude width calculation for "\\w"
  648.              */
  649.                   case 'w':
  650.                 s2 = ++s1;
  651.                 if (*s1) {
  652.                     s1++;
  653.                     while (*s1 && *s1 != *s2)
  654.                     s1++;
  655.                     tval = (double) (s1 - s2 - 1) * Scalen;
  656.                     if (*s1)
  657.                     s1++;
  658.                 }
  659.                 break;
  660.             /*
  661.              * Interpolate number register if "\\n".
  662.              */
  663.                   case 'n':
  664.                 s1 = Asmcode(&s1, nm);
  665.                 if ((i = Findnum(nm, 0, 0)) >= 0)
  666.                     tval = Numb[i].val;
  667.                     s1++;
  668.                  }
  669.             /*
  670.              * Assemble numeric value.
  671.              */
  672.                 } else if (*s1 == '.' || isdigit(*s1)) {
  673.                 for (i = 0; isdigit(*s1) || *s1 == '.'; s1++) {
  674.                     if (*s1 == '.') {
  675.                     i = 10;
  676.                     continue;
  677.                     }
  678.                     d = (double) (*s1 - '0');
  679.                     if (i) {
  680.                     tval = tval + (d / (double) i);
  681.                     i = i * 10;
  682.                     } else
  683.                     tval = (tval * 10.0) + d;
  684.                 }
  685.                 } else {
  686.             /*
  687.              * It's not an expression.  Ignore extra scale.
  688.              */
  689.                 if ((i = Findscale((int)*s1, 0.0, 0)) < 0) {
  690.                     (void) sprintf((char *)buf,
  691.                     " \"%s\" isn't an expression",
  692.                     (char *)s1);
  693.                     Error(WARN, LINE, (char *)buf, NULL);
  694.                 }
  695.                 s1++;
  696.                 }
  697.             /*
  698.              * Add term to expression value.
  699.              */
  700.                 if ((i = Findscale((int)*s1, 0.0, 0)) >= 0) {
  701.                 tval *= Scale[i].val;
  702.                 s1++;
  703.                 } else
  704.                 tval *= tscale;
  705.                 switch (op) {
  706.                 case '+':
  707.                 val += tval;
  708.                 break;
  709.                 case '-':
  710.                 val -= tval;
  711.                 break;
  712.                 case '*':
  713.                 val *= tval;
  714.                 break;
  715.                 case '/':
  716.                 case '%':
  717.                 i = (int) val;
  718.                 j = (int) tval;
  719.                 if (j == 0) {
  720.                     Error(WARN, LINE,
  721.                     (*s1 == '/') ? "div" : "mod",
  722.                         " by 0");
  723.                     return;
  724.                 }
  725.                 if (op == '/')
  726.                     val = (double) (i / j);
  727.                 else
  728.                     val = (double) (i % j);
  729.                 break;
  730.                 }
  731.                 c = *s1;
  732.             }
  733.             /*
  734.              * Save expression value and sign.
  735.              */
  736.             if (nexpr >= MAXEXP) {
  737.                 (void) sprintf((char *)buf,
  738.                     " at expression limit of %d", MAXEXP);
  739.                 Error(WARN, LINE, (char *)buf, NULL);
  740.                 return;
  741.             }
  742.             exsign[nexpr] = ssign;
  743.             expr[nexpr] = val;
  744.             if (ssign == '-')
  745.                 sexpr[nexpr] = -1.0 * val;
  746.             else
  747.                 sexpr[nexpr] = val;
  748.             nexpr++;
  749.             while (*s1 == ' ' || *s1 == '\t')
  750.                 s1++;
  751.         }
  752.         /*
  753.          * Set parameters "(ll|ls|lt|in|ti|po|pl)"
  754.          */
  755.         if (regexec(Pat[12].pat, &line[1])) {
  756.             nm[0] = line[1];
  757.             nm[1] = line[2];
  758.             if ((i = Findparms(nm)) < 0) {
  759.                 Error(WARN, LINE,
  760.                     " can't find parameter register ",
  761.                     (char *)nm);
  762.                 return;
  763.             }
  764.             if (nexpr == 0 || exscale == 0.0)
  765.                 j = Parms[i].prev;
  766.             else if (exsign[0] == '\0'
  767.                  ||  (nm[0] == 't' && nm[1] == 'i'))
  768.                  j = (int)(sexpr[0] / exscale);
  769.             else
  770.                 j = Parms[i].val + (int)(sexpr[0] / exscale);
  771.             Parms[i].prev = Parms[i].val;
  772.             Parms[i].val = j;
  773.             nm[0] = (nexpr) ? exsign[0] : '\0';     /* for .ti */
  774.             nm[1] = '\0';
  775.             Pass3(brk, (unsigned char *)Parms[i].cmd, 
  776.                 Parms[i].cmdl, nm, j);
  777.             return;
  778.         }
  779.         if (line[1] == 'n') {
  780.             switch(line[2]) {
  781.         /*
  782.          * Need - "^[.']ne <expression>"
  783.          */
  784.             case 'e':
  785.                 if (nexpr && Scalev > 0.0)
  786.                     i = (int) ((expr[0]/Scalev) + 0.99);
  787.                 else
  788.                     i = 0;
  789.                 Pass3(DOBREAK, (unsigned char *)"need", 4,
  790.                     NULL, i);
  791.                 return;
  792.         /*
  793.          * Number - "^[.']nr <name> <expression>"
  794.          */
  795.             case 'r':
  796.                 if ((s1 = Field(2, line, 0)) == NULL) {
  797.                     Error(WARN, LINE, " bad number register",
  798.                         NULL);
  799.                     return;
  800.                 }
  801.                 if ((i = Findnum(s1, 0, 0)) < 0)
  802.                     i = Findnum(s1, 0, 1);
  803.                 if (nexpr < 1) {
  804.                     Numb[i].val = 0;
  805.                     return;
  806.                 }
  807.                 if (exsign[0] == '\0')
  808.                     Numb[i].val = (int) expr[0];
  809.                 else
  810.                     Numb[i].val += (int) sexpr[0];
  811.                 return;
  812.             }
  813.         }
  814.         /*
  815.          * Space - "^[.']sp <expression>"
  816.          */
  817.         if (line[1] == 's' && line[2] == 'p') {
  818.             if (nexpr == 0)
  819.                 i = 1;
  820.             else
  821.                 i = (int)((expr[0] / Scalev) + 0.99);
  822.             while (i--)
  823.                 Pass3(brk, (unsigned char *)"space", 5,
  824.                     NULL, 0);
  825.             return;
  826.         }
  827.         /*
  828.          * Tab positions - "^[.']ta <pos1> <pos2> . . ."
  829.          */
  830.              if (line[1] == 't' && line[2] == 'a') {
  831.             tval = 0.0;
  832.             for (j = 0; j < nexpr; j++) {
  833.                 if (exsign[j] == '\0')
  834.                     tval = expr[j];
  835.                 else
  836.                     tval += sexpr[j];
  837.                 Tabs[j] = (int) (tval / Scalen);
  838.             }
  839.             Ntabs = nexpr;
  840.             return;
  841.         }
  842.     }
  843.     /*
  844.      * Process all other nroff requests via Nreq().
  845.      */
  846.     (void) Nreq(line, brk);
  847.     return;
  848. }
  849.