File: | src/gnu/usr.bin/texinfo/makeinfo/multi.c |
Warning: | line 465, column 19 The left operand of '+' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* multi.c -- multiple-column tables (@multitable) for makeinfo. | |||
2 | $Id: multi.c,v 1.7 2006/07/17 16:12:36 espie Exp $ | |||
3 | ||||
4 | Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004 Free Software | |||
5 | Foundation, Inc. | |||
6 | ||||
7 | This program is free software; you can redistribute it and/or modify | |||
8 | it under the terms of the GNU General Public License as published by | |||
9 | the Free Software Foundation; either version 2, or (at your option) | |||
10 | any later version. | |||
11 | ||||
12 | This program is distributed in the hope that it will be useful, | |||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
15 | GNU General Public License for more details. | |||
16 | ||||
17 | You should have received a copy of the GNU General Public License | |||
18 | along with this program; if not, write to the Free Software Foundation, | |||
19 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
20 | ||||
21 | Originally written by phr@gnu.org (Paul Rubin). */ | |||
22 | ||||
23 | #include "system.h" | |||
24 | #include "cmds.h" | |||
25 | #include "insertion.h" | |||
26 | #include "makeinfo.h" | |||
27 | #include "multi.h" | |||
28 | #include "xml.h" | |||
29 | ||||
30 | #define MAXCOLS100 100 /* remove this limit later @@ */ | |||
31 | ||||
32 | ||||
33 | /* | |||
34 | * Output environments. This is a hack grafted onto existing | |||
35 | * structure. The "output environment" used to consist of the | |||
36 | * global variables `output_paragraph', `fill_column', etc. | |||
37 | * Routines like add_char would manipulate these variables. | |||
38 | * | |||
39 | * Now, when formatting a multitable, we maintain separate environments | |||
40 | * for each column. That way we can build up the columns separately | |||
41 | * and write them all out at once. The "current" output environment" | |||
42 | * is still kept in those global variables, so that the old output | |||
43 | * routines don't have to change. But we provide routines to save | |||
44 | * and restore these variables in an "environment table". The | |||
45 | * `select_output_environment' function switches from one output | |||
46 | * environment to another. | |||
47 | * | |||
48 | * Environment #0 (i.e., element #0 of the table) is the regular | |||
49 | * environment that is used when we're not formatting a multitable. | |||
50 | * | |||
51 | * Environment #N (where N = 1,2,3,...) is the env. for column #N of | |||
52 | * the table, when a multitable is active. | |||
53 | */ | |||
54 | ||||
55 | /* contents of an output environment */ | |||
56 | /* some more vars may end up being needed here later @@ */ | |||
57 | struct env | |||
58 | { | |||
59 | unsigned char *output_paragraph; | |||
60 | int output_paragraph_offset; | |||
61 | int meta_char_pos; | |||
62 | int output_column; | |||
63 | int paragraph_is_open; | |||
64 | int current_indent; | |||
65 | int fill_column; | |||
66 | } envs[MAXCOLS100]; /* the environment table */ | |||
67 | ||||
68 | /* index in environment table of currently selected environment */ | |||
69 | static int current_env_no; | |||
70 | ||||
71 | /* current column number */ | |||
72 | static int current_column_no; | |||
73 | ||||
74 | /* We need to make a difference between template based widths and | |||
75 | @columnfractions for HTML tables' sake. Sigh. */ | |||
76 | static int seen_column_fractions; | |||
77 | ||||
78 | /* column number of last column in current multitable */ | |||
79 | static int last_column; | |||
80 | ||||
81 | /* flags indicating whether horizontal and vertical separators need | |||
82 | to be drawn, separating rows and columns in the current multitable. */ | |||
83 | static int hsep, vsep; | |||
84 | ||||
85 | /* whether this is the first row. */ | |||
86 | static int first_row; | |||
87 | ||||
88 | /* Called to handle a {...} template on the @multitable line. | |||
89 | We're at the { and our first job is to find the matching }; as a side | |||
90 | effect, we change *PARAMS to point to after it. Our other job is to | |||
91 | expand the template text and return the width of that string. */ | |||
92 | static unsigned | |||
93 | find_template_width (char **params) | |||
94 | { | |||
95 | char *template, *xtemplate; | |||
96 | unsigned len; | |||
97 | char *start = *params; | |||
98 | int brace_level = 0; | |||
99 | ||||
100 | /* The first character should be a {. */ | |||
101 | if (!params || !*params || **params != '{') | |||
102 | { | |||
103 | line_error ("find_template width internal error: passed %s", | |||
104 | params ? *params : "null"); | |||
105 | return 0; | |||
106 | } | |||
107 | ||||
108 | do | |||
109 | { | |||
110 | if (**params == '{' && (*params == start || (*params)[-1] != '@')) | |||
111 | brace_level++; | |||
112 | else if (**params == '}' && (*params)[-1] != '@') | |||
113 | brace_level--; | |||
114 | else if (**params == 0) | |||
115 | { | |||
116 | line_error (_("Missing } in @multitable template")((const char *) ("Missing } in @multitable template"))); | |||
117 | return 0; | |||
118 | } | |||
119 | (*params)++; | |||
120 | } | |||
121 | while (brace_level > 0); | |||
122 | ||||
123 | template = substring (start + 1, *params - 1); /* omit braces */ | |||
124 | xtemplate = expansion (template, 0); | |||
125 | len = strlen (xtemplate); | |||
126 | ||||
127 | free (template); | |||
128 | free (xtemplate); | |||
129 | ||||
130 | return len; | |||
131 | } | |||
132 | ||||
133 | /* Direct current output to environment number N. Used when | |||
134 | switching work from one column of a multitable to the next. | |||
135 | Returns previous environment number. */ | |||
136 | static int | |||
137 | select_output_environment (int n) | |||
138 | { | |||
139 | struct env *e = &envs[current_env_no]; | |||
140 | int old_env_no = current_env_no; | |||
141 | ||||
142 | /* stash current env info from global vars into the old environment */ | |||
143 | e->output_paragraph = output_paragraph; | |||
144 | e->output_paragraph_offset = output_paragraph_offset; | |||
145 | e->meta_char_pos = meta_char_pos; | |||
146 | e->output_column = output_column; | |||
147 | e->paragraph_is_open = paragraph_is_open; | |||
148 | e->current_indent = current_indent; | |||
149 | e->fill_column = fill_column; | |||
150 | ||||
151 | /* now copy new environment into global vars */ | |||
152 | current_env_no = n; | |||
153 | e = &envs[current_env_no]; | |||
154 | output_paragraph = e->output_paragraph; | |||
155 | output_paragraph_offset = e->output_paragraph_offset; | |||
156 | meta_char_pos = e->meta_char_pos; | |||
157 | output_column = e->output_column; | |||
158 | paragraph_is_open = e->paragraph_is_open; | |||
159 | current_indent = e->current_indent; | |||
160 | fill_column = e->fill_column; | |||
161 | return old_env_no; | |||
162 | } | |||
163 | ||||
164 | /* Initialize environment number ENV_NO, of width WIDTH. | |||
165 | The idea is that we're going to use one environment for each column of | |||
166 | a multitable, so we can build them up separately and print them | |||
167 | all out at the end. */ | |||
168 | static int | |||
169 | setup_output_environment (int env_no, int width) | |||
170 | { | |||
171 | int old_env = select_output_environment (env_no); | |||
172 | ||||
173 | /* clobber old environment and set width of new one */ | |||
174 | init_paragraph (); | |||
175 | ||||
176 | /* make our change */ | |||
177 | fill_column = width; | |||
178 | ||||
179 | /* Save new environment and restore previous one. */ | |||
180 | select_output_environment (old_env); | |||
181 | ||||
182 | return env_no; | |||
183 | } | |||
184 | ||||
185 | /* Read the parameters for a multitable from the current command | |||
186 | line, save the parameters away, and return the | |||
187 | number of columns. */ | |||
188 | static int | |||
189 | setup_multitable_parameters (void) | |||
190 | { | |||
191 | char *params = insertion_stack->item_function; | |||
192 | int nchars; | |||
193 | float columnfrac; | |||
194 | char command[200]; /* xx no fixed limits */ | |||
195 | int i = 1; | |||
196 | ||||
197 | /* We implement @hsep and @vsep even though TeX doesn't. | |||
198 | We don't get mixing of @columnfractions and templates right, | |||
199 | but TeX doesn't either. */ | |||
200 | hsep = vsep = 0; | |||
201 | ||||
202 | /* Assume no @columnfractions per default. */ | |||
203 | seen_column_fractions = 0; | |||
204 | ||||
205 | while (*params) { | |||
206 | while (whitespace (*params)((*params) == '\t' || (*params) == ' ')) | |||
207 | params++; | |||
208 | ||||
209 | if (*params == '@') { | |||
210 | sscanf (params, "%199s", command); | |||
211 | nchars = strlen (command); | |||
212 | params += nchars; | |||
213 | if (strcmp (command, "@hsep") == 0) | |||
214 | hsep++; | |||
215 | else if (strcmp (command, "@vsep") == 0) | |||
216 | vsep++; | |||
217 | else if (strcmp (command, "@columnfractions") == 0) { | |||
218 | seen_column_fractions = 1; | |||
219 | /* Clobber old environments and create new ones, starting at #1. | |||
220 | Environment #0 is the normal output, so don't mess with it. */ | |||
221 | for ( ; i <= MAXCOLS100; i++) { | |||
222 | if (sscanf (params, "%f", &columnfrac) < 1) | |||
223 | goto done; | |||
224 | /* Unfortunately, can't use %n since m68k-hp-bsd libc (at least) | |||
225 | doesn't support it. So skip whitespace (preceding the | |||
226 | number) and then non-whitespace (the number). */ | |||
227 | while (*params && (*params == ' ' || *params == '\t')) | |||
228 | params++; | |||
229 | /* Hmm, but what about @columnfractions 3foo. Oh well, | |||
230 | it's invalid input anyway. */ | |||
231 | while (*params && *params != ' ' && *params != '\t' | |||
232 | && *params != '\n' && *params != '@') | |||
233 | params++; | |||
234 | ||||
235 | { | |||
236 | /* For html/xml/docbook, translate fractions into integer | |||
237 | percentages, adding .005 to avoid rounding problems. For | |||
238 | info, we want the character width. */ | |||
239 | int width = xml || html ? (columnfrac + .005) * 100 | |||
240 | : (columnfrac * (fill_column - current_indent) + .5); | |||
241 | setup_output_environment (i, width); | |||
242 | } | |||
243 | } | |||
244 | } | |||
245 | ||||
246 | } else if (*params == '{') { | |||
247 | unsigned template_width = find_template_width (¶ms); | |||
248 | ||||
249 | /* This gives us two spaces between columns. Seems reasonable. | |||
250 | How to take into account current_indent here? */ | |||
251 | setup_output_environment (i++, template_width + 2); | |||
252 | ||||
253 | } else { | |||
254 | warning (_("ignoring stray text `%s' after @multitable")((const char *) ("ignoring stray text `%s' after @multitable" )), params); | |||
255 | break; | |||
256 | } | |||
257 | } | |||
258 | ||||
259 | done: | |||
260 | flush_output (); | |||
261 | inhibit_output_flushing (); | |||
262 | ||||
263 | last_column = i - 1; | |||
264 | return last_column; | |||
265 | } | |||
266 | ||||
267 | /* Output a row. Calls insert, but also flushes the buffered output | |||
268 | when we see a newline, since in multitable every line is a separate | |||
269 | paragraph. */ | |||
270 | static void | |||
271 | out_char (int ch) | |||
272 | { | |||
273 | if (html || xml) | |||
274 | add_char (ch); | |||
275 | else | |||
276 | { | |||
277 | int env = select_output_environment (0); | |||
278 | insert (ch); | |||
279 | if (ch == '\n') | |||
280 | { | |||
281 | uninhibit_output_flushing (); | |||
282 | flush_output (); | |||
283 | inhibit_output_flushing (); | |||
284 | } | |||
285 | select_output_environment (env); | |||
286 | } | |||
287 | } | |||
288 | ||||
289 | ||||
290 | static void | |||
291 | draw_horizontal_separator (void) | |||
292 | { | |||
293 | int i, j, s; | |||
294 | ||||
295 | if (html) | |||
296 | { | |||
297 | add_word ("<hr>"); | |||
298 | return; | |||
299 | } | |||
300 | if (xml) | |||
301 | return; | |||
302 | ||||
303 | for (s = 0; s < envs[0].current_indent; s++) | |||
304 | out_char (' '); | |||
305 | if (vsep) | |||
306 | out_char ('+'); | |||
307 | for (i = 1; i <= last_column; i++) { | |||
308 | for (j = 0; j <= envs[i].fill_column; j++) | |||
309 | out_char ('-'); | |||
310 | if (vsep) | |||
311 | out_char ('+'); | |||
312 | } | |||
313 | out_char (' '); | |||
314 | out_char ('\n'); | |||
315 | } | |||
316 | ||||
317 | ||||
318 | /* multitable strategy: | |||
319 | for each item { | |||
320 | for each column in an item { | |||
321 | initialize a new paragraph | |||
322 | do ordinary formatting into the new paragraph | |||
323 | save the paragraph away | |||
324 | repeat if there are more paragraphs in the column | |||
325 | } | |||
326 | dump out the saved paragraphs and free the storage | |||
327 | } | |||
328 | ||||
329 | For HTML we construct a simple HTML 3.2 table with <br>s inserted | |||
330 | to help non-tables browsers. `@item' inserts a <tr> and `@tab' | |||
331 | inserts <td>; we also try to close <tr>. The only real | |||
332 | alternative is to rely on the info formatting engine and present | |||
333 | preformatted text. */ | |||
334 | ||||
335 | void | |||
336 | do_multitable (void) | |||
337 | { | |||
338 | int ncolumns; | |||
339 | ||||
340 | if (multitable_active) | |||
341 | { | |||
342 | line_error ("Multitables cannot be nested"); | |||
343 | return; | |||
344 | } | |||
345 | ||||
346 | close_single_paragraph (); | |||
347 | ||||
348 | if (xml) | |||
349 | { | |||
350 | xml_no_para = 1; | |||
351 | if (output_paragraph[output_paragraph_offset-1] == '\n') | |||
352 | output_paragraph_offset--; | |||
353 | } | |||
354 | ||||
355 | /* scan the current item function to get the field widths | |||
356 | and number of columns, and set up the output environment list | |||
357 | accordingly. */ | |||
358 | ncolumns = setup_multitable_parameters (); | |||
359 | first_row = 1; | |||
360 | ||||
361 | /* <p> for non-tables browsers. @multitable implicitly ends the | |||
362 | current paragraph, so this is ok. */ | |||
363 | if (html) | |||
364 | add_html_block_elt ("<p><table summary=\"\">"); | |||
365 | /* else if (docbook)*/ /* 05-08 */ | |||
366 | else if (xml) | |||
367 | { | |||
368 | int *widths = xmalloc (ncolumns * sizeof (int)); | |||
369 | int i; | |||
370 | for (i=0; i<ncolumns; i++) | |||
371 | widths[i] = envs[i+1].fill_column; | |||
372 | xml_begin_multitable (ncolumns, widths); | |||
373 | free (widths); | |||
374 | } | |||
375 | ||||
376 | if (hsep) | |||
377 | draw_horizontal_separator (); | |||
378 | ||||
379 | /* The next @item command will direct stdout into the first column | |||
380 | and start processing. @tab will then switch to the next column, | |||
381 | and @item will flush out the saved output and return to the first | |||
382 | column. Environment #1 is the first column. (Environment #0 is | |||
383 | the normal output) */ | |||
384 | ||||
385 | ++multitable_active; | |||
386 | } | |||
387 | ||||
388 | /* advance to the next environment number */ | |||
389 | static void | |||
390 | nselect_next_environment (void) | |||
391 | { | |||
392 | if (current_env_no >= last_column) { | |||
393 | line_error (_("Too many columns in multitable item (max %d)")((const char *) ("Too many columns in multitable item (max %d)" )), last_column); | |||
394 | return; | |||
395 | } | |||
396 | select_output_environment (current_env_no + 1); | |||
397 | } | |||
398 | ||||
399 | ||||
400 | /* do anything needed at the beginning of processing a | |||
401 | multitable column. */ | |||
402 | static void | |||
403 | init_column (void) | |||
404 | { | |||
405 | /* don't indent 1st paragraph in the item */ | |||
406 | cm_noindent (); | |||
407 | ||||
408 | /* throw away possible whitespace after @item or @tab command */ | |||
409 | skip_whitespace ()while ((input_text_offset != input_text_length) && (( input_text[input_text_offset]) == '\t' || (input_text[input_text_offset ]) == ' ')) input_text_offset++; | |||
410 | } | |||
411 | ||||
412 | static void | |||
413 | output_multitable_row (void) | |||
414 | { | |||
415 | /* offset in the output paragraph of the next char needing | |||
416 | to be output for that column. */ | |||
417 | int offset[MAXCOLS100]; | |||
418 | int i, j, s, remaining; | |||
419 | int had_newline = 0; | |||
420 | ||||
421 | for (i = 0; i <= last_column; i++) | |||
422 | offset[i] = 0; | |||
423 | ||||
424 | /* select the current environment, to make sure the env variables | |||
425 | get updated */ | |||
426 | select_output_environment (current_env_no); | |||
427 | ||||
428 | #define CHAR_ADDR(n) (offset[i] + (n)) | |||
429 | #define CHAR_AT(n) (envs[i].output_paragraph[CHAR_ADDR(n)]) | |||
430 | ||||
431 | /* remove trailing whitespace from each column */ | |||
432 | for (i = 1; i <= last_column; i++) { | |||
433 | while (envs[i].output_paragraph_offset && | |||
434 | cr_or_whitespace (CHAR_AT (envs[i].output_paragraph_offset - 1))(((CHAR_AT (envs[i].output_paragraph_offset - 1)) == '\t' || ( CHAR_AT (envs[i].output_paragraph_offset - 1)) == ' ') || (CHAR_AT (envs[i].output_paragraph_offset - 1)) == '\r' || (CHAR_AT ( envs[i].output_paragraph_offset - 1)) == '\n')) | |||
435 | envs[i].output_paragraph_offset--; | |||
436 | ||||
437 | if (i == current_env_no) | |||
438 | output_paragraph_offset = envs[i].output_paragraph_offset; | |||
439 | } | |||
440 | ||||
441 | /* read the current line from each column, outputting them all | |||
442 | pasted together. Do this til all lines are output from all | |||
443 | columns. */ | |||
444 | for (;;) { | |||
445 | remaining = 0; | |||
446 | /* first, see if there is any work to do */ | |||
447 | for (i = 1; i <= last_column; i++) { | |||
448 | if (CHAR_ADDR (0) < envs[i].output_paragraph_offset) { | |||
449 | remaining = 1; | |||
450 | break; | |||
451 | } | |||
452 | } | |||
453 | if (!remaining
| |||
454 | break; | |||
455 | ||||
456 | for (s = 0; s < envs[0].current_indent; s++) | |||
457 | out_char (' '); | |||
458 | ||||
459 | if (vsep) | |||
460 | out_char ('|'); | |||
461 | ||||
462 | for (i = 1; i
| |||
463 | for (s = 0; s < envs[i].current_indent; s++) | |||
464 | out_char (' '); | |||
465 | for (j = 0; CHAR_ADDR (j) < envs[i].output_paragraph_offset; j++) { | |||
| ||||
466 | if (CHAR_AT (j) == '\n') | |||
467 | break; | |||
468 | out_char (CHAR_AT (j)); | |||
469 | } | |||
470 | offset[i] += j + 1; /* skip last text plus skip the newline */ | |||
471 | ||||
472 | /* Do not output trailing blanks if we're in the last column and | |||
473 | there will be no trailing |. */ | |||
474 | if (i < last_column && !vsep) | |||
475 | for (; j <= envs[i].fill_column; j++) | |||
476 | out_char (' '); | |||
477 | if (vsep
| |||
478 | out_char ('|'); /* draw column separator */ | |||
479 | } | |||
480 | out_char ('\n'); /* end of line */ | |||
481 | had_newline = 1; | |||
482 | } | |||
483 | ||||
484 | /* If completely blank item, get blank line despite no other output. */ | |||
485 | if (!had_newline) | |||
486 | out_char ('\n'); /* end of line */ | |||
487 | ||||
488 | if (hsep) | |||
489 | draw_horizontal_separator (); | |||
490 | ||||
491 | /* Now dispose of the buffered output. */ | |||
492 | for (i = 1; i <= last_column; i++) { | |||
493 | select_output_environment (i); | |||
494 | init_paragraph (); | |||
495 | } | |||
496 | } | |||
497 | ||||
498 | int after_headitem = 0; | |||
499 | int headitem_row = 0; | |||
500 | ||||
501 | /* start a new item (row) of a multitable */ | |||
502 | int | |||
503 | multitable_item (void) | |||
504 | { | |||
505 | if (!multitable_active) { | |||
506 | line_error ("multitable_item internal error: no active multitable"); | |||
507 | xexit (1); | |||
508 | } | |||
509 | ||||
510 | current_column_no = 1; | |||
511 | ||||
512 | if (html) | |||
513 | { | |||
514 | if (!first_row) | |||
515 | /* <br> for non-tables browsers. */ | |||
516 | add_word_args ("<br></%s></tr>", after_headitem ? "th" : "td"); | |||
517 | ||||
518 | if (seen_column_fractions) | |||
519 | add_word_args ("<tr align=\"left\"><%s valign=\"top\" width=\"%d%%\">", | |||
520 | headitem_flag ? "th" : "td", | |||
521 | envs[current_column_no].fill_column); | |||
522 | else | |||
523 | add_word_args ("<tr align=\"left\"><%s valign=\"top\">", | |||
524 | headitem_flag ? "th" : "td"); | |||
525 | ||||
526 | if (headitem_flag) | |||
527 | after_headitem = 1; | |||
528 | else | |||
529 | after_headitem = 0; | |||
530 | first_row = 0; | |||
531 | headitem_row = headitem_flag; | |||
532 | headitem_flag = 0; | |||
533 | return 0; | |||
534 | } | |||
535 | /* else if (docbook)*/ /* 05-08 */ | |||
536 | else if (xml) | |||
537 | { | |||
538 | xml_end_multitable_row (first_row); | |||
539 | if (headitem_flag) | |||
540 | after_headitem = 1; | |||
541 | else | |||
542 | after_headitem = 0; | |||
543 | first_row = 0; | |||
544 | headitem_flag = 0; | |||
545 | return 0; | |||
546 | } | |||
547 | first_row = 0; | |||
548 | ||||
549 | if (current_env_no > 0) { | |||
550 | output_multitable_row (); | |||
551 | } | |||
552 | /* start at column 1 */ | |||
553 | select_output_environment (1); | |||
554 | if (!output_paragraph) { | |||
555 | line_error (_("[unexpected] cannot select column #%d in multitable")((const char *) ("[unexpected] cannot select column #%d in multitable" )), | |||
556 | current_env_no); | |||
557 | xexit (1); | |||
558 | } | |||
559 | ||||
560 | init_column (); | |||
561 | ||||
562 | if (headitem_flag) | |||
563 | hsep = 1; | |||
564 | else | |||
565 | hsep = 0; | |||
566 | ||||
567 | if (headitem_flag) | |||
568 | after_headitem = 1; | |||
569 | else | |||
570 | after_headitem = 0; | |||
571 | headitem_flag = 0; | |||
572 | ||||
573 | return 0; | |||
574 | } | |||
575 | ||||
576 | #undef CHAR_AT | |||
577 | #undef CHAR_ADDR | |||
578 | ||||
579 | /* select a new column in current row of multitable */ | |||
580 | void | |||
581 | cm_tab (void) | |||
582 | { | |||
583 | if (!multitable_active) | |||
584 | error (_("ignoring @tab outside of multitable")((const char *) ("ignoring @tab outside of multitable"))); | |||
585 | ||||
586 | current_column_no++; | |||
587 | ||||
588 | if (html) | |||
589 | { | |||
590 | if (seen_column_fractions) | |||
591 | add_word_args ("</%s><%s valign=\"top\" width=\"%d%%\">", | |||
592 | headitem_row ? "th" : "td", | |||
593 | headitem_row ? "th" : "td", | |||
594 | envs[current_column_no].fill_column); | |||
595 | else | |||
596 | add_word_args ("</%s><%s valign=\"top\">", | |||
597 | headitem_row ? "th" : "td", | |||
598 | headitem_row ? "th" : "td"); | |||
599 | } | |||
600 | /* else if (docbook)*/ /* 05-08 */ | |||
601 | else if (xml) | |||
602 | xml_end_multitable_column (); | |||
603 | else | |||
604 | nselect_next_environment (); | |||
605 | ||||
606 | init_column (); | |||
607 | } | |||
608 | ||||
609 | /* close a multitable, flushing its output and resetting | |||
610 | whatever needs resetting */ | |||
611 | void | |||
612 | end_multitable (void) | |||
613 | { | |||
614 | if (!html && !docbook) | |||
| ||||
615 | output_multitable_row (); | |||
616 | ||||
617 | /* Multitables cannot be nested. Otherwise, we'd have to save the | |||
618 | previous output environment number on a stack somewhere, and then | |||
619 | restore to that environment. */ | |||
620 | select_output_environment (0); | |||
621 | multitable_active = 0; | |||
622 | uninhibit_output_flushing (); | |||
623 | close_insertion_paragraph (); | |||
624 | ||||
625 | if (html) | |||
626 | add_word_args ("<br></%s></tr></table>\n", headitem_row ? "th" : "td"); | |||
627 | /* else if (docbook)*/ /* 05-08 */ | |||
628 | else if (xml) | |||
629 | xml_end_multitable (); | |||
630 | ||||
631 | #if 0 | |||
632 | printf (_("** Multicolumn output from last row:\n")((const char *) ("** Multicolumn output from last row:\n"))); | |||
633 | for (i = 1; i <= last_column; i++) { | |||
634 | select_output_environment (i); | |||
635 | printf (_("* column #%d: output = %s\n")((const char *) ("* column #%d: output = %s\n")), i, output_paragraph); | |||
636 | } | |||
637 | #endif | |||
638 | } |