f6bc7a6f91b3bb6fbe96f920f46639ac5819071f
[pspp-builds.git] / src / output / ascii.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include <libpspp/message.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <libpspp/alloc.h>
27 #include <libpspp/message.h>
28 #include "chart.h"
29 #include <libpspp/compiler.h>
30 #include <data/filename.h>
31 #include <libpspp/misc.h>
32 #include "output.h"
33 #include <libpspp/pool.h>
34 #include <libpspp/start-date.h>
35 #include <libpspp/version.h>
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
40 /* ASCII driver options: (defaults listed first)
41
42    output-file="pspp.list"
43    char-set=ascii|latin1
44    form-feed-string="\f"        Written as a formfeed.
45    newline-string=default|"\r\n"|"\n"   
46                                 Written as a newline.
47    paginate=on|off              Formfeeds are desired?
48    tab-width=8                  Width of a tab; 0 to not use tabs.
49    init=""                      Written at beginning of output.
50    done=""                      Written at end of output.
51    
52    headers=on|off               Put headers at top of page?
53    length=66
54    width=130
55    lpi=6                        Only used to determine font size.
56    cpi=10                       
57    squeeze=off|on               Squeeze multiple newlines into exactly one.
58
59    left-margin=0
60    right-margin=0
61    top-margin=2
62    bottom-margin=2
63
64    box[x]="strng"               Sets box character X (X in base 4: 0-3333).
65    italic-on=overstrike|"strng" Turns on italic (underline).
66    italic-off=""|"strng"        Turns off italic; ignored for overstrike.
67    bold-on=overstrike|"strng"   Turns on bold.
68    bold-off=""|"strng"          Turns off bold; ignored for overstrike.
69    bold-italic-on=overstrike|"strng" Turns on bold-italic.
70    bold-italic-off=""|"strng"   Turns off bold-italic; ignored for overstrike.
71    overstrike-style=single|line Can we print a whole line then BS over it, or
72    must we go char by char, as on a terminal?
73    carriage-return-style=bs|cr  Must we return the carriage with a sequence of
74    BSes, or will a single CR do it?
75  */
76
77 /* Disable messages by failed range checks. */
78 /*#define SUPPRESS_WARNINGS 1 */
79
80 /* Character set. */
81 enum
82   {
83     CHS_ASCII,                  /* 7-bit ASCII */
84     CHS_LATIN1                  /* Latin 1; not really supported at the moment */
85   };
86
87 /* Overstrike style. */
88 enum
89   {
90     OVS_SINGLE,                 /* Overstrike each character: "a\b_b\b_c\b_" */
91     OVS_LINE                    /* Overstrike lines: "abc\b\b\b___" (or if
92                                    newline is "\r\n", then "abc\r___").  Easier
93                                    on the printer, doesn't work on a tty. */
94   };
95
96 /* Basic output strings. */
97 enum
98   {
99     OPS_INIT,                   /* Document initialization string. */
100     OPS_DONE,                   /* Document uninit string. */
101     OPS_FORMFEED,               /* Formfeed string. */
102     OPS_NEWLINE,                /* Newline string. */
103
104     OPS_COUNT                   /* Number of output strings. */
105   };
106
107 /* Line styles bit shifts. */
108 enum
109   {
110     LNS_TOP = 0,
111     LNS_LEFT = 2,
112     LNS_BOTTOM = 4,
113     LNS_RIGHT = 6,
114
115     LNS_COUNT = 256
116   };
117
118 /* Carriage return style. */
119 enum
120   {
121     CRS_BS,                     /* Multiple backspaces. */
122     CRS_CR                      /* Single carriage return. */
123   };
124
125 /* Assembles a byte from four taystes. */
126 #define TAYSTE2BYTE(T, L, B, R)                 \
127         (((T) << LNS_TOP)                       \
128          | ((L) << LNS_LEFT)                    \
129          | ((B) << LNS_BOTTOM)                  \
130          | ((R) << LNS_RIGHT))
131
132 /* Extract tayste with shift value S from byte B. */
133 #define BYTE2TAYSTE(B, S)                       \
134         (((B) >> (S)) & 3)
135
136 /* Font style; take one of the first group |'d with one of the second group. */
137 enum
138   {
139     FSTY_ON = 000,              /* Turn font on. */
140     FSTY_OFF = 001,             /* Turn font off. */
141
142     FSTY_ITALIC = 0,            /* Italic font. */
143     FSTY_BOLD = 2,              /* Bold font. */
144     FSTY_BOLD_ITALIC = 4,       /* Bold-italic font. */
145
146     FSTY_COUNT = 6              /* Number of font styles. */
147   };
148
149 /* A line of text. */
150 struct line 
151   {
152     unsigned short *chars;      /* Characters and attributes. */
153     int char_cnt;               /* Length. */
154     int char_cap;               /* Allocated bytes. */
155   };
156
157 /* ASCII output driver extension record. */
158 struct ascii_driver_ext
159   {
160     /* User parameters. */
161     int char_set;               /* CHS_ASCII/CHS_LATIN1; no-op right now. */
162     int headers;                /* 1=print headers at top of page. */
163     int page_length;            /* Page length in lines. */
164     int page_width;             /* Page width in characters. */
165     int lpi;                    /* Lines per inch. */
166     int cpi;                    /* Characters per inch. */
167     int left_margin;            /* Left margin in characters. */
168     int right_margin;           /* Right margin in characters. */
169     int top_margin;             /* Top margin in lines. */
170     int bottom_margin;          /* Bottom margin in lines. */
171     int paginate;               /* 1=insert formfeeds. */
172     int tab_width;              /* Width of a tab; 0 not to use tabs. */
173     struct fixed_string ops[OPS_COUNT]; /* Basic output strings. */
174     struct fixed_string box[LNS_COUNT]; /* Line & box drawing characters. */
175     struct fixed_string fonts[FSTY_COUNT]; /* Font styles; NULL=overstrike. */
176     int overstrike_style;       /* OVS_SINGLE or OVS_LINE. */
177     int carriage_return_style;  /* Carriage return style. */
178     int squeeze_blank_lines;    /* 1=squeeze multiple blank lines into one. */
179
180     /* Internal state. */
181     struct file_ext file;       /* Output file. */
182     int page_number;            /* Current page number. */
183     struct line *lines;         /* Page content. */
184     int lines_cap;              /* Number of lines allocated. */
185     int w, l;                   /* Actual width & length w/o margins, etc. */
186     int cur_font;               /* Current font by OUTP_F_*. */
187 #if DEBUGGING
188     int debug;                  /* Set by som_text_draw(). */
189 #endif
190   };
191
192 static int postopen (struct file_ext *);
193 static int preclose (struct file_ext *);
194
195 static struct outp_option_info *option_info;
196
197 static int
198 ascii_open_global (struct outp_class *this UNUSED)
199 {
200   option_info = xmalloc (sizeof *option_info);
201   option_info->initial = 0;
202   option_info->options = 0;
203   return 1;
204 }
205
206
207 static char *s;
208 static int
209 ascii_close_global (struct outp_class *this UNUSED)
210 {
211   free(option_info->initial);
212   free(option_info->options);
213   free(option_info);
214   free(s);
215   return 1;
216 }
217
218 static int *
219 ascii_font_sizes (struct outp_class *this UNUSED, int *n_valid_sizes)
220 {
221   static int valid_sizes[] = {12, 12, 0, 0};
222
223   assert (n_valid_sizes);
224   *n_valid_sizes = 1;
225   return valid_sizes;
226 }
227
228 static int
229 ascii_preopen_driver (struct outp_driver *this)
230 {
231   struct ascii_driver_ext *x;
232   int i;
233   
234   assert (this->driver_open == 0);
235   msg (VM (1), _("ASCII driver initializing as `%s'..."), this->name);
236   this->ext = x = xmalloc (sizeof *x);
237   x->char_set = CHS_ASCII;
238   x->headers = 1;
239   x->page_length = 66;
240   x->page_width = 79;
241   x->lpi = 6;
242   x->cpi = 10;
243   x->left_margin = 0;
244   x->right_margin = 0;
245   x->top_margin = 2;
246   x->bottom_margin = 2;
247   x->paginate = 1;
248   x->tab_width = 8;
249   for (i = 0; i < OPS_COUNT; i++)
250     ls_null (&x->ops[i]);
251   for (i = 0; i < LNS_COUNT; i++)
252     ls_null (&x->box[i]);
253   for (i = 0; i < FSTY_COUNT; i++)
254     ls_null (&x->fonts[i]);
255   x->overstrike_style = OVS_SINGLE;
256   x->carriage_return_style = CRS_BS;
257   x->squeeze_blank_lines = 0;
258   x->file.filename = NULL;
259   x->file.mode = "wb";
260   x->file.file = NULL;
261   x->file.sequence_no = &x->page_number;
262   x->file.param = x;
263   x->file.postopen = postopen;
264   x->file.preclose = preclose;
265   x->page_number = 0;
266   x->lines = NULL;
267   x->lines_cap = 0;
268   x->cur_font = OUTP_F_R;
269 #if DEBUGGING
270   x->debug = 0;
271 #endif
272   return 1;
273 }
274
275 static int
276 ascii_postopen_driver (struct outp_driver *this)
277 {
278   struct ascii_driver_ext *x = this->ext;
279   
280   assert (this->driver_open == 0);
281   
282   if (NULL == x->file.filename)
283     x->file.filename = xstrdup ("pspp.list");
284   
285   x->w = x->page_width - x->left_margin - x->right_margin;
286   x->l = (x->page_length - (x->headers ? 3 : 0) - x->top_margin
287           - x->bottom_margin - 1);
288   if (x->w < 59 || x->l < 15)
289     {
290       msg (SE, _("ascii driver: Area of page excluding margins and headers "
291                  "must be at least 59 characters wide by 15 lines long.  Page as "
292                  "configured is only %d characters by %d lines."), x->w, x->l);
293       return 0;
294     }
295   
296   this->res = x->lpi * x->cpi;
297   this->horiz = x->lpi;
298   this->vert = x->cpi;
299   this->width = x->w * this->horiz;
300   this->length = x->l * this->vert;
301   
302   if (ls_null_p (&x->ops[OPS_FORMFEED]))
303     ls_create (&x->ops[OPS_FORMFEED], "\f");
304   if (ls_null_p (&x->ops[OPS_NEWLINE])
305       || !strcmp (ls_c_str (&x->ops[OPS_NEWLINE]), "default"))
306     {
307       ls_create (&x->ops[OPS_NEWLINE], "\n");
308       x->file.mode = "wt";
309     }
310   
311   {
312     int i;
313     
314     for (i = 0; i < LNS_COUNT; i++)
315       {
316         char c[2];
317         c[1] = 0;
318         if (!ls_null_p (&x->box[i]))
319           continue;
320         switch (i)
321           {
322           case TAYSTE2BYTE (0, 0, 0, 0):
323             c[0] = ' ';
324             break;
325
326           case TAYSTE2BYTE (0, 1, 0, 0):
327           case TAYSTE2BYTE (0, 1, 0, 1):
328           case TAYSTE2BYTE (0, 0, 0, 1):
329             c[0] = '-';
330             break;
331
332           case TAYSTE2BYTE (1, 0, 0, 0):
333           case TAYSTE2BYTE (1, 0, 1, 0):
334           case TAYSTE2BYTE (0, 0, 1, 0):
335             c[0] = '|';
336             break;
337
338           case TAYSTE2BYTE (0, 3, 0, 0):
339           case TAYSTE2BYTE (0, 3, 0, 3):
340           case TAYSTE2BYTE (0, 0, 0, 3):
341           case TAYSTE2BYTE (0, 2, 0, 0):
342           case TAYSTE2BYTE (0, 2, 0, 2):
343           case TAYSTE2BYTE (0, 0, 0, 2):
344             c[0] = '=';
345             break;
346
347           case TAYSTE2BYTE (3, 0, 0, 0):
348           case TAYSTE2BYTE (3, 0, 3, 0):
349           case TAYSTE2BYTE (0, 0, 3, 0):
350           case TAYSTE2BYTE (2, 0, 0, 0):
351           case TAYSTE2BYTE (2, 0, 2, 0):
352           case TAYSTE2BYTE (0, 0, 2, 0):
353             c[0] = '#';
354             break;
355
356           default:
357             if (BYTE2TAYSTE (i, LNS_LEFT) > 1
358                 || BYTE2TAYSTE (i, LNS_TOP) > 1
359                 || BYTE2TAYSTE (i, LNS_RIGHT) > 1
360                 || BYTE2TAYSTE (i, LNS_BOTTOM) > 1)
361               c[0] = '#';
362             else
363               c[0] = '+';
364             break;
365           }
366         ls_create (&x->box[i], c);
367       }
368   }
369   
370   {
371     int i;
372     
373     this->cp_x = this->cp_y = 0;
374     this->font_height = this->vert;
375     this->prop_em_width = this->horiz;
376     this->fixed_width = this->horiz;
377
378     this->horiz_line_width[0] = 0;
379     this->vert_line_width[0] = 0;
380     
381     for (i = 1; i < OUTP_L_COUNT; i++)
382       {
383         this->horiz_line_width[i] = this->vert;
384         this->vert_line_width[i] = this->horiz;
385       }
386     
387     for (i = 0; i < (1 << OUTP_L_COUNT); i++)
388       {
389         this->horiz_line_spacing[i] = (i & ~1) ? this->vert : 0;
390         this->vert_line_spacing[i] = (i & ~1) ? this->horiz : 0;
391       }
392   }
393   
394   this->driver_open = 1;
395   msg (VM (2), _("%s: Initialization complete."), this->name);
396
397   return 1;
398 }
399
400 static int
401 ascii_close_driver (struct outp_driver *this)
402 {
403   struct ascii_driver_ext *x = this->ext;
404   int i;
405   
406   assert (this->driver_open == 1);
407   msg (VM (2), _("%s: Beginning closing..."), this->name);
408   
409   x = this->ext;
410   for (i = 0; i < OPS_COUNT; i++)
411     ls_destroy (&x->ops[i]);
412   for (i = 0; i < LNS_COUNT; i++)
413     ls_destroy (&x->box[i]);
414   for (i = 0; i < FSTY_COUNT; i++)
415     ls_destroy (&x->fonts[i]);
416   if (x->lines != NULL) 
417     {
418       int line;
419       
420       for (line = 0; line < x->lines_cap; line++) 
421         free (x->lines[line].chars);
422       free (x->lines); 
423     }
424   fn_close_ext (&x->file);
425   free (x->file.filename);
426   free (x);
427   
428   this->driver_open = 0;
429   msg (VM (3), _("%s: Finished closing."), this->name);
430   
431   return 1;
432 }
433
434 /* Generic option types. */
435 enum
436   {
437     pos_int_arg = -10,
438     nonneg_int_arg,
439     string_arg,
440     font_string_arg,
441     boolean_arg
442   };
443
444 static struct outp_option option_tab[] =
445   {
446     {"headers", boolean_arg, 0},
447     {"output-file", 1, 0},
448     {"char-set", 2, 0},
449     {"length", pos_int_arg, 0},
450     {"width", pos_int_arg, 1},
451     {"lpi", pos_int_arg, 2},
452     {"cpi", pos_int_arg, 3},
453     {"init", string_arg, 0},
454     {"done", string_arg, 1},
455     {"left-margin", nonneg_int_arg, 0},
456     {"right-margin", nonneg_int_arg, 1},
457     {"top-margin", nonneg_int_arg, 2},
458     {"bottom-margin", nonneg_int_arg, 3},
459     {"paginate", boolean_arg, 1},
460     {"form-feed-string", string_arg, 2},
461     {"newline-string", string_arg, 3},
462     {"italic-on", font_string_arg, 0},
463     {"italic-off", font_string_arg, 1},
464     {"bold-on", font_string_arg, 2},
465     {"bold-off", font_string_arg, 3},
466     {"bold-italic-on", font_string_arg, 4},
467     {"bold-italic-off", font_string_arg, 5},
468     {"overstrike-style", 3, 0},
469     {"tab-width", nonneg_int_arg, 4},
470     {"carriage-return-style", 4, 0},
471     {"squeeze", boolean_arg, 2},
472     {"", 0, 0},
473   };
474
475 static void
476 ascii_option (struct outp_driver *this, const char *key,
477               const struct string *val)
478 {
479   struct ascii_driver_ext *x = this->ext;
480   int cat, subcat;
481   const char *value;
482
483   value = ds_c_str (val);
484   if (!strncmp (key, "box[", 4))
485     {
486       char *tail;
487       int indx = strtol (&key[4], &tail, 4);
488       if (*tail != ']' || indx < 0 || indx > LNS_COUNT)
489         {
490           msg (SE, _("Bad index value for `box' key: syntax is box[INDEX], "
491                "0 <= INDEX < %d decimal, with INDEX expressed in base 4."),
492                LNS_COUNT);
493           return;
494         }
495       if (!ls_null_p (&x->box[indx]))
496         msg (SW, _("Duplicate value for key `%s'."), key);
497       ls_create (&x->box[indx], value);
498       return;
499     }
500
501   cat = outp_match_keyword (key, option_tab, option_info, &subcat);
502   switch (cat)
503     {
504     case 0:
505       msg (SE, _("Unknown configuration parameter `%s' for ascii device driver."),
506            key);
507       break;
508     case 1:
509       free (x->file.filename);
510       x->file.filename = xstrdup (value);
511       break;
512     case 2:
513       if (!strcmp (value, "ascii"))
514         x->char_set = CHS_ASCII;
515       else if (!strcmp (value, "latin1"))
516         x->char_set = CHS_LATIN1;
517       else
518         msg (SE, _("Unknown character set `%s'.  Valid character sets are "
519              "`ascii' and `latin1'."), value);
520       break;
521     case 3:
522       if (!strcmp (value, "single"))
523         x->overstrike_style = OVS_SINGLE;
524       else if (!strcmp (value, "line"))
525         x->overstrike_style = OVS_LINE;
526       else
527         msg (SE, _("Unknown overstrike style `%s'.  Valid overstrike styles "
528              "are `single' and `line'."), value);
529       break;
530     case 4:
531       if (!strcmp (value, "bs"))
532         x->carriage_return_style = CRS_BS;
533       else if (!strcmp (value, "cr"))
534         x->carriage_return_style = CRS_CR;
535       else
536         msg (SE, _("Unknown carriage return style `%s'.  Valid carriage "
537              "return styles are `cr' and `bs'."), value);
538       break;
539     case pos_int_arg:
540       {
541         char *tail;
542         int arg;
543
544         errno = 0;
545         arg = strtol (value, &tail, 0);
546         if (arg < 1 || errno == ERANGE || *tail)
547           {
548             msg (SE, _("Positive integer required as value for `%s'."), key);
549             break;
550           }
551         switch (subcat)
552           {
553           case 0:
554             x->page_length = arg;
555             break;
556           case 1:
557             x->page_width = arg;
558             break;
559           case 2:
560             x->lpi = arg;
561             break;
562           case 3:
563             x->cpi = arg;
564             break;
565           default:
566             assert (0);
567           }
568       }
569       break;
570     case nonneg_int_arg:
571       {
572         char *tail;
573         int arg;
574
575         errno = 0;
576         arg = strtol (value, &tail, 0);
577         if (arg < 0 || errno == ERANGE || *tail)
578           {
579             msg (SE, _("Zero or positive integer required as value for `%s'."),
580                  key);
581             break;
582           }
583         switch (subcat)
584           {
585           case 0:
586             x->left_margin = arg;
587             break;
588           case 1:
589             x->right_margin = arg;
590             break;
591           case 2:
592             x->top_margin = arg;
593             break;
594           case 3:
595             x->bottom_margin = arg;
596             break;
597           case 4:
598             x->tab_width = arg;
599             break;
600           default:
601             assert (0);
602           }
603       }
604       break;
605     case string_arg:
606       {
607         struct fixed_string *s;
608         switch (subcat)
609           {
610           case 0:
611             s = &x->ops[OPS_INIT];
612             break;
613           case 1:
614             s = &x->ops[OPS_DONE];
615             break;
616           case 2:
617             s = &x->ops[OPS_FORMFEED];
618             break;
619           case 3:
620             s = &x->ops[OPS_NEWLINE];
621             break;
622           default:
623             assert (0);
624             abort ();
625           }
626         ls_create (s, value);
627       }
628       break;
629     case font_string_arg:
630       {
631         if (!strcmp (value, "overstrike"))
632           {
633             ls_destroy (&x->fonts[subcat]);
634             return;
635           }
636         ls_create (&x->fonts[subcat], value);
637       }
638       break;
639     case boolean_arg:
640       {
641         int setting;
642         if (!strcmp (value, "on") || !strcmp (value, "true")
643             || !strcmp (value, "yes") || atoi (value))
644           setting = 1;
645         else if (!strcmp (value, "off") || !strcmp (value, "false")
646                  || !strcmp (value, "no") || !strcmp (value, "0"))
647           setting = 0;
648         else
649           {
650             msg (SE, _("Boolean value expected for %s."), key);
651             return;
652           }
653         switch (subcat)
654           {
655           case 0:
656             x->headers = setting;
657             break;
658           case 1:
659             x->paginate = setting;
660             break;
661           case 2:
662             x->squeeze_blank_lines = setting;
663             break;
664           default:
665             assert (0);
666           }
667       }
668       break;
669     default:
670       assert (0);
671     }
672 }
673
674 int
675 postopen (struct file_ext *f)
676 {
677   struct ascii_driver_ext *x = f->param;
678   struct fixed_string *s = &x->ops[OPS_INIT];
679
680   if (!ls_empty_p (s) && fwrite (ls_c_str (s), ls_length (s), 1, f->file) < 1)
681     {
682       msg (ME, _("ASCII output driver: %s: %s"),
683            f->filename, strerror (errno));
684       return 0;
685     }
686   return 1;
687 }
688
689 int
690 preclose (struct file_ext *f)
691 {
692   struct ascii_driver_ext *x = f->param;
693   struct fixed_string *d = &x->ops[OPS_DONE];
694
695   if (!ls_empty_p (d) && fwrite (ls_c_str (d), ls_length (d), 1, f->file) < 1)
696     {
697       msg (ME, _("ASCII output driver: %s: %s"),
698            f->filename, strerror (errno));
699       return 0;
700     }
701   return 1;
702 }
703
704 static int
705 ascii_open_page (struct outp_driver *this)
706 {
707   struct ascii_driver_ext *x = this->ext;
708   int i;
709
710   assert (this->driver_open && !this->page_open);
711   x->page_number++;
712   if (!fn_open_ext (&x->file))
713     {
714       msg (ME, _("ASCII output driver: %s: %s"), x->file.filename,
715            strerror (errno));
716       return 0;
717     }
718
719   if (x->l > x->lines_cap)
720     {
721       x->lines = xnrealloc (x->lines, x->l, sizeof *x->lines);
722       for (i = x->lines_cap; i < x->l; i++) 
723         {
724           struct line *line = &x->lines[i];
725           line->chars = NULL;
726           line->char_cap = 0;
727         }
728       x->lines_cap = x->l;
729     }
730
731   for (i = 0; i < x->l; i++)
732     x->lines[i].char_cnt = 0;
733
734   this->page_open = 1;
735   return 1;
736 }
737
738 /* Ensures that at least the first L characters of line I in the
739    driver identified by struct ascii_driver_ext *X have been cleared out. */
740 static inline void
741 expand_line (struct ascii_driver_ext *x, int i, int l)
742 {
743   struct line *line;
744   int j;
745
746   assert (i < x->lines_cap);
747   line = &x->lines[i];
748   if (l > line->char_cap) 
749     {
750       line->char_cap = l * 2;
751       line->chars = xnrealloc (line->chars,
752                                line->char_cap, sizeof *line->chars); 
753     }
754   for (j = line->char_cnt; j < l; j++)
755     line->chars[j] = ' ';
756   line->char_cnt = l;
757 }
758
759 /* Puts line L at (H,K) in the current output page.  Assumes
760    struct ascii_driver_ext named `ext'. */
761 #define draw_line(H, K, L)                              \
762         ext->lines[K].chars[H] = (L) | 0x800
763
764 /* Line styles for each position. */
765 #define T(STYLE) (STYLE<<LNS_TOP)
766 #define L(STYLE) (STYLE<<LNS_LEFT)
767 #define B(STYLE) (STYLE<<LNS_BOTTOM)
768 #define R(STYLE) (STYLE<<LNS_RIGHT)
769
770 static void
771 ascii_line_horz (struct outp_driver *this, const struct rect *r,
772                  const struct color *c UNUSED, int style)
773 {
774   struct ascii_driver_ext *ext = this->ext;
775   int x1 = r->x1 / this->horiz;
776   int x2 = r->x2 / this->horiz;
777   int y1 = r->y1 / this->vert;
778   int x;
779
780   assert (this->driver_open && this->page_open);
781   if (x1 == x2)
782     return;
783 #if DEBUGGING
784   if (x1 > x2
785       || x1 < 0 || x1 >= ext->w
786       || x2 <= 0 || x2 > ext->w
787       || y1 < 0 || y1 >= ext->l)
788     {
789 #if !SUPPRESS_WARNINGS
790       printf (_("ascii_line_horz: bad hline (%d,%d),%d out of (%d,%d)\n"),
791               x1, x2, y1, ext->w, ext->l);
792 #endif
793       return;
794     }
795 #endif
796
797   if (ext->lines[y1].char_cnt < x2)
798     expand_line (ext, y1, x2);
799
800   for (x = x1; x < x2; x++)
801     draw_line (x, y1, (style << LNS_LEFT) | (style << LNS_RIGHT));
802 }
803
804 static void
805 ascii_line_vert (struct outp_driver *this, const struct rect *r,
806                  const struct color *c UNUSED, int style)
807 {
808   struct ascii_driver_ext *ext = this->ext;
809   int x1 = r->x1 / this->horiz;
810   int y1 = r->y1 / this->vert;
811   int y2 = r->y2 / this->vert;
812   int y;
813
814   assert (this->driver_open && this->page_open);
815   if (y1 == y2)
816     return;
817 #if DEBUGGING
818   if (y1 > y2
819       || x1 < 0 || x1 >= ext->w
820       || y1 < 0 || y1 >= ext->l
821       || y2 < 0 || y2 > ext->l)
822     {
823 #if !SUPPRESS_WARNINGS
824       printf (_("ascii_line_vert: bad vline %d,(%d,%d) out of (%d,%d)\n"),
825               x1, y1, y2, ext->w, ext->l);
826 #endif
827       return;
828     }
829 #endif
830
831   for (y = y1; y < y2; y++)
832     if (ext->lines[y].char_cnt <= x1)
833       expand_line (ext, y, x1 + 1);
834
835   for (y = y1; y < y2; y++)
836     draw_line (x1, y, (style << LNS_TOP) | (style << LNS_BOTTOM));
837 }
838
839 static void
840 ascii_line_intersection (struct outp_driver *this, const struct rect *r,
841                          const struct color *c UNUSED,
842                          const struct outp_styles *style)
843 {
844   struct ascii_driver_ext *ext = this->ext;
845   int x = r->x1 / this->horiz;
846   int y = r->y1 / this->vert;
847   int l;
848
849   assert (this->driver_open && this->page_open);
850 #if DEBUGGING
851   if (x < 0 || x >= ext->w || y < 0 || y >= ext->l)
852     {
853 #if !SUPPRESS_WARNINGS
854       printf (_("ascii_line_intersection: bad intsct (%d,%d) out of (%d,%d)\n"),
855               x, y, ext->w, ext->l);
856 #endif
857       return;
858     }
859 #endif
860
861   l = ((style->l << LNS_LEFT) | (style->r << LNS_RIGHT)
862        | (style->t << LNS_TOP) | (style->b << LNS_BOTTOM));
863
864   if (ext->lines[y].char_cnt <= x)
865     expand_line (ext, y, x + 1);
866   draw_line (x, y, l);
867 }
868
869 /* FIXME: Later we could set this up so that for certain devices it
870    performs shading? */
871 static void
872 ascii_box (struct outp_driver *this UNUSED, const struct rect *r UNUSED,
873            const struct color *bord UNUSED, const struct color *fill UNUSED)
874 {
875   assert (this->driver_open && this->page_open);
876 }
877
878 /* Polylines not supported. */
879 static void
880 ascii_polyline_begin (struct outp_driver *this UNUSED, const struct color *c UNUSED)
881 {
882   assert (this->driver_open && this->page_open);
883 }
884 static void
885 ascii_polyline_point (struct outp_driver *this UNUSED, int x UNUSED, int y UNUSED)
886 {
887   assert (this->driver_open && this->page_open);
888 }
889 static void
890 ascii_polyline_end (struct outp_driver *this UNUSED)
891 {
892   assert (this->driver_open && this->page_open);
893 }
894
895 static void
896 ascii_text_set_font_by_name (struct outp_driver * this, const char *s)
897 {
898   struct ascii_driver_ext *x = this->ext;
899   int len = strlen (s);
900
901   assert (this->driver_open && this->page_open);
902   x->cur_font = OUTP_F_R;
903   if (len == 0)
904     return;
905   if (s[len - 1] == 'I')
906     {
907       if (len > 1 && s[len - 2] == 'B')
908         x->cur_font = OUTP_F_BI;
909       else
910         x->cur_font = OUTP_F_I;
911     }
912   else if (s[len - 1] == 'B')
913     x->cur_font = OUTP_F_B;
914 }
915
916 static void
917 ascii_text_set_font_by_position (struct outp_driver *this, int pos)
918 {
919   struct ascii_driver_ext *x = this->ext;
920   assert (this->driver_open && this->page_open);
921   x->cur_font = pos >= 0 && pos < 4 ? pos : 0;
922 }
923
924 static void
925 ascii_text_set_font_by_family (struct outp_driver *this UNUSED, const char *s UNUSED)
926 {
927   assert (this->driver_open && this->page_open);
928 }
929
930 static const char *
931 ascii_text_get_font_name (struct outp_driver *this)
932 {
933   struct ascii_driver_ext *x = this->ext;
934
935   assert (this->driver_open && this->page_open);
936   switch (x->cur_font)
937     {
938     case OUTP_F_R:
939       return "R";
940     case OUTP_F_I:
941       return "I";
942     case OUTP_F_B:
943       return "B";
944     case OUTP_F_BI:
945       return "BI";
946     default:
947       assert (0);
948     }
949   abort ();
950 }
951
952 static const char *
953 ascii_text_get_font_family (struct outp_driver *this UNUSED)
954 {
955   assert (this->driver_open && this->page_open);
956   return "";
957 }
958
959 static int
960 ascii_text_set_size (struct outp_driver *this, int size)
961 {
962   assert (this->driver_open && this->page_open);
963   return size == this->vert;
964 }
965
966 static int
967 ascii_text_get_size (struct outp_driver *this, int *em_width)
968 {
969   assert (this->driver_open && this->page_open);
970   if (em_width)
971     *em_width = this->horiz;
972   return this->vert;
973 }
974
975 static void text_draw (struct outp_driver *this, struct outp_text *t);
976
977 /* Divides the text T->S into lines of width T->H.  Sets T->V to the
978    number of lines necessary.  Actually draws the text if DRAW is
979    nonzero.
980
981    You probably don't want to look at this code. */
982 static void
983 delineate (struct outp_driver *this, struct outp_text *t, int draw)
984 {
985   /* Width we're fitting everything into. */
986   int width = t->h / this->horiz;
987
988   /* Maximum `y' position we can write to. */
989   int max_y;
990
991   /* Current position in string, character following end of string. */
992   const char *s = ls_c_str (&t->s);
993   const char *end = ls_end (&t->s);
994
995   /* Temporary struct outp_text to pass to low-level function. */
996   struct outp_text temp;
997
998 #if DEBUGGING && 0
999   if (!ext->debug)
1000     {
1001       ext->debug = 1;
1002       printf (_("%s: horiz=%d, vert=%d\n"), this->name, this->horiz, this->vert);
1003     }
1004 #endif
1005
1006   if (!width)
1007     {
1008       t->h = t->v = 0;
1009       return;
1010     }
1011
1012   if (draw)
1013     {
1014       temp.options = t->options;
1015       ls_shallow_copy (&temp.s, &t->s);
1016       temp.h = t->h / this->horiz;
1017       temp.x = t->x / this->horiz;
1018     }
1019   else
1020     t->y = 0;
1021   temp.y = t->y / this->vert;
1022
1023   if (t->options & OUTP_T_VERT)
1024     max_y = (t->v / this->vert) + temp.y - 1;
1025   else
1026     max_y = INT_MAX;
1027   
1028   while (end - s > width)
1029     {
1030       const char *beg = s;
1031       const char *space;
1032
1033       /* Find first space before &s[width]. */
1034       space = &s[width];
1035       for (;;)
1036         {
1037           if (space > s)
1038             {
1039               if (!isspace ((unsigned char) space[-1]))
1040                 {
1041                   space--;
1042                   continue;
1043                 }
1044               else
1045                 s = space;
1046             }
1047           else
1048             s = space = &s[width];
1049           break;
1050         }
1051
1052       /* Draw text. */
1053       if (draw)
1054         {
1055           ls_init (&temp.s, beg, space - beg);
1056           temp.w = space - beg;
1057           text_draw (this, &temp);
1058         }
1059       if (++temp.y > max_y)
1060         return;
1061
1062       /* Find first nonspace after space. */
1063       while (s < end && isspace ((unsigned char) *s))
1064         s++;
1065     }
1066   if (s < end)
1067     {
1068       if (draw)
1069         {
1070           ls_init (&temp.s, s, end - s);
1071           temp.w = end - s;
1072           text_draw (this, &temp);
1073         }
1074       temp.y++;
1075     }
1076
1077   t->v = (temp.y * this->vert) - t->y;
1078 }
1079
1080 static void
1081 ascii_text_metrics (struct outp_driver *this, struct outp_text *t)
1082 {
1083   assert (this->driver_open && this->page_open);
1084   if (!(t->options & OUTP_T_HORZ))
1085     {
1086       t->v = this->vert;
1087       t->h = ls_length (&t->s) * this->horiz;
1088     }
1089   else
1090     delineate (this, t, 0);
1091 }
1092
1093 static void
1094 ascii_text_draw (struct outp_driver *this, struct outp_text *t)
1095 {
1096   /* FIXME: orientations not supported. */
1097   assert (this->driver_open && this->page_open);
1098   if (!(t->options & OUTP_T_HORZ))
1099     {
1100       struct outp_text temp;
1101
1102       temp.options = t->options;
1103       temp.s = t->s;
1104       temp.h = temp.v = 0;
1105       temp.x = t->x / this->horiz;
1106       temp.y = t->y / this->vert;
1107       text_draw (this, &temp);
1108       ascii_text_metrics (this, t);
1109       
1110       return;
1111     }
1112   delineate (this, t, 1);
1113 }
1114
1115 static void
1116 text_draw (struct outp_driver *this, struct outp_text *t)
1117 {
1118   struct ascii_driver_ext *ext = this->ext;
1119   unsigned attr = ext->cur_font << 8;
1120
1121   int x = t->x;
1122   int y = t->y;
1123
1124   char *s = ls_c_str (&t->s);
1125
1126   /* Expand the line with the assumption that S takes up LEN character
1127      spaces (sometimes it takes up less). */
1128   int min_len;
1129
1130   assert (this->driver_open && this->page_open);
1131   switch (t->options & OUTP_T_JUST_MASK)
1132     {
1133     case OUTP_T_JUST_LEFT:
1134       break;
1135     case OUTP_T_JUST_CENTER:
1136       x -= (t->h - t->w) / 2;   /* fall through */
1137     case OUTP_T_JUST_RIGHT:
1138       x += (t->h - t->w);
1139       break;
1140     default:
1141       assert (0);
1142     }
1143
1144   if (!(t->y < ext->l && x < ext->w))
1145     return;
1146   min_len = min (x + ls_length (&t->s), ext->w);
1147   if (ext->lines[t->y].char_cnt < min_len)
1148     expand_line (ext, t->y, min_len);
1149
1150   {
1151     int len = ls_length (&t->s);
1152
1153     if (len + x > ext->w)
1154       len = ext->w - x;
1155     while (len--)
1156       ext->lines[y].chars[x++] = *s++ | attr;
1157   }
1158 }
1159 \f
1160 /* ascii_close_page () and support routines. */
1161
1162 #define LINE_BUF_SIZE 1024
1163 static char *line_buf;
1164 static char *line_p;
1165
1166 static inline int
1167 commit_line_buf (struct outp_driver *this)
1168 {
1169   struct ascii_driver_ext *x = this->ext;
1170   
1171   if ((int) fwrite (line_buf, 1, line_p - line_buf, x->file.file)
1172       < line_p - line_buf)
1173     {
1174       msg (ME, _("Writing `%s': %s"), x->file.filename, strerror (errno));
1175       return 0;
1176     }
1177
1178   line_p = line_buf;
1179   return 1;
1180 }
1181
1182 /* Writes everything from BP to EP exclusive into line_buf, or to
1183    THIS->output if line_buf overflows. */
1184 static inline void
1185 output_string (struct outp_driver *this, const char *bp, const char *ep)
1186 {
1187   if (LINE_BUF_SIZE - (line_p - line_buf) >= ep - bp)
1188     {
1189       memcpy (line_p, bp, ep - bp);
1190       line_p += ep - bp;
1191     }
1192   else
1193     while (bp < ep)
1194       {
1195         if (LINE_BUF_SIZE - (line_p - line_buf) <= 1 && !commit_line_buf (this))
1196           return;
1197         *line_p++ = *bp++;
1198       }
1199 }
1200
1201 /* Writes everything from BP to EP exclusive into line_buf, or to
1202    THIS->output if line_buf overflows.  Returns 1 if additional passes
1203    over the line are required.  FIXME: probably could do a lot of
1204    optimization here. */
1205 static inline int
1206 output_shorts (struct outp_driver *this,
1207                const unsigned short *bp, const unsigned short *ep)
1208 {
1209   struct ascii_driver_ext *ext = this->ext;
1210   size_t remaining = LINE_BUF_SIZE - (line_p - line_buf);
1211   int result = 0;
1212
1213   for (; bp < ep; bp++)
1214     {
1215       if (*bp & 0x800)
1216         {
1217           struct fixed_string *box = &ext->box[*bp & 0xff];
1218           size_t len = ls_length (box);
1219
1220           if (remaining >= len)
1221             {
1222               memcpy (line_p, ls_c_str (box), len);
1223               line_p += len;
1224               remaining -= len;
1225             }
1226           else
1227             {
1228               if (!commit_line_buf (this))
1229                 return 0;
1230               output_string (this, ls_c_str (box), ls_end (box));
1231               remaining = LINE_BUF_SIZE - (line_p - line_buf);
1232             }
1233         }
1234       else if (*bp & 0x0300)
1235         {
1236           struct fixed_string *on;
1237           char buf[5];
1238           int len;
1239
1240           switch (*bp & 0x0300)
1241             {
1242             case OUTP_F_I << 8:
1243               on = &ext->fonts[FSTY_ON | FSTY_ITALIC];
1244               break;
1245             case OUTP_F_B << 8:
1246               on = &ext->fonts[FSTY_ON | FSTY_BOLD];
1247               break;
1248             case OUTP_F_BI << 8:
1249               on = &ext->fonts[FSTY_ON | FSTY_BOLD_ITALIC];
1250               break;
1251             default:
1252               assert (0);
1253               abort ();
1254             }
1255           if (!on)
1256             {
1257               if (ext->overstrike_style == OVS_SINGLE)
1258                 switch (*bp & 0x0300)
1259                   {
1260                   case OUTP_F_I << 8:
1261                     buf[0] = '_';
1262                     buf[1] = '\b';
1263                     buf[2] = *bp;
1264                     len = 3;
1265                     break;
1266                   case OUTP_F_B << 8:
1267                     buf[0] = *bp;
1268                     buf[1] = '\b';
1269                     buf[2] = *bp;
1270                     len = 3;
1271                     break;
1272                   case OUTP_F_BI << 8:
1273                     buf[0] = '_';
1274                     buf[1] = '\b';
1275                     buf[2] = *bp;
1276                     buf[3] = '\b';
1277                     buf[4] = *bp;
1278                     len = 5;
1279                     break;
1280                   default:
1281                     assert (0);
1282                     abort ();
1283                   }
1284               else
1285                 {
1286                   buf[0] = *bp;
1287                   result = len = 1;
1288                 }
1289             }
1290           else
1291             {
1292               buf[0] = *bp;
1293               len = 1;
1294             }
1295           output_string (this, buf, &buf[len]);
1296         }
1297       else if (remaining)
1298         {
1299           *line_p++ = *bp;
1300           remaining--;
1301         }
1302       else
1303         {
1304           if (!commit_line_buf (this))
1305             return 0;
1306           remaining = LINE_BUF_SIZE - (line_p - line_buf);
1307           *line_p++ = *bp;
1308         }
1309     }
1310
1311   return result;
1312 }
1313
1314 /* Writes CH into line_buf N times, or to THIS->output if line_buf
1315    overflows. */
1316 static inline void
1317 output_char (struct outp_driver *this, int n, char ch)
1318 {
1319   if (LINE_BUF_SIZE - (line_p - line_buf) >= n)
1320     {
1321       memset (line_p, ch, n);
1322       line_p += n;
1323     }
1324   else
1325     while (n--)
1326       {
1327         if (LINE_BUF_SIZE - (line_p - line_buf) <= 1 && !commit_line_buf (this))
1328           return;
1329         *line_p++ = ch;
1330       }
1331 }
1332
1333 /* Advance the carriage from column 0 to the left margin. */
1334 static void
1335 advance_to_left_margin (struct outp_driver *this)
1336 {
1337   struct ascii_driver_ext *ext = this->ext;
1338   int margin;
1339
1340   margin = ext->left_margin;
1341   if (margin == 0)
1342     return;
1343   if (ext->tab_width && margin >= ext->tab_width)
1344     {
1345       output_char (this, margin / ext->tab_width, '\t');
1346       margin %= ext->tab_width;
1347     }
1348   if (margin)
1349     output_char (this, margin, ' ');
1350 }
1351
1352 /* Move the output file carriage N_CHARS left, to the left margin. */
1353 static void
1354 return_carriage (struct outp_driver *this, int n_chars)
1355 {
1356   struct ascii_driver_ext *ext = this->ext;
1357
1358   switch (ext->carriage_return_style)
1359     {
1360     case CRS_BS:
1361       output_char (this, n_chars, '\b');
1362       break;
1363     case CRS_CR:
1364       output_char (this, 1, '\r');
1365       advance_to_left_margin (this);
1366       break;
1367     default:
1368       assert (0);
1369       abort ();
1370     }
1371 }
1372
1373 /* Writes COUNT lines from the line buffer in THIS, starting at line
1374    number FIRST. */
1375 static void
1376 output_lines (struct outp_driver *this, int first, int count)
1377 {
1378   struct ascii_driver_ext *ext = this->ext;
1379   int line_num;
1380
1381   struct fixed_string *newline = &ext->ops[OPS_NEWLINE];
1382
1383   int n_chars;
1384   int n_passes;
1385
1386   if (NULL == ext->file.file)
1387     return;
1388
1389   /* Iterate over all the lines to be output. */
1390   for (line_num = first; line_num < first + count; line_num++)
1391     {
1392       struct line *line = &ext->lines[line_num];
1393       unsigned short *p = line->chars;
1394       unsigned short *end_p = p + line->char_cnt;
1395       unsigned short *bp, *ep;
1396       unsigned short attr = 0;
1397
1398       assert (end_p >= p);
1399
1400       /* Squeeze multiple blank lines into a single blank line if
1401          requested. */
1402       if (ext->squeeze_blank_lines
1403           && line_num > first
1404           && ext->lines[line_num].char_cnt == 0
1405           && ext->lines[line_num - 1].char_cnt == 0)
1406         continue;
1407
1408       /* Output every character in the line in the appropriate
1409          manner. */
1410       n_passes = 1;
1411       bp = ep = p;
1412       n_chars = 0;
1413       advance_to_left_margin (this);
1414       for (;;)                  
1415         {
1416           while (ep < end_p && attr == (*ep & 0x0300))
1417             ep++;
1418           if (output_shorts (this, bp, ep))
1419             n_passes = 2;
1420           n_chars += ep - bp;
1421           bp = ep;
1422
1423           if (bp >= end_p)
1424             break;
1425
1426           /* Turn off old font. */
1427           if (attr != (OUTP_F_R << 8))
1428             {
1429               struct fixed_string *off;
1430
1431               switch (attr)
1432                 {
1433                 case OUTP_F_I << 8:
1434                   off = &ext->fonts[FSTY_OFF | FSTY_ITALIC];
1435                   break;
1436                 case OUTP_F_B << 8:
1437                   off = &ext->fonts[FSTY_OFF | FSTY_BOLD];
1438                   break;
1439                 case OUTP_F_BI << 8:
1440                   off = &ext->fonts[FSTY_OFF | FSTY_BOLD_ITALIC];
1441                   break;
1442                 default:
1443                   assert (0);
1444                   abort ();
1445                 }
1446               if (off)
1447                 output_string (this, ls_c_str (off), ls_end (off));
1448             }
1449
1450           /* Turn on new font. */
1451           attr = (*bp & 0x0300);
1452           if (attr != (OUTP_F_R << 8))
1453             {
1454               struct fixed_string *on;
1455
1456               switch (attr)
1457                 {
1458                 case OUTP_F_I << 8:
1459                   on = &ext->fonts[FSTY_ON | FSTY_ITALIC];
1460                   break;
1461                 case OUTP_F_B << 8:
1462                   on = &ext->fonts[FSTY_ON | FSTY_BOLD];
1463                   break;
1464                 case OUTP_F_BI << 8:
1465                   on = &ext->fonts[FSTY_ON | FSTY_BOLD_ITALIC];
1466                   break;
1467                 default:
1468                   assert (0);
1469                   abort ();
1470                 }
1471               if (on)
1472                 output_string (this, ls_c_str (on), ls_end (on));
1473             }
1474
1475           ep = bp + 1;
1476         }
1477       if (n_passes > 1)
1478         {
1479           char ch;
1480
1481           return_carriage (this, n_chars);
1482           n_chars = 0;
1483           bp = ep = p;
1484           for (;;)
1485             {
1486               while (ep < end_p && (*ep & 0x0300) == (OUTP_F_R << 8))
1487                 ep++;
1488               if (ep >= end_p)
1489                 break;
1490               output_char (this, ep - bp, ' ');
1491
1492               switch (*ep & 0x0300)
1493                 {
1494                 case OUTP_F_I << 8:
1495                   ch = '_';
1496                   break;
1497                 case OUTP_F_B << 8:
1498                   ch = *ep;
1499                   break;
1500                 case OUTP_F_BI << 8:
1501                   ch = *ep;
1502                   n_passes = 3;
1503                   break;
1504                 default:
1505                   assert (0);
1506                   abort ();
1507                 }
1508               output_char (this, 1, ch);
1509               n_chars += ep - bp + 1;
1510               bp = ep + 1;
1511               ep = bp;
1512             }
1513         }
1514       if (n_passes > 2)
1515         {
1516           return_carriage (this, n_chars);
1517           bp = ep = p;
1518           for (;;)
1519             {
1520               while (ep < end_p && (*ep & 0x0300) != (OUTP_F_BI << 8))
1521                 ep++;
1522               if (ep >= end_p)
1523                 break;
1524               output_char (this, ep - bp, ' ');
1525               output_char (this, 1, '_');
1526               bp = ep + 1;
1527               ep = bp;
1528             }
1529         }
1530
1531       output_string (this, ls_c_str (newline), ls_end (newline));
1532     }
1533 }
1534
1535
1536 static int
1537 ascii_close_page (struct outp_driver *this)
1538 {
1539   static int s_len;
1540
1541   struct ascii_driver_ext *x = this->ext;
1542   int nl_len, ff_len, total_len;
1543   char *cp;
1544   int i;
1545
1546   assert (this->driver_open && this->page_open);
1547   
1548   if (!line_buf)
1549     line_buf = xmalloc (LINE_BUF_SIZE);
1550   line_p = line_buf;
1551
1552   nl_len = ls_length (&x->ops[OPS_NEWLINE]);
1553   if (x->top_margin)
1554     {
1555       total_len = x->top_margin * nl_len;
1556       if (s_len < total_len)
1557         {
1558           s_len = total_len;
1559           s = xrealloc (s, s_len);
1560         }
1561       for (cp = s, i = 0; i < x->top_margin; i++)
1562         {
1563           memcpy (cp, ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
1564           cp += nl_len;
1565         }
1566       output_string (this, s, &s[total_len]);
1567     }
1568   if (x->headers)
1569     {
1570       int len;
1571
1572       total_len = nl_len + x->w;
1573       if (s_len < total_len + 1)
1574         {
1575           s_len = total_len + 1;
1576           s = xrealloc (s, s_len);
1577         }
1578       
1579       memset (s, ' ', x->w);
1580
1581       {
1582         char temp[40];
1583
1584         snprintf (temp, 80, _("%s - Page %d"), get_start_date (),
1585                   x->page_number);
1586         memcpy (&s[x->w - strlen (temp)], temp, strlen (temp));
1587       }
1588
1589       if (outp_title && outp_subtitle)
1590         {
1591           len = min ((int) strlen (outp_title), x->w);
1592           memcpy (s, outp_title, len);
1593         }
1594       memcpy (&s[x->w], ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
1595       output_string (this, s, &s[total_len]);
1596
1597       memset (s, ' ', x->w);
1598       len = strlen (version) + 3 + strlen (host_system);
1599       if (len < x->w)
1600         sprintf (&s[x->w - len], "%s - %s" , version, host_system);
1601       if (outp_subtitle || outp_title)
1602         {
1603           char *string = outp_subtitle ? outp_subtitle : outp_title;
1604           len = min ((int) strlen (string), x->w);
1605           memcpy (s, string, len);
1606         }
1607       memcpy (&s[x->w], ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
1608       output_string (this, s, &s[total_len]);
1609       output_string (this, &s[x->w], &s[total_len]);
1610     }
1611   if (line_p != line_buf && !commit_line_buf (this))
1612     return 0;
1613
1614   output_lines (this, 0, x->l);
1615
1616   ff_len = ls_length (&x->ops[OPS_FORMFEED]);
1617   total_len = x->bottom_margin * nl_len + ff_len;
1618   if (s_len < total_len)
1619     s = xrealloc (s, total_len);
1620   for (cp = s, i = 0; i < x->bottom_margin; i++)
1621     {
1622       memcpy (cp, ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
1623       cp += nl_len;
1624     }
1625   memcpy (cp, ls_c_str (&x->ops[OPS_FORMFEED]), ff_len);
1626   if ( x->paginate ) 
1627           output_string (this, s, &s[total_len]);
1628
1629   if (line_p != line_buf && !commit_line_buf (this))
1630     return 0;
1631
1632   this->page_open = 0;
1633   return 1;
1634 }
1635
1636
1637
1638 static void
1639 ascii_chart_initialise(struct outp_driver *d UNUSED, struct chart *ch )
1640 {
1641   msg(MW, _("Charts are unsupported with ascii drivers."));
1642   ch->lp = 0;
1643 }
1644
1645 static void 
1646 ascii_chart_finalise(struct outp_driver *d UNUSED, struct chart *ch UNUSED)
1647 {
1648   
1649 }
1650
1651 struct outp_class ascii_class =
1652 {
1653   "ascii",
1654   0,
1655   0,
1656
1657   ascii_open_global,
1658   ascii_close_global,
1659   ascii_font_sizes,
1660
1661   ascii_preopen_driver,
1662   ascii_option,
1663   ascii_postopen_driver,
1664   ascii_close_driver,
1665
1666   ascii_open_page,
1667   ascii_close_page,
1668
1669   NULL,
1670
1671   ascii_line_horz,
1672   ascii_line_vert,
1673   ascii_line_intersection,
1674
1675   ascii_box,
1676   ascii_polyline_begin,
1677   ascii_polyline_point,
1678   ascii_polyline_end,
1679
1680   ascii_text_set_font_by_name,
1681   ascii_text_set_font_by_position,
1682   ascii_text_set_font_by_family,
1683   ascii_text_get_font_name,
1684   ascii_text_get_font_family,
1685   ascii_text_set_size,
1686   ascii_text_get_size,
1687   ascii_text_metrics,
1688   ascii_text_draw,
1689
1690   ascii_chart_initialise,
1691   ascii_chart_finalise
1692 };