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