1*1076333cSespie /* multi.c -- multiple-column tables (@multitable) for makeinfo.
2*1076333cSespie $Id: multi.c,v 1.7 2006/07/17 16:12:36 espie Exp $
340248eceSdownsj
4*1076333cSespie Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004 Free Software
5*1076333cSespie Foundation, Inc.
640248eceSdownsj
740248eceSdownsj This program is free software; you can redistribute it and/or modify
840248eceSdownsj it under the terms of the GNU General Public License as published by
940248eceSdownsj the Free Software Foundation; either version 2, or (at your option)
1040248eceSdownsj any later version.
1140248eceSdownsj
1240248eceSdownsj This program is distributed in the hope that it will be useful,
1340248eceSdownsj but WITHOUT ANY WARRANTY; without even the implied warranty of
1440248eceSdownsj MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1540248eceSdownsj GNU General Public License for more details.
1640248eceSdownsj
1740248eceSdownsj You should have received a copy of the GNU General Public License
1840248eceSdownsj along with this program; if not, write to the Free Software Foundation,
193aa90977Sespie Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
203aa90977Sespie
21*1076333cSespie Originally written by phr@gnu.org (Paul Rubin). */
2240248eceSdownsj
23840175f0Skstailey #include "system.h"
24*1076333cSespie #include "cmds.h"
25672dff93Sespie #include "insertion.h"
2640248eceSdownsj #include "makeinfo.h"
27*1076333cSespie #include "multi.h"
283aa90977Sespie #include "xml.h"
2940248eceSdownsj
3040248eceSdownsj #define MAXCOLS 100 /* remove this limit later @@ */
3140248eceSdownsj
3240248eceSdownsj
3340248eceSdownsj /*
3440248eceSdownsj * Output environments. This is a hack grafted onto existing
3540248eceSdownsj * structure. The "output environment" used to consist of the
3640248eceSdownsj * global variables `output_paragraph', `fill_column', etc.
3740248eceSdownsj * Routines like add_char would manipulate these variables.
3840248eceSdownsj *
3940248eceSdownsj * Now, when formatting a multitable, we maintain separate environments
4040248eceSdownsj * for each column. That way we can build up the columns separately
4140248eceSdownsj * and write them all out at once. The "current" output environment"
4240248eceSdownsj * is still kept in those global variables, so that the old output
4340248eceSdownsj * routines don't have to change. But we provide routines to save
4440248eceSdownsj * and restore these variables in an "environment table". The
4540248eceSdownsj * `select_output_environment' function switches from one output
4640248eceSdownsj * environment to another.
4740248eceSdownsj *
48840175f0Skstailey * Environment #0 (i.e., element #0 of the table) is the regular
4940248eceSdownsj * environment that is used when we're not formatting a multitable.
5040248eceSdownsj *
5140248eceSdownsj * Environment #N (where N = 1,2,3,...) is the env. for column #N of
5240248eceSdownsj * the table, when a multitable is active.
5340248eceSdownsj */
5440248eceSdownsj
5540248eceSdownsj /* contents of an output environment */
5640248eceSdownsj /* some more vars may end up being needed here later @@ */
5740248eceSdownsj struct env
5840248eceSdownsj {
5940248eceSdownsj unsigned char *output_paragraph;
6040248eceSdownsj int output_paragraph_offset;
61672dff93Sespie int meta_char_pos;
6240248eceSdownsj int output_column;
6340248eceSdownsj int paragraph_is_open;
6440248eceSdownsj int current_indent;
6540248eceSdownsj int fill_column;
6640248eceSdownsj } envs[MAXCOLS]; /* the environment table */
6740248eceSdownsj
6840248eceSdownsj /* index in environment table of currently selected environment */
6940248eceSdownsj static int current_env_no;
7040248eceSdownsj
71*1076333cSespie /* current column number */
72*1076333cSespie static int current_column_no;
73*1076333cSespie
74*1076333cSespie /* We need to make a difference between template based widths and
75*1076333cSespie @columnfractions for HTML tables' sake. Sigh. */
76*1076333cSespie static int seen_column_fractions;
77*1076333cSespie
7840248eceSdownsj /* column number of last column in current multitable */
7940248eceSdownsj static int last_column;
8040248eceSdownsj
8140248eceSdownsj /* flags indicating whether horizontal and vertical separators need
8240248eceSdownsj to be drawn, separating rows and columns in the current multitable. */
8340248eceSdownsj static int hsep, vsep;
84672dff93Sespie
85672dff93Sespie /* whether this is the first row. */
86672dff93Sespie static int first_row;
8740248eceSdownsj
88672dff93Sespie /* Called to handle a {...} template on the @multitable line.
89672dff93Sespie We're at the { and our first job is to find the matching }; as a side
90672dff93Sespie effect, we change *PARAMS to point to after it. Our other job is to
91672dff93Sespie expand the template text and return the width of that string. */
92672dff93Sespie static unsigned
find_template_width(char ** params)93*1076333cSespie find_template_width (char **params)
94672dff93Sespie {
95672dff93Sespie char *template, *xtemplate;
96672dff93Sespie unsigned len;
97672dff93Sespie char *start = *params;
98672dff93Sespie int brace_level = 0;
99672dff93Sespie
100672dff93Sespie /* The first character should be a {. */
101672dff93Sespie if (!params || !*params || **params != '{')
102672dff93Sespie {
103672dff93Sespie line_error ("find_template width internal error: passed %s",
104672dff93Sespie params ? *params : "null");
105672dff93Sespie return 0;
106672dff93Sespie }
107672dff93Sespie
108672dff93Sespie do
109672dff93Sespie {
1103aa90977Sespie if (**params == '{' && (*params == start || (*params)[-1] != '@'))
111672dff93Sespie brace_level++;
112672dff93Sespie else if (**params == '}' && (*params)[-1] != '@')
113672dff93Sespie brace_level--;
114672dff93Sespie else if (**params == 0)
115672dff93Sespie {
116672dff93Sespie line_error (_("Missing } in @multitable template"));
117672dff93Sespie return 0;
118672dff93Sespie }
119672dff93Sespie (*params)++;
120672dff93Sespie }
121672dff93Sespie while (brace_level > 0);
122672dff93Sespie
123672dff93Sespie template = substring (start + 1, *params - 1); /* omit braces */
124672dff93Sespie xtemplate = expansion (template, 0);
125672dff93Sespie len = strlen (xtemplate);
126672dff93Sespie
127672dff93Sespie free (template);
128672dff93Sespie free (xtemplate);
129672dff93Sespie
130672dff93Sespie return len;
131672dff93Sespie }
132672dff93Sespie
13340248eceSdownsj /* Direct current output to environment number N. Used when
13440248eceSdownsj switching work from one column of a multitable to the next.
13540248eceSdownsj Returns previous environment number. */
136*1076333cSespie static int
select_output_environment(int n)137*1076333cSespie select_output_environment (int n)
13840248eceSdownsj {
13940248eceSdownsj struct env *e = &envs[current_env_no];
14040248eceSdownsj int old_env_no = current_env_no;
14140248eceSdownsj
14240248eceSdownsj /* stash current env info from global vars into the old environment */
14340248eceSdownsj e->output_paragraph = output_paragraph;
14440248eceSdownsj e->output_paragraph_offset = output_paragraph_offset;
145672dff93Sespie e->meta_char_pos = meta_char_pos;
14640248eceSdownsj e->output_column = output_column;
14740248eceSdownsj e->paragraph_is_open = paragraph_is_open;
14840248eceSdownsj e->current_indent = current_indent;
14940248eceSdownsj e->fill_column = fill_column;
15040248eceSdownsj
15140248eceSdownsj /* now copy new environment into global vars */
15240248eceSdownsj current_env_no = n;
15340248eceSdownsj e = &envs[current_env_no];
15440248eceSdownsj output_paragraph = e->output_paragraph;
15540248eceSdownsj output_paragraph_offset = e->output_paragraph_offset;
156672dff93Sespie meta_char_pos = e->meta_char_pos;
15740248eceSdownsj output_column = e->output_column;
15840248eceSdownsj paragraph_is_open = e->paragraph_is_open;
15940248eceSdownsj current_indent = e->current_indent;
16040248eceSdownsj fill_column = e->fill_column;
16140248eceSdownsj return old_env_no;
16240248eceSdownsj }
16340248eceSdownsj
164*1076333cSespie /* Initialize environment number ENV_NO, of width WIDTH.
165*1076333cSespie The idea is that we're going to use one environment for each column of
166*1076333cSespie a multitable, so we can build them up separately and print them
167*1076333cSespie all out at the end. */
168*1076333cSespie static int
setup_output_environment(int env_no,int width)169*1076333cSespie setup_output_environment (int env_no, int width)
170*1076333cSespie {
171*1076333cSespie int old_env = select_output_environment (env_no);
172*1076333cSespie
173*1076333cSespie /* clobber old environment and set width of new one */
174*1076333cSespie init_paragraph ();
175*1076333cSespie
176*1076333cSespie /* make our change */
177*1076333cSespie fill_column = width;
178*1076333cSespie
179*1076333cSespie /* Save new environment and restore previous one. */
180*1076333cSespie select_output_environment (old_env);
181*1076333cSespie
182*1076333cSespie return env_no;
183*1076333cSespie }
184*1076333cSespie
185*1076333cSespie /* Read the parameters for a multitable from the current command
186*1076333cSespie line, save the parameters away, and return the
187*1076333cSespie number of columns. */
188*1076333cSespie static int
setup_multitable_parameters(void)189*1076333cSespie setup_multitable_parameters (void)
190*1076333cSespie {
191*1076333cSespie char *params = insertion_stack->item_function;
192*1076333cSespie int nchars;
193*1076333cSespie float columnfrac;
194*1076333cSespie char command[200]; /* xx no fixed limits */
195*1076333cSespie int i = 1;
196*1076333cSespie
197*1076333cSespie /* We implement @hsep and @vsep even though TeX doesn't.
198*1076333cSespie We don't get mixing of @columnfractions and templates right,
199*1076333cSespie but TeX doesn't either. */
200*1076333cSespie hsep = vsep = 0;
201*1076333cSespie
202*1076333cSespie /* Assume no @columnfractions per default. */
203*1076333cSespie seen_column_fractions = 0;
204*1076333cSespie
205*1076333cSespie while (*params) {
206*1076333cSespie while (whitespace (*params))
207*1076333cSespie params++;
208*1076333cSespie
209*1076333cSespie if (*params == '@') {
210*1076333cSespie sscanf (params, "%199s", command);
211*1076333cSespie nchars = strlen (command);
212*1076333cSespie params += nchars;
213*1076333cSespie if (strcmp (command, "@hsep") == 0)
214*1076333cSespie hsep++;
215*1076333cSespie else if (strcmp (command, "@vsep") == 0)
216*1076333cSespie vsep++;
217*1076333cSespie else if (strcmp (command, "@columnfractions") == 0) {
218*1076333cSespie seen_column_fractions = 1;
219*1076333cSespie /* Clobber old environments and create new ones, starting at #1.
220*1076333cSespie Environment #0 is the normal output, so don't mess with it. */
221*1076333cSespie for ( ; i <= MAXCOLS; i++) {
222*1076333cSespie if (sscanf (params, "%f", &columnfrac) < 1)
223*1076333cSespie goto done;
224*1076333cSespie /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least)
225*1076333cSespie doesn't support it. So skip whitespace (preceding the
226*1076333cSespie number) and then non-whitespace (the number). */
227*1076333cSespie while (*params && (*params == ' ' || *params == '\t'))
228*1076333cSespie params++;
229*1076333cSespie /* Hmm, but what about @columnfractions 3foo. Oh well,
230*1076333cSespie it's invalid input anyway. */
231*1076333cSespie while (*params && *params != ' ' && *params != '\t'
232*1076333cSespie && *params != '\n' && *params != '@')
233*1076333cSespie params++;
234*1076333cSespie
235*1076333cSespie {
236*1076333cSespie /* For html/xml/docbook, translate fractions into integer
237*1076333cSespie percentages, adding .005 to avoid rounding problems. For
238*1076333cSespie info, we want the character width. */
239*1076333cSespie int width = xml || html ? (columnfrac + .005) * 100
240*1076333cSespie : (columnfrac * (fill_column - current_indent) + .5);
241*1076333cSespie setup_output_environment (i, width);
242*1076333cSespie }
243*1076333cSespie }
244*1076333cSespie }
245*1076333cSespie
246*1076333cSespie } else if (*params == '{') {
247*1076333cSespie unsigned template_width = find_template_width (¶ms);
248*1076333cSespie
249*1076333cSespie /* This gives us two spaces between columns. Seems reasonable.
250*1076333cSespie How to take into account current_indent here? */
251*1076333cSespie setup_output_environment (i++, template_width + 2);
252*1076333cSespie
253*1076333cSespie } else {
254*1076333cSespie warning (_("ignoring stray text `%s' after @multitable"), params);
255*1076333cSespie break;
256*1076333cSespie }
257*1076333cSespie }
258*1076333cSespie
259*1076333cSespie done:
260*1076333cSespie flush_output ();
261*1076333cSespie inhibit_output_flushing ();
262*1076333cSespie
263*1076333cSespie last_column = i - 1;
264*1076333cSespie return last_column;
265*1076333cSespie }
266*1076333cSespie
267*1076333cSespie /* Output a row. Calls insert, but also flushes the buffered output
268*1076333cSespie when we see a newline, since in multitable every line is a separate
269*1076333cSespie paragraph. */
270*1076333cSespie static void
out_char(int ch)271*1076333cSespie out_char (int ch)
272*1076333cSespie {
273*1076333cSespie if (html || xml)
274*1076333cSespie add_char (ch);
275*1076333cSespie else
276*1076333cSespie {
277*1076333cSespie int env = select_output_environment (0);
278*1076333cSespie insert (ch);
279*1076333cSespie if (ch == '\n')
280*1076333cSespie {
281*1076333cSespie uninhibit_output_flushing ();
282*1076333cSespie flush_output ();
283*1076333cSespie inhibit_output_flushing ();
284*1076333cSespie }
285*1076333cSespie select_output_environment (env);
286*1076333cSespie }
287*1076333cSespie }
288*1076333cSespie
289*1076333cSespie
290*1076333cSespie static void
draw_horizontal_separator(void)291*1076333cSespie draw_horizontal_separator (void)
292*1076333cSespie {
293*1076333cSespie int i, j, s;
294*1076333cSespie
295*1076333cSespie if (html)
296*1076333cSespie {
297*1076333cSespie add_word ("<hr>");
298*1076333cSespie return;
299*1076333cSespie }
300*1076333cSespie if (xml)
301*1076333cSespie return;
302*1076333cSespie
303*1076333cSespie for (s = 0; s < envs[0].current_indent; s++)
304*1076333cSespie out_char (' ');
305*1076333cSespie if (vsep)
306*1076333cSespie out_char ('+');
307*1076333cSespie for (i = 1; i <= last_column; i++) {
308*1076333cSespie for (j = 0; j <= envs[i].fill_column; j++)
309*1076333cSespie out_char ('-');
310*1076333cSespie if (vsep)
311*1076333cSespie out_char ('+');
312*1076333cSespie }
313*1076333cSespie out_char (' ');
314*1076333cSespie out_char ('\n');
315*1076333cSespie }
316*1076333cSespie
317*1076333cSespie
318*1076333cSespie /* multitable strategy:
319*1076333cSespie for each item {
320*1076333cSespie for each column in an item {
321*1076333cSespie initialize a new paragraph
322*1076333cSespie do ordinary formatting into the new paragraph
323*1076333cSespie save the paragraph away
324*1076333cSespie repeat if there are more paragraphs in the column
325*1076333cSespie }
326*1076333cSespie dump out the saved paragraphs and free the storage
327*1076333cSespie }
328*1076333cSespie
329*1076333cSespie For HTML we construct a simple HTML 3.2 table with <br>s inserted
330*1076333cSespie to help non-tables browsers. `@item' inserts a <tr> and `@tab'
331*1076333cSespie inserts <td>; we also try to close <tr>. The only real
332*1076333cSespie alternative is to rely on the info formatting engine and present
333*1076333cSespie preformatted text. */
334*1076333cSespie
335840175f0Skstailey void
do_multitable(void)336*1076333cSespie do_multitable (void)
337*1076333cSespie {
338*1076333cSespie int ncolumns;
339*1076333cSespie
340*1076333cSespie if (multitable_active)
341*1076333cSespie {
342*1076333cSespie line_error ("Multitables cannot be nested");
343*1076333cSespie return;
344*1076333cSespie }
345*1076333cSespie
346*1076333cSespie close_single_paragraph ();
347*1076333cSespie
348*1076333cSespie if (xml)
349*1076333cSespie {
350*1076333cSespie xml_no_para = 1;
351*1076333cSespie if (output_paragraph[output_paragraph_offset-1] == '\n')
352*1076333cSespie output_paragraph_offset--;
353*1076333cSespie }
354*1076333cSespie
355*1076333cSespie /* scan the current item function to get the field widths
356*1076333cSespie and number of columns, and set up the output environment list
357*1076333cSespie accordingly. */
358*1076333cSespie ncolumns = setup_multitable_parameters ();
359*1076333cSespie first_row = 1;
360*1076333cSespie
361*1076333cSespie /* <p> for non-tables browsers. @multitable implicitly ends the
362*1076333cSespie current paragraph, so this is ok. */
363*1076333cSespie if (html)
364*1076333cSespie add_html_block_elt ("<p><table summary=\"\">");
365*1076333cSespie /* else if (docbook)*/ /* 05-08 */
366*1076333cSespie else if (xml)
367*1076333cSespie {
368*1076333cSespie int *widths = xmalloc (ncolumns * sizeof (int));
369*1076333cSespie int i;
370*1076333cSespie for (i=0; i<ncolumns; i++)
371*1076333cSespie widths[i] = envs[i+1].fill_column;
372*1076333cSespie xml_begin_multitable (ncolumns, widths);
373*1076333cSespie free (widths);
374*1076333cSespie }
375*1076333cSespie
376*1076333cSespie if (hsep)
377*1076333cSespie draw_horizontal_separator ();
378*1076333cSespie
379*1076333cSespie /* The next @item command will direct stdout into the first column
380*1076333cSespie and start processing. @tab will then switch to the next column,
381*1076333cSespie and @item will flush out the saved output and return to the first
382*1076333cSespie column. Environment #1 is the first column. (Environment #0 is
383*1076333cSespie the normal output) */
384*1076333cSespie
385*1076333cSespie ++multitable_active;
386*1076333cSespie }
387*1076333cSespie
388*1076333cSespie /* advance to the next environment number */
389*1076333cSespie static void
nselect_next_environment(void)390*1076333cSespie nselect_next_environment (void)
39140248eceSdownsj {
39240248eceSdownsj if (current_env_no >= last_column) {
393840175f0Skstailey line_error (_("Too many columns in multitable item (max %d)"), last_column);
394840175f0Skstailey return;
39540248eceSdownsj }
39640248eceSdownsj select_output_environment (current_env_no + 1);
39740248eceSdownsj }
39840248eceSdownsj
39940248eceSdownsj
40040248eceSdownsj /* do anything needed at the beginning of processing a
40140248eceSdownsj multitable column. */
402*1076333cSespie static void
init_column(void)403*1076333cSespie init_column (void)
40440248eceSdownsj {
40540248eceSdownsj /* don't indent 1st paragraph in the item */
40640248eceSdownsj cm_noindent ();
40740248eceSdownsj
40840248eceSdownsj /* throw away possible whitespace after @item or @tab command */
40940248eceSdownsj skip_whitespace ();
41040248eceSdownsj }
41140248eceSdownsj
41240248eceSdownsj static void
output_multitable_row(void)413*1076333cSespie output_multitable_row (void)
41440248eceSdownsj {
41540248eceSdownsj /* offset in the output paragraph of the next char needing
41640248eceSdownsj to be output for that column. */
41740248eceSdownsj int offset[MAXCOLS];
418672dff93Sespie int i, j, s, remaining;
419672dff93Sespie int had_newline = 0;
42040248eceSdownsj
42140248eceSdownsj for (i = 0; i <= last_column; i++)
42240248eceSdownsj offset[i] = 0;
42340248eceSdownsj
42440248eceSdownsj /* select the current environment, to make sure the env variables
42540248eceSdownsj get updated */
42640248eceSdownsj select_output_environment (current_env_no);
42740248eceSdownsj
42840248eceSdownsj #define CHAR_ADDR(n) (offset[i] + (n))
42940248eceSdownsj #define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)])
43040248eceSdownsj
43140248eceSdownsj /* remove trailing whitespace from each column */
43240248eceSdownsj for (i = 1; i <= last_column; i++) {
4336521fac2Sotto while (envs[i].output_paragraph_offset &&
4346521fac2Sotto cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1)))
43540248eceSdownsj envs[i].output_paragraph_offset--;
436672dff93Sespie
437672dff93Sespie if (i == current_env_no)
438672dff93Sespie output_paragraph_offset = envs[i].output_paragraph_offset;
43940248eceSdownsj }
44040248eceSdownsj
44140248eceSdownsj /* read the current line from each column, outputting them all
44240248eceSdownsj pasted together. Do this til all lines are output from all
44340248eceSdownsj columns. */
44440248eceSdownsj for (;;) {
44540248eceSdownsj remaining = 0;
44640248eceSdownsj /* first, see if there is any work to do */
44740248eceSdownsj for (i = 1; i <= last_column; i++) {
44840248eceSdownsj if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) {
44940248eceSdownsj remaining = 1;
45040248eceSdownsj break;
45140248eceSdownsj }
45240248eceSdownsj }
45340248eceSdownsj if (!remaining)
45440248eceSdownsj break;
45540248eceSdownsj
456840175f0Skstailey for (s = 0; s < envs[0].current_indent; s++)
457840175f0Skstailey out_char (' ');
458840175f0Skstailey
45940248eceSdownsj if (vsep)
46040248eceSdownsj out_char ('|');
46140248eceSdownsj
46240248eceSdownsj for (i = 1; i <= last_column; i++) {
463672dff93Sespie for (s = 0; s < envs[i].current_indent; s++)
464840175f0Skstailey out_char (' ');
46540248eceSdownsj for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) {
46640248eceSdownsj if (CHAR_AT (j) == '\n')
46740248eceSdownsj break;
46840248eceSdownsj out_char (CHAR_AT (j));
46940248eceSdownsj }
47040248eceSdownsj offset[i] += j + 1; /* skip last text plus skip the newline */
471672dff93Sespie
472672dff93Sespie /* Do not output trailing blanks if we're in the last column and
473672dff93Sespie there will be no trailing |. */
474672dff93Sespie if (i < last_column && !vsep)
47540248eceSdownsj for (; j <= envs[i].fill_column; j++)
47640248eceSdownsj out_char (' ');
47740248eceSdownsj if (vsep)
47840248eceSdownsj out_char ('|'); /* draw column separator */
47940248eceSdownsj }
48040248eceSdownsj out_char ('\n'); /* end of line */
481672dff93Sespie had_newline = 1;
48240248eceSdownsj }
48340248eceSdownsj
484672dff93Sespie /* If completely blank item, get blank line despite no other output. */
485672dff93Sespie if (!had_newline)
486672dff93Sespie out_char ('\n'); /* end of line */
487672dff93Sespie
48840248eceSdownsj if (hsep)
48940248eceSdownsj draw_horizontal_separator ();
49040248eceSdownsj
49140248eceSdownsj /* Now dispose of the buffered output. */
49240248eceSdownsj for (i = 1; i <= last_column; i++) {
49340248eceSdownsj select_output_environment (i);
49440248eceSdownsj init_paragraph ();
49540248eceSdownsj }
49640248eceSdownsj }
49740248eceSdownsj
498*1076333cSespie int after_headitem = 0;
499*1076333cSespie int headitem_row = 0;
500*1076333cSespie
501*1076333cSespie /* start a new item (row) of a multitable */
502*1076333cSespie int
multitable_item(void)503*1076333cSespie multitable_item (void)
504*1076333cSespie {
505*1076333cSespie if (!multitable_active) {
506*1076333cSespie line_error ("multitable_item internal error: no active multitable");
507*1076333cSespie xexit (1);
508*1076333cSespie }
509*1076333cSespie
510*1076333cSespie current_column_no = 1;
511*1076333cSespie
512*1076333cSespie if (html)
513*1076333cSespie {
514*1076333cSespie if (!first_row)
515*1076333cSespie /* <br> for non-tables browsers. */
516*1076333cSespie add_word_args ("<br></%s></tr>", after_headitem ? "th" : "td");
517*1076333cSespie
518*1076333cSespie if (seen_column_fractions)
519*1076333cSespie add_word_args ("<tr align=\"left\"><%s valign=\"top\" width=\"%d%%\">",
520*1076333cSespie headitem_flag ? "th" : "td",
521*1076333cSespie envs[current_column_no].fill_column);
522*1076333cSespie else
523*1076333cSespie add_word_args ("<tr align=\"left\"><%s valign=\"top\">",
524*1076333cSespie headitem_flag ? "th" : "td");
525*1076333cSespie
526*1076333cSespie if (headitem_flag)
527*1076333cSespie after_headitem = 1;
528*1076333cSespie else
529*1076333cSespie after_headitem = 0;
530*1076333cSespie first_row = 0;
531*1076333cSespie headitem_row = headitem_flag;
532*1076333cSespie headitem_flag = 0;
533*1076333cSespie return 0;
534*1076333cSespie }
535*1076333cSespie /* else if (docbook)*/ /* 05-08 */
536*1076333cSespie else if (xml)
537*1076333cSespie {
538*1076333cSespie xml_end_multitable_row (first_row);
539*1076333cSespie if (headitem_flag)
540*1076333cSespie after_headitem = 1;
541*1076333cSespie else
542*1076333cSespie after_headitem = 0;
543*1076333cSespie first_row = 0;
544*1076333cSespie headitem_flag = 0;
545*1076333cSespie return 0;
546*1076333cSespie }
547*1076333cSespie first_row = 0;
548*1076333cSespie
549*1076333cSespie if (current_env_no > 0) {
550*1076333cSespie output_multitable_row ();
551*1076333cSespie }
552*1076333cSespie /* start at column 1 */
553*1076333cSespie select_output_environment (1);
554*1076333cSespie if (!output_paragraph) {
555*1076333cSespie line_error (_("[unexpected] cannot select column #%d in multitable"),
556*1076333cSespie current_env_no);
557*1076333cSespie xexit (1);
558*1076333cSespie }
559*1076333cSespie
560*1076333cSespie init_column ();
561*1076333cSespie
562*1076333cSespie if (headitem_flag)
563*1076333cSespie hsep = 1;
564*1076333cSespie else
565*1076333cSespie hsep = 0;
566*1076333cSespie
567*1076333cSespie if (headitem_flag)
568*1076333cSespie after_headitem = 1;
569*1076333cSespie else
570*1076333cSespie after_headitem = 0;
571*1076333cSespie headitem_flag = 0;
572*1076333cSespie
573*1076333cSespie return 0;
574*1076333cSespie }
575*1076333cSespie
57640248eceSdownsj #undef CHAR_AT
57740248eceSdownsj #undef CHAR_ADDR
57840248eceSdownsj
57940248eceSdownsj /* select a new column in current row of multitable */
58040248eceSdownsj void
cm_tab(void)581*1076333cSespie cm_tab (void)
58240248eceSdownsj {
58340248eceSdownsj if (!multitable_active)
584840175f0Skstailey error (_("ignoring @tab outside of multitable"));
58540248eceSdownsj
586*1076333cSespie current_column_no++;
587*1076333cSespie
588672dff93Sespie if (html)
589*1076333cSespie {
590*1076333cSespie if (seen_column_fractions)
591*1076333cSespie add_word_args ("</%s><%s valign=\"top\" width=\"%d%%\">",
592*1076333cSespie headitem_row ? "th" : "td",
593*1076333cSespie headitem_row ? "th" : "td",
594*1076333cSespie envs[current_column_no].fill_column);
595*1076333cSespie else
596*1076333cSespie add_word_args ("</%s><%s valign=\"top\">",
597*1076333cSespie headitem_row ? "th" : "td",
598*1076333cSespie headitem_row ? "th" : "td");
599*1076333cSespie }
6003aa90977Sespie /* else if (docbook)*/ /* 05-08 */
6013aa90977Sespie else if (xml)
6023aa90977Sespie xml_end_multitable_column ();
603672dff93Sespie else
60440248eceSdownsj nselect_next_environment ();
605672dff93Sespie
60640248eceSdownsj init_column ();
60740248eceSdownsj }
60840248eceSdownsj
60940248eceSdownsj /* close a multitable, flushing its output and resetting
61040248eceSdownsj whatever needs resetting */
61140248eceSdownsj void
end_multitable(void)612*1076333cSespie end_multitable (void)
61340248eceSdownsj {
6143aa90977Sespie if (!html && !docbook)
61540248eceSdownsj output_multitable_row ();
61640248eceSdownsj
61740248eceSdownsj /* Multitables cannot be nested. Otherwise, we'd have to save the
61840248eceSdownsj previous output environment number on a stack somewhere, and then
61940248eceSdownsj restore to that environment. */
62040248eceSdownsj select_output_environment (0);
62140248eceSdownsj multitable_active = 0;
62240248eceSdownsj uninhibit_output_flushing ();
623672dff93Sespie close_insertion_paragraph ();
624672dff93Sespie
625672dff93Sespie if (html)
626*1076333cSespie add_word_args ("<br></%s></tr></table>\n", headitem_row ? "th" : "td");
6273aa90977Sespie /* else if (docbook)*/ /* 05-08 */
6283aa90977Sespie else if (xml)
6293aa90977Sespie xml_end_multitable ();
63040248eceSdownsj
63140248eceSdownsj #if 0
632840175f0Skstailey printf (_("** Multicolumn output from last row:\n"));
63340248eceSdownsj for (i = 1; i <= last_column; i++) {
63440248eceSdownsj select_output_environment (i);
635840175f0Skstailey printf (_("* column #%d: output = %s\n"), i, output_paragraph);
63640248eceSdownsj }
63740248eceSdownsj #endif
63840248eceSdownsj }
639