Line data Source code
1 : /* Copyright (C) 2002-2025 Free Software Foundation, Inc.
2 : *
3 : * Gaius Mulley (gaius@glam.ac.uk) wrote html-table.cpp
4 : *
5 : * html-table.h
6 : *
7 : * provides the methods necessary to handle indentation and tab
8 : * positions using html tables.
9 : */
10 :
11 : /*
12 : This file is part of groff, the GNU roff typesetting system.
13 :
14 : groff is free software; you can redistribute it and/or modify it under
15 : the terms of the GNU General Public License as published by the Free
16 : Software Foundation, either version 3 of the License, or
17 : (at your option) any later version.
18 :
19 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
20 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 : for more details.
23 :
24 : You should have received a copy of the GNU General Public License
25 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
26 :
27 : #ifdef HAVE_CONFIG_H
28 : #include <config.h>
29 : #endif
30 :
31 : #include <stdio.h> // FILE
32 : #include <stdlib.h> // atoi()
33 :
34 : #include "driver.h"
35 : #include "stringclass.h"
36 : #include "cset.h" // csspace()
37 : #include "html-table.h"
38 : #include "ctype.h"
39 : #include "html.h"
40 : #include "html-text.h"
41 :
42 : #if !defined(TRUE)
43 : # define TRUE (1==1)
44 : #endif
45 : #if !defined(FALSE)
46 : # define FALSE (1==0)
47 : #endif
48 :
49 : extern html_dialect dialect;
50 :
51 :
52 269 : tabs::tabs ()
53 269 : : tab(NULL)
54 : {
55 269 : }
56 :
57 538 : tabs::~tabs ()
58 : {
59 269 : delete_list();
60 269 : }
61 :
62 : /*
63 : * delete_list - frees the tab list and sets tab to NULL.
64 : */
65 :
66 288 : void tabs::delete_list (void)
67 : {
68 288 : tab_position *p = tab;
69 : tab_position *q;
70 :
71 1293 : while (p != NULL) {
72 1005 : q = p;
73 1005 : p = p->next;
74 1005 : delete q;
75 : }
76 288 : tab = NULL;
77 288 : }
78 :
79 19 : void tabs::clear (void)
80 : {
81 19 : delete_list();
82 19 : }
83 :
84 : /*
85 : * compatible - returns TRUE if the tab stops in, s, do
86 : * not conflict with the current tab stops.
87 : * The new tab stops are _not_ placed into
88 : * this class.
89 : */
90 :
91 33 : int tabs::compatible (const char *s)
92 : {
93 : char align;
94 33 : int total=0;
95 33 : tab_position *last = tab;
96 :
97 33 : if (last == NULL)
98 14 : return FALSE; // no tab stops defined
99 :
100 : // move over tag name
101 209 : while ((*s != '\0') && !csspace(*s))
102 190 : s++;
103 :
104 270 : while (*s != '\0' && last != NULL) {
105 : // move over whitespace
106 521 : while ((*s != '\0') && csspace(*s))
107 270 : s++;
108 : // collect alignment
109 251 : align = *s;
110 : // move over alignment
111 251 : s++;
112 : // move over whitespace
113 502 : while ((*s != '\0') && csspace(*s))
114 251 : s++;
115 : // collect tab position
116 251 : total = atoi(s);
117 : // move over tab position
118 1103 : while ((*s != '\0') && !csspace(*s))
119 852 : s++;
120 251 : if (last->alignment != align || last->position != total)
121 0 : return FALSE;
122 :
123 251 : last = last->next;
124 : }
125 19 : return TRUE;
126 : }
127 :
128 : /*
129 : * init - scans the string, s, and initializes the tab stops.
130 : */
131 :
132 19 : void tabs::init (const char *s)
133 : {
134 : char align;
135 19 : int total=0;
136 19 : tab_position *last = NULL;
137 :
138 19 : clear(); // remove any tab stops
139 :
140 : // move over tag name
141 209 : while ((*s != '\0') && !csspace(*s))
142 190 : s++;
143 :
144 1024 : while (*s != '\0') {
145 : // move over whitespace
146 2029 : while ((*s != '\0') && csspace(*s))
147 1024 : s++;
148 : // collect alignment
149 1005 : align = *s;
150 : // move over alignment
151 1005 : s++;
152 : // move over whitespace
153 2010 : while ((*s != '\0') && csspace(*s))
154 1005 : s++;
155 : // collect tab position
156 1005 : total = atoi(s);
157 : // move over tab position
158 5341 : while ((*s != '\0') && !csspace(*s))
159 4336 : s++;
160 1005 : if (last == NULL) {
161 19 : tab = new tab_position;
162 19 : last = tab;
163 : } else {
164 986 : last->next = new tab_position;
165 986 : last = last->next;
166 : }
167 1005 : last->alignment = align;
168 1005 : last->position = total;
169 1005 : last->next = NULL;
170 : }
171 19 : }
172 :
173 : /*
174 : * check_init - define tab stops using, s, providing none already exist.
175 : */
176 :
177 8 : void tabs::check_init (const char *s)
178 : {
179 8 : if (tab == NULL)
180 3 : init(s);
181 8 : }
182 :
183 : /*
184 : * find_tab - returns the tab number corresponding to the position, pos.
185 : */
186 :
187 9 : int tabs::find_tab (int pos)
188 : {
189 : tab_position *p;
190 9 : int i=0;
191 :
192 9 : for (p = tab; p != NULL; p = p->next) {
193 2 : i++;
194 2 : if (p->position == pos)
195 2 : return i;
196 : }
197 7 : return 0;
198 : }
199 :
200 : /*
201 : * get_tab_pos - returns the, nth, tab position
202 : */
203 :
204 11 : int tabs::get_tab_pos (int n)
205 : {
206 : tab_position *p;
207 :
208 11 : n--;
209 13 : for (p = tab; (p != NULL) && (n>0); p = p->next) {
210 6 : n--;
211 6 : if (n == 0)
212 4 : return p->position;
213 : }
214 7 : return 0;
215 : }
216 :
217 0 : char tabs::get_tab_align (int n)
218 : {
219 : tab_position *p;
220 :
221 0 : n--;
222 0 : for (p = tab; (p != NULL) && (n>0); p = p->next) {
223 0 : n--;
224 0 : if (n == 0)
225 0 : return p->alignment;
226 : }
227 0 : return 'L';
228 : }
229 :
230 : /*
231 : * dump_tab - display tab positions
232 : */
233 :
234 0 : void tabs::dump_tabs (void)
235 : {
236 0 : int i=1;
237 : tab_position *p;
238 :
239 0 : for (p = tab; p != NULL; p = p->next) {
240 0 : printf("tab %d is %d\n", i, p->position);
241 0 : i++;
242 : }
243 0 : }
244 :
245 : /*
246 : * html_table - methods
247 : */
248 :
249 269 : html_table::html_table (simple_output *op, int linelen)
250 269 : : out(op), columns(NULL), linelength(linelen), last_col(NULL), start_space(FALSE)
251 : {
252 269 : tab_stops = new tabs();
253 269 : }
254 :
255 538 : html_table::~html_table ()
256 : {
257 : cols *c;
258 269 : if (tab_stops != NULL)
259 269 : delete tab_stops;
260 :
261 269 : c = columns;
262 777 : while (columns != NULL) {
263 508 : columns = columns->next;
264 508 : delete c;
265 508 : c = columns;
266 : }
267 269 : }
268 :
269 : /*
270 : * remove_cols - remove a list of columns as defined by, c.
271 : */
272 :
273 10 : void html_table::remove_cols (cols *c)
274 : {
275 : cols *p;
276 :
277 10 : while (c != NULL) {
278 0 : p = c;
279 0 : c = c->next;
280 0 : delete p;
281 : }
282 10 : }
283 :
284 : /*
285 : * set_linelength - sets the line length value in this table.
286 : * It also adds an extra blank column to the
287 : * table should linelen exceed the last column.
288 : */
289 :
290 20 : void html_table::set_linelength (int linelen)
291 : {
292 20 : cols *p = NULL;
293 : cols *c;
294 20 : linelength = linelen;
295 :
296 38 : for (c = columns; c != NULL; c = c->next) {
297 28 : if (c->right > linelength) {
298 10 : c->right = linelength;
299 10 : remove_cols(c->next);
300 10 : c->next = NULL;
301 10 : return;
302 : }
303 18 : p = c;
304 : }
305 10 : if (p != NULL && p->right > 0 && linelength > p->right)
306 8 : add_column(p->no+1, p->right, linelength, 'L');
307 : }
308 :
309 : /*
310 : * get_effective_linelength -
311 : */
312 :
313 532 : int html_table::get_effective_linelength (void)
314 : {
315 532 : if (columns != NULL)
316 532 : return linelength - columns->left;
317 : else
318 0 : return linelength;
319 : }
320 :
321 : /*
322 : * add_indent - adds the indent to a table.
323 : */
324 :
325 250 : void html_table::add_indent (int indent)
326 : {
327 250 : if (columns != NULL && columns->left > indent)
328 242 : add_column(0, indent, columns->left, 'L');
329 250 : }
330 :
331 : /*
332 : * emit_table_header - emits the html header for this table.
333 : */
334 :
335 20 : void html_table::emit_table_header (int space)
336 : {
337 20 : if (columns == NULL)
338 0 : return;
339 :
340 : // dump_table();
341 :
342 20 : last_col = NULL;
343 20 : if (linelength > 0) {
344 20 : out->nl();
345 20 : out->nl();
346 :
347 20 : out->put_string("<table width=\"100%\"")
348 20 : .put_string(" border=\"0\" rules=\"none\" frame=\"void\"\n")
349 20 : .put_string(" cellspacing=\"0\" cellpadding=\"0\"");
350 20 : out->put_string(">")
351 20 : .nl();
352 20 : if (dialect == xhtml)
353 0 : emit_colspan();
354 20 : out->put_string("<tr valign=\"top\" align=\"left\"");
355 20 : if (space) {
356 0 : out->put_string(" style=\"margin-top: ");
357 0 : out->put_string(STYLE_VERTICAL_SPACE);
358 0 : out->put_string("\"");
359 : }
360 20 : out->put_string(">").nl();
361 : }
362 : }
363 :
364 : /*
365 : * get_right - returns the right most position of this column.
366 : */
367 :
368 85 : int html_table::get_right (cols *c)
369 : {
370 85 : if (c != NULL && c->right > 0)
371 78 : return c->right;
372 7 : if (c->next != NULL)
373 0 : return c->left;
374 7 : return linelength;
375 : }
376 :
377 : /*
378 : * set_space - assigns start_space. Used to determine the
379 : * vertical alignment when generating the next table row.
380 : */
381 :
382 0 : void html_table::set_space (int space)
383 : {
384 0 : start_space = space;
385 0 : }
386 :
387 : /*
388 : * emit_colspan - emits a series of colspan entries defining the
389 : * table columns.
390 : */
391 :
392 0 : void html_table::emit_colspan (void)
393 : {
394 0 : cols *b = columns;
395 0 : cols *c = columns;
396 0 : int width = 0;
397 :
398 0 : out->put_string("<colgroup>");
399 0 : while (c != NULL) {
400 0 : if (b != NULL && b != c && is_gap(b))
401 : /*
402 : * blank column for gap
403 : */
404 0 : out->put_string("<col width=\"")
405 0 : .put_number(is_gap(b))
406 0 : .put_string("%\" class=\"center\"></col>")
407 0 : .nl();
408 :
409 0 : width = (get_right(c)*100 + get_effective_linelength()/2)
410 0 : / get_effective_linelength()
411 0 : - (c->left*100 + get_effective_linelength()/2)
412 0 : /get_effective_linelength();
413 0 : switch (c->alignment) {
414 0 : case 'C':
415 0 : out->put_string("<col width=\"")
416 0 : .put_number(width)
417 0 : .put_string("%\" class=\"center\"></col>")
418 0 : .nl();
419 0 : break;
420 0 : case 'R':
421 0 : out->put_string("<col width=\"")
422 0 : .put_number(width)
423 0 : .put_string("%\" class=\"right\"></col>")
424 0 : .nl();
425 0 : break;
426 0 : default:
427 0 : out->put_string("<col width=\"")
428 0 : .put_number(width)
429 0 : .put_string("%\"></col>")
430 0 : .nl();
431 : }
432 0 : b = c;
433 0 : c = c->next;
434 : }
435 0 : out->put_string("</colgroup>").nl();
436 0 : }
437 :
438 : /*
439 : * emit_td - writes out a <td> tag with a corresponding width
440 : * if the dialect is html4.
441 : */
442 :
443 133 : void html_table::emit_td (int percentage, const char *s)
444 : {
445 133 : if (percentage) {
446 100 : if (dialect == html4) {
447 100 : out->put_string("<td width=\"")
448 100 : .put_number(percentage)
449 100 : .put_string("%\"");
450 100 : if (s != NULL)
451 100 : out->put_string(s);
452 100 : out->nl();
453 : }
454 : else {
455 0 : out->put_string("<td");
456 0 : if (s != NULL)
457 0 : out->put_string(s);
458 0 : out->nl();
459 : }
460 : }
461 133 : }
462 :
463 : /*
464 : * emit_col - moves onto column, n.
465 : */
466 :
467 63 : void html_table::emit_col (int n)
468 : {
469 63 : cols *c = columns;
470 63 : cols *b = columns;
471 63 : int width = 0;
472 :
473 : // must be a different row
474 63 : if (last_col != NULL && n <= last_col->no)
475 17 : emit_new_row();
476 :
477 122 : while (c != NULL && c->no < n)
478 59 : c = c->next;
479 :
480 : // can we find column, n?
481 63 : if (c != NULL && c->no == n) {
482 : // shutdown previous column
483 63 : if (last_col != NULL)
484 26 : out->put_string("</td>").nl();
485 :
486 : // find previous column
487 63 : if (last_col == NULL)
488 37 : b = columns;
489 : else
490 26 : b = last_col;
491 :
492 : // have we a gap?
493 63 : if (last_col != NULL) {
494 26 : emit_td(is_gap(b), "></td>");
495 26 : b = b->next;
496 : }
497 :
498 : // move across to column n
499 85 : while (b != c) {
500 : // we compute the difference after converting positions
501 : // to avoid rounding errors
502 22 : width = (get_right(b)*100 + get_effective_linelength()/2)
503 22 : / get_effective_linelength()
504 22 : - (b->left*100 + get_effective_linelength()/2)
505 22 : /get_effective_linelength();
506 22 : emit_td(width, "></td>");
507 : // have we a gap?
508 22 : emit_td(is_gap(b), "></td>");
509 22 : b = b->next;
510 : }
511 63 : width = (get_right(b)*100 + get_effective_linelength()/2)
512 63 : / get_effective_linelength()
513 63 : - (b->left*100 + get_effective_linelength()/2)
514 63 : /get_effective_linelength();
515 63 : switch (b->alignment) {
516 0 : case 'C':
517 0 : emit_td(width, " align=center>");
518 0 : break;
519 0 : case 'R':
520 0 : emit_td(width, " align=right>");
521 0 : break;
522 63 : default:
523 63 : emit_td(width);
524 : }
525 : // remember column, b
526 63 : last_col = b;
527 : }
528 63 : }
529 :
530 : /*
531 : * finish_row -
532 : */
533 :
534 37 : void html_table::finish_row (void)
535 : {
536 37 : int n = 0;
537 : cols *c;
538 :
539 37 : if (last_col != NULL) {
540 44 : for (c = last_col->next; c != NULL; c = c->next)
541 7 : n = c->no;
542 :
543 37 : if (n > 0)
544 7 : emit_col(n);
545 : #if 1
546 37 : if (last_col != NULL) {
547 37 : out->put_string("</td>");
548 37 : last_col = NULL;
549 : }
550 : #endif
551 37 : out->put_string("</tr>").nl();
552 : }
553 37 : }
554 :
555 : /*
556 : * emit_new_row - move to the next row.
557 : */
558 :
559 17 : void html_table::emit_new_row (void)
560 : {
561 17 : finish_row();
562 :
563 17 : out->put_string("<tr valign=\"top\" align=\"left\"");
564 17 : if (start_space) {
565 0 : out->put_string(" style=\"margin-top: ");
566 0 : out->put_string(STYLE_VERTICAL_SPACE);
567 0 : out->put_string("\"");
568 : }
569 17 : out->put_string(">").nl();
570 17 : start_space = FALSE;
571 17 : last_col = NULL;
572 17 : }
573 :
574 20 : void html_table::emit_finish_table (void)
575 : {
576 20 : finish_row();
577 20 : out->put_string("</table>");
578 20 : }
579 :
580 : /*
581 : * add_column - adds a column. It returns FALSE if hstart..hend
582 : * crosses into a different columns.
583 : */
584 :
585 850 : int html_table::add_column (int coln, int hstart, int hend, char align)
586 : {
587 850 : cols *c = get_column(coln);
588 :
589 850 : if (c == NULL)
590 517 : return insert_column(coln, hstart, hend, align);
591 : else
592 333 : return modify_column(c, hstart, hend, align);
593 : }
594 :
595 : /*
596 : * get_column - returns the column, coln.
597 : */
598 :
599 850 : cols *html_table::get_column (int coln)
600 : {
601 850 : cols *c = columns;
602 :
603 1307 : while (c != NULL && coln != c->no)
604 457 : c = c->next;
605 :
606 850 : if (c != NULL && coln == c->no)
607 333 : return c;
608 : else
609 517 : return NULL;
610 : }
611 :
612 : /*
613 : * insert_column - inserts a column, coln.
614 : * It returns TRUE if it does not bump into
615 : * another column.
616 : */
617 :
618 517 : int html_table::insert_column (int coln, int hstart, int hend, char align)
619 : {
620 517 : cols *c = columns;
621 517 : cols *l = columns;
622 517 : cols *n = NULL;
623 :
624 539 : while (c != NULL && c->no < coln) {
625 22 : l = c;
626 22 : c = c->next;
627 : }
628 517 : if (l != NULL && l->no>coln && hend > l->left)
629 4 : return FALSE; // new column bumps into previous one
630 :
631 513 : l = NULL;
632 513 : c = columns;
633 535 : while (c != NULL && c->no < coln) {
634 22 : l = c;
635 22 : c = c->next;
636 : }
637 :
638 513 : if ((l != NULL) && (hstart < l->right))
639 5 : return FALSE; // new column bumps into previous one
640 :
641 508 : if ((l != NULL) && (l->next != NULL) &&
642 0 : (l->next->left < hend))
643 0 : return FALSE; // new column bumps into next one
644 :
645 508 : n = new cols;
646 508 : if (l == NULL) {
647 493 : n->next = columns;
648 493 : columns = n;
649 : } else {
650 15 : n->next = l->next;
651 15 : l->next = n;
652 : }
653 508 : n->left = hstart;
654 508 : n->right = hend;
655 508 : n->no = coln;
656 508 : n->alignment = align;
657 508 : return TRUE;
658 : }
659 :
660 : /*
661 : * modify_column - given a column, c, modify the width to
662 : * contain hstart..hend.
663 : * It returns TRUE if it does not clash with
664 : * the next or previous column.
665 : */
666 :
667 333 : int html_table::modify_column (cols *c, int hstart, int hend, char align)
668 : {
669 333 : cols *l = columns;
670 :
671 503 : while (l != NULL && l->next != c)
672 170 : l = l->next;
673 :
674 333 : if ((l != NULL) && (hstart < l->right))
675 0 : return FALSE; // new column bumps into previous one
676 :
677 333 : if ((c->next != NULL) && (c->next->left < hend))
678 2 : return FALSE; // new column bumps into next one
679 :
680 331 : if (c->left > hstart)
681 0 : c->left = hstart;
682 :
683 331 : if (c->right < hend)
684 99 : c->right = hend;
685 :
686 331 : c->alignment = align;
687 :
688 331 : return TRUE;
689 : }
690 :
691 : /*
692 : * find_tab_column - finds the column number for position, pos.
693 : * It searches through the list tab stops.
694 : */
695 :
696 9 : int html_table::find_tab_column (int pos)
697 : {
698 : // remember the first column is reserved for untabbed glyphs
699 9 : return tab_stops->find_tab(pos)+1;
700 : }
701 :
702 : /*
703 : * find_column - find the column number for position, pos.
704 : * It searches through the list of columns.
705 : */
706 :
707 11 : int html_table::find_column (int pos)
708 : {
709 11 : int p=0;
710 : cols *c;
711 :
712 39 : for (c = columns; c != NULL; c = c->next) {
713 30 : if (c->left > pos)
714 2 : return p;
715 28 : p = c->no;
716 : }
717 9 : return p;
718 : }
719 :
720 : /*
721 : * no_columns - returns the number of table columns (rather than tabs)
722 : */
723 :
724 0 : int html_table::no_columns (void)
725 : {
726 0 : int n=0;
727 : cols *c;
728 :
729 0 : for (c = columns; c != NULL; c = c->next)
730 0 : n++;
731 0 : return n;
732 : }
733 :
734 : /*
735 : * is_gap - returns the gap between column, c, and the next column.
736 : */
737 :
738 48 : int html_table::is_gap (cols *c)
739 : {
740 48 : if (c == NULL || c->right <= 0 || c->next == NULL)
741 0 : return 0;
742 : else
743 : // we compute the difference after converting positions
744 : // to avoid rounding errors
745 48 : return (c->next->left*100 + get_effective_linelength()/2)
746 48 : / get_effective_linelength()
747 48 : - (c->right*100 + get_effective_linelength()/2)
748 48 : / get_effective_linelength();
749 : }
750 :
751 : /*
752 : * no_gaps - returns the number of table gaps between the columns
753 : */
754 :
755 0 : int html_table::no_gaps (void)
756 : {
757 0 : int n=0;
758 : cols *c;
759 :
760 0 : for (c = columns; c != NULL; c = c->next)
761 0 : if (is_gap(c))
762 0 : n++;
763 0 : return n;
764 : }
765 :
766 : /*
767 : * get_tab_pos - returns the, nth, tab position
768 : */
769 :
770 11 : int html_table::get_tab_pos (int n)
771 : {
772 11 : return tab_stops->get_tab_pos(n);
773 : }
774 :
775 0 : char html_table::get_tab_align (int n)
776 : {
777 0 : return tab_stops->get_tab_align(n);
778 : }
779 :
780 :
781 0 : void html_table::dump_table (void)
782 : {
783 0 : if (columns != NULL) {
784 : cols *c;
785 0 : for (c = columns; c != NULL; c = c->next) {
786 0 : printf("column %d %d..%d %c\n", c->no, c->left, c->right, c->alignment);
787 : }
788 : } else
789 0 : tab_stops->dump_tabs();
790 0 : }
791 :
792 : /*
793 : * html_indent - creates an indent with indentation, ind, given
794 : * a line length of linelength.
795 : */
796 :
797 230 : html_indent::html_indent (simple_output *op, int ind, int pageoffset, int linelength)
798 : {
799 230 : table = new html_table(op, linelength);
800 :
801 230 : table->add_column(1, ind+pageoffset, linelength, 'L');
802 230 : table->add_indent(pageoffset);
803 230 : in = ind;
804 230 : pg = pageoffset;
805 230 : ll = linelength;
806 230 : }
807 :
808 460 : html_indent::~html_indent (void)
809 : {
810 230 : end();
811 230 : delete table;
812 230 : }
813 :
814 130 : void html_indent::begin (int space)
815 : {
816 130 : if (in + pg == 0) {
817 0 : if (space) {
818 0 : table->out->put_string(" style=\"margin-top: ");
819 0 : table->out->put_string(STYLE_VERTICAL_SPACE);
820 0 : table->out->put_string("\"");
821 : }
822 : }
823 : else {
824 : //
825 : // we use exactly the same mechanism for calculating
826 : // indentation as html_table::emit_col
827 : //
828 130 : table->out->put_string(" style=\"margin-left:")
829 130 : .put_number(((in + pg) * 100 + ll/2) / ll -
830 130 : (ll/2)/ll)
831 130 : .put_string("%;");
832 :
833 130 : if (space) {
834 122 : table->out->put_string(" margin-top: ");
835 122 : table->out->put_string(STYLE_VERTICAL_SPACE);
836 : }
837 130 : table->out->put_string("\"");
838 : }
839 130 : }
840 :
841 230 : void html_indent::end (void)
842 : {
843 230 : }
844 :
845 : /*
846 : * get_reg - collects the registers as supplied during initialization.
847 : */
848 :
849 0 : void html_indent::get_reg (int *ind, int *pageoffset, int *linelength)
850 : {
851 0 : *ind = in;
852 0 : *pageoffset = pg;
853 0 : *linelength = ll;
854 0 : }
855 :
856 : // Local Variables:
857 : // fill-column: 72
858 : // mode: C++
859 : // End:
860 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|