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