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