Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / devices / grodvi / dvi.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING.  If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21
22 #include "driver.h"
23 #include "nonposix.h"
24
25 extern "C" const char *Version_string;
26
27 #define DEFAULT_LINEWIDTH 40
28 static int linewidth = DEFAULT_LINEWIDTH;
29
30 static int draw_flag = 1;
31
32 /* These values were chosen because:
33
34 (MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
35
36 and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
37
38 The width in the groff font file is the product of MULTIPLIER and the
39 width in the tfm file. */
40
41 #define RES 57816
42 #define RES_7227 (RES/7227)
43 #define UNITWIDTH 131072
44 #define SIZESCALE 100
45 #define MULTIPLIER 1
46
47 class dvi_font : public font {
48   dvi_font(const char *);
49 public:
50   int checksum;
51   int design_size;
52   ~dvi_font();
53   void handle_unknown_font_command(const char *command, const char *arg,
54                                    const char *filename, int lineno);
55   static dvi_font *load_dvi_font(const char *);
56 };
57
58 dvi_font *dvi_font::load_dvi_font(const char *s)
59 {
60   dvi_font *f = new dvi_font(s);
61   if (!f->load()) {
62     delete f;
63     return 0;
64   }
65   return f;
66 }
67
68 dvi_font::dvi_font(const char *nm)
69 : font(nm), checksum(0), design_size(0)
70 {
71 }
72
73 dvi_font::~dvi_font()
74 {
75 }
76
77 void dvi_font::handle_unknown_font_command(const char *command,
78                                            const char *arg,
79                                            const char *filename, int lineno)
80 {
81   char *ptr;
82   if (strcmp(command, "checksum") == 0) {
83     if (arg == 0)
84       fatal_with_file_and_line(filename, lineno,
85                                "`checksum' command requires an argument");
86     checksum = int(strtol(arg, &ptr, 10));
87     if (checksum == 0 && ptr == arg) {
88       fatal_with_file_and_line(filename, lineno, "bad checksum");
89     }
90   }
91   else if (strcmp(command, "designsize") == 0) {
92     if (arg == 0)
93       fatal_with_file_and_line(filename, lineno,
94                                "`designsize' command requires an argument");
95     design_size = int(strtol(arg, &ptr, 10));
96     if (design_size == 0 && ptr == arg) {
97       fatal_with_file_and_line(filename, lineno, "bad design size");
98     }
99   }
100 }
101
102 #define FONTS_MAX 256
103
104 struct output_font {
105   dvi_font *f;
106   int point_size;
107   output_font() : f(0) { }
108 };
109
110 class dvi_printer : public printer {
111   FILE *fp;
112   int max_drift;
113   int byte_count;
114   int last_bop;
115   int page_count;
116   int cur_h;
117   int cur_v;
118   int end_h;
119   int max_h;
120   int max_v;
121   output_font output_font_table[FONTS_MAX];
122   font *cur_font;
123   int cur_point_size;
124   color cur_color;
125   int pushed;
126   int pushed_h;
127   int pushed_v;
128   int have_pushed;
129   void preamble();
130   void postamble();
131   void define_font(int);
132   void set_font(int);
133   void possibly_begin_line();
134   void set_color(color *);
135 protected:
136   enum {
137     id_byte = 2,
138     set1 = 128,
139     put1 = 133,
140     put_rule = 137,
141     bop = 139,
142     eop = 140,
143     push = 141,
144     pop = 142,
145     right1 = 143,
146     down1 = 157,
147     fnt_num_0 = 171,
148     fnt1 = 235,
149     xxx1 = 239,
150     fnt_def1 = 243,
151     pre = 247,
152     post = 248,
153     post_post = 249,
154     filler = 223
155   };
156   int line_thickness;
157
158   void out1(int);
159   void out2(int);
160   void out3(int);
161   void out4(int);
162   void moveto(int, int);
163   void out_string(const char *);
164   void out_signed(unsigned char, int);
165   void out_unsigned(unsigned char, int);
166   void do_special(const char *);
167 public:
168   dvi_printer();
169   ~dvi_printer();
170   font *make_font(const char *);
171   void begin_page(int);
172   void end_page(int);
173   void set_char(int, font *, const environment *, int w, const char *name);
174   void special(char *arg, const environment *env, char type);
175   void end_of_line();
176   void draw(int code, int *p, int np, const environment *env);
177 };
178
179
180 class draw_dvi_printer : public dvi_printer {
181   int output_pen_size;
182   void set_line_thickness(const environment *);
183   void fill_next(const environment *);
184 public:
185   draw_dvi_printer();
186   ~draw_dvi_printer();
187   void draw(int code, int *p, int np, const environment *env);
188   void end_page(int);
189 };
190
191 dvi_printer::dvi_printer()
192 : fp(stdout), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
193   cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
194 {
195   if (font::res != RES)
196     fatal("resolution must be %1", RES);
197   if (font::unitwidth != UNITWIDTH)
198     fatal("unitwidth must be %1", UNITWIDTH);
199   if (font::hor != 1)
200     fatal("hor must be equal to 1");
201   if (font::vert != 1)
202     fatal("vert must be equal to 1");
203   if (font::sizescale != SIZESCALE)
204     fatal("sizescale must be equal to %1", SIZESCALE);
205   max_drift = font::res/1000;   // this is fairly arbitrary
206   preamble();
207 }
208
209 dvi_printer::~dvi_printer()
210 {
211   postamble();
212 }
213
214
215 draw_dvi_printer::draw_dvi_printer()
216 : output_pen_size(-1)
217 {
218 }
219
220 draw_dvi_printer::~draw_dvi_printer()
221 {
222 }
223
224
225 void dvi_printer::out1(int n)
226 {
227   byte_count += 1;
228   putc(n & 0xff, fp);
229 }
230
231 void dvi_printer::out2(int n)
232 {
233   byte_count += 2;
234   putc((n >> 8) & 0xff, fp);
235   putc(n & 0xff, fp);
236 }
237
238 void dvi_printer::out3(int n)
239 {
240   byte_count += 3;
241   putc((n >> 16) & 0xff, fp);
242   putc((n >> 8) & 0xff, fp);
243   putc(n & 0xff, fp);
244 }
245
246 void dvi_printer::out4(int n)
247 {
248   byte_count += 4;
249   putc((n >> 24) & 0xff, fp);
250   putc((n >> 16) & 0xff, fp);
251   putc((n >> 8) & 0xff, fp);
252   putc(n & 0xff, fp);
253 }
254
255 void dvi_printer::out_string(const char *s)
256 {
257   out1(strlen(s));
258   while (*s != 0)
259     out1(*s++);
260 }
261
262
263 void dvi_printer::end_of_line()
264 {
265   if (pushed) {
266     out1(pop);
267     pushed = 0;
268     cur_h = pushed_h;
269     cur_v = pushed_v;
270   }
271 }
272
273 void dvi_printer::possibly_begin_line()
274 {
275   if (!pushed) {
276     have_pushed = pushed = 1;
277     pushed_h = cur_h;
278     pushed_v = cur_v;
279     out1(push);
280   }
281 }
282
283 int scale(int x, int z)
284 {
285   int sw;
286   int a, b, c, d;
287   int alpha, beta;
288   alpha = 16*z; beta = 16;
289   while (z >= 040000000L) {
290     z /= 2; beta /= 2;
291   }
292   d = x & 255;
293   c = (x >> 8) & 255;
294   b = (x >> 16) & 255;
295   a = (x >> 24) & 255;
296   sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
297   if (a == 255)
298     sw -= alpha;
299   else
300     assert(a == 0);
301   return sw;
302 }
303
304 void dvi_printer::set_color(color *col)
305 {
306   cur_color = *col;
307   char buf[256];
308   unsigned int components[4];
309   color_scheme cs = col->get_components(components);
310   switch (cs) {
311   case DEFAULT:
312     sprintf(buf, "color gray 0");
313     break;
314   case RGB:
315     sprintf(buf, "color rgb %.3g %.3g %.3g",
316                  double(Red) / color::MAX_COLOR_VAL,
317                  double(Green) / color::MAX_COLOR_VAL,
318                  double(Blue) / color::MAX_COLOR_VAL);
319     break;
320   case CMY:
321     col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
322     // fall through
323   case CMYK:
324     sprintf(buf, "color cmyk %.3g %.3g %.3g %.3g",
325                  double(Cyan) / color::MAX_COLOR_VAL,
326                  double(Magenta) / color::MAX_COLOR_VAL,
327                  double(Yellow) / color::MAX_COLOR_VAL,
328                  double(Black) / color::MAX_COLOR_VAL);
329     break;
330   case GRAY:
331     sprintf(buf, "color gray %.3g",
332                  double(Gray) / color::MAX_COLOR_VAL);
333     break;
334   }
335   do_special(buf);
336 }
337
338 void dvi_printer::set_char(int index, font *f, const environment *env,
339                            int w, const char *name)
340 {
341   if (*env->col != cur_color)
342     set_color(env->col);
343   int code = f->get_code(index);
344   if (env->size != cur_point_size || f != cur_font) {
345     cur_font = f;
346     cur_point_size = env->size;
347     int i;
348     for (i = 0;; i++) {
349       if (i >= FONTS_MAX) {
350         fatal("too many output fonts required");
351       }
352       if (output_font_table[i].f == 0) {
353         output_font_table[i].f = (dvi_font *)cur_font;
354         output_font_table[i].point_size = cur_point_size;
355         define_font(i);
356       }
357       if (output_font_table[i].f == cur_font
358           && output_font_table[i].point_size == cur_point_size)
359         break;
360     }
361     set_font(i);
362   }
363   int distance = env->hpos - cur_h;
364   if (env->hpos != end_h && distance != 0) {
365     out_signed(right1, distance);
366     cur_h = env->hpos;
367   }
368   else if (distance > max_drift) {
369     out_signed(right1, distance - max_drift);
370     cur_h = env->hpos - max_drift;
371   }
372   else if (distance < -max_drift) {
373     out_signed(right1, distance + max_drift);
374     cur_h = env->hpos + max_drift;
375   }
376   if (env->vpos != cur_v) {
377     out_signed(down1, env->vpos - cur_v);
378     cur_v = env->vpos;
379   }
380   possibly_begin_line();
381   end_h = env->hpos + w;
382   cur_h += scale(f->get_width(index, UNITWIDTH)/MULTIPLIER,
383                  cur_point_size*RES_7227);
384   if (cur_h > max_h)
385     max_h = cur_h;
386   if (cur_v > max_v)
387     max_v = cur_v;
388   if (code >= 0 && code <= 127)
389     out1(code);
390   else
391     out_unsigned(set1, code);
392 }
393
394 void dvi_printer::define_font(int i)
395 {
396   out_unsigned(fnt_def1, i);
397   dvi_font *f = output_font_table[i].f;
398   out4(f->checksum);
399   out4(output_font_table[i].point_size*RES_7227);
400   out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
401   const char *nm = f->get_internal_name();
402   out1(0);
403   out_string(nm);
404 }
405
406 void dvi_printer::set_font(int i)
407 {
408   if (i >= 0 && i <= 63)
409     out1(fnt_num_0 + i);
410   else
411     out_unsigned(fnt1, i);
412 }
413
414 void dvi_printer::out_signed(unsigned char base, int param)
415 {
416   if (-128 <= param && param < 128) {
417     out1(base);
418     out1(param);
419   }
420   else if (-32768 <= param && param < 32768) {
421     out1(base+1);
422     out2(param);
423   }
424   else if (-(1 << 23) <= param && param < (1 << 23)) {
425     out1(base+2);
426     out3(param);
427   }
428   else {
429     out1(base+3);
430     out4(param);
431   }
432 }
433
434 void dvi_printer::out_unsigned(unsigned char base, int param)
435 {
436   if (param >= 0) {
437     if (param < 256) {
438       out1(base);
439       out1(param);
440     }
441     else if (param < 65536) {
442       out1(base+1);
443       out2(param);
444     }
445     else if (param < (1 << 24)) {
446       out1(base+2);
447       out3(param);
448     }
449     else {
450       out1(base+3);
451       out4(param);
452     }
453   }
454   else {
455     out1(base+3);
456     out4(param);
457   }
458 }
459
460 void dvi_printer::preamble()
461 {
462   out1(pre);
463   out1(id_byte);
464   out4(254000);
465   out4(font::res);
466   out4(1000);
467   out1(0);
468 }
469
470 void dvi_printer::postamble()
471 {
472   int tem = byte_count;
473   out1(post);
474   out4(last_bop);
475   out4(254000);
476   out4(font::res);
477   out4(1000);
478   out4(max_v);
479   out4(max_h);
480   out2(have_pushed); // stack depth
481   out2(page_count);
482   int i;
483   for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
484     define_font(i);
485   out1(post_post);
486   out4(tem);
487   out1(id_byte);
488   for (i = 0; i < 4 || byte_count % 4 != 0; i++)
489     out1(filler);
490 }  
491   
492 void dvi_printer::begin_page(int i)
493 {
494   page_count++;
495   int tem = byte_count;
496   out1(bop);
497   out4(i);
498   for (int j = 1; j < 10; j++)
499     out4(0);
500   out4(last_bop);
501   last_bop = tem;
502   if (cur_color != default_color)
503     set_color(&cur_color);
504   // By convention position (0,0) in a dvi file is placed at (1in, 1in).
505   cur_h = font::res;
506   cur_v = font::res;
507   end_h = 0;
508 }
509
510 void dvi_printer::end_page(int)
511 {
512   set_color(&default_color);
513   if (pushed)
514     end_of_line();
515   out1(eop);
516   cur_font = 0;
517 }
518
519 void draw_dvi_printer::end_page(int len)
520 {
521   dvi_printer::end_page(len);
522   output_pen_size = -1;
523 }
524
525 void dvi_printer::do_special(const char *s)
526 {
527   int len = strlen(s);
528   if (len == 0)
529     return;
530   possibly_begin_line();
531   out_unsigned(xxx1, len);
532   while (*s)
533     out1(*s++);
534 }
535
536 void dvi_printer::special(char *arg, const environment *env, char type)
537 {
538   if (type != 'p')
539     return;
540   moveto(env->hpos, env->vpos);
541   do_special(arg);
542 }
543
544 void dvi_printer::moveto(int h, int v)
545 {
546   if (h != cur_h) {
547     out_signed(right1, h - cur_h);
548     cur_h = h;
549     if (cur_h > max_h)
550       max_h = cur_h;
551   }
552   if (v != cur_v) {
553     out_signed(down1, v - cur_v);
554     cur_v = v;
555     if (cur_v > max_v)
556       max_v = cur_v;
557   }
558   end_h = 0;
559 }
560
561 void dvi_printer::draw(int code, int *p, int np, const environment *env)
562 {
563   if (code == 'l') {
564     int x = 0, y = 0;
565     int height = 0, width = 0;
566     int thickness;
567     if (line_thickness < 0)
568       thickness = env->size*RES_7227*linewidth/1000;
569     else if (line_thickness > 0)
570       thickness = line_thickness;
571     else
572       thickness = 1;
573     if (np != 2) {
574       error("2 arguments required for line");
575     }
576     else if (p[0] == 0) {
577       // vertical rule
578       if (p[1] > 0) {
579         x = env->hpos - thickness/2;
580         y = env->vpos + p[1] + thickness/2;
581         height = p[1] + thickness;
582         width = thickness;
583       }
584       else if (p[1] < 0) {
585         x = env->hpos - thickness/2;
586         y = env->vpos + thickness/2;
587         height = thickness - p[1];
588         width = thickness;
589       }
590     }
591     else if (p[1] == 0) {
592       if (p[0] > 0) {
593         x = env->hpos - thickness/2;
594         y = env->vpos + thickness/2;
595         height = thickness;
596         width = p[0] + thickness;
597       }
598       else if (p[0] < 0) {
599         x = env->hpos - p[0] - thickness/2;
600         y = env->vpos + thickness/2;
601         height = thickness;
602         width = thickness - p[0];
603       }
604     }
605     if (height != 0) {
606       moveto(x, y);
607       out1(put_rule);
608       out4(height);
609       out4(width);
610     }
611   }
612   else if (code == 't') {
613     if (np == 0) {
614       line_thickness = -1;
615     }
616     else {
617       // troff gratuitously adds an extra 0
618       if (np != 1 && np != 2)
619         error("0 or 1 argument required for thickness");
620       else
621         line_thickness = p[0];
622     }
623   }
624   else if (code == 'R') {
625     if (np != 2)
626       error("2 arguments required for rule");
627     else if (p[0] != 0 || p[1] != 0) {
628       int dh = p[0];
629       int dv = p[1];
630       int oh = env->hpos;
631       int ov = env->vpos;
632       if (dv > 0) {
633         ov += dv;
634         dv = -dv;
635       }
636       if (dh < 0) {
637         oh += dh;
638         dh = -dh;
639       }
640       moveto(oh, ov);
641       out1(put_rule);
642       out4(-dv);
643       out4(dh);
644     }
645   }
646 }
647
648 // XXX Will this overflow?
649
650 inline int milliinches(int n)
651 {
652   return (n*1000 + font::res/2)/font::res;
653 }
654
655 void draw_dvi_printer::set_line_thickness(const environment *env)
656 {
657   int desired_pen_size
658     = milliinches(line_thickness < 0
659                   // Will this overflow?
660                   ? env->size*RES_7227*linewidth/1000
661                   : line_thickness);
662   if (desired_pen_size != output_pen_size) {
663     char buf[256];
664     sprintf(buf, "pn %d", desired_pen_size);
665     do_special(buf);
666     output_pen_size = desired_pen_size;
667   }
668 }
669
670 void draw_dvi_printer::fill_next(const environment *env)
671 {
672   unsigned int g;
673   if (env->fill->is_default())
674     g = 0;
675   else {
676     // currently, only BW support
677     env->fill->get_gray(&g);
678   }
679   char buf[256];
680   sprintf(buf, "sh %.3g", 1 - double(g)/color::MAX_COLOR_VAL);
681   do_special(buf);
682 }
683
684 void draw_dvi_printer::draw(int code, int *p, int np, const environment *env)
685 {
686   char buf[1024];
687   int fill_flag = 0;
688   switch (code) {
689   case 'C':
690     fill_flag = 1;
691     // fall through
692   case 'c':
693     // troff adds an extra argument to C
694     if (np != 1 && !(code == 'C' && np == 2)) {
695       error("1 argument required for circle");
696       break;
697     }
698     moveto(env->hpos+p[0]/2, env->vpos);
699     if (fill_flag)
700       fill_next(env);
701     else
702       set_line_thickness(env);
703     int rad;
704     rad = milliinches(p[0]/2);
705     sprintf(buf, "%s 0 0 %d %d 0 6.28319",
706             (fill_flag ? "ia" : "ar"),
707             rad,
708             rad);
709     do_special(buf);
710     break;
711   case 'l':
712     if (np != 2) {
713       error("2 arguments required for line");
714       break;
715     }
716     moveto(env->hpos, env->vpos);
717     set_line_thickness(env);
718     do_special("pa 0 0");
719     sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
720     do_special(buf);
721     do_special("fp");
722     break;
723   case 'E':
724     fill_flag = 1;
725     // fall through
726   case 'e':
727     if (np != 2) {
728       error("2 arguments required for ellipse");
729       break;
730     }
731     moveto(env->hpos+p[0]/2, env->vpos);
732     if (fill_flag)
733       fill_next(env);
734     sprintf(buf, "%s 0 0 %d %d 0 6.28319",
735             (fill_flag ? "ia" : "ar"),
736             milliinches(p[0]/2),
737             milliinches(p[1]/2));
738     do_special(buf);
739     break;
740   case 'P':
741     fill_flag = 1;
742     // fall through
743   case 'p':
744     {
745       if (np & 1) {
746         error("even number of arguments required for polygon");
747         break;
748       }
749       if (np == 0) {
750         error("no arguments for polygon");
751         break;
752       }
753       moveto(env->hpos, env->vpos);
754       if (fill_flag)
755         fill_next(env);
756       else
757         set_line_thickness(env);
758       do_special("pa 0 0");
759       int h = 0, v = 0;
760       for (int i = 0; i < np; i += 2) {
761         h += p[i];
762         v += p[i+1];
763         sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
764         do_special(buf);
765       }
766       do_special("pa 0 0");
767       do_special(fill_flag ? "ip" : "fp");
768       break;
769     }
770   case '~':
771     {
772       if (np & 1) {
773         error("even number of arguments required for spline");
774         break;
775       }
776       if (np == 0) {
777         error("no arguments for spline");
778         break;
779       }
780       moveto(env->hpos, env->vpos);
781       set_line_thickness(env);
782       do_special("pa 0 0");
783       int h = 0, v = 0;
784       for (int i = 0; i < np; i += 2) {
785         h += p[i];
786         v += p[i+1];
787         sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
788         do_special(buf);
789       }
790       do_special("sp");
791       break;
792     }
793   case 'a':
794     {
795       if (np != 4) {
796         error("4 arguments required for arc");
797         break;
798       }
799       set_line_thickness(env);
800       double c[2];
801       if (adjust_arc_center(p, c)) {
802         int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5));
803         moveto(env->hpos + int(c[0]), env->vpos + int(c[1]));
804         sprintf(buf, "ar 0 0 %d %d %f %f",
805                 rad,
806                 rad,
807                 atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]),
808                 atan2(-c[1], -c[0]));
809         do_special(buf);
810       }
811       else {
812         moveto(env->hpos, env->vpos);
813         do_special("pa 0 0");
814         sprintf(buf,
815                 "pa %d %d",
816                 milliinches(p[0] + p[2]),
817                 milliinches(p[1] + p[3]));
818         do_special(buf);
819         do_special("fp");
820       }
821       break;
822     }
823   case 't':
824     {
825       if (np == 0) {
826         line_thickness = -1;
827       }
828       else {
829         // troff gratuitously adds an extra 0
830         if (np != 1 && np != 2) {
831           error("0 or 1 argument required for thickness");
832           break;
833         }
834         line_thickness = p[0];
835       }
836       break;
837     }
838   case 'R':
839     {
840       if (np != 2) {
841         error("2 arguments required for rule");
842         break;
843       }
844       int dh = p[0];
845       if (dh == 0)
846         break;
847       int dv = p[1];
848       if (dv == 0)
849         break;
850       int oh = env->hpos;
851       int ov = env->vpos;
852       if (dv > 0) {
853         ov += dv;
854         dv = -dv;
855       }
856       if (dh < 0) {
857         oh += dh;
858         dh = -dh;
859       }
860       moveto(oh, ov);
861       out1(put_rule);
862       out4(-dv);
863       out4(dh);
864       break;
865     }
866   default:
867     error("unrecognised drawing command `%1'", char(code));
868     break;
869   }
870 }
871
872 font *dvi_printer::make_font(const char *nm)
873 {
874   return dvi_font::load_dvi_font(nm);
875 }
876
877 printer *make_printer()
878 {
879   if (draw_flag)
880     return new draw_dvi_printer;
881   else
882     return new dvi_printer;
883 }
884
885 static void usage(FILE *stream);
886
887 int main(int argc, char **argv)
888 {
889   program_name = argv[0];
890   static char stderr_buf[BUFSIZ];
891   setbuf(stderr, stderr_buf);
892   int c;
893   static const struct option long_options[] = {
894     { "help", no_argument, 0, CHAR_MAX + 1 },
895     { "version", no_argument, 0, 'v' },
896     { NULL, 0, 0, 0 }
897   };
898   while ((c = getopt_long(argc, argv, "F:vw:d", long_options, NULL)) != EOF)
899     switch(c) {
900     case 'v':
901       {
902         printf("GNU grodvi (groff) version %s\n", Version_string);
903         exit(0);
904         break;
905       }
906     case 'w':
907       if (sscanf(optarg, "%d", &linewidth) != 1
908           || linewidth < 0 || linewidth > 1000) {
909         error("bad line width");
910         linewidth = DEFAULT_LINEWIDTH;
911       }
912       break;
913     case 'd':
914       draw_flag = 0;
915       break;
916     case 'F':
917       font::command_line_font_dir(optarg);
918       break;
919     case CHAR_MAX + 1: // --help
920       usage(stdout);
921       exit(0);
922       break;
923     case '?':
924       usage(stderr);
925       exit(1);
926       break;
927     default:
928       assert(0);
929     }
930 #ifdef SET_BINARY
931   SET_BINARY(fileno(stdout));
932 #endif
933   if (optind >= argc)
934     do_file("-");
935   else {
936     for (int i = optind; i < argc; i++)
937       do_file(argv[i]);
938   }
939   delete pr;
940   return 0;
941 }
942
943 static void usage(FILE *stream)
944 {
945   fprintf(stream, "usage: %s [-dv] [-F dir] [-w n] [files ...]\n",
946           program_name);
947 }