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