aboutsummaryrefslogtreecommitdiff
path: root/texinfo/makeinfo/multi.c
blob: 5d4bb70b28e7d4e975e848679bc99b8cc1854396 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
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
}