1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004
3151497Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru/*
23114402SruTODO
24114402Sru
25114402Sruoption to use beziers for circle/ellipse/arc
26114402Sruoption to use lines for spline (for LJ3)
27114402Sruleft/top offset registration
28114402Sruoutput bin selection option
29114402Srupaper source option
30114402Sruoutput non-integer parameters using fixed point numbers
31114402SruX command to insert contents of file
32114402SruX command to specify inline escape sequence (how to specify unprintable chars?)
33114402SruX command to include bitmap graphics
34114402Sru*/
35114402Sru
36114402Sru#include "driver.h"
37114402Sru#include "nonposix.h"
38114402Sru
39114402Sruextern "C" const char *Version_string;
40114402Sru
41114402Srustatic struct {
42114402Sru  const char *name;
43114402Sru  int code;
44114402Sru  // at 300dpi
45114402Sru  int x_offset_portrait;
46114402Sru  int x_offset_landscape;
47114402Sru} paper_table[] = {
48114402Sru  { "letter", 2, 75, 60 },
49114402Sru  { "legal", 3, 75, 60 },
50114402Sru  { "executive", 1, 75, 60 },
51114402Sru  { "a4", 26, 71, 59 },
52114402Sru  { "com10", 81, 75, 60 },
53114402Sru  { "monarch", 80, 75, 60 },
54114402Sru  { "c5", 91, 71, 59 },
55114402Sru  { "b5", 100, 71, 59 },
56114402Sru  { "dl", 90, 71, 59 },
57114402Sru};
58114402Sru
59114402Srustatic int user_paper_size = -1;
60114402Srustatic int landscape_flag = 0;
61114402Srustatic int duplex_flag = 0;
62114402Sru
63114402Sru// An upper limit on the paper size in centipoints,
64114402Sru// used for setting HPGL picture frame.
65114402Sru#define MAX_PAPER_WIDTH (12*720)
66114402Sru#define MAX_PAPER_HEIGHT (17*720)
67114402Sru
68114402Sru// Dotted lines that are thinner than this don't work right.
69114402Sru#define MIN_DOT_PEN_WIDTH .351
70114402Sru
71114402Sru#ifndef DEFAULT_LINE_WIDTH_FACTOR
72114402Sru// in ems/1000
73114402Sru#define DEFAULT_LINE_WIDTH_FACTOR 40
74114402Sru#endif
75114402Sru
76114402Sruconst int DEFAULT_HPGL_UNITS = 1016;
77114402Sruint line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
78114402Sruunsigned ncopies = 0;		// 0 means don't send ncopies command
79114402Sru
80114402Srustatic int lookup_paper_size(const char *);
81114402Sru
82114402Sruclass lj4_font : public font {
83114402Srupublic:
84114402Sru  ~lj4_font();
85114402Sru  void handle_unknown_font_command(const char *command, const char *arg,
86114402Sru				   const char *filename, int lineno);
87114402Sru  static lj4_font *load_lj4_font(const char *);
88114402Sru  int weight;
89114402Sru  int style;
90114402Sru  int proportional;
91114402Sru  int typeface;
92114402Sruprivate:
93114402Sru  lj4_font(const char *);
94114402Sru};
95114402Sru
96114402Srulj4_font::lj4_font(const char *nm)
97114402Sru: font(nm), weight(0), style(0), proportional(0), typeface(0)
98114402Sru{
99114402Sru}
100114402Sru
101114402Srulj4_font::~lj4_font()
102114402Sru{
103114402Sru}
104114402Sru
105114402Srulj4_font *lj4_font::load_lj4_font(const char *s)
106114402Sru{
107114402Sru  lj4_font *f = new lj4_font(s);
108114402Sru  if (!f->load()) {
109114402Sru    delete f;
110114402Sru    return 0;
111114402Sru  }
112114402Sru  return f;
113114402Sru}
114114402Sru
115114402Srustatic struct {
116114402Sru  const char *s;
117114402Sru  int lj4_font::*ptr;
118114402Sru  int min;
119114402Sru  int max;
120114402Sru} command_table[] = {
121114402Sru  { "pclweight", &lj4_font::weight, -7, 7 },
122114402Sru  { "pclstyle", &lj4_font::style, 0, 32767 },
123114402Sru  { "pclproportional", &lj4_font::proportional, 0, 1 },
124114402Sru  { "pcltypeface", &lj4_font::typeface, 0, 65535 },
125114402Sru};
126114402Sru
127114402Sruvoid lj4_font::handle_unknown_font_command(const char *command,
128114402Sru					   const char *arg,
129114402Sru					   const char *filename, int lineno)
130114402Sru{
131114402Sru  for (unsigned int i = 0;
132114402Sru       i < sizeof(command_table)/sizeof(command_table[0]); i++) {
133114402Sru    if (strcmp(command, command_table[i].s) == 0) {
134114402Sru      if (arg == 0)
135114402Sru	fatal_with_file_and_line(filename, lineno,
136114402Sru				 "`%1' command requires an argument",
137114402Sru				 command);
138114402Sru      char *ptr;
139114402Sru      long n = strtol(arg, &ptr, 10);
140114402Sru      if (n == 0 && ptr == arg)
141114402Sru	fatal_with_file_and_line(filename, lineno,
142114402Sru				 "`%1' command requires numeric argument",
143114402Sru				 command);
144114402Sru      if (n < command_table[i].min) {
145114402Sru	error_with_file_and_line(filename, lineno,
146114402Sru				 "argument for `%1' command must not be less than %2",
147114402Sru				 command, command_table[i].min);
148114402Sru	n = command_table[i].min;
149114402Sru      }
150114402Sru      else if (n > command_table[i].max) {
151114402Sru	error_with_file_and_line(filename, lineno,
152114402Sru				 "argument for `%1' command must not be greater than %2",
153114402Sru				 command, command_table[i].max);
154114402Sru	n = command_table[i].max;
155114402Sru      }
156114402Sru      this->*command_table[i].ptr = int(n);
157114402Sru      break;
158114402Sru    }
159114402Sru  }
160114402Sru}
161114402Sru
162114402Sruclass lj4_printer : public printer {
163114402Srupublic:
164114402Sru  lj4_printer(int);
165114402Sru  ~lj4_printer();
166114402Sru  void set_char(int, font *, const environment *, int, const char *name);
167114402Sru  void draw(int code, int *p, int np, const environment *env);
168114402Sru  void begin_page(int);
169114402Sru  void end_page(int page_length);
170114402Sru  font *make_font(const char *);
171114402Sru  void end_of_line();
172114402Sruprivate:
173114402Sru  void set_line_thickness(int size, int dot = 0);
174114402Sru  void hpgl_init();
175114402Sru  void hpgl_start();
176114402Sru  void hpgl_end();
177114402Sru  int moveto(int hpos, int vpos);
178114402Sru  int moveto1(int hpos, int vpos);
179114402Sru
180114402Sru  int cur_hpos;
181114402Sru  int cur_vpos;
182114402Sru  lj4_font *cur_font;
183114402Sru  int cur_size;
184114402Sru  unsigned short cur_symbol_set;
185114402Sru  int x_offset;
186114402Sru  int line_thickness;
187114402Sru  double pen_width;
188114402Sru  double hpgl_scale;
189114402Sru  int hpgl_inited;
190114402Sru  int paper_size;
191114402Sru};
192114402Sru
193114402Sruinline
194114402Sruint lj4_printer::moveto(int hpos, int vpos)
195114402Sru{
196114402Sru  if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
197114402Sru    return moveto1(hpos, vpos);
198114402Sru  else
199114402Sru    return 1;
200114402Sru}
201114402Sru
202114402Sruinline
203114402Sruvoid lj4_printer::hpgl_start()
204114402Sru{
205114402Sru  fputs("\033%1B", stdout);
206114402Sru}
207114402Sru
208114402Sruinline
209114402Sruvoid lj4_printer::hpgl_end()
210114402Sru{
211114402Sru  fputs(";\033%0A", stdout);
212114402Sru}
213114402Sru
214114402Srulj4_printer::lj4_printer(int ps)
215114402Sru: cur_hpos(-1),
216114402Sru  cur_font(0),
217114402Sru  cur_size(0),
218114402Sru  cur_symbol_set(0),
219114402Sru  line_thickness(-1),
220114402Sru  pen_width(-1.0),
221114402Sru  hpgl_inited(0)
222114402Sru{
223114402Sru  if (7200 % font::res != 0)
224114402Sru    fatal("invalid resolution %1: resolution must be a factor of 7200",
225114402Sru	  font::res);
226114402Sru  fputs("\033E", stdout);		// reset
227114402Sru  if (font::res != 300)
228114402Sru    printf("\033&u%dD", font::res);	// unit of measure
229114402Sru  if (ncopies > 0)
230114402Sru    printf("\033&l%uX", ncopies);
231114402Sru  paper_size = 0;		// default to letter
232114402Sru  if (font::papersize) {
233114402Sru    int n = lookup_paper_size(font::papersize);
234114402Sru    if (n < 0)
235114402Sru      error("unknown paper size `%1'", font::papersize);
236114402Sru    else
237114402Sru      paper_size = n;
238114402Sru  }
239114402Sru  if (ps >= 0)
240114402Sru    paper_size = ps;
241114402Sru  printf("\033&l%dA"		// paper size
242114402Sru	 "\033&l%dO"		// orientation
243114402Sru	 "\033&l0E",		// no top margin
244114402Sru	 paper_table[paper_size].code,
245114402Sru	 landscape_flag != 0);
246114402Sru  if (landscape_flag)
247114402Sru    x_offset = paper_table[paper_size].x_offset_landscape;
248114402Sru  else
249114402Sru    x_offset = paper_table[paper_size].x_offset_portrait;
250114402Sru  x_offset = (x_offset * font::res) / 300;
251114402Sru  if (duplex_flag)
252114402Sru     printf("\033&l%dS", duplex_flag);
253114402Sru}
254114402Sru
255114402Srulj4_printer::~lj4_printer()
256114402Sru{
257114402Sru  fputs("\033E", stdout);
258114402Sru}
259114402Sru
260114402Sruvoid lj4_printer::begin_page(int)
261114402Sru{
262114402Sru}
263114402Sru
264114402Sruvoid lj4_printer::end_page(int)
265114402Sru{
266114402Sru  putchar('\f');
267114402Sru  cur_hpos = -1;
268114402Sru}
269114402Sru
270114402Sruvoid lj4_printer::end_of_line()
271114402Sru{
272114402Sru  cur_hpos = -1;		// force absolute motion
273114402Sru}
274114402Sru
275114402Sruinline
276114402Sruint is_unprintable(unsigned char c)
277114402Sru{
278114402Sru  return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27);
279114402Sru}
280114402Sru
281151497Sruvoid lj4_printer::set_char(int idx, font *f, const environment *env,
282114402Sru			   int w, const char *)
283114402Sru{
284151497Sru  int code = f->get_code(idx);
285114402Sru
286114402Sru  unsigned char ch = code & 0xff;
287114402Sru  unsigned short symbol_set = code >> 8;
288114402Sru  if (symbol_set != cur_symbol_set) {
289114402Sru    printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
290114402Sru    cur_symbol_set = symbol_set;
291114402Sru  }
292114402Sru  if (f != cur_font) {
293114402Sru    lj4_font *psf = (lj4_font *)f;
294114402Sru    // FIXME only output those that are needed
295114402Sru    printf("\033(s%dp%ds%db%dT",
296114402Sru	   psf->proportional,
297114402Sru	   psf->style,
298114402Sru	   psf->weight,
299114402Sru	   psf->typeface);
300114402Sru    if (!psf->proportional || !cur_font || !cur_font->proportional)
301114402Sru      cur_size = 0;
302114402Sru    cur_font = psf;
303114402Sru  }
304114402Sru  if (env->size != cur_size) {
305114402Sru    if (cur_font->proportional) {
306114402Sru      static const char *quarters[] = { "", ".25", ".5", ".75" };
307114402Sru      printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
308114402Sru    }
309114402Sru    else {
310114402Sru      double pitch = double(font::res)/w;
311114402Sru      // PCL uses the next largest pitch, so round it down.
312114402Sru      pitch = floor(pitch*100.0)/100.0;
313114402Sru      printf("\033(s%.2fH", pitch);
314114402Sru    }
315114402Sru    cur_size = env->size;
316114402Sru  }
317114402Sru  if (!moveto(env->hpos, env->vpos))
318114402Sru    return;
319114402Sru  if (is_unprintable(ch))
320114402Sru    fputs("\033&p1X", stdout);
321114402Sru  putchar(ch);
322114402Sru  cur_hpos += w;
323114402Sru}
324114402Sru
325114402Sruint lj4_printer::moveto1(int hpos, int vpos)
326114402Sru{
327114402Sru  if (hpos < x_offset || vpos < 0)
328114402Sru    return 0;
329114402Sru  fputs("\033*p", stdout);
330114402Sru  if (cur_hpos < 0)
331114402Sru    printf("%dx%dY", hpos - x_offset, vpos);
332114402Sru  else {
333114402Sru    if (cur_hpos != hpos)
334114402Sru      printf("%s%d%c", hpos > cur_hpos ? "+" : "",
335114402Sru	     hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x');
336114402Sru    if (cur_vpos != vpos)
337114402Sru      printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos);
338114402Sru  }
339114402Sru  cur_hpos = hpos;
340114402Sru  cur_vpos = vpos;
341114402Sru  return 1;
342114402Sru}
343114402Sru
344114402Sruvoid lj4_printer::draw(int code, int *p, int np, const environment *env)
345114402Sru{
346114402Sru  switch (code) {
347114402Sru  case 'R':
348114402Sru    {
349114402Sru      if (np != 2) {
350114402Sru	error("2 arguments required for rule");
351114402Sru	break;
352114402Sru      }
353114402Sru      int hpos = env->hpos;
354114402Sru      int vpos = env->vpos;
355114402Sru      int hsize = p[0];
356114402Sru      int vsize = p[1];
357114402Sru      if (hsize < 0) {
358114402Sru	hpos += hsize;
359114402Sru	hsize = -hsize;
360114402Sru      }
361114402Sru      if (vsize < 0) {
362114402Sru	vpos += vsize;
363114402Sru	vsize = -vsize;
364114402Sru      }
365114402Sru      if (!moveto(hpos, vpos))
366114402Sru	return;
367114402Sru      printf("\033*c%da%db0P", hsize, vsize);
368114402Sru      break;
369114402Sru    }
370114402Sru  case 'l':
371114402Sru    if (np != 2) {
372114402Sru      error("2 arguments required for line");
373114402Sru      break;
374114402Sru    }
375114402Sru    hpgl_init();
376114402Sru    if (!moveto(env->hpos, env->vpos))
377114402Sru      return;
378114402Sru    hpgl_start();
379114402Sru    set_line_thickness(env->size, p[0] == 0 && p[1] == 0);
380114402Sru    printf("PD%d,%d", p[0], p[1]);
381114402Sru    hpgl_end();
382114402Sru    break;
383114402Sru  case 'p':
384114402Sru  case 'P':
385114402Sru    {
386114402Sru      if (np & 1) {
387114402Sru	error("even number of arguments required for polygon");
388114402Sru	break;
389114402Sru      }
390114402Sru      if (np == 0) {
391114402Sru	error("no arguments for polygon");
392114402Sru	break;
393114402Sru      }
394114402Sru      hpgl_init();
395114402Sru      if (!moveto(env->hpos, env->vpos))
396114402Sru	return;
397114402Sru      hpgl_start();
398114402Sru      if (code == 'p')
399114402Sru	set_line_thickness(env->size);
400114402Sru      printf("PMPD%d", p[0]);
401114402Sru      for (int i = 1; i < np; i++)
402114402Sru	printf(",%d", p[i]);
403114402Sru      printf("PM2%cP", code == 'p' ? 'E' : 'F');
404114402Sru      hpgl_end();
405114402Sru      break;
406114402Sru    }
407114402Sru  case '~':
408114402Sru    {
409114402Sru      if (np & 1) {
410114402Sru	error("even number of arguments required for spline");
411114402Sru	break;
412114402Sru      }
413114402Sru      if (np == 0) {
414114402Sru	error("no arguments for spline");
415114402Sru	break;
416114402Sru      }
417114402Sru      hpgl_init();
418114402Sru      if (!moveto(env->hpos, env->vpos))
419114402Sru	return;
420114402Sru      hpgl_start();
421114402Sru      set_line_thickness(env->size);
422114402Sru      printf("PD%d,%d", p[0]/2, p[1]/2);
423114402Sru      const int tnum = 2;
424114402Sru      const int tden = 3;
425114402Sru      if (np > 2) {
426114402Sru	fputs("BR", stdout);
427114402Sru	for (int i = 0; i < np - 2; i += 2) {
428114402Sru	  if (i != 0)
429114402Sru	    putchar(',');
430114402Sru	  printf("%d,%d,%d,%d,%d,%d",
431114402Sru		 (p[i]*tnum)/(2*tden),
432114402Sru		 (p[i + 1]*tnum)/(2*tden),
433114402Sru		 p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
434114402Sru		 p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
435114402Sru		 (p[i] - p[i]/2) + p[i + 2]/2,
436114402Sru		 (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
437114402Sru	}
438114402Sru      }
439114402Sru      printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
440114402Sru      hpgl_end();
441114402Sru      break;
442114402Sru    }
443114402Sru  case 'c':
444114402Sru  case 'C':
445114402Sru    // troff adds an extra argument to C
446114402Sru    if (np != 1 && !(code == 'C' && np == 2)) {
447114402Sru      error("1 argument required for circle");
448114402Sru      break;
449114402Sru    }
450114402Sru    hpgl_init();
451114402Sru    if (!moveto(env->hpos + p[0]/2, env->vpos))
452114402Sru      return;
453114402Sru    hpgl_start();
454114402Sru    if (code == 'c') {
455114402Sru      set_line_thickness(env->size);
456114402Sru      printf("CI%d", p[0]/2);
457114402Sru    }
458114402Sru    else
459114402Sru      printf("WG%d,0,360", p[0]/2);
460114402Sru    hpgl_end();
461114402Sru    break;
462114402Sru  case 'e':
463114402Sru  case 'E':
464114402Sru    if (np != 2) {
465114402Sru      error("2 arguments required for ellipse");
466114402Sru      break;
467114402Sru    }
468114402Sru    hpgl_init();
469114402Sru    if (!moveto(env->hpos + p[0]/2, env->vpos))
470114402Sru      return;
471114402Sru    hpgl_start();
472114402Sru    printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
473114402Sru    if (code == 'e') {
474114402Sru      set_line_thickness(env->size);
475114402Sru      printf("CI%d", p[1]/2);
476114402Sru    }
477114402Sru    else
478114402Sru      printf("WG%d,0,360", p[1]/2);
479114402Sru    printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
480114402Sru    hpgl_end();
481114402Sru    break;
482114402Sru  case 'a':
483114402Sru    {
484114402Sru      if (np != 4) {
485114402Sru	error("4 arguments required for arc");
486114402Sru	break;
487114402Sru      }
488114402Sru      hpgl_init();
489114402Sru      if (!moveto(env->hpos, env->vpos))
490114402Sru	return;
491114402Sru      hpgl_start();
492114402Sru      set_line_thickness(env->size);
493114402Sru      double c[2];
494114402Sru      if (adjust_arc_center(p, c)) {
495114402Sru	double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
496114402Sru			 - atan2(-c[1], -c[0]))
497114402Sru			* 180.0/PI);
498114402Sru	if (sweep > 0.0)
499114402Sru	  sweep -= 360.0;
500114402Sru	printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
501114402Sru      }
502114402Sru      else
503114402Sru	printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
504114402Sru      hpgl_end();
505114402Sru    }
506114402Sru    break;
507114402Sru  case 'f':
508114402Sru    if (np != 1 && np != 2) {
509114402Sru      error("1 argument required for fill");
510114402Sru      break;
511114402Sru    }
512114402Sru    hpgl_init();
513114402Sru    hpgl_start();
514114402Sru    if (p[0] >= 0 && p[0] <= 1000)
515114402Sru      printf("FT10,%d", p[0]/10);
516114402Sru    hpgl_end();
517114402Sru    break;
518114402Sru  case 'F':
519114402Sru    // not implemented yet
520114402Sru    break;
521114402Sru  case 't':
522114402Sru    {
523114402Sru      if (np == 0) {
524114402Sru	line_thickness = -1;
525114402Sru      }
526114402Sru      else {
527114402Sru	// troff gratuitously adds an extra 0
528114402Sru	if (np != 1 && np != 2) {
529114402Sru	  error("0 or 1 argument required for thickness");
530114402Sru	  break;
531114402Sru	}
532114402Sru	line_thickness = p[0];
533114402Sru      }
534114402Sru      break;
535114402Sru    }
536114402Sru  default:
537114402Sru    error("unrecognised drawing command `%1'", char(code));
538114402Sru    break;
539114402Sru  }
540114402Sru}
541114402Sru
542114402Sruvoid lj4_printer::hpgl_init()
543114402Sru{
544114402Sru  if (hpgl_inited)
545114402Sru    return;
546114402Sru  hpgl_inited = 1;
547114402Sru  hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
548114402Sru  printf("\033&f0S"		// push position
549114402Sru	 "\033*p0x0Y"		// move to 0,0
550114402Sru	 "\033*c%dx%dy0T"	// establish picture frame
551114402Sru	 "\033%%1B"		// switch to HPGL
552114402Sru	 "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
553114402Sru	 "LA1,4,2,4"		// round line ends and joins
554114402Sru	 "PR"			// relative plotting
555114402Sru	 "TR0"			// opaque
556114402Sru	 ";\033%%1A"		// back to PCL
557114402Sru	 "\033&f1S",		// pop position
558114402Sru	 MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
559114402Sru	 hpgl_scale, hpgl_scale);
560114402Sru}
561114402Sru
562114402Sruvoid lj4_printer::set_line_thickness(int size, int dot)
563114402Sru{
564114402Sru  double pw;
565114402Sru  if (line_thickness < 0)
566114402Sru    pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
567114402Sru  else
568114402Sru    pw = line_thickness*25.4/font::res;
569114402Sru  if (dot && pw < MIN_DOT_PEN_WIDTH)
570114402Sru    pw = MIN_DOT_PEN_WIDTH;
571114402Sru  if (pw != pen_width) {
572114402Sru    printf("PW%f", pw);
573114402Sru    pen_width = pw;
574114402Sru  }
575114402Sru}
576114402Sru
577114402Srufont *lj4_printer::make_font(const char *nm)
578114402Sru{
579114402Sru  return lj4_font::load_lj4_font(nm);
580114402Sru}
581114402Sru
582114402Sruprinter *make_printer()
583114402Sru{
584114402Sru  return new lj4_printer(user_paper_size);
585114402Sru}
586114402Sru
587114402Srustatic
588114402Sruint lookup_paper_size(const char *s)
589114402Sru{
590114402Sru  for (unsigned int i = 0;
591114402Sru       i < sizeof(paper_table)/sizeof(paper_table[0]); i++) {
592114402Sru    // FIXME Perhaps allow unique prefix.
593114402Sru    if (strcasecmp(s, paper_table[i].name) == 0)
594114402Sru      return i;
595114402Sru  }
596114402Sru  return -1;
597114402Sru}
598114402Sru
599114402Srustatic void usage(FILE *stream);
600114402Sru
601114402Sruextern "C" int optopt, optind;
602114402Sru
603114402Sruint main(int argc, char **argv)
604114402Sru{
605114402Sru  setlocale(LC_NUMERIC, "C");
606114402Sru  program_name = argv[0];
607114402Sru  static char stderr_buf[BUFSIZ];
608114402Sru  setbuf(stderr, stderr_buf);
609114402Sru  int c;
610114402Sru  static const struct option long_options[] = {
611114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
612114402Sru    { "version", no_argument, 0, 'v' },
613114402Sru    { NULL, 0, 0, 0 }
614114402Sru  };
615151497Sru  while ((c = getopt_long(argc, argv, "c:d:F:I:lp:vw:", long_options, NULL))
616114402Sru	 != EOF)
617114402Sru    switch(c) {
618114402Sru    case 'l':
619114402Sru      landscape_flag = 1;
620114402Sru      break;
621151497Sru    case 'I':
622151497Sru      // ignore include search path
623151497Sru      break;
624114402Sru    case ':':
625114402Sru      if (optopt == 'd') {
626114402Sru	fprintf(stderr, "duplex assumed to be long-side\n");
627114402Sru	duplex_flag = 1;
628114402Sru      } else
629151497Sru	fprintf(stderr, "option -%c requires an argument\n", optopt);
630114402Sru      fflush(stderr);
631114402Sru      break;
632114402Sru    case 'd':
633114402Sru      if (!isdigit(*optarg))	// this ugly hack prevents -d without
634114402Sru	optind--;		//  args from messing up the arg list
635114402Sru      duplex_flag = atoi(optarg);
636114402Sru      if (duplex_flag != 1 && duplex_flag != 2) {
637114402Sru	fprintf(stderr, "odd value for duplex; assumed to be long-side\n");
638114402Sru	duplex_flag = 1;
639114402Sru      }
640114402Sru      break;
641114402Sru    case 'p':
642114402Sru      {
643114402Sru	int n = lookup_paper_size(optarg);
644114402Sru	if (n < 0)
645114402Sru	  error("unknown paper size `%1'", optarg);
646114402Sru	else
647114402Sru	  user_paper_size = n;
648114402Sru	break;
649114402Sru      }
650114402Sru    case 'v':
651151497Sru      printf("GNU grolj4 (groff) version %s\n", Version_string);
652151497Sru      exit(0);
653151497Sru      break;
654114402Sru    case 'F':
655114402Sru      font::command_line_font_dir(optarg);
656114402Sru      break;
657114402Sru    case 'c':
658114402Sru      {
659114402Sru	char *ptr;
660114402Sru	long n = strtol(optarg, &ptr, 10);
661114402Sru	if (n == 0 && ptr == optarg)
662114402Sru	  error("argument for -c must be a positive integer");
663114402Sru	else if (n <= 0 || n > 32767)
664114402Sru	  error("out of range argument for -c");
665114402Sru	else
666114402Sru	  ncopies = unsigned(n);
667114402Sru	break;
668114402Sru      }
669114402Sru    case 'w':
670114402Sru      {
671114402Sru	char *ptr;
672114402Sru	long n = strtol(optarg, &ptr, 10);
673114402Sru	if (n == 0 && ptr == optarg)
674114402Sru	  error("argument for -w must be a non-negative integer");
675114402Sru	else if (n < 0 || n > INT_MAX)
676114402Sru	  error("out of range argument for -w");
677114402Sru	else
678114402Sru	  line_width_factor = int(n);
679114402Sru	break;
680114402Sru      }
681114402Sru    case CHAR_MAX + 1: // --help
682114402Sru      usage(stdout);
683114402Sru      exit(0);
684114402Sru      break;
685114402Sru    case '?':
686114402Sru      usage(stderr);
687114402Sru      exit(1);
688114402Sru      break;
689114402Sru    default:
690114402Sru      assert(0);
691114402Sru    }
692114402Sru  SET_BINARY(fileno(stdout));
693114402Sru  if (optind >= argc)
694114402Sru    do_file("-");
695114402Sru  else {
696114402Sru    for (int i = optind; i < argc; i++)
697114402Sru      do_file(argv[i]);
698114402Sru  }
699114402Sru  return 0;
700114402Sru}
701114402Sru
702114402Srustatic void usage(FILE *stream)
703114402Sru{
704114402Sru  fprintf(stream,
705114402Sru	  "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n"
706114402Sru	  "       [-w n] [-F dir] [files ...]\n",
707114402Sru	  program_name);
708114402Sru}
709