tslug.cc - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
tslug.cc (15793B)
---
     1 #include        "misc.h"
     2 #include        "slug.h"
     3 #include        
     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 = (x0x1)?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  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 }