5 static char *bufptr(int);
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
15 void slug::neutralize()
26 ERROR "neutralized %d (%s) with %s\n",
27 type, typename(), headstr() WARNING;
32 void slug::dump() // print contents of a slug
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());
41 const int HEADLEN = 65;
42 static char buf[2*HEADLEN];
45 int n = (this+1)->dp - dp;
48 for (int i = 0; i < n; i++)
63 static char *strindex(char s[], char t[]) // index of earliest t[] in s[]
65 for (int i = 0; s[i] != '\0'; i++) {
67 for (j = i, k = 0; t[k]!='\0' && s[j] == t[k]; j++, k++)
69 if (k > 0 && t[k] == '\0')
75 void slug::slugout(int col)
77 static int numout = 0;
79 ERROR "%s slug #%d seen %d times [%s]\n",
80 typename(), serialno(), seen, headstr() WARNING;
83 if ((p = strindex(bufptr(dp), "x X TM ")) != 0)
84 p += strlen("x X TM "); // skip junk
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++)
90 } else if (type == COORD) {
91 for (char *p = bufptr(dp); p < bufptr((this+1)->dp) && *p != '\n'; p++)
93 printf(" # P %d X %d", userpn, hpos + col*offset);
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);
100 fwrite(bufptr(dp), sizeof(char), (this+1)->dp - dp, stdout);
103 char *slug::typename()
106 char *p = buf; // return value
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);
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++)
151 static int fullrune(char *c, int n)
155 if(n>=1 && (unsigned char)c[0] < 0x80)
157 if(n>=2 && (unsigned char)c[0] < 0xE0)
164 static char *getutf(FILE *fp) // get 1 utf-encoded char (might be multiple bytes)
166 static char buf[100];
169 for (*p = 0; (*p++ = getc(fp)) != EOF; ) {
171 if (fullrune(buf, p-buf)) // found a valid character
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')
189 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
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.
204 if( j==k && long way 'round ) append north,west,south,east
207 append center+radius*[j-th of north,west,south,east unit vectors]
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 */
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 */
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);
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;
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 */
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
265 p++; // get to command letter
268 sscanf(p, "%d %d", dx, &y);
271 sscanf(p, "%d %d %d %d", &x, &y, &x1, &y1);
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);
279 for (*dx = *maxv = y = 0; (p=getstr(p, temp)) != NULL; ) {
280 // above getstr() gets x value
282 p = getstr(p, temp); // this one gets y value
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
289 case 'c': // circle, ellipse
291 *maxv = *dx/2; // high water mark is ht/2
294 sscanf(p, "%d %d", dx, &y);
295 *maxv = y/2; // high water mark is ht/2
297 default: // weird stuff
302 static int serialnum = 0;
307 ret.serialnum = serialnum;
313 slug getslug(FILE *fp)
316 if ((inbuf = (char *) malloc(ninbuf = DELTABUF)) == NULL)
317 ERROR "no room for %d character input buffer\n", ninbuf FATAL;
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;
336 int ocurV = curV, mxv = 0, dx = 0;
337 int sawD = 0; // > 0 if have seen D...
339 ret.serialnum = serialnum++;
340 ret.type = VBOX; // use the same as last by default
341 ret.dv = curV - baseV;
343 ret.base = ret.parm = ret.parm2 = ret.seen = 0;
349 ret.linenum = linenum; // might be low
352 int c, m, n; // for input values
353 int sign; // hoisted from case 'h' below
354 switch (c = getc(fp)) {
359 printf("# adjusted %d bases\n", baseadj);
360 printf("# %d characters, %d lines\n", wherebuf(), linenum);
363 fscanf(fp, "%d", &n);
368 sprintf(buf, "v%d", n - curV);
372 maxV = max(maxV, curV);
374 case 'H': // absolute H motion
375 fscanf(fp, "%d", &n);
379 sprintf(buf, "h%d", n - curH);
384 case 'h': // relative H motion
387 if ((c = getc(fp)) == '-') {
392 for (n = 0; isdigit(c); c = getc(fp)) {
394 n = 10 * n + c - '0';
399 case 'x': // device control: x ...
401 fgets(buf, (int) sizeof(buf), fp);
404 if (buf[0] == ' ' && buf[1] == 'X') { // x X ...
405 if (2 != sscanf(buf+2, "%s %d", str, &n))
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
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
429 } else if (eq(str, "BT")) {
430 ret.type = BT; // Bottom Title
432 } else if (eq(str, "END")) {
435 } else if (eq(str, "TM")) {
436 ret.type = TM; // Terminal Message
438 } else if (eq(str, "COORD")) {
439 ret.type = COORD;// page COORDinates
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);
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
455 else if (eq(str, "FL")) // FLush
457 else if (eq(str, "BP")) // Break Page
459 else ERROR "unknown command %s\n",
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
466 else if (eq(str, "FO")) // FOoter
468 else if (eq(str, "PL")) // Page Length
470 else if (eq(str, "MF")) // MinFull
472 else if (eq(str, "CT")) // ColTol
474 else if (eq(str, "WARN")) //WARNings?
476 else if (eq(str, "DBG"))// DeBuG
478 else ERROR "unknown parameter %s\n",
481 break; // out of switch
483 ERROR "weird x X %s in mid-VBOX\n",
488 case 'n': // end of line
489 fscanf(fp, "%d %d", &n, &m);
494 sprintf(buf, "n%d %d\n", ret.ht, ret.base);
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;
511 if (ret.type != VBOX)
512 ERROR "%s slug (type %d) has base = %d\n",
513 ret.typename(), ret.type, ret.base WARNING;
515 case 'p': // new page
516 fscanf(fp, "%d", &n);
518 curV = baseV = ret.dv = 0;
519 ret.parm = n; // just in case someone needs it
521 case 's': // size change snnn
522 fscanf(fp, "%d", &size);
523 sprintf(buf, "s%d\n", size);
526 case 'f': // font fnnn
527 fscanf(fp, "%d", &font);
528 sprintf(buf, "f%d\n", font);
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
543 curH += 10 * n + c - '0';
548 case 'c': // single ascii character
557 while ((c = getc(fp)) != ' ' && c != '\n')
564 case 'D': // draw function: D.*\n
566 p = bufptr(wherebuf()); // where does the D start
568 while ((c = getc(fp)) != '\n')
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);
579 case 'v': // relative vertical vnnn
584 if ((c = getc(fp)) == '-') {
589 for (n = 0; isdigit(c); c = getc(fp)) {
591 n = 10 * n + c - '0';
595 maxV = max(maxV, curV);
598 case 'w': // word space
603 while ((c = getc(fp)) != '\n')
609 ERROR "unknown input character %o %c (%50.50s)\n",
610 c, c, bufptr(wherebuf()-50) WARNING;