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