189857Sobrien/* multi.c -- multiple-column tables (@multitable) for makeinfo.
2218822Sdim   $Id: multi.c,v 1.8 2004/04/11 17:56:47 karl Exp $
3130561Sobrien
489857Sobrien   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004 Free Software
589857Sobrien   Foundation, Inc.
6130561Sobrien
789857Sobrien   This program is free software; you can redistribute it and/or modify
8130561Sobrien   it under the terms of the GNU General Public License as published by
989857Sobrien   the Free Software Foundation; either version 2, or (at your option)
10130561Sobrien   any later version.
11130561Sobrien
12130561Sobrien   This program is distributed in the hope that it will be useful,
13130561Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1489857Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15130561Sobrien   GNU General Public License for more details.
16130561Sobrien
17130561Sobrien   You should have received a copy of the GNU General Public License
18130561Sobrien   along with this program; if not, write to the Free Software Foundation,
1989857Sobrien   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20130561Sobrien
21130561Sobrien   Originally written by phr@gnu.org (Paul Rubin).  */
22218822Sdim
2389857Sobrien#include "system.h"
24130561Sobrien#include "cmds.h"
25130561Sobrien#include "insertion.h"
26130561Sobrien#include "makeinfo.h"
2789857Sobrien#include "multi.h"
28218822Sdim#include "xml.h"
29218822Sdim
3089857Sobrien#define MAXCOLS 100             /* remove this limit later @@ */
3189857Sobrien
3289857Sobrien
3389857Sobrien/*
34130561Sobrien * Output environments.  This is a hack grafted onto existing
3589857Sobrien * structure.  The "output environment" used to consist of the
3689857Sobrien * global variables `output_paragraph', `fill_column', etc.
3799461Sobrien * Routines like add_char would manipulate these variables.
38130561Sobrien *
39218822Sdim * Now, when formatting a multitable, we maintain separate environments
40218822Sdim * for each column.  That way we can build up the columns separately
4199461Sobrien * and write them all out at once.  The "current" output environment"
42130561Sobrien * is still kept in those global variables, so that the old output
4399461Sobrien * routines don't have to change.  But we provide routines to save
44130561Sobrien * and restore these variables in an "environment table".  The
4599461Sobrien * `select_output_environment' function switches from one output
46130561Sobrien * environment to another.
4799461Sobrien *
48130561Sobrien * Environment #0 (i.e., element #0 of the table) is the regular
4999461Sobrien * environment that is used when we're not formatting a multitable.
50130561Sobrien *
5199461Sobrien * Environment #N (where N = 1,2,3,...) is the env. for column #N of
52130561Sobrien * the table, when a multitable is active.
5399461Sobrien */
54130561Sobrien
55218822Sdim/* contents of an output environment */
56218822Sdim/* some more vars may end up being needed here later @@ */
5789857Sobrienstruct env
58130561Sobrien{
59130561Sobrien  unsigned char *output_paragraph;
60130561Sobrien  int output_paragraph_offset;
61130561Sobrien  int meta_char_pos;
62130561Sobrien  int output_column;
63130561Sobrien  int paragraph_is_open;
64130561Sobrien  int current_indent;
65218822Sdim  int fill_column;
66130561Sobrien} envs[MAXCOLS];                /* the environment table */
67130561Sobrien
68130561Sobrien/* index in environment table of currently selected environment */
69130561Sobrienstatic int current_env_no;
70130561Sobrien
71130561Sobrien/* current column number */
72130561Sobrienstatic int current_column_no;
73130561Sobrien
74130561Sobrien/* We need to make a difference between template based widths and
75130561Sobrien   @columnfractions for HTML tables' sake.  Sigh.  */
76218822Sdimstatic int seen_column_fractions;
77130561Sobrien
78130561Sobrien/* column number of last column in current multitable */
79130561Sobrienstatic int last_column;
80218822Sdim
81130561Sobrien/* flags indicating whether horizontal and vertical separators need
82130561Sobrien   to be drawn, separating rows and columns in the current multitable. */
83130561Sobrienstatic int hsep, vsep;
84130561Sobrien
85218822Sdim/* whether this is the first row. */
86130561Sobrienstatic int first_row;
87130561Sobrien
88130561Sobrien/* Called to handle a {...} template on the @multitable line.
89130561Sobrien   We're at the { and our first job is to find the matching }; as a side
90218822Sdim   effect, we change *PARAMS to point to after it.  Our other job is to
91130561Sobrien   expand the template text and return the width of that string.  */
92130561Sobrienstatic unsigned
93130561Sobrienfind_template_width (char **params)
94218822Sdim{
95218822Sdim  char *template, *xtemplate;
96218822Sdim  unsigned len;
97130561Sobrien  char *start = *params;
98218822Sdim  int brace_level = 0;
99130561Sobrien
100130561Sobrien  /* The first character should be a {.  */
101130561Sobrien  if (!params || !*params || **params != '{')
102130561Sobrien    {
103130561Sobrien      line_error ("find_template width internal error: passed %s",
104130561Sobrien                  params ? *params : "null");
105218822Sdim      return 0;
106218822Sdim    }
107130561Sobrien
108130561Sobrien  do
109130561Sobrien    {
110130561Sobrien      if (**params == '{' && (*params == start || (*params)[-1] != '@'))
111130561Sobrien        brace_level++;
112130561Sobrien      else if (**params == '}' && (*params)[-1] != '@')
113130561Sobrien        brace_level--;
11489857Sobrien      else if (**params == 0)
11589857Sobrien        {
11689857Sobrien          line_error (_("Missing } in @multitable template"));
11789857Sobrien          return 0;
11889857Sobrien        }
11989857Sobrien      (*params)++;
12089857Sobrien    }
12189857Sobrien  while (brace_level > 0);
12289857Sobrien
12389857Sobrien  template = substring (start + 1, *params - 1); /* omit braces */
12489857Sobrien  xtemplate = expansion (template, 0);
125130561Sobrien  len = strlen (xtemplate);
12689857Sobrien
127130561Sobrien  free (template);
128130561Sobrien  free (xtemplate);
129130561Sobrien
130130561Sobrien  return len;
131130561Sobrien}
132130561Sobrien
133130561Sobrien/* Direct current output to environment number N.  Used when
134130561Sobrien   switching work from one column of a multitable to the next.
13589857Sobrien   Returns previous environment number. */
13689857Sobrienstatic int
13789857Sobrienselect_output_environment (int n)
13889857Sobrien{
13989857Sobrien  struct env *e = &envs[current_env_no];
14089857Sobrien  int old_env_no = current_env_no;
14189857Sobrien
14289857Sobrien  /* stash current env info from global vars into the old environment */
14389857Sobrien  e->output_paragraph = output_paragraph;
144218822Sdim  e->output_paragraph_offset = output_paragraph_offset;
145130561Sobrien  e->meta_char_pos = meta_char_pos;
146130561Sobrien  e->output_column = output_column;
14789857Sobrien  e->paragraph_is_open = paragraph_is_open;
148130561Sobrien  e->current_indent = current_indent;
14989857Sobrien  e->fill_column = fill_column;
150218822Sdim
151130561Sobrien  /* now copy new environment into global vars */
152218822Sdim  current_env_no = n;
153218822Sdim  e = &envs[current_env_no];
154218822Sdim  output_paragraph = e->output_paragraph;
155218822Sdim  output_paragraph_offset = e->output_paragraph_offset;
156218822Sdim  meta_char_pos = e->meta_char_pos;
157218822Sdim  output_column = e->output_column;
158218822Sdim  paragraph_is_open = e->paragraph_is_open;
159218822Sdim  current_indent = e->current_indent;
160218822Sdim  fill_column = e->fill_column;
161218822Sdim  return old_env_no;
162218822Sdim}
163218822Sdim
164218822Sdim/* Initialize environment number ENV_NO, of width WIDTH.
165218822Sdim   The idea is that we're going to use one environment for each column of
166218822Sdim   a multitable, so we can build them up separately and print them
16789857Sobrien   all out at the end. */
16889857Sobrienstatic int
16989857Sobriensetup_output_environment (int env_no, int width)
17089857Sobrien{
17199461Sobrien  int old_env = select_output_environment (env_no);
17299461Sobrien
17399461Sobrien  /* clobber old environment and set width of new one */
17499461Sobrien  init_paragraph ();
175104834Sobrien
17689857Sobrien  /* make our change */
17789857Sobrien  fill_column = width;
17889857Sobrien
17989857Sobrien  /* Save new environment and restore previous one. */
18089857Sobrien  select_output_environment (old_env);
18189857Sobrien
18289857Sobrien  return env_no;
18389857Sobrien}
184218822Sdim
185218822Sdim/* Read the parameters for a multitable from the current command
186218822Sdim   line, save the parameters away, and return the
187218822Sdim   number of columns. */
188218822Sdimstatic int
18992828Sobriensetup_multitable_parameters (void)
19092828Sobrien{
191218822Sdim  char *params = insertion_stack->item_function;
192218822Sdim  int nchars;
193218822Sdim  float columnfrac;
194218822Sdim  char command[200]; /* xx no fixed limits */
19592828Sobrien  int i = 1;
19692828Sobrien
19789857Sobrien  /* We implement @hsep and @vsep even though TeX doesn't.
19889857Sobrien     We don't get mixing of @columnfractions and templates right,
19989857Sobrien     but TeX doesn't either.  */
200104834Sobrien  hsep = vsep = 0;
20189857Sobrien
20289857Sobrien  /* Assume no @columnfractions per default.  */
20389857Sobrien  seen_column_fractions = 0;
20489857Sobrien
205104834Sobrien  while (*params) {
206130561Sobrien    while (whitespace (*params))
20789857Sobrien      params++;
208130561Sobrien
20989857Sobrien    if (*params == '@') {
21089857Sobrien      sscanf (params, "%200s", command);
21189857Sobrien      nchars = strlen (command);
21289857Sobrien      params += nchars;
21389857Sobrien      if (strcmp (command, "@hsep") == 0)
214130561Sobrien        hsep++;
215130561Sobrien      else if (strcmp (command, "@vsep") == 0)
216130561Sobrien        vsep++;
21789857Sobrien      else if (strcmp (command, "@columnfractions") == 0) {
218104834Sobrien        seen_column_fractions = 1;
21989857Sobrien        /* Clobber old environments and create new ones, starting at #1.
22089857Sobrien           Environment #0 is the normal output, so don't mess with it. */
221130561Sobrien        for ( ; i <= MAXCOLS; i++) {
22289857Sobrien          if (sscanf (params, "%f", &columnfrac) < 1)
22389857Sobrien            goto done;
224130561Sobrien          /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least)
22589857Sobrien             doesn't support it.  So skip whitespace (preceding the
22689857Sobrien             number) and then non-whitespace (the number).  */
22789857Sobrien          while (*params && (*params == ' ' || *params == '\t'))
22889857Sobrien            params++;
22989857Sobrien          /* Hmm, but what about @columnfractions 3foo.  Oh well,
23089857Sobrien             it's invalid input anyway.  */
231130561Sobrien          while (*params && *params != ' ' && *params != '\t'
23289857Sobrien                 && *params != '\n' && *params != '@')
23389857Sobrien            params++;
23489857Sobrien
23589857Sobrien          {
236130561Sobrien            /* For html/xml/docbook, translate fractions into integer
23789857Sobrien               percentages, adding .005 to avoid rounding problems.  For
23889857Sobrien               info, we want the character width.  */
239130561Sobrien            int width = xml || html ? (columnfrac + .005) * 100
24089857Sobrien                        : (columnfrac * (fill_column - current_indent) + .5);
24189857Sobrien            setup_output_environment (i, width);
24289857Sobrien          }
24389857Sobrien        }
24489857Sobrien      }
24589857Sobrien
24689857Sobrien    } else if (*params == '{') {
247130561Sobrien      unsigned template_width = find_template_width (&params);
24889857Sobrien
24989857Sobrien      /* This gives us two spaces between columns.  Seems reasonable.
25089857Sobrien         How to take into account current_indent here?  */
25189857Sobrien      setup_output_environment (i++, template_width + 2);
252130561Sobrien
25389857Sobrien    } else {
254104834Sobrien      warning (_("ignoring stray text `%s' after @multitable"), params);
255130561Sobrien      break;
25689857Sobrien    }
25789857Sobrien  }
25889857Sobrien
25989857Sobriendone:
26089857Sobrien  flush_output ();
26189857Sobrien  inhibit_output_flushing ();
262130561Sobrien
26389857Sobrien  last_column = i - 1;
26489857Sobrien  return last_column;
26589857Sobrien}
26689857Sobrien
267130561Sobrien/* Output a row.  Calls insert, but also flushes the buffered output
26889857Sobrien   when we see a newline, since in multitable every line is a separate
26989857Sobrien   paragraph.  */
270130561Sobrienstatic void
27189857Sobrienout_char (int ch)
27289857Sobrien{
27389857Sobrien  if (html || xml)
27489857Sobrien    add_char (ch);
27589857Sobrien  else
27689857Sobrien    {
277130561Sobrien      int env = select_output_environment (0);
27889857Sobrien      insert (ch);
27989857Sobrien      if (ch == '\n')
28089857Sobrien	{
28189857Sobrien	  uninhibit_output_flushing ();
282130561Sobrien	  flush_output ();
28389857Sobrien	  inhibit_output_flushing ();
28489857Sobrien	}
285130561Sobrien      select_output_environment (env);
28689857Sobrien    }
28789857Sobrien}
28889857Sobrien
28989857Sobrien
29089857Sobrienstatic void
29189857Sobriendraw_horizontal_separator (void)
292130561Sobrien{
29389857Sobrien  int i, j, s;
29489857Sobrien
29589857Sobrien  if (html)
29689857Sobrien    {
297130561Sobrien      add_word ("<hr>");
29889857Sobrien      return;
29989857Sobrien    }
300130561Sobrien  if (xml)
30189857Sobrien    return;
30289857Sobrien
30389857Sobrien  for (s = 0; s < envs[0].current_indent; s++)
30489857Sobrien    out_char (' ');
30589857Sobrien  if (vsep)
30689857Sobrien    out_char ('+');
30789857Sobrien  for (i = 1; i <= last_column; i++) {
308130561Sobrien    for (j = 0; j <= envs[i].fill_column; j++)
30989857Sobrien      out_char ('-');
31089857Sobrien    if (vsep)
31199461Sobrien      out_char ('+');
31289857Sobrien  }
313130561Sobrien  out_char (' ');
31489857Sobrien  out_char ('\n');
31589857Sobrien}
316130561Sobrien
31789857Sobrien
31889857Sobrien/* multitable strategy:
31989857Sobrien    for each item {
32089857Sobrien       for each column in an item {
32189857Sobrien        initialize a new paragraph
32289857Sobrien        do ordinary formatting into the new paragraph
32389857Sobrien        save the paragraph away
324130561Sobrien        repeat if there are more paragraphs in the column
32589857Sobrien      }
32689857Sobrien      dump out the saved paragraphs and free the storage
327218822Sdim    }
32889857Sobrien
329130561Sobrien   For HTML we construct a simple HTML 3.2 table with <br>s inserted
33089857Sobrien   to help non-tables browsers.  `@item' inserts a <tr> and `@tab'
331104834Sobrien   inserts <td>; we also try to close <tr>.  The only real
332130561Sobrien   alternative is to rely on the info formatting engine and present
33389857Sobrien   preformatted text.  */
33489857Sobrien
33589857Sobrienvoid
33689857Sobriendo_multitable (void)
33789857Sobrien{
33889857Sobrien  int ncolumns;
33989857Sobrien
34089857Sobrien  if (multitable_active)
341130561Sobrien    {
34289857Sobrien      line_error ("Multitables cannot be nested");
34389857Sobrien      return;
34499461Sobrien    }
34589857Sobrien
346130561Sobrien  close_single_paragraph ();
34789857Sobrien
348104834Sobrien  if (xml)
349130561Sobrien    {
35089857Sobrien      xml_no_para = 1;
35189857Sobrien      if (output_paragraph[output_paragraph_offset-1] == '\n')
35289857Sobrien        output_paragraph_offset--;
35389857Sobrien    }
35489857Sobrien
35589857Sobrien  /* scan the current item function to get the field widths
35689857Sobrien     and number of columns, and set up the output environment list
35789857Sobrien     accordingly. */
358130561Sobrien  ncolumns = setup_multitable_parameters ();
35989857Sobrien  first_row = 1;
36089857Sobrien
36199461Sobrien  /* <p> for non-tables browsers.  @multitable implicitly ends the
36289857Sobrien     current paragraph, so this is ok.  */
363130561Sobrien  if (html)
36489857Sobrien    add_html_block_elt ("<p><table summary=\"\">");
365104834Sobrien  /*  else if (docbook)*/ /* 05-08 */
366130561Sobrien  else if (xml)
36789857Sobrien    {
36889857Sobrien      int *widths = xmalloc (ncolumns * sizeof (int));
36989857Sobrien      int i;
37089857Sobrien      for (i=0; i<ncolumns; i++)
37189857Sobrien	widths[i] = envs[i+1].fill_column;
37289857Sobrien      xml_begin_multitable (ncolumns, widths);
373130561Sobrien      free (widths);
37489857Sobrien    }
37589857Sobrien
376218822Sdim  if (hsep)
37789857Sobrien    draw_horizontal_separator ();
378130561Sobrien
37989857Sobrien  /* The next @item command will direct stdout into the first column
380104834Sobrien     and start processing.  @tab will then switch to the next column,
381130561Sobrien     and @item will flush out the saved output and return to the first
38289857Sobrien     column.  Environment #1 is the first column.  (Environment #0 is
38389857Sobrien     the normal output) */
38489857Sobrien
38589857Sobrien  ++multitable_active;
38689857Sobrien}
38789857Sobrien
388130561Sobrien/* advance to the next environment number */
38989857Sobrienstatic void
39089857Sobriennselect_next_environment (void)
391218822Sdim{
39289857Sobrien  if (current_env_no >= last_column) {
393130561Sobrien    line_error (_("Too many columns in multitable item (max %d)"), last_column);
39489857Sobrien    return;
395104834Sobrien  }
396130561Sobrien  select_output_environment (current_env_no + 1);
39789857Sobrien}
39889857Sobrien
39989857Sobrien
40089857Sobrien/* do anything needed at the beginning of processing a
40189857Sobrien   multitable column. */
40289857Sobrienstatic void
40389857Sobrieninit_column (void)
40489857Sobrien{
405130561Sobrien  /* don't indent 1st paragraph in the item */
40689857Sobrien  cm_noindent ();
40789857Sobrien
40899461Sobrien  /* throw away possible whitespace after @item or @tab command */
40989857Sobrien  skip_whitespace ();
410130561Sobrien}
41189857Sobrien
412104834Sobrienstatic void
413130561Sobrienoutput_multitable_row (void)
41489857Sobrien{
41589857Sobrien  /* offset in the output paragraph of the next char needing
41689857Sobrien     to be output for that column. */
41789857Sobrien  int offset[MAXCOLS];
41889857Sobrien  int i, j, s, remaining;
41989857Sobrien  int had_newline = 0;
42089857Sobrien
42189857Sobrien  for (i = 0; i <= last_column; i++)
422130561Sobrien    offset[i] = 0;
42389857Sobrien
42489857Sobrien  /* select the current environment, to make sure the env variables
42599461Sobrien     get updated */
42689857Sobrien  select_output_environment (current_env_no);
427130561Sobrien
42889857Sobrien#define CHAR_ADDR(n) (offset[i] + (n))
429104834Sobrien#define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)])
430130561Sobrien
43189857Sobrien  /* remove trailing whitespace from each column */
43289857Sobrien  for (i = 1; i <= last_column; i++) {
43389857Sobrien    if (envs[i].output_paragraph_offset)
43489857Sobrien      while (cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1)))
43589857Sobrien        envs[i].output_paragraph_offset--;
43689857Sobrien
43789857Sobrien    if (i == current_env_no)
438130561Sobrien      output_paragraph_offset = envs[i].output_paragraph_offset;
43989857Sobrien  }
44089857Sobrien
44199461Sobrien  /* read the current line from each column, outputting them all
44289857Sobrien     pasted together.  Do this til all lines are output from all
443130561Sobrien     columns.  */
44489857Sobrien  for (;;) {
44589857Sobrien    remaining = 0;
446130561Sobrien    /* first, see if there is any work to do */
44789857Sobrien    for (i = 1; i <= last_column; i++) {
44889857Sobrien      if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) {
44989857Sobrien        remaining = 1;
45089857Sobrien        break;
45189857Sobrien      }
45289857Sobrien    }
45389857Sobrien    if (!remaining)
454130561Sobrien      break;
45589857Sobrien
45689857Sobrien    for (s = 0; s < envs[0].current_indent; s++)
45799461Sobrien      out_char (' ');
45889857Sobrien
459130561Sobrien    if (vsep)
46089857Sobrien      out_char ('|');
46189857Sobrien
462130561Sobrien    for (i = 1; i <= last_column; i++) {
46389857Sobrien      for (s = 0; s < envs[i].current_indent; s++)
46489857Sobrien        out_char (' ');
46589857Sobrien      for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) {
46689857Sobrien        if (CHAR_AT (j) == '\n')
46789857Sobrien          break;
46889857Sobrien        out_char (CHAR_AT (j));
46989857Sobrien      }
470130561Sobrien      offset[i] += j + 1;       /* skip last text plus skip the newline */
47189857Sobrien
47289857Sobrien      /* Do not output trailing blanks if we're in the last column and
47399461Sobrien         there will be no trailing |.  */
47489857Sobrien      if (i < last_column && !vsep)
475130561Sobrien        for (; j <= envs[i].fill_column; j++)
47689857Sobrien          out_char (' ');
47789857Sobrien      if (vsep)
478130561Sobrien        out_char ('|'); /* draw column separator */
47989857Sobrien    }
48089857Sobrien    out_char ('\n');    /* end of line */
48189857Sobrien    had_newline = 1;
48289857Sobrien  }
48389857Sobrien
48489857Sobrien  /* If completely blank item, get blank line despite no other output.  */
48589857Sobrien  if (!had_newline)
486130561Sobrien    out_char ('\n');    /* end of line */
48789857Sobrien
48889857Sobrien  if (hsep)
48999461Sobrien    draw_horizontal_separator ();
49089857Sobrien
491130561Sobrien  /* Now dispose of the buffered output. */
49289857Sobrien  for (i = 1; i <= last_column; i++) {
49389857Sobrien    select_output_environment (i);
494130561Sobrien    init_paragraph ();
49589857Sobrien  }
49689857Sobrien}
49789857Sobrien
49889857Sobrienint after_headitem = 0;
49989857Sobrienint headitem_row = 0;
50089857Sobrien
50189857Sobrien/* start a new item (row) of a multitable */
50289857Sobrienint
503104834Sobrienmultitable_item (void)
504104834Sobrien{
505130561Sobrien  if (!multitable_active) {
50689857Sobrien    line_error ("multitable_item internal error: no active multitable");
507104834Sobrien    xexit (1);
508104834Sobrien  }
50989857Sobrien
510130561Sobrien  current_column_no = 1;
51189857Sobrien
51289857Sobrien  if (html)
513130561Sobrien    {
51489857Sobrien      if (!first_row)
51589857Sobrien        /* <br> for non-tables browsers. */
51689857Sobrien	add_word_args ("<br></%s></tr>", after_headitem ? "th" : "td");
51789857Sobrien
51889857Sobrien      if (seen_column_fractions)
51989857Sobrien        add_word_args ("<tr align=\"left\"><%s valign=\"top\" width=\"%d%%\">",
52089857Sobrien            headitem_flag ? "th" : "td",
521130561Sobrien            envs[current_column_no].fill_column);
52289857Sobrien      else
52389857Sobrien        add_word_args ("<tr align=\"left\"><%s valign=\"top\">",
52499461Sobrien            headitem_flag ? "th" : "td");
52589857Sobrien
526130561Sobrien      if (headitem_flag)
52789857Sobrien        after_headitem = 1;
528104834Sobrien      else
529130561Sobrien        after_headitem = 0;
53089857Sobrien      first_row = 0;
53189857Sobrien      headitem_row = headitem_flag;
53289857Sobrien      headitem_flag = 0;
53389857Sobrien      return 0;
53489857Sobrien    }
53589857Sobrien  /*  else if (docbook)*/ /* 05-08 */
53689857Sobrien  else if (xml)
537130561Sobrien    {
53889857Sobrien      xml_end_multitable_row (first_row);
53989857Sobrien      if (headitem_flag)
54099461Sobrien        after_headitem = 1;
54189857Sobrien      else
542130561Sobrien        after_headitem = 0;
54389857Sobrien      first_row = 0;
54489857Sobrien      headitem_flag = 0;
545130561Sobrien      return 0;
54689857Sobrien    }
54789857Sobrien  first_row = 0;
54889857Sobrien
54989857Sobrien  if (current_env_no > 0) {
55089857Sobrien    output_multitable_row ();
55189857Sobrien  }
55289857Sobrien  /* start at column 1 */
55389857Sobrien  select_output_environment (1);
554130561Sobrien  if (!output_paragraph) {
55589857Sobrien    line_error (_("[unexpected] cannot select column #%d in multitable"),
55689857Sobrien                current_env_no);
55789857Sobrien    xexit (1);
55889857Sobrien  }
559130561Sobrien
56089857Sobrien  init_column ();
561104834Sobrien
562130561Sobrien  if (headitem_flag)
56389857Sobrien    hsep = 1;
56489857Sobrien  else
56589857Sobrien    hsep = 0;
56689857Sobrien
56789857Sobrien  if (headitem_flag)
56889857Sobrien    after_headitem = 1;
569130561Sobrien  else
57089857Sobrien    after_headitem = 0;
57189857Sobrien  headitem_flag = 0;
57289857Sobrien
57389857Sobrien  return 0;
574130561Sobrien}
57589857Sobrien
57689857Sobrien#undef CHAR_AT
577130561Sobrien#undef CHAR_ADDR
57889857Sobrien
57989857Sobrien/* select a new column in current row of multitable */
58089857Sobrienvoid
58189857Sobriencm_tab (void)
58289857Sobrien{
58389857Sobrien  if (!multitable_active)
584130561Sobrien    error (_("ignoring @tab outside of multitable"));
58589857Sobrien
58689857Sobrien  current_column_no++;
58789857Sobrien
58889857Sobrien  if (html)
589130561Sobrien    {
59089857Sobrien      if (seen_column_fractions)
59189857Sobrien        add_word_args ("</%s><%s valign=\"top\" width=\"%d%%\">",
592130561Sobrien            headitem_row ? "th" : "td",
59389857Sobrien            headitem_row ? "th" : "td",
59489857Sobrien            envs[current_column_no].fill_column);
59589857Sobrien      else
59689857Sobrien        add_word_args ("</%s><%s valign=\"top\">",
59789857Sobrien            headitem_row ? "th" : "td",
59889857Sobrien            headitem_row ? "th" : "td");
599130561Sobrien    }
60089857Sobrien  /*  else if (docbook)*/ /* 05-08 */
601104834Sobrien  else if (xml)
60289857Sobrien    xml_end_multitable_column ();
60389857Sobrien  else
60489857Sobrien    nselect_next_environment ();
605130561Sobrien
60689857Sobrien  init_column ();
60789857Sobrien}
608130561Sobrien
60989857Sobrien/* close a multitable, flushing its output and resetting
61089857Sobrien   whatever needs resetting */
61189857Sobrienvoid
61289857Sobrienend_multitable (void)
61389857Sobrien{
61489857Sobrien  if (!html && !docbook)
615130561Sobrien    output_multitable_row ();
61689857Sobrien
61789857Sobrien  /* Multitables cannot be nested.  Otherwise, we'd have to save the
61899461Sobrien     previous output environment number on a stack somewhere, and then
61989857Sobrien     restore to that environment.  */
620130561Sobrien  select_output_environment (0);
62189857Sobrien  multitable_active = 0;
622104834Sobrien  uninhibit_output_flushing ();
623130561Sobrien  close_insertion_paragraph ();
62489857Sobrien
62589857Sobrien  if (html)
62689857Sobrien    add_word_args ("<br></%s></tr></table>\n", headitem_row ? "th" : "td");
62789857Sobrien  /*  else if (docbook)*/ /* 05-08 */
62889857Sobrien  else if (xml)
62989857Sobrien    xml_end_multitable ();
63089857Sobrien
631130561Sobrien#if 0
63289857Sobrien  printf (_("** Multicolumn output from last row:\n"));
63389857Sobrien  for (i = 1; i <= last_column; i++) {
63489857Sobrien    select_output_environment (i);
63589857Sobrien    printf (_("* column #%d: output = %s\n"), i, output_paragraph);
636130561Sobrien  }
63789857Sobrien#endif
638104834Sobrien}
639130561Sobrien