aboutsummaryrefslogtreecommitdiff
path: root/texinfo/makeinfo/multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'texinfo/makeinfo/multi.c')
-rw-r--r--texinfo/makeinfo/multi.c418
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
+}