PageRenderTime 66ms CodeModel.GetById 12ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/groff/src/devices/grolbp/lbp.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 725 lines | 607 code | 37 blank | 81 comment | 151 complexity | 5fb0c2c3b0ef69179d639d14d2345209 MD5 | raw file
  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
  8This file is part of groff.
  9
 10groff is free software; you can redistribute it and/or modify it under
 11the terms of the GNU General Public License as published by the Free
 12Software Foundation; either version 2, or (at your option) any later
 13version.
 14
 15groff is distributed in the hope that it will be useful, but WITHOUT ANY
 16WARRANTY; without even the implied warranty of MERCHANTABILITY or
 17FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 18for more details.
 19
 20You should have received a copy of the GNU General Public License along
 21with groff; see the file COPYING.  If not, write to the Free Software
 22Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
 23
 24/*
 25TODO
 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
 37extern "C" const char *Version_string;
 38
 39static int user_papersize = -1;		// papersize
 40static int orientation = -1;		// orientation
 41static double user_paperlength = 0;	// Custom Paper size
 42static double user_paperwidth = 0;
 43static int ncopies = 1;			// Number of copies
 44
 45#define DEFAULT_LINEWIDTH_FACTOR 40	// 0.04em
 46static int linewidth_factor = DEFAULT_LINEWIDTH_FACTOR;
 47
 48static int set_papersize(const char *paperformat);
 49
 50class lbp_font : public font {
 51public:
 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;
 58private:
 59  lbp_font(const char *);
 60};
 61
 62class lbp_printer : public printer {
 63public:
 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();
 72private:
 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
 94lbp_font::lbp_font(const char *nm)
 95: font(nm)
 96{
 97}
 98
 99lbp_font::~lbp_font()
100{
101}
102
103lbp_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
116void 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
136static 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
146lbp_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
189lbp_printer::~lbp_printer()
190{
191  lbpputs("\033P1y\033\\");
192  lbpputs("\033c\033<");
193}
194
195inline 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
219void lbp_printer::begin_page(int)
220{
221}
222
223void lbp_printer::end_page(int)
224{
225  if (vdminited())
226    vdmflush();
227  lbpputc('\f');
228  cur_hpos = -1;
229}
230
231void lbp_printer::end_of_line()
232{
233  cur_hpos = -1;		// force absolute motion
234}
235
236char *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
280void 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
353void 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
370void
371lbp_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
388inline 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
400inline 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
414void 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
552font *lbp_printer::make_font(const char *nm)
553{
554  return lbp_font::load_lbp_font(nm);
555}
556
557printer *make_printer()
558{
559  return new lbp_printer(user_papersize, user_paperwidth, user_paperlength);
560}
561
562static 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
572static 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
586static 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
611static 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
623static 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
640int 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}