6 static char *bufptr(int);
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
16 void slug::neutralize()
27 ERROR "neutralized %d (%s) with %s\n",
28 type, typename(), headstr() WARNING;
33 void slug::dump() // print contents of a slug
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());
42 const int HEADLEN = 65;
43 static char buf[2*HEADLEN];
46 int n = (this+1)->dp - dp;
49 for (int i = 0; i < n; i++)
64 static char *strindex(char s[], char t[]) // index of earliest t[] in s[]
66 for (int i = 0; s[i] != '\0'; i++) {
68 for (j = i, k = 0; t[k]!='\0' && s[j] == t[k]; j++, k++)
70 if (k > 0 && t[k] == '\0')
76 void slug::slugout(int col)
78 static int numout = 0;
80 ERROR "%s slug #%d seen %d times [%s]\n",
81 typename(), serialno(), seen, headstr() WARNING;
84 if ((p = strindex(bufptr(dp), "x X TM ")) != 0)
85 p += strlen("x X TM "); // skip junk
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++)
91 } else if (type == COORD) {
92 for (char *p = bufptr(dp); p < bufptr((this+1)->dp) && *p != '\n'; p++)
94 printf(" # P %d X %d", userpn, hpos + col*offset);
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()
107 char *p = buf; // return value
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);
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++)
152 static char *getutf(FILE *fp) // get 1 utf-encoded char (might be multiple bytes)
154 static char buf[100];
157 for (*p = 0; (*p++ = getc(fp)) != EOF; ) {
159 if (mblen(buf, sizeof buf) > 0) // found a valid character
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')
177 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
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.
192 if( j==k && long way 'round ) append north,west,south,east
195 append center+radius*[j-th of north,west,south,east unit vectors]
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 */
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 */
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);
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;
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 */
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
253 p++; // get to command letter
256 sscanf(p, "%d %d", dx, &y);
259 sscanf(p, "%d %d %d %d", &x, &y, &x1, &y1);
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);
267 for (*dx = *maxv = y = 0; (p=getstr(p, temp)) != NULL; ) {
268 // above getstr() gets x value
270 p = getstr(p, temp); // this one gets y value
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
277 case 'c': // circle, ellipse
279 *maxv = *dx/2; // high water mark is ht/2
282 sscanf(p, "%d %d", dx, &y);
283 *maxv = y/2; // high water mark is ht/2
285 default: // weird stuff
290 static int serialnum = 0;
295 ret.serialnum = serialnum;
301 slug getslug(FILE *fp)
304 if ((inbuf = (char *) malloc(ninbuf = DELTABUF)) == NULL)
305 ERROR "no room for %d character input buffer\n", ninbuf FATAL;
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;
324 int ocurV = curV, mxv = 0, dx = 0;
325 int sawD = 0; // > 0 if have seen D...
327 ret.serialnum = serialnum++;
328 ret.type = VBOX; // use the same as last by default
329 ret.dv = curV - baseV;
331 ret.base = ret.parm = ret.parm2 = ret.seen = 0;
337 ret.linenum = linenum; // might be low
340 int c, m, n; // for input values
341 int sign; // hoisted from case 'h' below
342 switch (c = getc(fp)) {
347 printf("# adjusted %d bases\n", baseadj);
348 printf("# %d characters, %d lines\n", wherebuf(), linenum);
351 fscanf(fp, "%d", &n);
356 sprintf(buf, "v%d", n - curV);
360 maxV = max(maxV, curV);
362 case 'H': // absolute H motion
363 fscanf(fp, "%d", &n);
367 sprintf(buf, "h%d", n - curH);
372 case 'h': // relative H motion
375 if ((c = getc(fp)) == '-') {
380 for (n = 0; isdigit(c); c = getc(fp)) {
382 n = 10 * n + c - '0';
387 case 'x': // device control: x ...
389 fgets(buf, (int) sizeof(buf), fp);
392 if (buf[0] == ' ' && buf[1] == 'X') { // x X ...
393 if (2 != sscanf(buf+2, "%s %d", str, &n))
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
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
417 } else if (eq(str, "BT")) {
418 ret.type = BT; // Bottom Title
420 } else if (eq(str, "END")) {
423 } else if (eq(str, "TM")) {
424 ret.type = TM; // Terminal Message
426 } else if (eq(str, "COORD")) {
427 ret.type = COORD;// page COORDinates
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);
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
443 else if (eq(str, "FL")) // FLush
445 else if (eq(str, "BP")) // Break Page
447 else ERROR "unknown command %s\n",
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
454 else if (eq(str, "FO")) // FOoter
456 else if (eq(str, "PL")) // Page Length
458 else if (eq(str, "MF")) // MinFull
460 else if (eq(str, "CT")) // ColTol
462 else if (eq(str, "WARN")) //WARNings?
464 else if (eq(str, "DBG"))// DeBuG
466 else ERROR "unknown parameter %s\n",
469 break; // out of switch
471 ERROR "weird x X %s in mid-VBOX\n",
476 case 'n': // end of line
477 fscanf(fp, "%d %d", &n, &m);
482 sprintf(buf, "n%d %d\n", ret.ht, ret.base);
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;
499 if (ret.type != VBOX)
500 ERROR "%s slug (type %d) has base = %d\n",
501 ret.typename(), ret.type, ret.base WARNING;
503 case 'p': // new page
504 fscanf(fp, "%d", &n);
506 curV = baseV = ret.dv = 0;
507 ret.parm = n; // just in case someone needs it
509 case 's': // size change snnn
510 fscanf(fp, "%d", &size);
511 sprintf(buf, "s%d\n", size);
514 case 'f': // font fnnn
515 fscanf(fp, "%d", &font);
516 sprintf(buf, "f%d\n", font);
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
531 curH += 10 * n + c - '0';
536 case 'c': // single ascii character
545 while ((c = getc(fp)) != ' ' && c != '\n')
552 case 'D': // draw function: D.*\n
554 p = bufptr(wherebuf()); // where does the D start
556 while ((c = getc(fp)) != '\n')
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);
567 case 'v': // relative vertical vnnn
572 if ((c = getc(fp)) == '-') {
577 for (n = 0; isdigit(c); c = getc(fp)) {
579 n = 10 * n + c - '0';
583 maxV = max(maxV, curV);
586 case 'w': // word space
591 while ((c = getc(fp)) != '\n')
597 ERROR "unknown input character %o %c (%50.50s)\n",
598 c, c, bufptr(wherebuf()-50) WARNING;