diff options
Diffstat (limited to 'support/word2x/reader.cc')
-rw-r--r-- | support/word2x/reader.cc | 1246 |
1 files changed, 1246 insertions, 0 deletions
diff --git a/support/word2x/reader.cc b/support/word2x/reader.cc new file mode 100644 index 0000000000..cf5ed66a33 --- /dev/null +++ b/support/word2x/reader.cc @@ -0,0 +1,1246 @@ +/* $Id: reader.cc,v 1.27 1997/04/17 20:24:41 dps Exp $ */ +/* Reads the word document */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#if HAVE_ALLOCA_H +#include <alloca.h> +#else /* Do not have alloca.h */ +#ifdef _AIX + #pragma alloca +#else /* not _AIX */ +extern "C" char *alloca(int); +#endif /* _AIX */ +#endif /* HAVE_ALLOCA_H */ +#endif /* __GNUC__ */ + +#include <iostream.h> +#include <stdio.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif /* HAVE_CTYPE_H */ +#include "word6.h" +#include "interface.h" +#include "reader.h" + +/* This code is basically a layered filtration process. At the bottom layer + is this next function that reads a character from a word document. Pointers + to object are used extensively to avoid implicit copies. */ + +/* Please be aware that the junk should be stripped from in */ +static int read_character(istream *in) +{ + unsigned char c,d; + static int s_ch=-1; + + if (s_ch==-1) + { + if (in->eof()) + return EOF; + + in->get(c); + } + else + { + c=(unsigned char) s_ch; + s_ch=-1; + } + + if (c=='\n') + c='\r'; + + switch(c) + { + case PAR_END: + return (CH_PAR | CONTROL_FLAG); + + case TABLE_SEP: + if (!in->eof()) + in->get(d); + else + d=c+1; // Not equal to c + if (d!=c) + { + s_ch=d; /* Push back character */ + return (CH_FIELD | CONTROL_FLAG); + } + return (CH_ROW | CONTROL_FLAG); + + case START_CTL: + return (CH_SPEC | CONTROL_FLAG); + + case END_CTL: + return (CH_ENDSPEC | CONTROL_FLAG); + + case HARD_RETURN: + return (CH_HDRTN | CONTROL_FLAG); + + case NEW_PAGE: + return (CH_PAGE | CONTROL_FLAG); + + case FOOTNOTE: + return (CH_FOOTNOTE | CONTROL_FLAG); + + default: + if (c<' ') + return (CH_OTHER | CONTROL_FLAG); + else + return c; + } + /* NOT REACHED */ +} + + +/* This function reads a paragraph, field of a table or whatever. It copies + everything in any embed tags unprocessed and leaves it in the element */ +void chunk_reader::read_chunk_raw(void) +{ + int c, is_ctl=0; + + text.zero(); // Zero text buffer + while ((c=read_character(in))!=(EOF | CONTROL_FLAG)) + { + if (c & CONTROL_FLAG) + { + c &= ~CONTROL_FLAG; + /* If in embedded item then ignore all but end embed */ + if (is_ctl) + { + if (c==CH_ENDSPEC) + { + is_ctl=0; + text.add(c); + } + continue; + } + + switch(c) + { + case CH_PAR: + case CH_FIELD: + case CH_ROW: + break; + + case CH_HDRTN: + text.add('\n'); // Add newline + continue; // Continue processing + + case CH_OTHER: + continue; // Just ignore character + + case CH_SPEC: + text.add(c); + if (!is_ctl) + is_ctl=1; + continue; + + case CH_ENDSPEC: + cerr<<"Suprious ^U ignored\n"; + continue; + + case CH_PAGE: + case CH_FOOTNOTE: + text.add(c); + continue; + + default: + cerr<<"Unexpected value "<<(c & (~CONTROL_FLAG))\ + <<" switch\n"; + continue; + } + type=c; + tptr=text; + return; + } + /* Not control or end of inclusion */ + text.add(c); + } + type=CH_EOF; + tptr=text; + return; +} + + +/* This function reads chunks from using read_chunk_raw and hands them + out in contigous peices of the same type. Emebedded stuff gets + seperated from the rest here. The partial flag is set if only some + of a field is returned (usually because of an embedded item). +*/ +struct chunk_rtn chunk_reader::read_chunk(void) +{ + const char *s; // Save stupid compilers + struct chunk_rtn res; + + if (tptr==NULL) + this->read_chunk_raw(); + + s=tptr; + + /* Embed */ + if (*s==CH_SPEC) + { + while (*(++s)) + { + if (*s==CH_ENDSPEC) + break; + res.txt.add(*s); + } + tptr=s+1; + res.type=CH_SPEC; + return res; + } + + /* New page */ + if (*s==CH_PAGE) + { + res.type=CH_PAGE; + tptr=s+1; + return res; + } + + if (*s==CH_FOOTNOTE) + { + res.type=CH_FOOTNOTE; + tptr=s+1; + return res; + } + + /* Normal */ + while (*s) + { + if (*s==CH_SPEC || *s==CH_PAGE || *s==CH_FOOTNOTE) + { + tptr=s; + res.type=(PART_FLAG | type); + return res; + } + + res.txt.add(*s); + s++; + } + res.type=type; + tptr=NULL; + text.zero(); // Save memory + return res; +} + + + +/*----------------------------------------------------------------------*/ +/* Tables and basic stuff */ + +/* + * Refill the token queue. + */ +int tok_seq::rd_token(void) +{ + struct chunk_rtn r; + tok *t; + char other[2]; + + r=read_chunk(); + if (r.type==CH_EOF) + return 0; + + switch(r.type & ~PART_FLAG) + { + case CH_ROW: + if (table==NULL) + table=new(table_info); + /* Handle 1 field rows properly */ + if (table->col==0) + { + t=new(tok)(T_ROW, (void *) NULL, tok::TOK_START); + table->enqueue(t); + } + table->col++; + if (table->col>table->cols) + table->cols=table->col; + table->rows++; + table->tok_push(T_FIELD, &(r.txt)); + t=new(tok)(T_ROW, (void *) NULL, tok::TOK_END); + table->enqueue(t); + table->col=0; + break; + + case CH_FIELD: + if (table==NULL) + { + table=new(table_info); + } + if (table->col==0) + { + t=new(tok)(T_ROW, (void *) NULL, tok::TOK_START); + table->enqueue(t); + } + table->col++; + table->tok_push(T_FIELD, &(r.txt)); + break; + + + case CH_PAR: + if (table!=NULL) + { + /* Table handling */ + if (table->col!=0) + { +#if 0 + table->tok_push(T_FIELD, &(r.txt)); + t=new(tok)(T_ROW, (void *) NULL, tok::TOK_END); + table->enqueue(t); + t=new(tok)(T_ROW, (void *) NULL, tok::TOK_START); + table->enqueue(t); + for (i=0; i<table->col; i++) + { + t=new(tok)(T_FIELD, "\0", tok::TOK_START); + table->enqueue(t); + t=new(tok)(T_FIELD, (void *) NULL, tok::TOK_END); + table->enqueue(t); + } + table->rows++; + break; +#else + t=new(tok)(T_ROW, (void *) NULL, tok::TOK_END); + table->enqueue(t); + if (table->col>table->cols) + table->cols=table->col; + table->rows++; +#endif + } + table->finish(&output); + delete(table); + table=NULL; + } + + if (r.type & PART_FLAG) + { + tok *td; + td=new(tok)(T_PARAGRAPH, (const char *) (r.txt), tok::TOK_START); + output.enqueue(td); + } + else + tok_push(T_PARAGRAPH, &(r.txt)); + break; + + case CH_SPEC: + tok_push(T_SPEC, &(r.txt)); + break; + + case CH_PAGE: + case CH_FOOTNOTE: + other[0]=r.type; + other[1]='\0'; + t=new(tok)(T_CODE, other, tok::TOK_START); + output.enqueue(t); + break; + + default: + break; + } + + return 1; +} + + + +/*----------------------------------------------------------------------*/ +/* Equations.... */ + +/* + * Code that scans forward to the end of stuff that looks like an extension + * of some maths that was the last thing. + */ +const char *math_forward_scan(const char *s) +{ + const char *scan, *end; + int blvl; + + end=scan=s; + + /* Check whether the first part looks like more of the equation */ + while (1) + { + /* Skip spaces */ + while (isspace(*scan)) + scan++; + + /* Look for binary operator */ + if (*scan=='+' || *scan=='-' || *scan=='*' || *scan=='/' || + *scan=='=') + { + /* skip spaces */ + scan++; + while (isspace(*scan)) + scan++; + + /* Grab next word */ + blvl=0; + while (!isspace(*scan) || blvl>0) + { + switch(*scan) + { + case '(': + blvl++; + break; + + case ')': + blvl--; + break; + + default: + break; + } + if (*scan=='\0') + break; // Robustness fix + scan++; + } + + end=scan; // Update end + } + else + break; // No binary operator, assume no text + } + return end; +} + +/* + * Code that scans backwards to the start of stuff that looks like it should + * ohave been prepended to the current maths. + */ +const char *math_reverse_scan(const char *s) +{ + const char *scan, *start; + int blvl; + + start=scan=s+strlen(s)-1; + + /* Check whether the first part looks like more of the equation */ + while (scan>=s) + { + /* Skip spaces */ + while (scan>=s && isspace(*scan)) + scan--; + if (scan<s) + return s; + + /* Look for binary operator */ + if (*scan=='+' || *scan=='-' || *scan=='*' || *scan=='/' || + *scan=='=') + { + /* skip spaces */ + scan--; + while (scan>=s && isspace(*scan)) + scan--; + if (scan<s) + return s; + + /* Grab next word */ + blvl=0; + while (!isspace(*scan) || blvl>0 ) + { + switch(*scan) + { + case ')': + blvl++; + break; + + case '(': + blvl--; + break; + + default: + break; + } + if (scan==s) + return s; // Robustness fix + scan--; + } + start=scan; // Update end + } + else + break; // No binary operator, assume no text + } + return start; +} + +/* + * Code to feed a token one at a time. (private, need prostproccessing + * to compensate for equation abuse by word users) + */ +const tok_seq::tok *tok_seq::feed_token(void) +{ + while (output.is_empty()) + { + if (!rd_token()) + return NULL; + } + return output.dequeue(); +} + +/* Private token reader, compensates for equation abuse */ +const tok_seq::tok *tok_seq::math_collect(void) +{ + const tok *rdt, *ntok, *nntok; + const char *mptr, *endptr; + char *s, *t; + + math_aggregate: ; + if ((rdt=this->saved_tok)==NULL) + { + if ((rdt=this->feed_token())==NULL) + return NULL; + } + else + saved_tok=NULL; + + switch (rdt->tokval & (~PART_FLAG)) + { + case T_PARAGRAPH: + if (rdt->end!=tok::TOK_START || (rdt->tokval & PART_FLAG==0) + || rdt->data.d==NULL) + break; + if ((ntok=this->feed_token())==NULL) + break; + /* Passed all the easy rejection cases, invoke math_reverse_scan */ + saved_tok=ntok; + if (ntok->tokval==T_SPEC && ntok->end==tok::TOK_START && + ntok->data.d!=NULL && strncmp(ntok->data.d, "eq ", 3)==0) + { + mptr=math_reverse_scan(rdt->data.d); + endptr=rdt->data.d+strlen(rdt->data.d)-1; + if (mptr>=endptr) + break; + /* Allocate memory */ + if ((s=(char *) malloc(mptr-rdt->data.d+1))==NULL) + { + cerr<<"Malloc read_token::malloc failure (fatal)\n"; + exit(1); + } + if ((t=(char *) malloc(strlen(ntok->data.d)+endptr-mptr+1))==NULL) + { + free((void *) s); + cerr<<"Malloc read_token::malloc failure (fatal)\n"; + exit(1); + } + /* Compute result strings */ + memcpy(s, rdt->data.d, mptr-rdt->data.d); + *(s+(mptr-rdt->data.d))='\0'; + memcpy(t, ntok->data.d, 3); + memcpy(t+3, mptr, endptr-mptr+1); + strcpy(t+3+(endptr-mptr)+1, ntok->data.d+3); + /* Replace original data */ + free((void *) rdt->data.d); + ((tok *) rdt)->data.d=s; + free((void *) ntok->data.d); + ((tok *) ntok)->data.d=t; + } + break; + + + case T_SPEC: + if (rdt->end!=tok::TOK_START || rdt->data.d==NULL || + strncmp(rdt->data.d, "eq ", 3)!=0) + break; + if ((nntok=this->feed_token())==NULL) + break; // this is the end of the SPEC. + if (nntok->tokval!=T_SPEC || nntok->end!=tok::TOK_END) + { + cerr<<"Unexpected value of nntok: type " + <<nntok->tokval<<" end "<<nntok->end<<"\n"; + } + if ((ntok=this->feed_token())==NULL) + { + output.insert(nntok); + break; + } + /* Passed all the easy rejection cases, invoke math_forward_scan */ + saved_tok=ntok; + if (ntok->tokval==T_PARAGRAPH && ntok->end!=tok::TOK_END && + ntok->data.d!=NULL) + { + mptr=math_forward_scan(ntok->data.d); + endptr=ntok->data.d+strlen(ntok->data.d); + if (mptr==ntok->data.d) + { + output.insert(ntok); // This comes out second + output.insert(nntok); + saved_tok=NULL; + break; + } + /* Allocate memory */ + if (*mptr!='\0') + { + if ((s=(char *) malloc(endptr-mptr))==NULL) + { + cerr<<"Malloc read_token::malloc failure (fatal)\n"; + exit(1); + } + memcpy(s, mptr, endptr-mptr); + *(s+(endptr-mptr))='\0'; + } + else + s=NULL; + + if ((t=(char *) + malloc(strlen(rdt->data.d)+mptr-ntok->data.d+1))==NULL) + { + if (s!=NULL) + free((void *) s); + cerr<<"Malloc read_token::malloc failure (fatal)\n"; + exit(1); + } + endptr=rdt->data.d+strlen(rdt->data.d); + memcpy(t, rdt->data.d, endptr-rdt->data.d); + memcpy(t+(endptr-rdt->data.d), ntok->data.d, mptr-ntok->data.d); + *(t+(endptr-rdt->data.d)+(mptr-ntok->data.d))='\0'; + /* Afjust result */ + free((void *) rdt->data.d); + ((tok *) rdt)->data.d=t; + if (*mptr=='\0') + { + /* If we consumed 100% continue seeking */ + delete(ntok); + saved_tok=rdt; + output.insert(nntok); // Re-insert end of spec. + goto math_aggregate; + } + free((void *) ntok->data.d); + ((tok *) ntok)->data.d=s; + /* Not all consumed, return result */ + } + else if (ntok->tokval==T_SPEC && ntok->end==tok::TOK_START && + ntok->data.d!=NULL && strncmp(ntok->data.d, "eq ", 3)==0) + { + /* Combine consecutive eq's */ + endptr=rdt->data.d+strlen(rdt->data.d); + if ((t=(char *) + malloc((endptr-rdt->data.d)+strlen(ntok->data.d)-2))==NULL) + { + cerr<<"Malloc read_token::malloc failure (fatal)\n"; + exit(1); + } + memcpy(t, rdt->data.d, endptr-rdt->data.d); + strcpy(t+(endptr-rdt->data.d), ntok->data.d+3); + delete(nntok); // Reply on end of spec following this eq + delete(ntok); // Junk this eq + free((void *) rdt->data.d); + ((tok *) rdt)->data.d=t; + saved_tok=rdt; + goto math_aggregate; + } + output.insert(ntok); // This comes out second + output.insert(nntok); + saved_tok=NULL; + break; + + + default: + break; + } + return rdt; +} + + +/* Private choke point between equations and lists token reader */ +const tok_seq::tok *tok_seq::eqn_rd_token(void) +{ + const tok *t, *n; + fifo<tok> *tf; + int tot, specs; + + if ((t=this->math_collect())==NULL) + return NULL; + + switch(t->tokval) + { + case T_PARAGRAPH: + if (t->end!=tok::TOK_START) + return t; + /* Check for spec only paragraph */ + + tf=new(fifo<tok>); + n=t; + tot=0; + specs=0; + /* + * This loop counts the number of characters in paragraphs and other + * items untilt the end of the paragraph. Each item is dumped on tf + * and this is inserted onto the beginning of the output queue. + */ + while(1) + { + tf->enqueue(n); + if (n->tokval==T_PARAGRAPH) + { + if (n->end==tok::TOK_END) + break; + if (n->data.d!=NULL) + tot+=strlen(n->data.d); + if (tot>DISPL_TRESHOLD) + break; + } + else + specs++; + + if (n->tokval!=T_SPEC && n->tokval!=T_OTHER && n->tokval!=T_PARAGRAPH) + { + tot+=DISPL_TRESHOLD; + break; + } + if ((n=this->math_collect())==NULL) + break; + } + /* + * If the total is small enough and there is one or more item that + * will make it through the filter. Since insert()ed things end up + * in reverse order we must first reverse the queue (this is the + * uncommon case, so it is OK if it costs a bit more). + */ + if (tot<DISPL_TRESHOLD && specs>0) + { + tf->rev(); + while ((n=tf->dequeue())!=NULL) + { + if (n->tokval!=T_PARAGRAPH) + output.insert(n); + else + delete(n); + } + } + else + { + output.ins_trans(tf); + } + delete(tf); + t=output.dequeue(); + break; + + default: + break; + } + + return t; +} + + +/*----------------------------------------------------------------------*/ +/* Now move on to lists.... */ + +/* Return NULL or a new list record */ +struct tok_seq::list_info *tok_seq::list_type(const char *txt) +{ + struct list_info *nl; + int i,n; + + /* Determine initial number, if any */ + if (!isdigit(txt[0])) + n=-1; + else + { + n=i=0; + for (n=0, i=0; isdigit(txt[i]); i++) + n=n*10+txt[i]-'0'; + } + + if (n==1) + { + nl=new(struct list_info); + nl->list_type=LIST_ENUMERATE; + nl->ldata.item_no=0; + nl->obj_cnt=0; + nl->text_cnt=0; + nl->last_item=new(fifo<tok_seq::tok>); + nl->items=0; + return nl; + } + + /* a., b., c. */ + if (txt[0]=='a') + { + i=(txt[1]=='.') ? 2 : 1; + if (isspace(txt[i])) + { + nl=new(struct list_info); + nl->list_type=LIST_ENUM_ALPHA; + nl->ldata.lbullet=txt[0]-1; + nl->obj_cnt=0; + nl->text_cnt=0; + nl->last_item=new(fifo<tok_seq::tok>); + nl->items=0; + return nl; + } + } + + /* A., B., C. */ + if (txt[0]=='A') + { + i=(txt[1]=='.') ? 2 : 1; + if (isspace(txt[i])) + { + nl=new(struct list_info); + nl->list_type=LIST_ENUM_ALPHA; + nl->ldata.lbullet=txt[0]-1; + nl->obj_cnt=0; + nl->text_cnt=0; + nl->last_item=new(fifo<tok_seq::tok>); + nl->items=0; + return nl; + } + } + + /* At present we only know about one of bullet */ + if (txt[0]==(char) BULLET_CODE) + { + nl=new(struct list_info); + nl->list_type=LIST_BULLET; + nl->ldata.lbullet=txt[0]; + nl->obj_cnt=0; + nl->text_cnt=0; + nl->last_item=new(fifo<tok_seq::tok>); + nl->items=0; + return nl; + } + + return NULL; +} + + +const char *tok_seq::l_type_name(const struct list_info *lp) +{ + switch(lp->list_type) + { + case LIST_BULLET: + return "itemize"; + /* Not reached */ + + case LIST_ENUMERATE: + return "enumerate"; + /* Not reached */ + + case LIST_ENUM_ALPHA: + return "listalpha"; + /* Not reached */ + + default: + return "programming error"; + /* Not reached */ + } + /* Not reached */ +} + + +/* Dequeue a list and queue it is as paragraphs */ +static void list_to_para(fifo<tok_seq::tok> *out, fifo<tok_seq::tok> *add) +{ + tblock txt; + int was_item_st; + const tok_seq::tok *t; + + was_item_st=0; + while(!add->is_empty()) + { + t=add->dequeue(); + switch(t->tokval) + { + case T_LIST: + delete(t); + continue; + /* Not reached */ + + case T_ITEM: + if (t->end==tok_seq::tok::TOK_START) + { + txt.add(t->data.d); + txt.add(' '); + was_item_st=1; + } + delete(t); + continue; + /* not reached */ + + case T_PARAGRAPH: + if (t->end!=tok_seq::tok::TOK_START) + break; + if (!was_item_st) + break; + + txt.add(t->data.d); + delete(t); + t=new(tok_seq::tok)(T_PARAGRAPH, (const char *) txt, + tok_seq::tok::TOK_START); + txt.zero(); + was_item_st=0; + break; + + default: + break; + } + out->enqueue(t); + } +} + +/* + * This handles cues for lists and the like. if ( ) else if () + * ... gets messy fast + */ +const char *tok_seq::list_check(const char *txt, list_info **lh) +{ + struct list_info *lp, *nl; + char *s; + tok *t; + int i,n; + + /* Determine initial number. This will not change */ + if (!isdigit(txt[0])) + n=-1; + else + { + n=i=0; + for (n=0, i=0; isdigit(txt[i]); i++) + n=n*10+txt[i]-'0'; + } + + lp=*lh; + list_reconsider: + while (lp!=NULL) + { + *lh=lp; // Makes no change unless lp changed below + switch (lp->list_type) + { + case LIST_ENUMERATE: + if (n==lp->ldata.item_no+1) + { + if (txt[i]=='.') + i++; + while (isspace(txt[i])) + i++; + if ((s=(char *) alloca(i+1))==NULL) + { + fprintf(stderr, + "Warning: item label skipped due to lack" + " of memory\n"); + } + else + { + memcpy(s, txt, i); + *(s+i)='\0'; + } + if (lp->items!=0) + { + outqueue.transfer(lp->last_item); + t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END); + outqueue.enqueue(t); + } + t=new(tok)(T_ITEM, s, tok::TOK_START); + lp->last_item->enqueue(t); + t=new(tok)(T_PARAGRAPH, txt+i, tok::TOK_START); + lp->last_item->enqueue(t); + + lp->ldata.item_no++; + lp->obj_cnt=0; // No not list objects after this one + lp->text_cnt=0; + lp->items++; + return NULL; + } + break; + + + case LIST_BULLET: + if (txt[0]==lp->ldata.lbullet) + { + for (i=0; (isspace(txt[i])); i++ ) ; + if ((s=(char *) alloca(2))==NULL) + { + fprintf(stderr, + "Warning: item label skipped due to lack" + " of memory\n"); + } + else + { + *s=lp->ldata.lbullet; + *(s+1)='\0'; + } + if (lp->items!=0) + { + outqueue.transfer(lp->last_item); + t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END); + outqueue.enqueue(t); + } + t=new(tok)(T_ITEM, s, tok::TOK_START); + lp->last_item->enqueue(t); + + while (isspace(*(++txt))); + t=new(tok)(T_PARAGRAPH, txt, tok::TOK_START); + lp->last_item->enqueue(t); + + lp->obj_cnt=0; // No not list objects after this one + lp->text_cnt=0; + lp->items++; + return NULL; + } + break; + + case LIST_ENUM_ALPHA: + if (txt[0]==lp->ldata.lbullet+1) + { + lp->ldata.lbullet++; + if ((s=(char *) alloca(3))==NULL) + { + fprintf(stderr, + "Warning: item label skipped due to lack" + " of memory\n"); + } + else + { + *s=lp->ldata.lbullet; + if (txt[1]=='.') + { + *(s+1)='.'; + *(s+2)='\0'; + } + else + *(s+1)='\0'; + } + if (lp->items!=0) + { + outqueue.transfer(lp->last_item); + t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END); + outqueue.enqueue(t); + } + t=new(tok)(T_ITEM, s, tok::TOK_START); + lp->last_item->enqueue(t); + + for (i=0; (!isspace(txt[i])); i++ ) ; + for ( ;(isspace(txt[i])); i++) ; + t=new(tok)(T_PARAGRAPH, txt+i, tok::TOK_START); + lp->last_item->enqueue(t); + + lp->obj_cnt=0; // No not list objects after this one + lp->text_cnt=0; + lp->items++; + return NULL; + } + break; + + default: + fprintf(stderr, "Popping invalid list type %d\n", + lp->ldata.item_no); + nl=lp->next_list; + free(lp); + continue; + } + + /* Not the right thing */ + if ((nl=list_type(txt))!=NULL) + { + if (lp!=NULL && !(lp->last_item->is_empty())) + outqueue.transfer(lp->last_item); // Output outstanding items + t=new(tok)(T_LIST, l_type_name(nl), tok::TOK_START); + nl->last_item->enqueue(t); + nl->next_list=lp; + lp=nl; + continue; + } + + lp->obj_cnt++; + lp->text_cnt +=strlen(txt); + if (lp->obj_cnt>PAR_ITEM_SEP_LIMIT || lp->text_cnt>TEXT_ITEM_SEP_LIMIT) + { + /* If only one item, not a list */ + if (lp->items<2) + { + recycled=new(fifo<tok_seq::tok>); + list_to_para(recycled, lp->last_item); + delete(lp->last_item); + nl=lp->next_list; + free(lp); + lp=nl; + *lh=lp; + if (lp!=NULL) + lp->last_item->enqueue(recycled->dequeue()); + else + outqueue.enqueue(recycled->dequeue()); + return NULL; + } + + /* Copy the list item */ + if (!(lp->last_item->is_empty())) + { + const tok *tf; + + tf=lp->last_item->dequeue(); + while (tf->tokval!=T_PARAGRAPH || tf->end!=tok::TOK_END) + { + outqueue.enqueue(tf); + if (lp->last_item->is_empty()) + goto lend_para_done; + tf=lp->last_item->dequeue(); + } + outqueue.enqueue(tf); + lend_para_done: ; + } + + /* Finish off the list */ + t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END); + outqueue.enqueue(t); + t=new(tok)(T_LIST, l_type_name(lp), tok::TOK_END); + outqueue.enqueue(t); + nl=lp->next_list; + recycled=lp->last_item; // Recycle elements queued + t=new(tok)(T_PARAGRAPH, txt, tok::TOK_START); + recycled->enqueue(t); + free(lp); + lp=nl; + *lh=lp; + return NULL; + } + + t=new(tok)(T_PARAGRAPH, txt, tok::TOK_START); + lp->last_item->enqueue(t); + return NULL; + } + + /* lp==NULL if we get here */ + + if ((nl=list_type(txt))!=NULL) + { + nl->next_list=lp; + lp=nl; + t=new(tok)(T_LIST, l_type_name(nl), tok::TOK_START); + nl->last_item->enqueue(t); + goto list_reconsider; + } + + return txt; + +} + +const tok_seq::tok *tok_seq::read_token(void) +{ + const tok *tf; + const char *tp; + tok *t; + struct list_info *nl; + + while(outqueue.is_empty()) + { + if (recycled!=NULL) + { + if (recycled->is_empty()) + { + delete(recycled); + recycled=NULL; + continue; // outqueue still empty + } + tf=recycled->dequeue(); + } + else + tf=this->eqn_rd_token(); + if (tf==NULL) + { + if (!done_end) + { + tok *t; + t=new(tok)(T_DOC, "End of word2x output", tok::TOK_END); + output.enqueue(t); + done_end=1; + continue; + } + else + return NULL; + } + + if (tf->tokval==T_DOC && tf->end==tok::TOK_END) + { + /* End all lists */ + while (lp!=NULL) + { + tp=l_type_name(lp); + nl=lp->next_list; + + if (!(lp->last_item->is_empty())) + outqueue.transfer(lp->last_item); + delete(lp->last_item); + free(lp); + t=new(tok)(T_ITEM, (void *) NULL, tok::TOK_END); + outqueue.enqueue(t); + t=new(tok)(T_LIST, tp, tok::TOK_END); + outqueue.enqueue(t); + lp=nl; + } + outqueue.enqueue(tf); + } + else if (tf->tokval==T_PARAGRAPH && tf->end==tok::TOK_START) + { + tp=list_check(tf->data.d, &lp); + if (tp!=NULL) + { + t=new(tok)(T_PARAGRAPH, tp, tok::TOK_START); + outqueue.enqueue(t); + /* End paragraph will come from previous stage */ + } + delete(tf); + } + else + { + if (lp==NULL) + outqueue.enqueue(tf); + else + lp->last_item->enqueue(tf); + } + } + tf=outqueue.dequeue(); + return tf; +} + + +ostream &operator<<(ostream &os, const tok_seq::tok *d) +{ + os<<'('<<d->tokval<<','; + switch(d->dtype) + { + case 1: + if (d->data.d!=NULL && strlen(d->data.d)>10) + { + char foo[11]; + int i; + + for(i=0; i<7; i++) + foo[i]=d->data.d[i]; + for ( ; i<10; i++) + foo[i]='.'; + foo[10]='\0'; + os<<foo; + } + else + os<<d->data.d; + break; + case 0: + os<<d->data.table.rows<<'x'<<d->data.table.cols; + break; + } + os<<','<<((d->end==tok_seq::tok::TOK_START) ? "start" : "end")<<')'; + return os; +} + +tok_seq::tok &tok_seq::tok::operator=(const tok_seq::tok &d) +{ + tokval=d.tokval; + end=d.end; + dtype=d.dtype; + if (d.dtype==TEXT && d.data.d!=NULL) + { + data.d=strdup(d.data.d); + } + return (*this); +} |