xref: /386bsd/usr/src/usr.bin/groff/grops/ps.cc (revision a2142627)
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4 
5 This file is part of groff.
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 2, or (at your option) any later
10 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 along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20 
21 #include "driver.h"
22 #include "stringclass.h"
23 #include "cset.h"
24 
25 #include "ps.h"
26 
27 static int landscape_flag = 0;
28 static int ncopies = 1;
29 static int linewidth = -1;
30 // Non-zero means generate PostScript code that guesses the paper
31 // length using the imageable area.
32 static int guess_flag = 0;
33 
34 // Non-zero if -b was specified on the command line.
35 static int bflag = 0;
36 unsigned broken_flags = 0;
37 
38 #define DEFAULT_LINEWIDTH 40	/* in ems/1000 */
39 #define FILL_MAX 1000
40 
41 const char *const dict_name = "grops";
42 const char *const defs_dict_name = "DEFS";
43 const int DEFS_DICT_SPARE = 50;
44 
degrees(double r)45 double degrees(double r)
46 {
47   return r*180.0/M_PI;
48 }
49 
radians(double d)50 double radians(double d)
51 {
52   return d*M_PI/180.0;
53 }
54 
transform_fill(int fill)55 inline double transform_fill(int fill)
56 {
57   return 1 - fill/double(FILL_MAX);
58 }
59 
ps_output(FILE * f,int n)60 ps_output::ps_output(FILE *f, int n)
61 : fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
62 {
63 }
64 
set_file(FILE * f)65 ps_output &ps_output::set_file(FILE *f)
66 {
67   fp = f;
68   col = 0;
69   return *this;
70 }
71 
copy_file(FILE * infp)72 ps_output &ps_output::copy_file(FILE *infp)
73 {
74   int c;
75   while ((c = getc(infp)) != EOF)
76     putc(c, fp);
77   return *this;
78 }
79 
end_line()80 ps_output &ps_output::end_line()
81 {
82   if (col != 0) {
83     putc('\n', fp);
84     col = 0;
85     need_space = 0;
86   }
87   return *this;
88 }
89 
special(const char * s)90 ps_output &ps_output::special(const char *s)
91 {
92   if (s == 0 || *s == '\0')
93     return *this;
94   if (col != 0) {
95     putc('\n', fp);
96     col = 0;
97   }
98   fputs(s, fp);
99   if (strchr(s, '\0')[-1] != '\n')
100     putc('\n', fp);
101   need_space = 0;
102   return *this;
103 }
104 
simple_comment(const char * s)105 ps_output &ps_output::simple_comment(const char *s)
106 {
107   if (col != 0)
108     putc('\n', fp);
109   putc('%', fp);
110   putc('%', fp);
111   fputs(s, fp);
112   putc('\n', fp);
113   col = 0;
114   need_space = 0;
115   return *this;
116 }
117 
begin_comment(const char * s)118 ps_output &ps_output::begin_comment(const char *s)
119 {
120   if (col != 0)
121     putc('\n', fp);
122   putc('%', fp);
123   putc('%', fp);
124   fputs(s, fp);
125   col = 2 + strlen(s);
126   return *this;
127 }
128 
end_comment()129 ps_output &ps_output::end_comment()
130 {
131   if (col != 0) {
132     putc('\n', fp);
133     col = 0;
134   }
135   need_space = 0;
136   return *this;
137 }
138 
comment_arg(const char * s)139 ps_output &ps_output::comment_arg(const char *s)
140 {
141   int len = strlen(s);
142   if (col + len + 1 > max_line_length) {
143     putc('\n', fp);
144     fputs("%%+", fp);
145     col = 3;
146   }
147   putc(' ',  fp);
148   fputs(s, fp);
149   col += len + 1;
150   return *this;
151 }
152 
set_fixed_point(int n)153 ps_output &ps_output::set_fixed_point(int n)
154 {
155   assert(n >= 0 && n <= 10);
156   fixed_point = n;
157   return *this;
158 }
159 
put_delimiter(char c)160 ps_output &ps_output::put_delimiter(char c)
161 {
162   if (col + 1 > max_line_length) {
163     putc('\n', fp);
164     col = 0;
165   }
166   putc(c, fp);
167   col++;
168   need_space = 0;
169   return *this;
170 }
171 
put_string(const char * s,int n)172 ps_output &ps_output::put_string(const char *s, int n)
173 {
174   int len = 0;
175   for (int i = 0; i < n; i++) {
176     char c = s[i];
177     if (isascii(c) && isprint(c)) {
178       if (c == '(' || c == ')' || c == '\\')
179 	len += 2;
180       else
181 	len += 1;
182     }
183     else
184       len += 4;
185   }
186   if (len > n*2) {
187     if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
188       putc('\n', fp);
189       col = 0;
190     }
191     if (col + 1 > max_line_length) {
192       putc('\n', fp);
193       col = 0;
194     }
195     putc('<', fp);
196     col++;
197     for (i = 0; i < n; i++) {
198       if (col + 2 > max_line_length) {
199 	putc('\n', fp);
200 	col = 0;
201       }
202       fprintf(fp, "%02x", s[i] & 0377);
203       col += 2;
204     }
205     putc('>', fp);
206     col++;
207   }
208   else {
209     if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
210       putc('\n', fp);
211       col = 0;
212     }
213     if (col + 2 > max_line_length) {
214       putc('\n', fp);
215       col = 0;
216     }
217     putc('(', fp);
218     col++;
219     for (i = 0; i < n; i++) {
220       char c = s[i];
221       if (isascii(c) && isprint(c)) {
222 	if (c == '(' || c == ')' || c == '\\')
223 	  len = 2;
224 	else
225 	  len = 1;
226       }
227       else
228 	len = 4;
229       if (col + len + 1 > max_line_length) {
230 	putc('\\', fp);
231 	putc('\n', fp);
232 	col = 0;
233       }
234       switch (len) {
235       case 1:
236 	putc(c, fp);
237 	break;
238       case 2:
239 	putc('\\', fp);
240 	putc(c, fp);
241 	break;
242       case 4:
243 	fprintf(fp, "\\%03o", c & 0377);
244 	break;
245       default:
246 	assert(0);
247       }
248       col += len;
249     }
250     putc(')', fp);
251     col++;
252   }
253   need_space = 0;
254   return *this;
255 }
256 
put_number(int n)257 ps_output &ps_output::put_number(int n)
258 {
259   char buf[1 + INT_DIGITS + 1];
260   sprintf(buf, "%d", n);
261   int len = strlen(buf);
262   if (col > 0 && col + len + need_space > max_line_length) {
263     putc('\n', fp);
264     col = 0;
265     need_space = 0;
266   }
267   if (need_space) {
268     putc(' ', fp);
269     col++;
270   }
271   fputs(buf, fp);
272   col += len;
273   need_space = 1;
274   return *this;
275 }
276 
put_fix_number(int i)277 ps_output &ps_output::put_fix_number(int i)
278 {
279   const char *p = iftoa(i, fixed_point);
280   int len = strlen(p);
281   if (col > 0 && col + len + need_space > max_line_length) {
282     putc('\n', fp);
283     col = 0;
284     need_space = 0;
285   }
286   if (need_space) {
287     putc(' ', fp);
288     col++;
289   }
290   fputs(p, fp);
291   col += len;
292   need_space = 1;
293   return *this;
294 }
295 
put_float(double d)296 ps_output &ps_output::put_float(double d)
297 {
298   char buf[128];
299   sprintf(buf, "%.4f", d);
300   int len = strlen(buf);
301   if (col > 0 && col + len + need_space > max_line_length) {
302     putc('\n', fp);
303     col = 0;
304     need_space = 0;
305   }
306   if (need_space) {
307     putc(' ', fp);
308     col++;
309   }
310   fputs(buf, fp);
311   col += len;
312   need_space = 1;
313   return *this;
314 }
315 
put_symbol(const char * s)316 ps_output &ps_output::put_symbol(const char *s)
317 {
318   int len = strlen(s);
319   if (col > 0 && col + len + need_space > max_line_length) {
320     putc('\n', fp);
321     col = 0;
322     need_space = 0;
323   }
324   if (need_space) {
325     putc(' ', fp);
326     col++;
327   }
328   fputs(s, fp);
329   col += len;
330   need_space = 1;
331   return *this;
332 }
333 
put_literal_symbol(const char * s)334 ps_output &ps_output::put_literal_symbol(const char *s)
335 {
336   int len = strlen(s);
337   if (col > 0 && col + len + 1 > max_line_length) {
338     putc('\n', fp);
339     col = 0;
340   }
341   putc('/', fp);
342   fputs(s, fp);
343   col += len + 1;
344   need_space = 1;
345   return *this;
346 }
347 
348 class ps_font : public font {
349   ps_font(const char *);
350 public:
351   int encoding_index;
352   char *encoding;
353   char *reencoded_name;
354   ~ps_font();
355   void handle_unknown_font_command(const char *command, const char *arg,
356 				   const char *filename, int lineno);
357   static ps_font *load_ps_font(const char *);
358 };
359 
load_ps_font(const char * s)360 ps_font *ps_font::load_ps_font(const char *s)
361 {
362   ps_font *f = new ps_font(s);
363   if (!f->load()) {
364     delete f;
365     return 0;
366   }
367   return f;
368 }
369 
ps_font(const char * nm)370 ps_font::ps_font(const char *nm)
371 : font(nm), encoding(0), reencoded_name(0), encoding_index(-1)
372 {
373 }
374 
~ps_font()375 ps_font::~ps_font()
376 {
377   a_delete encoding;
378   a_delete reencoded_name;
379 }
380 
handle_unknown_font_command(const char * command,const char * arg,const char * filename,int lineno)381 void ps_font::handle_unknown_font_command(const char *command, const char *arg,
382 					  const char *filename, int lineno)
383 {
384   if (strcmp(command, "encoding") == 0) {
385     if (arg == 0)
386       error_with_file_and_line(filename, lineno,
387 			       "`encoding' command requires an argument");
388     else
389       encoding = strsave(arg);
390   }
391 }
392 
handle_unknown_desc_command(const char * command,const char * arg,const char * filename,int lineno)393 static void handle_unknown_desc_command(const char *command, const char *arg,
394 					const char *filename, int lineno)
395 {
396   if (strcmp(command, "broken") == 0) {
397     if (arg == 0)
398       error_with_file_and_line(filename, lineno,
399 			       "`broken' command requires an argument");
400     else if (!bflag)
401       broken_flags = atoi(arg);
402   }
403 }
404 
405 struct style {
406   font *f;
407   int point_size;
408   int height;
409   int slant;
410   style();
411   style(font *, int, int, int);
412   int operator==(const style &) const;
413   int operator!=(const style &) const;
414 };
415 
style()416 style::style() : f(0)
417 {
418 }
419 
style(font * p,int sz,int h,int sl)420 style::style(font *p, int sz, int h, int sl)
421 : f(p), point_size(sz), height(h), slant(sl)
422 {
423 }
424 
operator ==(const style & s) const425 int style::operator==(const style &s) const
426 {
427   return (f == s.f && point_size == s.point_size
428 	  && height == s.height && slant == s.slant);
429 }
430 
operator !=(const style & s) const431 int style::operator!=(const style &s) const
432 {
433   return !(*this == s);
434 }
435 
436 class ps_printer : public printer {
437   FILE *tempfp;
438   ps_output out;
439   int res;
440   int space_char_index;
441   int pages_output;
442   int paper_length;
443   int equalise_spaces;
444   enum { SBUF_SIZE = 256 };
445   char sbuf[SBUF_SIZE];
446   int sbuf_len;
447   int sbuf_start_hpos;
448   int sbuf_vpos;
449   int sbuf_end_hpos;
450   int sbuf_space_width;
451   int sbuf_space_count;
452   int sbuf_space_diff_count;
453   int sbuf_space_code;
454   int sbuf_kern;
455   style sbuf_style;
456   style output_style;
457   int output_hpos;
458   int output_vpos;
459   int output_draw_point_size;
460   int line_thickness;
461   int output_line_thickness;
462   int fill;
463   unsigned char output_space_code;
464   enum { MAX_DEFINED_STYLES = 50 };
465   style defined_styles[MAX_DEFINED_STYLES];
466   int ndefined_styles;
467   int next_encoding_index;
468   string defs;
469   int ndefs;
470   resource_manager rm;
471   int invis_count;
472 
473   void flush_sbuf();
474   void set_style(const style &);
475   void set_space_code(unsigned char c);
476   int set_encoding_index(ps_font *);
477   void do_exec(char *, const environment *);
478   void do_import(char *, const environment *);
479   void do_def(char *, const environment *);
480   void do_mdef(char *, const environment *);
481   void do_file(char *, const environment *);
482   void do_invis(char *, const environment *);
483   void do_endinvis(char *, const environment *);
484   void set_line_thickness(const environment *);
485   void fill_path();
486   void encode_fonts();
487   void define_encoding(const char *, int);
488   void reencode_font(ps_font *);
489 public:
490   ps_printer();
491   ~ps_printer();
492   void set_char(int i, font *f, const environment *env, int w);
493   void draw(int code, int *p, int np, const environment *env);
494   void begin_page(int);
495   void end_page(int);
496   void special(char *arg, const environment *env);
497   font *make_font(const char *);
498   void end_of_line();
499 };
500 
ps_printer()501 ps_printer::ps_printer()
502 : pages_output(0),
503   sbuf_len(0),
504   output_hpos(-1),
505   output_vpos(-1),
506   out(0, 79),
507   ndefined_styles(0),
508   next_encoding_index(0),
509   line_thickness(-1),
510   fill(FILL_MAX + 1),
511   ndefs(0),
512   invis_count(0)
513 {
514   tempfp = xtmpfile();
515   out.set_file(tempfp);
516   if (linewidth < 0)
517     linewidth = DEFAULT_LINEWIDTH;
518   if (font::hor != 1)
519     fatal("horizontal resolution must be 1");
520   if (font::vert != 1)
521     fatal("vertical resolution must be 1");
522   if (font::res % (font::sizescale*72) != 0)
523     fatal("res must be a multiple of 72*sizescale");
524   int r = font::res;
525   int point = 0;
526   while (r % 10 == 0) {
527     r /= 10;
528     point++;
529   }
530   res = r;
531   out.set_fixed_point(point);
532   space_char_index = font::name_to_index("space");
533   paper_length = font::paperlength;
534   if (paper_length == 0)
535     paper_length = 11*font::res;
536   equalise_spaces = font::res >= 72000;
537 }
538 
set_encoding_index(ps_font * f)539 int ps_printer::set_encoding_index(ps_font *f)
540 {
541   if (f->encoding_index >= 0)
542     return f->encoding_index;
543   for (font_pointer_list *p = font_list; p; p = p->next)
544     if (p->p != f) {
545       char *encoding = ((ps_font *)p->p)->encoding;
546       int encoding_index = ((ps_font *)p->p)->encoding_index;
547       if (encoding != 0 && encoding_index >= 0
548 	  && strcmp(f->encoding, encoding) == 0) {
549 	return f->encoding_index = encoding_index;
550       }
551     }
552   return f->encoding_index = next_encoding_index++;
553 }
554 
set_char(int i,font * f,const environment * env,int w)555 void ps_printer::set_char(int i, font *f, const environment *env, int w)
556 {
557   if (i == space_char_index || invis_count > 0)
558     return;
559   unsigned char code = f->get_code(i);
560   style sty(f, env->size, env->height, env->slant);
561   if (sty.slant != 0) {
562     if (sty.slant > 80 || sty.slant < -80) {
563       error("silly slant `%1' degrees", sty.slant);
564       sty.slant = 0;
565     }
566   }
567   if (sbuf_len > 0) {
568     if (sbuf_len < SBUF_SIZE
569 	&& sty == sbuf_style
570 	&& sbuf_vpos == env->vpos) {
571       if (sbuf_end_hpos == env->hpos) {
572 	sbuf[sbuf_len++] = code;
573 	sbuf_end_hpos += w + sbuf_kern;
574 	return;
575       }
576       if (sbuf_len == 1 && sbuf_kern == 0) {
577 	sbuf_kern = env->hpos - sbuf_end_hpos;
578 	sbuf_end_hpos = env->hpos + sbuf_kern + w;
579 	sbuf[sbuf_len++] = code;
580 	return;
581       }
582       /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
583 	 starting a new string. */
584       if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
585 	  && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
586 	if (sbuf_space_code < 0) {
587 	  if (f->contains(space_char_index)) {
588 	    sbuf_space_code = f->get_code(space_char_index);
589 	    sbuf_space_width = env->hpos - sbuf_end_hpos;
590 	    sbuf_end_hpos = env->hpos + w + sbuf_kern;
591 	    sbuf[sbuf_len++] = sbuf_space_code;
592 	    sbuf[sbuf_len++] = code;
593 	    sbuf_space_count++;
594 	    return;
595 	  }
596 	}
597 	else {
598 	  int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
599 	  if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
600 	    sbuf_end_hpos = env->hpos + w + sbuf_kern;
601 	    sbuf[sbuf_len++] = sbuf_space_code;
602 	    sbuf[sbuf_len++] = code;
603 	    sbuf_space_count++;
604 	    if (diff == 1)
605 	      sbuf_space_diff_count++;
606 	    else if (diff == -1)
607 	      sbuf_space_diff_count--;
608 	    return;
609 	  }
610 	}
611       }
612     }
613     flush_sbuf();
614   }
615   sbuf_len = 1;
616   sbuf[0] = code;
617   sbuf_end_hpos = env->hpos + w;
618   sbuf_start_hpos = env->hpos;
619   sbuf_vpos = env->vpos;
620   sbuf_style = sty;
621   sbuf_space_code = -1;
622   sbuf_space_width = 0;
623   sbuf_space_count = sbuf_space_diff_count = 0;
624   sbuf_kern = 0;
625 }
626 
is_small_h(int n)627 int is_small_h(int n)
628 {
629   return n < (font::res*2)/72 && n > -(font::res*10)/72;
630 }
631 
is_small_v(int n)632 int is_small_v(int n)
633 {
634   return n < (font::res*4)/72 && n > -(font::res*4)/72;
635 }
636 
make_encoding_name(int encoding_index)637 static char *make_encoding_name(int encoding_index)
638 {
639   static char buf[3 + INT_DIGITS + 1];
640   sprintf(buf, "ENC%d", encoding_index);
641   return buf;
642 }
643 
644 const char *const WS = " \t\n\r";
645 
define_encoding(const char * encoding,int encoding_index)646 void ps_printer::define_encoding(const char *encoding, int encoding_index)
647 {
648   char *vec[256];
649   for (int i = 0; i < 256; i++)
650     vec[i] = 0;
651   char *path;
652   FILE *fp = font::open_file(encoding, &path);
653   if (fp == 0)
654     fatal("can't open encoding file `%1'", encoding);
655   int lineno = 1;
656   char buf[256];
657   while (fgets(buf, 512, fp) != 0) {
658     char *p = buf;
659     while (isascii(*p) && isspace(*p))
660       p++;
661     if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
662       char *q = strtok(0, WS);
663       int n;
664       if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
665 	fatal_with_file_and_line(path, lineno, "bad second field");
666       vec[n] = new char[strlen(p) + 1];
667       strcpy(vec[n], p);
668     }
669     lineno++;
670   }
671   a_delete path;
672   out.put_literal_symbol(make_encoding_name(encoding_index));
673   out.put_delimiter('[');
674   for (i = 0; i < 256; i++) {
675     if (vec[i] == 0)
676       out.put_literal_symbol(".notdef");
677     else {
678       out.put_literal_symbol(vec[i]);
679       a_delete vec[i];
680     }
681   }
682   out.put_delimiter(']').put_symbol("def");
683 }
684 
reencode_font(ps_font * f)685 void ps_printer::reencode_font(ps_font *f)
686 {
687   out.put_literal_symbol(f->reencoded_name)
688      .put_symbol(make_encoding_name(f->encoding_index))
689      .put_literal_symbol(f->get_internal_name())
690      .put_symbol("RE");
691 }
692 
encode_fonts()693 void ps_printer::encode_fonts()
694 {
695   if (next_encoding_index == 0)
696     return;
697   char *done_encoding = new char[next_encoding_index];
698   for (int i = 0; i < next_encoding_index; i++)
699     done_encoding[i] = 0;
700   for (font_pointer_list *f = font_list; f; f = f->next) {
701     int encoding_index = ((ps_font *)f->p)->encoding_index;
702     if (encoding_index >= 0) {
703       assert(encoding_index < next_encoding_index);
704       if (!done_encoding[encoding_index]) {
705 	done_encoding[encoding_index] = 1;
706 	define_encoding(((ps_font *)f->p)->encoding, encoding_index);
707       }
708       reencode_font((ps_font *)f->p);
709     }
710   }
711   a_delete done_encoding;
712 }
713 
set_style(const style & sty)714 void ps_printer::set_style(const style &sty)
715 {
716   char buf[1 + INT_DIGITS + 1];
717   for (int i = 0; i < ndefined_styles; i++)
718     if (sty == defined_styles[i]) {
719       sprintf(buf, "F%d", i);
720       out.put_symbol(buf);
721       return;
722     }
723   if (ndefined_styles >= MAX_DEFINED_STYLES)
724     ndefined_styles = 0;
725   sprintf(buf, "F%d", ndefined_styles);
726   out.put_literal_symbol(buf);
727   const char *psname = sty.f->get_internal_name();
728   if (psname == 0)
729     fatal("no internalname specified for font `%1'", sty.f->get_name());
730   char *encoding = ((ps_font *)sty.f)->encoding;
731   if (encoding != 0) {
732     char *s = ((ps_font *)sty.f)->reencoded_name;
733     if (s == 0) {
734       int ei = set_encoding_index((ps_font *)sty.f);
735       char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
736       sprintf(tem, "%s@%d", psname, ei);
737       psname = tem;
738       ((ps_font *)sty.f)->reencoded_name = tem;
739     }
740     else
741       psname = s;
742   }
743   out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
744   if (sty.height != 0 || sty.slant != 0) {
745     int h = sty.height == 0 ? sty.point_size : sty.height;
746     h *= font::res/(72*font::sizescale);
747     int c = int(h*tan(radians(sty.slant)) + .5);
748     out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname)
749        .put_symbol("MF");
750   }
751   else {
752     out.put_literal_symbol(psname).put_symbol("SF");
753   }
754   defined_styles[ndefined_styles++] = sty;
755 }
756 
set_space_code(unsigned char c)757 void ps_printer::set_space_code(unsigned char c)
758 {
759   out.put_literal_symbol("SC").put_number(c).put_symbol("def");
760 }
761 
end_of_line()762 void ps_printer::end_of_line()
763 {
764   flush_sbuf();
765   // this ensures that we do an absolute motion to the beginning of a line
766   output_vpos = output_hpos = -1;
767 }
768 
flush_sbuf()769 void ps_printer::flush_sbuf()
770 {
771   enum {
772     NONE,
773     RELATIVE_H,
774     RELATIVE_V,
775     RELATIVE_HV,
776     ABSOLUTE
777     } motion = NONE;
778   int space_flag = 0;
779   if (sbuf_len == 0)
780     return;
781   if (output_style != sbuf_style) {
782     set_style(sbuf_style);
783     output_style = sbuf_style;
784   }
785   int extra_space = 0;
786   if (output_hpos < 0 || output_vpos < 0
787       || !is_small_h(output_hpos - sbuf_start_hpos)
788       || !is_small_v(output_vpos - sbuf_vpos))
789     motion = ABSOLUTE;
790   else {
791     if (output_hpos != sbuf_start_hpos)
792       motion = RELATIVE_H;
793     if (output_vpos != sbuf_vpos) {
794       if  (motion != NONE)
795 	motion = RELATIVE_HV;
796       else
797 	motion = RELATIVE_V;
798     }
799   }
800   if (sbuf_space_code >= 0) {
801     int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
802     if (w + sbuf_kern != sbuf_space_width) {
803       if (sbuf_space_code != output_space_code) {
804 	set_space_code(sbuf_space_code);
805 	output_space_code = sbuf_space_code;
806       }
807       space_flag = 1;
808       extra_space = sbuf_space_width - w - sbuf_kern;
809       if (sbuf_space_diff_count > sbuf_space_count/2)
810 	extra_space++;
811       else if (sbuf_space_diff_count < -(sbuf_space_count/2))
812 	extra_space--;
813     }
814   }
815   if (space_flag)
816     out.put_fix_number(extra_space);
817   if (sbuf_kern != 0)
818     out.put_fix_number(sbuf_kern);
819   out.put_string(sbuf, sbuf_len);
820   char sym[2];
821   sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0);
822   sym[1] = '\0';
823   switch (motion) {
824   case NONE:
825     break;
826   case ABSOLUTE:
827     out.put_fix_number(sbuf_start_hpos)
828        .put_fix_number(sbuf_vpos);
829     break;
830   case RELATIVE_H:
831     out.put_fix_number(sbuf_start_hpos - output_hpos);
832     break;
833   case RELATIVE_V:
834     out.put_fix_number(sbuf_vpos - output_vpos);
835     break;
836   case RELATIVE_HV:
837     out.put_fix_number(sbuf_start_hpos - output_hpos)
838        .put_fix_number(sbuf_vpos - output_vpos);
839     break;
840   default:
841     assert(0);
842   }
843   out.put_symbol(sym);
844   output_hpos = sbuf_end_hpos;
845   output_vpos = sbuf_vpos;
846   sbuf_len = 0;
847 }
848 
849 
set_line_thickness(const environment * env)850 void ps_printer::set_line_thickness(const environment *env)
851 {
852   if (line_thickness < 0) {
853     if (output_draw_point_size != env->size) {
854       // we ought to check for overflow here
855       int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
856       out.put_fix_number(lw).put_symbol("LW");
857       output_draw_point_size = env->size;
858       output_line_thickness = -1;
859     }
860   }
861   else {
862     if (output_line_thickness != line_thickness) {
863       out.put_fix_number(line_thickness).put_symbol("LW");
864       output_line_thickness = line_thickness;
865       output_draw_point_size = -1;
866     }
867   }
868 }
869 
fill_path()870 void ps_printer::fill_path()
871 {
872   if (fill > FILL_MAX)
873     out.put_symbol("BL");
874   else
875     out.put_float(transform_fill(fill)).put_symbol("FL");
876 }
877 
draw(int code,int * p,int np,const environment * env)878 void ps_printer::draw(int code, int *p, int np, const environment *env)
879 {
880   if (invis_count > 0)
881     return;
882   int fill_flag = 0;
883   switch (code) {
884   case 'C':
885     fill_flag = 1;
886     // fall through
887   case 'c':
888     // troff adds an extra argument to C
889     if (np != 1 && !(code == 'C' && np == 2)) {
890       error("1 argument required for circle");
891       break;
892     }
893     out.put_fix_number(env->hpos + p[0]/2)
894        .put_fix_number(env->vpos)
895        .put_fix_number(p[0]/2)
896        .put_symbol("DC");
897     if (fill_flag) {
898       fill_path();
899     }
900     else {
901       set_line_thickness(env);
902       out.put_symbol("ST");
903     }
904     break;
905   case 'l':
906     if (np != 2) {
907       error("2 arguments required for line");
908       break;
909     }
910     set_line_thickness(env);
911     out.put_fix_number(p[0] + env->hpos)
912        .put_fix_number(p[1] + env->vpos)
913        .put_fix_number(env->hpos)
914        .put_fix_number(env->vpos)
915        .put_symbol("DL");
916     break;
917   case 'E':
918     fill_flag = 1;
919     // fall through
920   case 'e':
921     if (np != 2) {
922       error("2 arguments required for ellipse");
923       break;
924     }
925     out.put_fix_number(p[0])
926        .put_fix_number(p[1])
927        .put_fix_number(env->hpos + p[0]/2)
928        .put_fix_number(env->vpos)
929        .put_symbol("DE");
930     if (fill_flag) {
931       fill_path();
932     }
933     else {
934       set_line_thickness(env);
935       out.put_symbol("ST");
936     }
937     break;
938   case 'P':
939     fill_flag = 1;
940     // fall through
941   case 'p':
942     {
943       if (np & 1) {
944 	error("even number of arguments required for polygon");
945 	break;
946       }
947       if (np == 0) {
948 	error("no arguments for polygon");
949 	break;
950       }
951       out.put_fix_number(env->hpos)
952 	 .put_fix_number(env->vpos)
953 	 .put_symbol("MT");
954       for (int i = 0; i < np; i += 2)
955 	out.put_fix_number(p[i])
956 	   .put_fix_number(p[i+1])
957 	   .put_symbol("RL");
958       out.put_symbol("CL");
959       if (fill_flag) {
960 	fill_path();
961       }
962       else {
963 	set_line_thickness(env);
964 	out.put_symbol("ST");
965       }
966       break;
967     }
968   case '~':
969     {
970       if (np & 1) {
971 	error("even number of arguments required for spline");
972 	break;
973       }
974       if (np == 0) {
975 	error("no arguments for spline");
976 	break;
977       }
978       out.put_fix_number(env->hpos)
979 	 .put_fix_number(env->vpos)
980 	 .put_symbol("MT");
981       out.put_fix_number(p[0]/2)
982 	 .put_fix_number(p[1]/2)
983 	 .put_symbol("RL");
984       /* tnum/tden should be between 0 and 1; the closer it is to 1
985 	 the tighter the curve will be to the guiding lines; 2/3
986 	 is the standard value */
987       const int tnum = 2;
988       const int tden = 3;
989       for (int i = 0; i < np - 2; i += 2) {
990 	out.put_fix_number((p[i]*tnum)/(2*tden))
991 	   .put_fix_number((p[i + 1]*tnum)/(2*tden))
992 	   .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
993 	   .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
994 	   .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
995 	   .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
996 	   .put_symbol("RC");
997       }
998       out.put_fix_number(p[np - 2] - p[np - 2]/2)
999 	 .put_fix_number(p[np - 1] - p[np - 1]/2)
1000 	 .put_symbol("RL");
1001       set_line_thickness(env);
1002       out.put_symbol("ST");
1003     }
1004     break;
1005   case 'a':
1006     {
1007       if (np != 4) {
1008 	error("4 arguments required for arc");
1009 	break;
1010       }
1011       set_line_thickness(env);
1012       double c[2];
1013       if (adjust_arc_center(p, c))
1014 	out.put_fix_number(env->hpos + int(c[0]))
1015 	   .put_fix_number(env->vpos + int(c[1]))
1016 	   .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
1017 	   .put_float(degrees(atan2(-c[1], -c[0])))
1018 	   .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
1019 	   .put_symbol("DA");
1020       else
1021 	out.put_fix_number(p[0] + p[2] + env->hpos)
1022 	   .put_fix_number(p[1] + p[3] + env->vpos)
1023 	   .put_fix_number(env->hpos)
1024 	   .put_fix_number(env->vpos)
1025 	   .put_symbol("DL");
1026     }
1027     break;
1028   case 't':
1029     {
1030       if (np == 0) {
1031 	line_thickness = -1;
1032       }
1033       else {
1034 	// troff gratuitously adds an extra 0
1035 	if (np != 1 && np != 2) {
1036 	  error("0 or 1 argument required for thickness");
1037 	  break;
1038 	}
1039 	line_thickness = p[0];
1040       }
1041       break;
1042     }
1043   case 'f':
1044     {
1045       if (np != 1 && np != 2) {
1046 	error("1 argument required for fill");
1047 	break;
1048       }
1049       fill = p[0];
1050       if (fill < 0 || fill > FILL_MAX) {
1051 	// This means fill with the current color.
1052 	fill = FILL_MAX + 1;
1053       }
1054       break;
1055     }
1056   default:
1057     error("unrecognised drawing command `%1'", char(code));
1058     break;
1059   }
1060 
1061   output_hpos = output_vpos = -1;
1062 }
1063 
1064 
begin_page(int n)1065 void ps_printer::begin_page(int n)
1066 {
1067   out.begin_comment("Page:").comment_arg(itoa(n));
1068   out.comment_arg(itoa(++pages_output)).end_comment();
1069   output_style.f = 0;
1070   output_space_code = 32;
1071   output_draw_point_size = -1;
1072   output_line_thickness = -1;
1073   output_hpos = output_vpos = -1;
1074   ndefined_styles = 0;
1075   out.simple_comment("BeginPageSetup");
1076   out.put_symbol("BP");
1077   out.simple_comment("EndPageSetup");
1078 }
1079 
end_page(int)1080 void ps_printer::end_page(int)
1081 {
1082   flush_sbuf();
1083   out.put_symbol("EP");
1084   if (invis_count != 0) {
1085     error("missing `endinvis' command");
1086     invis_count = 0;
1087   }
1088 }
1089 
make_font(const char * nm)1090 font *ps_printer::make_font(const char *nm)
1091 {
1092   return ps_font::load_ps_font(nm);
1093 }
1094 
~ps_printer()1095 ps_printer::~ps_printer()
1096 {
1097   out.simple_comment("Trailer");
1098   out.put_symbol("end");
1099   out.simple_comment("EOF");
1100   if (fseek(tempfp, 0L, 0) < 0)
1101     fatal("fseek on temporary file failed");
1102   fputs("%!PS-Adobe-", stdout);
1103   fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
1104   putchar('\n');
1105   out.set_file(stdout);
1106   {
1107     extern const char *version_string;
1108     out.begin_comment("Creator:")
1109        .comment_arg("groff")
1110        .comment_arg("version")
1111        .comment_arg(version_string)
1112        .end_comment();
1113   }
1114   for (font_pointer_list *f = font_list; f; f = f->next) {
1115     ps_font *psf = (ps_font *)(f->p);
1116     rm.need_font(psf->get_internal_name());
1117   }
1118   rm.print_header_comments(out);
1119   out.begin_comment("Pages:").comment_arg(itoa(pages_output)).end_comment();
1120   out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment();
1121 #if 0
1122   fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n",
1123 	  font::paperwidth*72.0/font::res,
1124 	  paper_length*72.0/font::res);
1125 #endif
1126   out.begin_comment("Orientation:")
1127      .comment_arg(landscape_flag ? "Landscape" : "Portrait")
1128      .end_comment();
1129   if (ncopies != 1) {
1130     out.end_line();
1131     fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
1132   }
1133   out.simple_comment("EndComments");
1134   out.simple_comment("BeginProlog");
1135   rm.output_prolog(out);
1136   if (!(broken_flags & NO_SETUP_SECTION)) {
1137     out.simple_comment("EndProlog");
1138     out.simple_comment("BeginSetup");
1139   }
1140   rm.document_setup(out);
1141   out.put_symbol(dict_name).put_symbol("begin");
1142   if (ndefs > 0)
1143     ndefs += DEFS_DICT_SPARE;
1144   out.put_literal_symbol(defs_dict_name)
1145      .put_number(ndefs + 1)
1146      .put_symbol("dict")
1147      .put_symbol("def");
1148   out.put_symbol(defs_dict_name)
1149      .put_symbol("begin");
1150   out.put_literal_symbol("u")
1151      .put_delimiter('{')
1152      .put_fix_number(1)
1153      .put_symbol("mul")
1154      .put_delimiter('}')
1155      .put_symbol("bind")
1156      .put_symbol("def");
1157   defs += '\0';
1158   out.special(defs.contents());
1159   out.put_symbol("end");
1160   if (ncopies != 1)
1161     out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def");
1162   out.put_literal_symbol("RES").put_number(res).put_symbol("def");
1163   out.put_literal_symbol("PL");
1164   if (guess_flag)
1165     out.put_symbol("PLG");
1166   else
1167     out.put_fix_number(paper_length);
1168   out.put_symbol("def");
1169   out.put_literal_symbol("LS")
1170      .put_symbol(landscape_flag ? "true" : "false")
1171      .put_symbol("def");
1172   encode_fonts();
1173   out.simple_comment((broken_flags & NO_SETUP_SECTION)
1174 		     ? "EndProlog"
1175 		     : "EndSetup");
1176   out.end_line();
1177   out.copy_file(tempfp);
1178   fclose(tempfp);
1179 }
1180 
special(char * arg,const environment * env)1181 void ps_printer::special(char *arg, const environment *env)
1182 {
1183   typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1184   static struct {
1185     const char *name;
1186     SPECIAL_PROCP proc;
1187   } proc_table[] = {
1188     "exec", &ps_printer::do_exec,
1189     "def", &ps_printer::do_def,
1190     "mdef", &ps_printer::do_mdef,
1191     "import", &ps_printer::do_import,
1192     "file", &ps_printer::do_file,
1193     "invis", &ps_printer::do_invis,
1194     "endinvis", &ps_printer::do_endinvis,
1195   };
1196   for (char *p = arg; *p == ' ' || *p == '\n'; p++)
1197     ;
1198   char *tag = p;
1199   for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1200     ;
1201   if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1202     error("X command without `ps:' tag ignored");
1203     return;
1204   }
1205   p++;
1206   for (; *p == ' ' || *p == '\n'; p++)
1207     ;
1208   char *command = p;
1209   for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1210     ;
1211   if (*command == '\0') {
1212     error("X command without `ps:' tag ignored");
1213     return;
1214   }
1215   for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
1216     if (strncmp(command, proc_table[i].name, p - command) == 0) {
1217       (this->*(proc_table[i].proc))(p, env);
1218       return;
1219     }
1220   error("X command `%1' not recognised", command);
1221 }
1222 
1223 // A conforming PostScript document must not have lines longer
1224 // than 255 characters (excluding line termination characters).
1225 
check_line_lengths(const char * p)1226 static int check_line_lengths(const char *p)
1227 {
1228   for (;;) {
1229     const char *end = strchr(p, '\n');
1230     if (end == 0)
1231       end = strchr(p, '\0');
1232     if (end - p > 255)
1233       return 0;
1234     if (*end == '\0')
1235       break;
1236     p = end + 1;
1237   }
1238   return 1;
1239 }
1240 
do_exec(char * arg,const environment * env)1241 void ps_printer::do_exec(char *arg, const environment *env)
1242 {
1243   flush_sbuf();
1244   while (csspace(*arg))
1245     arg++;
1246   if (*arg == '\0') {
1247     error("missing argument to X exec command");
1248     return;
1249   }
1250   if (!check_line_lengths(arg)) {
1251     error("lines in X exec command must not be more than 255 characters long");
1252     return;
1253   }
1254   out.put_fix_number(env->hpos)
1255      .put_fix_number(env->vpos)
1256      .put_symbol("EBEGIN")
1257      .special(arg)
1258      .put_symbol("EEND");
1259   output_hpos = output_vpos = -1;
1260   output_style.f = 0;
1261   output_draw_point_size = -1;
1262   output_line_thickness = -1;
1263   ndefined_styles = 0;
1264   if (!ndefs)
1265     ndefs = 1;
1266 }
1267 
do_file(char * arg,const environment * env)1268 void ps_printer::do_file(char *arg, const environment *env)
1269 {
1270   flush_sbuf();
1271   while (csspace(*arg))
1272     arg++;
1273   if (*arg == '\0') {
1274     error("missing argument to X file command");
1275     return;
1276   }
1277   const char *filename = arg;
1278   do {
1279     ++arg;
1280   } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1281   out.put_fix_number(env->hpos)
1282      .put_fix_number(env->vpos)
1283      .put_symbol("EBEGIN");
1284   rm.import_file(filename, out);
1285   out.put_symbol("EEND");
1286   output_hpos = output_vpos = -1;
1287   output_style.f = 0;
1288   output_draw_point_size = -1;
1289   output_line_thickness = -1;
1290   ndefined_styles = 0;
1291   if (!ndefs)
1292     ndefs = 1;
1293 }
1294 
do_def(char * arg,const environment *)1295 void ps_printer::do_def(char *arg, const environment *)
1296 {
1297   flush_sbuf();
1298   while (csspace(*arg))
1299     arg++;
1300   if (!check_line_lengths(arg)) {
1301     error("lines in X def command must not be more than 255 characters long");
1302     return;
1303   }
1304   defs += arg;
1305   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1306     defs += '\n';
1307   ndefs++;
1308 }
1309 
1310 // Like def, but the first argument says how many definitions it contains.
1311 
do_mdef(char * arg,const environment *)1312 void ps_printer::do_mdef(char *arg, const environment *)
1313 {
1314   flush_sbuf();
1315   char *p;
1316   int n = (int)strtol(arg, &p, 10);
1317   if (n == 0 && p == arg) {
1318     error("first argument to X mdef must be an integer");
1319     return;
1320   }
1321   if (n < 0) {
1322     error("out of range argument `%1' to X mdef command", int(n));
1323     return;
1324   }
1325   arg = p;
1326   while (csspace(*arg))
1327     arg++;
1328   if (!check_line_lengths(arg)) {
1329     error("lines in X mdef command must not be more than 255 characters long");
1330     return;
1331   }
1332   defs += arg;
1333   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1334     defs += '\n';
1335   ndefs += n;
1336 }
1337 
do_import(char * arg,const environment * env)1338 void ps_printer::do_import(char *arg, const environment *env)
1339 {
1340   flush_sbuf();
1341   while (*arg == ' ' || *arg == '\n')
1342     arg++;
1343   for (char *p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1344     ;
1345   if (*p != '\0')
1346     *p++ = '\0';
1347   int parms[6];
1348   int nparms = 0;
1349   while (nparms < 6) {
1350     char *end;
1351     long n = strtol(p, &end, 10);
1352     if (n == 0 && end == p)
1353       break;
1354     parms[nparms++] = int(n);
1355     p = end;
1356   }
1357   if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1358     error("scaling indicators not allowed in arguments for X import command");
1359     return;
1360   }
1361   while (*p == ' ' || *p == '\n')
1362     p++;
1363   if (nparms < 5) {
1364     if (*p == '\0')
1365       error("too few arguments for X import command");
1366     else
1367       error("invalid argument `%1' for X import command", p);
1368     return;
1369   }
1370   if (*p != '\0') {
1371     error("superflous argument `%1' for X import command", p);
1372     return;
1373   }
1374   int llx = parms[0];
1375   int lly = parms[1];
1376   int urx = parms[2];
1377   int ury = parms[3];
1378   int desired_width = parms[4];
1379   int desired_height = parms[5];
1380   if (desired_width <= 0) {
1381     error("bad width argument `%1' for X import command: must be > 0",
1382 	  desired_width);
1383     return;
1384   }
1385   if (nparms == 6 && desired_height <= 0) {
1386     error("bad height argument `%1' for X import command: must be > 0",
1387 	  desired_height);
1388     return;
1389   }
1390   if (llx == urx) {
1391     error("llx and urx arguments for X import command must not be equal");
1392     return;
1393   }
1394   if (lly == ury) {
1395     error("lly and ury arguments for X import command must not be equal");
1396     return;
1397   }
1398   if (nparms == 5) {
1399     int old_wid = urx - llx;
1400     int old_ht = ury - lly;
1401     if (old_wid < 0)
1402       old_wid = -old_wid;
1403     if (old_ht < 0)
1404       old_ht = -old_ht;
1405     desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1406   }
1407   if (env->vpos - desired_height < 0)
1408     warning("top of imported graphic is above the top of the page");
1409   out.put_number(llx)
1410      .put_number(lly)
1411      .put_fix_number(desired_width)
1412      .put_number(urx - llx)
1413      .put_fix_number(-desired_height)
1414      .put_number(ury - lly)
1415      .put_fix_number(env->hpos)
1416      .put_fix_number(env->vpos)
1417      .put_symbol("PBEGIN");
1418   rm.import_file(arg, out);
1419   // do this here just in case application defines PEND
1420   out.put_symbol("end");
1421   out.put_symbol("PEND");
1422 }
1423 
do_invis(char *,const environment *)1424 void ps_printer::do_invis(char *, const environment *)
1425 {
1426   invis_count++;
1427 }
1428 
do_endinvis(char *,const environment *)1429 void ps_printer::do_endinvis(char *, const environment *)
1430 {
1431   if (invis_count == 0)
1432     error("unbalanced `endinvis' command");
1433   else
1434     --invis_count;
1435 }
1436 
make_printer()1437 printer *make_printer()
1438 {
1439   return new ps_printer;
1440 }
1441 
1442 static void usage();
1443 
main(int argc,char ** argv)1444 int main(int argc, char **argv)
1445 {
1446   program_name = argv[0];
1447   static char stderr_buf[BUFSIZ];
1448   setbuf(stderr, stderr_buf);
1449   int c;
1450   while ((c = getopt(argc, argv, "F:glc:w:vb:")) != EOF)
1451     switch(c) {
1452     case 'v':
1453       {
1454 	extern const char *version_string;
1455 	fprintf(stderr, "grops version %s\n", version_string);
1456 	fflush(stderr);
1457 	break;
1458       }
1459     case 'c':
1460       if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1461 	error("bad number of copies `%s'", optarg);
1462 	ncopies = 1;
1463       }
1464       break;
1465     case 'g':
1466       guess_flag = 1;
1467       break;
1468     case 'l':
1469       landscape_flag = 1;
1470       break;
1471     case 'F':
1472       font::command_line_font_dir(optarg);
1473       break;
1474     case 'w':
1475       if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1476 	error("bad linewidth `%s'", optarg);
1477 	linewidth = -1;
1478       }
1479       break;
1480     case 'b':
1481       // XXX check this
1482       broken_flags = atoi(optarg);
1483       bflag = 1;
1484       break;
1485     case '?':
1486       usage();
1487       break;
1488     default:
1489       assert(0);
1490     }
1491   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
1492   if (optind >= argc)
1493     do_file("-");
1494   else {
1495     for (int i = optind; i < argc; i++)
1496       do_file(argv[i]);
1497   }
1498   delete pr;
1499   exit(0);
1500 }
1501 
usage()1502 static void usage()
1503 {
1504   fprintf(stderr, "usage: %s [-glv] [-b n] [-c n] [-w n] [-F dir] [files ...]\n",
1505 	  program_name);
1506   exit(1);
1507 }
1508