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