3 * Copyright (c) 1995-1999 Markku Rossi.
5 * Author: Markku Rossi <mtr@iki.fi>
9 * This file is part of GNU Enscript.
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.
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.
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/>.
28 * Types and definitions.
31 #define CFG_FATAL(body) \
33 fprintf (stderr, "%s:%s:%d: ", program, buffer_ptr(&fname), line); \
35 fprintf (stderr, "\n"); \
46 * 7bit ASCII fi(nland), se (sweden) scand encodings (additions to 7bit ASCII
53 } enc_7bit_ascii_fise[] =
65 * 7bit ASCII dk (denmark), no(rway) scand encodings (additions to 7bit ASCII
72 } enc_7bit_ascii_dkno[] =
88 #define GET_TOKEN(from) (strtok ((from), " \t\n"))
89 #define GET_LINE_TOKEN(from) (strtok ((from), "\n"))
91 #define CHECK_TOKEN() \
93 CFG_FATAL ((stderr, _("missing argument: %s"), token));
95 void search_and_replace(char *str, char *search, char *replace) {
97 int search_len = strlen(search);
98 int replace_len = strlen(replace);
100 while ((pos = strstr(str, search)) != NULL) {
101 char tmp[strlen(str) + 1];
102 strcpy(tmp, pos + search_len);
103 strcpy(pos, replace);
104 strcpy(pos + replace_len, tmp);
105 str = pos + replace_len;
109 read_config (char *path, char *file)
114 char *token, *token2;
117 buffer_init (&fname);
118 buffer_append (&fname, path);
119 buffer_append (&fname, "/");
120 buffer_append (&fname, file);
122 fp = fopen (buffer_ptr (&fname), "r");
124 /* We wait to uninit the buffer so that CFG_FATAL can use it. */
128 buffer_uninit (&fname);
132 while (fgets (buf, sizeof (buf), fp))
139 token = GET_TOKEN (buf);
144 if (MATCH (token, "AcceptCompositeCharacters:"))
146 token2 = GET_TOKEN (NULL);
148 accept_composites = atoi (token2);
150 else if (MATCH (token, "AFMPath:"))
152 token2 = GET_TOKEN (NULL);
154 search_and_replace(token2,"$HOME",getenv("HOME"));
156 afm_path = xstrdup (token2);
158 else if (MATCH (token, "AppendCtrlD:"))
160 token2 = GET_TOKEN (NULL);
162 append_ctrl_D = atoi (token2);
164 else if (MATCH (token, "Clean7Bit:"))
166 token2 = GET_TOKEN (NULL);
168 clean_7bit = atoi (token2);
170 else if (MATCH (token, "DefaultEncoding:"))
172 token2 = GET_TOKEN (NULL);
174 xfree (encoding_name);
175 encoding_name = xstrdup (token2);
177 else if (MATCH (token, "DefaultFancyHeader:"))
179 token2 = GET_TOKEN (NULL);
181 xfree (fancy_header_default);
182 fancy_header_default = xstrdup (token2);
184 else if (MATCH (token, "DefaultMedia:"))
186 token2 = GET_TOKEN (NULL);
189 if (!strcasecmp("LC_PAPER", token2))
191 unsigned int paperheight = (unsigned int)nl_langinfo(_NL_PAPER_HEIGHT);
192 if (paperheight && paperheight == 279)
199 media_name = xstrdup (token2);
201 else if (MATCH (token, "DefaultOutputMethod:"))
203 token2 = GET_TOKEN (NULL);
205 if (MATCH (token2, "printer"))
206 output_file = OUTPUT_FILE_NONE;
207 else if (MATCH (token2, "stdout"))
208 output_file = OUTPUT_FILE_STDOUT;
210 CFG_FATAL ((stderr, _("illegal value \"%s\" for option %s"),
213 else if (MATCH (token, "DownloadFont:"))
215 token2 = GET_TOKEN (NULL);
217 strhash_put (download_fonts, token2, strlen (token2) + 1, NULL,
220 else if (MATCH (token, "EscapeChar:"))
222 token2 = GET_TOKEN (NULL);
224 escape_char = atoi (token2);
225 if (escape_char < 0 || escape_char > 255)
226 CFG_FATAL ((stderr, _("invalid value \"%s\" for option %s"),
229 else if (MATCH (token, "FormFeedType:"))
231 token2 = GET_TOKEN (NULL);
233 if (MATCH (token2, "column"))
234 formfeed_type = FORMFEED_COLUMN;
235 else if (MATCH (token2, "page"))
236 formfeed_type = FORMFEED_PAGE;
238 CFG_FATAL ((stderr, _("illegal value \"%s\" for option %s"),
241 else if (MATCH (token, "GeneratePageSize:"))
243 token2 = GET_TOKEN (NULL);
245 generate_PageSize = atoi (token2);
247 else if (MATCH (token, "HighlightBarGray:"))
249 token2 = GET_TOKEN (NULL);
251 highlight_bar_gray = atof (token2);
253 else if (MATCH (token, "HighlightBars:"))
255 token2 = GET_TOKEN (NULL);
257 highlight_bars = atoi (token2);
259 else if (MATCH (token, "LibraryPath:"))
261 token2 = GET_TOKEN (NULL);
264 libpath = xstrdup (token2);
266 else if (MATCH (token, "MarkWrappedLines:"))
268 token2 = GET_TOKEN (NULL);
270 xfree (mark_wrapped_lines_style_name);
271 mark_wrapped_lines_style_name = xstrdup (token2);
273 else if (MATCH (token, "Media:"))
276 int w, h, llx, lly, urx, ury;
278 token2 = GET_TOKEN (NULL);
282 token2 = GET_TOKEN (NULL);
286 token2 = GET_TOKEN (NULL);
290 token2 = GET_TOKEN (NULL);
294 token2 = GET_TOKEN (NULL);
298 token2 = GET_TOKEN (NULL);
302 token2 = GET_TOKEN (NULL);
306 add_media (name, w, h, llx, lly, urx, ury);
308 else if (MATCH (token, "NoJobHeaderSwitch:"))
310 token2 = GET_LINE_TOKEN (NULL);
312 xfree (no_job_header_switch);
313 no_job_header_switch = xstrdup (token2);
315 else if (MATCH (token, "NonPrintableFormat:"))
317 token2 = GET_TOKEN (NULL);
320 npf_name = xstrdup (token2);
322 else if (MATCH (token, "OutputFirstLine:"))
324 token2 = GET_LINE_TOKEN (NULL);
326 xfree (output_first_line);
327 output_first_line = xstrdup (token2);
329 else if (MATCH (token, "PageLabelFormat:"))
331 token2 = GET_TOKEN (NULL);
333 xfree (page_label_format);
334 page_label_format = xstrdup (token2);
336 else if (MATCH (token, "PagePrefeed:"))
338 token2 = GET_TOKEN (NULL);
340 page_prefeed = atoi (token2);
342 else if (MATCH (token, "PostScriptLevel:"))
344 token2 = GET_TOKEN (NULL);
346 pslevel = atoi (token2);
348 else if (MATCH (token, "Printer:"))
350 token2 = GET_TOKEN (NULL);
353 printer = xstrdup (token2);
355 else if (MATCH (token, "QueueParam:"))
357 token2 = GET_LINE_TOKEN (NULL);
360 queue_param = xstrdup (token2);
362 else if (MATCH (token, "SetPageDevice:"))
364 token2 = GET_LINE_TOKEN (NULL);
366 parse_key_value_pair (pagedevice, token2);
368 else if (MATCH (token, "Spooler:"))
370 token2 = GET_TOKEN (NULL);
372 xfree (spooler_command);
373 spooler_command = xstrdup (token2);
375 else if (MATCH (token, "StatesBinary:"))
377 token2 = GET_TOKEN (NULL);
379 xfree (states_binary);
380 states_binary = xstrdup (token2);
382 else if (MATCH (token, "StatesColor:"))
384 token2 = GET_TOKEN (NULL);
386 states_color = atoi (token2);
388 else if (MATCH (token, "StatesConfigFile:"))
390 token2 = GET_LINE_TOKEN (NULL);
392 xfree (states_config_file);
393 states_config_file = xstrdup (token2);
395 else if (MATCH (token, "StatesHighlightStyle:"))
397 token2 = GET_TOKEN (NULL);
399 xfree (states_highlight_style);
400 states_highlight_style = xstrdup (token2);
402 else if (MATCH (token, "StatesPath:"))
404 token2 = GET_LINE_TOKEN (NULL);
407 states_path = xstrdup (token2);
409 else if (MATCH (token, "StatusDict:"))
411 token2 = GET_TOKEN (NULL);
413 parse_key_value_pair (statusdict, token2);
415 else if (MATCH (token, "TOCFormat:"))
417 token2 = GET_LINE_TOKEN (NULL);
419 toc_fmt_string = xstrdup (token2);
421 else if (MATCH (token, "Underlay:"))
423 token2 = GET_LINE_TOKEN (NULL);
425 underlay = xmalloc (strlen (token2) + 1);
426 strcpy (underlay, token2);
428 else if (MATCH (token, "UnderlayAngle:"))
430 token2 = GET_TOKEN (NULL);
432 ul_angle = atof (token2);
435 else if (MATCH (token, "UnderlayFont:"))
437 token2 = GET_TOKEN (NULL);
439 if (!parse_font_spec (token2, &ul_font, &ul_ptsize, NULL))
440 CFG_FATAL ((stderr, _("malformed font spec: %s"), token2));
442 else if (MATCH (token, "UnderlayGray:"))
444 token2 = GET_TOKEN (NULL);
446 ul_gray = atof (token2);
448 else if (MATCH (token, "UnderlayPosition:"))
450 token2 = GET_TOKEN (NULL);
453 ul_position = xstrdup (token2);
456 else if (MATCH (token, "UnderlayStyle:"))
458 token2 = GET_TOKEN (NULL);
460 xfree (ul_style_str);
461 ul_style_str = xstrdup (token2);
464 CFG_FATAL ((stderr, _("illegal option: %s"), token));
468 buffer_uninit (&fname);
474 add_media (char *name, int w, int h, int llx, int lly, int urx, int ury)
480 "add_media: name=%s, w=%d, h=%d, llx=%d, lly=%d, urx=%d, ury=%d\n",
481 name, w, h, llx, lly, urx, ury));
483 entry = xcalloc (1, sizeof (*entry));
484 entry->name = xmalloc (strlen (name) + 1);
486 strcpy (entry->name, name);
494 entry->next = media_names;
500 do_list_missing_characters (int *array)
505 for (i = 0; i < 256; i++)
508 fprintf (stderr, "%3d ", i);
511 fprintf (stderr, "\n");
515 fprintf (stderr, "\n");
520 file_existsp (char *name, char *suffix)
526 ctx.suffix = suffix ? suffix : "";
527 ctx.fullname = buffer_alloc ();
529 result = pathwalk (libpath, file_lookup, &ctx);
531 buffer_free (ctx.fullname);
538 paste_file (char *name, char *suffix)
544 int pending_comment = 0;
548 ctx.suffix = suffix ? suffix : "";
549 ctx.fullname = buffer_alloc ();
551 if (!pathwalk (libpath, file_lookup, &ctx))
553 buffer_free (ctx.fullname);
556 fp = fopen (buffer_ptr (ctx.fullname), "r");
559 buffer_free (ctx.fullname);
563 /* Find the end of the header. */
564 #define HDR_TAG "% -- code follows this line --"
565 while ((fgets (buf, sizeof (buf), fp)))
568 if (strncmp (buf, HDR_TAG, strlen (HDR_TAG)) == 0)
572 /* Dump rest of file. */
573 while ((fgets (buf, sizeof (buf), fp)))
578 * Document needed resources?
580 #define RESOURCE_DSC "%%DocumentNeededResources:"
581 #define CONT_DSC "%%+"
582 if (strncmp (buf, RESOURCE_DSC, strlen (RESOURCE_DSC)) == 0)
586 strcpy (resources, buf + strlen (RESOURCE_DSC));
590 /* Register needed resources. */
591 cp = GET_TOKEN (resources);
593 /* Get the next line. */
596 if (MATCH (cp, "font"))
598 for (cp = GET_TOKEN (NULL); cp; cp = GET_TOKEN (NULL))
599 /* Is this font already known? */
600 if (!strhash_get (res_fonts, cp, strlen (cp) + 1,
603 /* Not it is not, we must include this resource. */
604 fprintf (ofp, "%%%%IncludeResource: font %s\n", cp);
607 * And register that this resource is needed in
610 strhash_put (res_fonts, cp, strlen (cp) + 1, NULL, NULL);
613 /* Do not pass this DSC row to the output. */
617 /* Unknown resource, ignore. */
620 else if (pending_comment
621 && strncmp (buf, CONT_DSC, strlen (CONT_DSC)) == 0)
623 strcpy (resources, buf + strlen (CONT_DSC));
624 goto parse_resources;
630 * `%Format' directive?
632 #define DIRECTIVE_FORMAT "%Format:"
633 if (strncmp (buf, DIRECTIVE_FORMAT, strlen (DIRECTIVE_FORMAT)) == 0)
640 /* Skip the leading whitespace. */
641 for (i = strlen (DIRECTIVE_FORMAT); buf[i] && isspace (buf[i]); i++)
644 FATAL ((stderr, _("%s:%d: %%Format: no name"),
645 buffer_ptr (ctx.fullname), line));
649 j < sizeof (name) - 1 && buf[i] && !isspace (buf[i]);
654 if (j >= sizeof (name) - 1)
655 FATAL ((stderr, _("%s:%d: %%Format: too long name, maxlen=%d"),
656 buffer_ptr (ctx.fullname), line, (int)(sizeof (name) - 1)));
658 /* Find the start of the format string. */
659 for (; buf[i] && isspace (buf[i]); i++)
664 for (j--; isspace (buf[j]) && j > i; j--)
668 MESSAGE (2, (stderr, "%%Format: %s %.*s\n", name, j - i, buf + i));
670 cp = xmalloc (j - i + 1);
671 memcpy (cp, buf + i, j - i);
674 strhash_put (user_strings, name, strlen (name) + 1, cp,
678 _("%s:%d: %%Format: name \"%s\" is already defined"),
679 buffer_ptr (ctx.fullname), line, name));
681 /* All done with the `%Format' directive. */
686 * `%HeaderHeight' directive?
688 #define DIRECTIVE_HEADERHEIGHT "%HeaderHeight:"
689 if (strncmp (buf, DIRECTIVE_HEADERHEIGHT,
690 strlen (DIRECTIVE_HEADERHEIGHT)) == 0)
694 /* Find the start of the pts argument. */
695 for (i = strlen (DIRECTIVE_HEADERHEIGHT);
696 buf[i] && !isspace (buf[i]); i++)
699 FATAL ((stderr, _("%s:%d: %%HeaderHeight: no argument"),
700 buffer_ptr (ctx.fullname), line));
702 d_header_h = atoi (buf + i);
703 MESSAGE (2, (stderr, "%%HeaderHeight: %d\n", d_header_h));
708 * `%FooterHeight' directive?
710 #define DIRECTIVE_FOOTERHEIGHT "%FooterHeight:"
711 if (strncmp (buf, DIRECTIVE_FOOTERHEIGHT,
712 strlen (DIRECTIVE_FOOTERHEIGHT)) == 0)
716 /* Find the start of the pts argument. */
717 for (i = strlen (DIRECTIVE_FOOTERHEIGHT);
718 buf[i] && !isspace (buf[i]); i++)
721 FATAL ((stderr, _("%s:%d: %%FooterHeight: no argument"),
722 buffer_ptr (ctx.fullname), line));
724 d_footer_h = atoi (buf + i);
725 MESSAGE (2, (stderr, "%%FooterHeight: %d\n", d_footer_h));
729 /* Nothing special, just copy it to the output. */
734 buffer_free (ctx.fullname);
741 parse_font_spec (char *spec_a, char **name_return, FontPoint *size_return,
742 InputEncoding *encoding_return)
749 spec = xstrdup (spec_a);
751 /* Check for the `namesize:encoding' format. */
752 encp = strrchr (spec, ':');
759 /* The `name@ptsize' format? */
760 cp = strchr (spec, '@');
766 /* No ptsize after '@'. */
774 /* The old `nameptsize' format. */
775 i = strlen (spec) - 1;
776 if (i <= 0 || !ISNUMBERDIGIT (spec[i]))
782 for (i--; i >= 0 && ISNUMBERDIGIT (spec[i]); i--)
791 /* We accept one slash for the `pt/pt' format. */
792 for (i--; i >= 0 && ISNUMBERDIGIT (spec[i]); i--)
802 /* Now, <i> points to the end of the name. Let's set the <cp>
803 to the beginning of the point size and share a little code
804 with the other format. */
808 /* Check the font point size. */
809 cp2 = strchr (cp, '/');
813 size_return->w = atof (cp);
814 size_return->h = atof (cp2);
817 size_return->w = size_return->h = atof (cp);
819 /* Extract the font name. */
820 *name_return = (char *) xcalloc (1, i + 1);
821 strncpy (*name_return, spec, i);
823 /* Check the input encoding. */
828 if (encoding_return == NULL)
830 /* We don't allow it here. */
835 for (i = 0; !found && encodings[i].names[0]; i++)
836 for (j = 0; j < 3; j++)
837 if (encodings[i].names[j] != NULL && MATCH (encodings[i].names[j],
841 *encoding_return = encodings[i].encoding;
842 encp = encodings[i].names[0];
855 /* The spec didn't contain the encoding part. Use our global default. */
856 encp = encoding_name;
858 *encoding_return = encoding;
863 "parse_font_spec(): name=%.*s, size=%g/%g, encoding=%s\n", i,
864 *name_return, size_return->w, size_return->h,
867 if (size_return->w < 0.0 && size_return->h < 0.0)
868 MESSAGE (0, (stderr, _("%s: warning: font size is negative\n"), program));
869 else if (size_return->w < 0.0)
870 MESSAGE (0, (stderr, _("%s: warning: font width is negative\n"), program));
871 else if (size_return->h < 0.0)
872 MESSAGE (0, (stderr, _("%s: warning: font height is negative\n"),
880 read_font_info (void)
882 CachedFontInfo *font_info;
884 int font_info_cached = 1;
887 unsigned int enc_flags = 0;
891 MESSAGE (2, (stderr, _("reading AFM info for font \"%s\"\n"), Fname));
893 if (accept_composites)
894 enc_flags = AFM_ENCODE_ACCEPT_COMPOSITES;
900 buffer_append (&fkey, Fname);
901 sprintf (buf, "@%f:%d", Fpt.w, encoding);
902 buffer_append (&fkey, buf);
904 if (!strhash_get (afm_info_cache, buffer_ptr (&fkey),
905 strlen (buffer_ptr (&fkey)), (void **) &font_info))
909 /* Couldn't find it from our cache, open open AFM file. */
910 if (!strhash_get (afm_cache, Fname, strlen (Fname), (void **) &font))
912 /* AFM file was not cached, open it from disk. */
913 error = afm_open_font (afm, AFM_I_COMPOSITES, Fname, &font);
914 if (error != AFM_SUCCESS)
916 #define COUR "Courier"
918 * Do not report failures for "Courier*" fonts because
919 * AFM library's default font will fix them.
921 if (strncmp (Fname, COUR, strlen (COUR)) != 0)
924 _("couldn't open AFM file for font \"%s\", using default\n"),
926 error = afm_open_default_font (afm, &font);
927 if (error != AFM_SUCCESS)
929 afm_error_to_string (error, buf);
931 _("couldn't open AFM file for the default font: %s"),
936 /* Apply encoding. */
940 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_1,
945 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_2,
950 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_3,
955 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_4,
960 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_5,
965 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_7,
970 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_9,
974 case ENC_ISO_8859_10:
975 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_10,
980 (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
984 /* First apply standard 7bit ASCII encoding. */
985 (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
987 /* Then add those scand characters. */
988 for (i = 0; enc_7bit_ascii_fise[i].name; i++)
989 (void) afm_font_encode (font, enc_7bit_ascii_fise[i].code,
990 enc_7bit_ascii_fise[i].name,
995 /* First apply standard 7bit ASCII encoding. */
996 (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
998 /* Then add those scand characters. */
999 for (i = 0; enc_7bit_ascii_dkno[i].name; i++)
1000 (void) afm_font_encode (font, enc_7bit_ascii_dkno[i].code,
1001 enc_7bit_ascii_dkno[i].name,
1006 (void) afm_font_encoding (font, AFM_ENCODING_IBMPC, enc_flags);
1010 (void) afm_font_encoding (font, AFM_ENCODING_MAC, enc_flags);
1014 (void) afm_font_encoding (font, AFM_ENCODING_VMS, enc_flags);
1018 (void) afm_font_encoding (font, AFM_ENCODING_HP8, enc_flags);
1022 (void) afm_font_encoding (font, AFM_ENCODING_KOI8, enc_flags);
1026 /* Let's use font's default encoding -- nothing here. */
1030 /* Put it to the AFM cache. */
1031 if (!strhash_put (afm_cache, Fname, strlen (Fname), font, NULL))
1035 font_info = (CachedFontInfo *) xcalloc (1, sizeof (*font_info));
1036 /* Read character widths and types. */
1037 for (i = 0; i < 256; i++)
1041 (void) afm_font_charwidth (font, Fpt.w, i, &w0x, &w0y);
1042 font_info->font_widths[i] = w0x;
1044 if (font->encoding[i] == AFM_ENC_NONE)
1045 font_info->font_ctype[i] = ' ';
1046 else if (font->encoding[i] == AFM_ENC_NON_EXISTENT)
1047 font_info->font_ctype[i] = '.';
1049 font_info->font_ctype[i] = '*';
1052 font_info->font_is_fixed
1053 = font->writing_direction_metrics[0].IsFixedPitch;
1054 font_info->font_bbox_lly = font->global_info.FontBBox_lly;
1057 (void) afm_close_font (font);
1059 /* Store font information to the AFM information cache. */
1060 if (!strhash_put (afm_info_cache, buffer_ptr (&fkey),
1061 strlen (buffer_ptr (&fkey)), font_info, NULL))
1062 font_info_cached = 0;
1065 /* Select character widths and types. */
1066 memcpy (font_widths, font_info->font_widths, 256 * sizeof (double));
1067 memcpy (font_ctype, font_info->font_ctype, 256);
1069 font_is_fixed = font_info->font_is_fixed;
1070 font_bbox_lly = font_info->font_bbox_lly;
1072 if (!font_info_cached)
1075 buffer_uninit (&fkey);
1080 download_font (char *name)
1084 struct stat stat_st;
1086 unsigned char buf[4096];
1091 /* Get font prefix. */
1092 error = afm_font_prefix (afm, name, &prefix);
1093 if (error != AFM_SUCCESS)
1094 /* Font is unknown, nothing to download. */
1097 /* Check if we have a font description file. */
1099 buffer_init (&fname);
1102 buffer_append (&fname, prefix);
1103 buffer_append (&fname, ".pfa");
1104 if (stat (buffer_ptr (&fname), &stat_st) != 0)
1107 buffer_clear (&fname);
1108 buffer_append (&fname, prefix);
1109 buffer_append (&fname, ".pfb");
1110 if (stat (buffer_ptr (&fname), &stat_st) != 0)
1112 /* Couldn't find font description file, nothing to download. */
1113 buffer_uninit (&fname);
1118 /* Ok, fine. Font was found. */
1120 MESSAGE (1, (stderr, _("downloading font \"%s\"\n"), name));
1121 fp = fopen (buffer_ptr (&fname), "rb");
1124 MESSAGE (0, (stderr,
1125 _("couldn't open font description file \"%s\": %s\n"),
1126 buffer_ptr (&fname), strerror (errno)));
1127 buffer_uninit (&fname);
1130 buffer_uninit (&fname);
1133 fprintf (ofp, "%%%%BeginResource: font %s\n", name);
1135 /* Check file type. */
1139 /* Not much to do here. */
1146 unsigned int to_read;
1156 /* Read 6-byte long header. */
1157 i = fread (buf, 1, 6, fp);
1161 chunk = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24);
1163 /* Check chunk type. */
1170 to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
1171 i = fread (buf, 1, to_read, fp);
1178 /* Check and fix Mac-newlines. */
1179 for (j = 0; j < i; j++)
1181 if (j == 0 && last_was_cr && buf[0] != '\n')
1184 fputc (buf[0], ofp);
1186 else if (buf[j] == '\r' && j + 1 < i
1187 && buf[j + 1] != '\n')
1191 else if (buf[j] != '\r')
1192 fputc (buf[j], ofp);
1196 last_was_cr = (buf[i - 1] == '\r');
1200 case 2: /* binary data */
1203 to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
1204 i = fread (buf, 1, to_read, fp);
1211 for (j = 0; j < i; j++)
1213 fprintf (ofp, "%02X", buf[j]);
1214 if ((j + 1) % 32 == 0)
1215 fprintf (ofp, "\n");
1226 /* Force a linebreak after each chunk. */
1227 fprintf (ofp, "\n");
1234 while ((i = fread (buf, 1, sizeof (buf), fp)) != 0)
1235 fwrite (buf, 1, i, ofp);
1238 fprintf (ofp, "%%%%EndResource\n");
1240 /* Remove font from needed resources. */
1241 (void) strhash_delete (res_fonts, name, strlen (name) + 1, (void **) &cp);
1248 escape_string (char *string)
1254 /* Count the length of the result string. */
1255 for (len = 0, i = 0; string[i]; i++)
1268 /* Create result. */
1269 cp = xmalloc (len + 1);
1272 for (i = 0, j = 0; string[i]; i++)
1282 cp[j++] = string[i];
1293 * Help macros for the format_user_string() function.
1296 #define NEED_NBYTES(n) \
1298 if (rbufpos + (n) >= rbuflen) \
1300 rbuflen += (n) + 1024; \
1301 rbuf = xrealloc (rbuf, rbuflen); \
1305 #define APPEND_CH(ch) \
1308 NEED_NBYTES (width); \
1309 if (width && justification < 0) \
1310 rbuf[rbufpos++] = (ch); \
1311 for (a = 0; a < width - 1; a++) \
1312 rbuf[rbufpos++] = ' '; \
1313 if (!width || justification > 0) \
1314 rbuf[rbufpos++] = (ch); \
1317 #define APPEND_STR(str) \
1319 int len = strlen ((str)); \
1325 nspace = width - len; \
1327 NEED_NBYTES (nspace + len); \
1328 if (width && justification > 0) \
1329 for (; nspace; nspace--) \
1330 rbuf[rbufpos++] = ' '; \
1332 memcpy (rbuf + rbufpos, str, len); \
1335 if (width && justification < 0) \
1336 for (; nspace; nspace--) \
1337 rbuf[rbufpos++] = ' '; \
1341 format_user_string (char *context_name, char *str)
1352 int justification = 1;
1354 /* Format string. */
1355 for (i = 0; str[i] != '\0'; i++)
1361 if (type == '%' || type == '$')
1367 /* Get optional width and justification. */
1373 while (isdigit (str[i]))
1374 width = width * 10 + str[i++] - '0';
1376 /* Handle escapes. */
1379 /* General state related %-escapes. */
1382 case '%': /* `%%' character `%' */
1386 case 'c': /* `%c' trailing component of pwd. */
1387 if (!getcwd (buf, sizeof (buf)))
1389 cp = strrchr (buf, '/');
1397 case 'C': /* `%C' runtime in `hh:mm:ss' format */
1398 sprintf (buf, "%02d:%02d:%02d", run_tm.tm_hour,
1399 run_tm.tm_min, run_tm.tm_sec);
1403 case 'd': /* `%d' current working directory */
1404 if (!getcwd (buf, sizeof (buf)))
1410 if (str[i + 1] == '{')
1412 /* `%D{}' format run date with strftime() */
1414 j < sizeof (buf2) && str[i] && str[i] != '}';
1419 _("%s: too long format for %%D{} escape"),
1423 strftime (buf, sizeof (buf), buf2, &run_tm);
1427 /* `%D' run date in `yy-mm-dd' format */
1428 sprintf (buf, "%02d-%02d-%02d", run_tm.tm_year % 100,
1429 run_tm.tm_mon + 1, run_tm.tm_mday);
1434 case 'E': /* `%E' run date in `yy/mm/dd' format */
1435 sprintf (buf, "%02d/%02d/%02d", run_tm.tm_year % 100,
1436 run_tm.tm_mon + 1, run_tm.tm_mday);
1440 case 'F': /* `%F' run date in `dd.mm.yyyy' format */
1441 sprintf (buf, "%d.%d.%d",
1444 run_tm.tm_year + 1900);
1448 case 'H': /* `%H' document title */
1452 case 'm': /* `%m' the hostname up to the first `.' */
1453 (void) gethostname (buf, sizeof (buf));
1454 cp = strchr (buf, '.');
1460 case 'M': /* `%M' the full hostname */
1461 (void) gethostname (buf, sizeof (buf));
1465 case 'n': /* `%n' username */
1466 APPEND_STR (passwd->pw_name);
1469 case 'N': /* `%N' pw_gecos up to the first `,' char */
1470 strcpy (buf, passwd->pw_gecos);
1471 cp = strchr (buf, ',');
1477 case 't': /* `%t' runtime in 12-hour am/pm format */
1478 sprintf (buf, "%d:%d%s",
1480 ? run_tm.tm_hour - 12 : run_tm.tm_hour,
1482 run_tm.tm_hour > 12 ? "pm" : "am");
1486 case 'T': /* `%T' runtime in 24-hour format */
1487 sprintf (buf, "%d:%d", run_tm.tm_hour, run_tm.tm_min);
1491 case '*': /* `%*' runtime in 24-hour format with secs */
1492 sprintf (buf, "%d:%d:%d", run_tm.tm_hour, run_tm.tm_min,
1497 case 'W': /* `%W' run date in `mm/dd/yy' format */
1498 sprintf (buf, "%02d/%02d/%02d", run_tm.tm_mon + 1,
1499 run_tm.tm_mday, run_tm.tm_year % 100);
1504 FATAL ((stderr, _("%s: unknown `%%' escape `%c' (%d)"),
1505 context_name, str[i], str[i]));
1511 /* Input file related $-escapes. */
1514 case '$': /* `$$' character `$' */
1518 case '%': /* `$%' current page number */
1520 sprintf (buf, "%d%c", current_pagenum, slice - 1 + 'A');
1522 sprintf (buf, "%d", current_pagenum);
1526 case '=': /* `$=' number of pages in this file */
1530 case 'p': /* `$p' number of pages processed so far */
1531 sprintf (buf, "%d", total_pages);
1535 case '(': /* $(ENVVAR) */
1537 str[i] && str[i] != ')' && j < sizeof (buf) - 1;
1542 FATAL ((stderr, _("%s: no closing ')' for $() escape"),
1545 FATAL ((stderr, _("%s: too long variable name for $() escape"),
1556 case 'C': /* `$C' modtime in `hh:mm:ss' format */
1557 sprintf (buf, "%02d:%02d:%02d", mod_tm.tm_hour,
1558 mod_tm.tm_min, mod_tm.tm_sec);
1563 if (str[i + 1] == '{')
1565 /* `$D{}' format modification date with strftime() */
1567 j < sizeof (buf2) && str[i] && str[i] != '}';
1572 _("%s: too long format for $D{} escape"),
1576 strftime (buf, sizeof (buf), buf2, &mod_tm);
1580 /* `$D' mod date in `yy-mm-dd' format */
1581 sprintf (buf, "%02d-%02d-%02d", mod_tm.tm_year % 100,
1582 mod_tm.tm_mon + 1, mod_tm.tm_mday);
1587 case 'E': /* `$E' mod date in `yy/mm/dd' format */
1588 sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_year % 100,
1589 mod_tm.tm_mon + 1, mod_tm.tm_mday);
1593 case 'F': /* `$F' run date in `dd.mm.yyyy' format */
1594 sprintf (buf, "%d.%d.%d",
1597 mod_tm.tm_year + 1900);
1601 case 't': /* `$t' runtime in 12-hour am/pm format */
1602 sprintf (buf, "%d:%d%s",
1604 ? mod_tm.tm_hour - 12 : mod_tm.tm_hour,
1606 mod_tm.tm_hour > 12 ? "pm" : "am");
1610 case 'T': /* `$T' runtime in 24-hour format */
1611 sprintf (buf, "%d:%d", mod_tm.tm_hour, mod_tm.tm_min);
1615 case '*': /* `$*' runtime in 24-hour format with secs */
1616 sprintf (buf, "%d:%d:%d", mod_tm.tm_hour, mod_tm.tm_min,
1621 case 'v': /* `$v': input file number */
1622 sprintf (buf, "%d", input_filenum);
1626 case 'V': /* `$V': input file number in --toc format */
1629 sprintf (buf, "%d-", input_filenum);
1634 case 'W': /* `$W' run date in `mm/dd/yy' format */
1635 sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_mon + 1,
1636 mod_tm.tm_mday, mod_tm.tm_year % 100);
1640 case 'N': /* `$N' the full name of the printed file */
1644 case 'n': /* `$n' input file name without directory */
1645 cp = strrchr (fname, '/');
1653 case 'L': /* `$L' number of lines in this file. */
1654 /* This is valid only for TOC-strings. */
1655 sprintf (buf, "%d", current_file_linenum - 1);
1660 FATAL ((stderr, _("%s: unknown `$' escape `%c' (%d)"),
1661 context_name, str[i], str[i]));
1665 /* Reset width so the else-arm goes ok at the next round. */
1674 /* Escape PS specials. */
1675 cp = escape_string (rbuf);
1683 parse_key_value_pair (StringHashPtr set, char *kv)
1688 cp = strchr (kv, ':');
1691 if (strhash_delete (set, kv, strlen (kv) + 1, (void **) &cp))
1697 buffer_append_len (&key, kv, cp - kv);
1699 strhash_put (set, buffer_ptr (&key), strlen (buffer_ptr (&key)) + 1,
1700 xstrdup (cp + 1), (void **) &cp);
1704 buffer_uninit (&key);
1710 count_key_value_set (StringHashPtr set)
1716 for (got = strhash_get_first (set, &cp, &j, &value); got;
1717 got = strhash_get_next (set, &cp, &j, &value))
1725 pathwalk (char *path, PathWalkProc proc, void *context)
1732 for (cp = path; cp; cp = strchr (cp, PATH_SEPARATOR))
1737 cp2 = strchr (cp, PATH_SEPARATOR);
1743 memcpy (buf, cp, len);
1746 i = (*proc) (buf, context);
1756 file_lookup (char *path, void *context)
1759 FileLookupCtx *ctx = context;
1760 struct stat stat_st;
1763 MESSAGE (2, (stderr, "file_lookup(): %s/%s%s\t", path, ctx->name,
1766 len = strlen (path);
1767 if (len && path[len - 1] == '/')
1770 buffer_clear (ctx->fullname);
1771 buffer_append_len (ctx->fullname, path, len);
1772 buffer_append (ctx->fullname, "/");
1773 buffer_append (ctx->fullname, ctx->name);
1774 buffer_append (ctx->fullname, ctx->suffix);
1776 i = stat (buffer_ptr (ctx->fullname), &stat_st) == 0;
1778 MESSAGE (2, (stderr, "#%c\n", i ? 't' : 'f'));
1785 tilde_subst (char *fname)
1789 struct passwd *pswd;
1793 if (fname[0] != '~')
1794 return xstrdup (fname);
1796 if (fname[1] == '/' || fname[1] == '\0')
1798 /* The the user's home directory from the `HOME' environment
1800 cp = getenv ("HOME");
1802 return xstrdup (fname);
1804 buffer_init (&buffer);
1805 buffer_append (&buffer, cp);
1806 buffer_append (&buffer, fname + 1);
1808 result = buffer_copy (&buffer);
1809 buffer_uninit (&buffer);
1814 /* Get user's login name. */
1815 for (i = 1; fname[i] && fname[i] != '/'; i++)
1818 buffer_init (&buffer);
1819 buffer_append_len (&buffer, fname + 1, i - 1);
1821 pswd = getpwnam (buffer_ptr (&buffer));
1822 buffer_uninit (&buffer);
1826 /* Found passwd entry. */
1827 buffer_init (&buffer);
1828 buffer_append (&buffer, pswd->pw_dir);
1829 buffer_append (&buffer, fname + i);
1831 result = buffer_copy (&buffer);
1832 buffer_uninit (&buffer);
1837 /* No match found. */
1838 return xstrdup (fname);
1843 parse_float (char *string, int units, int horizontal)
1848 val = strtod (string, &end);
1851 ERROR ((stderr, _("malformed float dimension: \"%s\""), string));
1873 val *= FNT_CHAR_WIDTH ('m');
1879 goto malformed_float;
1886 goto malformed_float;
1894 * InputStream functions.
1898 is_open (InputStream *is, FILE *fp, char *fname, char *input_filter)
1900 /* Init stream variables. */
1901 is->data_in_buf = 0;
1904 is->unget_ch = NULL;
1906 is->unget_alloc = 0;
1919 fname = input_filter_stdin;
1922 * Count the initial command length, this will grow dynamically
1923 * when file specifier `%s' is encountered from <input_filter>.
1925 cmdlen = strlen (input_filter) + 1;
1926 cmd = xmalloc (cmdlen);
1928 /* Create filter command. */
1930 for (i = 0; input_filter[i]; i++)
1932 if (input_filter[i] == '%')
1934 switch (input_filter[i + 1])
1937 /* Expand cmd-buffer. */
1938 if ((cp = shell_escape (fname)) != NULL)
1940 cmdlen += strlen (cp);
1941 cmd = xrealloc (cmd, cmdlen);
1943 /* Paste filename. */
1944 strcpy (cmd + pos, cp);
1958 cmd[pos++] = input_filter[i];
1963 cmd[pos++] = input_filter[i];
1967 is->fp = popen (cmd, "r");
1973 _("couldn't open input filter \"%s\" for file \"%s\": %s"),
1974 input_filter, fname ? fname : "(stdin)",
1981 /* Just open the stream. */
1987 is->fp = fopen (fname, "rb");
1990 ERROR ((stderr, _("couldn't open input file \"%s\": %s"), fname,
2002 is_close (InputStream *is)
2010 xfree (is->unget_ch);
2015 is_getc (InputStream *is)
2019 if (is->unget_pos > 0)
2021 ch = is->unget_ch[--is->unget_pos];
2027 /* Do we have any data left? */
2028 if (is->bufpos >= is->data_in_buf)
2031 if (is->nreads > 0 && is->data_in_buf < sizeof (is->buf))
2035 /* Read more data. */
2036 is->data_in_buf = fread (is->buf, 1, sizeof (is->buf), is->fp);
2043 return is->buf[is->bufpos++];
2048 is_ungetc (int ch, InputStream *is)
2050 if (is->unget_pos >= is->unget_alloc)
2052 is->unget_alloc += 1024;
2053 is->unget_ch = xrealloc (is->unget_ch, is->unget_alloc);
2056 is->unget_ch[is->unget_pos++] = ch;
2067 buffer_init (Buffer *buffer)
2069 buffer->allocated = 128;
2070 buffer->data = xmalloc (buffer->allocated);
2071 buffer->data[0] = '\0';
2077 buffer_uninit (Buffer *buffer)
2079 xfree (buffer->data);
2086 Buffer *buffer = (Buffer *) xcalloc (1, sizeof (Buffer));
2088 buffer_init (buffer);
2095 buffer_free (Buffer *buffer)
2097 buffer_uninit (buffer);
2103 buffer_append (Buffer *buffer, const char *data)
2105 buffer_append_len (buffer, data, strlen (data));
2110 buffer_append_len (Buffer *buffer, const char *data, size_t len)
2112 if (buffer->len + len + 1 >= buffer->allocated)
2114 buffer->allocated = buffer->len + len + 1024;
2115 buffer->data = xrealloc (buffer->data, buffer->allocated);
2118 memcpy (buffer->data + buffer->len, data, len);
2121 buffer->data[buffer->len] = '\0';
2126 buffer_copy (Buffer *buffer)
2128 char *copy = xmalloc (buffer->len + 1);
2130 memcpy (copy, buffer->data, buffer->len + 1);
2137 buffer_clear (Buffer *buffer)
2140 buffer->data[0] = '\0';
2145 buffer_ptr (Buffer *buffer)
2147 return buffer->data;
2152 buffer_len (Buffer *buffer)
2158 * Escapes the name of a file so that the shell groks it in 'single'
2159 * quotation marks. The resulting pointer has to be free()ed when not
2163 shell_escape(const char *fn)
2167 char *retval, *outp;
2169 for(inp = fn; *inp; ++inp)
2172 case '\'': len += 4; break;
2173 default: len += 1; break;
2176 outp = retval = malloc(len + 1);
2178 return NULL; /* perhaps one should do better error handling here */
2179 for(inp = fn; *inp; ++inp)
2182 case '\'': *outp++ = '\''; *outp++ = '\\'; *outp++ = '\'', *outp++ = '\''; break;
2183 default: *outp++ = *inp; break;