Blob


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