Line data Source code
1 : /* Copyright (C) 1994-2025 Free Software Foundation, Inc.
2 : Written by James Clark (jjc@jclark.com)
3 :
4 : This file is part of groff, the GNU roff typesetting system.
5 :
6 : groff is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
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 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : /*
20 : TODO
21 :
22 : option to use beziers for circle/ellipse/arc
23 : option to use lines for spline (for LJ3)
24 : left/top offset registration
25 : output bin selection option
26 : paper source option
27 : output non-integer parameters using fixed point numbers
28 : X command to insert contents of file
29 : X command to specify inline escape sequence (how to specify unprintable chars?)
30 : X command to include bitmap graphics
31 : */
32 :
33 : #ifdef HAVE_CONFIG_H
34 : #include <config.h>
35 : #endif
36 :
37 : #include <assert.h>
38 : #include <locale.h> // setlocale()
39 : #include <math.h> // atan2(), floor()
40 : #include <stdcountof.h>
41 : #include <stdio.h> // EOF, FILE, fflush(), fprintf(), printf(),
42 : // setbuf(), stderr, stdout
43 : #include <stdlib.h> // exit(), EXIT_SUCCESS, strtol()
44 : #include <string.h> // strcmp()
45 : #include <strings.h> // strcasecmp()
46 :
47 : #include <getopt.h> // getopt_long()
48 :
49 : #include "nonposix.h"
50 :
51 : #include "cset.h" // csdigit()
52 : #include "driver.h"
53 : #include "lib.h" // array_size(), PI
54 :
55 : extern "C" const char *Version_string;
56 :
57 : static struct lj4_paper_sizes {
58 : const char *name;
59 : int code;
60 : // at 300dpi
61 : int x_offset_portrait;
62 : int x_offset_landscape;
63 : } paper_table[] = {
64 : { "letter", 2, 75, 60 },
65 : { "legal", 3, 75, 60 },
66 : { "executive", 1, 75, 60 },
67 : { "a4", 26, 71, 59 },
68 : { "com10", 81, 75, 60 },
69 : { "monarch", 80, 75, 60 },
70 : { "c5", 91, 71, 59 },
71 : { "b5", 100, 71, 59 },
72 : { "dl", 90, 71, 59 },
73 : };
74 :
75 : static int user_paper_size = -1;
76 : static int landscape_flag = 0;
77 : static int duplex_flag = 0;
78 :
79 : // An upper limit on the paper dimensions in centipoints,
80 : // used for setting HPGL picture frame.
81 : #define MAX_PAPER_WIDTH (12*720)
82 : #define MAX_PAPER_HEIGHT (17*720)
83 :
84 : // Dotted lines that are thinner than this don't work right.
85 : #define MIN_DOT_PEN_WIDTH .351
86 :
87 : #ifndef DEFAULT_LINE_WIDTH_FACTOR
88 : // in ems/1000
89 : #define DEFAULT_LINE_WIDTH_FACTOR 40
90 : #endif
91 :
92 : const int DEFAULT_HPGL_UNITS = 1016;
93 : int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
94 : unsigned ncopies = 0; // 0 means don't send ncopies command
95 :
96 : class lj4_font : public font {
97 : public:
98 : ~lj4_font();
99 : void handle_unknown_font_command(const char * /* command */,
100 : const char * /* arg */,
101 : const char * /* fn */,
102 : int lineno);
103 : static lj4_font *load_lj4_font(const char * /* s */);
104 : int weight;
105 : int style;
106 : int proportional;
107 : int typeface;
108 : private:
109 : lj4_font(const char * /* nm */);
110 : };
111 :
112 1 : lj4_font::lj4_font(const char *nm)
113 1 : : font(nm), weight(0), style(0), proportional(0), typeface(0)
114 : {
115 1 : }
116 :
117 2 : lj4_font::~lj4_font()
118 : {
119 2 : }
120 :
121 1 : lj4_font *lj4_font::load_lj4_font(const char *s)
122 : {
123 1 : lj4_font *f = new lj4_font(s);
124 1 : if (!f->load()) {
125 0 : delete f;
126 0 : return 0 /* nullptr */;
127 : }
128 1 : return f;
129 : }
130 :
131 : static struct lj4_command_table {
132 : const char *s;
133 : int lj4_font::*ptr;
134 : int min;
135 : int max;
136 : } command_table[] = {
137 : { "pclweight", &lj4_font::weight, -7, 7 },
138 : { "pclstyle", &lj4_font::style, 0, 32767 },
139 : { "pclproportional", &lj4_font::proportional, 0, 1 },
140 : { "pcltypeface", &lj4_font::typeface, 0, 65535 },
141 : };
142 :
143 4 : void lj4_font::handle_unknown_font_command(const char *command,
144 : const char *arg,
145 : const char *fn,
146 : int lineno)
147 : {
148 10 : for (size_t i = 0; i < countof(command_table); i++) {
149 10 : if (strcmp(command, command_table[i].s) == 0) {
150 4 : if (0 /* nullptr */ == arg)
151 0 : fatal_with_file_and_line(fn, lineno,
152 : "'%1' command requires an argument",
153 0 : command);
154 : char *ptr;
155 4 : long n = strtol(arg, &ptr, 10);
156 4 : if (ptr == arg)
157 0 : fatal_with_file_and_line(fn, lineno,
158 : "'%1' command requires numeric"
159 0 : " argument", command);
160 4 : if (n < command_table[i].min) {
161 0 : error_with_file_and_line(fn, lineno,
162 : "'%1' command argument must not be"
163 : " less than %2", command,
164 0 : command_table[i].min);
165 0 : n = command_table[i].min;
166 : }
167 4 : else if (n > command_table[i].max) {
168 0 : error_with_file_and_line(fn, lineno,
169 : "'%1' command argument must not be"
170 : " greater than %2", command,
171 0 : command_table[i].max);
172 0 : n = command_table[i].max;
173 : }
174 4 : this->*command_table[i].ptr = int(n);
175 4 : break;
176 : }
177 : }
178 4 : }
179 :
180 1 : static ssize_t lookup_paper_size(const char *s)
181 : {
182 : // C++11: constexpr
183 1 : const size_t paper_table_length = countof(paper_table);
184 : // ...and once it's a constexpr, we can do this...
185 : //static_assert(paper_table_length < INT_MAX);
186 : // ...but until then...
187 : assert(paper_table_length < INT_MAX);
188 1 : for (size_t i = 0; i < paper_table_length; i++) {
189 : // FIXME Perhaps allow unique prefix.
190 1 : if (strcasecmp(s, paper_table[i].name) == 0)
191 1 : return ssize_t(i);
192 : }
193 0 : return ssize_t(-1);
194 : }
195 :
196 : class lj4_printer : public printer {
197 : public:
198 : lj4_printer(int);
199 : ~lj4_printer();
200 : void set_char(glyph * /* g */,
201 : font * /* f */,
202 : const environment * /* env */,
203 : int /* w */,
204 : const char * /* UNUSED */);
205 : void draw(int /* code */, int * /* p */, int /* np */,
206 : const environment * /* env */);
207 : void begin_page(int /* UNUSED */);
208 : void end_page(int /* page_length */);
209 : font *make_font(const char * /* nm */);
210 : void end_of_line();
211 : private:
212 : void set_line_thickness(int /* size */, int /* dot */ = 0);
213 : void hpgl_init();
214 : void hpgl_start();
215 : void hpgl_end();
216 : int moveto(int /* hpos */, int /* vpos */);
217 : int moveto1(int /* hpos */, int /* vpos */);
218 :
219 : int cur_hpos;
220 : int cur_vpos;
221 : lj4_font *cur_font;
222 : int cur_size;
223 : unsigned short cur_symbol_set;
224 : int x_offset;
225 : int line_thickness;
226 : double pen_width;
227 : double hpgl_scale;
228 : int hpgl_inited;
229 : int paper_size;
230 : };
231 :
232 : inline
233 12 : int lj4_printer::moveto(int hpos, int vpos)
234 : {
235 12 : if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
236 4 : return moveto1(hpos, vpos);
237 : else
238 8 : return 1;
239 : }
240 :
241 : inline
242 0 : void lj4_printer::hpgl_start()
243 : {
244 0 : fputs("\033%1B", stdout);
245 0 : }
246 :
247 : inline
248 0 : void lj4_printer::hpgl_end()
249 : {
250 0 : fputs(";\033%0A", stdout);
251 0 : }
252 :
253 1 : lj4_printer::lj4_printer(int ps)
254 : : cur_hpos(-1),
255 : cur_font(0 /* nullptr */),
256 : cur_size(0),
257 : cur_symbol_set(0),
258 : line_thickness(-1),
259 : pen_width(-1.0),
260 1 : hpgl_inited(0)
261 : {
262 1 : if (7200 % font::res != 0)
263 0 : fatal("invalid resolution %1: resolution must be a factor of 7200",
264 0 : font::res);
265 1 : fputs("\033E", stdout); // reset
266 1 : if (font::res != 300)
267 1 : printf("\033&u%dD", font::res); // unit of measure
268 1 : if (ncopies > 0)
269 0 : printf("\033&l%uX", ncopies);
270 1 : paper_size = 0; // default to letter
271 1 : if (font::papersize) {
272 1 : int n = lookup_paper_size(font::papersize);
273 1 : if (n < 0)
274 0 : error("ignoring invalid paper format '%1'", font::papersize);
275 : else
276 1 : paper_size = n;
277 : }
278 1 : if (ps >= 0)
279 0 : paper_size = ps;
280 1 : printf("\033&l%dA" // paper format
281 : "\033&l%dO" // orientation
282 : "\033&l0E", // no top margin
283 1 : paper_table[paper_size].code,
284 : landscape_flag != 0);
285 1 : if (landscape_flag)
286 0 : x_offset = paper_table[paper_size].x_offset_landscape;
287 : else
288 1 : x_offset = paper_table[paper_size].x_offset_portrait;
289 1 : x_offset = (x_offset * font::res) / 300;
290 1 : if (duplex_flag)
291 0 : printf("\033&l%dS", duplex_flag);
292 1 : }
293 :
294 2 : lj4_printer::~lj4_printer()
295 : {
296 1 : current_lineno = 0; // At this point, we've read all the input.
297 1 : fputs("\033E", stdout);
298 2 : }
299 :
300 1 : void lj4_printer::begin_page(int)
301 : {
302 1 : }
303 :
304 1 : void lj4_printer::end_page(int)
305 : {
306 1 : putchar('\f');
307 1 : cur_hpos = -1;
308 1 : }
309 :
310 1 : void lj4_printer::end_of_line()
311 : {
312 1 : cur_hpos = -1; // force absolute motion
313 1 : }
314 :
315 : inline
316 12 : int is_unprintable(unsigned char c)
317 : {
318 12 : return c < 32 && (0 == c || (7 <= c && c <= 15) || 27 == c);
319 : }
320 :
321 12 : void lj4_printer::set_char(glyph *g, font *f, const environment *env,
322 : int w, const char *)
323 : {
324 12 : int code = f->get_code(g);
325 :
326 12 : unsigned char ch = code & 0xff;
327 12 : unsigned short symbol_set = code >> 8;
328 12 : if (symbol_set != cur_symbol_set) {
329 1 : printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
330 1 : cur_symbol_set = symbol_set;
331 : }
332 12 : if (f != cur_font) {
333 1 : lj4_font *psf = (lj4_font *)f;
334 : // FIXME only output those that are needed
335 1 : printf("\033(s%dp%ds%db%dT",
336 : psf->proportional,
337 : psf->style,
338 : psf->weight,
339 : psf->typeface);
340 1 : if (!psf->proportional || !cur_font || !cur_font->proportional)
341 1 : cur_size = 0;
342 1 : cur_font = psf;
343 : }
344 12 : if (env->size != cur_size) {
345 1 : if (cur_font->proportional) {
346 : static const char *quarters[] = { "", ".25", ".5", ".75" };
347 1 : printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
348 : }
349 : else {
350 0 : double pitch = double(font::res)/w;
351 : // PCL uses the next largest pitch, so round it down.
352 0 : pitch = floor(pitch*100.0)/100.0;
353 0 : printf("\033(s%.2fH", pitch);
354 : }
355 1 : cur_size = env->size;
356 : }
357 12 : if (!moveto(env->hpos, env->vpos))
358 0 : return;
359 12 : if (is_unprintable(ch))
360 0 : fputs("\033&p1X", stdout);
361 12 : putchar(ch);
362 12 : cur_hpos += w;
363 : }
364 :
365 4 : int lj4_printer::moveto1(int hpos, int vpos)
366 : {
367 4 : if (hpos < x_offset || vpos < 0)
368 0 : return 0;
369 4 : fputs("\033*p", stdout);
370 4 : if (cur_hpos < 0)
371 1 : printf("%dx%dY", hpos - x_offset, vpos);
372 : else {
373 3 : if (cur_hpos != hpos)
374 6 : printf("%s%d%c", (hpos > cur_hpos) ? "+" : "",
375 6 : hpos - cur_hpos, (vpos == cur_vpos) ? 'X' : 'x');
376 3 : if (cur_vpos != vpos)
377 0 : printf("%s%dY", (vpos > cur_vpos) ? "+" : "", vpos - cur_vpos);
378 : }
379 4 : cur_hpos = hpos;
380 4 : cur_vpos = vpos;
381 4 : return 1;
382 : }
383 :
384 0 : void lj4_printer::draw(int code, int *p, int np, const environment *env)
385 : {
386 0 : switch (code) {
387 0 : case 'R':
388 : {
389 0 : if (np != 2) {
390 0 : error("2 arguments required for rule");
391 0 : break;
392 : }
393 0 : int hpos = env->hpos;
394 0 : int vpos = env->vpos;
395 0 : int hsize = p[0];
396 0 : int vsize = p[1];
397 0 : if (hsize < 0) {
398 0 : hpos += hsize;
399 0 : hsize = -hsize;
400 : }
401 0 : if (vsize < 0) {
402 0 : vpos += vsize;
403 0 : vsize = -vsize;
404 : }
405 0 : if (!moveto(hpos, vpos))
406 0 : return;
407 0 : printf("\033*c%da%db0P", hsize, vsize);
408 0 : break;
409 : }
410 0 : case 'l':
411 0 : if (np != 2) {
412 0 : error("2 arguments required for line");
413 0 : break;
414 : }
415 0 : hpgl_init();
416 0 : if (!moveto(env->hpos, env->vpos))
417 0 : return;
418 0 : hpgl_start();
419 0 : set_line_thickness(env->size, 0 == p[0] && 0 == p[1]);
420 0 : printf("PD%d,%d", p[0], p[1]);
421 0 : hpgl_end();
422 0 : break;
423 0 : case 'p':
424 : case 'P':
425 : {
426 0 : if ((np % 2) == 1) {
427 0 : error("even number of arguments required for polygon");
428 0 : break;
429 : }
430 0 : if (0 == np) {
431 0 : error("no arguments for polygon");
432 0 : break;
433 : }
434 0 : hpgl_init();
435 0 : if (!moveto(env->hpos, env->vpos))
436 0 : return;
437 0 : hpgl_start();
438 0 : if ('p' == code)
439 0 : set_line_thickness(env->size);
440 0 : printf("PMPD%d", p[0]);
441 0 : for (int i = 1; i < np; i++)
442 0 : printf(",%d", p[i]);
443 0 : printf("PM2%cP", ('p' == code) ? 'E' : 'F');
444 0 : hpgl_end();
445 0 : break;
446 : }
447 0 : case '~':
448 : {
449 0 : if ((np % 2) == 1) {
450 0 : error("even number of arguments required for spline");
451 0 : break;
452 : }
453 0 : if (0 == np) {
454 0 : error("no arguments for spline");
455 0 : break;
456 : }
457 0 : hpgl_init();
458 0 : if (!moveto(env->hpos, env->vpos))
459 0 : return;
460 0 : hpgl_start();
461 0 : set_line_thickness(env->size);
462 0 : printf("PD%d,%d", p[0]/2, p[1]/2);
463 0 : const int tnum = 2;
464 0 : const int tden = 3;
465 0 : if (np > 2) {
466 0 : fputs("BR", stdout);
467 0 : for (int i = 0; i < np - 2; i += 2) {
468 0 : if (i != 0)
469 0 : putchar(',');
470 0 : printf("%d,%d,%d,%d,%d,%d",
471 0 : (p[i]*tnum)/(2*tden),
472 0 : (p[i + 1]*tnum)/(2*tden),
473 0 : p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
474 0 : p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
475 0 : (p[i] - p[i]/2) + p[i + 2]/2,
476 0 : (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
477 : }
478 : }
479 0 : printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
480 0 : hpgl_end();
481 0 : break;
482 : }
483 0 : case 'c':
484 : case 'C':
485 : // troff adds an extra argument to C
486 0 : if (np != 1 && !(('C' == code) && (2 == np))) {
487 0 : error("1 argument required for circle");
488 0 : break;
489 : }
490 0 : hpgl_init();
491 0 : if (!moveto(env->hpos + p[0]/2, env->vpos))
492 0 : return;
493 0 : hpgl_start();
494 0 : if ('c' == code) {
495 0 : set_line_thickness(env->size);
496 0 : printf("CI%d", p[0]/2);
497 : }
498 : else
499 0 : printf("WG%d,0,360", p[0]/2);
500 0 : hpgl_end();
501 0 : break;
502 0 : case 'e':
503 : case 'E':
504 0 : if (np != 2) {
505 0 : error("2 arguments required for ellipse");
506 0 : break;
507 : }
508 0 : hpgl_init();
509 0 : if (!moveto(env->hpos + p[0]/2, env->vpos))
510 0 : return;
511 0 : hpgl_start();
512 0 : printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
513 0 : if ('e' == code) {
514 0 : set_line_thickness(env->size);
515 0 : printf("CI%d", p[1]/2);
516 : }
517 : else
518 0 : printf("WG%d,0,360", p[1]/2);
519 0 : printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
520 0 : hpgl_end();
521 0 : break;
522 0 : case 'a':
523 : {
524 0 : if (np != 4) {
525 0 : error("4 arguments required for arc");
526 0 : break;
527 : }
528 0 : hpgl_init();
529 0 : if (!moveto(env->hpos, env->vpos))
530 0 : return;
531 0 : hpgl_start();
532 0 : set_line_thickness(env->size);
533 : double c[2];
534 0 : if (adjust_arc_center(p, c)) {
535 0 : double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
536 0 : - atan2(-c[1], -c[0]))
537 0 : * 180.0/PI);
538 0 : if (sweep > 0.0)
539 0 : sweep -= 360.0;
540 0 : printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
541 : }
542 : else
543 0 : printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
544 0 : hpgl_end();
545 : }
546 0 : break;
547 0 : case 'f':
548 0 : if (np != 1 && np != 2) {
549 0 : error("1 argument required for fill");
550 0 : break;
551 : }
552 0 : hpgl_init();
553 0 : hpgl_start();
554 0 : if (p[0] >= 0 && p[0] <= 1000)
555 0 : printf("FT10,%d", p[0]/10);
556 0 : hpgl_end();
557 0 : break;
558 0 : case 'F':
559 : // not implemented yet
560 0 : break;
561 0 : case 't':
562 : {
563 0 : if (0 == np) {
564 0 : line_thickness = -1;
565 : }
566 : else {
567 : // troff gratuitously adds an extra 0
568 0 : if (np != 1 && np != 2) {
569 0 : error("0 or 1 argument required for thickness");
570 0 : break;
571 : }
572 0 : line_thickness = p[0];
573 : }
574 0 : break;
575 : }
576 0 : default:
577 0 : error("unrecognized drawing command '%1'", char(code));
578 0 : break;
579 : }
580 : }
581 :
582 0 : void lj4_printer::hpgl_init()
583 : {
584 0 : if (hpgl_inited)
585 0 : return;
586 0 : hpgl_inited = 1;
587 0 : hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
588 0 : printf("\033&f0S" // push position
589 : "\033*p0x0Y" // move to 0,0
590 : "\033*c%dx%dy0T" // establish picture frame
591 : "\033%%1B" // switch to HPGL
592 : "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
593 : "LA1,4,2,4" // round line ends and joins
594 : "PR" // relative plotting
595 : "TR0" // opaque
596 : ";\033%%1A" // back to PCL
597 : "\033&f1S", // pop position
598 : MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
599 : hpgl_scale, hpgl_scale);
600 : }
601 :
602 0 : void lj4_printer::set_line_thickness(int size, int dot)
603 : {
604 : double pw;
605 0 : if (line_thickness < 0)
606 0 : pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
607 : else
608 0 : pw = line_thickness*25.4/font::res;
609 0 : if (dot && pw < MIN_DOT_PEN_WIDTH)
610 0 : pw = MIN_DOT_PEN_WIDTH;
611 0 : if (pw != pen_width) {
612 0 : printf("PW%f", pw);
613 0 : pen_width = pw;
614 : }
615 0 : }
616 :
617 1 : font *lj4_printer::make_font(const char *nm)
618 : {
619 1 : return lj4_font::load_lj4_font(nm);
620 : }
621 :
622 1 : printer *make_printer()
623 : {
624 1 : return new lj4_printer(user_paper_size);
625 : }
626 :
627 : static void usage(FILE *stream);
628 :
629 : extern "C" int optopt, optind;
630 :
631 1 : int main(int argc, char **argv)
632 : {
633 1 : setlocale(LC_NUMERIC, "C");
634 1 : program_name = argv[0];
635 : static char stderr_buf[BUFSIZ];
636 1 : setbuf(stderr, stderr_buf);
637 : int c;
638 : static const struct option long_options[] = {
639 : { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
640 : { "version", no_argument, 0 /* nullptr */, 'v' },
641 : { 0 /* nullptr */, 0, 0 /* nullptr */, 0 }
642 : };
643 1 : while ((c = getopt_long(argc, argv, ":c:d:F:I:lp:vw:", long_options,
644 : 0 /* nullptr */))
645 1 : != EOF)
646 0 : switch (c) {
647 0 : case 'l':
648 0 : landscape_flag = 1;
649 0 : break;
650 0 : case 'I':
651 : // ignore include search path
652 0 : break;
653 0 : case 'd':
654 0 : if (!csdigit(*optarg)) // this ugly hack prevents -d without
655 0 : optind--; // args from messing up the arg list
656 0 : duplex_flag = atoi(optarg);
657 0 : if (duplex_flag != 1 && duplex_flag != 2) {
658 0 : fprintf(stderr, "argument to command-line option 'd' out of"
659 : " range; assuming long-side duplexing\n");
660 0 : duplex_flag = 1;
661 : }
662 0 : break;
663 0 : case 'p':
664 : {
665 0 : ssize_t n = lookup_paper_size(optarg);
666 0 : if (n < 0)
667 0 : error("ignoring invalid paper format '%1'", font::papersize);
668 : else
669 0 : user_paper_size = n;
670 0 : break;
671 : }
672 0 : case 'v':
673 0 : printf("GNU grolj4 (groff) version %s\n", Version_string);
674 0 : exit(EXIT_SUCCESS);
675 : break;
676 0 : case 'F':
677 0 : font::command_line_font_dir(optarg);
678 0 : break;
679 0 : case 'c':
680 : {
681 : char *ptr;
682 0 : long n = strtol(optarg, &ptr, 10);
683 0 : if (ptr == optarg)
684 0 : error("argument to command-line option 'c' option must be a"
685 : " positive integer");
686 0 : else if (n <= 0 || n > 32767)
687 0 : error("argument to command-line option 'c' must be in range"
688 0 : " (0, 32767]; got '%1'", optarg);
689 : else
690 0 : ncopies = unsigned(n);
691 0 : break;
692 : }
693 0 : case 'w':
694 : {
695 : char *ptr;
696 0 : long n = strtol(optarg, &ptr, 10);
697 0 : if (ptr == optarg)
698 0 : error("argument to command-line option 'w' must be a"
699 : " non-negative integer");
700 0 : else if (n < 0 || n > INT_MAX)
701 0 : error("argument to command-line option 'w' must be in range"
702 0 : " [0, %1]; got '%2'", INT_MAX, optarg);
703 : else
704 0 : line_width_factor = int(n);
705 0 : break;
706 : }
707 0 : case CHAR_MAX + 1: // --help
708 0 : usage(stdout);
709 0 : exit(EXIT_SUCCESS);
710 : break;
711 0 : case '?':
712 0 : if (optopt != 0)
713 0 : error("unrecognized command-line option '%1'", char(optopt));
714 : else
715 0 : error("unrecognized command-line option '%1'",
716 0 : argv[(optind - 1)]);
717 0 : usage(stderr);
718 0 : exit(2);
719 : break;
720 0 : case ':':
721 0 : if ('d' == optopt) {
722 0 : warning("command-line option 'd' requires an argument; assuming"
723 : " duplexing on long side");
724 0 : duplex_flag = 1;
725 : }
726 : else {
727 0 : error("command-line option '%1' requires an argument",
728 0 : char(optopt));
729 0 : usage(stderr);
730 0 : exit(2);
731 : }
732 0 : break;
733 0 : default:
734 0 : assert(0 == "unhandled getopt_long return value");
735 : }
736 : SET_BINARY(fileno(stdout));
737 1 : if (optind >= argc)
738 1 : do_file("-");
739 : else {
740 0 : for (int i = optind; i < argc; i++)
741 0 : do_file(argv[i]);
742 : }
743 1 : return 0;
744 : }
745 :
746 0 : static void usage(FILE *stream)
747 : {
748 0 : fprintf(stream,
749 : "usage: %s [-l] [-c n] [-d [n]] [-F dir] [-p paper-format] [-w n]"
750 : " [file ...]\n"
751 : "usage: %s {-v | --version}\n"
752 : "usage: %s --help\n",
753 : program_name, program_name, program_name);
754 0 : if (stdout == stream)
755 0 : fputs("\n"
756 : "Translate the output of troff(1) into PCL 5/LaserJet 4 format. See\n"
757 : "the grolj4(1) manual page.\n", stream);
758 0 : }
759 :
760 : // Local Variables:
761 : // fill-column: 72
762 : // mode: C++
763 : // End:
764 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|