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