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 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