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 96 lbp_font::lbp_font(const char *nm) 97 : font(nm) 98 { 99 } 100 101 lbp_font::~lbp_font() 102 { 103 } 104 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 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 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 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 191 lbp_printer::~lbp_printer() 192 { 193 lbpputs("\033P1y\033\\"); 194 lbpputs("\033c\033<"); 195 } 196 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 221 void lbp_printer::begin_page(int) 222 { 223 } 224 225 void lbp_printer::end_page(int) 226 { 227 if (vdminited()) 228 vdmflush(); 229 lbpputc('\f'); 230 cur_hpos = -1; 231 } 232 233 void lbp_printer::end_of_line() 234 { 235 cur_hpos = -1; // force absolute motion 236 } 237 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 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 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 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 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 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 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 554 font *lbp_printer::make_font(const char *nm) 555 { 556 return lbp_font::load_lbp_font(nm); 557 } 558 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 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 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 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 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