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 (¶ms); 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