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