/* * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts. * All rights reserved. */ /* * Copyright (c) 1994 * Open Software Foundation, Inc. * * Permission is hereby granted to use, copy, modify and freely distribute * the software in this file and its documentation for any purpose without * fee, provided that the above copyright notice appears in all copies and * that both the copyright notice and this permission notice appear in * supporting documentation. Further, provided that the name of Open * Software Foundation, Inc. ("OSF") not be used in advertising or * publicity pertaining to distribution of the software without prior * written permission from OSF. OSF makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. */ /* * Copyright (c) 1996 X Consortium * Copyright (c) 1995, 1996 Dalrymple Consulting * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the X Consortium and * Dalrymple Consulting shall not be used in advertising or otherwise to * promote the sale, use or other dealings in this Software without prior * written authorization. */ /* ________________________________________________________________________ * * Program to manipulate SGML instances. * * Originally coded for OSF DTD tables, now recoded (fld 3/27/95) * for CALS-type tables (fragment taken from the DocBook DTD). Then, * *really* upgraded to CALS tables by FLD on 5/28/96. * * This module is for handling table markup, printing TeX or tbl * (tbl) markup to the output stream. Also, table markup checking is * done here. Yes, this depends on the DTD, but it makes translation * specs much cleaner (and makes some things possible). * * Incomplete / not implemented / limitations / notes: * vertical alignment (valign attr) * vertical spanning * row separators are for the whole line, not per cell (the prog looks * at rowsep for the 1st cell and applies it to the whole row) * trusts that units in colwidths are acceptable to LaTeX and tbl * "s" is an acceptable shorthand for "span" in model attributes * * A note on use of OutputString(): Strings with backslashes (\) need lots * of backslashes. You have to escape them for the C compiler, and escape * them again for OutputString() itself. * ________________________________________________________________________ */ #ifndef lint static char *RCSid = "$Header: /usr/src/docbook-to-man/Instant/RCS/tables.c,v 1.11 1996/06/15 03:45:02 fld Exp $"; #endif #include #include #include #include #include #include #include #include #include "general.h" #include "translate.h" /* text width of page, in inches */ #define TEXTWIDTH 5.5 #define MAXCOLS 100 #define SPAN_NOT 0 #define SPAN_START 1 #define SPAN_CONT 2 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /*table parameters */ #define TBLMAXCOL 30 /* max number of columns in tbl table */ #define NAMELEN 40 /* max length of a name */ #define BOFTTHRESHOLD 35 /* text length over which to consider * generating a block of filled text */ /* handy declarations */ typedef enum { Left, Right, Center, Justify, Char, Span } tblalign; typedef enum { TGroup, THead, TFoot, TBody } tblsource; /* source of a spec */ /* table line format information structures */ struct tblcolspec { char name[NAMELEN]; /* colspec's name */ short num; /* column number */ tblsource source; /* where defined */ tblalign align; /* column's alignment */ char alignchar; /* character for alignment */ short aligncharoff; /* offset for alignment */ char colwidth[10]; /* width for column */ char colpwidth[10]; /* proportional widths for column */ bool colsep; /* separator to right of column? */ bool rowsep; /* separator to bottom of column? */ short moreRows; /* value for Morerows */ struct tblcolspec * next; /* next colspec */ }; struct tblspanspec { char name[NAMELEN]; /* spanspec's name */ tblsource source; /* where defined */ struct tblcolspec * start; /* start column */ struct tblcolspec * end; /* end column */ tblalign align; /* span's alignment */ char alignchar; /* character for alignment */ short aligncharoff; /* offset for alignment */ bool colsep; /* separator to right of column? */ bool rowsep; /* separator to bottom of column? */ struct tblspanspec * next; /* next spanspec */ }; struct tblformat { short count; /* count of rows matching this spec */ short cols; /* # of columns */ short rowNum; /* row number */ char colformat[TBLMAXCOL]; /* per-column formats */ char colwidth[TBLMAXCOL][10]; /* per-column widths */ char colpwidth[TBLMAXCOL][10]; /* per-column proportional widths */ char font[TBLMAXCOL][3]; /* column fonts (headers) */ bool colsep[TBLMAXCOL]; /* column separators */ bool rowsep[TBLMAXCOL]; /* row separators */ short moreRows[TBLMAXCOL]; /* moreRows indicator */ struct tblformat * next; /* for the next row */ }; /* table state info */ static short tblcols = 0; /* number of columns in the table */ static short tblrow = 0; /* the current row in the table */ static bool tblTGroupSeen = FALSE; /* seen a TGroup in this table yet? */ static char * tblFrame; /* table frame info */ static bool tblgcolsep; /* global colsep (in table) */ static bool tblgrowsep; /* global rowsep (in table) */ static int tblBOFTCount = 0; /* count of bofts that we've created * (per table) */ int BOFTTextThresh = BOFTTHRESHOLD; /* length of text before we * call it a BOFT */ static bool tblboft = FALSE; /* within a block of filled text? */ static bool tblinBOFT = FALSE; /* within a boft now? */ static struct tblformat * formP = 0; /* THead/TBody format lines */ static struct tblcolspec * tblColSpec = 0; /* colspec structure for table */ static struct tblspanspec * tblSpanSpec = 0; /* spanspec structure for table */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* these cover the attributes on the Table, TGroup, Colspec elements */ typedef struct { char *cols; char *align, **align_v; char *colwidth, **colwidth_v; char *colsep, **colsep_v; char *rowsep, **rowsep_v; char *frame; char *orient; int pgwide; int n_align, n_model, n_colwidth, n_colsep; int nc; } TableInfo; /* some flags, set when the table tag is processed, used later */ static int rowsep, siderules; static int frametop, framebot, frameall; static char basemodel[128]; /* model for table (in formatting language) */ static int spaninfo[MAXCOLS]; /* 100 columns, max */ static TableInfo TheTab; /* forward references */ void SetTabAtts(Element_t *, TableInfo *, int); void FreeTabAtts(TableInfo *); void ClearTable(TableInfo *); void CheckTable(Element_t *); void TblTStart(Element_t *, FILE *); void TblTEnd(Element_t *, FILE *); void TblTGroup(Element_t *, FILE *); void TblTGroupEnd(Element_t *, FILE *); void TblTFoot(Element_t *, FILE *); void TblBuildFormat(Element_t *, struct tblformat **, tblsource); struct tblformat * TblBuild1Format(Element_t *, bool, tblsource); char TblGetAlign(short, Element_t *, tblsource); char * TblGetWidth(short, Element_t *, bool, tblsource); char * TblGetFont(short, Element_t *, tblsource); bool TblGetColSep(short, Element_t *, tblsource); bool TblGetRowSep(short, Element_t *, tblsource); short TblGetMoreRows(short, Element_t *, tblsource); bool TblColAdv(short, Element_t *, struct tblformat *, tblsource); struct tblcolspec * TblEntryColSpec(short, Element_t *, tblsource); struct tblspanspec * TblEntrySpanSpec(short, Element_t *, tblsource); bool TblFormatMatch(struct tblformat *, struct tblformat *); void TblPrintFormat(FILE *, struct tblformat *); void TblTRowStart(Element_t *, FILE *); void TblTRowEnd(Element_t *, FILE *); void TblTCellStart(Element_t *, FILE *); int TblCountContent(Element_t *); void TblTCellEnd(Element_t *, FILE *); struct tblcolspec * TblDoColSpec(short, Element_t *, struct tblcolspec *, tblsource); struct tblspanspec * TblDoSpanSpec(Element_t *, struct tblspanspec *, tblsource); struct tblcolspec * TblFindColSpec(char *, tblsource); struct tblcolspec * TblFindColNum(short, tblsource); struct tblspanspec * TblFindSpanSpec(char *, tblsource); void TexTable(Element_t *, FILE *); void TexTableCellStart(Element_t *, FILE *); void TexTableCellEnd(Element_t *, FILE *); void TexTableRowStart(Element_t *, FILE *); void TexTableRowEnd(Element_t *, FILE *); void TexTableTop(Element_t *, FILE *); void TexTableBottom(Element_t *, FILE *); /* ______________________________________________________________________ */ /* Hard-coded stuff for CALS-style DTD tables. * Here are the TABLE attributes (for handy reference): * * Table/InformalTable: * Colsep NUMBER separate all columns in table? * Frame (Top|Bottom|Topbot|All|Sides|None) frame style * Orient (Port | Land) orientation * Pgwide NUMBER wide table? * Rowsep NUMBER separate all rows in the table? * Tabstyle NMTOKEN FOSI table style * * TGroup: * Align (Left|Right|Center|Justify|Char) alignment of cols * Char CDATA Alignment specifier * Charoff NUTOKEN "" "" * Cols NUMBER number of columns * Colsep NUMBER separate all columns in tgroup? * Rowsep NUMBER separate all rows in tgroup? * TGroupstyle NMTOKEN FOSI table group style * * Colspec: * Align (Left|Right|Center|Justify|Char) entry align * Char CDATA Alignment specifier * Charoff NUTOKEN "" "" * Colname NMTOKEN Column identifier * Colnum NUMBER number of column * Colsep NUMBER separate this col from next? * Colwidth CDATA width spec * Rowsep NUMBER serarate entry from following row? * * SpanSpec: * Align (Left|Right|Center|Justify|Char) entry align * Char CDATA Alignment specifier * Charoff NUTOKEN "" "" * Colsep NUMBER separate this col from next? * Nameend NMTOKEN name of rightmost col of a span * Namest NMTOKEN name of leftmost col of a span * Rowsep NUMBER serarate entry from following row? * Spanname NMTOKEN name of a horiz. span * * THead/TFoot/TBody: * VAlign (Top | Middle | Bottom) group placement * * Row: * Rowsep NUMBER separate this row from next? * VAlign (Top | Middle | Bottom) row placement * * Entry: * Align (Left|Right|Center|Justify|Char) entry align * Char CDATA Alignment specifier * Charoff NUTOKEN "" "" * Colname NMTOKEN Column identifier * Colsep NUMBER separate this col from next? * Morerows NUMBER number of addn'l rows in vert straddle * Nameend NMTOKEN name of rightmost col of a span * Namest NMTOKEN name of leftmost col of a span * Rotate NUMBER 90 degree rotation counterclockwise to table? * Rowsep NUMBER serarate entry from following row? * Spanname NMTOKEN name of a horiz. span * VAlign (Top | Middle | Bottom) text vert alignment * * ** OBSOLETE OSF DTD FORM (still used for TeX form): ** Usage in transpec: _calstable [tex|check|clear] ['aspect'] ** where 'aspect' is: ** rowstart stuff to do at start of a row (tests for spanning) ** rowend stuff to do at end of a row (eg, rules, etc.) ** cellstart stuff to do at start of a cell (eg, handle actual ** spanning instructions, etc.) ** cellend stuff to do at end of a cell (eg, cell separator) ** top stuff to do at top of the table ** (like whether or not it needs a starting horiz rule) ** bottom stuff to do at bottom of the table ** (like whether or not it needs an ending horiz rule) ** (nothing) the 'cols' param to LaTeX's \begin{tabular}[pos]{cols} ** or 'options' and 'formats' part in tbl * * * New tbl form: * Usage in transpec: _calstable [tbl] ['aspect'] * where 'aspect' is: * tablestart start a table and do style info * tableend end the table and clean up * tablegroup table TGroup (.T& if not 1st, line format info) * tablegroupend end a TGroup * tablefoot TFoot within a TGroup * rowstart start of a row * rowend end of a row * entrystart start of an entry (block of filled text, if * appropriate) * entryend end of a cell (eg, cell separator) */ /* Procedure to * Arguments: * Pointer to element under consideration. * FILE pointer to where to write output. * Vector of args to _osftable * Count of args to _osftable */ void CALStable( Element_t *e, FILE *fp, char **av, int ac ) { /* Check params and dispatch to appropriate routine */ if (!strcmp(av[1], "tbl")) { if (ac > 2) { if (!strcmp(av[2], "tablestart")) TblTStart(e, fp); else if (!strcmp(av[2], "tableend")) TblTEnd(e, fp); else if (!strcmp(av[2], "tablegroup")) TblTGroup(e, fp); else if (!strcmp(av[2], "tablegroupend")) TblTGroupEnd(e, fp); else if (!strcmp(av[2], "tablefoot")) TblTFoot(e, fp); else if (!strcmp(av[2], "rowstart")) TblTRowStart(e, fp); else if (!strcmp(av[2], "rowend")) TblTRowEnd(e, fp); else if (!strcmp(av[2], "entrystart")) TblTCellStart(e, fp); else if (!strcmp(av[2], "entryend")) TblTCellEnd(e, fp); else fprintf(stderr, "Unknown %s table instruction: %s\n", av[1], av[2]); } else { fprintf(stderr, "Incomplete %s table instruction\n"); } } else if (!strcmp(av[1], "tex")) { if (ac > 1 && !strcmp(av[1], "check")) CheckTable(e); else if (ac > 1 && !strcmp(av[1], "clear")) ClearTable(&TheTab); if (ac > 2) { if (!strcmp(av[2], "cellstart")) TexTableCellStart(e, fp); else if (!strcmp(av[2], "cellend")) TexTableCellEnd(e, fp); else if (!strcmp(av[2], "rowstart")) TexTableRowStart(e, fp); else if (!strcmp(av[2], "rowend")) TexTableRowEnd(e, fp); else if (!strcmp(av[2], "top")) TexTableTop(e, fp); else if (!strcmp(av[2], "bottom")) TexTableBottom(e, fp); else fprintf(stderr, "Unknown %s table instruction: %s\n", av[1], av[2]); } else TexTable(e, fp); } else fprintf(stderr, "Unknown table type: %s\n", av[1]); } /* ClearTable -- start a new table process * */ void ClearTable( TableInfo * t ) { memset(t, 0, sizeof(TableInfo)); } /* ______________________________________________________________________ */ /* Set values of the our internal table structure based on the table's * attributes. (This is called for tables, tgroups, colspecs, and rows, * since tables and rows share many of the same attributes.) * Arguments: * Pointer to element under consideration. * Pointer table info structure which will be filled in. * Flag saying whether or not to set global variables based on attrs. */ void SetTabAtts( Element_t *e, TableInfo *t, int set_globals ) { char *at; Element_t * ep; /* remember values of attributes */ if ((at = FindAttValByName(e, "ALIGN"))) t->align = at; if ((at = FindAttValByName(e, "COLWIDTH"))) t->colwidth = at; if ((at = FindAttValByName(e, "COLSEP"))) t->colsep = at; if ((at = FindAttValByName(e, "FRAME"))) t->frame = at; if ((at = FindAttValByName(e, "COLS"))) t->cols = at; /* Set some things for later when processing this table */ if (set_globals) { rowsep = 1; frametop = framebot = 1; /* default style */ /* For now we look at the first number of rowsep - it controls the * horiz rule for then entire row. (not easy to specify lines that * span only some columns in tex or tbl. */ if ((at = FindAttValByName(e, "ROWSEP"))) rowsep = atoi(at); } if (t->frame) { /* Top|Bottom|Topbot|All|Sides|None */ if (!strcmp(t->frame, "NONE") || !strcmp(t->frame, "SIDES")) frametop = framebot = 0; else if (!strcmp(t->frame, "TOP")) framebot = 0; else if (!strcmp(t->frame, "BOTTOM")) frametop = 0; } /* tbl and tex like lower case for units. convert. */ if (t->colwidth) { char *cp; for (cp=t->colwidth; *cp; cp++) if (isupper(*cp)) *cp = tolower(*cp); } /* Now, split (space-separated) strings into vectors. Hopefully, the * number of elements in each vector matches the number of columns. */ t->align_v = Split(t->align, &t->n_align, S_STRDUP|S_ALVEC); t->colwidth_v = Split(t->colwidth, &t->n_colwidth, S_STRDUP|S_ALVEC); t->colsep_v = Split(t->colsep, &t->n_colsep, S_STRDUP|S_ALVEC); /* Determin the _numeric_ number of columns, "nc". MUST be specified * in Cols attribute of TGroup element. */ if (t->cols) t->nc = atoi(t->cols); } /* ______________________________________________________________________ */ /* Free the storage of info use by the table info structure. (not the * structure itself, but the strings its elements point to) * Arguments: * Pointer table info structure to be freed. */ void FreeTabAtts( TableInfo *t ) { if (!t) return; if (t->align_v) free(*t->align_v); if (t->colwidth_v) free(*t->colwidth_v); if (t->colsep_v) free(*t->colsep_v); } /* ______________________________________________________________________ */ /* Check the attributes and children of the table pointed to by e. * Report problems and inconsistencies to stderr. * Arguments: * Pointer to element (table) under consideration. */ void CheckTable( Element_t *e ) { int pr_loc=0; /* flag to say if we printed location */ int i, r, c; Element_t *ep, *ep2; float wt; char *tpref = "Table Check"; /* prefix for err messages */ char *ncolchk = "Table Check: %s ('%s') has wrong number of tokens. Expecting %d.\n"; if (strcmp(e->gi, "TABLE") && strcmp(e->gi, "INFORMALTABLE") && strcmp(e->gi, "TGROUP") && strcmp(e->gi, "COLSPEC") && strcmp(e->gi, "ROW") ) { fprintf(stderr, "%s: Not pointing to a table element(%s)!\n", tpref, e->gi); return; } FreeTabAtts(&TheTab); /* free storage, if allocated earlier */ SetTabAtts(e, &TheTab, 1); /* look at attributes */ #if FALSE /* NCOLS attribute set? */ if (!TheTab.ncols) { pr_loc++; fprintf(stderr, "%s: NCOLS attribute missing. Inferred as %d.\n", tpref, TheTab.nc); } /* ALIGN attribute set? */ if (!TheTab.align) { pr_loc++; fprintf(stderr, "%s: ALIGN attribute missing.\n", tpref); } /* See if the number of cells in each row matches */ for (r=0; rnecont && (ep=e->econt[r]); r++) { /* each TGroup */ for (i=0; inecont && (ep2=ep->econt[i]); i++) { if ( strcmp(ep2->gi, "TBODY") ) /* only TBodys */ continue; for (c=0; cnecont; c++) { if (ep2->econt[c]->necont != TheTab.nc) { pr_loc++; fprintf(stderr, "%s: COLS (%d) differs from actual number of cells (%d) in row %d.\n", tpref, TheTab.nc, ep2->econt[c]->necont, c); } } } } #endif /* Check ALIGN */ if (TheTab.align) { if (TheTab.nc != TheTab.n_align) { /* number of tokens OK? */ pr_loc++; fprintf(stderr, ncolchk, "ALIGN", TheTab.align, TheTab.nc); } else { /* values OK? */ for (i=0; inecont; i++) { } if ((at = FindAttValByName(e, "MODEL"))) { /* Split into tokens, then look at each for the word "span" */ n = TheTab.nc; spans = Split(at, &n, S_STRDUP|S_ALVEC); /* Mark columns as start-of-span, in-span, or not spanned. Remember * in at list, "spaningo". (Span does not make sense in 1st column.) */ for (i=1,inspan=0; iecont[0], &TheTab, 1); /* attrs of TGroup */ /* Figure out the widths, based either on "colwidth". */ if (TheTab.colwidth && TheTab.nc == TheTab.n_colwidth) { widths = TheTab.colwidth_v; } siderules = 1; if (TheTab.frame) if (strcmp(TheTab.frame, "ALL") && strcmp(TheTab.frame, "SIDES")) siderules = 0; if (siderules) OutputString("|", fp, 1); for (i=0; imy_eorder] == SPAN_START) { for (i=e->my_eorder+1,n=1; ; i++) { if (spaninfo[i] == SPAN_CONT) n++; else break; } sprintf(buf, "\\\\multicolumn{%d}{%sc%s}", n, (siderules?"|":""), (siderules?"|":"")); OutputString(buf, fp, 1); } #ifdef New if ((at = FindAttValByName(e->parent, "ALIGN"))) { /* no span, but user wants to change the alignment */ h_v = Split(wbuf, 0, S_ALVEC|S_STRDUP); OutputString("\\\\multicolumn{1}{%sc%s}", n, fp, 1); } #endif if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("{", fp, 1); } /* * Arguments: * Pointer to element (cell) under consideration. * FILE pointer to where to write output. */ void TexTableCellEnd( Element_t *e, FILE *fp ) { if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("} ", fp, 1); /* do cell/col separators */ if (e->my_eorder < (TheTab.nc-1)) { if (spaninfo[e->my_eorder] == SPAN_NOT || spaninfo[e->my_eorder+1] != SPAN_CONT) OutputString("& ", fp, 1); } } /* Look at model for spanning. If set, remember it for when doing the cells. * Arguments: * Pointer to element (row) under consideration. * FILE pointer to where to write output. */ void TexTableRowStart( Element_t *e, FILE *fp ) { check_for_spans(e); } /* * Arguments: * Pointer to element (row) under consideration. * FILE pointer to where to write output. */ void TexTableRowEnd( Element_t *e, FILE *fp ) { char *at; /* check this row's attributes */ if ((at = FindAttValByName(e, "ROWSEP"))) { if (at[0] == '1') OutputString("\\\\\\\\[2mm] \\\\hline ", fp, 1); } else if (rowsep) OutputString("\\\\\\\\ ", fp, 1); else OutputString("\\\\\\\\ ", fp, 1); } /* * Arguments: * Pointer to element (table) under consideration. * FILE pointer to where to write output. */ void TexTableTop(Element_t *e, FILE *fp) { if (frametop) OutputString("\\\\hline", fp, 1); } void TexTableBottom(Element_t *e, FILE *fp) { if (framebot) OutputString("\\\\hline", fp, 1); } /* ______________________________________________________________________ */ /* ______________________________________________________________________ */ /* ______________________________________________________________________ */ /* ______________________________________________________________________ */ /* ______________________________________________________________________ */ /* ___________________________| |____________________________ */ /* ___________________________| TBL STUFF |____________________________ */ /* ___________________________| |____________________________ */ /* ___________________________|_____________|____________________________ */ /* ______________________________________________________________________ */ /* ______________________________________________________________________ */ /* ______________________________________________________________________ */ /* ______________________________________________________________________ */ /* TblTStart() -- start a table and do style information * * TO DO: * * do .TS * find global rowsep and colsep */ void TblTStart(Element_t * ep, FILE * fP) { register char * cp; register struct Element_t * ep2; OutputString("^.TS^", fP, 1); tblTGroupSeen = FALSE; tblinBOFT = FALSE; /* within a boft? */ tblBOFTCount = 0; /* count of Blocks of Filled Text that * we've created */ tblgcolsep = (cp = FindAttValByName(ep, "COLSEP")) && !strcmp(cp, "1"); tblgrowsep = (cp = FindAttValByName(ep, "ROWSEP")) && !strcmp(cp, "1"); } /* TblTEnd() -- end a table and do any cleanup * * TO DO: * * do .TE * * deallocate format line info */ void TblTEnd(Element_t * ep, FILE * fP) { register struct tblformat * ffp, * ffp2; if ( tblBOFTCount > 31 ) { fprintf(stderr, "# warning, line %d: created %d blocks of filled text in one table\n", ep->lineno, tblBOFTCount); fprintf(stderr, "#\t\t(31 is the limit in some systems)\n"); } OutputString("^.TE^", fP, 1); for ( ffp=formP; ffp; ffp=ffp2 ) { ffp2 = ffp->next; free(ffp); /* clear entire list */ } formP = 0; } /* TblTTGroup() -- do body work (row format info) * * TO DO: * * set number of columns * * if this is the first TGroup of this table, do style info: * a. alignment * b. defaults: tab * c. box vx allbox * * do format info: * a. generate tableformat structure * b. output it * * prepare structures for colspecs and spanspecs * */ void TblTGroup(Element_t * ep, FILE * fP) { register int i, j, k; register char * cp, * cp2; register Element_t * ep2, ep3; register struct tblcolspec * tcsp, * tcsp2; register struct tblspanspec * tssp, * tssp2; tblColSpec = 0; /* make sure they're clear */ tblSpanSpec = 0; /* set the number of columns */ tblcols = atoi(FindAttValByName(ep, "COLS")); /* do colspecs */ tblColSpec = tcsp = TblDoColSpec(0, ep, 0, TGroup); /* do TGroup first -- it becomes the default */ for ( i=0, k=1; i < ep->necont; i++ ) { if ( !strcmp(ep->econt[i]->gi, "COLSPEC") ) { tcsp2 = TblDoColSpec(k, ep->econt[i], tblColSpec, TGroup); tcsp->next = tcsp2; /* put into list */ tcsp = tcsp2; k = tcsp2->num + 1; /* next column number */ } if ( !strcmp(ep->econt[i]->gi, "THEAD") ) { ep2 = ep->econt[i]; for ( j=0, k=1; j < ep2->necont; j++ ) { if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) { tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, THead); tcsp->next = tcsp2; /* put into list */ tcsp = tcsp2; k = tcsp2->num + 1; /* next column number */ } } } if ( !strcmp(ep->econt[i]->gi, "TFOOT") ) { ep2 = ep->econt[i]; for ( j=0, k=1; j < ep2->necont; j++ ) { if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) { tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TFoot); tcsp->next = tcsp2; /* put into list */ tcsp = tcsp2; k = tcsp2->num + 1; /* next column number */ } } } if ( !strcmp(ep->econt[i]->gi, "TBODY") ) { ep2 = ep->econt[i]; for ( j=0, k=1; j < ep2->necont; j++ ) { if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) { tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TBody); tcsp->next = tcsp2; /* put into list */ tcsp = tcsp2; k = tcsp2->num + 1; /* next column number */ } } } } /* do spanspecs */ tblSpanSpec = tssp = TblDoSpanSpec(ep, 0, TGroup); /* do TGroup first -- it becomes the default */ for ( i=0; i < ep->necont; i++ ) { if ( !strcmp(ep->econt[i]->gi, "SPANSPEC") ) { tssp2 = TblDoSpanSpec(ep->econt[i], tblSpanSpec, TGroup); tssp->next = tssp2; /* put into list */ tssp = tssp2; } } /* if this is the first TGroup in this table, do style stuff */ if ( ! tblTGroupSeen ) { OutputString("tab(\007)", fP, 1); ep2 = ep->parent; if ( ! (tblFrame = FindAttValByName(ep2, "FRAME")) ) tblFrame = ""; if ( !strcmp(tblFrame, "ALL") ) { if ( tcsp->colsep && tcsp->rowsep ) OutputString(" allbox", fP, 1); else OutputString(" box", fP, 1); } if ( (cp = FindAttValByName(ep, "ALIGN")) && !strcmp(cp, "CENTER") ) { OutputString(" center", fP, 1); } OutputString(";\n", fP, 1); tblTGroupSeen = TRUE; } /* do format stuff -- step through all THead rows then all TBody * rows. Build a list of tblformats that describe all of them. * then output the resulting list. */ for ( i=0; i < ep->necont; i++ ) { if ( !strcmp(ep->econt[i]->gi, "THEAD") ) { TblBuildFormat(ep->econt[i], &formP, THead); /* add in those rows */ break; } } for ( i=0; i < ep->necont; i++ ) { if ( !strcmp(ep->econt[i]->gi, "TBODY") ) { TblBuildFormat(ep->econt[i], &formP, TBody); /* add in those rows */ break; } } TblPrintFormat(fP, formP); tblrow = 0; /* the current row within this format */ } /* TblTGroupEnd() -- end a TGroup * * TO DO: * * deallocate colspecs and spanspecs */ void TblTGroupEnd(Element_t * ep, FILE * fP) { register struct tblcolspec * tcsp, * tcsp2; register struct tblspanspec * tssp, * tssp2; for ( tcsp=tblColSpec; tcsp; tcsp=tcsp2 ) { tcsp2 = tcsp->next; free(tcsp); } for ( tssp=tblSpanSpec; tssp; tssp=tssp2 ) { tssp2 = tssp->next; free(tssp); } } /* TblTTFoot() -- do body foot work (row format info) * * TO DO: * * do format info: * a. generate tableformat structure * i. if it is only 1 line long and matches the * prevailing format, just output rows. * ii. else, output a .T& and the new format specs */ void TblTFoot(Element_t * ep, FILE * fP) { register struct tblformat * ffp, * ffp2; static struct tblformat * tfp, * tfp2; TblBuildFormat(ep, &tfp, TFoot); /* gen format for the foot */ for ( tfp2=formP; tfp2 && tfp2->next; tfp2=tfp2->next ) ; if ( tfp->next || !TblFormatMatch(tfp, tfp2) ) { for ( ffp=formP; ffp; ffp=ffp2 ) { ffp2 = ffp->next; free(ffp); /* clear entire list */ } formP = tfp; /* this becomes the prevailing format */ OutputString("^.T&^", fP, 1); TblPrintFormat(fP, formP); } tblrow = 0; /* the current row within this format */ } /* TblBuildFormat() -- build a format structure out of a set of * rows and columns * */ void TblBuildFormat(Element_t * ep, /* parent of rows.. */ struct tblformat ** fp, /* pointer to head of struct we're * building */ tblsource source) /* type of record */ { register int i; register struct tblformat * lfp; /* "current" format */ register struct tblformat * nfp; /* the next format */ for ( lfp= *fp; lfp && lfp->next; lfp=lfp->next ) ; /* find end of format list */ for ( i=0; i < ep->necont; i++ ) if ( !strcmp(ep->econt[i]->gi, "ROW") ) break; /* find where rows start */ for ( ; i < ep->necont; i++ ) { nfp = TblBuild1Format(ep->econt[i], FALSE, source); /* do one row */ if ( !lfp ) lfp = *fp = nfp; /* first one */ else if ( TblFormatMatch(lfp, nfp) ) lfp->count++; /* matches */ else { lfp->count = 1; /* only 1 so far */ lfp->next = nfp; /* new one */ lfp = nfp; } } } /* TblBuild1Format() -- build one row's worth of format information * */ struct tblformat * TblBuild1Format(Element_t * rp, /* the row to deal with */ bool addinRowsep, /* insert rowsep into model? */ tblsource source) /* type type of row */ { register int i; register bool allProp; float totalProp; register struct tblformat * tfp; register Element_t * ep; /* entry pointer */ Calloc(1, tfp, struct tblformat); tfp->cols = tblcols; ep = (rp->necont) ? rp->econt[0] : 0; /* first entry */ allProp = TRUE; totalProp = 0; for ( i=1; i <= tblcols; i++ ) { tfp->colformat[i] = TblGetAlign(i, ep, source); strcpy(tfp->colwidth[i], TblGetWidth(i, ep, TRUE, source)); strcpy(tfp->colpwidth[i], TblGetWidth(i, ep, FALSE, source)); if ( allProp ) { allProp = tfp->colpwidth[i][0]; totalProp += atof(tfp->colpwidth[i]); } strcpy(tfp->font[i], TblGetFont(i, ep, source)); tfp->colsep[i] = tblgcolsep || TblGetColSep(i, ep, source); if ( addinRowsep ) tfp->rowsep[i] = tblgrowsep || TblGetRowSep(i, ep, source); tfp->moreRows[i] = TblGetMoreRows(i, ep, source); if ( (i < rp->necont) && TblColAdv(i, ep, tfp, source) ) { ep = rp->econt[i]; } } /* turn proportional widths into real widths */ if ( allProp ) { for ( i=1; i <= tblcols; i++ ) { sprintf(tfp->colwidth[i], "%fi", (atof(tfp->colpwidth[i]) / totalProp) * TEXTWIDTH); } } return tfp; } /* TblGetAlign() -- get alignment spec for a entry * */ char TblGetAlign(short col, /* column number */ Element_t * entry, /* the entry */ tblsource source) /* context */ { register struct tblcolspec * tcsp; register struct tblspanspec * tssp; register tblalign talign; if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) { talign = tssp->align; free(tssp); } else if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) { talign = tcsp->align; free(tcsp); } else { return 'l'; } switch ( talign ) { case Left: return 'l'; case Right: return 'r'; case Center: return 'c'; case Justify: return 'l'; case Char: return 'd'; case Span: return 's'; } } /* TblGetWidth() -- get width spec, if any, for a entry * */ char * TblGetWidth(short col, /* column number */ Element_t * entry, /* the entry */ bool literal, /* literal (or proportional) */ tblsource source) /* context */ { register struct tblcolspec * tcsp; register struct tblspanspec * tssp; static char colWidth[10]; colWidth[0] = 0; if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) && tcsp->colwidth[0] ) { if ( !strstr(tcsp->colwidth, "*") ) { if ( literal ) strcpy(colWidth, tcsp->colwidth); } else { if ( ! literal ) strcpy(colWidth, tcsp->colwidth); } free(tcsp); } return colWidth; } /* TblGetFont() -- get font spec, if any, for a entry * */ char * TblGetFont(short col, /* column number */ Element_t * entry, /* the entry */ tblsource source) /* context */ { register struct tblcolspec * tcsp; register struct tblspanspec * tssp; return ""; } /* TblGetColSep() -- get column separater spec, if any, for a entry * */ bool TblGetColSep(short col, /* column number */ Element_t * entry, /* the entry */ tblsource source) /* context */ { register struct tblcolspec * tcsp; register struct tblspanspec * tssp; register bool colsep; if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) { colsep = tssp->colsep; free(tssp); } else if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) { colsep = tcsp->colsep; free(tcsp); } else colsep = FALSE; return colsep; } /* TblGetRowSep() -- get row separater spec, if any, for a entry * */ bool TblGetRowSep(short col, /* column number */ Element_t * entry, /* the entry */ tblsource source) /* context */ { register struct tblcolspec * tcsp; register struct tblspanspec * tssp; register bool rowsep; if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) { rowsep = tssp->rowsep; free(tssp); } else if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) { rowsep = tcsp->rowsep; free(tcsp); } else { rowsep = FALSE; } return rowsep; } /* TblGetmoreRows() -- get moreRows value * */ bool TblGetMoreRows(short col, /* column number */ Element_t * entry, /* the entry */ tblsource source) /* context */ { register char * cp; if ( cp = FindAttValByName(entry, "MOREROWS") ) return atoi(cp); else return 0; } /* TblColAdv() -- advance pointer to next entry, if appropriate * */ bool TblColAdv(short col, /* the current column */ Element_t *ep, /* pointer to entry */ struct tblformat * tfp, /* pointer to prevailing format */ tblsource source) /* context */ { register bool bump; register struct tblspanspec * tssp; bump = TRUE; if ( tssp = TblEntrySpanSpec(col, ep, source) ) { bump = tssp->align != Span; free(tssp); } return bump; } /* TblEntryColSpec() -- get a completely localized colspec for an entry * */ struct tblcolspec * TblEntryColSpec(short num, /* column number */ Element_t * ep, /* entry */ tblsource source) /* context */ { register int i; register bool throwAway; register char * cp; register struct tblcolspec * tcsp, * tcsp2; tcsp = tcsp2 = 0; throwAway = FALSE; if ( (cp = FindAttValByName(ep, "COLNAME")) ) { if ( ! (tcsp = TblFindColSpec(cp, source)) ) { fprintf(stderr, "? can't find column name '%s'\n", cp); } } if ( tcsp2 = TblFindColNum(num, source) ) { tcsp = TblDoColSpec(num, ep, tcsp2, source); throwAway = TRUE; } tcsp2 = TblDoColSpec(num, ep, tcsp, source); if ( throwAway ) free(tcsp); return tcsp2; } /* TblEntrySpanSpec() -- get a completely localized spanspec for an entry * */ struct tblspanspec * TblEntrySpanSpec(short num, /* column number */ Element_t * ep, /* entry */ tblsource source) /* context */ { register char * cp, * cp2; register struct tblspanspec * tssp, * tssp2; tssp2 = 0; if ( !(cp = FindAttValByName(ep, "SPANNAME")) || !(tssp2 = TblFindSpanSpec(cp, source)) ) { if ( !FindAttValByName(ep, "NAMEST") ) return 0; } tssp = TblDoSpanSpec(ep, tssp2, source); if ( tssp->start && tssp->end && (tssp->start->num < num) && (tssp->end->num >= num) ) { tssp->align = Span; } return tssp; } /* TblFormatMatch() -- compare two format rows for consistency * */ bool TblFormatMatch(struct tblformat * tf1, /* one row */ struct tblformat * tf2) /* the other */ { register int i; if ( tf1->cols != tf2->cols ) { return FALSE; } for ( i=0; i < tf1->cols; i++ ) { if ( tf1->colformat[i] != tf2->colformat[i] ) { return FALSE; } if ( strcmp(tf1->colwidth[i], tf2->colwidth[i]) ) { return FALSE; } if ( strcmp(tf1->font[i], tf2->font[i]) ) { return FALSE; } if ( tf1->colsep[i] != tf2->colsep[i] ) { return FALSE; } if ( tf1->rowsep[i] != tf2->rowsep[i] ) { return FALSE; } if ( tf1->moreRows[i] || tf2->moreRows[i] ) { return FALSE; } } return TRUE; } /* TblPrintFormat() -- print a tbl format structure * */ void TblPrintFormat(FILE * fP, /* where to print */ struct tblformat * tfp) /* the structure */ { register int i; register struct tblformat * tfp2, * tfp3; static char buf[3] = "\000\000"; for ( tfp2=tfp, tfp3=0; tfp2; tfp2=tfp2->next ) { for ( i=1; i <= tfp->cols; i++ ) { if ( i > 1 ) OutputString(" ", fP, 1); if ( tfp3 && tfp3->moreRows[i] ) OutputString("\\^", fP, 1); else { buf[0] = tfp2->colformat[i]; OutputString(buf, fP, 1); } if ( tfp2->colwidth[i][0] ) { OutputString("w(", fP, 1); OutputString(tfp2->colwidth[i], fP, 1); OutputString(")", fP, 1); } if ( tfp2->font[i][0] ) OutputString(tfp2->font[i], fP, 1); if ( tfp2->colsep[i] ) OutputString("|", fP, 1); } if ( ! tfp2->next ) OutputString(".", fP, 1); OutputString("^", fP, 1); tfp3 = tfp2; } } /* TblTRowStart() -- start a row (not much to do) * * TO DO: * * nothing.. * */ void TblTRowStart(Element_t * ep, FILE * fP) { /* nothing to do */ tblrow++; /* except note that we're within a new row */ } /* TblTRowEnd() -- end a row * * TO DO: * * output a row end character (newline) * if the current row had a rowsep, then output a "fake" row * with underlines in the proper place(s). */ void TblTRowEnd(Element_t * ep, FILE * fP) { register int i, k; register tblsource source; register bool startedRow, didSep; register struct tblformat * rfp; OutputString("^", fP, 1); /* get the format for this row */ if ( !strcmp(ep->parent->gi, "TFoot") ) source = TFoot; else if ( !strcmp(ep->parent->gi, "THead") ) source = THead; else source = TBody; rfp = TblBuild1Format(ep, TRUE, source); startedRow = FALSE; didSep = FALSE; for ( i=1; i <= formP->cols; i++ ) { if ( rfp->rowsep[i] || (didSep && (rfp->colformat[i] == 's')) ) { if ( ! startedRow ) { OutputString("^", fP, 1); for ( k=1; k < i; k++ ) OutputString("\007", fP, 1); startedRow = TRUE; } OutputString("_\007", fP, 1); didSep = TRUE; } else { if ( startedRow ) OutputString("\007", fP, 1); } didSep = FALSE; } free(rfp); /* clear that row.. */ if ( startedRow ) OutputString("^", fP, 1); } /* TblTEntryStart() -- start an entry (block of filled text if * appropriate) * * TO DO: * * if text length > BOFTTextThresh or there is PI, * then output "T{\n", else do nothing * */ void TblTCellStart(Element_t * ep, FILE * fP) { register int i; register Element_t * ep2; register bool sawPI; for ( i=0, sawPI=FALSE; (i < ep->ncont) && !sawPI; i++ ) if ( ep->cont[i].type == '?' ) sawPI = TRUE; if ( sawPI || (TblCountContent(ep) > BOFTTextThresh) ) { tblBOFTCount++; OutputString("T{^", fP, 1); tblinBOFT = TRUE; /* within a boft now */ } } /* TblCountContent() -- count all content below the given element * * */ int TblCountContent(Element_t * ep) /* the element to look under */ { register int i, count; count = 0; for ( i=0; i < ep->ncont; i++ ) { if ( ep->cont[i].type == '-' ) { count += strlen(ep->cont[i].ch.data); } else if ( ep->cont[i].type == '(' ) { count += TblCountContent(ep->cont[i].ch.elem); } } return count; } /* TblTEntryEnd() -- end an entry * * TO DO: * * if within BOFT, output "T}" * if not last entry, output tab character * */ void TblTCellEnd(Element_t * ep, FILE * fP) { register Element_t * ep2; if ( tblinBOFT ) { OutputString("^T}", fP, 1); tblinBOFT = FALSE; /* back out again */ } for ( ep2=ep->next; ep2; ep2=ep2->next ) { if ( !strcmp(ep2->gi, "ENTRY") || !strcmp(ep2->gi, "ENTRYTBL") ) { OutputString("\007", fP, 1); break; } if ( !strcmp(ep2->gi, "ROW") ) break; } } /* TblDoColSpec() -- process one element to create a new colspec * * */ struct tblcolspec * TblDoColSpec(short number, /* this column number */ Element_t * ep, /* element containing colspec stuff */ struct tblcolspec * pcsp, /* prevailing colspec (with defaults) */ tblsource source) /* precedence level of the resulting spec */ { register char * cp; register struct tblcolspec * tcsp; Calloc(1, tcsp, struct tblcolspec); if ( cp = FindAttValByName(ep, "COLNAME") ) strcpy(tcsp->name, cp); tcsp->num = number; tcsp->source = source; if ( cp = FindAttValByName(ep, "ALIGN") ) { if ( !strcmp(cp, "LEFT") ) tcsp->align = Left; else if ( !strcmp(cp, "RIGHT") ) tcsp->align = Right; else if ( !strcmp(cp, "CENTER") ) tcsp->align = Center; else if ( !strcmp(cp, "JUSTIFY") ) tcsp->align = Justify; else if ( !strcmp(cp, "CHAR") ) tcsp->align = Char; } else tcsp->align = ( pcsp ) ? pcsp->align : Left; if ( cp = FindAttValByName(ep, "CHAR") ) tcsp->alignchar = cp[0]; else tcsp->alignchar = ( pcsp ) ? pcsp->alignchar : 0; if ( cp = FindAttValByName(ep, "CHAROFF") ) tcsp->aligncharoff = atoi(cp); else tcsp->aligncharoff = ( pcsp ) ? pcsp->aligncharoff : 0; if ( cp = FindAttValByName(ep, "COLWIDTH") ) strcpy(tcsp->colwidth, cp); else strcpy(tcsp->colwidth, ( pcsp ) ? pcsp->colwidth : ""); if ( cp = FindAttValByName(ep, "COLSEP") ) tcsp->colsep = !strcmp(cp, "1"); else tcsp->colsep = ( pcsp ) ? pcsp->colsep : FALSE; if ( cp = FindAttValByName(ep, "ROWSEP") ) tcsp->rowsep = !strcmp(cp, "1"); else tcsp->rowsep = ( pcsp ) ? pcsp->rowsep : FALSE; return tcsp; } /* TblDoSpanSpec() -- process one element to create a new spanspec * * Note that there's a hack inside here... NameSt and NameEnd are * supposed to point at colnames, but if no colname is found, this * code will look for a colnum by the same value. */ struct tblspanspec * TblDoSpanSpec(Element_t * ep, /* element containing spanspec stuff */ struct tblspanspec * pssp, /* prevailing spanspec (with defaults) */ tblsource source) /* precedence level of the resulting spec */ { register char * cp; register struct tblspanspec * tssp; register struct tblcolspec * tcsp; Calloc(1, tssp, struct tblspanspec); if ( cp = FindAttValByName(ep, "SPANNAME") ) strcpy(tssp->name, cp); tssp->source = source; if ( cp = FindAttValByName(ep, "NAMEST") ) { if ( (tcsp = TblFindColSpec(cp, source)) || (tcsp = TblFindColNum(atoi(cp), source)) ) { tssp->start = tcsp; } else { fprintf(stderr, "? spanspec namest points to unknown column '%s'\n", cp); tssp->start = 0; } } else { if ( pssp && pssp->start ) { tssp->start = pssp->start; } } if ( cp = FindAttValByName(ep, "NAMEEND") ) { if ( (tcsp = TblFindColSpec(cp, source)) || (tcsp = TblFindColNum(atoi(cp), source)) ) { tssp->end = tcsp; } else { fprintf(stderr, "? spanspec nameend points to unknown column '%s'\n", cp); tssp->end = 0; } } else { if ( pssp && pssp->end ) { tssp->end = pssp->end; } } if ( cp = FindAttValByName(ep, "ALIGN") ) { if ( !strcmp(cp, "LEFT") ) tssp->align = Left; else if ( !strcmp(cp, "RIGHT") ) tssp->align = Right; else if ( !strcmp(cp, "CENTER") ) tssp->align = Center; else if ( !strcmp(cp, "JUSTIFY") ) tssp->align = Justify; else if ( !strcmp(cp, "CHAR") ) tssp->align = Char; } else { if ( pssp ) tssp->align = pssp->align; } if ( cp = FindAttValByName(ep, "CHAR") ) tssp->alignchar = cp[0]; else { if ( pssp ) tssp->alignchar = pssp->alignchar; } if ( cp = FindAttValByName(ep, "CHAROFF") ) tssp->aligncharoff = atoi(cp); else { if ( pssp ) tssp->alignchar = pssp->alignchar; } if ( cp = FindAttValByName(ep, "COLSEP") ) tssp->colsep = !strcmp(cp, "1"); else { if ( pssp ) tssp->colsep = pssp->colsep; } if ( cp = FindAttValByName(ep, "ROWSEP") ) tssp->rowsep = !strcmp(cp, "1"); else { if ( pssp ) tssp->rowsep = pssp->rowsep; } return tssp; } /* TblFindColSpec() -- find a table colspec by name (colname) * */ struct tblcolspec * TblFindColSpec(char * name, /* the name we're looking for */ tblsource source) /* the context in which to find it */ { register struct tblcolspec * tcsp; /* first, try to find the one in the right "source" */ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) { if ( (tcsp->source == source) && !strcmp(tcsp->name, name) ) return tcsp; } /* else, try to find one from a TGroup.. */ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) { if ( (tcsp->source == TGroup) && !strcmp(tcsp->name, name) ) return tcsp; } /* else not found.. */ return 0; } /* TblFindColNum() -- find a table colspec by number * */ struct tblcolspec * TblFindColNum(short number, /* the number we're looking for */ tblsource source) /* the context in which to find it */ { register struct tblcolspec * tcsp; /* first, try to find the one in the right "source" */ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) { if ( (tcsp->num == number) && ((tcsp->source == source) || ((source == THead) && (tcsp->source == TGroup))) ) return tcsp; } /* else, try to find one from a TGroup.. */ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) { if ( (tcsp->source == TGroup) && (tcsp->num == number) ) return tcsp; } /* else not found.. */ return 0; } /* TblFindSpanSpec() -- find a table spanspec by name (spanname) * */ struct tblspanspec * TblFindSpanSpec(char * name, /* the name we're looking for */ tblsource source) /* the context in which to find it */ { register struct tblspanspec * tssp; /* first, try to find the one in the right "source" */ for ( tssp=tblSpanSpec; tssp; tssp=tssp->next ) { if ( !strcmp(tssp->name, name) && ((tssp->source == source) || ((source == THead) && (tssp->source == TGroup))) ) return tssp; } /* else not found.. */ return 0; }