Blob


1 #include "misc.h"
2 #include "slug.h"
3 #include <math.h>
5 static char *bufptr(int);
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 }
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 }
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 }
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 }
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 }
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);
103 char *slug::typename()
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);
127 return p;
130 // ================================================================================
132 // troff output-specific functions
134 // ================================================================================
136 const int DELTABUF = 500000; // grow the input buffer in chunks
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
143 static inline void addc(int c) { *inbp++ = c; }
145 static void adds(char *s)
147 for (char *p = s; *p; p++)
148 addc(*p);
151 static int fullrune(char *c, int n)
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;
164 static char *getutf(FILE *fp) // get 1 utf-encoded char (might be multiple bytes)
166 static char buf[100];
167 char *p = buf;
169 for (*p = 0; (*p++ = getc(fp)) != EOF; ) {
170 *p = 0;
171 if (fullrune(buf, p-buf)) // found a valid character
172 break;
174 return buf;
177 static char *bufptr(int n) { return inbuf + n; } // scope of inbuf is too local
179 static inline int wherebuf() { return inbp - inbuf; }
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);
189 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
190 *temp++ = *p++;
191 *temp = '\0';
192 return(p);
195 /***************************************************************************
196 bounding box of a circular arc Eric Grosse 24 May 84
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 ***********************************************************************/
213 static int quadrant(double x, double y)
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 */
222 static double xmin, ymin, xmax, ymax; // used by getDy
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 */
251 j = j%4 + 1;
255 xmin += xc; ymin += yc; ymin = -ymin;
256 xmax += xc; ymax += yc; ymax = -ymax;
260 static int getDy(char *p, int *dx, int *maxv)
261 // figure out where we are after a D'...'
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
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;
302 static int serialnum = 0;
304 slug eofslug()
306 slug ret;
307 ret.serialnum = serialnum;
308 ret.type = EOF;
309 ret.dp = wherebuf();
310 return ret;
313 slug getslug(FILE *fp)
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;
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
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
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);
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);
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);
392 for (n = 0; isdigit(c); c = getc(fp)) {
393 addc(c);
394 n = 10 * n + c - '0';
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;
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++;
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);
589 for (n = 0; isdigit(c); c = getc(fp)) {
590 addc(c);
591 n = 10 * n + c - '0';
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;