source: trunk/third/enscript/src/util.c @ 17620

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