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