diff options
Diffstat (limited to 'texinfo/makeinfo/multi.c')
-rw-r--r-- | texinfo/makeinfo/multi.c | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/texinfo/makeinfo/multi.c b/texinfo/makeinfo/multi.c new file mode 100644 index 00000000000..5d4bb70b28e --- /dev/null +++ b/texinfo/makeinfo/multi.c @@ -0,0 +1,418 @@ +/* multi.c -- Multitable stuff for makeinfo. + $Id: multi.c,v 1.1 1997/08/21 22:58:08 jason Exp $ + + Copyright (C) 1996 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include "makeinfo.h" + +#define MAXCOLS 100 /* remove this limit later @@ */ + + +/* + * Output environments. This is a hack grafted onto existing + * structure. The "output environment" used to consist of the + * global variables `output_paragraph', `fill_column', etc. + * Routines like add_char would manipulate these variables. + * + * Now, when formatting a multitable, we maintain separate environments + * for each column. That way we can build up the columns separately + * and write them all out at once. The "current" output environment" + * is still kept in those global variables, so that the old output + * routines don't have to change. But we provide routines to save + * and restore these variables in an "environment table". The + * `select_output_environment' function switches from one output + * environment to another. + * + * Environment #0 (i.e. element #0 of the table) is the regular + * environment that is used when we're not formatting a multitable. + * + * Environment #N (where N = 1,2,3,...) is the env. for column #N of + * the table, when a multitable is active. + */ + +/* contents of an output environment */ +/* some more vars may end up being needed here later @@ */ +struct env +{ + unsigned char *output_paragraph; + int output_paragraph_offset; + int output_column; + int paragraph_is_open; + int current_indent; + int fill_column; +} envs[MAXCOLS]; /* the environment table */ + +/* index in environment table of currently selected environment */ +static int current_env_no; + +/* column number of last column in current multitable */ +static int last_column; + +/* flags indicating whether horizontal and vertical separators need + to be drawn, separating rows and columns in the current multitable. */ +static int hsep, vsep; + +void +do_multitable () +{ + int ncolumns; + + /* + * multitable strategy: + * for each item { + * for each column in an item { + * initialize a new paragraph + * do ordinary formatting into the new paragraph + * save the paragraph away + * repeat if there are more paragraphs in the column + * } + * dump out the saved paragraphs and free the storage + * } + */ + + if (multitable_active) + { + line_error ("Multitables cannot be nested"); + return; + } + + /* scan the current item function to get the field widths + and number of columns, and set up the output environment list + accordingly. */ + ncolumns = setup_multitable_parameters (); + if (hsep) + draw_horizontal_separator (); + + /* The next @item command will direct stdout into the first column + and start processing. @tab will then switch to the next column, + and @item will flush out the saved output and return to the first + column. Environment #1 is the first column. (Environment #0 is + the normal output) */ + + ++multitable_active; +} + +/* Read the parameters for a multitable from the current command + line, save the parameters away, and return the + number of columns. */ +int +setup_multitable_parameters () +{ + char *params = insertion_stack->item_function; + int nchars; + float columnfrac; + char command[200]; + int i = 1; + + /* We implement @hsep and @vsep even though TeX doesn't. + We don't get mixing of @columnfractions and templates right, + but TeX doesn't either. */ + hsep = vsep = 0; + + while (*params) { + while (whitespace (*params)) + params++; + + if (*params == '@') { + sscanf (params, "%s%n", command, &nchars); + params += nchars; + if (strcmp (command, "@hsep") == 0) + hsep++; + else if (strcmp (command, "@vsep") == 0) + vsep++; + else if (strcmp (command, "@columnfractions") == 0) { + /* Clobber old environments and create new ones, + starting at #1. Environment #0 is the normal standard output, + so we don't mess with it. */ + for ( ; i <= MAXCOLS; i++) { + if (sscanf (params, "%f%n", &columnfrac, &nchars) < 1) + goto done; + params += nchars; + setup_output_environment (i, (int) (columnfrac * fill_column + .5)); + } + } + + } else if (*params == '{') { + char *start = params; + while ((*params != '}' || params[-1] == '@') && *params) { + params++; + } + /* This gives us two spaces between columns. Seems reasonable. + Really should expand the text, though, so a template of + `@code{foo}' has a width of three, not ten. Also have to match + braces, then. */ + setup_output_environment (i++, params++ - start); + + } else { + warning ("ignoring stray text `%s' after @multitable", params); + break; + } + } + +done: + + flush_output (); + inhibit_output_flushing (); + + last_column = i - 1; + return last_column; +} + +/* Initialize environment number ENV_NO, of width WIDTH. + The idea is that we're going to use one environment for each column of + a multitable, so we can build them up separately and print them + all out at the end. */ +int +setup_output_environment (env_no, width) + int env_no; + int width; +{ + int old_env = select_output_environment (env_no); + + /* clobber old environment and set width of new one */ + init_paragraph (); + + /* make our change */ + fill_column = width; + + /* Save new environment and restore previous one. */ + select_output_environment (old_env); + + return env_no; +} + +/* Direct current output to environment number N. Used when + switching work from one column of a multitable to the next. + Returns previous environment number. */ +int +select_output_environment (n) + int n; +{ + struct env *e = &envs[current_env_no]; + int old_env_no = current_env_no; + + /* stash current env info from global vars into the old environment */ + e->output_paragraph = output_paragraph; + e->output_paragraph_offset = output_paragraph_offset; + e->output_column = output_column; + e->paragraph_is_open = paragraph_is_open; + e->current_indent = current_indent; + e->fill_column = fill_column; + + /* now copy new environment into global vars */ + current_env_no = n; + e = &envs[current_env_no]; + output_paragraph = e->output_paragraph; + output_paragraph_offset = e->output_paragraph_offset; + output_column = e->output_column; + paragraph_is_open = e->paragraph_is_open; + current_indent = e->current_indent; + fill_column = e->fill_column; + return old_env_no; +} + +/* advance to the next environment number */ +int +nselect_next_environment () +{ + if (current_env_no >= last_column) { + line_error ("Too many columns in multitable item (max %d)", last_column); + return 1; + } + select_output_environment (current_env_no + 1); +} + + +static void output_multitable_row (); + +/* start a new item (row) of a multitable */ +multitable_item () +{ + if (!multitable_active) { + /* impossible, I think. */ + error ("multitable item not in active multitable"); + exit (1); + } + if (current_env_no > 0) { + output_multitable_row (); + } + /* start at column 1 */ + select_output_environment (1); + if (!output_paragraph) { + line_error ("Cannot select column #%d in multitable", current_env_no); + exit (FATAL); + } + + init_column (); + + return 0; +} + +/* do anything needed at the beginning of processing a + multitable column. */ +init_column () +{ + /* don't indent 1st paragraph in the item */ + cm_noindent (); + + /* throw away possible whitespace after @item or @tab command */ + skip_whitespace (); +} + +/* Output a row. Have to keep `output_position' up-to-date for each + character we output, or the tags table will be off, leading to + chopped-off output files and undefined nodes (because they're in the + wrong file, etc.). Perhaps it would be better to accumulate this + value somewhere and add it once at the end of the table, or return it + as the value, but this seems simplest. */ + +static void +out_char (ch) + int ch; +{ + extern int output_position; + putc (ch, output_stream); + output_position++; +} + + +static void +output_multitable_row () +{ + int i, j, remaining; + + /* offset in the output paragraph of the next char needing + to be output for that column. */ + int offset[MAXCOLS]; + + for (i = 0; i <= last_column; i++) + offset[i] = 0; + + /* select the current environment, to make sure the env variables + get updated */ + select_output_environment (current_env_no); + +#define CHAR_ADDR(n) (offset[i] + (n)) +#define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)]) + + /* remove trailing whitespace from each column */ + for (i = 1; i <= last_column; i++) { + while (cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1))) { + envs[i].output_paragraph_offset--; + } + } + + /* read the current line from each column, outputting them all + pasted together. Do this til all lines are output from all + columns. */ + for (;;) { + remaining = 0; + /* first, see if there is any work to do */ + for (i = 1; i <= last_column; i++) { + if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) { + remaining = 1; + break; + } + } + if (!remaining) + break; + + if (vsep) + out_char ('|'); + + for (i = 1; i <= last_column; i++) { + for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) { + if (CHAR_AT (j) == '\n') + break; + out_char (CHAR_AT (j)); + } + offset[i] += j + 1; /* skip last text plus skip the newline */ + for (; j <= envs[i].fill_column; j++) + out_char (' '); + if (vsep) + out_char ('|'); /* draw column separator */ + } + out_char ('\n'); /* end of line */ + } + + if (hsep) + draw_horizontal_separator (); + + /* Now dispose of the buffered output. */ + for (i = 1; i <= last_column; i++) { + select_output_environment (i); + init_paragraph (); + } +} + +#undef CHAR_AT +#undef CHAR_ADDR + +int +draw_horizontal_separator () +{ + int i, j; + if (vsep) + out_char ('+'); + for (i = 1; i <= last_column; i++) { + for (j = 0; j <= envs[i].fill_column; j++) + out_char ('-'); + if (vsep) + out_char ('+'); + } + out_char ('\n'); +} + +/* select a new column in current row of multitable */ +void +cm_tab () +{ + if (!multitable_active) + error ("ignoring @tab outside of multitable"); + + nselect_next_environment (); + init_column (); +} + +/* close a multitable, flushing its output and resetting + whatever needs resetting */ +void +end_multitable () +{ + int i; + + output_multitable_row (); + + /* Multitables cannot be nested. Otherwise, we'd have to save the + previous output environment number on a stack somewhere, and then + restore to that environment. */ + select_output_environment (0); + close_paragraph (); + insert ('\n'); /* we swallow newlines, so insert one of our own */ + + multitable_active = 0; + uninhibit_output_flushing (); + +#if 0 + printf ("** Multicolumn output from last row:\n"); + for (i = 1; i <= last_column; i++) { + select_output_environment (i); + printf ("* column #%d: output = %s\n", i, output_paragraph); + } +#endif +} |