dtprintinfo: Coverity 89561
[oweals/cde.git] / cde / programs / dtdocbook / 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 /*
24  *  Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
25  *  All rights reserved.
26  */
27 /*
28  * Copyright (c) 1994  
29  * Open Software Foundation, Inc. 
30  *  
31  * Permission is hereby granted to use, copy, modify and freely distribute 
32  * the software in this file and its documentation for any purpose without 
33  * fee, provided that the above copyright notice appears in all copies and 
34  * that both the copyright notice and this permission notice appear in 
35  * supporting documentation.  Further, provided that the name of Open 
36  * Software Foundation, Inc. ("OSF") not be used in advertising or 
37  * publicity pertaining to distribution of the software without prior 
38  * written permission from OSF.  OSF makes no representations about the 
39  * suitability of this software for any purpose.  It is provided "as is" 
40  * without express or implied warranty. 
41  */
42 /* ________________________________________________________________________
43  *
44  *  Program to manipulate SGML instances.
45  *
46  *  This module is for handling OSF table markup, printing TeX or tbl
47  *  (tbl) markup to the output stream.  Also, table markup checking is
48  *  done here.  Yes, this depends on the DTD, but it makes translation
49  *  specs much cleaner (and makes some things possible.
50  *
51  *  Incomplete / not implemented / limitations / notes:
52  *      vertical alignment (valign attr)
53  *      vertical spanning
54  *      'wrap hint' attribute
55  *      row separators are for the whole line, not per cell (the prog looks
56  *              at rowsep for the 1st cell and applies it to the whole row)
57  *      trusts that units if colwidths are acceptable to LaTeX and tbl
58  *      "s" is an acceptable shorthand for "span" in model attributes
59  *
60  *  A note on use of OutputString():  Strings with backslashes (\) need lots
61  *  of backslashes.  You have to escape them for the C compiler, and escape
62  *  them again for OutputString() itself.
63  * ________________________________________________________________________
64  */
65
66 #ifndef lint
67 static char *RCSid =
68   "$XConsortium: tables.c /main/3 1996/06/19 17:13:17 drk $";
69 #endif
70
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <ctype.h>
74 #include <string.h>
75 #include <memory.h>
76 #include <sys/types.h>
77 #include <errno.h>
78
79 #include <tptregexp.h>
80 #include "general.h"
81 #include "translate.h"
82
83 /* text width of page, in inches */
84 #define TEXTWIDTH       6.0
85 #define MAXCOLS         100
86 #define SPAN_NOT        0
87 #define SPAN_START      1
88 #define SPAN_CONT       2
89
90 /* these cover the attributes on the table element */
91 typedef struct {
92     char        *ncols;
93     char        *halign,    **halign_v;
94     char        *model,     **model_v;
95     char        *colwidth,  **colwidth_v;
96     char        *colsep,    **colsep_v;
97     char        *colweight, **colweight_v;
98     char        *frame;
99     int         n_halign, n_model, n_colwidth, n_colsep, n_colweight;
100     int         repeathead;     
101     int         nc;
102 } TableInfo;
103
104
105 /* some flags, set when the table tag is processed, used later */
106 static int      rowsep, siderules;
107 static int      frametop, framebot, frameall;
108 static char     basemodel[128]; /* model for table (in formatting language) */
109 static int      spaninfo[MAXCOLS];      /* 100 columns, max */
110 static TableInfo        TheTab;
111
112 /* forward references */
113 void    SetTabAtts(Element_t *, TableInfo *, int);
114 void    FreeTabAtts(TableInfo   *);
115 void    CheckTable(Element_t *);
116 void    TblTable(Element_t *, FILE *);
117 void    TblTableCellStart(Element_t *, FILE *);
118 void    TblTableCellEnd(Element_t *, FILE *);
119 void    TblTableRowStart(Element_t *, FILE *);
120 void    TblTableRowEnd(Element_t *, FILE *);
121 void    TblTableTop(Element_t *, FILE *);
122 void    TblTableBottom(Element_t *, FILE *);
123 void    TexTable(Element_t *, FILE *);
124 void    TexTableCellStart(Element_t *, FILE *);
125 void    TexTableCellEnd(Element_t *, FILE *);
126 void    TexTableRowStart(Element_t *, FILE *);
127 void    TexTableRowEnd(Element_t *, FILE *);
128 void    TexTableTop(Element_t *, FILE *);
129 void    TexTableBottom(Element_t *, FILE *);
130
131 /* ______________________________________________________________________ */
132 /*  Hard-coded stuff for OSF DTD tables.
133  *  Here are the TABLE attributes (for handy reference):
134  *      ncols      NUMBER       num of cells/row should match 
135  *      model      CDATA        column prototypes for this table 
136  *      colwidth   NUTOKENS     absolute widths of cols 
137  *      colweight  NUMBERS      column weights 
138  *      halign     CDATA        horiz alignment for columns 
139  *      valign     CDATA        vertical alignment for columns 
140  *      colsep     NUMBERS      use col separators (lines)? 
141  *      rowsep     NUMBERS      use row separators (lines)? 
142  *      wrap       NUMBERS      wrap hints for columns
143  *      repeathead NUMBER       carry title rows to other pages
144  *      frame      (top|bottom|topbot|all|sides|none)   frame style
145  *
146  *  The 'wrap' attribute is never used.
147  *
148  *  Usage in transpec: _osftable [tex|tbl|check] ['aspect']
149  *  where 'aspect' is:
150  *      rowstart        stuff to do at start of a row (tests for spanning)
151  *      rowend          stuff to do at end of a row (eg, rules, etc.)
152  *      cellstart       stuff to do at start of a cell (eg, handle actual
153  *                      spanning instructions, etc.)
154  *      cellend         stuff to do at end of a cell  (eg, cell separator)
155  *      top             stuff to do at top of the table
156  *                      (like whether or not it needs a starting horiz rule)
157  *      bottom          stuff to do at bottom of the table
158  *                      (like whether or not it needs an ending horiz rule)
159  *      (nothing)       the 'cols' param to LaTeX's \begin{tabular}[pos]{cols}
160  *                      or 'options' and 'formats' part in tbl
161  */
162
163 /*  Procedure to
164  *  Arguments:
165  *      Pointer to element under consideration.
166  *      FILE pointer to where to write output.
167  *      Vector of args to _osftable
168  *      Count of args to _osftable
169  */
170 void
171 OSFtable(
172     Element_t   *e,
173     FILE        *fp,
174     char        **av,
175     int         ac
176 )
177 {
178     /* Check params and dispatch to appropriate routine */
179
180     if (ac > 1 && !strcmp(av[1], "check")) CheckTable(e);
181
182     else if (!strcmp(av[1], "tbl")) {
183         if (ac > 2) {
184             if (!strcmp(av[2], "cellstart"))    TblTableCellStart(e, fp);
185             else if (!strcmp(av[2], "cellend")) TblTableCellEnd(e, fp);
186             else if (!strcmp(av[2], "rowstart")) TblTableRowStart(e, fp);
187             else if (!strcmp(av[2], "rowend"))  TblTableRowEnd(e, fp);
188             else if (!strcmp(av[2], "top"))     TblTableTop(e, fp);
189             else if (!strcmp(av[2], "bottom"))  TblTableBottom(e, fp);
190             else fprintf(stderr, "Unknown %s table instruction: %s\n",
191                 av[1], av[2]);
192         }
193         else TblTable(e, fp);
194     }
195
196     else if (!strcmp(av[1], "tex")) {
197         if (ac > 2) {
198             if (!strcmp(av[2], "cellstart"))    TexTableCellStart(e, fp);
199             else if (!strcmp(av[2], "cellend")) TexTableCellEnd(e, fp);
200             else if (!strcmp(av[2], "rowstart")) TexTableRowStart(e, fp);
201             else if (!strcmp(av[2], "rowend"))  TexTableRowEnd(e, fp);
202             else if (!strcmp(av[2], "top"))     TexTableTop(e, fp);
203             else if (!strcmp(av[2], "bottom"))  TexTableBottom(e, fp);
204             else fprintf(stderr, "Unknown %s table instruction: %s\n",
205                 av[1], av[2]);
206         }
207         else TexTable(e, fp);
208     }
209
210     else fprintf(stderr, "Unknown table type: %s\n", av[1]);
211
212 }
213
214 /* ______________________________________________________________________ */
215 /*  Set values of the our internal table structure based on the table's
216  *  attributes.  (This is also called for rows, since tables and rows
217  *  share many of the same attributes.)
218  *  Arguments:
219  *      Pointer to element under consideration.
220  *      Pointer table info structure which will be filled in.
221  *      Flag saying whether or not to set global variables based on attrs.
222  */
223 void
224 SetTabAtts(
225     Element_t   *e,
226     TableInfo   *t,
227     int         set_globals
228 )
229 {
230     char        *at;
231
232     memset(t, 0, sizeof(TableInfo));
233
234     /* remember values of attributes */
235     if ((at = FindAttValByName(e, "HALIGN")))     t->halign     = at;
236     if ((at = FindAttValByName(e, "MODEL")))      t->model      = at;
237     if ((at = FindAttValByName(e, "COLWIDTH")))   t->colwidth   = at;
238     if ((at = FindAttValByName(e, "COLSEP")))     t->colsep     = at;
239     if ((at = FindAttValByName(e, "COLWEIGHT")))  t->colweight  = at;
240     if ((at = FindAttValByName(e, "FRAME")))      t->frame      = at;
241     if ((at = FindAttValByName(e, "REPEATHEAD"))) t->repeathead = atoi(at);
242     if ((at = FindAttValByName(e, "NCOLS")))      t->ncols      = at;
243
244     /* Set some things for later when processing this table */
245     if (set_globals) {
246
247         rowsep = 1;
248         frametop = framebot = 1;                /* default style */
249
250         /* For now we look at the first number of rowsep - it controls the
251          * horiz rule for then entire row.  (not easy to specify lines that
252          * span only some columns in tex or tbl. */
253         if ((at = FindAttValByName(e, "ROWSEP")))       rowsep = atoi(at);
254     }
255
256     if (t->frame) {
257         /* top|bottom|topbot|all|sides|none */
258         if (!strcmp(t->frame, "NONE") || !strcmp(t->frame, "SIDES"))
259             frametop = framebot = 0;
260         else if (!strcmp(t->frame, "TOP"))    framebot = 0;
261         else if (!strcmp(t->frame, "BOTTOM")) frametop = 0;
262     }
263
264     /* tbl and tex like lower case for units. convert. */
265     if (t->colwidth) {
266         char *cp;
267         for (cp=t->colwidth; *cp; cp++)
268             if (isupper(*cp)) *cp = tolower(*cp);
269     }
270
271     /* Now, split (space-separated) strings into vectors.  Hopefully, the
272      * number of elements in each vector matches the number of columns.
273      */
274     t->halign_v    = Split(t->halign, &t->n_halign, S_STRDUP|S_ALVEC);
275     t->model_v     = Split(t->model, &t->n_model, S_STRDUP|S_ALVEC);
276     t->colwidth_v  = Split(t->colwidth, &t->n_colwidth, S_STRDUP|S_ALVEC);
277     t->colweight_v = Split(t->colweight, &t->n_colweight, S_STRDUP|S_ALVEC);
278     t->colsep_v    = Split(t->colsep, &t->n_colsep, S_STRDUP|S_ALVEC);
279
280     /* Determin the _numeric_ number of columns, "nc".  The order in which we
281      * check things to set nc is: NCOLS attribute, # of child element of 1st
282      * row, number of tokens in the various attr lists.
283      */
284     if (t->ncols) t->nc = atoi(t->ncols);
285
286     /* If ncols attribute not set, see how many children first child has.
287      * I can't see how this can be non-zero (unless there are no rows, or
288      * no rows have any cells).
289      */
290     if (!t->nc && e->necont) t->nc = e->econt[0]->necont;
291
292     /* If ncols still not set, guess it from other attrs. Last resort. */
293     if (!t->nc) {
294         if (t->n_halign)   t->nc = t->n_halign;
295         else if (t->n_model)     t->nc = t->n_model;
296         else if (t->n_colwidth)  t->nc = t->n_colwidth;
297         else if (t->n_colweight) t->nc = t->n_colweight;
298         else if (t->n_colsep)    t->nc = t->n_colsep;
299     }
300 }
301
302 /* ______________________________________________________________________ */
303
304 /*  Free the storage of info use by the table info structure.  (not the
305  *  structure itself, but the strings its elements point to)
306  *  Arguments:
307  *      Pointer table info structure to be freed.
308  */
309 void
310 FreeTabAtts(
311     TableInfo   *t
312 )
313 {
314     if (!t) return;
315     if (t->halign_v)    free(*t->halign_v);
316     if (t->model_v)     free(*t->model_v);
317     if (t->colwidth_v)  free(*t->colwidth_v);
318     if (t->colweight_v) free(*t->colweight_v);
319     if (t->colsep_v)    free(*t->colsep_v);
320 }
321
322 /* ______________________________________________________________________ */
323 /*  Check the attributes and children of the table pointed to by e.
324  *  Report problems and inconsistencies to stderr.
325  *  Arguments:
326  *      Pointer to element (table) under consideration.
327  */
328
329 void
330 CheckTable(
331     Element_t   *e
332 )
333 {
334     int         pr_loc=0;       /* flag to say if we printed location */
335     int         i, r, c;
336     float       wt;
337     char        *tpref = "Table Check";         /* prefix for err messages */
338     char        *ncolchk =
339         "Table Check: %s ('%s') has wrong number of tokens.  Expecting %d.\n";
340
341     if (strcmp(e->gi, "TABLE")) {
342         fprintf(stderr, "%s: Not pointing to a table!\n", tpref);
343         return;
344     }
345
346     FreeTabAtts(&TheTab);       /* free storage, if allocated earlier */
347     SetTabAtts(e, &TheTab, 1);  /* look at attributes */
348
349     /* NCOLS attribute set? */
350     if (!TheTab.ncols) {
351         pr_loc++;
352         fprintf(stderr, "%s: NCOLS attribute missing. Inferred as %d.\n",
353                 tpref, TheTab.nc);
354     }
355
356     /* HALIGN attribute set? */
357     if (!TheTab.halign) {
358         pr_loc++;
359         fprintf(stderr, "%s: HALIGN attribute missing.\n", tpref);
360     }
361
362     /* See if the number of cells in each row matches */
363     for (r=0; r<e->necont; r++) {
364         if (e->econt[r]->necont != TheTab.nc) {
365             pr_loc++;
366             fprintf(stderr, "%s: NCOLS (%d) differs from actual number of cells (%d) in row %d.\n",
367                 tpref, TheTab.nc, e->econt[r]->necont, r);
368         }
369     }
370
371     /* Check HALIGN */
372     if (TheTab.halign) {
373         if (TheTab.nc != TheTab.n_halign) {     /* number of tokens OK? */
374             pr_loc++;
375             fprintf(stderr, ncolchk, "HALIGN", TheTab.halign, TheTab.nc);
376         }
377         else {                          /* values OK? */
378             for (i=0; i<TheTab.nc; i++) {
379                 if (*TheTab.halign_v[i] != 'c' && *TheTab.halign_v[i] != 'l' &&
380                         *TheTab.halign_v[i] != 'r') {
381                     pr_loc++;
382                     fprintf(stderr, "%s: HALIGN (%d) value wrong: %s\n",
383                         tpref, i, TheTab.halign_v[i]);
384                 }
385             }
386         }
387     }
388
389     /* check COLWIDTH */
390     if (TheTab.colwidth) {
391         if (TheTab.nc != TheTab.n_colwidth) {   /* number of tokens OK? */
392             pr_loc++;
393             fprintf(stderr, ncolchk, "COLWIDTH", TheTab.colwidth, TheTab.nc);
394         }
395         else {                          /* values OK? */
396             for (i=0; i<TheTab.nc; i++) {
397
398                 /* check that the units after the numbers are OK
399                     we want "in", "cm".
400                  */
401             }
402         }
403     }
404
405     /* check COLWEIGHT */
406     if (TheTab.colweight) {
407         if (TheTab.nc != TheTab.n_colweight) {  /* number of tokens OK? */
408             pr_loc++;
409             fprintf(stderr, ncolchk, "COLWEIGHT", TheTab.colweight, TheTab.nc);
410         }
411         else {                          /* values OK? */
412             for (i=0; i<TheTab.nc; i++) {       /* check that magitude is reasonable */
413                 wt = atof(TheTab.colweight_v[i]);
414                 if (wt > 50.0) {
415                     pr_loc++;
416                     fprintf(stderr, "%s: unreasonable COLWEIGHT value: %f.\n",
417                                 tpref, wt);
418                 }
419             }
420         }
421     }
422
423     /* check COLSEP */
424     if (TheTab.colsep) {
425         if (TheTab.nc != TheTab.n_colsep) {     /* number of tokens OK? */
426             pr_loc++;
427             fprintf(stderr, ncolchk, "COLSEP", TheTab.colsep, TheTab.nc);
428         }
429         else {                          /* values OK? */
430             for (i=0; i<TheTab.nc; i++) {
431             }
432         }
433     }
434
435     /* See if MODEL has the same number of tokens as NCOLS.  Then do model. */
436     if (TheTab.model) {
437         if (TheTab.nc != TheTab.n_model) {
438             pr_loc++;
439             fprintf(stderr, ncolchk, "MODEL", TheTab.model, TheTab.nc);
440         }
441
442         for (r=0; r<e->necont; r++) {
443             /* only check normal rows */
444             if (strcmp(e->econt[r]->gi, "ROW")) continue;
445             for (c=0; c<e->econt[r]->necont; c++) {
446                 if (!strcmp(TheTab.model_v[c], "text") ||
447                         !strcmp(TheTab.model_v[c], "-")) continue;
448                 if (e->econt[r]->econt[c]->necont &&
449                     strcmp(e->econt[r]->econt[c]->econt[0]->gi, TheTab.model_v[c])) {
450                     fprintf(stderr, "%s: MODEL wants %s, but cell contains %s: row %d, cell %d.\n",
451                         tpref, TheTab.model_v[c],
452                         e->econt[r]->econt[c]->econt[0]->gi, r, c);
453                     pr_loc++;
454                 }
455             }
456         }
457     }
458
459     if (pr_loc) {
460         fprintf(stderr, "%s: Above problem in table located at:\n", tpref);
461         PrintLocation(e, stderr);
462     }
463 }
464
465 /* ______________________________________________________________________ */
466 /*  Do the "right thing" for the table spec for tbl (troff) tables.  This will
467  *  generate the "center,box,tab(@)..." and the column justification stuff.
468  *  Arguments:
469  *      Pointer to element (table) under consideration.
470  *      FILE pointer to where to write output.
471  */
472 void
473 TblTable(
474     Element_t   *e,
475     FILE        *fp
476 )
477 {
478     int         i, n;
479     char        *fr;
480     float       tot;
481     char        *cp, wbuf[1500], **widths=0, **widths_v=0, *mp;
482
483     FreeTabAtts(&TheTab);       /* free storage, if allocated earlier */
484     SetTabAtts(e, &TheTab, 1);  /* look at attributes */
485
486     fr = "box";                         /* default framing */
487     frameall = 1;
488     siderules = 0;
489     if (TheTab.frame) {
490         if (!strcmp(TheTab.frame, "ALL")) {
491             fr = "box";
492             frametop = framebot = 0;
493         }
494         else {
495             fr = "";
496             frameall = 0;
497         }
498         if (!strcmp(TheTab.frame, "SIDES")) siderules = 1;
499     }
500     else frametop = framebot = 0;       /* because 'box' is default */
501     fprintf(fp, "center, %s%s tab(@);\n", fr, ((*fr)?",":""));
502
503     /* Figure out the widths, based either on "colwidth" or "colweight".
504      * (we pick width over weight if both are specified). */
505     if (TheTab.colwidth && TheTab.nc == TheTab.n_colwidth) {
506         widths = TheTab.colwidth_v;
507     }
508     else if (TheTab.colweight && TheTab.nc == TheTab.n_colweight) {
509         for (n=0,i=0; i<TheTab.nc; i++) n += atoi(TheTab.colweight_v[i]);
510         tot = (float)n;
511         cp = wbuf;
512         for (i=0; i<TheTab.nc; i++) {
513             sprintf(cp, "%5.3fin", atof(TheTab.colweight_v[i])*(TEXTWIDTH/tot));
514             while (*cp) cp++;
515             *cp++ = ' ';
516         }
517         *cp = EOS;
518         widths_v = Split(wbuf, 0, S_ALVEC);
519         widths = widths_v;
520     }
521
522     /* Remember the base model in case we do spans later.  We write it
523      * into a static buffer, then output it at once. */
524     mp = basemodel;
525     if (siderules) *mp++ = '|';
526     for (i=0; i<TheTab.nc; i++) {
527         /* If width specified, use it; else if halign set, use it; else left. */
528         if (widths && widths[i][0] != '0' && widths[i][1] != EOS) {
529             if (i) *mp++ = ' ';
530             strcpy(mp, TheTab.halign_v[i]);
531             while (*mp) mp++;
532             *mp++ = 'w';
533             *mp++ = '(';
534             strcpy(mp, widths[i]);
535             while (*mp) mp++;
536             *mp++ = ')';
537         }
538         else if (TheTab.halign && TheTab.nc == TheTab.n_halign) {
539             if (i) *mp++ = ' ';
540             strcpy(mp, TheTab.halign_v[i]);
541             while (*mp) mp++;
542         }
543         else {
544             if (i) *mp++ = ' ';
545             *mp++ = 'l';
546         }
547         /* See if we want column separators. */
548
549         if (TheTab.colsep) {
550             if ( (i+1) < TheTab.nc ) {
551                 if ( *TheTab.colsep_v[i] == '1' )
552                     *mp++ = '|';
553                 if ( *TheTab.colsep_v[i] == '2') {
554                     *mp++ = '|';
555                     *mp++ = '|';
556                 }
557             }
558         }
559     }
560     if (siderules) *mp++ = '|';
561 /*    *mp++ = '.';*/
562 /*    *mp++ = '^';*/
563     *mp = EOS;
564     OutputString(basemodel, fp, 1);
565     OutputString(".^", fp, 1);
566
567     if (widths_v) free(widths_v);
568 }
569
570 /*
571  *  Arguments:
572  *      Pointer to element (cell) under consideration.
573  *      FILE pointer to where to write output.
574  */
575 void
576 TblTableCellStart(
577     Element_t   *e,
578     FILE        *fp
579 )
580 {
581     /* nothing to do at start of cell */
582 }
583
584 /*
585  *  Arguments:
586  *      Pointer to element (cell) under consideration.
587  *      FILE pointer to where to write output.
588  */
589 void
590 TblTableCellEnd(
591     Element_t   *e,
592     FILE        *fp
593 )
594 {
595     /* do cell/col separators */
596     if (e->my_eorder < (TheTab.nc-1)) {
597         if (spaninfo[e->my_eorder] == SPAN_NOT ||
598                         spaninfo[e->my_eorder+1] != SPAN_CONT)
599             OutputString("@", fp, 1);
600     }
601 }
602
603 /*  Look at model attribute for spanning.  If set, remember info for when
604  *  doing the cells.  Called by TblTableRowStart() and TexTableRowStart().
605  *  Arguments:
606  *      Pointer to element (row) under consideration.
607  */
608 int
609 check_for_spans(
610     Element_t   *e
611 )
612 {
613     char        *at;
614     char        **spans;
615     int         n, i, inspan;
616
617     /* See if MODEL attr is set */
618     if ((at = FindAttValByName(e, "MODEL"))) {
619
620         /* Split into tokens, then look at each for the word "span" */
621         n = TheTab.nc;
622         spans = Split(at, &n, S_STRDUP|S_ALVEC);
623
624         /* Mark columns as start-of-span, in-span, or not spanned.  Remember
625          * in at list, "spaningo".  (Span does not make sense in 1st column.)
626          */
627         for (i=1,inspan=0; i<n; i++) {
628             if (StrEq(spans[i], "span") || StrEq(spans[i], "s")) {
629                 if (inspan == 0) spaninfo[i-1] = SPAN_START;
630                 spaninfo[i] = SPAN_CONT;
631                 inspan = 1;
632             }
633             else {
634                 spaninfo[i] = SPAN_NOT;
635                 inspan = 0;
636             }
637         }
638         free(*spans);                           /* free string */
639         free(spans);                            /* free vector */
640         spaninfo[TheTab.nc] = SPAN_NOT;         /* after last cell */
641         return 1;
642     }
643     /* if model not set, mark all as not spanning */
644     else
645         for (i=0; i<MAXCOLS; i++) spaninfo[i] = SPAN_NOT;
646     return 0;
647 }
648
649 /*  Output format for cell.  Called from TblTableRowStart().
650  *  Arguments:
651  *      Pointer to table info structure (for this row)
652  *      Which cell/column we're considering
653  *      Flag saying whether we're on last column
654  *      Default format of col, if none is set for this row or table
655  *      FILE pointer to where to write output.
656  */
657
658 void
659 tbl_cell_fmt(
660     TableInfo   *t,
661     int         i,
662     int         lastcol,
663     char        *def_fmt,
664     FILE        *fp
665 )
666 {
667     if (t->halign) OutputString(t->halign_v[i], fp, 1);
668     else if (TheTab.halign) OutputString(TheTab.halign_v[i], fp, 1);
669     else OutputString(def_fmt, fp, 1);
670
671     if (!lastcol && spaninfo[i+1] != SPAN_CONT) {
672         if (t->colsep) {
673             if (*t->colsep_v[i] == '1')
674                 OutputString("|", fp, 1);
675             if (*t->colsep_v[i] == '2')
676                 OutputString("||", fp, 1);
677         }
678         else if (TheTab.colsep) {
679             if (*TheTab.colsep_v[i] == '1')
680                 OutputString("|", fp, 1);
681             if (*TheTab.colsep_v[i] == '2')
682                 OutputString("||", fp, 1);
683         }
684         else OutputString("|", fp, 1);
685     }
686     OutputString(" ", fp, 1);
687 }
688
689 /*  
690  *  Arguments:
691  *      Pointer to element (row) under consideration.
692  *      FILE pointer to where to write output.
693  */
694
695 void
696 TblTableRowStart(
697     Element_t   *e,
698     FILE        *fp
699 )
700 {
701     int         i, lastcol, stayhere;
702     char        **basev, *cp;
703     TableInfo   RowInfo;
704
705     /* check if we're spanning, or if HALIGN set */
706     stayhere = 0;
707     if (check_for_spans(e)) stayhere = 1;
708     SetTabAtts(e, &RowInfo, 0);
709     if (RowInfo.halign) stayhere = 1;
710
711     if (!stayhere) return;
712
713     /* Change table layout because we have a span, or the row has HALIGN. */
714     OutputString("^.T&^", fp, 1);
715     basev = Split(basemodel, 0, S_ALVEC|S_STRDUP);
716
717     for (i=0; i<TheTab.nc; i++) {
718
719         lastcol = !(i < TheTab.nc-1);
720         if (spaninfo[i] == SPAN_START) {
721             tbl_cell_fmt(&RowInfo, i, lastcol, "c ", fp);
722         }
723         else if (spaninfo[i] == SPAN_CONT) {
724             /* See if next col is NOT spanned, and we're not in last col */
725             OutputString("s", fp, 1);
726             if (!lastcol && spaninfo[i+1] != SPAN_CONT) {
727                 if (RowInfo.colsep) cp = RowInfo.colsep_v[i];
728                 else if (TheTab.colsep) cp = TheTab.colsep_v[i];
729                 else cp = "1";
730
731                 if (*cp == '1')
732                     OutputString("|", fp, 1);
733                 if (*cp == '2')
734                     OutputString("||", fp, 1);
735             }
736             OutputString(" ", fp, 1);
737         }
738         else
739             tbl_cell_fmt(&RowInfo, i, lastcol, "l ", fp);
740     }
741     OutputString("^", fp, 1);
742     OutputString(basemodel, fp, 1);
743     OutputString(".^", fp, 1);
744     free(*basev);
745     free(basev);
746     FreeTabAtts(&RowInfo);
747 }
748
749 /*
750  *  Arguments:
751  *      Pointer to element (row) under consideration.
752  *      FILE pointer to where to write output.
753  */
754 void
755 TblTableRowEnd(
756     Element_t   *e,
757     FILE        *fp
758 )
759 {
760     char        *at;
761
762     /* See if we're on the last row, then if we're putting a frame
763      * around the whole table.  If so, we need no bottom separator. */
764     if ((e->parent->necont-1) == e->my_eorder) {
765         if (frameall || framebot) return;
766     }
767     /* check this row's attributes */
768     if ((at = FindAttValByName(e, "ROWSEP"))) {
769         if (at[0] == '1') fprintf(fp, "_\n");
770     }
771     else if (rowsep) /* fprintf(fp, "_\n") */ ;
772 }
773
774 /*
775  *  Arguments:
776  *      Pointer to element (table) under consideration.
777  *      FILE pointer to where to write output.
778  */
779 void
780 TblTableTop(Element_t *e, FILE *fp)
781 {
782     if (frametop) OutputString("^_^", fp, 1);
783 }
784
785 void
786 TblTableBottom(Element_t *e, FILE *fp)
787 {
788     if (framebot) OutputString("^_^", fp, 1);
789 }
790
791 /* ______________________________________________________________________ */
792 /* Do the "right thing" for the table spec for TeX tables.  This will
793  * generate the arg to \begin{tabular}[xxx].
794  *  Arguments:
795  *      Pointer to element (table) under consideration.
796  *      FILE pointer to where to write output.
797  */
798 void
799 TexTable(
800     Element_t   *e,
801     FILE        *fp
802 )
803 {
804     int         i, n;
805     float       tot;
806     char        *cp, wbuf[1500], **widths=0, **widths_v=0;
807
808     FreeTabAtts(&TheTab);       /* free storage, if allocated earlier */
809     SetTabAtts(e, &TheTab, 1);  /* look at attributes */
810
811     /* Figure out the widths, based either on "colwidth" or "colweight".
812      * (we pick width over weight if both are specified). */
813     if (TheTab.colwidth && TheTab.nc == TheTab.n_colwidth) {
814         widths = TheTab.colwidth_v;
815     }
816     else if (TheTab.colweight && TheTab.nc == TheTab.n_colweight) {
817         for (n=0,i=0; i<TheTab.nc; i++) n += atoi(TheTab.colweight_v[i]);
818         tot = (float)n;
819         cp = wbuf;
820         for (i=0; i<TheTab.nc; i++) {
821             sprintf(cp, "%5.3fin", atof(TheTab.colweight_v[i])*(TEXTWIDTH/tot));
822             while (*cp) cp++;
823             *cp++ = ' ';
824         }
825         *cp = EOS;
826         widths_v = Split(wbuf, 0, S_ALVEC);
827         widths = widths_v;
828     }
829     siderules = 1;
830     if (TheTab.frame)
831         if (strcmp(TheTab.frame, "ALL") && strcmp(TheTab.frame, "SIDES"))
832             siderules = 0;
833
834     if (siderules) OutputString("|", fp, 1);
835     for (i=0; i<TheTab.nc; i++) {
836         /* If width specified, use it; else if halign set, use it; else left. */
837         if (widths && widths[i][0] != '0' && widths[i][1] != EOS) {
838             fprintf(fp, "%sp{%s}", (i?" ":""), widths[i]);
839         }
840         else if (TheTab.halign && TheTab.nc == TheTab.n_halign) {
841             fprintf(fp, "%s%s", (i?" ":""), TheTab.halign_v[i]);
842         }
843         else
844             fprintf(fp, "%sl", (i?" ":""));
845         /* See if we want column separators. */
846         if (TheTab.colsep) {
847
848             if ( (i+1) < TheTab.nc ) {
849                 if ( *TheTab.colsep_v[i] == '1' ) {
850                     fprintf(fp, " |");
851                 }
852                 if ( *TheTab.colsep_v[i] == '2' ) {
853                     fprintf(fp, " ||");
854                 }
855             }
856
857         }
858     }
859     if (siderules) OutputString("|", fp, 1);
860
861     if (widths_v) free(widths_v);
862 }
863
864 /*
865  *  Arguments:
866  *      Pointer to element (cell) under consideration.
867  *      FILE pointer to where to write output.
868  */
869 void
870 TexTableCellStart(
871     Element_t   *e,
872     FILE        *fp
873 )
874 {
875     int         n, i;
876     char        buf[50], *at;
877
878     if (spaninfo[e->my_eorder] == SPAN_START) {
879         for (i=e->my_eorder+1,n=1; ; i++) {
880             if (spaninfo[i] == SPAN_CONT) n++;
881             else break;
882         }
883         sprintf(buf, "\\\\multicolumn{%d}{%sc%s}", n,
884                 (siderules?"|":""), (siderules?"|":""));
885         OutputString(buf, fp, 1);
886     }
887 #ifdef New
888     if ((at = FindAttValByName(e->parent, "HALIGN"))) {
889         /* no span, but user wants to change the alignment */
890         h_v = Split(wbuf, 0, S_ALVEC|S_STRDUP);
891         OutputString("\\\\multicolumn{1}{%sc%s}", n,
892                 fp, 1);
893     }
894 #endif
895
896     if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("{", fp, 1);
897 }
898
899 /*
900  *  Arguments:
901  *      Pointer to element (cell) under consideration.
902  *      FILE pointer to where to write output.
903  */
904 void
905 TexTableCellEnd(
906     Element_t   *e,
907     FILE        *fp
908 )
909 {
910     if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("} ", fp, 1);
911
912     /* do cell/col separators */
913     if (e->my_eorder < (TheTab.nc-1)) {
914         if (spaninfo[e->my_eorder] == SPAN_NOT ||
915                         spaninfo[e->my_eorder+1] != SPAN_CONT)
916             OutputString("& ", fp, 1);
917     }
918 }
919
920 /*  Look at model for spanning.  If set, remember it for when doing the cells.
921  *  Arguments:
922  *      Pointer to element (row) under consideration.
923  *      FILE pointer to where to write output.
924  */
925 void
926 TexTableRowStart(
927     Element_t   *e,
928     FILE        *fp
929 )
930 {
931     check_for_spans(e);
932 }
933
934 /*
935  *  Arguments:
936  *      Pointer to element (row) under consideration.
937  *      FILE pointer to where to write output.
938  */
939 void
940 TexTableRowEnd(
941     Element_t   *e,
942     FILE        *fp
943 )
944 {
945     char        *at;
946
947     /* check this row's attributes */
948     if ((at = FindAttValByName(e, "ROWSEP"))) {
949         if (at[0] == '1') OutputString("\\\\\\\\[2mm] \\\\hline ", fp, 1);
950     }
951     else if (rowsep) OutputString("\\\\\\\\ ", fp, 1);
952     else 
953         OutputString("\\\\\\\\ ", fp, 1);
954
955 }
956
957 /*
958  *  Arguments:
959  *      Pointer to element (table) under consideration.
960  *      FILE pointer to where to write output.
961  */
962 void
963 TexTableTop(Element_t *e, FILE *fp)
964 {
965     if (frametop) OutputString("\\\\hline", fp, 1);
966 }
967
968 void
969 TexTableBottom(Element_t *e, FILE *fp)
970 {
971     if (framebot) OutputString("\\\\hline", fp, 1);
972 }
973
974 /* ______________________________________________________________________ */
975