Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / devices / grolbp / lbp.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1994, 2000, 2001, 2002 Free Software Foundation, Inc.
3      Written by Francisco Andrés Verdú <pandres@dragonet.es> with many ideas
4      taken from the other groff drivers.
5
6
7 This file is part of groff.
8
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
13
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING.  If not, write to the Free Software
21 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22
23 /*
24 TODO
25
26  - Add X command to include bitmaps
27 */
28 #define _GNU_SOURCE
29
30 #include "driver.h"
31 #include "lbp.h"
32 #include "charset.h"
33 #include "paper.h"
34
35 #include "nonposix.h"
36
37 extern "C" const char *Version_string;
38
39 static int user_papersize = -1;         // papersize
40 static int orientation = -1;            // orientation
41 static double user_paperlength = 0;     // Custom Paper size
42 static double user_paperwidth = 0;
43 static int ncopies = 1;                 // Number of copies
44
45 #define DEFAULT_LINEWIDTH_FACTOR 40     // 0.04em
46 static int linewidth_factor = DEFAULT_LINEWIDTH_FACTOR;
47
48 static int set_papersize(const char *paperformat);
49
50 class lbp_font : public font {
51 public:
52   ~lbp_font();
53   void handle_unknown_font_command(const char *command, const char *arg,
54                                    const char *filename, int lineno);
55   static lbp_font *load_lbp_font(const char *);
56   char *lbpname;
57   char is_scalable;
58 private:
59   lbp_font(const char *);
60 };
61
62 class lbp_printer : public printer {
63 public:
64   lbp_printer(int, double, double);
65   ~lbp_printer();
66   void set_char(int, font *, const environment *, int, const char *name);
67   void draw(int code, int *p, int np, const environment *env);
68   void begin_page(int);
69   void end_page(int page_length);
70   font *make_font(const char *);
71   void end_of_line();
72 private:
73   void set_line_thickness(int size,const environment *env);
74   void vdmstart();
75   void vdmflush(); // the name vdmend was already used in lbp.h
76   void setfillmode(int mode);
77   void polygon( int hpos,int vpos,int np,int *p);
78   char *font_name(const lbp_font *f, const int siz);
79
80   int fill_pattern;
81   int fill_mode;
82   int cur_hpos;
83   int cur_vpos;
84   lbp_font *cur_font;
85   int cur_size;
86   unsigned short cur_symbol_set;
87   int line_thickness;
88   int req_linethickness; // requested line thickness
89   int papersize;
90   int paperlength;      // custom paper size
91   int paperwidth;
92 };
93
94 //   Compatibility section.
95 //
96 //   Here we define some functions not present in some of the targets
97 //   platforms
98 #ifndef HAVE_STRSEP
99 // Solaris 8 doesn't have the strsep function
100 static char *strsep(char **pcadena, const char *delim)
101 {
102   char *p;
103   p = strtok(*pcadena, delim);
104   *pcadena = strtok(NULL, delim);
105   return p;
106 }
107 #endif
108
109 lbp_font::lbp_font(const char *nm)
110 : font(nm)
111 {
112 }
113
114 lbp_font::~lbp_font()
115 {
116 }
117
118 lbp_font *lbp_font::load_lbp_font(const char *s)
119 {
120   lbp_font *f = new lbp_font(s);
121   f->lbpname = NULL;
122   f->is_scalable = 1; // Default is that fonts are scalable
123   if (!f->load()) {
124     delete f;
125     return 0;
126   }
127   return f;
128 }
129
130
131 void lbp_font::handle_unknown_font_command(const char *command,
132                                            const char *arg,
133                                            const char *filename, int lineno)
134 {
135   if (strcmp(command, "lbpname") == 0) {
136     if (arg == 0)
137       fatal_with_file_and_line(filename, lineno,
138                                "`%1' command requires an argument",
139                                command);
140     this->lbpname = new char[strlen(arg) + 1];
141     strcpy(this->lbpname, arg);
142     // we recognize bitmapped fonts by the first character of its name
143     if (arg[0] == 'N')
144       this->is_scalable = 0;
145     // fprintf(stderr, "Loading font \"%s\" \n", arg);
146   }
147   // fprintf(stderr, "Loading font  %s \"%s\" in %s at %d\n",
148   //         command, arg, filename, lineno);
149 }
150
151 static void wp54charset()
152 {
153   unsigned int i;
154   lbpputs("\033[714;100;29;0;32;120.}");
155   for (i = 0; i < sizeof(symset); i++)
156     lbpputc(symset[i]);
157   lbpputs("\033[100;0 D");
158   return;
159 }
160
161 lbp_printer::lbp_printer(int ps, double pw, double pl)
162 : fill_pattern(1),
163   fill_mode(0),
164   cur_hpos(-1),
165   cur_font(0),
166   cur_size(0),
167   cur_symbol_set(0),
168   req_linethickness(-1)
169 {
170 #ifdef SET_BINARY
171   SET_BINARY(fileno(stdout));
172 #endif
173   lbpinit(stdout);
174   lbpputs("\033c\033;\033[2&z\033[7 I\033[?32h\033[?33h\033[11h");
175   wp54charset(); // Define the new symbol set
176   lbpputs("\033[7 I\033[?32h\033[?33h\033[11h");
177   // Paper size handling
178   if (orientation < 0)
179     orientation = 0;    // Default orientation is portrait
180   papersize = 14;       // Default paper size is A4
181   if (font::papersize) {
182     papersize = set_papersize(font::papersize);
183     paperlength = font::paperlength;
184     paperwidth = font::paperwidth;
185   }
186   if (ps >= 0) {
187     papersize = ps;
188     paperlength = int(pl * font::res + 0.5);
189     paperwidth = int(pw * font::res + 0.5);
190   }
191   if (papersize < 80)   // standard paper
192     lbpprintf("\033[%dp", (papersize | orientation));
193   else                  // Custom paper
194     lbpprintf("\033[%d;%d;%dp", (papersize | orientation),
195               paperlength, paperwidth);
196   // Number of copies
197   lbpprintf("\033[%dv\n", ncopies);
198   lbpputs("\033[0u\033[1u\033P1y Grolbp\033\\");
199   lbpmoveabs(0, 0);
200   lbpputs("\033[0t\033[2t");
201   lbpputs("\033('$2\033)' 1");  // Primary symbol set IBML
202                                 // Secondary symbol set IBMR1
203   cur_symbol_set = 0;
204 }
205
206 lbp_printer::~lbp_printer()
207 {
208   lbpputs("\033P1y\033\\");
209   lbpputs("\033c\033<");
210 }
211
212 void lbp_printer::begin_page(int)
213 {
214 }
215
216 void lbp_printer::end_page(int)
217 {
218   if (vdminited())
219     vdmflush();
220   lbpputc('\f');
221   cur_hpos = -1;
222 }
223
224 void lbp_printer::end_of_line()
225 {
226   cur_hpos = -1;                // force absolute motion
227 }
228
229 char *lbp_printer::font_name(const lbp_font *f, const int siz)
230 {
231   static char bfont_name[255];  // The resulting font name
232   char type,    // Italic, Roman, Bold
233        ori,     // Normal or Rotated
234        *nam;    // The font name without other data.
235   int cpi;      // The font size in characters per inch
236                 // (bitmapped fonts are monospaced).
237   /* Bitmap font selection is ugly in this printer, so don't expect
238      this function to be elegant. */
239   bfont_name[0] = 0x00;
240   if (orientation)      // Landscape
241     ori = 'R';
242   else                  // Portrait
243     ori = 'N';
244   type = f->lbpname[strlen(f->lbpname) - 1];
245   nam = new char[strlen(f->lbpname) - 2];
246   strncpy(nam, &(f->lbpname[1]), strlen(f->lbpname) - 2);
247   nam[strlen(f->lbpname) - 2] = 0x00;
248   // fprintf(stderr, "Bitmap font '%s' %d %c %c \n", nam, siz, type, ori);
249   /* Since these fonts are available only at certain sizes,
250      10 and 17 cpi for courier,  12 and 17 cpi for elite,
251      we adjust the resulting size. */
252   cpi = 17;
253   // Fortunately there are only two bitmapped fonts shipped with the printer.
254   if (!strcasecmp(nam, "courier")) {
255     // Courier font
256     if (siz >= 12)
257       cpi = 10;
258     else cpi = 17;
259   }
260   if (!strcasecmp(nam, "elite")) {
261     if (siz >= 10)
262       cpi = 12;
263     else cpi = 17;
264   }
265   // Now that we have all the data, let's generate the font name.
266   if ((type != 'B') && (type != 'I')) // Roman font
267     sprintf(bfont_name, "%c%s%d", ori, nam, cpi);
268   else
269     sprintf(bfont_name, "%c%s%d%c", ori, nam, cpi, type);
270   return bfont_name;
271 }
272
273 void lbp_printer::set_char(int index, font *f, const environment *env,
274                            int w, const char *name)
275 {
276   int code = f->get_code(index);
277   unsigned char ch = code & 0xff;
278   unsigned short symbol_set = code >> 8;
279   if (f != cur_font) {
280     lbp_font *psf = (lbp_font *)f;
281     // fprintf(stderr, "Loading font %s \"%d\" \n", psf->lbpname, env->size);
282     if (psf->is_scalable) {
283       // Scalable font selection is different from bitmaped
284       lbpprintf("\033Pz%s.IBML\033\\\033[%d C", psf->lbpname,
285                 (int)((env->size * font::res) / 72));
286     }
287     else
288       // bitmapped font
289       lbpprintf("\033Pz%s.IBML\033\\\n", font_name(psf, env->size));
290     lbpputs("\033)' 1");        // Select IBML and IBMR1 symbol set
291     cur_font = psf;
292     cur_symbol_set = 0;
293      // Update the line thickness if needed
294     if ((req_linethickness < 0 ) && (env->size != cur_size))
295         set_line_thickness(req_linethickness,env);
296     cur_size = env->size;
297   }
298   if (symbol_set != cur_symbol_set) {
299     if (cur_symbol_set == 3)
300       // if current symbol set is Symbol we must restore the font
301       lbpprintf("\033Pz%s.IBML\033\\\033[%d C", cur_font->lbpname,
302                 (int)((env->size * font::res) / 72));
303     switch (symbol_set) {
304     case 0:
305       lbpputs("\033('$2\033)' 1");      // Select IBML and IBMR1 symbol sets
306       break;
307     case 1:
308       lbpputs("\033(d\033)' 1");        // Select wp54 symbol set
309       break;
310     case 2:
311       lbpputs("\033('$2\033)'!0");      // Select IBMP symbol set
312       break;
313     case 3:
314       lbpprintf("\033PzSymbol.SYML\033\\\033[%d C",
315                 (int)((env->size * font::res) / 72));
316       lbpputs("\033(\"!!0\033)\"!!1");  // Select symbol font
317       break;
318     case 4:
319       lbpputs("\033)\"! 1\033(\"!$2");  // Select PS symbol set
320       break;
321     }
322     cur_symbol_set = symbol_set;
323   }
324   if (env->size != cur_size) {
325     if (!cur_font->is_scalable)
326       lbpprintf("\033Pz%s.IBML\033\\\n", font_name(cur_font, env->size));
327     else
328       lbpprintf("\033[%d C", (int)((env->size * font::res) / 72));
329     cur_size = env->size;
330      // Update the line thickness if needed
331     if (req_linethickness < 0 ) 
332         set_line_thickness(req_linethickness,env);
333   }
334   if ((env->hpos != cur_hpos) || (env->vpos != cur_vpos)) {
335     // lbpmoveabs(env->hpos - ((5 * 300) / 16), env->vpos);
336     lbpmoveabs(env->hpos - 64, env->vpos - 64);
337     cur_vpos = env->vpos;
338     cur_hpos = env->hpos;
339   }
340   if ((ch & 0x7F) < 32)
341     lbpputs("\033[1.v");
342   lbpputc(ch);
343   cur_hpos += w;
344 }
345
346 void lbp_printer::vdmstart()
347 {
348   FILE *f;
349   static int changed_origin = 0;
350   errno = 0;
351   f = tmpfile();
352   // f = fopen("/tmp/gtmp","w+");
353   if (f == NULL)
354     perror("Opening temporary file");
355   vdminit(f);
356   if (!changed_origin) {        // we should change the origin only one time
357     changed_origin = 1;
358     vdmorigin(-63, 0);
359   }
360   vdmlinewidth(line_thickness);
361 }
362
363 void
364 lbp_printer::vdmflush()
365 {
366   char buffer[1024];
367   int bytes_read = 1;
368   vdmend();
369   fflush(lbpoutput);
370   /* let's copy the vdm code to the output */
371   rewind(vdmoutput);
372   do {
373     bytes_read = fread(buffer, 1, sizeof(buffer), vdmoutput);
374     bytes_read = fwrite(buffer, 1, bytes_read, lbpoutput);
375   } while (bytes_read == sizeof(buffer));
376   fclose(vdmoutput);    // This will also delete the file,
377                         // since it is created by tmpfile()
378   vdmoutput = NULL;
379 }
380
381 inline void lbp_printer::setfillmode(int mode)
382 {
383   if (mode != fill_mode) {
384     if (mode != 1)
385       vdmsetfillmode(mode, 1, 0);
386     else
387       vdmsetfillmode(mode, 1, 1);       // To get black we must use white
388                                         // inverted
389       fill_mode = mode;
390   }
391 }
392
393 inline void lbp_printer::polygon(int hpos, int vpos, int np, int *p)
394 {
395   int *points, i;
396   points = new int[np + 2];
397   points[0] = hpos;
398   points[1] = vpos;
399   // fprintf(stderr, "Poligon (%d,%d) ", points[0], points[1]);
400   for (i = 0; i < np; i++)
401     points[i + 2] = p[i];
402   // for (i = 0; i < np; i++) fprintf(stderr, " %d ", p[i]);
403   // fprintf(stderr, "\n");
404   vdmpolygon((np /2) + 1, points);
405 }
406
407 inline void lbp_printer::set_line_thickness(int size,const environment *env)
408 {
409       if (size == 0)
410         line_thickness = 1;
411       else {
412         if (size < 0)
413                 // line_thickness =
414                 //   (env->size * (font::res/72)) * (linewidth_factor/1000)
415                 // we ought to check for overflow
416                 line_thickness =
417                   env->size * linewidth_factor * font::res / 72000;
418         else // size > 0
419                 line_thickness = size;
420       } // else from if (size == 0)
421       if (line_thickness < 1)
422         line_thickness = 1;
423       if (vdminited())
424         vdmlinewidth(line_thickness);
425       req_linethickness = size; // an size requested
426       /*  fprintf(stderr, "thickness: %d == %d, size %d, %d \n",
427         size, line_thickness, env->size,req_linethickness); */
428    return;
429 }; // lbp_printer::set_line_thickness
430
431 void lbp_printer::draw(int code, int *p, int np, const environment *env)
432 {
433   if ((req_linethickness < 0 ) && (env->size != cur_size))
434                 set_line_thickness(req_linethickness,env);
435
436   switch (code) {
437   case 't':
438     if (np == 0)
439       line_thickness = 1;
440     else { // troff gratuitously adds an extra 0
441       if (np != 1 && np != 2) {
442         error("0 or 1 argument required for thickness");
443         break;
444       };
445     set_line_thickness(p[0],env);
446     };
447     break;
448   case 'l':     // Line
449     if (np != 2) {
450       error("2 arguments required for line");
451       break;
452     }
453     if (!vdminited())
454       vdmstart();
455     vdmline(env->hpos, env->vpos, p[0], p[1]);
456 /*     fprintf(stderr, "\nline: %d,%d - %d,%d thickness %d == %d\n",
457              env->hpos - 64,env->vpos -64, env->hpos - 64 + p[0],
458              env->vpos -64 + p[1], env->size, line_thickness);*/
459     break;
460   case 'R':     // Rule
461     if (np != 2) {
462       error("2 arguments required for Rule");
463       break;
464     }
465     if (vdminited()) {
466       setfillmode(fill_pattern); // Solid Rule
467       vdmrectangle(env->hpos, env->vpos, p[0], p[1]);
468     }
469     else {
470       lbpruleabs(env->hpos - 64, env->vpos -64, p[0], p[1]);
471       cur_vpos = p[1];
472       cur_hpos = p[0];
473     }
474     // fprintf(stderr, "\nrule: thickness %d == %d\n",
475     //         env->size, line_thickness);
476     break;
477   case 'P':     // Filled Polygon
478     if (!vdminited())
479       vdmstart();
480     setfillmode(fill_pattern);
481     polygon(env->hpos, env->vpos, np, p);
482     break;
483   case 'p':     // Empty Polygon
484     if (!vdminited())
485       vdmstart();
486     setfillmode(0);
487     polygon(env->hpos, env->vpos, np, p);
488     break;
489   case 'C':     // Filled Circle
490     if (!vdminited())
491       vdmstart();
492     // fprintf(stderr, "Circle (%d,%d) Fill %d\n",
493     //         env->hpos, env->vpos, fill_pattern);
494     setfillmode(fill_pattern);
495     vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2);
496     break;
497   case 'c':     // Empty Circle
498     if (!vdminited())
499       vdmstart();
500     setfillmode(0);
501     vdmcircle(env->hpos + (p[0]/2), env->vpos, p[0]/2);
502     break;
503   case 'E':     // Filled Ellipse
504     if (!vdminited())
505       vdmstart();
506     setfillmode(fill_pattern);
507     vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0);
508     break;
509   case 'e':      // Empty Ellipse
510     if (!vdminited())
511       vdmstart();
512     setfillmode(0);
513     vdmellipse(env->hpos + (p[0]/2), env->vpos, p[0]/2, p[1]/2, 0);
514     break;
515   case 'a':     // Arc
516     if (!vdminited())
517       vdmstart();
518     setfillmode(0);
519     // VDM draws arcs clockwise and pic counterclockwise
520     // We must compensate for that, exchanging the starting and
521     // ending points
522     vdmvarc(env->hpos + p[0], env->vpos+p[1],
523             int(sqrt(double((p[0]*p[0]) + (p[1]*p[1])))),
524             p[2], p[3],
525             (-p[0]), (-p[1]), 1, 2);
526     break;
527   case '~':     // Spline
528     if (!vdminited())
529       vdmstart();
530     setfillmode(0);
531     vdmspline(np/2, env->hpos, env->vpos, p);
532     break;
533   case 'f':
534     if (np != 1 && np != 2) {
535       error("1 argument required for fill");
536       break;
537     }
538     // fprintf(stderr, "Fill %d\n", p[0]);
539     if ((p[0] == 1) || (p[0] >= 1000)) { // Black
540       fill_pattern = 1;
541       break;
542     }
543     if (p[0] == 0) { // White
544       fill_pattern = 0;
545       break;
546     }
547     if ((p[0] > 1) && (p[0] < 1000))
548       {
549         if (p[0] >= 990)  fill_pattern = -23;
550         else if (p[0] >= 700)  fill_pattern = -28;
551         else if (p[0] >= 500)  fill_pattern = -27;
552         else if (p[0] >= 400)  fill_pattern = -26;
553         else if (p[0] >= 300)  fill_pattern = -25;
554         else if (p[0] >= 200)  fill_pattern = -22;
555         else if (p[0] >= 100)  fill_pattern = -24;
556         else fill_pattern = -21;
557       }
558     break;
559   case 'F':
560     // not implemented yet
561     break;
562   default:
563     error("unrecognised drawing command `%1'", char(code));
564     break;
565   }
566   return;
567 }
568
569 font *lbp_printer::make_font(const char *nm)
570 {
571   return lbp_font::load_lbp_font(nm);
572 }
573
574 printer *make_printer()
575 {
576   return new lbp_printer(user_papersize, user_paperwidth, user_paperlength);
577 }
578
579 static struct {
580   const char *name;
581   int code;
582 } lbp_papersizes[] =
583   {{ "A4", 14 },
584    { "letter", 30 },
585    { "legal", 32 },
586    { "executive", 40 },
587   };
588
589 static int set_papersize(const char *paperformat)
590 {
591   unsigned int i;
592   // First test for a standard (i.e. supported directly by the printer)
593   // paper size
594   for (i = 0 ; i < sizeof(lbp_papersizes) / sizeof(lbp_papersizes[0]); i++)
595   {
596     if (strcasecmp(lbp_papersizes[i].name,paperformat) == 0)
597       return lbp_papersizes[i].code;
598   }
599   // Otherwise, we assume a custom paper size
600   return 82;
601 }
602
603 static void handle_unknown_desc_command(const char *command, const char *arg,
604                                         const char *filename, int lineno)
605 {
606   // orientation command
607   if (strcasecmp(command, "orientation") == 0) {
608     // We give priority to command line options
609     if (orientation > 0)
610       return;
611     if (arg == 0)
612       error_with_file_and_line(filename, lineno,
613                                "`orientation' command requires an argument");
614     else {
615       if (strcasecmp(arg, "portrait") == 0)
616         orientation = 0;
617       else {
618         if (strcasecmp(arg, "landscape") == 0)
619           orientation = 1;
620         else
621           error_with_file_and_line(filename, lineno,
622                                    "invalid argument to `orientation' command");
623       }
624     }
625   }
626 }
627
628 static struct option long_options[] = {
629   { "orientation", required_argument, NULL, 'o' },
630   { "version", no_argument, NULL, 'v' },
631   { "copies", required_argument, NULL, 'c' },
632   { "landscape", no_argument, NULL, 'l' },
633   { "papersize", required_argument, NULL, 'p' },
634   { "linewidth", required_argument, NULL, 'w' },
635   { "fontdir", required_argument, NULL, 'F' },
636   { "help", no_argument, NULL, 'h' },
637   { NULL, 0, 0, 0 }
638 };
639
640 static void usage(FILE *stream)
641 {
642   fprintf(stream,
643           "usage: %s [-lvh] [-c n] [-p paper_size] [-F dir] [-o or]\n"
644           "       [-w width] [files ...]\n"
645           "\n"
646           "  -o --orientation=[portrait|landscape]\n"
647           "  -v --version\n"
648           "  -c --copies=numcopies\n"
649           "  -l --landscape\n"
650           "  -p --papersize=paper_size\n"
651           "  -w --linewidth=width\n"
652           "  -F --fontdir=dir\n"
653           "  -h --help\n",
654           program_name);
655 }
656
657 int main(int argc, char **argv)
658 {
659   if (program_name == NULL)
660     program_name = strsave(argv[0]);
661   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
662   // command line parsing
663   int c = 0;
664   int option_index = 0;
665   while (c >= 0) {
666     c = getopt_long (argc, argv, "F:p:lvo:c:hw:",
667                      long_options, &option_index);
668     switch (c) {
669     case 'F':
670       font::command_line_font_dir(optarg);
671       break;
672     case 'p':
673       {
674         const char *s;
675         if (!font::scan_papersize(optarg, &s,
676                                   &user_paperlength, &user_paperwidth))
677           error("invalid paper size `%1' ignored", optarg);
678         else
679           user_papersize = set_papersize(s);
680         break;
681       }
682     case 'l':
683       orientation = 1;
684       break;
685     case 'v':
686       printf("GNU grolbp (groff) version %s\n", Version_string);
687       exit(0);
688       break;
689     case 'o':
690       if (strcasecmp(optarg, "portrait") == 0)
691         orientation = 0;
692       else {
693         if (strcasecmp(optarg, "landscape") == 0)
694           orientation = 1;
695         else
696           error("unknown orientation '%1'", optarg);
697       };
698       break;
699     case 'c':
700       {
701         char *ptr;
702         long n = strtol(optarg, &ptr, 10);
703         if ((n <= 0) && (ptr == optarg))
704           error("argument for -c must be a positive integer");
705         else if (n <= 0 || n > 32767)
706           error("out of range argument for -c");
707         else
708           ncopies = unsigned(n);
709         break;
710       }
711     case 'w':
712       {
713         char *ptr;
714         long n = strtol(optarg, &ptr, 10);
715         if (n == 0 && ptr == optarg)
716           error("argument for -w must be a non-negative integer");
717         else if (n < 0 || n > INT_MAX)
718           error("out of range argument for -w");
719         else
720           linewidth_factor = int(n);
721         break;
722       }
723     case 'h':
724       usage(stdout);
725       exit(0);
726       break;
727     case '?':
728       usage(stderr);
729       exit(1);
730       break;
731     }
732   }
733   if (optind >= argc)
734     do_file("-");
735   while (optind < argc)
736     do_file(argv[optind++]);
737   lbpputs("\033c\033<");
738   delete pr;
739   return 0;
740 }