home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / word2x0a.zip / source / reader.cc < prev    next >
C/C++ Source or Header  |  1998-04-20  |  26KB  |  1,297 lines

  1. /* $Id: reader.cc,v 1.27 1997/04/17 20:24:41 dps Exp $ */
  2. /* Reads the word document */
  3. #ifdef __GNUC__
  4. #define alloca __builtin_alloca
  5. #else
  6. #if HAVE_ALLOCA_H
  7. #include <alloca.h>
  8. #else /* Do not have alloca.h */
  9. #ifdef _AIX
  10.  #pragma alloca
  11. #else /* not _AIX */
  12. char *alloca();
  13. #endif /* _AIX */
  14. #endif /* HAVE_ALLOCA_H */
  15. #endif /* __GNUC__ */
  16.  
  17. #include <iostream.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include "word6.h"
  22. #include "interface.h"
  23. #include "reader.h"
  24.  
  25. #define RESUME_CHARS 30
  26. #define ST_MIN_ZEROS 16
  27. #define ST_ASC_VCHARS 6 
  28.  
  29. /* Seek start */
  30. int chunk_reader::seek_start(FILE *f)
  31. {
  32.     int nz,i,c;
  33.  
  34.     rewind(f);
  35.     nz=0;
  36.     while ((c=fgetc(in))!=EOF)
  37.     {
  38.     if (c==0)
  39.     {
  40.         nz++;
  41.         continue;
  42.     }
  43.  
  44.     if (nz<ST_MIN_ZEROS)
  45.     {
  46.         nz=0;
  47.         continue;
  48.     }
  49.  
  50.  
  51.     /* Deliberately ignore the first non-zero character */
  52.     nz=0;
  53.     for (i=0; i<ST_ASC_VCHARS; i++)
  54.     {
  55.         if ((c=fgetc(in))==EOF)
  56.         break;
  57.         if (c<' ' || c>126)
  58.         break;
  59.     }
  60.     if (i==ST_ASC_VCHARS)
  61.     {
  62.         fseek(f, -ST_ASC_VCHARS-1, SEEK_CUR);
  63.         return 1;
  64.     }
  65.     }
  66.     return 0;
  67. }
  68.  
  69.  
  70. /* Hanlde ^M^G---I think this is ^G */
  71. static int read_char(FILE *in)
  72. {
  73.     int c, d;
  74.  
  75.     c=fgetc(in);
  76.     if (c!=PAR_END)
  77.     return c;
  78.     d=fgetc(in);
  79.     if (d==TABLE_SEP)
  80.     return d;
  81.     ungetc(d, in);
  82.     return c;
  83. }
  84.  
  85. /* This code is basically a layered filtration process. At the bottom layer
  86.    is this next function that reads a character from a word document. Pointers
  87.    to object are used extensively to avoid implicit copies. */
  88. static int read_character(FILE *in)
  89. {
  90.     int c, d, asc;
  91.     static int s_ch=-42;    // Assume EOF not -42
  92.  
  93.  re_feed:
  94.     if (s_ch!=-42)
  95.     {
  96.     c=s_ch;
  97.     s_ch=-42;
  98.     }
  99.     else
  100.     c=read_char(in);
  101.     switch(c)
  102.     {
  103.     case 0:
  104.     /* This is a hack to skip junk---seems to work quite well */
  105.     asc=0;
  106.     while (asc<RESUME_CHARS)
  107.     {
  108.         if ((c=fgetc(in))==EOF)
  109.         goto re_feed;
  110.         if (c>=' ' && c<127)
  111.         asc++;
  112.         else
  113.         asc=0;
  114.     }
  115.     fseek(in, -RESUME_CHARS, SEEK_CUR);
  116.     return CH_SUSPECT;
  117.  
  118.     case PAR_END:
  119.     return (CH_PAR | CONTROL_FLAG);
  120.  
  121.     case TABLE_SEP:
  122.     d=read_char(in);
  123.     if (d!=c)
  124.     {
  125.         s_ch=d;        /* Push back character */
  126.         return (CH_FIELD | CONTROL_FLAG);
  127.     }    
  128.     return (CH_ROW | CONTROL_FLAG);
  129.  
  130.     case START_CTL:
  131.     return (CH_SPEC | CONTROL_FLAG);
  132.  
  133.     case END_CTL:
  134.     return (CH_ENDSPEC | CONTROL_FLAG);
  135.  
  136.     case HARD_RETURN:
  137.     return (CH_HDRTN | CONTROL_FLAG);
  138.  
  139.     case NEW_PAGE:
  140.     return (CH_PAGE | CONTROL_FLAG);
  141.  
  142.     case FOOTNOTE:
  143.     return (CH_FOOTNOTE | CONTROL_FLAG);
  144.  
  145.     default:
  146.     if (c<' ' && c!=EOF)
  147.         return (CH_OTHER | CONTROL_FLAG);
  148.     else
  149.         return c;
  150.     }
  151. }
  152.  
  153.  
  154. /* This function reads a paragraph, field of a table or whatever. It copies
  155.    everything in any embed tags unprocessed and leaves it in the element */
  156. void chunk_reader::read_chunk_raw(void)
  157. {
  158.     int c, is_ctl=0;
  159.  
  160.     text.zero();        // Zero text buffer
  161.     while ((c=read_character(in))!=(EOF | CONTROL_FLAG))
  162.     {
  163.     if (c & CONTROL_FLAG)
  164.     {
  165.         c &= ~CONTROL_FLAG;
  166.         /* If in embedded item then ignore all but end embed */
  167.         if (is_ctl)
  168.         {
  169.         if (c==CH_ENDSPEC)
  170.         {
  171.             is_ctl=0;
  172.             text.add(c);
  173.         }
  174.         continue;
  175.         }
  176.  
  177.         switch(c)
  178.         {
  179.         case CH_PAR:
  180.         case CH_FIELD:
  181.         case CH_ROW:
  182.         break;
  183.         
  184.         case CH_HDRTN:
  185.         text.add('\n');    // Add newline
  186.         continue;    // Continue processing
  187.  
  188.         case CH_OTHER:
  189.         continue;    // Just ignore character
  190.  
  191.         case CH_SPEC:
  192.         text.add(c);
  193.         if (!is_ctl)
  194.             is_ctl=1;
  195.         continue;
  196.         
  197.         case CH_ENDSPEC:
  198.         cerr<<"Suprious ^U ignored\n";
  199.         continue;
  200.     
  201.         case CH_PAGE:
  202.         case CH_FOOTNOTE:
  203.         text.add(c);
  204.         continue;
  205.  
  206.         default:
  207.         cerr<<"Unexpected value "<<(c & (~CONTROL_FLAG))\
  208.             <<" switch\n";
  209.         continue;
  210.         }
  211.         type=c;
  212.         tptr=text;
  213.         return;
  214.     }
  215.     /* Not control or end of inclusion */
  216.     text.add(c);
  217.     }
  218.     type=CH_EOF;
  219.     tptr=text;
  220.     return;
  221. }
  222.  
  223.  
  224. /* This function reads chunks from using read_chunk_raw and hands them
  225.    out in contigous peices of the same type. Emebedded stuff gets
  226.    seperated from the rest here. The partial flag is set if only some
  227.    of a field is returned (usually because of an embedded item).
  228. */
  229. struct chunk_rtn chunk_reader::read_chunk(void)
  230. {
  231.     const char *s;        // Save stupid compilers
  232.     struct chunk_rtn res;
  233.  
  234.     if (tptr==NULL)
  235.     this->read_chunk_raw();
  236.  
  237.     s=tptr;
  238.  
  239.     /* Embed */
  240.     if (*s==CH_SPEC)
  241.     {
  242.     while (*(++s))
  243.     {
  244.         if (*s==CH_ENDSPEC)
  245.         break;
  246.         res.txt.add(*s);
  247.     }
  248.         tptr=s+1;
  249.     res.type=CH_SPEC;
  250.     return res;
  251.     }
  252.  
  253.     /* New page */
  254.     if (*s==CH_PAGE)
  255.     {
  256.     res.type=CH_PAGE;
  257.     tptr=s+1;
  258.     return res;
  259.     }
  260.  
  261.     if (*s==CH_FOOTNOTE)
  262.     {
  263.     res.type=CH_FOOTNOTE;
  264.     tptr=s+1;
  265.     return res;
  266.     }
  267.  
  268.     /* Normal */
  269.     while (*s)
  270.     {
  271.     if (*s==CH_SPEC || *s==CH_PAGE || *s==CH_FOOTNOTE)
  272.     {
  273.         tptr=s;
  274.         res.type=(PART_FLAG | type);
  275.         return res;
  276.     }
  277.  
  278.     res.txt.add(*s);
  279.     s++;
  280.     }
  281.     res.type=type;
  282.     tptr=NULL;
  283.     text.zero();        // Save memory
  284.     return res;
  285. }
  286.  
  287.  
  288.  
  289. /*----------------------------------------------------------------------*/
  290. /* Tables and basic stuff */
  291.  
  292. /*
  293.  * Refill the token queue.
  294.  */
  295. int tok_seq::rd_token(void)
  296. {
  297.     struct chunk_rtn r;
  298.     tok *t;
  299.     char other[2];
  300.  
  301.     r=read_chunk();
  302.     if (r.type==CH_EOF)
  303.     return 0;
  304.  
  305.     switch(r.type & ~PART_FLAG)
  306.     {
  307.     case CH_ROW:
  308.     if (table==NULL)
  309.         table=new(table_info);
  310.     /* Handle 1 field rows properly */
  311.     if (table->col==0)
  312.     {
  313.         t=new(tok)(T_ROW, (void *) NULL, tok::TOK_START);
  314.         table->enqueue(t);
  315.     }
  316.     table->col++;
  317.     if (table->col>table->cols)
  318.         table->cols=table->col;
  319.     table->rows++;
  320.     table->tok_push(T_FIELD, &(r.txt));
  321.     t=new(tok)(T_ROW, (void *) NULL, tok::TOK_END);
  322.     table->enqueue(t);
  323.     table->col=0;
  324.     break;
  325.     
  326.     case CH_FIELD:
  327.     if (table==NULL)
  328.     {
  329.         table=new(table_info);
  330.     }
  331.     if (table->col==0)
  332.     {
  333.         t=new(tok)(T_ROW, (void *) NULL, tok::TOK_START);
  334.         table->enqueue(t);
  335.     }
  336.     table->col++;
  337.     table->tok_push(T_FIELD, &(r.txt));
  338.     break;
  339.     
  340.  
  341.     case CH_PAR:
  342.     if (table!=NULL)
  343.     {
  344.         /* Table handling */
  345.         if (table->col!=0)
  346.         {
  347. #if 0
  348.         table->tok_push(T_FIELD, &(r.txt));
  349.         t=new(tok)(T_ROW, (void *) NULL, tok::TOK_END);
  350.         table->enqueue(t);
  351.         t=new(tok)(T_ROW, (void *) NULL, tok::TOK_START);
  352.         table->enqueue(t);
  353.         for (i=0; i<table->col; i++)
  354.         {
  355.             t=new(tok)(T_FIELD, "\0", tok::TOK_START);
  356.             table->enqueue(t);
  357.             t=new(tok)(T_FIELD, (void *) NULL, tok::TOK_END);
  358.             table->enqueue(t);
  359.         }
  360.         table->rows++;
  361.         break;
  362. #else
  363.         t=new(tok)(T_ROW, (void *) NULL, tok::TOK_END);
  364.         table->enqueue(t);
  365.         if (table->col>table->cols)
  366.             table->cols=table->col;
  367.         table->rows++;
  368. #endif        
  369.         }
  370.         table->finish(&output);
  371.         delete(table);
  372.         table=NULL;
  373.     }
  374.  
  375.     if (r.type & PART_FLAG)
  376.     {
  377.         tok *td;
  378.         td=new(tok)(T_PARAGRAPH, (const char *) (r.txt), tok::TOK_START);
  379.         output.enqueue(td);
  380.     }
  381.     else
  382.         tok_push(T_PARAGRAPH, &(r.txt));
  383.     break;
  384.     
  385.     case CH_SPEC:
  386.     tok_push(T_SPEC, &(r.txt));
  387.     break;
  388.  
  389.     case CH_PAGE:
  390.     case CH_FOOTNOTE:
  391.     other[0]=r.type;
  392.     other[1]='\0';
  393.     t=new(tok)(T_CODE, other, tok::TOK_START);
  394.     output.enqueue(t);
  395.     break;
  396.  
  397.     default:
  398.     break;
  399.     }
  400.  
  401.     return 1;
  402. }
  403.  
  404.  
  405.  
  406. /*----------------------------------------------------------------------*/
  407. /* Equations.... */
  408.  
  409. /*
  410.  * Code that scans forward to the end of stuff that looks like an extension
  411.  * of some maths that was the last thing.
  412.  */
  413. const char *math_forward_scan(const char *s)
  414. {
  415.     const char *scan, *end;
  416.     int blvl;
  417.  
  418.     end=scan=s;
  419.  
  420.     /* Check whether the first part looks like more of the equation */
  421.     while (1)
  422.     {
  423.     /* Skip spaces */
  424.     while (isspace(*scan))
  425.         scan++;
  426.  
  427.     /* Look for binary operator */
  428.     if (*scan=='+' || *scan=='-' || *scan=='*' || *scan=='/' ||
  429.         *scan=='=')
  430.     {
  431.         /* skip spaces */
  432.         scan++;
  433.         while (isspace(*scan))
  434.         scan++;
  435.  
  436.         /* Grab next word */
  437.         blvl=0;
  438.         while (!isspace(*scan) || blvl>0)
  439.         {
  440.         switch(*scan)
  441.         {
  442.         case '(':
  443.             blvl++;
  444.             break;
  445.  
  446.         case ')':
  447.             blvl--;
  448.             break;
  449.  
  450.         default:
  451.             break;
  452.         }
  453.         if (*scan=='\0')
  454.             break;    // Robustness fix
  455.         scan++;
  456.         }
  457.  
  458.         end=scan;        // Update end
  459.     }
  460.     else
  461.         break;        // No binary operator, assume no text
  462.     }
  463.     return end;
  464. }
  465.  
  466. /*
  467.  * Code that scans backwards to the start of stuff that looks like it should
  468.  * ohave been prepended to the current maths.
  469.  */
  470. const char *math_reverse_scan(const char *s)
  471. {
  472.     const char *scan, *start;
  473.     int blvl;
  474.  
  475.     start=scan=s+strlen(s)-1;
  476.  
  477.     /* Check whether the first part looks like more of the equation */
  478.     while (scan>=s)
  479.     {
  480.     /* Skip spaces */
  481.     while (scan>=s && isspace(*scan))
  482.         scan--;
  483.     if (scan<s)
  484.         return s;
  485.  
  486.     /* Look for binary operator */
  487.     if (*scan=='+' || *scan=='-' || *scan=='*' || *scan=='/' ||
  488.         *scan=='=')
  489.     {
  490.         /* skip spaces */
  491.         scan--;
  492.         while (scan>=s && isspace(*scan))
  493.         scan--;
  494.         if (scan<s)
  495.         return s;
  496.  
  497.         /* Grab next word */
  498.         blvl=0;
  499.         while (!isspace(*scan) || blvl>0 )
  500.         {
  501.         switch(*scan)
  502.         {
  503.         case ')':
  504.             blvl++;
  505.             break;
  506.  
  507.         case '(':
  508.             blvl--;
  509.             break;
  510.  
  511.         default:
  512.             break;
  513.         }
  514.         if (scan==s)
  515.             return s;    // Robustness fix
  516.         scan--;
  517.         }
  518.         start=scan;        // Update end
  519.     }
  520.     else
  521.         break;        // No binary operator, assume no text
  522.     }
  523.     return start;
  524. }
  525.  
  526. /*
  527.  * Code to feed a token one at a time. (private, need prostproccessing
  528.  * to compensate for equation abuse by word users)
  529.  */
  530. const tok_seq::tok *tok_seq::feed_token(void)
  531. {
  532.     while (output.is_empty())
  533.     {
  534.     if (!rd_token())
  535.         return NULL;
  536.     }
  537.     return output.dequeue();
  538. }
  539.  
  540. /* Private token reader, compensates for equation abuse */
  541. const tok_seq::tok *tok_seq::math_collect(void)
  542. {
  543.     const tok *rdt, *ntok, *nntok;
  544.     const char *mptr, *endptr;
  545.     char *s, *t;
  546.     
  547.  math_aggregate: ;
  548.     if ((rdt=this->saved_tok)==NULL)
  549.     {
  550.     if ((rdt=this->feed_token())==NULL)
  551.         return NULL;
  552.     }
  553.     else
  554.     saved_tok=NULL;
  555.     
  556.     switch (rdt->tok & (~PART_FLAG))
  557.     {
  558.     case T_PARAGRAPH:
  559.     if (rdt->end!=tok::TOK_START || (rdt->tok & PART_FLAG==0)
  560.         || rdt->data.d==NULL)
  561.         break;
  562.     if ((ntok=this->feed_token())==NULL)
  563.         break;
  564.     /* Passed all the easy rejection cases, invoke math_reverse_scan */
  565.     saved_tok=ntok;
  566.     if (ntok->tok==T_SPEC && ntok->end==tok::TOK_START &&
  567.         ntok->data.d!=NULL && strncmp(ntok->data.d, "eq ", 3)==0)
  568.     {
  569.         mptr=math_reverse_scan(rdt->data.d);
  570.         endptr=rdt->data.d+strlen(rdt->data.d)-1;
  571.         if (mptr>=endptr)
  572.         break;
  573.         /* Allocate memory */
  574.         if ((s=(char *) malloc(mptr-rdt->data.d+1))==NULL)
  575.         {
  576.         cerr<<"Malloc read_token::malloc failure (fatal)\n";
  577.         exit(1);
  578.         }
  579.         if ((t=(char *) malloc(strlen(ntok->data.d)+endptr-mptr+1))==NULL)
  580.         {
  581.         free((void *) s);
  582.         cerr<<"Malloc read_token::malloc failure (fatal)\n";
  583.         exit(1);
  584.         }
  585.         /* Compute result strings */
  586.         memcpy(s, rdt->data.d, mptr-rdt->data.d);
  587.         *(s+(mptr-rdt->data.d))='\0';
  588.         memcpy(t, ntok->data.d, 3);
  589.         memcpy(t+3, mptr, endptr-mptr+1);
  590.         strcpy(t+3+(endptr-mptr)+1, ntok->data.d+3);
  591.         /* Replace original data */
  592.         free((void *) rdt->data.d);
  593.         ((tok *) rdt)->data.d=s;
  594.         free((void *) ntok->data.d);
  595.         ((tok *) ntok)->data.d=t;
  596.     }
  597.     break;
  598.         
  599.         
  600.     case T_SPEC:
  601.     if (rdt->end!=tok::TOK_START || rdt->data.d==NULL ||
  602.         strncmp(rdt->data.d, "eq ", 3)!=0)
  603.         break;
  604.     if ((nntok=this->feed_token())==NULL)
  605.         break;        // this is the end of the SPEC.
  606.     if (nntok->tok!=T_SPEC || nntok->end!=tok::TOK_END)
  607.     {
  608.         cerr<<"Unexpected value of nntok: type "
  609.         <<nntok->tok<<" end "<<nntok->end<<"\n";
  610.     }
  611.     if ((ntok=this->feed_token())==NULL)
  612.     {
  613.         output.insert(nntok);
  614.         break;
  615.     }
  616.     /* Passed all the easy rejection cases, invoke math_forward_scan */
  617.     saved_tok=ntok;
  618.     if (ntok->tok==T_PARAGRAPH && ntok->end!=tok::TOK_END &&
  619.         ntok->data.d!=NULL)
  620.     {
  621.         mptr=math_forward_scan(ntok->data.d);
  622.         endptr=ntok->data.d+strlen(ntok->data.d);
  623.         if (mptr==ntok->data.d)
  624.         {
  625.         output.insert(ntok); // This comes out second
  626.         output.insert(nntok);
  627.         saved_tok=NULL;
  628.         break;
  629.         }
  630.         /* Allocate memory */
  631.         if (*mptr!='\0')
  632.         {
  633.         if ((s=(char *) malloc(endptr-mptr))==NULL)
  634.         {
  635.             cerr<<"Malloc read_token::malloc failure (fatal)\n";
  636.             exit(1);
  637.         }
  638.         memcpy(s, mptr, endptr-mptr);
  639.         *(s+(endptr-mptr))='\0';
  640.         }
  641.         else
  642.         s=NULL;
  643.  
  644.         if ((t=(char *)
  645.          malloc(strlen(rdt->data.d)+mptr-ntok->data.d+1))==NULL)
  646.         {
  647.         if (s!=NULL)
  648.             free((void *) s);
  649.         cerr<<"Malloc read_token::malloc failure (fatal)\n";
  650.         exit(1);
  651.         }
  652.         endptr=rdt->data.d+strlen(rdt->data.d);
  653.         memcpy(t, rdt->data.d, endptr-rdt->data.d);
  654.         memcpy(t+(endptr-rdt->data.d), ntok->data.d, mptr-ntok->data.d);
  655.         *(t+(endptr-rdt->data.d)+(mptr-ntok->data.d))='\0';
  656.         /* Afjust result */
  657.         free((void *) rdt->data.d);
  658.         ((tok *) rdt)->data.d=t;
  659.         if (*mptr=='\0')
  660.         {
  661.         /* If we consumed 100% continue seeking */
  662.         delete(ntok);
  663.         saved_tok=rdt;
  664.         output.insert(nntok); // Re-insert end of spec.
  665.         goto math_aggregate;
  666.         }
  667.         free((void *) ntok->data.d);
  668.         ((tok *) ntok)->data.d=s;
  669.         /* Not all consumed, return result */
  670.     }
  671.     else if (ntok->tok==T_SPEC && ntok->end==tok::TOK_START &&
  672.          ntok->data.d!=NULL && strncmp(ntok->data.d, "eq ", 3)==0)
  673.     {
  674.         /* Combine consecutive eq's */
  675.         endptr=rdt->data.d+strlen(rdt->data.d);
  676.         if ((t=(char *)
  677.          malloc((endptr-rdt->data.d)+strlen(ntok->data.d)-2))==NULL)
  678.         {
  679.         cerr<<"Malloc read_token::malloc failure (fatal)\n";
  680.         exit(1);
  681.         }
  682.         memcpy(t, rdt->data.d, endptr-rdt->data.d);
  683.         strcpy(t+(endptr-rdt->data.d), ntok->data.d+3);
  684.         delete(nntok);    // Reply on end of spec following this eq
  685.         delete(ntok);    // Junk this eq
  686.         free((void *) rdt->data.d);
  687.         ((tok *) rdt)->data.d=t;
  688.         saved_tok=rdt;
  689.         goto math_aggregate;
  690.     }
  691.     output.insert(ntok); // This comes out second
  692.         output.insert(nntok);
  693.     saved_tok=NULL;
  694.     break;
  695.  
  696.         
  697.     default:
  698.     break;
  699.     }
  700.     return rdt;
  701. }
  702.  
  703.  
  704. /* Private choke point between equations and lists token reader */
  705. const tok_seq::tok *tok_seq::eqn_rd_token(void)
  706. {
  707.     const tok *t, *n;
  708.     fifo<tok> *tf;
  709.     int tot, specs;
  710.  
  711.     if ((t=this->math_collect())==NULL)
  712.     return NULL;
  713.  
  714.     switch(t->tok)
  715.     {
  716.     case T_PARAGRAPH:
  717.     if (t->end!=tok::TOK_START)
  718.         return t;
  719.     /* Check for spec only paragraph */
  720.  
  721.     tf=new(fifo<tok>);
  722.     n=t;
  723.     tot=0;
  724.     specs=0;
  725.     /*
  726.      * This loop counts the number of characters in paragraphs and other
  727.      * items untilt the end of the paragraph. Each item is dumped on tf
  728.      * and this is inserted onto the beginning of the output queue.
  729.      */
  730.     while(1)
  731.     {
  732.         tf->enqueue(n);
  733.         if (n->tok==T_PARAGRAPH)
  734.         {
  735.         if (n->end==tok::TOK_END)
  736.             break;
  737.         if (n->data.d!=NULL)
  738.             tot+=strlen(n->data.d);
  739.         if (tot>DISPL_TRESHOLD)
  740.             break;
  741.         }
  742.         else
  743.         specs++;
  744.  
  745.         if (n->tok!=T_SPEC && n->tok!=T_OTHER && n->tok!=T_PARAGRAPH)
  746.         {
  747.         tot+=DISPL_TRESHOLD;
  748.         break;
  749.         }
  750.         if ((n=this->math_collect())==NULL)
  751.         break;
  752.     }
  753.     /*
  754.      * If the total is small enough and there is one or more item that
  755.      * will make it through the filter. Since insert()ed things end up
  756.      * in reverse order we must first reverse the queue (this is the
  757.      * uncommon case, so it is OK if it costs a bit more).
  758.      */
  759.     if (tot<DISPL_TRESHOLD && specs>0)
  760.     {
  761.         tf->rev();
  762.         while ((n=tf->dequeue())!=NULL)
  763.         {
  764.         if (n->tok!=T_PARAGRAPH)
  765.             output.insert(n);
  766.         else
  767.             delete(n);
  768.         }
  769.     }
  770.     else
  771.     {
  772.         output.ins_trans(tf);
  773.     }
  774.     delete(tf);
  775.     t=output.dequeue();
  776.     break;
  777.  
  778.     default:
  779.     break;
  780.     }
  781.  
  782.     return t;
  783. }
  784.     
  785.  
  786. /*----------------------------------------------------------------------*/
  787. /* Now move on to lists.... */
  788.  
  789. /* Return NULL or a new list record */
  790. struct tok_seq::list_info *tok_seq::list_type(const char *txt)
  791. {
  792.     struct list_info *nl;
  793.     int i,n;
  794.  
  795.     /* Determine initial number, if any */
  796.     if (!isdigit(txt[0]))
  797.     n=-1;
  798.     else
  799.     {
  800.     n=i=0;
  801.     for (n=0, i=0; isdigit(txt[i]); i++)
  802.         n=n*10+txt[i]-'0';
  803.     }
  804.  
  805.     if (n==1)
  806.     {
  807.     nl=new(struct list_info);
  808.     nl->list_type=LIST_ENUMERATE;
  809.     nl->ldata.item_no=0;
  810.     nl->obj_cnt=0;
  811.     nl->text_cnt=0;
  812.     nl->last_item=new(fifo<tok_seq::tok>);
  813.     nl->items=0;
  814.     return nl;
  815.     }
  816.  
  817.     /* a., b., c. */
  818.     if (txt[0]=='a')
  819.     {
  820.     i=(txt[1]=='.') ? 2 : 1;
  821.     if (isspace(txt[i]))
  822.     {
  823.         nl=new(struct list_info);
  824.         nl->list_type=LIST_ENUM_ALPHA;
  825.         nl->ldata.lbullet=txt[0]-1;
  826.         nl->obj_cnt=0;
  827.         nl->text_cnt=0;
  828.         nl->last_item=new(fifo<tok_seq::tok>);
  829.         nl->items=0;
  830.         return nl;
  831.     }
  832.     }
  833.  
  834.     /* A., B., C. */
  835.     if (txt[0]=='A')
  836.     {
  837.     i=(txt[1]=='.') ? 2 : 1;
  838.     if (isspace(txt[i]))
  839.     {
  840.         nl=new(struct list_info);
  841.         nl->list_type=LIST_ENUM_ALPHA;
  842.         nl->ldata.lbullet=txt[0]-1;
  843.         nl->obj_cnt=0;
  844.         nl->text_cnt=0;
  845.         nl->last_item=new(fifo<tok_seq::tok>);
  846.         nl->items=0;
  847.         return nl;
  848.     }
  849.     }
  850.  
  851.     /* At present we only know about one of bullet */
  852.     if (txt[0]==(char) BULLET_CODE)
  853.     {
  854.     nl=new(struct list_info);
  855.     nl->list_type=LIST_BULLET;
  856.     nl->ldata.lbullet=txt[0];
  857.     nl->obj_cnt=0;
  858.     nl->text_cnt=0;
  859.     nl->last_item=new(fifo<tok_seq::tok>);
  860.     nl->items=0;
  861.     return nl;
  862.     }
  863.  
  864.     return NULL;
  865. }
  866.  
  867.  
  868. const char *tok_seq::l_type_name(const struct list_info *lp)
  869. {
  870.     switch(lp->list_type)
  871.     {
  872.     case LIST_BULLET:
  873.     return "itemize";
  874.     /* Not reached */
  875.  
  876.     case LIST_ENUMERATE:
  877.     return "enumerate";
  878.     /* Not reached */
  879.  
  880.     case LIST_ENUM_ALPHA:
  881.     return "listalpha";
  882.     /* Not reached */
  883.  
  884.     default:
  885.     return "programming error";
  886.     /* Not reached */
  887.     }
  888.     /* Not reached */
  889. }
  890.  
  891.  
  892. /* Dequeue a list and queue it is as paragraphs */
  893. static void list_to_para(fifo<tok_seq::tok> *out, fifo<tok_seq::tok> *add)
  894. {
  895.     tblock txt;
  896.     int was_item_st;
  897.     const tok_seq::tok *t;
  898.  
  899.     was_item_st=0;
  900.     while(!add->is_empty())
  901.     {
  902.     t=add->dequeue();
  903.     switch(t->tok)
  904.     {
  905.     case T_LIST:
  906.         delete(t);
  907.         continue;
  908.         /* Not reached */
  909.  
  910.     case T_ITEM:
  911.         if (t->end==tok_seq::tok::TOK_START)
  912.         {
  913.         txt.add(t->data.d);
  914.         txt.add(' ');
  915.         was_item_st=1;
  916.         }
  917.         delete(t);
  918.         continue;
  919.         /* not reached */
  920.     
  921.     case T_PARAGRAPH:
  922.         if (t->end!=tok_seq::tok::TOK_START)
  923.         break;
  924.         if (!was_item_st)
  925.         break;
  926.  
  927.         txt.add(t->data.d);
  928.         delete(t);
  929.         t=new(tok_seq::tok)(T_PARAGRAPH, (const char *) txt,
  930.                 tok_seq::tok::TOK_START);
  931.         txt.zero();
  932.         was_item_st=0;
  933.         break;
  934.  
  935.     default:
  936.         break;
  937.     }
  938.     out->enqueue(t);
  939.     }
  940. }
  941.  
  942. /*
  943.  * This handles cues for lists and the like. if ( ) else if ()
  944.  * ... gets messy fast
  945.  */
  946. const char *tok_seq::list_check(const char *txt, list_info **lh)
  947. {
  948.     struct list_info *lp, *nl;
  949.     char *s;
  950.     tok *t;
  951.     int i,n;
  952.  
  953.     /* Determine initial number. This will not change */
  954.     if (!isdigit(txt[0]))
  955.     n=-1;
  956.     else
  957.     {
  958.     n=i=0;
  959.     for (n=0, i=0; isdigit(txt[i]); i++)
  960.         n=n*10+txt[i]-'0';
  961.     }
  962.  
  963.     lp=*lh;
  964.  list_reconsider:
  965.     while (lp!=NULL)
  966.     {
  967.     *lh=lp;            // Makes no change unless lp changed below
  968.     switch (lp->list_type)
  969.     {
  970.     case LIST_ENUMERATE:
  971.         if (n==lp->ldata.item_no+1)
  972.         {
  973.         if (txt[i]=='.')
  974.             i++;
  975.         while (isspace(txt[i]))
  976.             i++;
  977.         if ((s=(char *) alloca(i+1))==NULL)
  978.         {
  979.             fprintf(stderr,
  980.                 "Warning: item label skipped due to lack"
  981.                 " of memory\n");
  982.         }
  983.         else
  984.         {
  985.             memcpy(s, txt, i);
  986.             *(s+i)='\0';
  987.         }
  988.         if (lp->items!=0)
  989.         {
  990.             outqueue.transfer(lp->last_item);
  991.             t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END);
  992.             outqueue.enqueue(t);
  993.         }
  994.         t=new(tok)(T_ITEM, s, tok::TOK_START);
  995.         lp->last_item->enqueue(t);
  996.         t=new(tok)(T_PARAGRAPH, txt+i, tok::TOK_START);
  997.         lp->last_item->enqueue(t);
  998.  
  999.         lp->ldata.item_no++;
  1000.         lp->obj_cnt=0;    // No not list objects after this one
  1001.         lp->text_cnt=0;
  1002.         lp->items++;
  1003.         return NULL;
  1004.         }
  1005.         break;
  1006.  
  1007.             
  1008.     case LIST_BULLET:
  1009.         if (txt[0]==lp->ldata.lbullet)
  1010.         {
  1011.         for (i=0; (isspace(txt[i])); i++ ) ;
  1012.         if ((s=(char *) alloca(2))==NULL)
  1013.         {
  1014.             fprintf(stderr,
  1015.                 "Warning: item label skipped due to lack"
  1016.                 " of memory\n");
  1017.         }
  1018.         else
  1019.         {
  1020.             *s=lp->ldata.lbullet;
  1021.             *(s+1)='\0';
  1022.         }
  1023.         if (lp->items!=0)
  1024.         {
  1025.             outqueue.transfer(lp->last_item);
  1026.             t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END);
  1027.             outqueue.enqueue(t);
  1028.         }
  1029.         t=new(tok)(T_ITEM, s, tok::TOK_START);
  1030.         lp->last_item->enqueue(t);
  1031.  
  1032.         while (isspace(*(++txt)));
  1033.         t=new(tok)(T_PARAGRAPH, txt, tok::TOK_START);
  1034.         lp->last_item->enqueue(t);
  1035.  
  1036.         lp->obj_cnt=0;    // No not list objects after this one
  1037.         lp->text_cnt=0;
  1038.         lp->items++;
  1039.         return NULL;
  1040.         }
  1041.         break;
  1042.  
  1043.     case LIST_ENUM_ALPHA:
  1044.         if (txt[0]==lp->ldata.lbullet+1)
  1045.         {
  1046.         lp->ldata.lbullet++;
  1047.         if ((s=(char *) alloca(3))==NULL)
  1048.         {
  1049.             fprintf(stderr,
  1050.                 "Warning: item label skipped due to lack"
  1051.                 " of memory\n");
  1052.         }
  1053.         else
  1054.         {
  1055.             *s=lp->ldata.lbullet;
  1056.             if (txt[1]=='.')
  1057.             {
  1058.             *(s+1)='.';
  1059.             *(s+2)='\0';
  1060.             }
  1061.             else
  1062.             *(s+1)='\0';
  1063.         }
  1064.         if (lp->items!=0)
  1065.         {
  1066.             outqueue.transfer(lp->last_item);
  1067.             t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END);
  1068.             outqueue.enqueue(t);
  1069.         }
  1070.         t=new(tok)(T_ITEM, s, tok::TOK_START);
  1071.         lp->last_item->enqueue(t);
  1072.  
  1073.         for (i=0; (!isspace(txt[i])); i++ ) ;
  1074.         for ( ;(isspace(txt[i])); i++) ;
  1075.         t=new(tok)(T_PARAGRAPH, txt+i, tok::TOK_START);
  1076.         lp->last_item->enqueue(t);
  1077.  
  1078.         lp->obj_cnt=0;    // No not list objects after this one
  1079.         lp->text_cnt=0;
  1080.         lp->items++;
  1081.         return NULL;
  1082.         }
  1083.         break;
  1084.         
  1085.     default:
  1086.         fprintf(stderr, "Popping invalid list type %d\n",
  1087.             lp->ldata.item_no);
  1088.         nl=lp->next_list;
  1089.         free(lp);
  1090.         continue;
  1091.     }
  1092.     
  1093.     /* Not the right thing */
  1094.     if ((nl=list_type(txt))!=NULL)
  1095.     {
  1096.         if (lp!=NULL && !(lp->last_item->is_empty()))
  1097.         outqueue.transfer(lp->last_item); // Output outstanding items
  1098.         t=new(tok)(T_LIST, l_type_name(nl), tok::TOK_START);
  1099.         nl->last_item->enqueue(t);
  1100.         nl->next_list=lp;
  1101.         lp=nl;
  1102.         continue;
  1103.     }
  1104.     
  1105.     lp->obj_cnt++;
  1106.     lp->text_cnt +=strlen(txt);
  1107.     if (lp->obj_cnt>PAR_ITEM_SEP_LIMIT || lp->text_cnt>TEXT_ITEM_SEP_LIMIT)
  1108.     {
  1109.         /* If only one item, not a list */
  1110.         if (lp->items<2)
  1111.         {
  1112.         recycled=new(fifo<tok_seq::tok>);
  1113.         list_to_para(recycled, lp->last_item);
  1114.         delete(lp->last_item);
  1115.         nl=lp->next_list;
  1116.         free(lp);
  1117.         lp=nl;
  1118.         *lh=lp;
  1119.         if (lp!=NULL)
  1120.             lp->last_item->enqueue(recycled->dequeue());
  1121.         else
  1122.             outqueue.enqueue(recycled->dequeue());
  1123.         return NULL;
  1124.         }
  1125.  
  1126.         /* Copy the list item */
  1127.         if (!(lp->last_item->is_empty()))
  1128.         {
  1129.         const tok *tf;
  1130.  
  1131.         tf=lp->last_item->dequeue();
  1132.         while (tf->tok!=T_PARAGRAPH || tf->end!=tok::TOK_END)
  1133.         {
  1134.             outqueue.enqueue(tf);
  1135.             if (lp->last_item->is_empty())
  1136.             goto lend_para_done;
  1137.             tf=lp->last_item->dequeue();
  1138.         }
  1139.         outqueue.enqueue(tf);
  1140.         lend_para_done: ;
  1141.         }
  1142.  
  1143.         /* Finish off the list */
  1144.         t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END);
  1145.         outqueue.enqueue(t);
  1146.         t=new(tok)(T_LIST, l_type_name(lp), tok::TOK_END);
  1147.         outqueue.enqueue(t);
  1148.         nl=lp->next_list;
  1149.         recycled=lp->last_item;    // Recycle elements queued
  1150.         t=new(tok)(T_PARAGRAPH, txt, tok::TOK_START);
  1151.         recycled->enqueue(t);
  1152.         free(lp);
  1153.         lp=nl;
  1154.         *lh=lp;
  1155.         return NULL;
  1156.     }
  1157.     
  1158.     t=new(tok)(T_PARAGRAPH, txt, tok::TOK_START);
  1159.     lp->last_item->enqueue(t);
  1160.     return NULL;
  1161.     }
  1162.  
  1163.     /* lp==NULL if we get here */
  1164.  
  1165.     if ((nl=list_type(txt))!=NULL)
  1166.     {
  1167.     nl->next_list=lp;
  1168.     lp=nl;
  1169.     t=new(tok)(T_LIST, l_type_name(nl), tok::TOK_START);
  1170.     nl->last_item->enqueue(t);
  1171.     goto list_reconsider;
  1172.     }
  1173.  
  1174.     return txt;
  1175.  
  1176. }             
  1177.  
  1178. const tok_seq::tok *tok_seq::read_token(void)
  1179. {
  1180.     const tok *tf;
  1181.     const char *tp;
  1182.     tok *t;
  1183.     struct list_info *nl;
  1184.  
  1185.     while(outqueue.is_empty())
  1186.     {
  1187.     if (recycled!=NULL)
  1188.     {
  1189.         if (recycled->is_empty())
  1190.         {
  1191.         delete(recycled);
  1192.         recycled=NULL;
  1193.         continue;    // outqueue still empty
  1194.         }
  1195.         tf=recycled->dequeue();
  1196.     }
  1197.     else
  1198.         tf=this->eqn_rd_token();
  1199.     if (tf==NULL)
  1200.     {
  1201.         if (!done_end)
  1202.         {
  1203.         tok *t;
  1204.         t=new(tok)(T_DOC, "End of word2x output", tok::TOK_END);
  1205.         output.enqueue(t);
  1206.         done_end=1;
  1207.         continue;
  1208.         }
  1209.         else
  1210.         return NULL;
  1211.     }
  1212.  
  1213.     if (tf->tok==T_DOC && tf->end==tok::TOK_END)
  1214.     {
  1215.         /* End all lists */
  1216.         while (lp!=NULL)
  1217.         {
  1218.         tp=l_type_name(lp);
  1219.         nl=lp->next_list;
  1220.  
  1221.         if (!(lp->last_item->is_empty()))
  1222.             outqueue.transfer(lp->last_item);
  1223.         delete(lp->last_item);
  1224.         free(lp);
  1225.         t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END);
  1226.         outqueue.enqueue(t);
  1227.         t=new(tok)(T_LIST, tp, tok::TOK_END);
  1228.         outqueue.enqueue(t);
  1229.         lp=nl;
  1230.         }
  1231.         outqueue.enqueue(tf);
  1232.     }
  1233.     else if (tf->tok==T_PARAGRAPH && tf->end==tok::TOK_START)
  1234.     {
  1235.         tp=list_check(tf->data.d, &lp);
  1236.         if (tp!=NULL)
  1237.         {
  1238.         t=new(tok)(T_PARAGRAPH, tp, tok::TOK_START);
  1239.         outqueue.enqueue(t);
  1240.         /* End paragraph will come from previous stage */
  1241.         }
  1242.         delete(tf);
  1243.     }
  1244.     else
  1245.     {
  1246.         if (lp==NULL)
  1247.         outqueue.enqueue(tf);
  1248.         else
  1249.         lp->last_item->enqueue(tf);
  1250.     }
  1251.     }
  1252.     tf=outqueue.dequeue();
  1253.     return tf;
  1254. }
  1255.  
  1256.  
  1257. ostream &operator<<(ostream &os, const tok_seq::tok *d)
  1258. {
  1259.     os<<'('<<d->tok<<',';
  1260.     switch(d->dtype)
  1261.     {
  1262.     case 1:
  1263.     if (d->data.d!=NULL && strlen(d->data.d)>10)
  1264.     {
  1265.         char foo[11];
  1266.         int i;
  1267.         
  1268.         for(i=0; i<7; i++)
  1269.         foo[i]=d->data.d[i];
  1270.         for ( ; i<10; i++)
  1271.         foo[i]='.';
  1272.         foo[10]='\0';
  1273.         os<<foo;
  1274.     }
  1275.     else
  1276.         os<<d->data.d;
  1277.     break;
  1278.     case 0:
  1279.     os<<d->data.table.rows<<'x'<<d->data.table.cols;
  1280.     break;
  1281.     }
  1282.     os<<','<<((d->end==tok_seq::tok::TOK_START) ? "start" : "end")<<')';
  1283.     return os;
  1284. }
  1285.  
  1286. tok_seq::tok &tok_seq::tok::operator=(const tok_seq::tok &d)
  1287. {
  1288.     tok=d.tok;
  1289.     end=d.end;
  1290.     dtype=d.dtype;
  1291.     if (d.dtype==TEXT && d.data.d!=NULL)
  1292.     {
  1293.     data.d=strdup(d.data.d);
  1294.     }
  1295.     return (*this);
  1296. }
  1297.