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

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