plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

slug.cc (15793B)


      1 #include	"misc.h"
      2 #include	"slug.h"
      3 #include	<math.h>
      4 
      5 static char	*bufptr(int);
      6 
      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 }
     14 
     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 }
     31 
     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 }
     38 
     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 }
     62 
     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 }
     74 
     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);
    101 }
    102 
    103 char *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 
    136 const int	DELTABUF = 500000;	// grow the input buffer in chunks
    137 
    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
    142 
    143 static inline void addc(int c) { *inbp++ = c; }
    144 
    145 static void adds(char *s)
    146 {
    147 	for (char *p = s; *p; p++)
    148 		addc(*p);
    149 }
    150 
    151 static 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 
    164 static 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;
    171 		if (fullrune(buf, p-buf))	// found a valid character
    172 			break;
    173 	}
    174 	return buf;
    175 }
    176 
    177 static char *bufptr(int n) { return inbuf + n; }  // scope of inbuf is too local
    178 
    179 static inline int wherebuf() { return inbp - inbuf; }
    180 
    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);
    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 
    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 ***********************************************************************/
    212 
    213 static 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 
    222 static double xmin, ymin, xmax, ymax;	// used by getDy
    223 
    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 */
    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 
    260 static 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 
    302 static int serialnum = 0;
    303 
    304 slug eofslug()
    305 {
    306 	slug ret;
    307 	ret.serialnum = serialnum;
    308 	ret.type = EOF;
    309 	ret.dp = wherebuf();
    310 	return ret;
    311 }
    312 
    313 slug 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 }