URI:
       trange.cc - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       trange.cc (12710B)
       ---
            1 #include        <math.h>
            2 #include        "misc.h"
            3 #include        "slug.h"
            4 #include        "range.h"
            5 
            6 void sprange::reheight(int *cv, int *mv)
            7 {
            8         if (*cv != *mv)
            9                 ERROR "slug %d: an imbedded SP, line %d\n",
           10                         first->serialno(), first->lineno() WARNING;
           11         *cv += dv;
           12         *mv = max(*mv, *cv);
           13 }
           14 
           15 void sprange::rerawht(int *cv, int *mv)
           16 {
           17         *cv += rawht();
           18         *mv = max(*mv, *cv);
           19 }
           20 
           21 void nestrange::restore()
           22 {
           23         subrange->restoreall();
           24 }
           25 
           26 void stream::freeall()        // not a destructor;  called explicitly
           27 {
           28         strblk *p, *q;
           29         for (p = first; p; p = q) {
           30                 q = p->next;
           31                 delete p;
           32         }
           33         first = last = curr = 0;
           34 }
           35 
           36 void stream::dump()
           37 {
           38         for (stream s = *this; s.more(); s.advance())
           39                 s.current()->dump();
           40 }
           41 
           42 void stream::rdump()
           43 {
           44         for (stream s = *this; s.more(); s.advance())
           45                 s.current()->rdump();
           46 }
           47 
           48 int stream::restoreall()
           49 {
           50         for (stream s = *this; s.more(); s.advance())
           51                 s.current()->restore();
           52         return measure(this);
           53 }
           54 
           55 range *stream::append(range *r)
           56 {
           57         if (last == 0)
           58                 curr = first = last = new strblk;
           59         else {
           60                 last->next = new strblk;
           61                 last = last->next;
           62                 if (curr == 0)
           63                         curr = last;
           64         }
           65         last->next = 0;
           66         return last->rp = r;
           67 }
           68 
           69 void stream::split()        // duplicate current() range
           70 {
           71         strblk *s2 = new strblk;
           72         range *r2 = curr->rp->clone();
           73         s2->rp = r2;
           74         s2->next = curr->next;
           75         if (last == curr)
           76                 last = s2;
           77         curr->next = s2;
           78         curr->rp->killkids();                // children only in the 2nd one
           79         // r2->crosslink(r1);
           80 }
           81 
           82 int stream::height()
           83 {
           84         int h;
           85         stream s = *this;
           86         for (h = 0; s.more(); s.advance())
           87                 h += s.current()->height();
           88         return h;
           89 }
           90 
           91 int stream::rawht()
           92 {
           93         int h;
           94         stream s = *this;
           95         for (h = 0; s.more(); s.advance())
           96                 h += s.current()->rawht();
           97         return h;
           98 }
           99 
          100 int measure(stream *sp)                // record high-water mark of stream
          101 {                                // sets nested stream heights
          102         stream s = *sp;
          103         int curv, maxv;
          104         for (maxv = curv = 0; s.more(); s.advance())
          105                 s.current()->reheight(&curv, &maxv);
          106         return maxv;
          107 }
          108 
          109 int rawmeasure(stream *sp)
          110 {
          111         stream s = *sp;
          112         int curv, maxv;
          113         for (maxv = curv = 0; s.more(); s.advance())
          114                 s.current()->rerawht(&curv, &maxv);
          115         return maxv;
          116 }
          117 
          118 void nestrange::rdump()
          119 {
          120         dump();
          121         if (subrange)
          122                 subrange->rdump();
          123 }
          124 
          125 void nestrange::killkids()
          126 {
          127         subrange = new stream;
          128 }
          129 
          130 int nestrange::print(int curv, int col)
          131 {
          132         int ocurv = curv;
          133         first->slugout(col);
          134         for (stream s = *subrange; s.more(); s.advance())
          135                 curv = s.current()->print(curv, col);
          136         return ocurv + height();
          137 }
          138 
          139 #define macroclone(rangetype) range *rangetype::clone() {\
          140         rangetype *t = new rangetype;\
          141         *t = *this;\
          142         return t; }
          143 
          144 macroclone(usrange);
          145 macroclone(ufrange);
          146 macroclone(bfrange);
          147 
          148 #undef macroclone
          149 
          150 #define macropickgoal(rangetype) void rangetype::pickgoal(int acv, double scale) {\
          151         if (scale > 1) {\
          152                 goalV = (int)(scale*goalV);\
          153                 goal2 = (int)(scale*goal2);\
          154         }\
          155         if (abs(acv - goalV) > abs(acv-goal2))\
          156                 goalV = goal2; }
          157 
          158 macropickgoal(ufrange)
          159 macropickgoal(bfrange)
          160 
          161 #undef macropickgoal
          162 
          163 range *generator::next()
          164 {
          165         range *r;
          166         if (child) {
          167                 if ((r = child->next()) != 0)
          168                         return r;
          169                 delete child;
          170                 child = 0;
          171         }
          172         if (!s.more())
          173                 return 0;
          174         r = s.current();
          175         if (r->isnested())
          176                 child = new generator(r->children());
          177         s.advance();
          178         return r;
          179 }
          180 
          181 range *queue::enqueue(range *r)
          182 {
          183         if (dbg & 8)
          184                 printf("#entering queue::enqueue()\n");
          185         check("queue::enqueue");
          186         if (!last || last->rp->serialno() < r->serialno())        // common case
          187                 return append(r);
          188         if (dbg & 8)
          189                 printf("#queue::enqueue() pushing back\n");
          190         newguy = new strblk;
          191         newguy->rp = r;
          192         if (r->serialno() < first->rp->serialno()) {
          193                 newguy->next = first;
          194                 curr = first = newguy;
          195                 return newguy->rp;
          196         }
          197         if (dbg & 8)
          198                 printf("#queue::enqueue() searching down queue\n");
          199         for (curr = first;
          200                 next() && next()->serialno() < r->serialno();
          201                 curr = curr->next)
          202                 ;
          203         newguy->next = curr->next;
          204         curr->next = newguy;
          205         curr = first;                        // restore important queue condition
          206         return newguy->rp;
          207 }
          208 
          209 range *queue::dequeue()
          210 {
          211         if (dbg & 8)
          212                 printf("#entering queue::dequeue()\n");
          213         check("queue::dequeue");
          214         curr = first->next;
          215         range *retval = first->rp;
          216         delete first;
          217         first = curr;
          218         if (!curr)
          219                 last = 0;
          220         return retval;
          221 }
          222 
          223 // ================================================================================
          224 
          225 //        functions that munge the troff output stored in slugs[]
          226 
          227 // ================================================================================
          228 
          229 static void doprefix(FILE *fp) // copy 1st "x" commands to output
          230 {
          231         int c;
          232 
          233         while ((c = getc(fp)) != EOF) {
          234                 if (c != 'x') {
          235                         ungetc(c, fp);
          236                         break;
          237                 }
          238                 putchar(c);
          239                 do {
          240                         putchar(c = getc(fp));
          241                 } while (c != '\n');
          242                 linenum++;
          243         }
          244 //        printf("x font 1 R\n");        // horrible kludge: ensure a font for first f1 command 
          245 }
          246 
          247 #define        DELTASLUGS        15000
          248 
          249 static slug        *slugs = 0;
          250 static int        nslugs = 0;        // slugs has nslugs slots
          251 static slug        *slugp = 0;        // next free slug in slugs
          252 
          253 static void readslugs(FILE *fp)
          254 {
          255         if ((slugs = (slug *) malloc((nslugs = DELTASLUGS)*sizeof(slug))) == NULL)
          256                 ERROR "no room for %d-slug array\n", nslugs FATAL;
          257         slugp = slugs;
          258         for (slugp = slugs; ; slugp++) {
          259                 if (slugp >= slugs+nslugs-2) {
          260                         int where = slugp - slugs;
          261                         if ((slugs = (slug *) realloc((char *) slugs, (nslugs += DELTASLUGS)*sizeof(slug))) == NULL)
          262                                 ERROR "no room for %d slugs\n", nslugs FATAL;
          263                         ERROR "now slug array can hold %d slugs\n", nslugs WARNING;
          264                         slugp = slugs + where;
          265                 }
          266                 *slugp = getslug(fp);
          267                 if (slugp->type == EOF)
          268                         break;
          269         }
          270         *++slugp = eofslug();
          271         printf("# %d slugs\n", slugp-slugs);
          272 }
          273 
          274 static slug *findend(slug *sp)
          275 {
          276         slug *p;
          277         for (p = sp; p->type == sp->type; p++)        // skip runs
          278                 ;                                // espec UF UF UF 
          279         for ( ; p < slugp; p++)
          280                 switch (p->type) {
          281                 case US:
          282                 case UF:
          283                 case BF:
          284                 case PT:
          285                 case BT:
          286                         p = findend(p);
          287                         break;
          288                 case END:
          289                         return p;
          290                 }
          291         ERROR "walked past EOF in findend looking for %d (%s), line %d\n",
          292                 sp->type, sp->typename(), sp->lineno() FATAL;
          293         return sp;
          294 }
          295 
          296 static int markp(int i, int n, int parm)
          297 {        // should VBOX i of n be marked to brevent breaking after it?
          298         if (i >= n-1)
          299                 return 0;
          300         return i <= parm-2 || i >= n-parm;
          301 }
          302 
          303 static void markbreak(slug *p)
          304 {
          305         // Mark impermissible breakpoints in BS's.
          306         // The parm field of a VBOX is >0 if we shouldn't break after it.
          307         int parm = 0;                // how many lines must stay on page
          308         int goahead = 1;        // true until we see the next BS
          309         int nowmark = 0;        // true when we should be marking
          310         int n = 0;
          311         while (p->type == BS)
          312                 parm = p++->parm;        // latest BS parm applies
          313         slug *op = p;
          314         while (goahead) {
          315                 switch (p->type) {
          316                 case VBOX:                // count VBOXes so second pass knows
          317                         if (p->dv > 0)        // knows how far to end of BS
          318                                 n++;
          319                         break;
          320                 case US:                // mark around EQ/EN, etc.
          321                         nowmark = 1;
          322                         p = findend(p);
          323                         break;
          324                 case UF:                // but not around floats, PTs, and BTs
          325                 case BF:
          326                 case PT:
          327                 case BT:
          328                         p = findend(p);
          329                         break;
          330                 case SP:                // naked SP:  probable macro botch
          331                         nowmark = 1;        // mark around it anyhow
          332                         break;
          333                 case BS:                // beginning of next paragraph
          334                 case END:                // probable macro botch
          335                 case EOF:
          336                         goahead = 0;        // stop work after marking
          337                         nowmark = 1;
          338                 default:
          339                         break;
          340                 }
          341                 p++;
          342                 if (nowmark) {
          343                         int i = 0;        // VBOX counter for second pass
          344                         while (op < p) {
          345                                 switch (op->type) {
          346                                 case VBOX:
          347                                         if (op->dv > 0)
          348                                                 op->parm = markp(i, n, parm);
          349                                         i++;
          350                                         break;
          351                                 case US:        // caused second pass to begin
          352                                 case SP:
          353                                 case BS:
          354                                 case END:
          355                                 case EOF:
          356                                         op = p;
          357                                         break;
          358                                 case UF:        // skip on this pass too
          359                                 case BF:
          360                                 case PT:
          361                                 case BT:
          362                                         op = findend(op);
          363                                         break;
          364                                 default:
          365                                         break;
          366                                 }
          367                         op++;
          368                         }
          369                         if (i != n)
          370                                 ERROR "markbreak failed : i %d n %d\n",
          371                                         i, n WARNING;
          372                         op = p;
          373                         nowmark = n = 0;
          374                 }
          375         }
          376 }
          377 
          378 static void fixslugs()                // adjust bases and dv's, set parameters, etc.
          379 {
          380         slug *p, *prevV = 0;
          381         for (p = slugs; p < slugp; p++) {
          382                 if (p->type == VBOX) {
          383                         prevV = p;
          384                         continue;
          385                 }
          386                 if (p->base != 0) {
          387                         ERROR "%s slug (type %d) has base = %d, line %d\n",
          388                                 p->typename(), p->type, p->base, p->lineno() WARNING;
          389                 }
          390                 if ((p->type == SP) || (p->type == NE))
          391                         continue;
          392                 if (p->type == PAGE)
          393                         prevV = 0;
          394                 if (p->dv != 0)
          395                         if (prevV) {
          396                                 prevV->base = max(prevV->base, p->dv);
          397                                 p->dv = 0;
          398                         } else {
          399                                 ERROR "%s slug (type %d) has dv = %d, line %d\n",
          400                                         p->typename(), p->type, p->dv, p->lineno() WARNING;
          401                         }
          402         }
          403         prevV = 0;
          404         int firstNP = 0, firstFO = 0, firstPL = 0;
          405         for (p = slugs; p < slugp; p++) {
          406                 switch (p->type) {
          407                 // adjust the dv in a sequence of VBOXes
          408                 // by subtracting from each the base of the preceding VBOX
          409                 case VBOX:
          410                         if (prevV)
          411                                 p->dv -= prevV->base;
          412                         prevV = p;
          413                         break;
          414                 case SP:
          415                         p->dv = max(p->dv, 0);
          416                         break;
          417                 case PAGE:
          418                         p->neutralize();
          419                         prevV = 0;
          420                         break;
          421                 // record only first "declarations" of Page Top and bottom (FO);
          422                 case PARM:
          423                         switch (p->parm) {
          424                         case NP:
          425                                 if (firstNP++ == 0)
          426                                         pagetop = p->parm2;
          427                                 p->neutralize();
          428                                 break;
          429                         case FO:
          430                                 if (firstFO++ == 0)
          431                                         pagebot = p->parm2;
          432                                 p->neutralize();
          433                                 break;
          434                         case PL:
          435                                 if (firstPL++ == 0)
          436                                         physbot = p->parm2;
          437                                 p->neutralize();
          438                                 break;
          439                         }
          440                         break;
          441                 // things that begin groups; not US, which should nest properly
          442                 case UF:
          443                 case BF:
          444                         while ((p+1)->type == p->type) {
          445                                                         // join adjacent identical
          446                                 (p+1)->parm2 = p->parm;        // parm is latest
          447                                                         // parm2 is previous
          448                                 p->neutralize();        // so it's not seen later
          449                                 p++;
          450                         }
          451                         break;
          452                 // none of the above
          453                 case US:
          454                 case PT:
          455                 case BT:
          456                 case BS:
          457                 case END:
          458                 case TM:
          459                 case COORD:
          460                 case NE:
          461                 case MC:
          462                 case CMD:
          463                 case EOF:
          464                         break;
          465                 default:
          466                         ERROR "Unknown slug type %d in fixslugs, line %d\n",
          467                                 p->type, p->lineno() WARNING;
          468                         break;
          469                 }
          470         }
          471         int pagesize = pagebot - pagetop;
          472         if (pagesize == 0)
          473                 ERROR "Page dimensions not declared\n" FATAL;
          474         if (physbot == 0)
          475                 physbot = pagebot + pagetop;
          476         printf("# page top %d bot %d size %d physbot %d\n",
          477                 pagetop, pagebot, pagesize, physbot);
          478         for (p = slugs; p < slugp; p++) {
          479                 switch (p->type) {
          480                 // normalize float parameters
          481                 case BF:
          482                 case UF:
          483                                         // primary goal
          484                         p->parm = max(min(p->parm-pagetop, pagesize), 0);
          485                                         // secondary goal
          486                         p->parm2 = max(min(p->parm2-pagetop, pagesize), 0);
          487                         break;
          488                 // normalize need parameters
          489                 case NE:
          490                         p->dv = max( min(p->dv, pagesize), 0);
          491                         break;
          492                 // mark permissible breaks
          493                 case BS:
          494                         markbreak(p);
          495                         break;
          496                 }
          497                 if (dbg & 1)
          498                         p->dump();
          499         }
          500 }
          501 
          502 void checkout()
          503 {
          504         for (slug *p = slugs; p < slugp; p++)
          505                 switch (p->type) {
          506                 case PT:
          507                 case BT:
          508                         p = findend(p);
          509                         break;
          510                 case SP:
          511                 case VBOX:
          512                         if (p->seen != 1)
          513                                 ERROR "%s slug %d seen %d times\n",
          514                                         p->typename(), p->serialno(),
          515                                         p->seen WARNING;
          516                         break;
          517                 }
          518 }
          519 
          520 eofrange *lastrange;
          521 stream        ptlist, btlist;
          522 
          523 static slug *makeranges(slug *p, stream *s, int level)
          524 {
          525         stream *t;
          526 
          527         for ( ; p < slugp; p++)
          528                 switch (p->type) {
          529                 case VBOX:
          530                         s->append(new vboxrange(p));
          531                         break;
          532                 case SP:
          533                         s->append(new sprange(p));
          534                         break;
          535                 case BS:
          536                         s->append(new bsrange(p));
          537                         break;
          538                 case US:
          539                         s->append(new usrange(p, t = new stream));
          540                         p = makeranges(p+1, t, level+1);
          541                         break;
          542                 case BF:
          543                         s->append(new bfrange(p, t = new stream));
          544                         p = makeranges(p+1, t, level+1);
          545                         break;
          546                 case UF:
          547                         s->append(new ufrange(p, t = new stream));
          548                         p = makeranges(p+1, t, level+1);
          549                         break;
          550                 case PT:
          551                         ptlist.append(new ptrange(p, t = new stream));
          552                         p = makeranges(p+1, t, level+1);
          553                         break;
          554                 case BT:
          555                         btlist.append(new btrange(p, t = new stream));
          556                         p = makeranges(p+1, t, level+1);
          557                         break;
          558                 case END:
          559                         s->append(new endrange(p));
          560                         return p;
          561                 case TM:
          562                         s->append(new tmrange(p));
          563                         break;
          564                 case COORD:
          565                         s->append(new coordrange(p));
          566                         break;
          567                 case NE:
          568                         if (level) {
          569                                 ERROR "Nested NE commands are ignored, line %d\n",
          570                                         p->lineno() WARNING;
          571                                 p->dv = 0;
          572                         }
          573                         s->append(new nerange(p));
          574                         break;
          575                 case MC:
          576                         s->append(new mcrange(p));
          577                         break;
          578                 case CMD:
          579                         if (level)
          580                                 ERROR "Nested command ignored, line %d\n",
          581                                         p->lineno() WARNING;
          582                         s->append(new cmdrange(p));
          583                         break;
          584                 case PARM:
          585                         if (level)
          586                                 ERROR "Nested parameter ignored, line %d\n",
          587                                         p->lineno() WARNING;
          588                         s->append(new parmrange(p));
          589                         break;
          590                 case EOF:
          591                         lastrange = new eofrange(p);
          592                         return 0;
          593                 }
          594         return p;
          595 }
          596 
          597 static queue text;                        // unexamined input ranges; the real data
          598 
          599 void startup(FILE *fp)
          600 {
          601         doprefix(fp);                        // peel off 'x' commands
          602         readslugs(fp);                        // read everything into slugs[]
          603         fixslugs();                        // measure parameters and clean up
          604         makeranges(slugs, &text, 0);        // add range superstructure
          605         measure(&text);                // heights of nested things
          606         rawmeasure(&text);
          607         while (text.more()) {
          608                 range *r = text.dequeue();
          609                 if (dbg & 2)
          610                         r->dump();
          611                 r->enqueue();
          612         }
          613 }