slug.cc (15793B)
1 #include "misc.h" 2 #include "slug.h" 3 #include <math.h> 4 5 static char *bufptr(int); 6 7 void slug::coalesce() 8 { 9 (this+1)->dp = dp; // pretty grimy, but meant to ensure 10 // that all output goes out. 11 // maybe it has to skip over PT's; 12 // some stuff is getting pushed inside PT..END 13 } 14 15 void slug::neutralize() 16 { 17 switch (type) { 18 case PAGE: 19 case UF: 20 case BF: 21 case PARM: 22 type = NEUTRAL; 23 coalesce(); 24 break; 25 default: 26 ERROR "neutralized %d (%s) with %s\n", 27 type, typename(), headstr() WARNING; 28 break; 29 } 30 } 31 32 void slug::dump() // print contents of a slug 33 { 34 printf("# %d %-4.4s parm %d dv %d base %d s%d f%d H%d\n#\t\t%s\n", 35 serialno(), typename(), parm, dv, base, 36 size, font, hpos, headstr()); 37 } 38 39 char *slug::headstr() 40 { 41 const int HEADLEN = 65; 42 static char buf[2*HEADLEN]; 43 int j = 0; 44 char *s = bufptr(dp); 45 int n = (this+1)->dp - dp; 46 if (n >= HEADLEN) 47 n = HEADLEN; 48 for (int i = 0; i < n; i++) 49 switch (s[i]) { 50 case '\n': 51 case '\t': 52 case '\0': 53 case ' ': 54 break; 55 default: 56 buf[j++] = s[i]; 57 break; 58 } 59 buf[j] = 0; 60 return buf; 61 } 62 63 static char *strindex(char s[], char t[]) // index of earliest t[] in s[] 64 { 65 for (int i = 0; s[i] != '\0'; i++) { 66 int j, k; 67 for (j = i, k = 0; t[k]!='\0' && s[j] == t[k]; j++, k++) 68 ; 69 if (k > 0 && t[k] == '\0') 70 return s+i; 71 } 72 return 0; 73 } 74 75 void slug::slugout(int col) 76 { 77 static int numout = 0; 78 if (seen++) 79 ERROR "%s slug #%d seen %d times [%s]\n", 80 typename(), serialno(), seen, headstr() WARNING; 81 if (type == TM) { 82 char *p; 83 if ((p = strindex(bufptr(dp), "x X TM ")) != 0) 84 p += strlen("x X TM "); // skip junk 85 else 86 ERROR "strange TM [%s]\n", headstr() FATAL; 87 fprintf(stderr, "%d\t", userpn); // page # as prefix 88 for ( ; p < bufptr((this+1)->dp); p++) 89 putc(*p, stderr); 90 } else if (type == COORD) { 91 for (char *p = bufptr(dp); p < bufptr((this+1)->dp) && *p != '\n'; p++) 92 putc(*p, stdout); 93 printf(" # P %d X %d", userpn, hpos + col*offset); 94 return; 95 } else if (type == VBOX) { 96 if (numout++ > 0) // BUG??? might miss something 97 printf("s%d\nf%d\n", size, font); 98 printf("H%d\n", hpos + col*offset); 99 } 100 fwrite(bufptr(dp), sizeof(char), (this+1)->dp - dp, stdout); 101 } 102 103 char *slug::typename() 104 { 105 static char buf[50]; 106 char *p = buf; // return value 107 switch(type) { 108 case EOF: p = "EOF"; break; 109 case VBOX: p = "VBOX"; break; 110 case SP: p = "SP"; break; 111 case BS: p = "BS"; break; 112 case US: p = "US"; break; 113 case BF: p = "BF"; break; 114 case UF: p = "UF"; break; 115 case PT: p = "PT"; break; 116 case BT: p = "BT"; break; 117 case END: p = "END"; break; 118 case NEUTRAL: p = "NEUT"; break; 119 case PAGE: p = "PAGE"; break; 120 case TM: p = "TM"; break; 121 case COORD: p = "COORD"; break; 122 case NE: p = "NE"; break; 123 case CMD: p = "CMD"; break; 124 case PARM: p = "PARM"; break; 125 default: sprintf(buf, "weird type %d", type); 126 } 127 return p; 128 } 129 130 // ================================================================================ 131 132 // troff output-specific functions 133 134 // ================================================================================ 135 136 const int DELTABUF = 500000; // grow the input buffer in chunks 137 138 static char *inbuf = 0; // raw text input collects here 139 static int ninbuf = 0; // byte count for inbuf 140 static char *inbp = 0; // next free slot in inbuf 141 int linenum = 0; // input line number 142 143 static inline void addc(int c) { *inbp++ = c; } 144 145 static void adds(char *s) 146 { 147 for (char *p = s; *p; p++) 148 addc(*p); 149 } 150 151 static int fullrune(char *c, int n) 152 { 153 if(n <= 0) 154 return 0; 155 if(n>=1 && (unsigned char)c[0] < 0x80) 156 return 1; 157 if(n>=2 && (unsigned char)c[0] < 0xE0) 158 return 1; 159 if(n>=3) 160 return 1; 161 return 0; 162 } 163 164 static char *getutf(FILE *fp) // get 1 utf-encoded char (might be multiple bytes) 165 { 166 static char buf[100]; 167 char *p = buf; 168 169 for (*p = 0; (*p++ = getc(fp)) != EOF; ) { 170 *p = 0; 171 if (fullrune(buf, p-buf)) // found a valid character 172 break; 173 } 174 return buf; 175 } 176 177 static char *bufptr(int n) { return inbuf + n; } // scope of inbuf is too local 178 179 static inline int wherebuf() { return inbp - inbuf; } 180 181 static char *getstr(char *p, char *temp) 182 { // copy next non-blank string from p to temp, update p 183 while (*p == ' ' || *p == '\t' || *p == '\n') 184 p++; 185 if (*p == '\0') { 186 temp[0] = 0; 187 return(NULL); 188 } 189 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 190 *temp++ = *p++; 191 *temp = '\0'; 192 return(p); 193 } 194 195 /*************************************************************************** 196 bounding box of a circular arc Eric Grosse 24 May 84 197 198 Conceptually, this routine generates a list consisting of the start, 199 end, and whichever north, east, south, and west points lie on the arc. 200 The bounding box is then the range of this list. 201 list = {start,end} 202 j = quadrant(start) 203 k = quadrant(end) 204 if( j==k && long way 'round ) append north,west,south,east 205 else 206 while( j != k ) 207 append center+radius*[j-th of north,west,south,east unit vectors] 208 j += 1 (mod 4) 209 return( bounding box of list ) 210 The following code implements this, with simple optimizations. 211 ***********************************************************************/ 212 213 static int quadrant(double x, double y) 214 { 215 if ( x>=0.0 && y> 0.0) return(1); 216 else if( x< 0.0 && y>=0.0) return(2); 217 else if( x<=0.0 && y< 0.0) return(3); 218 else if( x> 0.0 && y<=0.0) return(4); 219 else return 0; /* shut up lint */ 220 } 221 222 static double xmin, ymin, xmax, ymax; // used by getDy 223 224 static void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc) 225 /* start, end, center */ 226 { /* assumes center isn't too far out */ 227 double r; 228 int j, k; 229 printf("#start %g,%g, end %g,%g, ctr %g,%g\n", x0,y0, x1,y1, xc,yc); 230 y0 = -y0; y1 = -y1; yc = -yc; // troff's up is eric's down 231 x0 -= xc; y0 -= yc; /* move to center */ 232 x1 -= xc; y1 -= yc; 233 xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1; 234 xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1; 235 r = sqrt(x0*x0 + y0*y0); 236 if (r > 0.0) { 237 j = quadrant(x0,y0); 238 k = quadrant(x1,y1); 239 if (j == k && y1*x0 < x1*y0) { 240 /* viewed as complex numbers, if Im(z1/z0)<0, arc is big */ 241 if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r; 242 if( xmax < r) xmax = r; if( ymax < r) ymax = r; 243 } else { 244 while (j != k) { 245 switch (j) { 246 case 1: if( ymax < r) ymax = r; break; /* north */ 247 case 2: if( xmin > -r) xmin = -r; break; /* west */ 248 case 3: if( ymin > -r) ymin = -r; break; /* south */ 249 case 4: if( xmax < r) xmax = r; break; /* east */ 250 } 251 j = j%4 + 1; 252 } 253 } 254 } 255 xmin += xc; ymin += yc; ymin = -ymin; 256 xmax += xc; ymax += yc; ymax = -ymax; 257 } 258 259 260 static int getDy(char *p, int *dx, int *maxv) 261 // figure out where we are after a D'...' 262 { 263 int x, y, x1, y1; // for input values 264 char temp[50]; 265 p++; // get to command letter 266 switch (*p++) { 267 case 'l': // line 268 sscanf(p, "%d %d", dx, &y); 269 return *maxv = y; 270 case 'a': // arc 271 sscanf(p, "%d %d %d %d", &x, &y, &x1, &y1); 272 *dx = x1 - x; 273 arc_extreme(0, 0, x+x1, y+y1, x, y); // sets [xy][max|min] 274 printf("#arc bounds x %g, %g; y %g, %g\n", 275 xmin, xmax, ymin, ymax); 276 *maxv = (int) (ymin+0.5); 277 return y + y1; 278 case '~': // spline 279 for (*dx = *maxv = y = 0; (p=getstr(p, temp)) != NULL; ) { 280 // above getstr() gets x value 281 *dx += atoi(temp); 282 p = getstr(p, temp); // this one gets y value 283 y += atoi(temp); 284 *maxv = max(*maxv, y); // ok??? 285 if (*p == '\n' || *p == 0) // input is a single line; 286 break; // don't walk off end if realloc 287 } 288 return y; 289 case 'c': // circle, ellipse 290 sscanf(p, "%d", dx); 291 *maxv = *dx/2; // high water mark is ht/2 292 return 0; 293 case 'e': 294 sscanf(p, "%d %d", dx, &y); 295 *maxv = y/2; // high water mark is ht/2 296 return 0; 297 default: // weird stuff 298 return 0; 299 } 300 } 301 302 static int serialnum = 0; 303 304 slug eofslug() 305 { 306 slug ret; 307 ret.serialnum = serialnum; 308 ret.type = EOF; 309 ret.dp = wherebuf(); 310 return ret; 311 } 312 313 slug getslug(FILE *fp) 314 { 315 if (inbuf == NULL) { 316 if ((inbuf = (char *) malloc(ninbuf = DELTABUF)) == NULL) 317 ERROR "no room for %d character input buffer\n", ninbuf FATAL; 318 inbp = inbuf; 319 } 320 if (wherebuf() > ninbuf-5000) { 321 // this is still flaky -- lines can be very long 322 int where = wherebuf(); // where we were 323 if ((inbuf = (char *) realloc(inbuf, ninbuf += DELTABUF)) == NULL) 324 ERROR "no room for %d character input buffer\n", ninbuf FATAL; 325 ERROR "grew input buffer to %d characters\n", ninbuf WARNING; 326 inbp = inbuf + where; // same offset in new array 327 } 328 static int baseV = 0; // first V command of preceding slug 329 static int curV = 0, curH = 0; 330 static int font = 0, size = 0; 331 static int baseadj = 0; 332 static int ncol = 1, offset = 0; // multi-column stuff 333 char str[1000], str2[1000], buf[3000], *p; 334 int firstV = 0, firstH = 0; 335 int maxV = curV; 336 int ocurV = curV, mxv = 0, dx = 0; 337 int sawD = 0; // > 0 if have seen D... 338 slug ret; 339 ret.serialnum = serialnum++; 340 ret.type = VBOX; // use the same as last by default 341 ret.dv = curV - baseV; 342 ret.hpos = curH; 343 ret.base = ret.parm = ret.parm2 = ret.seen = 0; 344 ret.font = font; 345 ret.size = size; 346 ret.dp = wherebuf(); 347 ret.ncol = ncol; 348 ret.offset = offset; 349 ret.linenum = linenum; // might be low 350 351 for (;;) { 352 int c, m, n; // for input values 353 int sign; // hoisted from case 'h' below 354 switch (c = getc(fp)) { 355 case EOF: 356 ret.type = EOF; 357 ret.dv = 0; 358 if (baseadj) 359 printf("# adjusted %d bases\n", baseadj); 360 printf("# %d characters, %d lines\n", wherebuf(), linenum); 361 return ret; 362 case 'V': 363 fscanf(fp, "%d", &n); 364 if (firstV++ == 0) { 365 ret.dv = n - baseV; 366 baseV = n; 367 } else { 368 sprintf(buf, "v%d", n - curV); 369 adds(buf); 370 } 371 curV = n; 372 maxV = max(maxV, curV); 373 break; 374 case 'H': // absolute H motion 375 fscanf(fp, "%d", &n); 376 if (firstH++ == 0) { 377 ret.hpos = n; 378 } else { 379 sprintf(buf, "h%d", n - curH); 380 adds(buf); 381 } 382 curH = n; 383 break; 384 case 'h': // relative H motion 385 addc(c); 386 sign = 1; 387 if ((c = getc(fp)) == '-') { 388 addc(c); 389 sign = -1; 390 c = getc(fp); 391 } 392 for (n = 0; isdigit(c); c = getc(fp)) { 393 addc(c); 394 n = 10 * n + c - '0'; 395 } 396 curH += n * sign; 397 ungetc(c, fp); 398 break; 399 case 'x': // device control: x ... 400 addc(c); 401 fgets(buf, (int) sizeof(buf), fp); 402 linenum++; 403 adds(buf); 404 if (buf[0] == ' ' && buf[1] == 'X') { // x X ... 405 if (2 != sscanf(buf+2, "%s %d", str, &n)) 406 n = 0; 407 if (eq(str, "SP")) { // X SP n 408 ret.type = SP; // paddable SPace 409 ret.dv = n; // of height n 410 } else if (eq(str, "BS")) { 411 ret.type = BS; // Breakable Stream 412 ret.parm = n; // >=n VBOXES on a page 413 } else if (eq(str, "BF")) { 414 ret.type = BF; // Breakable Float 415 ret.parm = ret.parm2 = n; 416 // n = pref center (as UF) 417 } else if (eq(str, "US")) { 418 ret.type = US; // Unbreakable Stream 419 ret.parm = n; 420 } else if (eq(str, "UF")) { 421 ret.type = UF; // Unbreakable Float 422 ret.parm = ret.parm2 = n; 423 // n = preferred center 424 // to select several, 425 // use several UF lines 426 } else if (eq(str, "PT")) { 427 ret.type = PT; // Page Title 428 ret.parm = n; 429 } else if (eq(str, "BT")) { 430 ret.type = BT; // Bottom Title 431 ret.parm = n; 432 } else if (eq(str, "END")) { 433 ret.type = END; 434 ret.parm = n; 435 } else if (eq(str, "TM")) { 436 ret.type = TM; // Terminal Message 437 ret.dv = 0; 438 } else if (eq(str, "COORD")) { 439 ret.type = COORD;// page COORDinates 440 ret.dv = 0; 441 } else if (eq(str, "NE")) { 442 ret.type = NE; // NEed to break page 443 ret.dv = n; // if <n units left 444 } else if (eq(str, "MC")) { 445 ret.type = MC; // Multiple Columns 446 sscanf(buf+2, "%s %d %d", 447 str, &ncol, &offset); 448 ret.ncol = ncol; 449 ret.offset = offset; 450 } else if (eq(str, "CMD")) { 451 ret.type = CMD; // CoMmaNd 452 sscanf(buf+2, "%s %s", str2, str); 453 if (eq(str, "FC")) // Freeze 2-Col 454 ret.parm = FC; 455 else if (eq(str, "FL")) // FLush 456 ret.parm = FL; 457 else if (eq(str, "BP")) // Break Page 458 ret.parm = BP; 459 else ERROR "unknown command %s\n", 460 str WARNING; 461 } else if (eq(str, "PARM")) { 462 ret.type = PARM;// PARaMeter 463 sscanf(buf+2, "%s %s %d", str2, str, &ret.parm2); 464 if (eq(str, "NP")) // New Page 465 ret.parm = NP; 466 else if (eq(str, "FO")) // FOoter 467 ret.parm = FO; 468 else if (eq(str, "PL")) // Page Length 469 ret.parm = PL; 470 else if (eq(str, "MF")) // MinFull 471 ret.parm = MF; 472 else if (eq(str, "CT")) // ColTol 473 ret.parm = CT; 474 else if (eq(str, "WARN")) //WARNings? 475 ret.parm = WARN; 476 else if (eq(str, "DBG"))// DeBuG 477 ret.parm = DBG; 478 else ERROR "unknown parameter %s\n", 479 str WARNING; 480 } else 481 break; // out of switch 482 if (firstV > 0) 483 ERROR "weird x X %s in mid-VBOX\n", 484 str WARNING; 485 return ret; 486 } 487 break; 488 case 'n': // end of line 489 fscanf(fp, "%d %d", &n, &m); 490 ret.ht = n; 491 ret.base = m; 492 getc(fp); // newline 493 linenum++; 494 sprintf(buf, "n%d %d\n", ret.ht, ret.base); 495 adds(buf); 496 if (!firstV++) 497 baseV = curV; 498 // older incarnations of this program used ret.base 499 // in complicated and unreliable ways; 500 // example: if ret.ht + ret.base < ret.dv, ret.base = 0 501 // this was meant to avoid double-counting the space 502 // around displayed equations; it didn't work 503 // Now, we believe ret.base = 0, otherwise we give it 504 // a value we have computed. 505 if (ret.base == 0 && sawD == 0) 506 return ret; // don't fiddle 0-bases 507 if (ret.base != maxV - baseV) { 508 ret.base = maxV - baseV; 509 baseadj++; 510 } 511 if (ret.type != VBOX) 512 ERROR "%s slug (type %d) has base = %d\n", 513 ret.typename(), ret.type, ret.base WARNING; 514 return ret; 515 case 'p': // new page 516 fscanf(fp, "%d", &n); 517 ret.type = PAGE; 518 curV = baseV = ret.dv = 0; 519 ret.parm = n; // just in case someone needs it 520 return ret; 521 case 's': // size change snnn 522 fscanf(fp, "%d", &size); 523 sprintf(buf, "s%d\n", size); 524 adds(buf); 525 break; 526 case 'f': // font fnnn 527 fscanf(fp, "%d", &font); 528 sprintf(buf, "f%d\n", font); 529 adds(buf); 530 break; 531 case '\n': 532 linenum++; 533 /* fall through */ 534 case ' ': 535 addc(c); 536 break; 537 case '0': case '1': case '2': case '3': case '4': 538 case '5': case '6': case '7': case '8': case '9': 539 // two motion digits plus a character 540 addc(c); 541 n = c - '0'; 542 addc(c = getc(fp)); 543 curH += 10 * n + c - '0'; 544 adds(getutf(fp)); 545 if (!firstV++) 546 baseV = curV; 547 break; 548 case 'c': // single ascii character 549 addc(c); 550 adds(getutf(fp)); 551 if (!firstV++) 552 baseV = curV; 553 break; 554 case 'C': // Cxyz\n 555 case 'N': // Nnnn\n 556 addc(c); 557 while ((c = getc(fp)) != ' ' && c != '\n') 558 addc(c); 559 addc(c); 560 if (!firstV++) 561 baseV = curV; 562 linenum++; 563 break; 564 case 'D': // draw function: D.*\n 565 sawD++; 566 p = bufptr(wherebuf()); // where does the D start 567 addc(c); 568 while ((c = getc(fp)) != '\n') 569 addc(c); 570 addc(c); 571 if (!firstV++) 572 baseV = curV; 573 ocurV = curV, mxv = 0, dx = 0; 574 curV += getDy(p, &dx, &mxv); // figure out how big it is 575 maxV = max(max(maxV, curV), ocurV+mxv); 576 curH += dx; 577 linenum++; 578 break; 579 case 'v': // relative vertical vnnn 580 addc(c); 581 if (!firstV++) 582 baseV = curV; 583 sign = 1; 584 if ((c = getc(fp)) == '-') { 585 addc(c); 586 sign = -1; 587 c = getc(fp); 588 } 589 for (n = 0; isdigit(c); c = getc(fp)) { 590 addc(c); 591 n = 10 * n + c - '0'; 592 } 593 ungetc(c, fp); 594 curV += n * sign; 595 maxV = max(maxV, curV); 596 addc('\n'); 597 break; 598 case 'w': // word space 599 addc(c); 600 break; 601 case '#': // comment 602 addc(c); 603 while ((c = getc(fp)) != '\n') 604 addc(c); 605 addc('\n'); 606 linenum++; 607 break; 608 default: 609 ERROR "unknown input character %o %c (%50.50s)\n", 610 c, c, bufptr(wherebuf()-50) WARNING; 611 abort(); 612 break; 613 } 614 } 615 }