aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/mpm/slug.cc
diff options
context:
space:
mode:
authorwkj <devnull@localhost>2004-05-16 07:56:41 +0000
committerwkj <devnull@localhost>2004-05-16 07:56:41 +0000
commit5f1cf8e6fb130fd48d6f016d13baf5408b3181f8 (patch)
tree7f77f458df8c8b34db139fb4551df602ab6286be /src/cmd/mpm/slug.cc
parentc5561c23cf394806cbf6d70a96f2dc0253f93745 (diff)
downloadplan9port-5f1cf8e6fb130fd48d6f016d13baf5408b3181f8.tar.gz
plan9port-5f1cf8e6fb130fd48d6f016d13baf5408b3181f8.tar.bz2
plan9port-5f1cf8e6fb130fd48d6f016d13baf5408b3181f8.zip
Checkpoint: pull in mpm; merge pic from Taj's version of the world
Diffstat (limited to 'src/cmd/mpm/slug.cc')
-rw-r--r--src/cmd/mpm/slug.cc603
1 files changed, 603 insertions, 0 deletions
diff --git a/src/cmd/mpm/slug.cc b/src/cmd/mpm/slug.cc
new file mode 100644
index 00000000..14378b90
--- /dev/null
+++ b/src/cmd/mpm/slug.cc
@@ -0,0 +1,603 @@
+#include "misc.h"
+#include "slug.h"
+//#include <libc.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 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 (mblen(buf, sizeof buf) > 0) // 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;
+ }
+ }
+}