blob: 92d48db30ce25061018b9afe4a5c410fa0ed9d4e [file] [log] [blame]
wkj5f1cf8e2004-05-16 07:56:41 +00001#include "misc.h"
2#include "slug.h"
wkj5f1cf8e2004-05-16 07:56:41 +00003#include <math.h>
4
5static char *bufptr(int);
6
7void 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
15void 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
32void 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
39char *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
63static 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
75void 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
103char *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
136const int DELTABUF = 500000; // grow the input buffer in chunks
137
138static char *inbuf = 0; // raw text input collects here
139static int ninbuf = 0; // byte count for inbuf
140static char *inbp = 0; // next free slot in inbuf
141int linenum = 0; // input line number
142
143static inline void addc(int c) { *inbp++ = c; }
144
145static void adds(char *s)
146{
147 for (char *p = s; *p; p++)
148 addc(*p);
149}
150
rsc05f5d462006-02-17 18:21:29 +0000151static 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
wkj5f1cf8e2004-05-16 07:56:41 +0000164static 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;
rsc54357362006-02-14 19:42:58 +0000171 if (fullrune(buf, p-buf)) // found a valid character
wkj5f1cf8e2004-05-16 07:56:41 +0000172 break;
173 }
174 return buf;
175}
176
177static char *bufptr(int n) { return inbuf + n; } // scope of inbuf is too local
178
179static inline int wherebuf() { return inbp - inbuf; }
180
181static 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
198Conceptually, this routine generates a list consisting of the start,
199end, and whichever north, east, south, and west points lie on the arc.
200The 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 )
210The following code implements this, with simple optimizations.
211***********************************************************************/
212
213static 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
222static double xmin, ymin, xmax, ymax; // used by getDy
223
224static 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
260static 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
302static int serialnum = 0;
303
304slug eofslug()
305{
306 slug ret;
307 ret.serialnum = serialnum;
308 ret.type = EOF;
309 ret.dp = wherebuf();
310 return ret;
311}
312
313slug 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}