1 /*
2  *  Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
3  *  All rights reserved.
4  */
5 /*
6  * Copyright (c) 1994
7  * Open Software Foundation, Inc.
8  *
9  * Permission is hereby granted to use, copy, modify and freely distribute
10  * the software in this file and its documentation for any purpose without
11  * fee, provided that the above copyright notice appears in all copies and
12  * that both the copyright notice and this permission notice appear in
13  * supporting documentation.  Further, provided that the name of Open
14  * Software Foundation, Inc. ("OSF") not be used in advertising or
15  * publicity pertaining to distribution of the software without prior
16  * written permission from OSF.  OSF makes no representations about the
17  * suitability of this software for any purpose.  It is provided "as is"
18  * without express or implied warranty.
19  */
20 /*
21  * Copyright (c) 1996 X Consortium
22  * Copyright (c) 1995, 1996 Dalrymple Consulting
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy
25  * of this software and associated documentation files (the "Software"), to deal
26  * in the Software without restriction, including without limitation the rights
27  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28  * copies of the Software, and to permit persons to whom the Software is
29  * furnished to do so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in
32  * all copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
37  * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
38  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
39  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
40  * OTHER DEALINGS IN THE SOFTWARE.
41  *
42  * Except as contained in this notice, the names of the X Consortium and
43  * Dalrymple Consulting shall not be used in advertising or otherwise to
44  * promote the sale, use or other dealings in this Software without prior
45  * written authorization.
46  */
47 /* ________________________________________________________________________
48  *
49  *  Program to manipulate SGML instances.
50  *
51  * Originally coded for OSF DTD tables, now recoded (fld 3/27/95)
52  * for CALS-type tables (fragment taken from the DocBook DTD).  Then,
53  * *really* upgraded to CALS tables by FLD on 5/28/96.
54  *
55  *  This module is for handling table markup, printing TeX or tbl
56  *  (tbl) markup to the output stream.  Also, table markup checking is
57  *  done here.  Yes, this depends on the DTD, but it makes translation
58  *  specs much cleaner (and makes some things possible).
59  *
60  *  Incomplete / not implemented / limitations / notes:
61  *	vertical alignment (valign attr)
62  *	vertical spanning
63  *	row separators are for the whole line, not per cell (the prog looks
64  *		at rowsep for the 1st cell and applies it to the whole row)
65  *	trusts that units in colwidths are acceptable to LaTeX and tbl
66  *	"s" is an acceptable shorthand for "span" in model attributes
67  *
68  *  A note on use of OutputString():  Strings with backslashes (\) need lots
69  *  of backslashes.  You have to escape them for the C compiler, and escape
70  *  them again for OutputString() itself.
71  * ________________________________________________________________________
72  */
73 
74 #ifndef lint
75 static char *RCSid =
76   "$Header: /usr/src/docbook-to-man/Instant/RCS/tables.c,v 1.11 1996/06/15 03:45:02 fld Exp $";
77 #endif
78 
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <ctype.h>
82 #include <string.h>
83 #include <memory.h>
84 #include <sys/types.h>
85 #include <errno.h>
86 
87 #include <tptregexp.h>
88 #include "general.h"
89 #include "translate.h"
90 
91 /* text width of page, in inches */
92 #define TEXTWIDTH	5.5
93 #define MAXCOLS		100
94 #define SPAN_NOT	0
95 #define SPAN_START	1
96 #define SPAN_CONT	2
97 
98 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
99 /*table parameters */
100 
101 #define TBLMAXCOL	30	/* max number of columns in tbl table */
102 #define NAMELEN		40	/* max length of a name */
103 #define BOFTTHRESHOLD	35	/* text length over which to consider
104 				 * generating a block of filled text */
105 
106 
107 /* handy declarations */
108 
109 typedef enum { Left, Right, Center, Justify, Char, Span } tblalign;
110 
111 typedef enum { TGroup, THead, TFoot, TBody } tblsource;	/* source of a spec */
112 
113 
114 /* table line format information structures */
115 
116 struct tblcolspec	{
117 
118 	char		name[NAMELEN];	/* colspec's name */
119 	short		num;		/* column number */
120 	tblsource	source;		/* where defined */
121 
122 	tblalign	align;		/* column's alignment */
123 	char		alignchar;	/* character for alignment */
124 	short		aligncharoff;	/* offset for alignment */
125 	char		colwidth[10];	/* width for column */
126 	char		colpwidth[10];	/* proportional widths for column */
127 	bool		colsep;		/* separator to right of column? */
128 	bool		rowsep;		/* separator to bottom of column? */
129 	short		moreRows;	/* value for Morerows */
130 
131 	struct tblcolspec * next;	/* next colspec */
132 };
133 
134 struct tblspanspec	{
135 
136 	char		name[NAMELEN];	/* spanspec's name */
137 	tblsource	source;		/* where defined */
138 
139 	struct tblcolspec * start;	/* start column */
140 	struct tblcolspec * end;	/* end column */
141 	tblalign	align;		/* span's alignment */
142 	char		alignchar;	/* character for alignment */
143 	short		aligncharoff;	/* offset for alignment */
144 	bool		colsep;		/* separator to right of column? */
145 	bool		rowsep;		/* separator to bottom of column? */
146 
147 	struct tblspanspec * next;	/* next spanspec */
148 };
149 
150 struct tblformat	{
151 	short	count;			/* count of rows matching this spec */
152 
153 	short	cols;			/* # of columns */
154 	short	rowNum;			/* row number */
155 	char	colformat[TBLMAXCOL];	/* per-column formats */
156 	char	colwidth[TBLMAXCOL][10]; /* per-column widths */
157 	char	colpwidth[TBLMAXCOL][10]; /* per-column proportional widths */
158 	char	font[TBLMAXCOL][3];	/* column fonts (headers) */
159 	bool	colsep[TBLMAXCOL];	/* column separators */
160 	bool	rowsep[TBLMAXCOL];	/* row separators */
161 	short	moreRows[TBLMAXCOL];	/* moreRows indicator */
162 
163 	struct tblformat * next;	/* for the next row */
164 };
165 
166 
167 /* table state info */
168 
169 static short	tblcols = 0;		/* number of columns in the table */
170 static short	tblrow = 0;		/* the current row in the table */
171 
172 static bool tblTGroupSeen = FALSE;	/* seen a TGroup in this table yet? */
173 
174 static char *	tblFrame;		/* table frame info */
175 static bool	tblgcolsep;		/* global colsep (in table) */
176 static bool	tblgrowsep;		/* global rowsep (in table) */
177 
178 static int	tblBOFTCount = 0;	/* count of bofts that we've created
179 					 * (per table) */
180 int	BOFTTextThresh = BOFTTHRESHOLD;
181 					/* length of text before we
182 					 * call it a BOFT */
183 static bool	tblboft = FALSE;	/* within a block of filled text? */
184 static bool	tblinBOFT = FALSE;	/* within a boft now? */
185 
186 static struct tblformat * formP = 0;	/* THead/TBody format lines */
187 
188 static struct tblcolspec * tblColSpec = 0;   /* colspec structure for table */
189 static struct tblspanspec * tblSpanSpec = 0; /* spanspec structure for table */
190 
191 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
192 
193 /* these cover the attributes on the Table, TGroup, Colspec elements */
194 typedef struct {
195     char	*cols;
196     char	*align,     **align_v;
197     char	*colwidth,  **colwidth_v;
198     char	*colsep,    **colsep_v;
199     char	*rowsep,    **rowsep_v;
200     char	*frame;
201     char	*orient;
202     int		pgwide;
203     int		n_align, n_model, n_colwidth, n_colsep;
204     int		nc;
205 } TableInfo;
206 
207 
208 /* some flags, set when the table tag is processed, used later */
209 static int	rowsep, siderules;
210 static int	frametop, framebot, frameall;
211 static char	basemodel[128];	/* model for table (in formatting language) */
212 static int	spaninfo[MAXCOLS];	/* 100 columns, max */
213 static TableInfo	TheTab;
214 
215 /* forward references */
216 void	SetTabAtts(Element_t *, TableInfo *, int);
217 void	FreeTabAtts(TableInfo	*);
218 void	ClearTable(TableInfo *);
219 void	CheckTable(Element_t *);
220 void	TblTStart(Element_t *, FILE *);
221 void	TblTEnd(Element_t *, FILE *);
222 void	TblTGroup(Element_t *, FILE *);
223 void	TblTGroupEnd(Element_t *, FILE *);
224 void	TblTFoot(Element_t *, FILE *);
225 void	TblBuildFormat(Element_t *, struct tblformat **, tblsource);
226 struct tblformat * TblBuild1Format(Element_t *, bool, tblsource);
227 char	TblGetAlign(short, Element_t *, tblsource);
228 char *	TblGetWidth(short, Element_t *, bool, tblsource);
229 char *	TblGetFont(short, Element_t *, tblsource);
230 bool	TblGetColSep(short, Element_t *, tblsource);
231 bool	TblGetRowSep(short, Element_t *, tblsource);
232 short	TblGetMoreRows(short, Element_t *, tblsource);
233 bool	TblColAdv(short, Element_t *, struct tblformat *, tblsource);
234 struct tblcolspec * TblEntryColSpec(short, Element_t *, tblsource);
235 struct tblspanspec * TblEntrySpanSpec(short, Element_t *, tblsource);
236 bool	TblFormatMatch(struct tblformat *, struct tblformat *);
237 void	TblPrintFormat(FILE *, struct tblformat *);
238 void	TblTRowStart(Element_t *, FILE *);
239 void	TblTRowEnd(Element_t *, FILE *);
240 void	TblTCellStart(Element_t *, FILE *);
241 int	TblCountContent(Element_t *);
242 void	TblTCellEnd(Element_t *, FILE *);
243 struct tblcolspec * TblDoColSpec(short, Element_t *, struct tblcolspec *, tblsource);
244 struct tblspanspec * TblDoSpanSpec(Element_t *, struct tblspanspec *, tblsource);
245 struct tblcolspec * TblFindColSpec(char *, tblsource);
246 struct tblcolspec * TblFindColNum(short, tblsource);
247 struct tblspanspec * TblFindSpanSpec(char *, tblsource);
248 void	TexTable(Element_t *, FILE *);
249 void	TexTableCellStart(Element_t *, FILE *);
250 void	TexTableCellEnd(Element_t *, FILE *);
251 void	TexTableRowStart(Element_t *, FILE *);
252 void	TexTableRowEnd(Element_t *, FILE *);
253 void	TexTableTop(Element_t *, FILE *);
254 void	TexTableBottom(Element_t *, FILE *);
255 
256 /* ______________________________________________________________________ */
257 /*  Hard-coded stuff for CALS-style DTD tables.
258  *  Here are the TABLE attributes (for handy reference):
259  *
260  *  Table/InformalTable:
261  *	Colsep	   NUMBER	separate all columns in table?
262  *	Frame	   (Top|Bottom|Topbot|All|Sides|None)	frame style
263  *	Orient	   (Port | Land)	orientation
264  *	Pgwide	   NUMBER	wide table?
265  *	Rowsep	   NUMBER	separate all rows in the table?
266  *	Tabstyle   NMTOKEN	FOSI table style
267  *
268  *  TGroup:
269  *	Align	   (Left|Right|Center|Justify|Char)	alignment of cols
270  *	Char	   CDATA	Alignment specifier
271  *	Charoff	   NUTOKEN	    ""       ""
272  *	Cols	   NUMBER	number of columns
273  *	Colsep	   NUMBER	separate all columns in tgroup?
274  *	Rowsep	   NUMBER	separate all rows in tgroup?
275  *	TGroupstyle NMTOKEN	FOSI table group style
276  *
277  *  Colspec:
278  *	Align      (Left|Right|Center|Justify|Char)	entry align
279  *	Char       CDATA	Alignment specifier
280  *	Charoff    NUTOKEN	    ""       ""
281  *	Colname    NMTOKEN	Column identifier
282  *	Colnum	   NUMBER	number of column
283  *	Colsep     NUMBER	separate this col from next?
284  *	Colwidth   CDATA	width spec
285  *	Rowsep     NUMBER	serarate entry from following row?
286  *
287  *  SpanSpec:
288  *	Align      (Left|Right|Center|Justify|Char)	entry align
289  *	Char       CDATA	Alignment specifier
290  *	Charoff    NUTOKEN	    ""       ""
291  *	Colsep     NUMBER	separate this col from next?
292  *	Nameend    NMTOKEN	name of rightmost col of a span
293  *	Namest     NMTOKEN	name of leftmost col of a span
294  *	Rowsep     NUMBER	serarate entry from following row?
295  *	Spanname   NMTOKEN	name of a horiz. span
296  *
297  *  THead/TFoot/TBody:
298  *	VAlign	   (Top | Middle | Bottom)	group placement
299  *
300  *  Row:
301  *	Rowsep	   NUMBER	separate this row from next?
302  *	VAlign	   (Top | Middle | Bottom)	row placement
303  *
304  *  Entry:
305  *	Align      (Left|Right|Center|Justify|Char)	entry align
306  *	Char       CDATA	Alignment specifier
307  *	Charoff    NUTOKEN	    ""       ""
308  *	Colname    NMTOKEN	Column identifier
309  *	Colsep     NUMBER	separate this col from next?
310  *	Morerows   NUMBER	number of addn'l rows in vert straddle
311  *	Nameend    NMTOKEN	name of rightmost col of a span
312  *	Namest     NMTOKEN	name of leftmost col of a span
313  *	Rotate     NUMBER	90 degree rotation counterclockwise to table?
314  *	Rowsep     NUMBER	serarate entry from following row?
315  *	Spanname   NMTOKEN	name of a horiz. span
316  *	VAlign     (Top | Middle | Bottom)	text vert alignment
317  *
318  *
319  ** OBSOLETE OSF DTD FORM (still used for TeX form):
320  **  Usage in transpec: _calstable [tex|check|clear] ['aspect']
321  **  where 'aspect' is:
322  **	rowstart	stuff to do at start of a row (tests for spanning)
323  **	rowend		stuff to do at end of a row (eg, rules, etc.)
324  **	cellstart	stuff to do at start of a cell (eg, handle actual
325  **			spanning instructions, etc.)
326  **	cellend		stuff to do at end of a cell  (eg, cell separator)
327  **	top		stuff to do at top of the table
328  **			(like whether or not it needs a starting horiz rule)
329  **	bottom		stuff to do at bottom of the table
330  **			(like whether or not it needs an ending horiz rule)
331  **	(nothing)	the 'cols' param to LaTeX's \begin{tabular}[pos]{cols}
332  **			or 'options' and 'formats' part in tbl
333  *
334  *
335  * New tbl form:
336  *  Usage in transpec: _calstable [tbl] ['aspect']
337  *  where 'aspect' is:
338  *	tablestart	start a table and do style info
339  *	tableend	end the table and clean up
340  *	tablegroup	table TGroup (.T& if not 1st, line format info)
341  *	tablegroupend	end a TGroup
342  *	tablefoot	TFoot within a TGroup
343  *	rowstart	start of a row
344  *	rowend		end of a row
345  *	entrystart	start of an entry (block of filled text, if
346  *				appropriate)
347  *	entryend	end of a cell  (eg, cell separator)
348  */
349 
350 /*  Procedure to
351  *  Arguments:
352  *	Pointer to element under consideration.
353  *	FILE pointer to where to write output.
354  *	Vector of args to _osftable
355  *	Count of args to _osftable
356  */
357 void
CALStable(Element_t * e,FILE * fp,char ** av,int ac)358 CALStable(
359     Element_t	*e,
360     FILE	*fp,
361     char	**av,
362     int		ac
363 )
364 {
365     /* Check params and dispatch to appropriate routine */
366 
367     if (!strcmp(av[1], "tbl")) {
368 
369 	if (ac > 2) {
370 	    if (!strcmp(av[2], "tablestart"))		TblTStart(e, fp);
371 	    else if (!strcmp(av[2], "tableend"))	TblTEnd(e, fp);
372 	    else if (!strcmp(av[2], "tablegroup"))	TblTGroup(e, fp);
373 	    else if (!strcmp(av[2], "tablegroupend"))	TblTGroupEnd(e, fp);
374 	    else if (!strcmp(av[2], "tablefoot"))	TblTFoot(e, fp);
375 	    else if (!strcmp(av[2], "rowstart"))	TblTRowStart(e, fp);
376 	    else if (!strcmp(av[2], "rowend"))		TblTRowEnd(e, fp);
377 	    else if (!strcmp(av[2], "entrystart"))	TblTCellStart(e, fp);
378 	    else if (!strcmp(av[2], "entryend"))	TblTCellEnd(e, fp);
379 	    else fprintf(stderr, "Unknown %s table instruction: %s\n",
380 		av[1], av[2]);
381 	}
382 	else	{
383 		fprintf(stderr, "Incomplete %s table instruction\n");
384 	}
385     }
386 
387     else if (!strcmp(av[1], "tex")) {
388 
389         if (ac > 1 && !strcmp(av[1], "check")) CheckTable(e);
390 
391         else
392         if (ac > 1 && !strcmp(av[1], "clear")) ClearTable(&TheTab);
393 
394 	if (ac > 2) {
395 	    if (!strcmp(av[2], "cellstart"))	TexTableCellStart(e, fp);
396 	    else if (!strcmp(av[2], "cellend"))	TexTableCellEnd(e, fp);
397 	    else if (!strcmp(av[2], "rowstart")) TexTableRowStart(e, fp);
398 	    else if (!strcmp(av[2], "rowend"))	TexTableRowEnd(e, fp);
399 	    else if (!strcmp(av[2], "top"))	TexTableTop(e, fp);
400 	    else if (!strcmp(av[2], "bottom"))	TexTableBottom(e, fp);
401 	    else fprintf(stderr, "Unknown %s table instruction: %s\n",
402 		av[1], av[2]);
403 	}
404 	else TexTable(e, fp);
405     }
406 
407     else fprintf(stderr, "Unknown table type: %s\n", av[1]);
408 
409 }
410 
411 /*  ClearTable -- start a new table process
412  *
413  */
414 
415 
416 void
ClearTable(TableInfo * t)417 ClearTable( TableInfo * t )
418 {
419     memset(t, 0, sizeof(TableInfo));
420 }
421 
422 
423 /* ______________________________________________________________________ */
424 /*  Set values of the our internal table structure based on the table's
425  *  attributes.  (This is called for tables, tgroups, colspecs, and rows,
426  *  since tables and rows share many of the same attributes.)
427  *  Arguments:
428  *	Pointer to element under consideration.
429  *	Pointer table info structure which will be filled in.
430  *	Flag saying whether or not to set global variables based on attrs.
431  */
432 void
SetTabAtts(Element_t * e,TableInfo * t,int set_globals)433 SetTabAtts(
434     Element_t	*e,
435     TableInfo	*t,
436     int		set_globals
437 )
438 {
439     char	*at;
440     Element_t	* ep;
441 
442     /* remember values of attributes */
443     if ((at = FindAttValByName(e, "ALIGN")))	  t->align      = at;
444     if ((at = FindAttValByName(e, "COLWIDTH")))	  t->colwidth   = at;
445     if ((at = FindAttValByName(e, "COLSEP")))	  t->colsep     = at;
446     if ((at = FindAttValByName(e, "FRAME")))	  t->frame      = at;
447     if ((at = FindAttValByName(e, "COLS")))	  t->cols	= at;
448 
449     /* Set some things for later when processing this table */
450     if (set_globals) {
451 
452 	rowsep = 1;
453 	frametop = framebot = 1;		/* default style */
454 
455 	/* For now we look at the first number of rowsep - it controls the
456 	 * horiz rule for then entire row.  (not easy to specify lines that
457 	 * span only some columns in tex or tbl. */
458 	if ((at = FindAttValByName(e, "ROWSEP")))	rowsep = atoi(at);
459     }
460 
461     if (t->frame) {
462 	/* Top|Bottom|Topbot|All|Sides|None */
463 	if (!strcmp(t->frame, "NONE") || !strcmp(t->frame, "SIDES"))
464 	    frametop = framebot = 0;
465 	else if (!strcmp(t->frame, "TOP"))    framebot = 0;
466 	else if (!strcmp(t->frame, "BOTTOM")) frametop = 0;
467     }
468 
469     /* tbl and tex like lower case for units. convert. */
470     if (t->colwidth) {
471 	char *cp;
472 	for (cp=t->colwidth; *cp; cp++)
473 	    if (isupper(*cp)) *cp = tolower(*cp);
474     }
475 
476     /* Now, split (space-separated) strings into vectors.  Hopefully, the
477      * number of elements in each vector matches the number of columns.
478      */
479     t->align_v     = Split(t->align, &t->n_align, S_STRDUP|S_ALVEC);
480     t->colwidth_v  = Split(t->colwidth, &t->n_colwidth, S_STRDUP|S_ALVEC);
481     t->colsep_v    = Split(t->colsep, &t->n_colsep, S_STRDUP|S_ALVEC);
482 
483     /* Determin the _numeric_ number of columns, "nc".  MUST be specified
484      * in Cols attribute of TGroup element.
485      */
486     if (t->cols) t->nc = atoi(t->cols);
487 }
488 
489 /* ______________________________________________________________________ */
490 
491 /*  Free the storage of info use by the table info structure.  (not the
492  *  structure itself, but the strings its elements point to)
493  *  Arguments:
494  *	Pointer table info structure to be freed.
495  */
496 void
FreeTabAtts(TableInfo * t)497 FreeTabAtts(
498     TableInfo	*t
499 )
500 {
501     if (!t) return;
502     if (t->align_v)     free(*t->align_v);
503     if (t->colwidth_v)  free(*t->colwidth_v);
504     if (t->colsep_v)    free(*t->colsep_v);
505 }
506 
507 /* ______________________________________________________________________ */
508 /*  Check the attributes and children of the table pointed to by e.
509  *  Report problems and inconsistencies to stderr.
510  *  Arguments:
511  *	Pointer to element (table) under consideration.
512  */
513 
514 void
CheckTable(Element_t * e)515 CheckTable(
516     Element_t	*e
517 )
518 {
519     int		pr_loc=0;	/* flag to say if we printed location */
520     int		i, r, c;
521     Element_t	*ep, *ep2;
522     float	wt;
523     char	*tpref = "Table Check";		/* prefix for err messages */
524     char	*ncolchk =
525 	"Table Check: %s ('%s') has wrong number of tokens.  Expecting %d.\n";
526 
527     if (strcmp(e->gi, "TABLE") &&
528 	strcmp(e->gi, "INFORMALTABLE") &&
529 	strcmp(e->gi, "TGROUP") &&
530 	strcmp(e->gi, "COLSPEC") &&
531 	strcmp(e->gi, "ROW") ) {
532 	fprintf(stderr, "%s: Not pointing to a table element(%s)!\n",
533 						tpref, e->gi);
534 	return;
535     }
536 
537     FreeTabAtts(&TheTab);	/* free storage, if allocated earlier */
538     SetTabAtts(e, &TheTab, 1);	/* look at attributes */
539 
540 #if FALSE
541     /* NCOLS attribute set? */
542     if (!TheTab.ncols) {
543 	pr_loc++;
544 	fprintf(stderr, "%s: NCOLS attribute missing. Inferred as %d.\n",
545 		tpref, TheTab.nc);
546     }
547 
548     /* ALIGN attribute set? */
549     if (!TheTab.align) {
550 	pr_loc++;
551 	fprintf(stderr, "%s: ALIGN attribute missing.\n", tpref);
552     }
553 
554     /* See if the number of cells in each row matches */
555     for (r=0; r<e->necont && (ep=e->econt[r]); r++) {	/* each TGroup */
556 	for (i=0;  i<ep->necont && (ep2=ep->econt[i]);  i++)	{
557 	    if ( strcmp(ep2->gi, "TBODY") )	/* only TBodys */
558 		continue;
559 
560 	    for (c=0;  c<ep2->necont;  c++)	{
561 	    	if (ep2->econt[c]->necont != TheTab.nc) {
562 		    pr_loc++;
563 		    fprintf(stderr, "%s: COLS (%d) differs from actual number of cells (%d) in row %d.\n",
564 				tpref, TheTab.nc, ep2->econt[c]->necont, c);
565 		}
566 	    }
567 	}
568     }
569 #endif
570 
571     /* Check ALIGN */
572     if (TheTab.align) {
573 	if (TheTab.nc != TheTab.n_align) {	/* number of tokens OK? */
574 	    pr_loc++;
575 	    fprintf(stderr, ncolchk, "ALIGN", TheTab.align, TheTab.nc);
576 	}
577 	else {				/* values OK? */
578 	    for (i=0; i<TheTab.nc; i++) {
579 		if (*TheTab.align_v[i] != 'C' && *TheTab.align_v[i] != 'L' &&
580 			*TheTab.align_v[i] != 'R') {
581 		    pr_loc++;
582 		    fprintf(stderr, "%s: ALIGN (%d) value wrong: %s\n",
583 			tpref, i, TheTab.align_v[i]);
584 		}
585 	    }
586 	}
587     }
588 
589     /* check COLWIDTH */
590     if (TheTab.colwidth) {
591 	if (TheTab.nc != TheTab.n_colwidth) {	/* number of tokens OK? */
592 	    pr_loc++;
593 	    fprintf(stderr, ncolchk, "COLWIDTH", TheTab.colwidth, TheTab.nc);
594 	}
595 	else {				/* values OK? */
596 	    for (i=0; i<TheTab.nc; i++) {
597 
598 		/* check that the units after the numbers are OK
599 		    we want "in", "cm".
600 		 */
601 	    }
602 	}
603     }
604 
605     /* check COLSEP */
606     if (TheTab.colsep) {
607 	if (TheTab.nc != TheTab.n_colsep) {	/* number of tokens OK? */
608 	    pr_loc++;
609 	    fprintf(stderr, ncolchk, "COLSEP", TheTab.colsep, TheTab.nc);
610 	}
611 	else {				/* values OK? */
612 	    for (i=0; i<TheTab.nc; i++) {
613 	    }
614 	}
615     }
616 
617     if (pr_loc) {
618 	fprintf(stderr, "%s: Above problem in table located at:\n", tpref);
619 	PrintLocation(e, stderr);
620     }
621 }
622 
623 /* ______________________________________________________________________ */
624 
625 /*  Look at colspec attribute for spanning.  If set, remember info for when
626  *  doing the cells.  Called by TblTableRowStart() and TexTableRowStart().
627  *  Arguments:
628  *	Pointer to element (row) under consideration.
629  */
630 int
check_for_spans(Element_t * e)631 check_for_spans(
632     Element_t	*e
633 )
634 {
635     char	*at;
636     char	**spans;
637     int		n, i, inspan;
638 
639 #if FALSE	/* NOT IMPLEMENTED RIGHT NOW */
640 
641     /* See if COLSPEC element present */
642     for (i=0;  i < e->necont;  i++)	{
643 
644     }
645 
646 
647     if ((at = FindAttValByName(e, "MODEL"))) {
648 
649 	/* Split into tokens, then look at each for the word "span" */
650 	n = TheTab.nc;
651 	spans = Split(at, &n, S_STRDUP|S_ALVEC);
652 
653 	/* Mark columns as start-of-span, in-span, or not spanned.  Remember
654 	 * in at list, "spaningo".  (Span does not make sense in 1st column.)
655 	 */
656 	for (i=1,inspan=0; i<n; i++) {
657 	    if (StrEq(spans[i], "span") || StrEq(spans[i], "s")) {
658 		if (inspan == 0) spaninfo[i-1] = SPAN_START;
659 		spaninfo[i] = SPAN_CONT;
660 		inspan = 1;
661 	    }
662 	    else {
663 		spaninfo[i] = SPAN_NOT;
664 		inspan = 0;
665 	    }
666 	}
667 	free(*spans);				/* free string */
668 	free(spans);				/* free vector */
669 	spaninfo[TheTab.nc] = SPAN_NOT;		/* after last cell */
670 	return 1;
671     }
672     /* if model not set, mark all as not spanning */
673     else
674 
675 #endif	/* NOT CURRENTLY IMPLEMENTED */
676 
677 	for (i=0; i<MAXCOLS; i++) spaninfo[i] = SPAN_NOT;
678     return 0;
679 }
680 
681 /* ______________________________________________________________________ */
682 /* Do the "right thing" for the table spec for TeX tables.  This will
683  * generate the arg to \begin{tabular}[xxx].
684  *  Arguments:
685  *	Pointer to element (table) under consideration.
686  *	FILE pointer to where to write output.
687  */
688 void
TexTable(Element_t * e,FILE * fp)689 TexTable(
690     Element_t	*e,
691     FILE	*fp
692 )
693 {
694     int		i, n;
695     float	tot;
696     char	*cp, wbuf[1500], **widths=0, **widths_v=0;
697 
698     FreeTabAtts(&TheTab);	/* free storage, if allocated earlier */
699     SetTabAtts(e, &TheTab, 1);	/* look at attributes */
700     SetTabAtts(e->econt[0], &TheTab, 1);	/* attrs of TGroup */
701 
702     /* Figure out the widths, based either on "colwidth".
703      */
704     if (TheTab.colwidth && TheTab.nc == TheTab.n_colwidth) {
705 	widths = TheTab.colwidth_v;
706     }
707 
708     siderules = 1;
709     if (TheTab.frame)
710 	if (strcmp(TheTab.frame, "ALL") && strcmp(TheTab.frame, "SIDES"))
711 	    siderules = 0;
712 
713     if (siderules) OutputString("|", fp, 1);
714     for (i=0; i<TheTab.nc; i++) {
715 	/* If width specified, use it; else if align set, use it; else left. */
716 	if (widths && widths[i][0] != '0' && widths[i][1] != EOS) {
717 	    fprintf(fp, "%sp{%s}", (i?" ":""), widths[i]);
718 	}
719 	else if (TheTab.align && TheTab.nc == TheTab.n_align) {
720 	    fprintf(fp, "%s%s", (i?" ":""), TheTab.align_v[i]);
721 	}
722 	else
723 	    fprintf(fp, "%sl", (i?" ":""));
724 	/* See if we want column separators. */
725 	if (TheTab.colsep) {
726 
727 	    if ( (i+1) < TheTab.nc ) {
728 		if ( *TheTab.colsep_v[i] == '1' ) {
729 		    fprintf(fp, " |");
730 		}
731 		if ( *TheTab.colsep_v[i] == '2' ) {
732 		    fprintf(fp, " ||");
733 		}
734 	    }
735 
736 	}
737     }
738     if (siderules) OutputString("|", fp, 1);
739 
740     if (widths_v) free(widths_v);
741 }
742 
743 /*
744  *  Arguments:
745  *	Pointer to element (cell) under consideration.
746  *	FILE pointer to where to write output.
747  */
748 void
TexTableCellStart(Element_t * e,FILE * fp)749 TexTableCellStart(
750     Element_t	*e,
751     FILE	*fp
752 )
753 {
754     int		n, i;
755     char	buf[50], *at;
756 
757     if (spaninfo[e->my_eorder] == SPAN_START) {
758 	for (i=e->my_eorder+1,n=1; ; i++) {
759 	    if (spaninfo[i] == SPAN_CONT) n++;
760 	    else break;
761 	}
762 	sprintf(buf, "\\\\multicolumn{%d}{%sc%s}", n,
763 		(siderules?"|":""), (siderules?"|":""));
764 	OutputString(buf, fp, 1);
765     }
766 #ifdef New
767     if ((at = FindAttValByName(e->parent, "ALIGN"))) {
768 	/* no span, but user wants to change the alignment */
769 	h_v = Split(wbuf, 0, S_ALVEC|S_STRDUP);
770 	OutputString("\\\\multicolumn{1}{%sc%s}", n,
771 		fp, 1);
772     }
773 #endif
774 
775     if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("{", fp, 1);
776 }
777 
778 /*
779  *  Arguments:
780  *	Pointer to element (cell) under consideration.
781  *	FILE pointer to where to write output.
782  */
783 void
TexTableCellEnd(Element_t * e,FILE * fp)784 TexTableCellEnd(
785     Element_t	*e,
786     FILE	*fp
787 )
788 {
789     if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("} ", fp, 1);
790 
791     /* do cell/col separators */
792     if (e->my_eorder < (TheTab.nc-1)) {
793 	if (spaninfo[e->my_eorder] == SPAN_NOT ||
794 			spaninfo[e->my_eorder+1] != SPAN_CONT)
795 	    OutputString("& ", fp, 1);
796     }
797 }
798 
799 /*  Look at model for spanning.  If set, remember it for when doing the cells.
800  *  Arguments:
801  *	Pointer to element (row) under consideration.
802  *	FILE pointer to where to write output.
803  */
804 void
TexTableRowStart(Element_t * e,FILE * fp)805 TexTableRowStart(
806     Element_t	*e,
807     FILE	*fp
808 )
809 {
810     check_for_spans(e);
811 }
812 
813 /*
814  *  Arguments:
815  *	Pointer to element (row) under consideration.
816  *	FILE pointer to where to write output.
817  */
818 void
TexTableRowEnd(Element_t * e,FILE * fp)819 TexTableRowEnd(
820     Element_t	*e,
821     FILE	*fp
822 )
823 {
824     char	*at;
825 
826     /* check this row's attributes */
827     if ((at = FindAttValByName(e, "ROWSEP"))) {
828 	if (at[0] == '1') OutputString("\\\\\\\\[2mm] \\\\hline ", fp, 1);
829     }
830     else if (rowsep) OutputString("\\\\\\\\ ", fp, 1);
831     else
832         OutputString("\\\\\\\\ ", fp, 1);
833 
834 }
835 
836 /*
837  *  Arguments:
838  *	Pointer to element (table) under consideration.
839  *	FILE pointer to where to write output.
840  */
841 void
TexTableTop(Element_t * e,FILE * fp)842 TexTableTop(Element_t *e, FILE *fp)
843 {
844     if (frametop) OutputString("\\\\hline", fp, 1);
845 }
846 
847 void
TexTableBottom(Element_t * e,FILE * fp)848 TexTableBottom(Element_t *e, FILE *fp)
849 {
850     if (framebot) OutputString("\\\\hline", fp, 1);
851 }
852 
853 /* ______________________________________________________________________ */
854 /* ______________________________________________________________________ */
855 /* ______________________________________________________________________ */
856 /* ______________________________________________________________________ */
857 /* ______________________________________________________________________ */
858 /* ___________________________|             |____________________________ */
859 /* ___________________________|  TBL STUFF  |____________________________ */
860 /* ___________________________|             |____________________________ */
861 /* ___________________________|_____________|____________________________ */
862 /* ______________________________________________________________________ */
863 /* ______________________________________________________________________ */
864 /* ______________________________________________________________________ */
865 /* ______________________________________________________________________ */
866 
867 
868 
869 /*	TblTStart()  --  start a table and do style information
870  *
871  *  TO DO:
872  *
873  *	do .TS
874  *	find global rowsep and colsep
875  */
876 
877 
878 void
TblTStart(Element_t * ep,FILE * fP)879 TblTStart(Element_t * ep,
880 	  FILE * fP)
881 {
882 	register char * cp;
883 	register struct Element_t * ep2;
884 
885 
886 
887 	OutputString("^.TS^", fP, 1);
888 
889 	tblTGroupSeen = FALSE;
890 	tblinBOFT = FALSE;	/* within a boft? */
891 	tblBOFTCount = 0;	/* count of Blocks of Filled Text that
892 				 * we've created */
893 
894 	tblgcolsep = (cp = FindAttValByName(ep, "COLSEP")) && !strcmp(cp, "1");
895 	tblgrowsep = (cp = FindAttValByName(ep, "ROWSEP")) && !strcmp(cp, "1");
896 }
897 
898 /*      TblTEnd()  --  end a table and do any cleanup
899  *
900  *  TO DO:
901  *
902  *	do .TE
903  *
904  *	deallocate format line info
905  */
906 
907 
908 
909 void
TblTEnd(Element_t * ep,FILE * fP)910 TblTEnd(Element_t * ep,
911 	FILE * fP)
912 {
913 	register struct tblformat * ffp, * ffp2;
914 
915 
916 	if ( tblBOFTCount > 31 )	{
917 		fprintf(stderr, "# warning, line %d: created %d blocks of filled text in one table\n",
918 					ep->lineno, tblBOFTCount);
919 		fprintf(stderr, "#\t\t(31 is the limit in some systems)\n");
920 	}
921 
922 	OutputString("^.TE^", fP, 1);
923 
924 	for ( ffp=formP;  ffp;  ffp=ffp2 )	{
925 		ffp2 = ffp->next;
926 		free(ffp);		/* clear entire list */
927 	}
928 	formP = 0;
929 }
930 
931 /*      TblTTGroup()  --  do body work (row format info)
932  *
933  *  TO DO:
934  *
935  *	set number of columns
936  *
937  *	if this is the first TGroup of this table, do style info:
938  *	   a. alignment
939  *	   b. defaults:  tab
940  *	   c. box vx allbox
941  *
942  *	do format info:
943  *	   a. generate tableformat structure
944  *	   b. output it
945  *
946  *	prepare structures for colspecs and spanspecs
947  *
948  */
949 
950 
951 
952 void
TblTGroup(Element_t * ep,FILE * fP)953 TblTGroup(Element_t * ep,
954 	  FILE * fP)
955 {
956 	register int i, j, k;
957 	register char * cp, * cp2;
958 	register Element_t * ep2, ep3;
959 	register struct tblcolspec * tcsp, * tcsp2;
960 	register struct tblspanspec * tssp, * tssp2;
961 
962 
963 	tblColSpec = 0;		/* make sure they're clear */
964 	tblSpanSpec = 0;
965 
966     /* set the number of columns */
967 
968     	tblcols = atoi(FindAttValByName(ep, "COLS"));
969 
970     /* do colspecs */
971 
972     	tblColSpec = tcsp = TblDoColSpec(0, ep, 0, TGroup);
973     			/* do TGroup first -- it becomes the default */
974 
975 	for ( i=0, k=1;  i < ep->necont;  i++ )	{
976 
977 		if ( !strcmp(ep->econt[i]->gi, "COLSPEC") )	{
978 			tcsp2 = TblDoColSpec(k, ep->econt[i], tblColSpec, TGroup);
979 			tcsp->next = tcsp2;	/* put into list */
980 			tcsp = tcsp2;
981 			k = tcsp2->num + 1;	/* next column number */
982 		}
983 
984 		if ( !strcmp(ep->econt[i]->gi, "THEAD") )	{
985 			ep2 = ep->econt[i];
986 			for ( j=0, k=1;  j < ep2->necont;  j++ )	{
987 				if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") )	{
988 					tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, THead);
989 					tcsp->next = tcsp2;	/* put into list */
990 					tcsp = tcsp2;
991 					k = tcsp2->num + 1;	/* next column number */
992 				}
993 			}
994 		}
995 
996 		if ( !strcmp(ep->econt[i]->gi, "TFOOT") )	{
997 			ep2 = ep->econt[i];
998 			for ( j=0, k=1;  j < ep2->necont;  j++ )	{
999 				if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") )	{
1000 					tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TFoot);
1001 					tcsp->next = tcsp2;	/* put into list */
1002 					tcsp = tcsp2;
1003 					k = tcsp2->num + 1;	/* next column number */
1004 				}
1005 			}
1006 		}
1007 
1008 		if ( !strcmp(ep->econt[i]->gi, "TBODY") )	{
1009 			ep2 = ep->econt[i];
1010 			for ( j=0, k=1;  j < ep2->necont;  j++ )	{
1011 				if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") )	{
1012 					tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TBody);
1013 					tcsp->next = tcsp2;	/* put into list */
1014 					tcsp = tcsp2;
1015 					k = tcsp2->num + 1;	/* next column number */
1016 				}
1017 			}
1018 		}
1019 	}
1020 
1021     /* do spanspecs */
1022 
1023 	tblSpanSpec = tssp = TblDoSpanSpec(ep, 0, TGroup);
1024 			/* do TGroup first -- it becomes the default */
1025 
1026 	for ( i=0;  i < ep->necont;  i++ )	{
1027 		if ( !strcmp(ep->econt[i]->gi, "SPANSPEC") )	{
1028 			tssp2 = TblDoSpanSpec(ep->econt[i], tblSpanSpec, TGroup);
1029 			tssp->next = tssp2;	/* put into list */
1030 			tssp = tssp2;
1031 		}
1032 	}
1033 
1034 
1035     /* if this is the first TGroup in this table, do style stuff */
1036 
1037 	if ( ! tblTGroupSeen )	{
1038 
1039 		OutputString("tab(\007)", fP, 1);
1040 
1041 		ep2 = ep->parent;
1042 		if ( ! (tblFrame = FindAttValByName(ep2, "FRAME")) )
1043 			tblFrame = "";
1044 
1045 		if ( !strcmp(tblFrame, "ALL") )	{
1046 			if ( tcsp->colsep && tcsp->rowsep )
1047 				OutputString(" allbox", fP, 1);
1048 			else
1049 				OutputString(" box", fP, 1);
1050 		}
1051 
1052 		if ( (cp = FindAttValByName(ep, "ALIGN")) &&
1053 		     !strcmp(cp, "CENTER") )	{
1054 		     	OutputString(" center", fP, 1);
1055 		}
1056 
1057 		OutputString(";\n", fP, 1);
1058 
1059 		tblTGroupSeen = TRUE;
1060 	}
1061 
1062 
1063     /* do format stuff -- step through all THead rows then all TBody
1064      * rows.  Build a list of tblformats that describe all of them.
1065      * then output the resulting list.
1066      */
1067 
1068     	for ( i=0;  i < ep->necont;  i++ )	{
1069 		if ( !strcmp(ep->econt[i]->gi, "THEAD") )	{
1070 			TblBuildFormat(ep->econt[i], &formP, THead);
1071 						/* add in those rows */
1072 			break;
1073     		}
1074     	}
1075 
1076     	for ( i=0;  i < ep->necont;  i++ )	{
1077 		if ( !strcmp(ep->econt[i]->gi, "TBODY") )	{
1078 			TblBuildFormat(ep->econt[i], &formP, TBody);
1079 						/* add in those rows */
1080 			break;
1081     		}
1082     	}
1083 
1084 	TblPrintFormat(fP, formP);
1085 
1086 	tblrow = 0;		/* the current row within this format */
1087 }
1088 
1089 /*      TblTGroupEnd()  --  end a TGroup
1090  *
1091  *  TO DO:
1092  *
1093  *	deallocate colspecs and spanspecs
1094  */
1095 
1096 
1097 void
TblTGroupEnd(Element_t * ep,FILE * fP)1098 TblTGroupEnd(Element_t * ep,
1099 	      FILE * fP)
1100 {
1101 	register struct tblcolspec * tcsp, * tcsp2;
1102 	register struct tblspanspec * tssp, * tssp2;
1103 
1104 
1105 	for ( tcsp=tblColSpec;  tcsp;  tcsp=tcsp2 )	{
1106 		tcsp2 = tcsp->next;
1107 		free(tcsp);
1108 	}
1109 	for ( tssp=tblSpanSpec;  tssp;  tssp=tssp2 )	{
1110 		tssp2 = tssp->next;
1111 		free(tssp);
1112 	}
1113 }
1114 
1115 /*      TblTTFoot()  --  do body foot work (row format info)
1116  *
1117  *  TO DO:
1118  *
1119  *	do format info:
1120  *	   a. generate tableformat structure
1121  *	      i. if it is only 1 line long and matches the
1122  *		 prevailing format, just output rows.
1123  *	     ii. else, output a .T& and the new format specs
1124  */
1125 
1126 
1127 
1128 void
TblTFoot(Element_t * ep,FILE * fP)1129 TblTFoot(Element_t * ep,
1130 	 FILE * fP)
1131 {
1132 	register struct tblformat * ffp, * ffp2;
1133 	static struct tblformat * tfp, * tfp2;
1134 
1135 
1136 	TblBuildFormat(ep, &tfp, TFoot);	/* gen format for the foot */
1137 
1138 	for ( tfp2=formP;  tfp2 && tfp2->next;  tfp2=tfp2->next )
1139 		;
1140 
1141 	if ( tfp->next || !TblFormatMatch(tfp, tfp2) )	{
1142 
1143 		for ( ffp=formP;  ffp;  ffp=ffp2 )	{
1144 			ffp2 = ffp->next;
1145 			free(ffp);		/* clear entire list */
1146 		}
1147 
1148 		formP = tfp;	/* this becomes the prevailing format */
1149 
1150 		OutputString("^.T&^", fP, 1);
1151 		TblPrintFormat(fP, formP);
1152 	}
1153 
1154 	tblrow = 0;		/* the current row within this format */
1155 }
1156 
1157 /*	TblBuildFormat()  --  build a format structure out of a set of
1158  *				rows and columns
1159  *
1160  */
1161 
1162 
1163 void
TblBuildFormat(Element_t * ep,struct tblformat ** fp,tblsource source)1164 TblBuildFormat(Element_t * ep,		/* parent of rows.. */
1165 	       struct tblformat ** fp,	/* pointer to head of struct we're
1166 	       				 * building */
1167 	       tblsource source)	/* type of record */
1168 {
1169 	register int i;
1170 	register struct tblformat * lfp; /* "current" format */
1171 	register struct tblformat * nfp; /* the next format */
1172 
1173 
1174 	for ( lfp= *fp;  lfp && lfp->next;  lfp=lfp->next )
1175 		;			/* find end of format list */
1176 
1177     	for ( i=0;  i < ep->necont;  i++ )
1178 		if ( !strcmp(ep->econt[i]->gi, "ROW") )
1179 			break;		/* find where rows start */
1180 
1181 	for (  ;  i < ep->necont;  i++ )	{
1182 
1183 		nfp = TblBuild1Format(ep->econt[i], FALSE, source);
1184 						/* do one row */
1185 
1186 		if ( !lfp )
1187 			lfp = *fp = nfp;	/* first one */
1188 		else
1189 		if ( TblFormatMatch(lfp, nfp) )
1190 			lfp->count++;		/* matches */
1191 		else	{
1192 			lfp->count = 1;		/* only 1 so far */
1193 			lfp->next = nfp;	/* new one */
1194 			lfp = nfp;
1195 		}
1196 	}
1197 }
1198 
1199 /*	TblBuild1Format()  --  build one row's worth of format information
1200  *
1201  */
1202 
1203 
1204 
1205 struct tblformat *
TblBuild1Format(Element_t * rp,bool addinRowsep,tblsource source)1206 TblBuild1Format(Element_t * rp,		/* the row to deal with */
1207 		bool addinRowsep,	/* insert rowsep into model? */
1208 		tblsource source)	/* type type of row */
1209 {
1210 	register int i;
1211 	register bool allProp;
1212 	float totalProp;
1213 	register struct tblformat * tfp;
1214 	register Element_t * ep;	/* entry pointer */
1215 
1216 
1217 	Calloc(1, tfp, struct tblformat);
1218 	tfp->cols = tblcols;
1219 	ep = (rp->necont) ? rp->econt[0] : 0;	/* first entry */
1220 	allProp = TRUE;
1221 	totalProp = 0;
1222 
1223 	for ( i=1;  i <= tblcols;  i++ )	{
1224 		tfp->colformat[i] = TblGetAlign(i, ep, source);
1225 		strcpy(tfp->colwidth[i], TblGetWidth(i, ep, TRUE, source));
1226 		strcpy(tfp->colpwidth[i], TblGetWidth(i, ep, FALSE, source));
1227 		if ( allProp )	{
1228 			allProp = tfp->colpwidth[i][0];
1229 			totalProp += atof(tfp->colpwidth[i]);
1230 		}
1231 		strcpy(tfp->font[i], TblGetFont(i, ep, source));
1232 		tfp->colsep[i] = tblgcolsep || TblGetColSep(i, ep, source);
1233 		if ( addinRowsep )
1234 			tfp->rowsep[i] = tblgrowsep || TblGetRowSep(i, ep, source);
1235 		tfp->moreRows[i] = TblGetMoreRows(i, ep, source);
1236 
1237 		if ( (i < rp->necont) && TblColAdv(i, ep, tfp, source) )	{
1238 			ep = rp->econt[i];
1239 		}
1240 	}
1241 
1242     /* turn proportional widths into real widths */
1243 
1244     	if ( allProp )	{
1245     		for ( i=1;  i <= tblcols;  i++ )	{
1246     			sprintf(tfp->colwidth[i], "%fi",
1247     				(atof(tfp->colpwidth[i]) / totalProp) * TEXTWIDTH);
1248     		}
1249     	}
1250 
1251 	return tfp;
1252 }
1253 
1254 /*	TblGetAlign()  --  get alignment spec for a entry
1255  *
1256  */
1257 
1258 
1259 char
TblGetAlign(short col,Element_t * entry,tblsource source)1260 TblGetAlign(short col,			/* column number */
1261 	    Element_t * entry,		/* the entry */
1262 	    tblsource	source)		/* context */
1263 {
1264 	register struct tblcolspec * tcsp;
1265 	register struct tblspanspec * tssp;
1266 	register tblalign talign;
1267 
1268 
1269 	if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) )	{
1270 		talign = tssp->align;
1271 		free(tssp);
1272 	} else
1273 	if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) )	{
1274 		talign = tcsp->align;
1275 		free(tcsp);
1276 	} else	{
1277 		return 'l';
1278 	}
1279 
1280 	switch ( talign )	{
1281 	case Left:	return 'l';
1282 	case Right:	return 'r';
1283 	case Center:	return 'c';
1284 	case Justify:	return 'l';
1285 	case Char:	return 'd';
1286 	case Span:	return 's';
1287 	}
1288 }
1289 
1290 /*	TblGetWidth()  --  get width spec, if any, for a entry
1291  *
1292  */
1293 
1294 
1295 char *
TblGetWidth(short col,Element_t * entry,bool literal,tblsource source)1296 TblGetWidth(short col,			/* column number */
1297 	    Element_t * entry,		/* the entry */
1298 	    bool	literal,	/* literal (or proportional) */
1299 	    tblsource	source)		/* context */
1300 {
1301 	register struct tblcolspec * tcsp;
1302 	register struct tblspanspec * tssp;
1303 	static char colWidth[10];
1304 
1305 
1306 	colWidth[0] = 0;
1307 
1308 	if ( entry &&
1309 	     (tcsp = TblEntryColSpec(col, entry, source)) &&
1310 	     tcsp->colwidth[0] )	{
1311 
1312 		if ( !strstr(tcsp->colwidth, "*") )	{
1313 			if ( literal )
1314 				strcpy(colWidth, tcsp->colwidth);
1315 		} else	{
1316 			if ( ! literal )
1317 				strcpy(colWidth, tcsp->colwidth);
1318 		}
1319 		free(tcsp);
1320 	}
1321 
1322 	return colWidth;
1323 }
1324 
1325 /*	TblGetFont()  --  get font spec, if any, for a entry
1326  *
1327  */
1328 
1329 
1330 char *
TblGetFont(short col,Element_t * entry,tblsource source)1331 TblGetFont(short col,			/* column number */
1332 	   Element_t * entry,		/* the entry */
1333 	   tblsource source)		/* context */
1334 {
1335 	register struct tblcolspec * tcsp;
1336 	register struct tblspanspec * tssp;
1337 
1338 
1339 	return "";
1340 }
1341 
1342 /*	TblGetColSep()  --  get column separater spec, if any, for a entry
1343  *
1344  */
1345 
1346 
1347 bool
TblGetColSep(short col,Element_t * entry,tblsource source)1348 TblGetColSep(short col,			/* column number */
1349 	     Element_t * entry,		/* the entry */
1350 	     tblsource	source)		/* context */
1351 {
1352 	register struct tblcolspec * tcsp;
1353 	register struct tblspanspec * tssp;
1354 	register bool colsep;
1355 
1356 
1357 	if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) )	{
1358 		colsep = tssp->colsep;
1359 		free(tssp);
1360 	} else
1361 	if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) )	{
1362 		colsep = tcsp->colsep;
1363 		free(tcsp);
1364 	} else
1365 		colsep = FALSE;
1366 
1367 	return colsep;
1368 }
1369 
1370 /*	TblGetRowSep()  --  get row separater spec, if any, for a entry
1371  *
1372  */
1373 
1374 
1375 bool
TblGetRowSep(short col,Element_t * entry,tblsource source)1376 TblGetRowSep(short col,			/* column number */
1377 	     Element_t * entry,		/* the entry */
1378 	     tblsource	source)		/* context */
1379 {
1380 	register struct tblcolspec * tcsp;
1381 	register struct tblspanspec * tssp;
1382 	register bool rowsep;
1383 
1384 	if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) )	{
1385 		rowsep = tssp->rowsep;
1386 		free(tssp);
1387 	} else
1388 	if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) )	{
1389 		rowsep = tcsp->rowsep;
1390 		free(tcsp);
1391 	} else	{
1392 		rowsep = FALSE;
1393 	}
1394 
1395 	return rowsep;
1396 }
1397 
1398 /*	TblGetmoreRows()  --  get moreRows value
1399  *
1400  */
1401 
1402 
1403 bool
TblGetMoreRows(short col,Element_t * entry,tblsource source)1404 TblGetMoreRows(short col,		/* column number */
1405 	       Element_t * entry,	/* the entry */
1406 	       tblsource	source)	/* context */
1407 {
1408 	register char * cp;
1409 
1410 
1411 	if ( cp = FindAttValByName(entry, "MOREROWS") )
1412 		return atoi(cp);
1413 	else
1414 		return 0;
1415 }
1416 
1417 /*	TblColAdv()  --  advance pointer to next entry, if appropriate
1418  *
1419  */
1420 
1421 
1422 bool
TblColAdv(short col,Element_t * ep,struct tblformat * tfp,tblsource source)1423 TblColAdv(short col,		/* the current column */
1424 	  Element_t *ep,	/* pointer to entry */
1425 	  struct tblformat * tfp, /* pointer to prevailing format */
1426 	  tblsource source)	/* context */
1427 {
1428 	register bool bump;
1429 	register struct tblspanspec * tssp;
1430 
1431 
1432 	bump = TRUE;
1433 
1434 	if ( tssp = TblEntrySpanSpec(col, ep, source) )	{
1435 		bump = tssp->align != Span;
1436 		free(tssp);
1437 	}
1438 
1439 	return bump;
1440 }
1441 
1442 /*	TblEntryColSpec()  --  get a completely localized colspec for an entry
1443  *
1444  */
1445 
1446 
1447 struct tblcolspec *
TblEntryColSpec(short num,Element_t * ep,tblsource source)1448 TblEntryColSpec(short num,		/* column number */
1449 		Element_t * ep,		/* entry */
1450 		tblsource source)	/* context */
1451 {
1452 	register int i;
1453 	register bool throwAway;
1454 	register char * cp;
1455 	register struct tblcolspec * tcsp, * tcsp2;
1456 
1457 
1458 	tcsp = tcsp2 = 0;
1459 	throwAway = FALSE;
1460 
1461 	if ( (cp = FindAttValByName(ep, "COLNAME")) )	{
1462 		if ( ! (tcsp = TblFindColSpec(cp, source)) )	{
1463 			fprintf(stderr, "? can't find column name '%s'\n", cp);
1464 		}
1465 	}
1466 
1467 	if ( tcsp2 = TblFindColNum(num, source) )	{
1468 		tcsp = TblDoColSpec(num, ep, tcsp2, source);
1469 		throwAway = TRUE;
1470 	}
1471 
1472 	tcsp2 = TblDoColSpec(num, ep, tcsp, source);
1473 
1474 	if ( throwAway )
1475 		free(tcsp);
1476 
1477 	return tcsp2;
1478 }
1479 
1480 /*	TblEntrySpanSpec()  --  get a completely localized spanspec for an entry
1481  *
1482  */
1483 
1484 
1485 struct tblspanspec *
TblEntrySpanSpec(short num,Element_t * ep,tblsource source)1486 TblEntrySpanSpec(short num,		/* column number */
1487 		 Element_t * ep,	/* entry */
1488 		 tblsource source)	/* context */
1489 {
1490 	register char * cp, * cp2;
1491 	register struct tblspanspec * tssp, * tssp2;
1492 
1493 
1494 	tssp2 = 0;
1495 
1496 	if ( !(cp = FindAttValByName(ep, "SPANNAME")) ||
1497 	     !(tssp2 = TblFindSpanSpec(cp, source)) )	{
1498 
1499 	     	if ( !FindAttValByName(ep, "NAMEST") )
1500 	     		return 0;
1501 	}
1502 
1503 	tssp = TblDoSpanSpec(ep, tssp2, source);
1504 
1505 	if ( tssp->start && tssp->end &&
1506 	     (tssp->start->num < num) && (tssp->end->num >= num) )	{
1507 		tssp->align = Span;
1508 	}
1509 
1510 	return tssp;
1511 }
1512 
1513 /*	TblFormatMatch()  --  compare two format rows for consistency
1514  *
1515  */
1516 
1517 
1518 bool
TblFormatMatch(struct tblformat * tf1,struct tblformat * tf2)1519 TblFormatMatch(struct tblformat * tf1,	/* one row */
1520 	       struct tblformat * tf2)	/* the other */
1521 {
1522 	register int i;
1523 
1524 	if ( tf1->cols != tf2->cols )	{
1525 		return FALSE;
1526 	}
1527 
1528 	for ( i=0;  i < tf1->cols;  i++ )	{
1529 
1530 		if ( tf1->colformat[i] != tf2->colformat[i] )	{
1531 			return FALSE;
1532 		}
1533 		if ( strcmp(tf1->colwidth[i], tf2->colwidth[i]) )	{
1534 			return FALSE;
1535 		}
1536 		if ( strcmp(tf1->font[i], tf2->font[i]) )	{
1537 			return FALSE;
1538 		}
1539 		if ( tf1->colsep[i] != tf2->colsep[i] )	{
1540 			return FALSE;
1541 		}
1542 		if ( tf1->rowsep[i] != tf2->rowsep[i] )	{
1543 			return FALSE;
1544 		}
1545 		if ( tf1->moreRows[i] || tf2->moreRows[i] )	{
1546 			return FALSE;
1547 		}
1548 	}
1549 
1550 	return TRUE;
1551 }
1552 
1553 /*	TblPrintFormat()  --  print a tbl format structure
1554  *
1555  */
1556 
1557 
1558 void
TblPrintFormat(FILE * fP,struct tblformat * tfp)1559 TblPrintFormat(FILE * fP,		/* where to print */
1560 	       struct tblformat * tfp)	/* the structure */
1561 {
1562 	register int i;
1563 	register struct tblformat * tfp2, * tfp3;
1564 	static char buf[3] = "\000\000";
1565 
1566 
1567 	for ( tfp2=tfp, tfp3=0;  tfp2;  tfp2=tfp2->next )	{
1568 		for ( i=1;  i <= tfp->cols;  i++ )	{
1569 			if ( i > 1 )
1570 				OutputString(" ", fP, 1);
1571 			if ( tfp3 && tfp3->moreRows[i] )
1572 				OutputString("\\^", fP, 1);
1573 			else	{
1574 				buf[0] = tfp2->colformat[i];
1575 				OutputString(buf, fP, 1);
1576 			}
1577 			if ( tfp2->colwidth[i][0] )	{
1578 				OutputString("w(", fP, 1);
1579 				OutputString(tfp2->colwidth[i], fP, 1);
1580 				OutputString(")", fP, 1);
1581 			}
1582 			if ( tfp2->font[i][0] )
1583 				OutputString(tfp2->font[i], fP, 1);
1584 			if ( tfp2->colsep[i] )
1585 				OutputString("|", fP, 1);
1586 		}
1587 		if ( ! tfp2->next )
1588 			OutputString(".", fP, 1);
1589 		OutputString("^", fP, 1);
1590 		tfp3 = tfp2;
1591 	}
1592 }
1593 
1594 /*      TblTRowStart()  --  start a row (not much to do)
1595  *
1596  *  TO DO:
1597  *
1598  *	nothing..
1599  *
1600  */
1601 
1602 
1603 
1604 void
TblTRowStart(Element_t * ep,FILE * fP)1605 TblTRowStart(Element_t * ep,
1606 	     FILE * fP)
1607 {
1608 
1609     /* nothing to do */
1610 
1611 	tblrow++;	/* except note that we're within a new row */
1612 
1613 }
1614 
1615 /*      TblTRowEnd()  --  end a row
1616  *
1617  *  TO DO:
1618  *
1619  *	output a row end character (newline)
1620  *	if the current row had a rowsep, then output a "fake" row
1621  *	with underlines in the proper place(s).
1622  */
1623 
1624 
1625 
1626 void
TblTRowEnd(Element_t * ep,FILE * fP)1627 TblTRowEnd(Element_t * ep,
1628 	   FILE * fP)
1629 {
1630 	register int i, k;
1631 	register tblsource source;
1632 	register bool startedRow, didSep;
1633 	register struct tblformat * rfp;
1634 
1635 
1636 	OutputString("^", fP, 1);
1637 
1638     /* get the format for this row */
1639 
1640     	if ( !strcmp(ep->parent->gi, "TFoot") )
1641     		source = TFoot;
1642 	else
1643     	if ( !strcmp(ep->parent->gi, "THead") )
1644 		source = THead;
1645 	else
1646 		source = TBody;
1647 
1648     	rfp = TblBuild1Format(ep, TRUE, source);
1649 	startedRow = FALSE;
1650 	didSep = FALSE;
1651 
1652 	for ( i=1;  i <= formP->cols;  i++ )	{
1653 		if ( rfp->rowsep[i] ||
1654 		     (didSep && (rfp->colformat[i] == 's')) )	{
1655 			if ( ! startedRow )	{
1656 				OutputString("^", fP, 1);
1657 				for ( k=1;  k < i;  k++ )
1658 					OutputString("\007", fP, 1);
1659 				startedRow = TRUE;
1660 			}
1661 			OutputString("_\007", fP, 1);
1662 			didSep = TRUE;
1663 		} else	{
1664 		if ( startedRow )
1665 			OutputString("\007", fP, 1);
1666 		}
1667 		didSep = FALSE;
1668 	}
1669 	free(rfp);		/* clear that row.. */
1670 
1671 	if ( startedRow )
1672 		OutputString("^", fP, 1);
1673 }
1674 
1675 /*      TblTEntryStart()  --  start an entry (block of filled text if
1676  *				appropriate)
1677  *
1678  *  TO DO:
1679  *
1680  *	if text length > BOFTTextThresh or there is PI,
1681  *	then output "T{\n", else do nothing
1682  *
1683  */
1684 
1685 
1686 
1687 void
TblTCellStart(Element_t * ep,FILE * fP)1688 TblTCellStart(Element_t * ep,
1689 	      FILE * fP)
1690 {
1691 	register int i;
1692 	register Element_t * ep2;
1693 	register bool sawPI;
1694 
1695 
1696 	for ( i=0, sawPI=FALSE;  (i < ep->ncont) && !sawPI;  i++ )
1697 		if ( ep->cont[i].type == '?' )
1698 			sawPI = TRUE;
1699 
1700 	if ( sawPI || (TblCountContent(ep) > BOFTTextThresh) )	{
1701 		tblBOFTCount++;
1702 		OutputString("T{^", fP, 1);
1703 		tblinBOFT = TRUE;	/* within a boft now */
1704 	}
1705 }
1706 
1707 /*	TblCountContent()  --  count all content below the given element
1708  *
1709  *
1710  */
1711 
1712 
1713 
1714 int
TblCountContent(Element_t * ep)1715 TblCountContent(Element_t * ep)		/* the element to look under */
1716 {
1717 	register int i, count;
1718 
1719 
1720 	count = 0;
1721 
1722 	for ( i=0;  i < ep->ncont;  i++ )	{
1723 		if ( ep->cont[i].type == '-' )	{
1724 			count += strlen(ep->cont[i].ch.data);
1725 		} else
1726 		if ( ep->cont[i].type == '(' )	{
1727 			count += TblCountContent(ep->cont[i].ch.elem);
1728 		}
1729 	}
1730 
1731 	return count;
1732 }
1733 
1734 /*      TblTEntryEnd()  --  end an entry
1735  *
1736  *  TO DO:
1737  *
1738  *	if within BOFT, output "T}"
1739  *	if not last entry, output tab character
1740  *
1741  */
1742 
1743 
1744 
1745 void
TblTCellEnd(Element_t * ep,FILE * fP)1746 TblTCellEnd(Element_t * ep,
1747 	    FILE * fP)
1748 {
1749 	register Element_t * ep2;
1750 
1751 
1752 	if ( tblinBOFT )	{
1753 		OutputString("^T}", fP, 1);
1754 		tblinBOFT = FALSE;	/* back out again */
1755 	}
1756 
1757 	for ( ep2=ep->next;  ep2;  ep2=ep2->next )	{
1758 		if ( !strcmp(ep2->gi, "ENTRY") || !strcmp(ep2->gi, "ENTRYTBL") )	{
1759 			OutputString("\007", fP, 1);
1760 			break;
1761 		}
1762 		if ( !strcmp(ep2->gi, "ROW") )
1763 			break;
1764 	}
1765 }
1766 
1767 /*	TblDoColSpec()  --  process one element to create a new colspec
1768  *
1769  *
1770  */
1771 
1772 
1773 
1774 struct tblcolspec *
TblDoColSpec(short number,Element_t * ep,struct tblcolspec * pcsp,tblsource source)1775 TblDoColSpec(short number,		/* this column number */
1776 	     Element_t * ep,		/* element containing colspec stuff */
1777 	     struct tblcolspec * pcsp,	/* prevailing colspec (with defaults) */
1778 	     tblsource source)		/* precedence level of the resulting spec */
1779 {
1780 	register char * cp;
1781 	register struct tblcolspec * tcsp;
1782 
1783 
1784 	Calloc(1, tcsp, struct tblcolspec);
1785 
1786 	if ( cp = FindAttValByName(ep, "COLNAME") )
1787 		strcpy(tcsp->name, cp);
1788 
1789 	tcsp->num = number;
1790 	tcsp->source = source;
1791 
1792 	if ( cp = FindAttValByName(ep, "ALIGN") )	{
1793 		if      ( !strcmp(cp, "LEFT") )		tcsp->align = Left;
1794 		else if ( !strcmp(cp, "RIGHT") )	tcsp->align = Right;
1795 		else if ( !strcmp(cp, "CENTER") )	tcsp->align = Center;
1796 		else if ( !strcmp(cp, "JUSTIFY") )	tcsp->align = Justify;
1797 		else if ( !strcmp(cp, "CHAR") )		tcsp->align = Char;
1798 	} else
1799 		tcsp->align = ( pcsp ) ? pcsp->align : Left;
1800 
1801 	if ( cp = FindAttValByName(ep, "CHAR") )
1802 		tcsp->alignchar = cp[0];
1803 	else
1804 		tcsp->alignchar = ( pcsp ) ? pcsp->alignchar : 0;
1805 
1806 	if ( cp = FindAttValByName(ep, "CHAROFF") )
1807 		tcsp->aligncharoff = atoi(cp);
1808 	else
1809 		tcsp->aligncharoff = ( pcsp ) ? pcsp->aligncharoff : 0;
1810 
1811 	if ( cp = FindAttValByName(ep, "COLWIDTH") )
1812 		strcpy(tcsp->colwidth, cp);
1813 	else
1814 		strcpy(tcsp->colwidth, ( pcsp ) ? pcsp->colwidth : "");
1815 
1816 	if ( cp = FindAttValByName(ep, "COLSEP") )
1817 		tcsp->colsep = !strcmp(cp, "1");
1818 	else
1819 		tcsp->colsep = ( pcsp ) ? pcsp->colsep : FALSE;
1820 
1821 	if ( cp = FindAttValByName(ep, "ROWSEP") )
1822 		tcsp->rowsep = !strcmp(cp, "1");
1823 	else
1824 		tcsp->rowsep = ( pcsp ) ? pcsp->rowsep : FALSE;
1825 
1826 	return tcsp;
1827 }
1828 
1829 /*	TblDoSpanSpec()  --  process one element to create a new spanspec
1830  *
1831  *	Note that there's a hack inside here...  NameSt and NameEnd are
1832  *	supposed to point at colnames, but if no colname is found, this
1833  *	code will look for a colnum by the same value.
1834  */
1835 
1836 
1837 
1838 struct tblspanspec *
TblDoSpanSpec(Element_t * ep,struct tblspanspec * pssp,tblsource source)1839 TblDoSpanSpec(Element_t * ep,		/* element containing spanspec stuff */
1840 	      struct tblspanspec * pssp, /* prevailing spanspec (with defaults) */
1841 	      tblsource source)		/* precedence level of the resulting spec */
1842 {
1843 	register char * cp;
1844 	register struct tblspanspec * tssp;
1845 	register struct tblcolspec * tcsp;
1846 
1847 
1848 	Calloc(1, tssp, struct tblspanspec);
1849 
1850 	if ( cp = FindAttValByName(ep, "SPANNAME") ) strcpy(tssp->name, cp);
1851 	tssp->source = source;
1852 
1853 	if ( cp = FindAttValByName(ep, "NAMEST") )	{
1854 		if ( (tcsp = TblFindColSpec(cp, source)) ||
1855 		     (tcsp = TblFindColNum(atoi(cp), source)) )	{
1856 		     	tssp->start = tcsp;
1857 		} else	{
1858 			fprintf(stderr, "? spanspec namest points to unknown column '%s'\n", cp);
1859 			tssp->start = 0;
1860 		}
1861 	} else	{
1862 		if ( pssp && pssp->start )	{
1863 			tssp->start = pssp->start;
1864 		}
1865 	}
1866 
1867 	if ( cp = FindAttValByName(ep, "NAMEEND") )	{
1868 		if ( (tcsp = TblFindColSpec(cp, source)) ||
1869 		     (tcsp = TblFindColNum(atoi(cp), source)) )	{
1870 		     	tssp->end = tcsp;
1871 		} else	{
1872 			fprintf(stderr, "? spanspec nameend points to unknown column '%s'\n", cp);
1873 			tssp->end = 0;
1874 		}
1875 	} else	{
1876 		if ( pssp && pssp->end )	{
1877 			tssp->end = pssp->end;
1878 		}
1879 	}
1880 
1881 	if ( cp = FindAttValByName(ep, "ALIGN") )	{
1882 		if      ( !strcmp(cp, "LEFT") )		tssp->align = Left;
1883 		else if ( !strcmp(cp, "RIGHT") )	tssp->align = Right;
1884 		else if ( !strcmp(cp, "CENTER") )	tssp->align = Center;
1885 		else if ( !strcmp(cp, "JUSTIFY") )	tssp->align = Justify;
1886 		else if ( !strcmp(cp, "CHAR") )		tssp->align = Char;
1887 	} else	{
1888 		if ( pssp )
1889 			tssp->align = pssp->align;
1890 	}
1891 
1892 	if ( cp = FindAttValByName(ep, "CHAR") )
1893 		tssp->alignchar = cp[0];
1894 	else	{
1895 		if ( pssp )
1896 			tssp->alignchar = pssp->alignchar;
1897 	}
1898 	if ( cp = FindAttValByName(ep, "CHAROFF") )
1899 		tssp->aligncharoff = atoi(cp);
1900 	else	{
1901 		if ( pssp )
1902 			tssp->alignchar = pssp->alignchar;
1903 	}
1904 
1905 	if ( cp = FindAttValByName(ep, "COLSEP") )
1906 		tssp->colsep = !strcmp(cp, "1");
1907 	else	{
1908 		if ( pssp )
1909 			tssp->colsep = pssp->colsep;
1910 	}
1911 	if ( cp = FindAttValByName(ep, "ROWSEP") )
1912 		tssp->rowsep = !strcmp(cp, "1");
1913 	else	{
1914 		if ( pssp )
1915 			tssp->rowsep = pssp->rowsep;
1916 	}
1917 
1918 	return tssp;
1919 }
1920 
1921 /*	TblFindColSpec()  --  find a table colspec by name (colname)
1922  *
1923  */
1924 
1925 
1926 
1927 struct tblcolspec *
TblFindColSpec(char * name,tblsource source)1928 TblFindColSpec(char * name,		/* the name we're looking for */
1929 	       tblsource source)	/* the context in which to find it */
1930 {
1931 	register struct tblcolspec * tcsp;
1932 
1933 
1934     /* first, try to find the one in the right "source" */
1935 
1936     	for ( tcsp=tblColSpec;  tcsp;  tcsp=tcsp->next )	{
1937     		if ( (tcsp->source == source) && !strcmp(tcsp->name, name) )
1938     			return tcsp;
1939     	}
1940 
1941     /* else, try to find one from a TGroup.. */
1942 
1943     	for ( tcsp=tblColSpec;  tcsp;  tcsp=tcsp->next )	{
1944     		if ( (tcsp->source == TGroup) && !strcmp(tcsp->name, name) )
1945     			return tcsp;
1946     	}
1947 
1948     /* else not found.. */
1949 
1950 	return 0;
1951 }
1952 
1953 /*	TblFindColNum()  --  find a table colspec by number
1954  *
1955  */
1956 
1957 
1958 
1959 struct tblcolspec *
TblFindColNum(short number,tblsource source)1960 TblFindColNum(short number,		/* the number we're looking for */
1961 	      tblsource source)		/* the context in which to find it */
1962 {
1963 	register struct tblcolspec * tcsp;
1964 
1965 
1966 
1967     /* first, try to find the one in the right "source" */
1968 
1969     	for ( tcsp=tblColSpec;  tcsp;  tcsp=tcsp->next )	{
1970     		if ( (tcsp->num == number) &&
1971     		     ((tcsp->source == source) ||
1972     		      ((source == THead) && (tcsp->source == TGroup))) )
1973     			return tcsp;
1974     	}
1975 
1976     /* else, try to find one from a TGroup.. */
1977 
1978     	for ( tcsp=tblColSpec;  tcsp;  tcsp=tcsp->next )	{
1979     		if ( (tcsp->source == TGroup) && (tcsp->num == number) )
1980     			return tcsp;
1981     	}
1982 
1983     /* else not found.. */
1984 
1985 	return 0;
1986 }
1987 
1988 /*	TblFindSpanSpec()  --  find a table spanspec by name (spanname)
1989  *
1990  */
1991 
1992 
1993 
1994 struct tblspanspec *
TblFindSpanSpec(char * name,tblsource source)1995 TblFindSpanSpec(char * name,		/* the name we're looking for */
1996 	        tblsource source)	/* the context in which to find it */
1997 {
1998 	register struct tblspanspec * tssp;
1999 
2000 
2001     /* first, try to find the one in the right "source" */
2002 
2003     	for ( tssp=tblSpanSpec;  tssp;  tssp=tssp->next )	{
2004     		if ( !strcmp(tssp->name, name) &&
2005     		     ((tssp->source == source) ||
2006     		      ((source == THead) && (tssp->source == TGroup))) )
2007     			return tssp;
2008     	}
2009 
2010     /* else not found.. */
2011 
2012 	return 0;
2013 }
2014