1 /*
2  * Help utilities.
3  * Copyright (c) 1995-1999 Markku Rossi.
4  *
5  * Author: Markku Rossi <mtr@iki.fi>
6  */
7 
8 /*
9  * This file is part of GNU Enscript.
10  *
11  * Enscript is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * Enscript is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Enscript.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #include "gsint.h"
26 
27 /*
28  * Types and definitions.
29  */
30 
31 #define CFG_FATAL(body)						\
32   do {								\
33     fprintf (stderr, "%s:%s:%d: ", program, buffer_ptr(&fname), line); \
34     fprintf body;						\
35     fprintf (stderr, "\n");					\
36     fflush (stderr);						\
37     exit (1);							\
38   } while (0)
39 
40 
41 /*
42  * Static variables.
43  */
44 
45 /*
46  * 7bit ASCII fi(nland), se (sweden) scand encodings (additions to 7bit ASCII
47  * enc).
48  */
49 static struct
50 {
51   int code;
52   char *name;
53 } enc_7bit_ascii_fise[] =
54 {
55   {'{',		"adieresis"},
56   {'|',		"odieresis"},
57   {'}',		"aring"},
58   {'[',		"Adieresis"},
59   {'\\',	"Odieresis"},
60   {']',		"Aring"},
61   {0, NULL},
62 };
63 
64 /*
65  * 7bit ASCII dk (denmark), no(rway) scand encodings (additions to 7bit ASCII
66  * enc).
67  */
68 static struct
69 {
70   int code;
71   char *name;
72 } enc_7bit_ascii_dkno[] =
73 {
74   {'{',		"ae"},
75   {'|',		"oslash"},
76   {'}',		"aring"},
77   {'[',		"AE"},
78   {'\\',	"Oslash"},
79   {']',		"Aring"},
80   {0, NULL},
81 };
82 
83 
84 /*
85  * Global functions.
86  */
87 
88 #define GET_TOKEN(from) (strtok ((from), " \t\n"))
89 #define GET_LINE_TOKEN(from) (strtok ((from), "\n"))
90 
91 #define CHECK_TOKEN() 							\
92   if (token2 == NULL) 							\
93     CFG_FATAL ((stderr, _("missing argument: %s"), token));
94 
95 int
read_config(char * path,char * file)96 read_config (char *path, char *file)
97 {
98   FILE *fp;
99   Buffer fname;
100   char buf[4096];
101   char *token, *token2;
102   int line = 0;
103 
104   buffer_init (&fname);
105   buffer_append (&fname, path);
106   buffer_append (&fname, "/");
107   buffer_append (&fname, file);
108 
109   fp = fopen (buffer_ptr (&fname), "r");
110 
111   /* We wait to uninit the buffer so that CFG_FATAL can use it. */
112 
113   if (fp == NULL)
114     {
115       buffer_uninit (&fname);
116       return 0;
117     }
118 
119   while (fgets (buf, sizeof (buf), fp))
120     {
121       line++;
122 
123       if (buf[0] == '#')
124 	continue;
125 
126       token = GET_TOKEN (buf);
127       if (token == NULL)
128 	/* Empty line. */
129 	continue;
130 
131       if (MATCH (token, "AcceptCompositeCharacters:"))
132 	{
133 	  token2 = GET_TOKEN (NULL);
134 	  CHECK_TOKEN ();
135 	  accept_composites = atoi (token2);
136 	}
137       else if (MATCH (token, "AFMPath:"))
138 	{
139 	  token2 = GET_TOKEN (NULL);
140 	  CHECK_TOKEN ();
141 	  xfree (afm_path);
142 	  afm_path = xstrdup (token2);
143 	}
144       else if (MATCH (token, "AppendCtrlD:"))
145 	{
146 	  token2 = GET_TOKEN (NULL);
147 	  CHECK_TOKEN ();
148 	  append_ctrl_D = atoi (token2);
149 	}
150       else if (MATCH (token, "Clean7Bit:"))
151 	{
152 	  token2 = GET_TOKEN (NULL);
153 	  CHECK_TOKEN ();
154 	  clean_7bit = atoi (token2);
155 	}
156       else if (MATCH (token, "DefaultEncoding:"))
157 	{
158 	  token2 = GET_TOKEN (NULL);
159 	  CHECK_TOKEN ();
160 	  xfree (encoding_name);
161 	  encoding_name = xstrdup (token2);
162 	}
163       else if (MATCH (token, "DefaultFancyHeader:"))
164 	{
165 	  token2 = GET_TOKEN (NULL);
166 	  CHECK_TOKEN ();
167 	  xfree (fancy_header_default);
168 	  fancy_header_default = xstrdup (token2);
169 	}
170       else if (MATCH (token, "DefaultMedia:"))
171 	{
172 	  token2 = GET_TOKEN (NULL);
173 	  CHECK_TOKEN ();
174 	  xfree (media_name);
175 	  media_name = xstrdup (token2);
176 	}
177       else if (MATCH (token, "DefaultOutputMethod:"))
178 	{
179 	  token2 = GET_TOKEN (NULL);
180 	  CHECK_TOKEN ();
181 	  if (MATCH (token2, "printer"))
182 	    output_file = OUTPUT_FILE_NONE;
183 	  else if (MATCH (token2, "stdout"))
184 	    output_file = OUTPUT_FILE_STDOUT;
185 	  else
186 	    CFG_FATAL ((stderr, _("illegal value \"%s\" for option %s"),
187 			token2, token));
188 	}
189       else if (MATCH (token, "DownloadFont:"))
190 	{
191 	  token2 = GET_TOKEN (NULL);
192 	  CHECK_TOKEN ();
193 	  strhash_put (download_fonts, token2, strlen (token2) + 1, NULL,
194 		       NULL);
195 	}
196       else if (MATCH (token, "EscapeChar:"))
197 	{
198 	  token2 = GET_TOKEN (NULL);
199 	  CHECK_TOKEN ();
200 	  escape_char = atoi (token2);
201 	  if (escape_char < 0 || escape_char > 255)
202 	    CFG_FATAL ((stderr, _("invalid value \"%s\" for option %s"),
203 			token2, token));
204 	}
205       else if (MATCH (token, "FormFeedType:"))
206 	{
207 	  token2 = GET_TOKEN (NULL);
208 	  CHECK_TOKEN ();
209 	  if (MATCH (token2, "column"))
210 	    formfeed_type = FORMFEED_COLUMN;
211 	  else if (MATCH (token2, "page"))
212 	    formfeed_type = FORMFEED_PAGE;
213 	  else
214 	    CFG_FATAL ((stderr, _("illegal value \"%s\" for option %s"),
215 			token2, token));
216 	}
217       else if (MATCH (token, "GeneratePageSize:"))
218 	{
219 	  token2 = GET_TOKEN (NULL);
220 	  CHECK_TOKEN ();
221 	  generate_PageSize = atoi (token2);
222 	}
223       else if (MATCH (token, "HighlightBarGray:"))
224 	{
225 	  token2 = GET_TOKEN (NULL);
226 	  CHECK_TOKEN ();
227 	  highlight_bar_gray = atof (token2);
228 	}
229       else if (MATCH (token, "HighlightBars:"))
230 	{
231 	  token2 = GET_TOKEN (NULL);
232 	  CHECK_TOKEN ();
233 	  highlight_bars = atoi (token2);
234 	}
235       else if (MATCH (token, "LibraryPath:"))
236 	{
237 	  token2 = GET_TOKEN (NULL);
238 	  CHECK_TOKEN ();
239 	  xfree (libpath);
240 	  libpath = xstrdup (token2);
241 	}
242       else if (MATCH (token, "MarkWrappedLines:"))
243 	{
244 	  token2 = GET_TOKEN (NULL);
245 	  CHECK_TOKEN ();
246 	  xfree (mark_wrapped_lines_style_name);
247 	  mark_wrapped_lines_style_name = xstrdup (token2);
248 	}
249       else if (MATCH (token, "Media:"))
250 	{
251 	  char *name;
252 	  int w, h, llx, lly, urx, ury;
253 
254 	  token2 = GET_TOKEN (NULL);
255 	  CHECK_TOKEN ();
256 	  name = token2;
257 
258 	  token2 = GET_TOKEN (NULL);
259 	  CHECK_TOKEN ();
260 	  w = atoi (token2);
261 
262 	  token2 = GET_TOKEN (NULL);
263 	  CHECK_TOKEN ();
264 	  h = atoi (token2);
265 
266 	  token2 = GET_TOKEN (NULL);
267 	  CHECK_TOKEN ();
268 	  llx = atoi (token2);
269 
270 	  token2 = GET_TOKEN (NULL);
271 	  CHECK_TOKEN ();
272 	  lly = atoi (token2);
273 
274 	  token2 = GET_TOKEN (NULL);
275 	  CHECK_TOKEN ();
276 	  urx = atoi (token2);
277 
278 	  token2 = GET_TOKEN (NULL);
279 	  CHECK_TOKEN ();
280 	  ury = atoi (token2);
281 
282 	  add_media (name, w, h, llx, lly, urx, ury);
283 	}
284       else if (MATCH (token, "NoJobHeaderSwitch:"))
285 	{
286 	  token2 = GET_LINE_TOKEN (NULL);
287 	  CHECK_TOKEN ();
288 	  xfree (no_job_header_switch);
289 	  no_job_header_switch = xstrdup (token2);
290 	}
291       else if (MATCH (token, "NonPrintableFormat:"))
292 	{
293 	  token2 = GET_TOKEN (NULL);
294 	  CHECK_TOKEN ();
295 	  xfree (npf_name);
296 	  npf_name = xstrdup (token2);
297 	}
298       else if (MATCH (token, "OutputFirstLine:"))
299 	{
300 	  token2 = GET_LINE_TOKEN (NULL);
301 	  CHECK_TOKEN ();
302 	  xfree (output_first_line);
303 	  output_first_line = xstrdup (token2);
304 	}
305       else if (MATCH (token, "PageLabelFormat:"))
306 	{
307 	  token2 = GET_TOKEN (NULL);
308 	  CHECK_TOKEN ();
309 	  xfree (page_label_format);
310 	  page_label_format = xstrdup (token2);
311 	}
312       else if (MATCH (token, "PagePrefeed:"))
313 	{
314 	  token2 = GET_TOKEN (NULL);
315 	  CHECK_TOKEN ();
316 	  page_prefeed = atoi (token2);
317 	}
318       else if (MATCH (token, "PostScriptLevel:"))
319 	{
320 	  token2 = GET_TOKEN (NULL);
321 	  CHECK_TOKEN ();
322 	  pslevel = atoi (token2);
323 	}
324       else if (MATCH (token, "Printer:"))
325 	{
326 	  token2 = GET_TOKEN (NULL);
327 	  CHECK_TOKEN ();
328 	  xfree (printer);
329 	  printer = xstrdup (token2);
330 	}
331       else if (MATCH (token, "QueueParam:"))
332 	{
333 	  token2 = GET_LINE_TOKEN (NULL);
334 	  CHECK_TOKEN ();
335 	  xfree (queue_param);
336 	  queue_param = xstrdup (token2);
337 	}
338       else if (MATCH (token, "SetPageDevice:"))
339 	{
340 	  token2 = GET_LINE_TOKEN (NULL);
341 	  CHECK_TOKEN ();
342 	  parse_key_value_pair (pagedevice, token2);
343 	}
344       else if (MATCH (token, "Spooler:"))
345 	{
346 	  token2 = GET_TOKEN (NULL);
347 	  CHECK_TOKEN ();
348 	  xfree (spooler_command);
349 	  spooler_command = xstrdup (token2);
350 	}
351       else if (MATCH (token, "StatesBinary:"))
352 	{
353 	  token2 = GET_TOKEN (NULL);
354 	  CHECK_TOKEN ();
355 	  xfree (states_binary);
356 	  states_binary = xstrdup (token2);
357 	}
358       else if (MATCH (token, "StatesColor:"))
359 	{
360 	  token2 = GET_TOKEN (NULL);
361 	  CHECK_TOKEN ();
362 	  states_color = atoi (token2);
363 	}
364       else if (MATCH (token, "StatesConfigFile:"))
365 	{
366 	  token2 = GET_LINE_TOKEN (NULL);
367 	  CHECK_TOKEN ();
368 	  xfree (states_config_file);
369 	  states_config_file = xstrdup (token2);
370 	}
371       else if (MATCH (token, "StatesHighlightStyle:"))
372 	{
373 	  token2 = GET_TOKEN (NULL);
374 	  CHECK_TOKEN ();
375 	  xfree (states_highlight_style);
376 	  states_highlight_style = xstrdup (token2);
377 	}
378       else if (MATCH (token, "StatesPath:"))
379 	{
380 	  token2 = GET_LINE_TOKEN (NULL);
381 	  CHECK_TOKEN ();
382 	  xfree (states_path);
383 	  states_path = xstrdup (token2);
384 	}
385       else if (MATCH (token, "StatusDict:"))
386 	{
387 	  token2 = GET_TOKEN (NULL);
388 	  CHECK_TOKEN ();
389 	  parse_key_value_pair (statusdict, token2);
390 	}
391       else if (MATCH (token, "TOCFormat:"))
392 	{
393 	  token2 = GET_LINE_TOKEN (NULL);
394 	  CHECK_TOKEN ();
395 	  toc_fmt_string = xstrdup (token2);
396 	}
397       else if (MATCH (token, "Underlay:"))
398 	{
399 	  token2 = GET_LINE_TOKEN (NULL);
400 	  CHECK_TOKEN ();
401 	  underlay = xmalloc (strlen (token2) + 1);
402 	  strcpy (underlay, token2);
403 	}
404       else if (MATCH (token, "UnderlayAngle:"))
405 	{
406 	  token2 = GET_TOKEN (NULL);
407 	  CHECK_TOKEN ();
408 	  ul_angle = atof (token2);
409 	  ul_angle_p = 1;
410 	}
411       else if (MATCH (token, "UnderlayFont:"))
412 	{
413 	  token2 = GET_TOKEN (NULL);
414 	  CHECK_TOKEN ();
415 	  if (!parse_font_spec (token2, &ul_font, &ul_ptsize, NULL))
416 	    CFG_FATAL ((stderr, _("malformed font spec: %s"), token2));
417 	}
418       else if (MATCH (token, "UnderlayGray:"))
419 	{
420 	  token2 = GET_TOKEN (NULL);
421 	  CHECK_TOKEN ();
422 	  ul_gray = atof (token2);
423 	}
424       else if (MATCH (token, "UnderlayPosition:"))
425 	{
426 	  token2 = GET_TOKEN (NULL);
427 	  CHECK_TOKEN ();
428 	  xfree (ul_position);
429 	  ul_position = xstrdup (token2);
430 	  ul_position_p = 1;
431 	}
432       else if (MATCH (token, "UnderlayStyle:"))
433 	{
434 	  token2 = GET_TOKEN (NULL);
435 	  CHECK_TOKEN ();
436 	  xfree (ul_style_str);
437 	  ul_style_str = xstrdup (token2);
438 	}
439       else
440 	CFG_FATAL ((stderr, _("illegal option: %s"), token));
441     }
442 
443   buffer_uninit (&fname);
444   return 1;
445 }
446 
447 
448 void
add_media(char * name,int w,int h,int llx,int lly,int urx,int ury)449 add_media (char *name, int w, int h, int llx, int lly, int urx, int ury)
450 {
451   MediaEntry *entry;
452 
453   MESSAGE (2,
454 	   (stderr,
455 	    "add_media: name=%s, w=%d, h=%d, llx=%d, lly=%d, urx=%d, ury=%d\n",
456 	    name, w, h, llx, lly, urx, ury));
457 
458   entry = xcalloc (1, sizeof (*entry));
459   entry->name = xmalloc (strlen (name) + 1);
460 
461   strcpy (entry->name, name);
462   entry->w = w;
463   entry->h = h;
464   entry->llx = llx;
465   entry->lly = lly;
466   entry->urx = urx;
467   entry->ury = ury;
468 
469   entry->next = media_names;
470   media_names = entry;
471 }
472 
473 
474 void
do_list_missing_characters(int * array)475 do_list_missing_characters (int *array)
476 {
477   int i;
478   int count = 0;
479 
480   for (i = 0; i < 256; i++)
481     if (array[i])
482       {
483 	fprintf (stderr, "%3d ", i);
484 	count++;
485 	if (count % 15 == 0)
486 	  fprintf (stderr, "\n");
487       }
488 
489   if (count % 15 != 0)
490     fprintf (stderr, "\n");
491 }
492 
493 
494 int
file_existsp(char * name,char * suffix)495 file_existsp (char *name, char *suffix)
496 {
497   FileLookupCtx ctx;
498   int result;
499 
500   ctx.name = name;
501   ctx.suffix =  suffix ? suffix : "";
502   ctx.fullname = buffer_alloc ();
503 
504   result = pathwalk (libpath, file_lookup, &ctx);
505 
506   buffer_free (ctx.fullname);
507 
508   return result;
509 }
510 
511 
512 int
paste_file(char * name,char * suffix)513 paste_file (char *name, char *suffix)
514 {
515   char buf[512];
516   char resources[512];
517   FILE *fp;
518   FileLookupCtx ctx;
519   int pending_comment = 0;
520   int line = 0;
521 
522   ctx.name = name;
523   ctx.suffix = suffix ? suffix : "";
524   ctx.fullname = buffer_alloc ();
525 
526   if (!pathwalk (libpath, file_lookup, &ctx))
527     {
528       buffer_free (ctx.fullname);
529       return 0;
530     }
531   fp = fopen (buffer_ptr (ctx.fullname), "r");
532   if (fp == NULL)
533     {
534       buffer_free (ctx.fullname);
535       return 0;
536     }
537 
538   /* Find the end of the header. */
539 #define HDR_TAG "% -- code follows this line --"
540   while ((fgets (buf, sizeof (buf), fp)))
541     {
542       line++;
543       if (strncmp (buf, HDR_TAG, strlen (HDR_TAG)) == 0)
544 	break;
545     }
546 
547   /* Dump rest of file. */
548   while ((fgets (buf, sizeof (buf), fp)))
549     {
550       line++;
551 
552       /*
553        * Document needed resources?
554        */
555 #define RESOURCE_DSC 	"%%DocumentNeededResources:"
556 #define CONT_DSC 	"%%+"
557       if (strncmp (buf, RESOURCE_DSC, strlen (RESOURCE_DSC)) == 0)
558 	{
559 	  char *cp, *cp2;
560 
561 	  strcpy (resources, buf + strlen (RESOURCE_DSC));
562 	  pending_comment = 1;
563 
564 	parse_resources:
565 	  /* Register needed resources. */
566 	  cp = GET_TOKEN (resources);
567 	  if (cp == NULL)
568 	    /* Get the next line. */
569 	    continue;
570 
571 	  if (MATCH (cp, "font"))
572 	    {
573 	      for (cp = GET_TOKEN (NULL); cp; cp = GET_TOKEN (NULL))
574 		/* Is this font already known? */
575 		if (!strhash_get (res_fonts, cp, strlen (cp) + 1,
576 				  (void **) &cp2))
577 		  {
578 		    /* Not it is not,  we must include this resource. */
579 		    fprintf (ofp, "%%%%IncludeResource: font %s\n", cp);
580 
581 		    /*
582 		     * And register that this resource is needed in
583 		     * this document.
584 		     */
585 		    strhash_put (res_fonts, cp, strlen (cp) + 1, NULL, NULL);
586 		  }
587 
588 	      /* Do not pass this DSC row to the output. */
589 	      continue;
590 	    }
591 	  else
592 	    /* Unknown resource, ignore. */
593 	    continue;
594 	}
595       else if (pending_comment
596 	       && strncmp (buf, CONT_DSC, strlen (CONT_DSC)) == 0)
597 	{
598 	  strcpy (resources, buf + strlen (CONT_DSC));
599 	  goto parse_resources;
600 	}
601       else
602 	pending_comment = 0;
603 
604       /*
605        * `%Format' directive?
606        */
607 #define DIRECTIVE_FORMAT "%Format:"
608       if (strncmp (buf, DIRECTIVE_FORMAT, strlen (DIRECTIVE_FORMAT)) == 0)
609 	{
610 	  int i, j;
611 	  char name[256];
612 	  char *cp, *cp2;
613 	  errno = 0;
614 
615 	  /* Skip the leading whitespace. */
616 	  for (i = strlen (DIRECTIVE_FORMAT); buf[i] && isspace (buf[i]); i++)
617 	    ;
618 	  if (!buf[i])
619 	    FATAL ((stderr, _("%s:%d: %%Format: no name"),
620 		    buffer_ptr (ctx.fullname), line));
621 
622 	  /* Copy name. */
623 	  for (j = 0;
624 	       j < sizeof (name) - 1 && buf[i] && !isspace (buf[i]);
625 	       i++)
626 	    name[j++] = buf[i];
627 	  name[j] = '\0';
628 
629 	  if (j >= sizeof (name) - 1)
630 	    FATAL ((stderr, _("%s:%d: %%Format: too long name, maxlen=%d"),
631 		    buffer_ptr (ctx.fullname), line, sizeof (name) - 1));
632 
633 	  /* Find the start of the format string. */
634 	  for (; buf[i] && isspace (buf[i]); i++)
635 	    ;
636 
637 	  /* Find the end. */
638 	  j = strlen (buf);
639 	  for (j--; isspace (buf[j]) && j > i; j--)
640 	    ;
641 	  j++;
642 
643 	  MESSAGE (2, (stderr, "%%Format: %s %.*s\n", name, j - i, buf + i));
644 
645 	  cp = xmalloc (j - i + 1);
646 	  memcpy (cp, buf + i, j - i);
647 	  cp[j - i] = '\0';
648 
649 	  strhash_put (user_strings, name, strlen (name) + 1, cp,
650 		       (void **) &cp2);
651 	  if (cp2)
652 	    FATAL ((stderr,
653 		    _("%s:%d: %%Format: name \"%s\" is already defined"),
654 		    buffer_ptr (ctx.fullname), line, name));
655 
656 	  /* All done with the `%Format' directive. */
657 	  continue;
658 	}
659 
660       /*
661        * `%HeaderHeight' directive?
662        */
663 #define DIRECTIVE_HEADERHEIGHT "%HeaderHeight:"
664       if (strncmp (buf, DIRECTIVE_HEADERHEIGHT,
665 		   strlen (DIRECTIVE_HEADERHEIGHT)) == 0)
666 	  {
667 	    int i;
668 
669 	    /* Find the start of the pts argument. */
670 	    for (i = strlen (DIRECTIVE_HEADERHEIGHT);
671 		 buf[i] && !isspace (buf[i]); i++)
672 	      ;
673 	    if (!buf[i])
674 	      FATAL ((stderr, _("%s:%d: %%HeaderHeight: no argument"),
675 		      buffer_ptr (ctx.fullname), line));
676 
677 	    d_header_h = atoi (buf + i);
678 	    MESSAGE (2, (stderr, "%%HeaderHeight: %d\n", d_header_h));
679 	    continue;
680 	  }
681 
682       /*
683        * `%FooterHeight' directive?
684        */
685 #define DIRECTIVE_FOOTERHEIGHT "%FooterHeight:"
686       if (strncmp (buf, DIRECTIVE_FOOTERHEIGHT,
687 		   strlen (DIRECTIVE_FOOTERHEIGHT)) == 0)
688 	{
689 	  int i;
690 
691 	  /* Find the start of the pts argument. */
692 	  for (i = strlen (DIRECTIVE_FOOTERHEIGHT);
693 	       buf[i] && !isspace (buf[i]); i++)
694 	    ;
695 	  if (!buf[i])
696 	    FATAL ((stderr, _("%s:%d: %%FooterHeight: no argument"),
697 		    buffer_ptr (ctx.fullname), line));
698 
699 	  d_footer_h = atoi (buf + i);
700 	  MESSAGE (2, (stderr, "%%FooterHeight: %d\n", d_footer_h));
701 	  continue;
702 	}
703 
704       /* Nothing special, just copy it to the output. */
705       fputs (buf, ofp);
706     }
707 
708   fclose (fp);
709   buffer_free (ctx.fullname);
710 
711   return 1;
712 }
713 
714 
715 int
parse_font_spec(char * spec_a,char ** name_return,FontPoint * size_return,InputEncoding * encoding_return)716 parse_font_spec (char *spec_a, char **name_return, FontPoint *size_return,
717 		 InputEncoding *encoding_return)
718 {
719   int i, j;
720   char *cp, *cp2;
721   char *spec;
722   char *encp;
723 
724   spec = xstrdup (spec_a);
725 
726   /* Check for the `namesize:encoding' format. */
727   encp = strrchr (spec, ':');
728   if (encp)
729     {
730       *encp = '\0';
731       encp++;
732     }
733 
734   /* The `name@ptsize' format? */
735   cp = strchr (spec, '@');
736   if (cp)
737     {
738       i = cp - spec;
739       if (cp[1] == '\0')
740 	{
741 	  /* No ptsize after '@'. */
742 	  xfree (spec);
743 	  return 0;
744 	}
745       cp++;
746     }
747   else
748     {
749       /* The old `nameptsize' format. */
750       i = strlen (spec) - 1;
751       if (i <= 0 || !ISNUMBERDIGIT (spec[i]))
752 	{
753 	  xfree (spec);
754 	  return 0;
755 	}
756 
757       for (i--; i >= 0 && ISNUMBERDIGIT (spec[i]); i--)
758 	;
759       if (i < 0)
760 	{
761 	  xfree (spec);
762 	  return 0;
763 	}
764       if (spec[i] == '/')
765 	{
766 	  /* We accept one slash for the `pt/pt' format. */
767 	  for (i--; i >= 0 && ISNUMBERDIGIT (spec[i]); i--)
768 	    ;
769 	  if (i < 0)
770 	    {
771 	      xfree (spec);
772 	      return 0;
773 	    }
774 	}
775       i++;
776 
777       /* Now, <i> points to the end of the name.  Let's set the <cp>
778          to the beginning of the point size and share a little code
779          with the other format. */
780       cp = spec + i;
781     }
782 
783   /* Check the font point size. */
784   cp2 = strchr (cp, '/');
785   if (cp2)
786     {
787       *cp2++ = '\0';
788       size_return->w = atof (cp);
789       size_return->h = atof (cp2);
790     }
791   else
792     size_return->w = size_return->h = atof (cp);
793 
794   /* Extract the font name. */
795   *name_return = (char *) xcalloc (1, i + 1);
796   strncpy (*name_return, spec, i);
797 
798   /* Check the input encoding. */
799   if (encp)
800     {
801       int found = 0;
802 
803       if (encoding_return == NULL)
804 	{
805 	  /* We don't allow it here. */
806 	  xfree (spec);
807 	  return 0;
808 	}
809 
810       for (i = 0; !found && encodings[i].names[0]; i++)
811 	for (j = 0; j < 3; j++)
812 	  if (encodings[i].names[j] != NULL && MATCH (encodings[i].names[j],
813 						      encp))
814 	    {
815 	      /* Found a match. */
816 	      *encoding_return = encodings[i].encoding;
817 	      encp = encodings[i].names[0];
818 	      found = 1;
819 	      break;
820 	    }
821 
822       if (!found)
823 	{
824 	  xfree (spec);
825 	  return 0;
826 	}
827     }
828   else
829     {
830       /* The spec didn't contain the encoding part.  Use our global default. */
831       encp = encoding_name;
832       if (encoding_return)
833 	*encoding_return = encoding;
834     }
835   xfree (spec);
836 
837   MESSAGE (2, (stderr,
838 	       "parse_font_spec(): name=%.*s, size=%g/%g, encoding=%s\n", i,
839 	       *name_return, size_return->w, size_return->h,
840 	       encp));
841 
842   if (size_return->w < 0.0 && size_return->h < 0.0)
843     MESSAGE (0, (stderr, _("%s: warning: font size is negative\n"), program));
844   else if (size_return->w < 0.0)
845     MESSAGE (0, (stderr, _("%s: warning: font width is negative\n"), program));
846   else if (size_return->h < 0.0)
847     MESSAGE (0, (stderr, _("%s: warning: font height is negative\n"),
848 		 program));
849 
850   return 1;
851 }
852 
853 
854 void
read_font_info(void)855 read_font_info (void)
856 {
857   CachedFontInfo *font_info;
858   AFMFont font;
859   int font_info_cached = 1;
860   int font_cached = 1;
861   int i;
862   unsigned int enc_flags = 0;
863   char buf[256];
864   Buffer fkey;
865 
866   MESSAGE (2, (stderr, _("reading AFM info for font \"%s\"\n"), Fname));
867 
868   if (accept_composites)
869     enc_flags = AFM_ENCODE_ACCEPT_COMPOSITES;
870 
871   /* Open font */
872 
873   buffer_init (&fkey);
874 
875   buffer_append (&fkey, Fname);
876   sprintf (buf, "@%f:%d", Fpt.w, encoding);
877   buffer_append (&fkey, buf);
878 
879   if (!strhash_get (afm_info_cache, buffer_ptr (&fkey),
880 		    strlen (buffer_ptr (&fkey)), (void **) &font_info))
881     {
882       AFMError error;
883 
884       /* Couldn't find it from our cache, open open AFM file. */
885       if (!strhash_get (afm_cache, Fname, strlen (Fname), (void **) &font))
886 	{
887 	  /* AFM file was not cached, open it from disk. */
888 	  error = afm_open_font (afm, AFM_I_COMPOSITES, Fname, &font);
889 	  if (error != AFM_SUCCESS)
890 	    {
891 #define COUR "Courier"
892 	      /*
893 	       * Do not report failures for "Courier*" fonts because
894 	       * AFM library's default font will fix them.
895 	       */
896 	      if (strncmp (Fname, COUR, strlen (COUR)) != 0)
897 		MESSAGE (0,
898 			 (stderr,
899 			  _("couldn't open AFM file for font \"%s\", using default\n"),
900 			  Fname));
901 	      error = afm_open_default_font (afm, &font);
902 	      if (error != AFM_SUCCESS)
903 		{
904 		  afm_error_to_string (error, buf);
905 		  FATAL ((stderr,
906 			  _("couldn't open AFM file for the default font: %s"),
907 			  buf));
908 		}
909 	    }
910 
911 	  /* Apply encoding. */
912 	  switch (encoding)
913 	    {
914 	    case ENC_ISO_8859_1:
915 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_1,
916 					enc_flags);
917 	      break;
918 
919 	    case ENC_ISO_8859_2:
920 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_2,
921 					enc_flags);
922 	      break;
923 
924 	    case ENC_ISO_8859_3:
925 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_3,
926 					enc_flags);
927 	      break;
928 
929 	    case ENC_ISO_8859_4:
930 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_4,
931 					enc_flags);
932 	      break;
933 
934 	    case ENC_ISO_8859_5:
935 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_5,
936 					enc_flags);
937 	      break;
938 
939 	    case ENC_ISO_8859_7:
940 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_7,
941 					enc_flags);
942 	      break;
943 
944 	    case ENC_ISO_8859_9:
945 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_9,
946 					enc_flags);
947 	      break;
948 
949 	    case ENC_ISO_8859_10:
950 	      (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_10,
951 					enc_flags);
952 	      break;
953 
954 	    case ENC_ASCII:
955 	      (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
956 	      break;
957 
958 	    case ENC_ASCII_FISE:
959 	      /* First apply standard 7bit ASCII encoding. */
960 	      (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
961 
962 	      /* Then add those scand characters. */
963 	      for (i = 0; enc_7bit_ascii_fise[i].name; i++)
964 		(void) afm_font_encode (font, enc_7bit_ascii_fise[i].code,
965 					enc_7bit_ascii_fise[i].name,
966 					enc_flags);
967 	      break;
968 
969 	    case ENC_ASCII_DKNO:
970 	      /* First apply standard 7bit ASCII encoding. */
971 	      (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
972 
973 	      /* Then add those scand characters. */
974 	      for (i = 0; enc_7bit_ascii_dkno[i].name; i++)
975 		(void) afm_font_encode (font, enc_7bit_ascii_dkno[i].code,
976 					enc_7bit_ascii_dkno[i].name,
977 					enc_flags);
978 	      break;
979 
980 	    case ENC_IBMPC:
981 	      (void) afm_font_encoding (font, AFM_ENCODING_IBMPC, enc_flags);
982 	      break;
983 
984 	    case ENC_MAC:
985 	      (void) afm_font_encoding (font, AFM_ENCODING_MAC, enc_flags);
986 	      break;
987 
988 	    case ENC_VMS:
989 	      (void) afm_font_encoding (font, AFM_ENCODING_VMS, enc_flags);
990 	      break;
991 
992 	    case ENC_HP8:
993 	      (void) afm_font_encoding (font, AFM_ENCODING_HP8, enc_flags);
994 	      break;
995 
996 	    case ENC_KOI8:
997 	      (void) afm_font_encoding (font, AFM_ENCODING_KOI8, enc_flags);
998 	      break;
999 
1000 	    case ENC_PS:
1001 	      /* Let's use font's default encoding -- nothing here. */
1002 	      break;
1003 	    }
1004 
1005 	  /* Put it to the AFM cache. */
1006 	  if (!strhash_put (afm_cache, Fname, strlen (Fname), font, NULL))
1007 	    font_cached = 0;
1008 	}
1009 
1010       font_info = (CachedFontInfo *) xcalloc (1, sizeof (*font_info));
1011       /* Read character widths and types. */
1012       for (i = 0; i < 256; i++)
1013 	{
1014 	  AFMNumber w0x, w0y;
1015 
1016 	  (void) afm_font_charwidth (font, Fpt.w, i, &w0x, &w0y);
1017 	  font_info->font_widths[i] = w0x;
1018 
1019 	  if (font->encoding[i] == AFM_ENC_NONE)
1020 	    font_info->font_ctype[i] = ' ';
1021 	  else if (font->encoding[i] == AFM_ENC_NON_EXISTENT)
1022 	    font_info->font_ctype[i] = '.';
1023 	  else
1024 	    font_info->font_ctype[i] = '*';
1025 	}
1026 
1027       font_info->font_is_fixed
1028 	= font->writing_direction_metrics[0].IsFixedPitch;
1029       font_info->font_bbox_lly = font->global_info.FontBBox_lly;
1030 
1031       if (!font_cached)
1032 	(void) afm_close_font (font);
1033 
1034       /* Store font information to the AFM information cache. */
1035       if (!strhash_put (afm_info_cache, buffer_ptr (&fkey),
1036 			strlen (buffer_ptr (&fkey)), font_info, NULL))
1037 	font_info_cached = 0;
1038     }
1039 
1040   /* Select character widths and types. */
1041   memcpy (font_widths, font_info->font_widths, 256 * sizeof (double));
1042   memcpy (font_ctype, font_info->font_ctype, 256);
1043 
1044   font_is_fixed = font_info->font_is_fixed;
1045   font_bbox_lly = font_info->font_bbox_lly;
1046 
1047   if (!font_info_cached)
1048     xfree (font_info);
1049 
1050   buffer_uninit (&fkey);
1051 }
1052 
1053 
1054 void
download_font(char * name)1055 download_font (char *name)
1056 {
1057   AFMError error;
1058   const char *prefix;
1059   struct stat stat_st;
1060   Buffer fname;
1061   unsigned char buf[4096];
1062   FILE *fp;
1063   int i;
1064   char *cp;
1065 
1066   /* Get font prefix. */
1067   error = afm_font_prefix (afm, name, &prefix);
1068   if (error != AFM_SUCCESS)
1069     /* Font is unknown, nothing to download. */
1070     return;
1071 
1072   /* Check if we have a font description file. */
1073 
1074   buffer_init (&fname);
1075 
1076   /* .pfa */
1077   buffer_append (&fname, prefix);
1078   buffer_append (&fname, ".pfa");
1079   if (stat (buffer_ptr (&fname), &stat_st) != 0)
1080     {
1081       /* .pfb */
1082       buffer_clear (&fname);
1083       buffer_append (&fname, prefix);
1084       buffer_append (&fname, ".pfb");
1085       if (stat (buffer_ptr (&fname), &stat_st) != 0)
1086 	{
1087 	  /* Couldn't find font description file, nothing to download. */
1088 	  buffer_uninit (&fname);
1089 	  return;
1090 	}
1091     }
1092 
1093   /* Ok, fine.  Font was found. */
1094 
1095   MESSAGE (1, (stderr, _("downloading font \"%s\"\n"), name));
1096   fp = fopen (buffer_ptr (&fname), "rb");
1097   if (fp == NULL)
1098     {
1099       MESSAGE (0, (stderr,
1100 		   _("couldn't open font description file \"%s\": %s\n"),
1101 		   buffer_ptr (&fname), strerror (errno)));
1102       buffer_uninit (&fname);
1103       return;
1104     }
1105   buffer_uninit (&fname);
1106 
1107   /* Dump file. */
1108   fprintf (ofp, "%%%%BeginResource: font %s\n", name);
1109 
1110   /* Check file type. */
1111   i = fgetc (fp);
1112   if (i == EOF)
1113     {
1114       /* Not much to do here. */
1115       ;
1116     }
1117   else if (i == 128)
1118     {
1119       int done = 0;
1120       unsigned int chunk;
1121       unsigned int to_read;
1122       int last_was_cr;
1123       int j;
1124 
1125       /* IBM PC Format */
1126 
1127       ungetc (i, fp);
1128 
1129       while (!done)
1130 	{
1131 	  /* Read 6-byte long header. */
1132 	  i = fread (buf, 1, 6, fp);
1133 	  if (i != 6)
1134 	    break;
1135 
1136 	  chunk = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24);
1137 
1138 	  /* Check chunk type. */
1139 	  switch (buf[1])
1140 	    {
1141 	    case 1:		/* ASCII */
1142 	      last_was_cr = 0;
1143 	      while (chunk > 0)
1144 		{
1145 		  to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
1146 		  i = fread (buf, 1, to_read, fp);
1147 		  if (i == 0)
1148 		    {
1149 		      done = 1;
1150 		      break;
1151 		    }
1152 
1153 		  /* Check and fix Mac-newlines. */
1154 		  for (j = 0; j < i; j++)
1155 		    {
1156 		      if (j == 0 && last_was_cr && buf[0] != '\n')
1157 			{
1158 			  fputc ('\n', ofp);
1159 			  fputc (buf[0], ofp);
1160 			}
1161 		      else if (buf[j] == '\r' && j + 1 < i
1162 			       && buf[j + 1] != '\n')
1163 			{
1164 			  fputc ('\n', ofp);
1165 			}
1166 		      else if (buf[j] != '\r')
1167 			fputc (buf[j], ofp);
1168 		    }
1169 
1170 		  chunk -= i;
1171 		  last_was_cr = (buf[i - 1] == '\r');
1172 		}
1173 	      break;
1174 
1175 	    case 2:		/* binary data */
1176 	      while (chunk > 0)
1177 		{
1178 		  to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
1179 		  i = fread (buf, 1, to_read, fp);
1180 		  if (i == 0)
1181 		    {
1182 		      done = 1;
1183 		      break;
1184 		    }
1185 
1186 		  for (j = 0; j < i; j++)
1187 		    {
1188 		      fprintf (ofp, "%02X", buf[j]);
1189 		      if ((j + 1) % 32 == 0)
1190 			fprintf (ofp, "\n");
1191 		    }
1192 		  chunk -= i;
1193 		}
1194 	      break;
1195 
1196 	    case 3:		/* EOF */
1197 	      done = 1;
1198 	      break;
1199 	    }
1200 
1201 	  /* Force a linebreak after each chunk. */
1202 	  fprintf (ofp, "\n");
1203 	}
1204     }
1205   else
1206     {
1207       /* Plain ASCII. */
1208       ungetc (i, fp);
1209       while ((i = fread (buf, 1, sizeof (buf), fp)) != 0)
1210 	fwrite (buf, 1, i, ofp);
1211     }
1212 
1213   fprintf (ofp, "%%%%EndResource\n");
1214 
1215   /* Remove font from needed resources. */
1216   (void) strhash_delete (res_fonts, name, strlen (name) + 1, (void **) &cp);
1217 
1218   fclose (fp);
1219 }
1220 
1221 
1222 char *
escape_string(char * string)1223 escape_string (char *string)
1224 {
1225   int i, j;
1226   int len;
1227   char *cp;
1228 
1229   /* Count the length of the result string. */
1230   for (len = 0, i = 0; string[i]; i++)
1231     switch (string[i])
1232       {
1233       case '(':
1234       case ')':
1235       case '\\':
1236 	len += 2;
1237 	break;
1238 
1239       default:
1240 	len++;
1241       }
1242 
1243   /* Create result. */
1244   cp = xmalloc (len + 1);
1245   if (cp == NULL)
1246       return NULL;
1247   for (i = 0, j = 0; string[i]; i++)
1248     switch (string[i])
1249       {
1250       case '(':
1251       case ')':
1252       case '\\':
1253 	cp[j++] = '\\';
1254 	/* FALLTHROUGH */
1255 
1256       default:
1257 	cp[j++] = string[i];
1258 	break;
1259       }
1260   cp[j++] = '\0';
1261 
1262   return cp;
1263 }
1264 
1265 
1266 
1267 /*
1268  * Help macros for the format_user_string() function.
1269  */
1270 
1271 #define NEED_NBYTES(n) 				\
1272   do {						\
1273     if (rbufpos + (n) >= rbuflen)		\
1274       {						\
1275         rbuflen += (n) + 1024;			\
1276         rbuf = xrealloc (rbuf, rbuflen);	\
1277       }						\
1278   } while (0)
1279 
1280 #define APPEND_CH(ch)				\
1281   do {						\
1282     int a;					\
1283     NEED_NBYTES (width);			\
1284     if (width && justification < 0)		\
1285       rbuf[rbufpos++] = (ch);			\
1286     for (a = 0; a < width - 1; a++)		\
1287       rbuf[rbufpos++] = ' ';			\
1288     if (!width || justification > 0)		\
1289       rbuf[rbufpos++] = (ch);			\
1290   } while (0)
1291 
1292 #define APPEND_STR(str)				\
1293   do {						\
1294     int len = strlen ((str));			\
1295     int nspace;					\
1296 						\
1297     if (len > width)				\
1298       nspace = 0;				\
1299     else					\
1300       nspace = width - len;			\
1301 						\
1302     NEED_NBYTES (nspace + len);			\
1303     if (width && justification > 0)		\
1304       for (; nspace; nspace--)			\
1305 	rbuf[rbufpos++] = ' ';			\
1306 						\
1307     memcpy (rbuf + rbufpos, str, len);		\
1308     rbufpos += len;				\
1309 						\
1310     if (width && justification < 0)		\
1311       for (; nspace; nspace--)			\
1312 	rbuf[rbufpos++] = ' ';			\
1313   } while (0)
1314 
1315 char *
format_user_string(char * context_name,char * str)1316 format_user_string (char *context_name, char *str)
1317 {
1318   char *cp;
1319   char *rbuf = NULL;
1320   int rbuflen = 0;
1321   int rbufpos = 0;
1322   int i = 0;
1323   int j;
1324   char buf[512];
1325   char buf2[512];
1326   int width = 0;
1327   int justification = 1;
1328 
1329   /* Format string. */
1330   for (i = 0; str[i] != '\0'; i++)
1331     {
1332       int type;
1333 
1334       type = str[i];
1335 
1336       if (type == '%' || type == '$')
1337 	{
1338 	  i++;
1339 	  width = 0;
1340 	  justification = 1;
1341 
1342 	  /* Get optional width and justification. */
1343 	  if (str[i] == '-')
1344 	    {
1345 	      i++;
1346 	      justification = -1;
1347 	    }
1348 	  while (isdigit (str[i]))
1349 	    width = width * 10 + str[i++] - '0';
1350 
1351 	  /* Handle escapes. */
1352 	  if (type == '%')
1353 	    {
1354 	      /* General state related %-escapes. */
1355 	      switch (str[i])
1356 		{
1357 		case '%':	/* `%%' character `%' */
1358 		  APPEND_CH ('%');
1359 		  break;
1360 
1361 		case 'c':	/* `%c' trailing component of pwd. */
1362 		  getcwd (buf, sizeof (buf));
1363 		  cp = strrchr (buf, '/');
1364 		  if (cp)
1365 		    cp++;
1366 		  else
1367 		    cp = buf;
1368 		  APPEND_STR (cp);
1369 		  break;
1370 
1371 		case 'C':	/* `%C' runtime in `hh:mm:ss' format */
1372 		  sprintf (buf, "%02d:%02d:%02d", run_tm.tm_hour,
1373 			   run_tm.tm_min, run_tm.tm_sec);
1374 		  APPEND_STR (buf);
1375 		  break;
1376 
1377 		case 'd':	/* `%d' current working directory */
1378 		  getcwd (buf, sizeof (buf));
1379 		  APPEND_STR (buf);
1380 		  break;
1381 
1382 		case 'D':
1383 		  if (str[i + 1] == '{')
1384 		    {
1385 		      /* `%D{}' format run date with strftime() */
1386 		      for (j = 0, i += 2;
1387 			   j < sizeof (buf2) && str[i] && str[i] != '}';
1388 			   i++, j++)
1389 			buf2[j] = str[i];
1390 		      if (str[i] != '}')
1391 			FATAL ((stderr,
1392 				_("%s: too long format for %%D{} escape"),
1393 				context_name));
1394 
1395 		      buf2[j] = '\0';
1396 		      strftime (buf, sizeof (buf), buf2, &run_tm);
1397 		    }
1398 		  else
1399 		    {
1400 		      /* `%D' run date in `yy-mm-dd' format */
1401 		      sprintf (buf, "%02d-%02d-%02d", run_tm.tm_year % 100,
1402 			       run_tm.tm_mon + 1, run_tm.tm_mday);
1403 		    }
1404 		  APPEND_STR (buf);
1405 		  break;
1406 
1407 		case 'E':	/* `%E' run date in `yy/mm/dd' format */
1408 		  sprintf (buf, "%02d/%02d/%02d", run_tm.tm_year % 100,
1409 			   run_tm.tm_mon + 1, run_tm.tm_mday);
1410 		  APPEND_STR (buf);
1411 		  break;
1412 
1413 		case 'F':	/* `%F' run date in `dd.mm.yyyy' format */
1414 		  sprintf (buf, "%d.%d.%d",
1415 			   run_tm.tm_mday,
1416 			   run_tm.tm_mon + 1,
1417 			   run_tm.tm_year + 1900);
1418 		  APPEND_STR (buf);
1419 		  break;
1420 
1421 		case 'H':	/* `%H' document title */
1422 		  APPEND_STR (title);
1423 		  break;
1424 
1425 		case 'm':	/* `%m' the hostname up to the first `.' */
1426 		  (void) gethostname (buf, sizeof (buf));
1427 		  cp = strchr (buf, '.');
1428 		  if (cp)
1429 		    *cp = '\0';
1430 		  APPEND_STR (buf);
1431 		  break;
1432 
1433 		case 'M':	/* `%M' the full hostname */
1434 		  (void) gethostname (buf, sizeof (buf));
1435 		  APPEND_STR (buf);
1436 		  break;
1437 
1438 		case 'n':	/* `%n' username */
1439 		  APPEND_STR (passwd->pw_name);
1440 		  break;
1441 
1442 		case 'N':	/* `%N' pw_gecos up to the first `,' char */
1443 		  strcpy (buf, passwd->pw_gecos);
1444 		  cp = strchr (buf, ',');
1445 		  if (cp)
1446 		    *cp = '\0';
1447 		  APPEND_STR (buf);
1448 		  break;
1449 
1450 		case 't':	/* `%t' runtime in 12-hour am/pm format */
1451 		  sprintf (buf, "%d:%d%s",
1452 			   run_tm.tm_hour > 12
1453 			   ? run_tm.tm_hour - 12 : run_tm.tm_hour,
1454 			   run_tm.tm_min,
1455 			   run_tm.tm_hour > 12 ? "pm" : "am");
1456 		  APPEND_STR (buf);
1457 		  break;
1458 
1459 		case 'T':	/* `%T' runtime in 24-hour format */
1460 		  sprintf (buf, "%d:%d", run_tm.tm_hour, run_tm.tm_min);
1461 		  APPEND_STR (buf);
1462 		  break;
1463 
1464 		case '*':	/* `%*' runtime in 24-hour format with secs */
1465 		  sprintf (buf, "%d:%d:%d", run_tm.tm_hour, run_tm.tm_min,
1466 			   run_tm.tm_sec);
1467 		  APPEND_STR (buf);
1468 		  break;
1469 
1470 		case 'W':	/* `%W' run date in `mm/dd/yy' format */
1471 		  sprintf (buf, "%02d/%02d/%02d", run_tm.tm_mon + 1,
1472 			   run_tm.tm_mday, run_tm.tm_year % 100);
1473 		  APPEND_STR (buf);
1474 		  break;
1475 
1476 		default:
1477 		  FATAL ((stderr, _("%s: unknown `%%' escape `%c' (%d)"),
1478 			  context_name, str[i], str[i]));
1479 		  break;
1480 		}
1481 	    }
1482 	  else
1483 	    {
1484 	      /* Input file related $-escapes. */
1485 	      switch (str[i])
1486 		{
1487 		case '$':	/* `$$' character `$' */
1488 		  APPEND_CH ('$');
1489 		  break;
1490 
1491 		case '%':	/* `$%' current page number */
1492 		  if (slicing)
1493 		    sprintf (buf, "%d%c", current_pagenum, slice - 1 + 'A');
1494 		  else
1495 		    sprintf (buf, "%d", current_pagenum);
1496 		  APPEND_STR (buf);
1497 		  break;
1498 
1499 		case '=':	/* `$=' number of pages in this file */
1500 		  APPEND_CH ('\001');
1501 		  break;
1502 
1503 		case 'p':	/* `$p' number of pages processed so far */
1504 		  sprintf (buf, "%d", total_pages);
1505 		  APPEND_STR (buf);
1506 		  break;
1507 
1508 		case '(':	/* $(ENVVAR)  */
1509 		  for (j = 0, i++;
1510 		       str[i] && str[i] != ')' && j < sizeof (buf) - 1;
1511 		       i++)
1512 		    buf[j++] = str[i];
1513 
1514 		  if (str[i] == '\0')
1515 		    FATAL ((stderr, _("%s: no closing ')' for $() escape"),
1516 			    context_name));
1517 		  if (str[i] != ')')
1518 		    FATAL ((stderr, _("%s: too long variable name for $() escape"),
1519 			    context_name));
1520 
1521 		  buf[j] = '\0';
1522 
1523 		  cp = getenv (buf);
1524 		  if (cp == NULL)
1525 		    cp = "";
1526 		  APPEND_STR (cp);
1527 		  break;
1528 
1529 		case 'C':	/* `$C' modtime in `hh:mm:ss' format */
1530 		  sprintf (buf, "%02d:%02d:%02d", mod_tm.tm_hour,
1531 			   mod_tm.tm_min, mod_tm.tm_sec);
1532 		  APPEND_STR (buf);
1533 		  break;
1534 
1535 		case 'D':
1536 		  if (str[i + 1] == '{')
1537 		    {
1538 		      /* `$D{}' format modification date with strftime() */
1539 		      for (j = 0, i += 2;
1540 			   j < sizeof (buf2) && str[i] && str[i] != '}';
1541 			   i++, j++)
1542 			buf2[j] = str[i];
1543 		      if (str[i] != '}')
1544 			FATAL ((stderr,
1545 				_("%s: too long format for $D{} escape"),
1546 				context_name));
1547 
1548 		      buf2[j] = '\0';
1549 		      strftime (buf, sizeof (buf), buf2, &mod_tm);
1550 		    }
1551 		  else
1552 		    {
1553 		      /* `$D' mod date in `yy-mm-dd' format */
1554 		      sprintf (buf, "%02d-%02d-%02d", mod_tm.tm_year % 100,
1555 			       mod_tm.tm_mon + 1, mod_tm.tm_mday);
1556 		    }
1557 		  APPEND_STR (buf);
1558 		  break;
1559 
1560 		case 'E':	/* `$E' mod date in `yy/mm/dd' format */
1561 		  sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_year % 100,
1562 			   mod_tm.tm_mon + 1, mod_tm.tm_mday);
1563 		  APPEND_STR (buf);
1564 		  break;
1565 
1566 		case 'F':	/* `$F' run date in `dd.mm.yyyy' format */
1567 		  sprintf (buf, "%d.%d.%d",
1568 			   mod_tm.tm_mday,
1569 			   mod_tm.tm_mon + 1,
1570 			   mod_tm.tm_year + 1900);
1571 		  APPEND_STR (buf);
1572 		  break;
1573 
1574 		case 't':	/* `$t' runtime in 12-hour am/pm format */
1575 		  sprintf (buf, "%d:%d%s",
1576 			   mod_tm.tm_hour > 12
1577 			   ? mod_tm.tm_hour - 12 : mod_tm.tm_hour,
1578 			   mod_tm.tm_min,
1579 			   mod_tm.tm_hour > 12 ? "pm" : "am");
1580 		  APPEND_STR (buf);
1581 		  break;
1582 
1583 		case 'T':	/* `$T' runtime in 24-hour format */
1584 		  sprintf (buf, "%d:%d", mod_tm.tm_hour, mod_tm.tm_min);
1585 		  APPEND_STR (buf);
1586 		  break;
1587 
1588 		case '*':	/* `$*' runtime in 24-hour format with secs */
1589 		  sprintf (buf, "%d:%d:%d", mod_tm.tm_hour, mod_tm.tm_min,
1590 			   mod_tm.tm_sec);
1591 		  APPEND_STR (buf);
1592 		  break;
1593 
1594 		case 'v':	/* `$v': input file number */
1595 		  sprintf (buf, "%d", input_filenum);
1596 		  APPEND_STR (buf);
1597 		  break;
1598 
1599 		case 'V':	/* `$V': input file number in --toc format */
1600 		  if (toc)
1601 		    {
1602 		      sprintf (buf, "%d-", input_filenum);
1603 		      APPEND_STR (buf);
1604 		    }
1605 		  break;
1606 
1607 		case 'W':	/* `$W' run date in `mm/dd/yy' format */
1608 		  sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_mon + 1,
1609 			   mod_tm.tm_mday, mod_tm.tm_year % 100);
1610 		  APPEND_STR (buf);
1611 		  break;
1612 
1613 		case 'N':	/* `$N' the full name of the printed file */
1614 		  APPEND_STR (fname);
1615 		  break;
1616 
1617 		case 'n':	/* `$n' input file name without directory */
1618 		  cp = strrchr (fname, '/');
1619 		  if (cp)
1620 		    cp++;
1621 		  else
1622 		    cp = fname;
1623 		  APPEND_STR (cp);
1624 		  break;
1625 
1626 		case 'L':	/* `$L' number of lines in this file. */
1627 		  /* This is valid only for TOC-strings. */
1628 		  sprintf (buf, "%d", current_file_linenum - 1);
1629 		  APPEND_STR (buf);
1630 		  break;
1631 
1632 		default:
1633 		  FATAL ((stderr, _("%s: unknown `$' escape `%c' (%d)"),
1634 			  context_name, str[i], str[i]));
1635 		  break;
1636 		}
1637 	    }
1638 	  /* Reset width so the else-arm goes ok at the next round. */
1639 	  width = 0;
1640 	  justification = 1;
1641 	}
1642       else
1643 	APPEND_CH (str[i]);
1644     }
1645   APPEND_CH ('\0');
1646 
1647   /* Escape PS specials. */
1648   cp = escape_string (rbuf);
1649   xfree (rbuf);
1650 
1651   return cp;
1652 }
1653 
1654 
1655 void
parse_key_value_pair(StringHashPtr set,char * kv)1656 parse_key_value_pair (StringHashPtr set, char *kv)
1657 {
1658   char *cp;
1659   Buffer key;
1660 
1661   cp = strchr (kv, ':');
1662   if (cp == NULL)
1663     {
1664       if (strhash_delete (set, kv, strlen (kv) + 1, (void **) &cp))
1665 	xfree (cp);
1666     }
1667   else
1668     {
1669       buffer_init (&key);
1670       buffer_append_len (&key, kv, cp - kv);
1671 
1672       strhash_put (set, buffer_ptr (&key), strlen (buffer_ptr (&key)) + 1,
1673 		   xstrdup (cp + 1), (void **) &cp);
1674       if (cp)
1675 	xfree (cp);
1676 
1677       buffer_uninit (&key);
1678     }
1679 }
1680 
1681 
1682 int
count_key_value_set(StringHashPtr set)1683 count_key_value_set (StringHashPtr set)
1684 {
1685   int i = 0, got, j;
1686   char *cp;
1687   void *value;
1688 
1689   for (got = strhash_get_first (set, &cp, &j, &value); got;
1690        got = strhash_get_next (set, &cp, &j, &value))
1691     i++;
1692 
1693   return i;
1694 }
1695 
1696 
1697 int
pathwalk(char * path,PathWalkProc proc,void * context)1698 pathwalk (char *path, PathWalkProc proc, void *context)
1699 {
1700   char buf[512];
1701   char *cp;
1702   char *cp2;
1703   int len, i;
1704 
1705   for (cp = path; cp; cp = strchr (cp, PATH_SEPARATOR))
1706     {
1707       if (cp != path)
1708 	cp++;
1709 
1710       cp2 = strchr (cp, PATH_SEPARATOR);
1711       if (cp2)
1712 	len = cp2 - cp;
1713       else
1714 	len = strlen (cp);
1715 
1716       memcpy (buf, cp, len);
1717       buf[len] = '\0';
1718 
1719       i = (*proc) (buf, context);
1720       if (i != 0)
1721 	return i;
1722     }
1723 
1724   return 0;
1725 }
1726 
1727 
1728 int
file_lookup(char * path,void * context)1729 file_lookup (char *path, void *context)
1730 {
1731   int len;
1732   FileLookupCtx *ctx = context;
1733   struct stat stat_st;
1734   int i;
1735 
1736   MESSAGE (2, (stderr, "file_lookup(): %s/%s%s\t", path, ctx->name,
1737 	       ctx->suffix));
1738 
1739   len = strlen (path);
1740   if (len && path[len - 1] == '/')
1741     len--;
1742 
1743   buffer_clear (ctx->fullname);
1744   buffer_append_len (ctx->fullname, path, len);
1745   buffer_append (ctx->fullname, "/");
1746   buffer_append (ctx->fullname, ctx->name);
1747   buffer_append (ctx->fullname, ctx->suffix);
1748 
1749   i = stat (buffer_ptr (ctx->fullname), &stat_st) == 0;
1750 
1751   MESSAGE (2, (stderr, "#%c\n", i ? 't' : 'f'));
1752 
1753   return i;
1754 }
1755 
1756 
1757 char *
tilde_subst(char * fname)1758 tilde_subst (char *fname)
1759 {
1760   char *cp;
1761   int i;
1762   struct passwd *pswd;
1763   Buffer buffer;
1764   char *result;
1765 
1766   if (fname[0] != '~')
1767     return xstrdup (fname);
1768 
1769   if (fname[1] == '/' || fname[1] == '\0')
1770     {
1771       /* The the user's home directory from the `HOME' environment
1772          variable. */
1773       cp = getenv ("HOME");
1774       if (cp == NULL)
1775 	return xstrdup (fname);
1776 
1777       buffer_init (&buffer);
1778       buffer_append (&buffer, cp);
1779       buffer_append (&buffer, fname + 1);
1780 
1781       result = buffer_copy (&buffer);
1782       buffer_uninit (&buffer);
1783 
1784       return result;
1785     }
1786 
1787   /* Get user's login name. */
1788   for (i = 1; fname[i] && fname[i] != '/'; i++)
1789     ;
1790 
1791   buffer_init (&buffer);
1792   buffer_append_len (&buffer, fname + 1, i - 1);
1793 
1794   pswd = getpwnam (buffer_ptr (&buffer));
1795   buffer_uninit (&buffer);
1796 
1797   if (pswd)
1798     {
1799       /* Found passwd entry. */
1800       buffer_init (&buffer);
1801       buffer_append (&buffer, pswd->pw_dir);
1802       buffer_append (&buffer, fname + i);
1803 
1804       result = buffer_copy (&buffer);
1805       buffer_uninit (&buffer);
1806 
1807       return result;
1808     }
1809 
1810   /* No match found. */
1811   return xstrdup (fname);
1812 }
1813 
1814 
1815 double
parse_float(char * string,int units,int horizontal)1816 parse_float (char *string, int units, int horizontal)
1817 {
1818   double val;
1819   char *end;
1820 
1821   val = strtod (string, &end);
1822   if (end == string)
1823   malformed_float:
1824     ERROR ((stderr, _("malformed float dimension: \"%s\""), string));
1825 
1826   if (units)
1827     {
1828       switch (*end)
1829 	{
1830 	case 'c':
1831 	  val *= 72 / 2.54;
1832 	  break;
1833 
1834 	case 'p':
1835 	  break;
1836 
1837 	case 'i':
1838 	  val *= 72;
1839 	  break;
1840 
1841 	case '\0':
1842 	  /* FALLTHROUGH */
1843 
1844 	case 'l':
1845 	  if (horizontal)
1846 	    val *= CHAR_WIDTH ('m');
1847 	  else
1848 	    val *= LINESKIP;
1849 	  break;
1850 
1851 	default:
1852 	  goto malformed_float;
1853 	  break;
1854 	}
1855     }
1856   else
1857     {
1858       if (*end != '\0')
1859 	goto malformed_float;
1860     }
1861 
1862   return val;
1863 }
1864 
1865 
1866 /*
1867  * InputStream functions.
1868  */
1869 
1870 int
is_open(InputStream * is,FILE * fp,char * fname,char * input_filter)1871 is_open (InputStream *is, FILE *fp, char *fname, char *input_filter)
1872 {
1873   /* Init stream variables. */
1874   is->data_in_buf = 0;
1875   is->bufpos = 0;
1876   is->nreads = 0;
1877   is->unget_ch = NULL;
1878   is->unget_pos = 0;
1879   is->unget_alloc = 0;
1880 
1881   /* Input filter? */
1882   if (input_filter)
1883     {
1884       char *cmd = NULL;
1885       int cmdlen;
1886       int i, pos;
1887       char *cp;
1888 
1889       is->is_pipe = 1;
1890 
1891       if (fname == NULL)
1892 	fname = input_filter_stdin;
1893 
1894       /*
1895        * Count the initial command length, this will grow dynamically
1896        * when file specifier `%s' is encountered from <input_filter>.
1897        */
1898       cmdlen = strlen (input_filter) + 1;
1899       cmd = xmalloc (cmdlen);
1900 
1901       /* Create filter command. */
1902       pos = 0;
1903       for (i = 0; input_filter[i]; i++)
1904 	{
1905 	  if (input_filter[i] == '%')
1906 	    {
1907 	      switch (input_filter[i + 1])
1908 		{
1909 		case 's':
1910 		  /* Expand cmd-buffer. */
1911 		  if ((cp = shell_escape (fname)) != NULL)
1912 		    {
1913 		      cmdlen += strlen (cp);
1914 		      cmd = xrealloc (cmd, cmdlen);
1915 
1916 		      /* Paste filename. */
1917 		      strcpy (cmd + pos, cp);
1918 		      pos += strlen (cp);
1919 		      free (cp);
1920 		    }
1921 
1922 		  i++;
1923 		  break;
1924 
1925 		case '%':
1926 		  cmd[pos++] = '%';
1927 		  i++;
1928 		  break;
1929 
1930 		default:
1931 		  cmd[pos++] = input_filter[i];
1932 		  break;
1933 		}
1934 	    }
1935 	  else
1936 	    cmd[pos++] = input_filter[i];
1937 	}
1938       cmd[pos++] = '\0';
1939 
1940       is->fp = popen (cmd, "r");
1941       xfree (cmd);
1942 
1943       if (is->fp == NULL)
1944 	{
1945 	  ERROR ((stderr,
1946 		  _("couldn't open input filter \"%s\" for file \"%s\": %s"),
1947 		  input_filter, fname ? fname : "(stdin)",
1948 		  strerror (errno)));
1949 	  return 0;
1950 	}
1951     }
1952   else
1953     {
1954       /* Just open the stream. */
1955       is->is_pipe = 0;
1956       if (fp)
1957 	is->fp = fp;
1958       else
1959 	{
1960 	  is->fp = fopen (fname, "rb");
1961 	  if (is->fp == NULL)
1962 	    {
1963 	      ERROR ((stderr, _("couldn't open input file \"%s\": %s"), fname,
1964 		      strerror (errno)));
1965 	      return 0;
1966 	    }
1967 	}
1968     }
1969 
1970   return 1;
1971 }
1972 
1973 
1974 void
is_close(InputStream * is)1975 is_close (InputStream *is)
1976 {
1977   if (is->is_pipe)
1978     pclose (is->fp);
1979   else
1980     fclose (is->fp);
1981 
1982   if (is->unget_ch)
1983     xfree (is->unget_ch);
1984 }
1985 
1986 
1987 int
is_getc(InputStream * is)1988 is_getc (InputStream *is)
1989 {
1990   int ch;
1991 
1992   if (is->unget_pos > 0)
1993     {
1994       ch = is->unget_ch[--is->unget_pos];
1995       return ch;
1996     }
1997 
1998  retry:
1999 
2000   /* Do we have any data left? */
2001   if (is->bufpos >= is->data_in_buf)
2002     {
2003       /* At the EOF? */
2004       if (is->nreads > 0 && is->data_in_buf < sizeof (is->buf))
2005 	/* Yes. */
2006 	return EOF;
2007 
2008       /* Read more data. */
2009       is->data_in_buf = fread (is->buf, 1, sizeof (is->buf), is->fp);
2010       is->bufpos = 0;
2011       is->nreads++;
2012 
2013       goto retry;
2014     }
2015 
2016   return is->buf[is->bufpos++];
2017 }
2018 
2019 
2020 int
is_ungetc(int ch,InputStream * is)2021 is_ungetc (int ch, InputStream *is)
2022 {
2023   if (is->unget_pos >= is->unget_alloc)
2024     {
2025       is->unget_alloc += 1024;
2026       is->unget_ch = xrealloc (is->unget_ch, is->unget_alloc);
2027     }
2028 
2029   is->unget_ch[is->unget_pos++] = ch;
2030 
2031   return 1;
2032 }
2033 
2034 
2035 /*
2036  * Buffer Functions.
2037  */
2038 
2039 void
buffer_init(Buffer * buffer)2040 buffer_init (Buffer *buffer)
2041 {
2042   buffer->allocated = 128;
2043   buffer->data = xmalloc (buffer->allocated);
2044   buffer->data[0] = '\0';
2045   buffer->len = 0;
2046 }
2047 
2048 
2049 void
buffer_uninit(Buffer * buffer)2050 buffer_uninit (Buffer *buffer)
2051 {
2052   xfree (buffer->data);
2053 }
2054 
2055 
2056 Buffer *
buffer_alloc()2057 buffer_alloc ()
2058 {
2059   Buffer *buffer = (Buffer *) xcalloc (1, sizeof (Buffer));
2060 
2061   buffer_init (buffer);
2062 
2063   return buffer;
2064 }
2065 
2066 
2067 void
buffer_free(Buffer * buffer)2068 buffer_free (Buffer *buffer)
2069 {
2070   buffer_uninit (buffer);
2071   xfree (buffer);
2072 }
2073 
2074 
2075 void
buffer_append(Buffer * buffer,const char * data)2076 buffer_append (Buffer *buffer, const char *data)
2077 {
2078   buffer_append_len (buffer, data, strlen (data));
2079 }
2080 
2081 
2082 void
buffer_append_len(Buffer * buffer,const char * data,size_t len)2083 buffer_append_len (Buffer *buffer, const char *data, size_t len)
2084 {
2085   if (buffer->len + len + 1 >= buffer->allocated)
2086     {
2087       buffer->allocated = buffer->len + len + 1024;
2088       buffer->data = xrealloc (buffer->data, buffer->allocated);
2089     }
2090 
2091   memcpy (buffer->data + buffer->len, data, len);
2092   buffer->len += len;
2093 
2094   buffer->data[buffer->len] = '\0';
2095 }
2096 
2097 
2098 char *
buffer_copy(Buffer * buffer)2099 buffer_copy (Buffer *buffer)
2100 {
2101   char *copy = xmalloc (buffer->len + 1);
2102 
2103   memcpy (copy, buffer->data, buffer->len + 1);
2104 
2105   return copy;
2106 }
2107 
2108 
2109 void
buffer_clear(Buffer * buffer)2110 buffer_clear (Buffer *buffer)
2111 {
2112   buffer->len = 0;
2113   buffer->data[0] = '\0';
2114 }
2115 
2116 
2117 char *
buffer_ptr(Buffer * buffer)2118 buffer_ptr (Buffer *buffer)
2119 {
2120   return buffer->data;
2121 }
2122 
2123 
2124 size_t
buffer_len(Buffer * buffer)2125 buffer_len (Buffer *buffer)
2126 {
2127   return buffer->len;
2128 }
2129 
2130 /*
2131  * Escapes the name of a file so that the shell groks it in 'single'
2132  * quotation marks.  The resulting pointer has to be free()ed when not
2133  * longer used.
2134 */
2135 char *
shell_escape(const char * fn)2136 shell_escape(const char *fn)
2137 {
2138   size_t len = 0;
2139   const char *inp;
2140   char *retval, *outp;
2141 
2142   for(inp = fn; *inp; ++inp)
2143     switch(*inp)
2144     {
2145       case '\'': len += 4; break;
2146       default:   len += 1; break;
2147     }
2148 
2149   outp = retval = malloc(len + 1);
2150   if(!outp)
2151     return NULL; /* perhaps one should do better error handling here */
2152   for(inp = fn; *inp; ++inp)
2153     switch(*inp)
2154     {
2155       case '\'': *outp++ = '\''; *outp++ = '\\'; *outp++ = '\'', *outp++ = '\''; break;
2156       default:   *outp++ = *inp; break;
2157     }
2158   *outp = 0;
2159 
2160   return retval;
2161 }
2162