1114472Sru/* multi.c -- multiple-column tables (@multitable) for makeinfo. 2146515Sru $Id: multi.c,v 1.8 2004/04/11 17:56:47 karl Exp $ 321495Sjmacd 4146515Sru Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004 Free Software 5114472Sru Foundation, Inc. 621495Sjmacd 721495Sjmacd This program is free software; you can redistribute it and/or modify 821495Sjmacd it under the terms of the GNU General Public License as published by 921495Sjmacd the Free Software Foundation; either version 2, or (at your option) 1021495Sjmacd any later version. 1121495Sjmacd 1221495Sjmacd This program is distributed in the hope that it will be useful, 1321495Sjmacd but WITHOUT ANY WARRANTY; without even the implied warranty of 1421495Sjmacd MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1521495Sjmacd GNU General Public License for more details. 1621495Sjmacd 1721495Sjmacd You should have received a copy of the GNU General Public License 1821495Sjmacd along with this program; if not, write to the Free Software Foundation, 1993139Sru Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2093139Sru 21146515Sru Originally written by phr@gnu.org (Paul Rubin). */ 2221495Sjmacd 2342660Smarkm#include "system.h" 24146515Sru#include "cmds.h" 2556160Sru#include "insertion.h" 2621495Sjmacd#include "makeinfo.h" 27146515Sru#include "multi.h" 2893139Sru#include "xml.h" 2921495Sjmacd 3042660Smarkm#define MAXCOLS 100 /* remove this limit later @@ */ 3121495Sjmacd 3221495Sjmacd 3321495Sjmacd/* 3421495Sjmacd * Output environments. This is a hack grafted onto existing 3521495Sjmacd * structure. The "output environment" used to consist of the 3621495Sjmacd * global variables `output_paragraph', `fill_column', etc. 3721495Sjmacd * Routines like add_char would manipulate these variables. 3821495Sjmacd * 3921495Sjmacd * Now, when formatting a multitable, we maintain separate environments 4021495Sjmacd * for each column. That way we can build up the columns separately 4121495Sjmacd * and write them all out at once. The "current" output environment" 4221495Sjmacd * is still kept in those global variables, so that the old output 4321495Sjmacd * routines don't have to change. But we provide routines to save 4421495Sjmacd * and restore these variables in an "environment table". The 4521495Sjmacd * `select_output_environment' function switches from one output 4621495Sjmacd * environment to another. 4721495Sjmacd * 4842660Smarkm * Environment #0 (i.e., element #0 of the table) is the regular 4921495Sjmacd * environment that is used when we're not formatting a multitable. 5021495Sjmacd * 5121495Sjmacd * Environment #N (where N = 1,2,3,...) is the env. for column #N of 5221495Sjmacd * the table, when a multitable is active. 5321495Sjmacd */ 5421495Sjmacd 5521495Sjmacd/* contents of an output environment */ 5621495Sjmacd/* some more vars may end up being needed here later @@ */ 5721495Sjmacdstruct env 5821495Sjmacd{ 5921495Sjmacd unsigned char *output_paragraph; 6021495Sjmacd int output_paragraph_offset; 6156160Sru int meta_char_pos; 6221495Sjmacd int output_column; 6321495Sjmacd int paragraph_is_open; 6421495Sjmacd int current_indent; 6521495Sjmacd int fill_column; 6642660Smarkm} envs[MAXCOLS]; /* the environment table */ 6721495Sjmacd 6821495Sjmacd/* index in environment table of currently selected environment */ 6921495Sjmacdstatic int current_env_no; 7021495Sjmacd 71146515Sru/* current column number */ 72146515Srustatic int current_column_no; 73146515Sru 74146515Sru/* We need to make a difference between template based widths and 75146515Sru @columnfractions for HTML tables' sake. Sigh. */ 76146515Srustatic int seen_column_fractions; 77146515Sru 7821495Sjmacd/* column number of last column in current multitable */ 7921495Sjmacdstatic int last_column; 8021495Sjmacd 8121495Sjmacd/* flags indicating whether horizontal and vertical separators need 8221495Sjmacd to be drawn, separating rows and columns in the current multitable. */ 8321495Sjmacdstatic int hsep, vsep; 8456160Sru 8556160Sru/* whether this is the first row. */ 8656160Srustatic int first_row; 8721495Sjmacd 8856160Sru/* Called to handle a {...} template on the @multitable line. 8956160Sru We're at the { and our first job is to find the matching }; as a side 9056160Sru effect, we change *PARAMS to point to after it. Our other job is to 9156160Sru expand the template text and return the width of that string. */ 9256160Srustatic unsigned 93146515Srufind_template_width (char **params) 9456160Sru{ 9556160Sru char *template, *xtemplate; 9656160Sru unsigned len; 9756160Sru char *start = *params; 9856160Sru int brace_level = 0; 9956160Sru 10056160Sru /* The first character should be a {. */ 10156160Sru if (!params || !*params || **params != '{') 10256160Sru { 10356160Sru line_error ("find_template width internal error: passed %s", 10456160Sru params ? *params : "null"); 10556160Sru return 0; 10656160Sru } 10756160Sru 10856160Sru do 10956160Sru { 11093139Sru if (**params == '{' && (*params == start || (*params)[-1] != '@')) 11156160Sru brace_level++; 11256160Sru else if (**params == '}' && (*params)[-1] != '@') 11356160Sru brace_level--; 11456160Sru else if (**params == 0) 11556160Sru { 11656160Sru line_error (_("Missing } in @multitable template")); 11756160Sru return 0; 11856160Sru } 11956160Sru (*params)++; 12056160Sru } 12156160Sru while (brace_level > 0); 12256160Sru 12356160Sru template = substring (start + 1, *params - 1); /* omit braces */ 12456160Sru xtemplate = expansion (template, 0); 12556160Sru len = strlen (xtemplate); 12656160Sru 12756160Sru free (template); 12856160Sru free (xtemplate); 12956160Sru 13056160Sru return len; 13156160Sru} 13256160Sru 133146515Sru/* Direct current output to environment number N. Used when 134146515Sru switching work from one column of a multitable to the next. 135146515Sru Returns previous environment number. */ 136146515Srustatic int 137146515Sruselect_output_environment (int n) 138146515Sru{ 139146515Sru struct env *e = &envs[current_env_no]; 140146515Sru int old_env_no = current_env_no; 14156160Sru 142146515Sru /* stash current env info from global vars into the old environment */ 143146515Sru e->output_paragraph = output_paragraph; 144146515Sru e->output_paragraph_offset = output_paragraph_offset; 145146515Sru e->meta_char_pos = meta_char_pos; 146146515Sru e->output_column = output_column; 147146515Sru e->paragraph_is_open = paragraph_is_open; 148146515Sru e->current_indent = current_indent; 149146515Sru e->fill_column = fill_column; 150146515Sru 151146515Sru /* now copy new environment into global vars */ 152146515Sru current_env_no = n; 153146515Sru e = &envs[current_env_no]; 154146515Sru output_paragraph = e->output_paragraph; 155146515Sru output_paragraph_offset = e->output_paragraph_offset; 156146515Sru meta_char_pos = e->meta_char_pos; 157146515Sru output_column = e->output_column; 158146515Sru paragraph_is_open = e->paragraph_is_open; 159146515Sru current_indent = e->current_indent; 160146515Sru fill_column = e->fill_column; 161146515Sru return old_env_no; 162146515Sru} 163146515Sru 164146515Sru/* Initialize environment number ENV_NO, of width WIDTH. 165146515Sru The idea is that we're going to use one environment for each column of 166146515Sru a multitable, so we can build them up separately and print them 167146515Sru all out at the end. */ 168146515Srustatic int 169146515Srusetup_output_environment (int env_no, int width) 170146515Sru{ 171146515Sru int old_env = select_output_environment (env_no); 172146515Sru 173146515Sru /* clobber old environment and set width of new one */ 174146515Sru init_paragraph (); 175146515Sru 176146515Sru /* make our change */ 177146515Sru fill_column = width; 178146515Sru 179146515Sru /* Save new environment and restore previous one. */ 180146515Sru select_output_environment (old_env); 181146515Sru 182146515Sru return env_no; 183146515Sru} 184146515Sru 18521495Sjmacd/* Read the parameters for a multitable from the current command 18621495Sjmacd line, save the parameters away, and return the 18721495Sjmacd number of columns. */ 188146515Srustatic int 189146515Srusetup_multitable_parameters (void) 19021495Sjmacd{ 19121495Sjmacd char *params = insertion_stack->item_function; 19221495Sjmacd int nchars; 19321495Sjmacd float columnfrac; 19456160Sru char command[200]; /* xx no fixed limits */ 19521495Sjmacd int i = 1; 19621495Sjmacd 19721495Sjmacd /* We implement @hsep and @vsep even though TeX doesn't. 19821495Sjmacd We don't get mixing of @columnfractions and templates right, 19921495Sjmacd but TeX doesn't either. */ 20021495Sjmacd hsep = vsep = 0; 20121495Sjmacd 202146515Sru /* Assume no @columnfractions per default. */ 203146515Sru seen_column_fractions = 0; 204146515Sru 20521495Sjmacd while (*params) { 20621495Sjmacd while (whitespace (*params)) 20721495Sjmacd params++; 20821495Sjmacd 20921495Sjmacd if (*params == '@') { 21042660Smarkm sscanf (params, "%200s", command); 21142660Smarkm nchars = strlen (command); 21221495Sjmacd params += nchars; 21321495Sjmacd if (strcmp (command, "@hsep") == 0) 21442660Smarkm hsep++; 21521495Sjmacd else if (strcmp (command, "@vsep") == 0) 21642660Smarkm vsep++; 21721495Sjmacd else if (strcmp (command, "@columnfractions") == 0) { 218146515Sru seen_column_fractions = 1; 21942660Smarkm /* Clobber old environments and create new ones, starting at #1. 22042660Smarkm Environment #0 is the normal output, so don't mess with it. */ 22142660Smarkm for ( ; i <= MAXCOLS; i++) { 22242660Smarkm if (sscanf (params, "%f", &columnfrac) < 1) 22342660Smarkm goto done; 22456160Sru /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least) 22542660Smarkm doesn't support it. So skip whitespace (preceding the 22642660Smarkm number) and then non-whitespace (the number). */ 22742660Smarkm while (*params && (*params == ' ' || *params == '\t')) 22842660Smarkm params++; 229146515Sru /* Hmm, but what about @columnfractions 3foo. Oh well, 23042660Smarkm it's invalid input anyway. */ 23142660Smarkm while (*params && *params != ' ' && *params != '\t' 23242660Smarkm && *params != '\n' && *params != '@') 23342660Smarkm params++; 234146515Sru 235146515Sru { 236146515Sru /* For html/xml/docbook, translate fractions into integer 237146515Sru percentages, adding .005 to avoid rounding problems. For 238146515Sru info, we want the character width. */ 239146515Sru int width = xml || html ? (columnfrac + .005) * 100 240146515Sru : (columnfrac * (fill_column - current_indent) + .5); 241146515Sru setup_output_environment (i, width); 242146515Sru } 24342660Smarkm } 24421495Sjmacd } 24521495Sjmacd 24621495Sjmacd } else if (*params == '{') { 24756160Sru unsigned template_width = find_template_width (¶ms); 248146515Sru 24921495Sjmacd /* This gives us two spaces between columns. Seems reasonable. 25056160Sru How to take into account current_indent here? */ 25156160Sru setup_output_environment (i++, template_width + 2); 25221495Sjmacd 25321495Sjmacd } else { 25442660Smarkm warning (_("ignoring stray text `%s' after @multitable"), params); 25521495Sjmacd break; 25621495Sjmacd } 25721495Sjmacd } 25821495Sjmacd 25921495Sjmacddone: 26021495Sjmacd flush_output (); 26121495Sjmacd inhibit_output_flushing (); 26221495Sjmacd 26321495Sjmacd last_column = i - 1; 26421495Sjmacd return last_column; 26521495Sjmacd} 26621495Sjmacd 267146515Sru/* Output a row. Calls insert, but also flushes the buffered output 268146515Sru when we see a newline, since in multitable every line is a separate 269146515Sru paragraph. */ 270146515Srustatic void 271146515Sruout_char (int ch) 27221495Sjmacd{ 273146515Sru if (html || xml) 274146515Sru add_char (ch); 275146515Sru else 276146515Sru { 277146515Sru int env = select_output_environment (0); 278146515Sru insert (ch); 279146515Sru if (ch == '\n') 280146515Sru { 281146515Sru uninhibit_output_flushing (); 282146515Sru flush_output (); 283146515Sru inhibit_output_flushing (); 284146515Sru } 285146515Sru select_output_environment (env); 286146515Sru } 287146515Sru} 28821495Sjmacd 28921495Sjmacd 290146515Srustatic void 291146515Srudraw_horizontal_separator (void) 292146515Sru{ 293146515Sru int i, j, s; 29421495Sjmacd 295146515Sru if (html) 296146515Sru { 297146515Sru add_word ("<hr>"); 298146515Sru return; 299146515Sru } 300146515Sru if (xml) 301146515Sru return; 30221495Sjmacd 303146515Sru for (s = 0; s < envs[0].current_indent; s++) 304146515Sru out_char (' '); 305146515Sru if (vsep) 306146515Sru out_char ('+'); 307146515Sru for (i = 1; i <= last_column; i++) { 308146515Sru for (j = 0; j <= envs[i].fill_column; j++) 309146515Sru out_char ('-'); 310146515Sru if (vsep) 311146515Sru out_char ('+'); 312146515Sru } 313146515Sru out_char (' '); 314146515Sru out_char ('\n'); 31521495Sjmacd} 31621495Sjmacd 317146515Sru 318146515Sru/* multitable strategy: 319146515Sru for each item { 320146515Sru for each column in an item { 321146515Sru initialize a new paragraph 322146515Sru do ordinary formatting into the new paragraph 323146515Sru save the paragraph away 324146515Sru repeat if there are more paragraphs in the column 325146515Sru } 326146515Sru dump out the saved paragraphs and free the storage 327146515Sru } 328146515Sru 329146515Sru For HTML we construct a simple HTML 3.2 table with <br>s inserted 330146515Sru to help non-tables browsers. `@item' inserts a <tr> and `@tab' 331146515Sru inserts <td>; we also try to close <tr>. The only real 332146515Sru alternative is to rely on the info formatting engine and present 333146515Sru preformatted text. */ 334146515Sru 335146515Sruvoid 336146515Srudo_multitable (void) 33721495Sjmacd{ 338146515Sru int ncolumns; 33921495Sjmacd 340146515Sru if (multitable_active) 341146515Sru { 342146515Sru line_error ("Multitables cannot be nested"); 343146515Sru return; 344146515Sru } 34521495Sjmacd 346146515Sru close_single_paragraph (); 347146515Sru 348146515Sru if (xml) 349146515Sru { 350146515Sru xml_no_para = 1; 351146515Sru if (output_paragraph[output_paragraph_offset-1] == '\n') 352146515Sru output_paragraph_offset--; 353146515Sru } 354146515Sru 355146515Sru /* scan the current item function to get the field widths 356146515Sru and number of columns, and set up the output environment list 357146515Sru accordingly. */ 358146515Sru ncolumns = setup_multitable_parameters (); 359146515Sru first_row = 1; 360146515Sru 361146515Sru /* <p> for non-tables browsers. @multitable implicitly ends the 362146515Sru current paragraph, so this is ok. */ 363146515Sru if (html) 364146515Sru add_html_block_elt ("<p><table summary=\"\">"); 365146515Sru /* else if (docbook)*/ /* 05-08 */ 366146515Sru else if (xml) 367146515Sru { 368146515Sru int *widths = xmalloc (ncolumns * sizeof (int)); 369146515Sru int i; 370146515Sru for (i=0; i<ncolumns; i++) 371146515Sru widths[i] = envs[i+1].fill_column; 372146515Sru xml_begin_multitable (ncolumns, widths); 373146515Sru free (widths); 374146515Sru } 375146515Sru 376146515Sru if (hsep) 377146515Sru draw_horizontal_separator (); 378146515Sru 379146515Sru /* The next @item command will direct stdout into the first column 380146515Sru and start processing. @tab will then switch to the next column, 381146515Sru and @item will flush out the saved output and return to the first 382146515Sru column. Environment #1 is the first column. (Environment #0 is 383146515Sru the normal output) */ 384146515Sru 385146515Sru ++multitable_active; 38621495Sjmacd} 38721495Sjmacd 38821495Sjmacd/* advance to the next environment number */ 389146515Srustatic void 390146515Srunselect_next_environment (void) 39121495Sjmacd{ 39221495Sjmacd if (current_env_no >= last_column) { 39342660Smarkm line_error (_("Too many columns in multitable item (max %d)"), last_column); 39442660Smarkm return; 39521495Sjmacd } 39621495Sjmacd select_output_environment (current_env_no + 1); 39721495Sjmacd} 39821495Sjmacd 39921495Sjmacd 40042660Smarkm/* do anything needed at the beginning of processing a 40142660Smarkm multitable column. */ 402146515Srustatic void 403146515Sruinit_column (void) 40442660Smarkm{ 40542660Smarkm /* don't indent 1st paragraph in the item */ 40642660Smarkm cm_noindent (); 40742660Smarkm 40842660Smarkm /* throw away possible whitespace after @item or @tab command */ 40942660Smarkm skip_whitespace (); 41042660Smarkm} 41142660Smarkm 41221495Sjmacdstatic void 413146515Sruoutput_multitable_row (void) 41421495Sjmacd{ 41521495Sjmacd /* offset in the output paragraph of the next char needing 41621495Sjmacd to be output for that column. */ 41721495Sjmacd int offset[MAXCOLS]; 41856160Sru int i, j, s, remaining; 41956160Sru int had_newline = 0; 42021495Sjmacd 42121495Sjmacd for (i = 0; i <= last_column; i++) 42221495Sjmacd offset[i] = 0; 42321495Sjmacd 42421495Sjmacd /* select the current environment, to make sure the env variables 42521495Sjmacd get updated */ 42621495Sjmacd select_output_environment (current_env_no); 42721495Sjmacd 42821495Sjmacd#define CHAR_ADDR(n) (offset[i] + (n)) 42921495Sjmacd#define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)]) 43021495Sjmacd 43121495Sjmacd /* remove trailing whitespace from each column */ 43221495Sjmacd for (i = 1; i <= last_column; i++) { 43356160Sru if (envs[i].output_paragraph_offset) 43456160Sru while (cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1))) 43556160Sru envs[i].output_paragraph_offset--; 43656160Sru 43756160Sru if (i == current_env_no) 43856160Sru output_paragraph_offset = envs[i].output_paragraph_offset; 43921495Sjmacd } 44021495Sjmacd 44121495Sjmacd /* read the current line from each column, outputting them all 44221495Sjmacd pasted together. Do this til all lines are output from all 44321495Sjmacd columns. */ 44421495Sjmacd for (;;) { 44521495Sjmacd remaining = 0; 44621495Sjmacd /* first, see if there is any work to do */ 44721495Sjmacd for (i = 1; i <= last_column; i++) { 44821495Sjmacd if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) { 44942660Smarkm remaining = 1; 45042660Smarkm break; 45121495Sjmacd } 45221495Sjmacd } 45321495Sjmacd if (!remaining) 45421495Sjmacd break; 45542660Smarkm 45642660Smarkm for (s = 0; s < envs[0].current_indent; s++) 45742660Smarkm out_char (' '); 45842660Smarkm 45921495Sjmacd if (vsep) 46021495Sjmacd out_char ('|'); 46121495Sjmacd 46221495Sjmacd for (i = 1; i <= last_column; i++) { 46356160Sru for (s = 0; s < envs[i].current_indent; s++) 46442660Smarkm out_char (' '); 46521495Sjmacd for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) { 46642660Smarkm if (CHAR_AT (j) == '\n') 46742660Smarkm break; 46842660Smarkm out_char (CHAR_AT (j)); 46921495Sjmacd } 47042660Smarkm offset[i] += j + 1; /* skip last text plus skip the newline */ 47156160Sru 47256160Sru /* Do not output trailing blanks if we're in the last column and 47356160Sru there will be no trailing |. */ 47456160Sru if (i < last_column && !vsep) 47556160Sru for (; j <= envs[i].fill_column; j++) 47656160Sru out_char (' '); 47721495Sjmacd if (vsep) 47842660Smarkm out_char ('|'); /* draw column separator */ 47921495Sjmacd } 48042660Smarkm out_char ('\n'); /* end of line */ 48156160Sru had_newline = 1; 48221495Sjmacd } 48356160Sru 48456160Sru /* If completely blank item, get blank line despite no other output. */ 48556160Sru if (!had_newline) 48656160Sru out_char ('\n'); /* end of line */ 48721495Sjmacd 48821495Sjmacd if (hsep) 48921495Sjmacd draw_horizontal_separator (); 49021495Sjmacd 49121495Sjmacd /* Now dispose of the buffered output. */ 49221495Sjmacd for (i = 1; i <= last_column; i++) { 49321495Sjmacd select_output_environment (i); 49421495Sjmacd init_paragraph (); 49521495Sjmacd } 49621495Sjmacd} 49721495Sjmacd 498146515Sruint after_headitem = 0; 499146515Sruint headitem_row = 0; 500146515Sru 501146515Sru/* start a new item (row) of a multitable */ 502146515Sruint 503146515Srumultitable_item (void) 504146515Sru{ 505146515Sru if (!multitable_active) { 506146515Sru line_error ("multitable_item internal error: no active multitable"); 507146515Sru xexit (1); 508146515Sru } 509146515Sru 510146515Sru current_column_no = 1; 511146515Sru 512146515Sru if (html) 513146515Sru { 514146515Sru if (!first_row) 515146515Sru /* <br> for non-tables browsers. */ 516146515Sru add_word_args ("<br></%s></tr>", after_headitem ? "th" : "td"); 517146515Sru 518146515Sru if (seen_column_fractions) 519146515Sru add_word_args ("<tr align=\"left\"><%s valign=\"top\" width=\"%d%%\">", 520146515Sru headitem_flag ? "th" : "td", 521146515Sru envs[current_column_no].fill_column); 522146515Sru else 523146515Sru add_word_args ("<tr align=\"left\"><%s valign=\"top\">", 524146515Sru headitem_flag ? "th" : "td"); 525146515Sru 526146515Sru if (headitem_flag) 527146515Sru after_headitem = 1; 528146515Sru else 529146515Sru after_headitem = 0; 530146515Sru first_row = 0; 531146515Sru headitem_row = headitem_flag; 532146515Sru headitem_flag = 0; 533146515Sru return 0; 534146515Sru } 535146515Sru /* else if (docbook)*/ /* 05-08 */ 536146515Sru else if (xml) 537146515Sru { 538146515Sru xml_end_multitable_row (first_row); 539146515Sru if (headitem_flag) 540146515Sru after_headitem = 1; 541146515Sru else 542146515Sru after_headitem = 0; 543146515Sru first_row = 0; 544146515Sru headitem_flag = 0; 545146515Sru return 0; 546146515Sru } 547146515Sru first_row = 0; 548146515Sru 549146515Sru if (current_env_no > 0) { 550146515Sru output_multitable_row (); 551146515Sru } 552146515Sru /* start at column 1 */ 553146515Sru select_output_environment (1); 554146515Sru if (!output_paragraph) { 555146515Sru line_error (_("[unexpected] cannot select column #%d in multitable"), 556146515Sru current_env_no); 557146515Sru xexit (1); 558146515Sru } 559146515Sru 560146515Sru init_column (); 561146515Sru 562146515Sru if (headitem_flag) 563146515Sru hsep = 1; 564146515Sru else 565146515Sru hsep = 0; 566146515Sru 567146515Sru if (headitem_flag) 568146515Sru after_headitem = 1; 569146515Sru else 570146515Sru after_headitem = 0; 571146515Sru headitem_flag = 0; 572146515Sru 573146515Sru return 0; 574146515Sru} 575146515Sru 57621495Sjmacd#undef CHAR_AT 57721495Sjmacd#undef CHAR_ADDR 57821495Sjmacd 57921495Sjmacd/* select a new column in current row of multitable */ 58021495Sjmacdvoid 581146515Srucm_tab (void) 58221495Sjmacd{ 58321495Sjmacd if (!multitable_active) 58442660Smarkm error (_("ignoring @tab outside of multitable")); 585146515Sru 586146515Sru current_column_no++; 58721495Sjmacd 58856160Sru if (html) 589146515Sru { 590146515Sru if (seen_column_fractions) 591146515Sru add_word_args ("</%s><%s valign=\"top\" width=\"%d%%\">", 592146515Sru headitem_row ? "th" : "td", 593146515Sru headitem_row ? "th" : "td", 594146515Sru envs[current_column_no].fill_column); 595146515Sru else 596146515Sru add_word_args ("</%s><%s valign=\"top\">", 597146515Sru headitem_row ? "th" : "td", 598146515Sru headitem_row ? "th" : "td"); 599146515Sru } 60093139Sru /* else if (docbook)*/ /* 05-08 */ 60193139Sru else if (xml) 60293139Sru xml_end_multitable_column (); 60356160Sru else 60456160Sru nselect_next_environment (); 60556160Sru 60621495Sjmacd init_column (); 60721495Sjmacd} 60821495Sjmacd 60921495Sjmacd/* close a multitable, flushing its output and resetting 61021495Sjmacd whatever needs resetting */ 61121495Sjmacdvoid 612146515Sruend_multitable (void) 61321495Sjmacd{ 61493139Sru if (!html && !docbook) 61556160Sru output_multitable_row (); 61621495Sjmacd 61721495Sjmacd /* Multitables cannot be nested. Otherwise, we'd have to save the 61821495Sjmacd previous output environment number on a stack somewhere, and then 61921495Sjmacd restore to that environment. */ 62021495Sjmacd select_output_environment (0); 62121495Sjmacd multitable_active = 0; 62221495Sjmacd uninhibit_output_flushing (); 62356160Sru close_insertion_paragraph (); 62421495Sjmacd 62556160Sru if (html) 626146515Sru add_word_args ("<br></%s></tr></table>\n", headitem_row ? "th" : "td"); 62793139Sru /* else if (docbook)*/ /* 05-08 */ 62893139Sru else if (xml) 62993139Sru xml_end_multitable (); 63056160Sru 63121495Sjmacd#if 0 63242660Smarkm printf (_("** Multicolumn output from last row:\n")); 63321495Sjmacd for (i = 1; i <= last_column; i++) { 63421495Sjmacd select_output_environment (i); 63542660Smarkm printf (_("* column #%d: output = %s\n"), i, output_paragraph); 63621495Sjmacd } 63721495Sjmacd#endif 63821495Sjmacd} 639