1 /* multi.c -- multitable stuff for makeinfo.
2 $Id: multi.c,v 1.23 2002/01/19 01:09:08 karl Exp $
4 Copyright (C) 1996, 97, 98, 99, 2000, 01, 02 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 Written by phr@gnu.org (Paul Rubin). */
23 #include "insertion.h"
27 #define MAXCOLS 100 /* remove this limit later @@ */
31 * Output environments. This is a hack grafted onto existing
32 * structure. The "output environment" used to consist of the
33 * global variables `output_paragraph', `fill_column', etc.
34 * Routines like add_char would manipulate these variables.
36 * Now, when formatting a multitable, we maintain separate environments
37 * for each column. That way we can build up the columns separately
38 * and write them all out at once. The "current" output environment"
39 * is still kept in those global variables, so that the old output
40 * routines don't have to change. But we provide routines to save
41 * and restore these variables in an "environment table". The
42 * `select_output_environment' function switches from one output
43 * environment to another.
45 * Environment #0 (i.e., element #0 of the table) is the regular
46 * environment that is used when we're not formatting a multitable.
48 * Environment #N (where N = 1,2,3,...) is the env. for column #N of
49 * the table, when a multitable is active.
52 /* contents of an output environment */
53 /* some more vars may end up being needed here later @@ */
56 unsigned char *output_paragraph;
57 int output_paragraph_offset;
60 int paragraph_is_open;
63 } envs[MAXCOLS]; /* the environment table */
65 /* index in environment table of currently selected environment */
66 static int current_env_no;
68 /* column number of last column in current multitable */
69 static int last_column;
71 /* flags indicating whether horizontal and vertical separators need
72 to be drawn, separating rows and columns in the current multitable. */
73 static int hsep, vsep;
75 /* whether this is the first row. */
78 static void output_multitable_row ();
80 /* Output a row. Calls insert, but also flushes the buffered output
81 when we see a newline, since in multitable every line is a separate
91 int env = select_output_environment (0);
95 uninhibit_output_flushing ();
97 inhibit_output_flushing ();
99 select_output_environment (env);
105 draw_horizontal_separator ()
117 for (s = 0; s < envs[0].current_indent; s++)
121 for (i = 1; i <= last_column; i++) {
122 for (j = 0; j <= envs[i].fill_column; j++)
131 /* multitable strategy:
133 for each column in an item {
134 initialize a new paragraph
135 do ordinary formatting into the new paragraph
136 save the paragraph away
137 repeat if there are more paragraphs in the column
139 dump out the saved paragraphs and free the storage
142 For HTML we construct a simple HTML 3.2 table with <br>s inserted
143 to help non-tables browsers. `@item' inserts a <tr> and `@tab'
144 inserts <td>; we also try to close <tr>. The only real
145 alternative is to rely on the info formatting engine and present
146 preformatted text. */
153 if (multitable_active)
155 line_error ("Multitables cannot be nested");
159 close_single_paragraph ();
161 /* scan the current item function to get the field widths
162 and number of columns, and set up the output environment list
164 /* if (docbook)*/ /* 05-08 */
167 ncolumns = setup_multitable_parameters ();
170 /* <p> for non-tables browsers. @multitable implicitly ends the
171 current paragraph, so this is ok. */
173 add_word ("<p><table>");
174 /* else if (docbook)*/ /* 05-08 */
177 int *widths = xmalloc (ncolumns * sizeof (int));
179 for (i=0; i<ncolumns; i++)
180 widths[i] = envs[i+1].fill_column;
181 xml_begin_multitable (ncolumns, widths);
186 draw_horizontal_separator ();
188 /* The next @item command will direct stdout into the first column
189 and start processing. @tab will then switch to the next column,
190 and @item will flush out the saved output and return to the first
191 column. Environment #1 is the first column. (Environment #0 is
192 the normal output) */
197 /* Called to handle a {...} template on the @multitable line.
198 We're at the { and our first job is to find the matching }; as a side
199 effect, we change *PARAMS to point to after it. Our other job is to
200 expand the template text and return the width of that string. */
202 find_template_width (params)
205 char *template, *xtemplate;
207 char *start = *params;
210 /* The first character should be a {. */
211 if (!params || !*params || **params != '{')
213 line_error ("find_template width internal error: passed %s",
214 params ? *params : "null");
220 if (**params == '{' && (*params == start || (*params)[-1] != '@'))
222 else if (**params == '}' && (*params)[-1] != '@')
224 else if (**params == 0)
226 line_error (_("Missing } in @multitable template"));
231 while (brace_level > 0);
233 template = substring (start + 1, *params - 1); /* omit braces */
234 xtemplate = expansion (template, 0);
235 len = strlen (xtemplate);
244 /* Read the parameters for a multitable from the current command
245 line, save the parameters away, and return the
246 number of columns. */
248 setup_multitable_parameters ()
250 char *params = insertion_stack->item_function;
253 char command[200]; /* xx no fixed limits */
256 /* We implement @hsep and @vsep even though TeX doesn't.
257 We don't get mixing of @columnfractions and templates right,
258 but TeX doesn't either. */
262 while (whitespace (*params))
265 if (*params == '@') {
266 sscanf (params, "%200s", command);
267 nchars = strlen (command);
269 if (strcmp (command, "@hsep") == 0)
271 else if (strcmp (command, "@vsep") == 0)
273 else if (strcmp (command, "@columnfractions") == 0) {
274 /* Clobber old environments and create new ones, starting at #1.
275 Environment #0 is the normal output, so don't mess with it. */
276 for ( ; i <= MAXCOLS; i++) {
277 if (sscanf (params, "%f", &columnfrac) < 1)
279 /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least)
280 doesn't support it. So skip whitespace (preceding the
281 number) and then non-whitespace (the number). */
282 while (*params && (*params == ' ' || *params == '\t'))
284 /* Hmm, but what about @columnfractions 3foo. Well, I suppose
285 it's invalid input anyway. */
286 while (*params && *params != ' ' && *params != '\t'
287 && *params != '\n' && *params != '@')
289 setup_output_environment (i,
290 (int) (columnfrac * (fill_column - current_indent) + .5));
294 } else if (*params == '{') {
295 unsigned template_width = find_template_width (¶ms);
297 /* This gives us two spaces between columns. Seems reasonable.
298 How to take into account current_indent here? */
299 setup_output_environment (i++, template_width + 2);
302 warning (_("ignoring stray text `%s' after @multitable"), params);
309 inhibit_output_flushing ();
315 /* Initialize environment number ENV_NO, of width WIDTH.
316 The idea is that we're going to use one environment for each column of
317 a multitable, so we can build them up separately and print them
318 all out at the end. */
320 setup_output_environment (env_no, width)
324 int old_env = select_output_environment (env_no);
326 /* clobber old environment and set width of new one */
329 /* make our change */
332 /* Save new environment and restore previous one. */
333 select_output_environment (old_env);
338 /* Direct current output to environment number N. Used when
339 switching work from one column of a multitable to the next.
340 Returns previous environment number. */
342 select_output_environment (n)
345 struct env *e = &envs[current_env_no];
346 int old_env_no = current_env_no;
348 /* stash current env info from global vars into the old environment */
349 e->output_paragraph = output_paragraph;
350 e->output_paragraph_offset = output_paragraph_offset;
351 e->meta_char_pos = meta_char_pos;
352 e->output_column = output_column;
353 e->paragraph_is_open = paragraph_is_open;
354 e->current_indent = current_indent;
355 e->fill_column = fill_column;
357 /* now copy new environment into global vars */
359 e = &envs[current_env_no];
360 output_paragraph = e->output_paragraph;
361 output_paragraph_offset = e->output_paragraph_offset;
362 meta_char_pos = e->meta_char_pos;
363 output_column = e->output_column;
364 paragraph_is_open = e->paragraph_is_open;
365 current_indent = e->current_indent;
366 fill_column = e->fill_column;
370 /* advance to the next environment number */
372 nselect_next_environment ()
374 if (current_env_no >= last_column) {
375 line_error (_("Too many columns in multitable item (max %d)"), last_column);
378 select_output_environment (current_env_no + 1);
382 /* do anything needed at the beginning of processing a
383 multitable column. */
387 /* don't indent 1st paragraph in the item */
390 /* throw away possible whitespace after @item or @tab command */
394 /* start a new item (row) of a multitable */
398 if (!multitable_active) {
399 line_error ("multitable_item internal error: no active multitable");
406 add_word ("<br></td></tr>"); /* <br> for non-tables browsers. */
407 add_word ("<tr align=\"left\"><td valign=\"top\">");
411 /* else if (docbook)*/ /* 05-08 */
414 xml_end_multitable_row (first_row);
420 if (current_env_no > 0) {
421 output_multitable_row ();
423 /* start at column 1 */
424 select_output_environment (1);
425 if (!output_paragraph) {
426 line_error (_("Cannot select column #%d in multitable"), current_env_no);
436 output_multitable_row ()
438 /* offset in the output paragraph of the next char needing
439 to be output for that column. */
441 int i, j, s, remaining;
444 for (i = 0; i <= last_column; i++)
447 /* select the current environment, to make sure the env variables
449 select_output_environment (current_env_no);
451 #define CHAR_ADDR(n) (offset[i] + (n))
452 #define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)])
454 /* remove trailing whitespace from each column */
455 for (i = 1; i <= last_column; i++) {
456 if (envs[i].output_paragraph_offset)
457 while (cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1)))
458 envs[i].output_paragraph_offset--;
460 if (i == current_env_no)
461 output_paragraph_offset = envs[i].output_paragraph_offset;
464 /* read the current line from each column, outputting them all
465 pasted together. Do this til all lines are output from all
469 /* first, see if there is any work to do */
470 for (i = 1; i <= last_column; i++) {
471 if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) {
479 for (s = 0; s < envs[0].current_indent; s++)
485 for (i = 1; i <= last_column; i++) {
486 for (s = 0; s < envs[i].current_indent; s++)
488 for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) {
489 if (CHAR_AT (j) == '\n')
491 out_char (CHAR_AT (j));
493 offset[i] += j + 1; /* skip last text plus skip the newline */
495 /* Do not output trailing blanks if we're in the last column and
496 there will be no trailing |. */
497 if (i < last_column && !vsep)
498 for (; j <= envs[i].fill_column; j++)
501 out_char ('|'); /* draw column separator */
503 out_char ('\n'); /* end of line */
507 /* If completely blank item, get blank line despite no other output. */
509 out_char ('\n'); /* end of line */
512 draw_horizontal_separator ();
514 /* Now dispose of the buffered output. */
515 for (i = 1; i <= last_column; i++) {
516 select_output_environment (i);
524 /* select a new column in current row of multitable */
528 if (!multitable_active)
529 error (_("ignoring @tab outside of multitable"));
532 add_word ("</td><td valign=\"top\">");
533 /* else if (docbook)*/ /* 05-08 */
535 xml_end_multitable_column ();
537 nselect_next_environment ();
542 /* close a multitable, flushing its output and resetting
543 whatever needs resetting */
547 if (!html && !docbook)
548 output_multitable_row ();
550 /* Multitables cannot be nested. Otherwise, we'd have to save the
551 previous output environment number on a stack somewhere, and then
552 restore to that environment. */
553 select_output_environment (0);
554 multitable_active = 0;
555 uninhibit_output_flushing ();
556 close_insertion_paragraph ();
559 add_word ("<br></td></tr></table>\n");
560 /* else if (docbook)*/ /* 05-08 */
562 xml_end_multitable ();
565 printf (_("** Multicolumn output from last row:\n"));
566 for (i = 1; i <= last_column; i++) {
567 select_output_environment (i);
568 printf (_("* column #%d: output = %s\n"), i, output_paragraph);