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