|  | #include	"misc.h" | 
|  | #include	"slug.h" | 
|  | #include	<math.h> | 
|  |  | 
|  | static char	*bufptr(int); | 
|  |  | 
|  | void slug::coalesce() | 
|  | { | 
|  | (this+1)->dp = dp;	// pretty grimy, but meant to ensure | 
|  | // that all output goes out. | 
|  | // maybe it has to skip over PT's; | 
|  | // some stuff is getting pushed inside PT..END | 
|  | } | 
|  |  | 
|  | void slug::neutralize() | 
|  | { | 
|  | switch (type) { | 
|  | case PAGE: | 
|  | case UF: | 
|  | case BF: | 
|  | case PARM: | 
|  | type = NEUTRAL; | 
|  | coalesce(); | 
|  | break; | 
|  | default: | 
|  | ERROR "neutralized %d (%s) with %s\n", | 
|  | type, typename(), headstr() WARNING; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void slug::dump()	// print contents of a slug | 
|  | { | 
|  | printf("# %d %-4.4s parm %d dv %d base %d s%d f%d H%d\n#\t\t%s\n", | 
|  | serialno(), typename(), parm, dv, base, | 
|  | size, font, hpos, headstr()); | 
|  | } | 
|  |  | 
|  | char *slug::headstr() | 
|  | { | 
|  | const int HEADLEN = 65; | 
|  | static char buf[2*HEADLEN]; | 
|  | int j = 0; | 
|  | char *s = bufptr(dp); | 
|  | int n = (this+1)->dp - dp; | 
|  | if (n >= HEADLEN) | 
|  | n = HEADLEN; | 
|  | for (int i = 0; i < n; i++) | 
|  | switch (s[i]) { | 
|  | case '\n': | 
|  | case '\t': | 
|  | case '\0': | 
|  | case ' ': | 
|  | break; | 
|  | default: | 
|  | buf[j++] = s[i]; | 
|  | break; | 
|  | } | 
|  | buf[j] = 0; | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static char *strindex(char s[], char t[])	// index of earliest t[] in s[] | 
|  | { | 
|  | for (int i = 0; s[i] != '\0'; i++) { | 
|  | int j, k; | 
|  | for (j = i, k = 0; t[k]!='\0' && s[j] == t[k]; j++, k++) | 
|  | ; | 
|  | if (k > 0 && t[k] == '\0') | 
|  | return s+i; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void slug::slugout(int col) | 
|  | { | 
|  | static int numout = 0; | 
|  | if (seen++) | 
|  | ERROR "%s slug #%d seen %d times [%s]\n", | 
|  | typename(), serialno(), seen, headstr() WARNING; | 
|  | if (type == TM) { | 
|  | char *p; | 
|  | if ((p = strindex(bufptr(dp), "x X TM ")) != 0) | 
|  | p += strlen("x X TM ");		// skip junk | 
|  | else | 
|  | ERROR "strange TM [%s]\n", headstr() FATAL; | 
|  | fprintf(stderr, "%d\t", userpn);	// page # as prefix | 
|  | for ( ; p < bufptr((this+1)->dp); p++) | 
|  | putc(*p, stderr); | 
|  | } else if (type == COORD) { | 
|  | for (char *p = bufptr(dp); p < bufptr((this+1)->dp) && *p != '\n'; p++) | 
|  | putc(*p, stdout); | 
|  | printf(" # P %d X %d", userpn, hpos + col*offset); | 
|  | return; | 
|  | } else if (type == VBOX) { | 
|  | if (numout++ > 0)	// BUG??? might miss something | 
|  | printf("s%d\nf%d\n", size, font); | 
|  | printf("H%d\n", hpos + col*offset); | 
|  | } | 
|  | fwrite(bufptr(dp), sizeof(char), (this+1)->dp - dp, stdout); | 
|  | } | 
|  |  | 
|  | char *slug::typename() | 
|  | { | 
|  | static char buf[50]; | 
|  | char *p = buf;		// return value | 
|  | switch(type) { | 
|  | case EOF:	p = "EOF"; break; | 
|  | case VBOX:	p = "VBOX"; break; | 
|  | case SP:	p = "SP"; break; | 
|  | case BS:	p = "BS"; break; | 
|  | case US:	p = "US"; break; | 
|  | case BF:	p = "BF"; break; | 
|  | case UF:	p = "UF"; break; | 
|  | case PT:	p = "PT"; break; | 
|  | case BT:	p = "BT"; break; | 
|  | case END:	p = "END"; break; | 
|  | case NEUTRAL:	p = "NEUT"; break; | 
|  | case PAGE:	p = "PAGE"; break; | 
|  | case TM:	p = "TM"; break; | 
|  | case COORD:	p = "COORD"; break; | 
|  | case NE:	p = "NE"; break; | 
|  | case CMD:	p = "CMD"; break; | 
|  | case PARM:	p = "PARM"; break; | 
|  | default:	sprintf(buf, "weird type %d", type); | 
|  | } | 
|  | return p; | 
|  | } | 
|  |  | 
|  | // ================================================================================ | 
|  |  | 
|  | // 	troff output-specific functions | 
|  |  | 
|  | // ================================================================================ | 
|  |  | 
|  | const int	DELTABUF = 500000;	// grow the input buffer in chunks | 
|  |  | 
|  | static char	*inbuf = 0;		// raw text input collects here | 
|  | static int	ninbuf = 0;		// byte count for inbuf | 
|  | static char	*inbp = 0;		// next free slot in inbuf | 
|  | int		linenum = 0;		// input line number | 
|  |  | 
|  | static inline void addc(int c) { *inbp++ = c; } | 
|  |  | 
|  | static void adds(char *s) | 
|  | { | 
|  | for (char *p = s; *p; p++) | 
|  | addc(*p); | 
|  | } | 
|  |  | 
|  | static int fullrune(char *c, int n) | 
|  | { | 
|  | if(n <= 0) | 
|  | return 0; | 
|  | if(n>=1 && (unsigned char)c[0] < 0x80) | 
|  | return 1; | 
|  | if(n>=2 && (unsigned char)c[0] < 0xE0) | 
|  | return 1; | 
|  | if(n>=3) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static char *getutf(FILE *fp)	// get 1 utf-encoded char (might be multiple bytes) | 
|  | { | 
|  | static char buf[100]; | 
|  | char *p = buf; | 
|  |  | 
|  | for (*p = 0; (*p++ = getc(fp)) != EOF; ) { | 
|  | *p = 0; | 
|  | if (fullrune(buf, p-buf))	// found a valid character | 
|  | break; | 
|  | } | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static char *bufptr(int n) { return inbuf + n; }  // scope of inbuf is too local | 
|  |  | 
|  | static inline int wherebuf() { return inbp - inbuf; } | 
|  |  | 
|  | static char *getstr(char *p, char *temp) | 
|  | {		// copy next non-blank string from p to temp, update p | 
|  | while (*p == ' ' || *p == '\t' || *p == '\n') | 
|  | p++; | 
|  | if (*p == '\0') { | 
|  | temp[0] = 0; | 
|  | return(NULL); | 
|  | } | 
|  | while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') | 
|  | *temp++ = *p++; | 
|  | *temp = '\0'; | 
|  | return(p); | 
|  | } | 
|  |  | 
|  | /*************************************************************************** | 
|  | bounding box of a circular arc             Eric Grosse  24 May 84 | 
|  |  | 
|  | Conceptually, this routine generates a list consisting of the start, | 
|  | end, and whichever north, east, south, and west points lie on the arc. | 
|  | The bounding box is then the range of this list. | 
|  | list = {start,end} | 
|  | j = quadrant(start) | 
|  | k = quadrant(end) | 
|  | if( j==k && long way 'round )  append north,west,south,east | 
|  | else | 
|  | while( j != k ) | 
|  | append center+radius*[j-th of north,west,south,east unit vectors] | 
|  | j += 1  (mod 4) | 
|  | return( bounding box of list ) | 
|  | The following code implements this, with simple optimizations. | 
|  | ***********************************************************************/ | 
|  |  | 
|  | static int quadrant(double x, double y) | 
|  | { | 
|  | if (     x>=0.0 && y> 0.0) return(1); | 
|  | else if( x< 0.0 && y>=0.0) return(2); | 
|  | else if( x<=0.0 && y< 0.0) return(3); | 
|  | else if( x> 0.0 && y<=0.0) return(4); | 
|  | else			   return 0;	/* shut up lint */ | 
|  | } | 
|  |  | 
|  | static double xmin, ymin, xmax, ymax;	// used by getDy | 
|  |  | 
|  | static void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc) | 
|  | /* start, end, center */ | 
|  | {		/* assumes center isn't too far out */ | 
|  | double r; | 
|  | int j, k; | 
|  | printf("#start %g,%g, end %g,%g, ctr %g,%g\n", x0,y0, x1,y1, xc,yc); | 
|  | y0 = -y0; y1 = -y1; yc = -yc;	// troff's up is eric's down | 
|  | x0 -= xc; y0 -= yc;	/* move to center */ | 
|  | x1 -= xc; y1 -= yc; | 
|  | xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1; | 
|  | xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1; | 
|  | r = sqrt(x0*x0 + y0*y0); | 
|  | if (r > 0.0) { | 
|  | j = quadrant(x0,y0); | 
|  | k = quadrant(x1,y1); | 
|  | if (j == k && y1*x0 < x1*y0) { | 
|  | /* viewed as complex numbers, if Im(z1/z0)<0, arc is big */ | 
|  | if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r; | 
|  | if( xmax <  r) xmax =  r; if( ymax <  r) ymax =  r; | 
|  | } else { | 
|  | while (j != k) { | 
|  | switch (j) { | 
|  | case 1: if( ymax <  r) ymax =  r; break; /* north */ | 
|  | case 2: if( xmin > -r) xmin = -r; break; /* west */ | 
|  | case 3: if( ymin > -r) ymin = -r; break; /* south */ | 
|  | case 4: if( xmax <  r) xmax =  r; break; /* east */ | 
|  | } | 
|  | j = j%4 + 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | xmin += xc; ymin += yc; ymin = -ymin; | 
|  | xmax += xc; ymax += yc; ymax = -ymax; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int getDy(char *p, int *dx, int *maxv) | 
|  | // figure out where we are after a D'...' | 
|  | { | 
|  | int x, y, x1, y1;	// for input values | 
|  | char temp[50]; | 
|  | p++;		// get to command letter | 
|  | switch (*p++) { | 
|  | case 'l':	// line | 
|  | sscanf(p, "%d %d", dx, &y); | 
|  | return *maxv = y; | 
|  | case 'a':	// arc | 
|  | sscanf(p, "%d %d %d %d", &x, &y, &x1, &y1); | 
|  | *dx = x1 - x; | 
|  | arc_extreme(0, 0, x+x1, y+y1, x, y);	// sets [xy][max|min] | 
|  | printf("#arc bounds x %g, %g; y %g, %g\n", | 
|  | xmin, xmax, ymin, ymax); | 
|  | *maxv = (int) (ymin+0.5); | 
|  | return y + y1; | 
|  | case '~':	// spline | 
|  | for (*dx = *maxv = y = 0; (p=getstr(p, temp)) != NULL; ) { | 
|  | // above getstr() gets x value | 
|  | *dx += atoi(temp); | 
|  | p = getstr(p, temp);	// this one gets y value | 
|  | y += atoi(temp); | 
|  | *maxv = max(*maxv, y);	// ok??? | 
|  | if (*p == '\n' || *p == 0)	// input is a single line; | 
|  | break;			// don't walk off end if realloc | 
|  | } | 
|  | return y; | 
|  | case 'c':	// circle, ellipse | 
|  | sscanf(p, "%d", dx); | 
|  | *maxv = *dx/2;		// high water mark is ht/2 | 
|  | return 0; | 
|  | case 'e': | 
|  | sscanf(p, "%d %d", dx, &y); | 
|  | *maxv = y/2;		// high water mark is ht/2 | 
|  | return 0; | 
|  | default:	// weird stuff | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int serialnum = 0; | 
|  |  | 
|  | slug eofslug() | 
|  | { | 
|  | slug ret; | 
|  | ret.serialnum = serialnum; | 
|  | ret.type = EOF; | 
|  | ret.dp = wherebuf(); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | slug getslug(FILE *fp) | 
|  | { | 
|  | if (inbuf == NULL) { | 
|  | if ((inbuf = (char *) malloc(ninbuf = DELTABUF)) == NULL) | 
|  | ERROR "no room for %d character input buffer\n", ninbuf FATAL; | 
|  | inbp = inbuf; | 
|  | } | 
|  | if (wherebuf() > ninbuf-5000) { | 
|  | // this is still flaky -- lines can be very long | 
|  | int where = wherebuf();	// where we were | 
|  | if ((inbuf = (char *) realloc(inbuf, ninbuf += DELTABUF)) == NULL) | 
|  | ERROR "no room for %d character input buffer\n", ninbuf FATAL; | 
|  | ERROR "grew input buffer to %d characters\n", ninbuf WARNING; | 
|  | inbp = inbuf + where;	// same offset in new array | 
|  | } | 
|  | static int baseV = 0;	// first V command of preceding slug | 
|  | static int curV = 0, curH = 0; | 
|  | static int font = 0, size = 0; | 
|  | static int baseadj = 0; | 
|  | static int ncol = 1, offset = 0;	// multi-column stuff | 
|  | char str[1000], str2[1000], buf[3000], *p; | 
|  | int firstV = 0, firstH = 0; | 
|  | int maxV = curV; | 
|  | int ocurV = curV, mxv = 0, dx = 0; | 
|  | int sawD = 0;		// > 0 if have seen D... | 
|  | slug ret; | 
|  | ret.serialnum = serialnum++; | 
|  | ret.type = VBOX;	// use the same as last by default | 
|  | ret.dv = curV - baseV; | 
|  | ret.hpos = curH; | 
|  | ret.base =  ret.parm = ret.parm2 = ret.seen = 0; | 
|  | ret.font = font; | 
|  | ret.size = size; | 
|  | ret.dp = wherebuf(); | 
|  | ret.ncol = ncol; | 
|  | ret.offset = offset; | 
|  | ret.linenum = linenum;	// might be low | 
|  |  | 
|  | for (;;) { | 
|  | int c, m, n;	// for input values | 
|  | int sign;		// hoisted from case 'h' below | 
|  | switch (c = getc(fp)) { | 
|  | case EOF: | 
|  | ret.type = EOF; | 
|  | ret.dv = 0; | 
|  | if (baseadj) | 
|  | printf("# adjusted %d bases\n", baseadj); | 
|  | printf("# %d characters, %d lines\n", wherebuf(), linenum); | 
|  | return ret; | 
|  | case 'V': | 
|  | fscanf(fp, "%d", &n); | 
|  | if (firstV++ == 0) { | 
|  | ret.dv = n - baseV; | 
|  | baseV = n; | 
|  | } else { | 
|  | sprintf(buf, "v%d", n - curV); | 
|  | adds(buf); | 
|  | } | 
|  | curV = n; | 
|  | maxV = max(maxV, curV); | 
|  | break; | 
|  | case 'H':		// absolute H motion | 
|  | fscanf(fp, "%d", &n); | 
|  | if (firstH++ == 0) { | 
|  | ret.hpos = n; | 
|  | } else { | 
|  | sprintf(buf, "h%d", n - curH); | 
|  | adds(buf); | 
|  | } | 
|  | curH = n; | 
|  | break; | 
|  | case 'h':		// relative H motion | 
|  | addc(c); | 
|  | sign = 1; | 
|  | if ((c = getc(fp)) == '-') { | 
|  | addc(c); | 
|  | sign = -1; | 
|  | c = getc(fp); | 
|  | } | 
|  | for (n = 0; isdigit(c); c = getc(fp)) { | 
|  | addc(c); | 
|  | n = 10 * n + c - '0'; | 
|  | } | 
|  | curH += n * sign; | 
|  | ungetc(c, fp); | 
|  | break; | 
|  | case 'x':	// device control: x ... | 
|  | addc(c); | 
|  | fgets(buf, (int) sizeof(buf), fp); | 
|  | linenum++; | 
|  | adds(buf); | 
|  | if (buf[0] == ' ' && buf[1] == 'X') {	// x X ... | 
|  | if (2 != sscanf(buf+2, "%s %d", str, &n)) | 
|  | n = 0; | 
|  | if (eq(str, "SP")) {	// X SP n | 
|  | ret.type = SP;	// paddable SPace | 
|  | ret.dv = n;	// of height n | 
|  | } else if (eq(str, "BS")) { | 
|  | ret.type = BS;	// Breakable Stream | 
|  | ret.parm = n;	// >=n VBOXES on a page | 
|  | } else if (eq(str, "BF")) { | 
|  | ret.type = BF;	// Breakable Float | 
|  | ret.parm = ret.parm2 = n; | 
|  | // n = pref center (as UF) | 
|  | } else if (eq(str, "US")) { | 
|  | ret.type = US;	// Unbreakable Stream | 
|  | ret.parm = n; | 
|  | } else if (eq(str, "UF")) { | 
|  | ret.type = UF;	// Unbreakable Float | 
|  | ret.parm = ret.parm2 = n; | 
|  | // n = preferred center | 
|  | // to select several, | 
|  | // use several UF lines | 
|  | } else if (eq(str, "PT")) { | 
|  | ret.type = PT;	// Page Title | 
|  | ret.parm = n; | 
|  | } else if (eq(str, "BT")) { | 
|  | ret.type = BT;	// Bottom Title | 
|  | ret.parm = n; | 
|  | } else if (eq(str, "END")) { | 
|  | ret.type = END; | 
|  | ret.parm = n; | 
|  | } else if (eq(str, "TM")) { | 
|  | ret.type = TM;	// Terminal Message | 
|  | ret.dv = 0; | 
|  | } else if (eq(str, "COORD")) { | 
|  | ret.type = COORD;// page COORDinates | 
|  | ret.dv = 0; | 
|  | } else if (eq(str, "NE")) { | 
|  | ret.type = NE;	// NEed to break page | 
|  | ret.dv = n;	// if <n units left | 
|  | } else if (eq(str, "MC")) { | 
|  | ret.type = MC;	// Multiple Columns | 
|  | sscanf(buf+2, "%s %d %d", | 
|  | str, &ncol, &offset); | 
|  | ret.ncol = ncol; | 
|  | ret.offset = offset; | 
|  | } else if (eq(str, "CMD")) { | 
|  | ret.type = CMD;	// CoMmaNd | 
|  | sscanf(buf+2, "%s %s", str2, str); | 
|  | if (eq(str, "FC"))	// Freeze 2-Col | 
|  | ret.parm = FC; | 
|  | else if (eq(str, "FL"))	// FLush | 
|  | ret.parm = FL; | 
|  | else if (eq(str, "BP"))	// Break Page | 
|  | ret.parm = BP; | 
|  | else ERROR "unknown command %s\n", | 
|  | str WARNING; | 
|  | } else if (eq(str, "PARM")) { | 
|  | ret.type = PARM;// PARaMeter | 
|  | sscanf(buf+2, "%s %s %d", str2, str, &ret.parm2); | 
|  | if (eq(str, "NP"))	// New Page | 
|  | ret.parm = NP; | 
|  | else if (eq(str, "FO"))	// FOoter | 
|  | ret.parm = FO; | 
|  | else if (eq(str, "PL")) // Page Length | 
|  | ret.parm = PL; | 
|  | else if (eq(str, "MF")) // MinFull | 
|  | ret.parm = MF; | 
|  | else if (eq(str, "CT")) // ColTol | 
|  | ret.parm = CT; | 
|  | else if (eq(str, "WARN")) //WARNings? | 
|  | ret.parm = WARN; | 
|  | else if (eq(str, "DBG"))// DeBuG | 
|  | ret.parm = DBG; | 
|  | else ERROR "unknown parameter %s\n", | 
|  | str WARNING; | 
|  | } else | 
|  | break;		// out of switch | 
|  | if (firstV > 0) | 
|  | ERROR "weird x X %s in mid-VBOX\n", | 
|  | str WARNING; | 
|  | return ret; | 
|  | } | 
|  | break; | 
|  | case 'n':	// end of line | 
|  | fscanf(fp, "%d %d", &n, &m); | 
|  | ret.ht = n; | 
|  | ret.base = m; | 
|  | getc(fp);	// newline | 
|  | linenum++; | 
|  | sprintf(buf, "n%d %d\n", ret.ht, ret.base); | 
|  | adds(buf); | 
|  | if (!firstV++) | 
|  | baseV = curV; | 
|  | // older incarnations of this program used ret.base | 
|  | // in complicated and unreliable ways; | 
|  | // example:  if ret.ht + ret.base < ret.dv, ret.base = 0 | 
|  | // this was meant to avoid double-counting the space | 
|  | // around displayed equations; it didn't work | 
|  | // Now, we believe ret.base = 0, otherwise we give it | 
|  | // a value we have computed. | 
|  | if (ret.base == 0 && sawD == 0) | 
|  | return ret;	// don't fiddle 0-bases | 
|  | if (ret.base != maxV - baseV) { | 
|  | ret.base = maxV - baseV; | 
|  | baseadj++; | 
|  | } | 
|  | if (ret.type != VBOX) | 
|  | ERROR "%s slug (type %d) has base = %d\n", | 
|  | ret.typename(), ret.type, ret.base WARNING; | 
|  | return ret; | 
|  | case 'p':	// new page | 
|  | fscanf(fp, "%d", &n); | 
|  | ret.type = PAGE; | 
|  | curV = baseV = ret.dv = 0; | 
|  | ret.parm = n;	// just in case someone needs it | 
|  | return ret; | 
|  | case 's':	// size change snnn | 
|  | fscanf(fp, "%d", &size); | 
|  | sprintf(buf, "s%d\n", size); | 
|  | adds(buf); | 
|  | break; | 
|  | case 'f':	// font fnnn | 
|  | fscanf(fp, "%d", &font); | 
|  | sprintf(buf, "f%d\n", font); | 
|  | adds(buf); | 
|  | break; | 
|  | case '\n': | 
|  | linenum++; | 
|  | /* fall through */ | 
|  | case ' ': | 
|  | addc(c); | 
|  | break; | 
|  | case '0': case '1': case '2': case '3': case '4': | 
|  | case '5': case '6': case '7': case '8': case '9': | 
|  | // two motion digits plus a character | 
|  | addc(c); | 
|  | n = c - '0'; | 
|  | addc(c = getc(fp)); | 
|  | curH += 10 * n + c - '0'; | 
|  | adds(getutf(fp)); | 
|  | if (!firstV++) | 
|  | baseV = curV; | 
|  | break; | 
|  | case 'c':	// single ascii character | 
|  | addc(c); | 
|  | adds(getutf(fp)); | 
|  | if (!firstV++) | 
|  | baseV = curV; | 
|  | break; | 
|  | case 'C':	// Cxyz\n | 
|  | case 'N':	// Nnnn\n | 
|  | addc(c); | 
|  | while ((c = getc(fp)) != ' ' && c != '\n') | 
|  | addc(c); | 
|  | addc(c); | 
|  | if (!firstV++) | 
|  | baseV = curV; | 
|  | linenum++; | 
|  | break; | 
|  | case 'D':	// draw function: D.*\n | 
|  | sawD++; | 
|  | p = bufptr(wherebuf());	// where does the D start | 
|  | addc(c); | 
|  | while ((c = getc(fp)) != '\n') | 
|  | addc(c); | 
|  | addc(c); | 
|  | if (!firstV++) | 
|  | baseV = curV; | 
|  | ocurV = curV, mxv = 0, dx = 0; | 
|  | curV += getDy(p, &dx, &mxv);	// figure out how big it is | 
|  | maxV = max(max(maxV, curV), ocurV+mxv); | 
|  | curH += dx; | 
|  | linenum++; | 
|  | break; | 
|  | case 'v':	// relative vertical vnnn | 
|  | addc(c); | 
|  | if (!firstV++) | 
|  | baseV = curV; | 
|  | sign = 1; | 
|  | if ((c = getc(fp)) == '-') { | 
|  | addc(c); | 
|  | sign = -1; | 
|  | c = getc(fp); | 
|  | } | 
|  | for (n = 0; isdigit(c); c = getc(fp)) { | 
|  | addc(c); | 
|  | n = 10 * n + c - '0'; | 
|  | } | 
|  | ungetc(c, fp); | 
|  | curV += n * sign; | 
|  | maxV = max(maxV, curV); | 
|  | addc('\n'); | 
|  | break; | 
|  | case 'w':	// word space | 
|  | addc(c); | 
|  | break; | 
|  | case '#':	// comment | 
|  | addc(c); | 
|  | while ((c = getc(fp)) != '\n') | 
|  | addc(c); | 
|  | addc('\n'); | 
|  | linenum++; | 
|  | break; | 
|  | default: | 
|  | ERROR "unknown input character %o %c (%50.50s)\n", | 
|  | c, c, bufptr(wherebuf()-50) WARNING; | 
|  | abort(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } |