3 * Copyright (c) 1995-1999 Markku Rossi.
5 * Author: Markku Rossi <mtr@iki.fi>
9 * This file is part of GNU enscript.
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)
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.
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, 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
30 * Types and definitions.
33 #define CFG_FATAL(body) \
35 fprintf (stderr, "%s:%s:%d: ", program, fname, line); \
37 fprintf (stderr, "\n"); \
48 * 7bit ASCII fi(nland), se (sweden) scand encodings (additions to 7bit ASCII
55 } enc_7bit_ascii_fise[] =
67 * 7bit ASCII dk (denmark), no(rway) scand encodings (additions to 7bit ASCII
74 } enc_7bit_ascii_dkno[] =
90 #define GET_TOKEN(from) (strtok ((from), " \t\n"))
91 #define GET_LINE_TOKEN(from) (strtok ((from), "\n"))
93 #define CHECK_TOKEN() \
95 CFG_FATAL ((stderr, _("missing argument: %s"), token));
98 read_config (char *path, char *file)
103 char *token, *token2;
106 buffer_init (&fname);
107 buffer_append (&fname, path);
108 buffer_append (&fname, "/");
109 buffer_append (&fname, file);
111 fp = fopen (buffer_ptr (&fname), "r");
113 buffer_uninit (&fname);
118 while (fgets (buf, sizeof (buf), fp))
125 token = GET_TOKEN (buf);
130 if (MATCH (token, "AcceptCompositeCharacters:"))
132 token2 = GET_TOKEN (NULL);
134 accept_composites = atoi (token2);
136 else if (MATCH (token, "AFMPath:"))
138 token2 = GET_TOKEN (NULL);
141 afm_path = xstrdup (token2);
143 else if (MATCH (token, "AppendCtrlD:"))
145 token2 = GET_TOKEN (NULL);
147 append_ctrl_D = atoi (token2);
149 else if (MATCH (token, "Clean7Bit:"))
151 token2 = GET_TOKEN (NULL);
153 clean_7bit = atoi (token2);
155 else if (MATCH (token, "DefaultEncoding:"))
157 token2 = GET_TOKEN (NULL);
159 xfree (encoding_name);
160 encoding_name = xstrdup (token2);
162 else if (MATCH (token, "DefaultFancyHeader:"))
164 token2 = GET_TOKEN (NULL);
166 xfree (fancy_header_default);
167 fancy_header_default = xstrdup (token2);
169 else if (MATCH (token, "DefaultMedia:"))
171 token2 = GET_TOKEN (NULL);
174 media_name = xstrdup (token2);
176 else if (MATCH (token, "DefaultOutputMethod:"))
178 token2 = GET_TOKEN (NULL);
180 if (MATCH (token2, "printer"))
181 output_file = OUTPUT_FILE_NONE;
182 else if (MATCH (token2, "stdout"))
183 output_file = OUTPUT_FILE_STDOUT;
185 CFG_FATAL ((stderr, _("illegal value \"%s\" for option %s"),
188 else if (MATCH (token, "DownloadFont:"))
190 token2 = GET_TOKEN (NULL);
192 strhash_put (download_fonts, token2, strlen (token2) + 1, NULL,
195 else if (MATCH (token, "EscapeChar:"))
197 token2 = GET_TOKEN (NULL);
199 escape_char = atoi (token2);
200 if (escape_char < 0 || escape_char > 255)
201 CFG_FATAL ((stderr, _("invalid value \"%s\" for option %s"),
204 else if (MATCH (token, "FormFeedType:"))
206 token2 = GET_TOKEN (NULL);
208 if (MATCH (token2, "column"))
209 formfeed_type = FORMFEED_COLUMN;
210 else if (MATCH (token2, "page"))
211 formfeed_type = FORMFEED_PAGE;
213 CFG_FATAL ((stderr, _("illegal value \"%s\" for option %s"),
216 else if (MATCH (token, "GeneratePageSize:"))
218 token2 = GET_TOKEN (NULL);
220 generate_PageSize = atoi (token2);
222 else if (MATCH (token, "HighlightBarGray:"))
224 token2 = GET_TOKEN (NULL);
226 highlight_bar_gray = atof (token2);
228 else if (MATCH (token, "HighlightBars:"))
230 token2 = GET_TOKEN (NULL);
232 highlight_bars = atoi (token2);
234 else if (MATCH (token, "LibraryPath:"))
236 token2 = GET_TOKEN (NULL);
239 libpath = xstrdup (token2);
241 else if (MATCH (token, "MarkWrappedLines:"))
243 token2 = GET_TOKEN (NULL);
245 xfree (mark_wrapped_lines_style_name);
246 mark_wrapped_lines_style_name = xstrdup (token2);
248 else if (MATCH (token, "Media:"))
251 int w, h, llx, lly, urx, ury;
253 token2 = GET_TOKEN (NULL);
257 token2 = GET_TOKEN (NULL);
261 token2 = GET_TOKEN (NULL);
265 token2 = GET_TOKEN (NULL);
269 token2 = GET_TOKEN (NULL);
273 token2 = GET_TOKEN (NULL);
277 token2 = GET_TOKEN (NULL);
281 add_media (name, w, h, llx, lly, urx, ury);
283 else if (MATCH (token, "NoJobHeaderSwitch:"))
285 token2 = GET_LINE_TOKEN (NULL);
287 xfree (no_job_header_switch);
288 no_job_header_switch = xstrdup (token2);
290 else if (MATCH (token, "NonPrintableFormat:"))
292 token2 = GET_TOKEN (NULL);
295 npf_name = xstrdup (token2);
297 else if (MATCH (token, "OutputFirstLine:"))
299 token2 = GET_LINE_TOKEN (NULL);
301 xfree (output_first_line);
302 output_first_line = xstrdup (token2);
304 else if (MATCH (token, "PageLabelFormat:"))
306 token2 = GET_TOKEN (NULL);
308 xfree (page_label_format);
309 page_label_format = xstrdup (token2);
311 else if (MATCH (token, "PagePrefeed:"))
313 token2 = GET_TOKEN (NULL);
315 page_prefeed = atoi (token2);
317 else if (MATCH (token, "PostScriptLevel:"))
319 token2 = GET_TOKEN (NULL);
321 pslevel = atoi (token2);
323 else if (MATCH (token, "Printer:"))
325 token2 = GET_TOKEN (NULL);
328 printer = xstrdup (token2);
330 else if (MATCH (token, "QueueParam:"))
332 token2 = GET_LINE_TOKEN (NULL);
335 queue_param = xstrdup (token2);
337 else if (MATCH (token, "SetPageDevice:"))
339 token2 = GET_LINE_TOKEN (NULL);
341 parse_key_value_pair (pagedevice, token2);
343 else if (MATCH (token, "Spooler:"))
345 token2 = GET_TOKEN (NULL);
347 xfree (spooler_command);
348 spooler_command = xstrdup (token2);
350 else if (MATCH (token, "StatesBinary:"))
352 token2 = GET_TOKEN (NULL);
354 xfree (states_binary);
355 states_binary = xstrdup (token2);
357 else if (MATCH (token, "StatesColor:"))
359 token2 = GET_TOKEN (NULL);
361 states_color = atoi (token2);
363 else if (MATCH (token, "StatesConfigFile:"))
365 token2 = GET_LINE_TOKEN (NULL);
367 xfree (states_config_file);
368 states_config_file = xstrdup (token2);
370 else if (MATCH (token, "StatesHighlightStyle:"))
372 token2 = GET_TOKEN (NULL);
374 xfree (states_highlight_style);
375 states_highlight_style = xstrdup (token2);
377 else if (MATCH (token, "StatesPath:"))
379 token2 = GET_LINE_TOKEN (NULL);
382 states_path = xstrdup (token2);
384 else if (MATCH (token, "StatusDict:"))
386 token2 = GET_TOKEN (NULL);
388 parse_key_value_pair (statusdict, token2);
390 else if (MATCH (token, "TOCFormat:"))
392 token2 = GET_LINE_TOKEN (NULL);
394 toc_fmt_string = xstrdup (token2);
396 else if (MATCH (token, "Underlay:"))
398 token2 = GET_LINE_TOKEN (NULL);
400 underlay = xmalloc (strlen (token2) + 1);
401 strcpy (underlay, token2);
403 else if (MATCH (token, "UnderlayAngle:"))
405 token2 = GET_TOKEN (NULL);
407 ul_angle = atof (token2);
410 else if (MATCH (token, "UnderlayFont:"))
412 token2 = GET_TOKEN (NULL);
414 if (!parse_font_spec (token2, &ul_font, &ul_ptsize, NULL))
415 CFG_FATAL ((stderr, _("malformed font spec: %s"), token2));
417 else if (MATCH (token, "UnderlayGray:"))
419 token2 = GET_TOKEN (NULL);
421 ul_gray = atof (token2);
423 else if (MATCH (token, "UnderlayPosition:"))
425 token2 = GET_TOKEN (NULL);
428 ul_position = xstrdup (token2);
431 else if (MATCH (token, "UnderlayStyle:"))
433 token2 = GET_TOKEN (NULL);
435 xfree (ul_style_str);
436 ul_style_str = xstrdup (token2);
439 CFG_FATAL ((stderr, _("illegal option: %s"), token));
446 add_media (char *name, int w, int h, int llx, int lly, int urx, int ury)
452 "add_media: name=%s, w=%d, h=%d, llx=%d, lly=%d, urx=%d, ury=%d\n",
453 name, w, h, llx, lly, urx, ury));
455 entry = xcalloc (1, sizeof (*entry));
456 entry->name = xmalloc (strlen (name) + 1);
458 strcpy (entry->name, name);
466 entry->next = media_names;
472 do_list_missing_characters (int *array)
477 for (i = 0; i < 256; i++)
480 fprintf (stderr, "%3d ", i);
483 fprintf (stderr, "\n");
487 fprintf (stderr, "\n");
492 file_existsp (char *name, char *suffix)
498 ctx.suffix = suffix ? suffix : "";
499 ctx.fullname = buffer_alloc ();
501 result = pathwalk (libpath, file_lookup, &ctx);
503 buffer_free (ctx.fullname);
510 paste_file (char *name, char *suffix)
516 int pending_comment = 0;
520 ctx.suffix = suffix ? suffix : "";
521 ctx.fullname = buffer_alloc ();
523 if (!pathwalk (libpath, file_lookup, &ctx))
525 buffer_free (ctx.fullname);
528 fp = fopen (buffer_ptr (ctx.fullname), "r");
531 buffer_free (ctx.fullname);
535 /* Find the end of the header. */
536 #define HDR_TAG "% -- code follows this line --"
537 while ((fgets (buf, sizeof (buf), fp)))
540 if (strncmp (buf, HDR_TAG, strlen (HDR_TAG)) == 0)
544 /* Dump rest of file. */
545 while ((fgets (buf, sizeof (buf), fp)))
550 * Document needed resources?
552 #define RESOURCE_DSC "%%DocumentNeededResources:"
553 #define CONT_DSC "%%+"
554 if (strncmp (buf, RESOURCE_DSC, strlen (RESOURCE_DSC)) == 0)
558 strcpy (resources, buf + strlen (RESOURCE_DSC));
562 /* Register needed resources. */
563 cp = GET_TOKEN (resources);
565 /* Get the next line. */
568 if (MATCH (cp, "font"))
570 for (cp = GET_TOKEN (NULL); cp; cp = GET_TOKEN (NULL))
571 /* Is this font already known? */
572 if (!strhash_get (res_fonts, cp, strlen (cp) + 1,
575 /* Not it is not, we must include this resource. */
576 fprintf (ofp, "%%%%IncludeResource: font %s\n", cp);
579 * And register that this resource is needed in
582 strhash_put (res_fonts, cp, strlen (cp) + 1, NULL, NULL);
585 /* Do not pass this DSC row to the output. */
589 /* Unknown resource, ignore. */
592 else if (pending_comment
593 && strncmp (buf, CONT_DSC, strlen (CONT_DSC)) == 0)
595 strcpy (resources, buf + strlen (CONT_DSC));
596 goto parse_resources;
602 * `%Format' directive?
604 #define DIRECTIVE_FORMAT "%Format:"
605 if (strncmp (buf, DIRECTIVE_FORMAT, strlen (DIRECTIVE_FORMAT)) == 0)
612 /* Skip the leading whitespace. */
613 for (i = strlen (DIRECTIVE_FORMAT); buf[i] && isspace (buf[i]); i++)
616 FATAL ((stderr, _("%s:%d: %%Format: no name"),
617 buffer_ptr (ctx.fullname), line));
621 j < sizeof (name) - 1 && buf[i] && !isspace (buf[i]);
626 if (j >= sizeof (name) - 1)
627 FATAL ((stderr, _("%s:%d: %%Format: too long name, maxlen=%d"),
628 buffer_ptr (ctx.fullname), line, sizeof (name) - 1));
630 /* Find the start of the format string. */
631 for (; buf[i] && isspace (buf[i]); i++)
636 for (j--; isspace (buf[j]) && j > i; j--)
640 MESSAGE (2, (stderr, "%%Format: %s %.*s\n", name, j - i, buf + i));
642 cp = xmalloc (j - i + 1);
643 memcpy (cp, buf + i, j - i);
646 strhash_put (user_strings, name, strlen (name) + 1, cp,
650 _("%s:%d: %%Format: name \"%s\" is already defined"),
651 buffer_ptr (ctx.fullname), line, name));
653 /* All done with the `%Format' directive. */
658 * `%HeaderHeight' directive?
660 #define DIRECTIVE_HEADERHEIGHT "%HeaderHeight:"
661 if (strncmp (buf, DIRECTIVE_HEADERHEIGHT,
662 strlen (DIRECTIVE_HEADERHEIGHT)) == 0)
666 /* Find the start of the pts argument. */
667 for (i = strlen (DIRECTIVE_HEADERHEIGHT);
668 buf[i] && !isspace (buf[i]); i++)
671 FATAL ((stderr, _("%s:%d: %%HeaderHeight: no argument"),
672 buffer_ptr (ctx.fullname), line));
674 d_header_h = atoi (buf + i);
675 MESSAGE (2, (stderr, "%%HeaderHeight: %d\n", d_header_h));
680 * `%FooterHeight' directive?
682 #define DIRECTIVE_FOOTERHEIGHT "%FooterHeight:"
683 if (strncmp (buf, DIRECTIVE_FOOTERHEIGHT,
684 strlen (DIRECTIVE_FOOTERHEIGHT)) == 0)
688 /* Find the start of the pts argument. */
689 for (i = strlen (DIRECTIVE_FOOTERHEIGHT);
690 buf[i] && !isspace (buf[i]); i++)
693 FATAL ((stderr, _("%s:%d: %%FooterHeight: no argument"),
694 buffer_ptr (ctx.fullname), line));
696 d_footer_h = atoi (buf + i);
697 MESSAGE (2, (stderr, "%%FooterHeight: %d\n", d_footer_h));
701 /* Nothing special, just copy it to the output. */
706 buffer_free (ctx.fullname);
713 parse_font_spec (char *spec_a, char **name_return, FontPoint *size_return,
714 InputEncoding *encoding_return)
721 spec = xstrdup (spec_a);
723 /* Check for the `namesize:encoding' format. */
724 encp = strrchr (spec, ':');
731 /* The `name@ptsize' format? */
732 cp = strchr (spec, '@');
738 /* No ptsize after '@'. */
746 /* The old `nameptsize' format. */
747 i = strlen (spec) - 1;
748 if (i <= 0 || !ISNUMBERDIGIT (spec[i]))
754 for (i--; i >= 0 && ISNUMBERDIGIT (spec[i]); i--)
763 /* We accept one slash for the `pt/pt' format. */
764 for (i--; i >= 0 && ISNUMBERDIGIT (spec[i]); i--)
774 /* Now, <i> points to the end of the name. Let's set the <cp>
775 to the beginning of the point size and share a little code
776 with the other format. */
780 /* Check the font point size. */
781 cp2 = strchr (cp, '/');
785 size_return->w = atof (cp);
786 size_return->h = atof (cp2);
789 size_return->w = size_return->h = atof (cp);
791 /* Extract the font name. */
792 *name_return = (char *) xcalloc (1, i + 1);
793 strncpy (*name_return, spec, i);
795 /* Check the input encoding. */
800 if (encoding_return == NULL)
802 /* We don't allow it here. */
807 for (i = 0; !found && encodings[i].names[0]; i++)
808 for (j = 0; j < 3; j++)
809 if (encodings[i].names[j] != NULL && MATCH (encodings[i].names[j],
813 *encoding_return = encodings[i].encoding;
814 encp = encodings[i].names[0];
827 /* The spec didn't contain the encoding part. Use our global default. */
828 encp = encoding_name;
830 *encoding_return = encoding;
835 "parse_font_spec(): name=%.*s, size=%g/%g, encoding=%s\n", i,
836 *name_return, size_return->w, size_return->h,
839 if (size_return->w < 0.0 && size_return->h < 0.0)
840 MESSAGE (0, (stderr, _("%s: warning: font size is negative\n"), program));
841 else if (size_return->w < 0.0)
842 MESSAGE (0, (stderr, _("%s: warning: font width is negative\n"), program));
843 else if (size_return->h < 0.0)
844 MESSAGE (0, (stderr, _("%s: warning: font height is negative\n"),
852 read_font_info (void)
854 CachedFontInfo *font_info;
856 int font_info_cached = 1;
859 unsigned int enc_flags = 0;
863 MESSAGE (2, (stderr, _("reading AFM info for font \"%s\"\n"), Fname));
865 if (accept_composites)
866 enc_flags = AFM_ENCODE_ACCEPT_COMPOSITES;
872 buffer_append (&fkey, Fname);
873 sprintf (buf, "@%f:%d", Fpt.w, encoding);
874 buffer_append (&fkey, buf);
876 if (!strhash_get (afm_info_cache, buffer_ptr (&fkey),
877 strlen (buffer_ptr (&fkey)), (void **) &font_info))
881 /* Couldn't find it from our cache, open open AFM file. */
882 if (!strhash_get (afm_cache, Fname, strlen (Fname), (void **) &font))
884 /* AFM file was not cached, open it from disk. */
885 error = afm_open_font (afm, AFM_I_COMPOSITES, Fname, &font);
886 if (error != AFM_SUCCESS)
888 #define COUR "Courier"
890 * Do not report failures for "Courier*" fonts because
891 * AFM library's default font will fix them.
893 if (strncmp (Fname, COUR, strlen (COUR)) != 0)
896 _("couldn't open AFM file for font \"%s\", using default\n"),
898 error = afm_open_default_font (afm, &font);
899 if (error != AFM_SUCCESS)
901 afm_error_to_string (error, buf);
903 _("couldn't open AFM file for the default font: %s"),
908 /* Apply encoding. */
912 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_1,
917 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_2,
922 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_3,
927 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_4,
932 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_5,
937 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_7,
942 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_9,
946 case ENC_ISO_8859_10:
947 (void) afm_font_encoding (font, AFM_ENCODING_ISO_8859_10,
952 (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
956 /* First apply standard 7bit ASCII encoding. */
957 (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
959 /* Then add those scand characters. */
960 for (i = 0; enc_7bit_ascii_fise[i].name; i++)
961 (void) afm_font_encode (font, enc_7bit_ascii_fise[i].code,
962 enc_7bit_ascii_fise[i].name,
967 /* First apply standard 7bit ASCII encoding. */
968 (void) afm_font_encoding (font, AFM_ENCODING_ASCII, enc_flags);
970 /* Then add those scand characters. */
971 for (i = 0; enc_7bit_ascii_dkno[i].name; i++)
972 (void) afm_font_encode (font, enc_7bit_ascii_dkno[i].code,
973 enc_7bit_ascii_dkno[i].name,
978 (void) afm_font_encoding (font, AFM_ENCODING_IBMPC, enc_flags);
982 (void) afm_font_encoding (font, AFM_ENCODING_MAC, enc_flags);
986 (void) afm_font_encoding (font, AFM_ENCODING_VMS, enc_flags);
990 (void) afm_font_encoding (font, AFM_ENCODING_HP8, enc_flags);
994 (void) afm_font_encoding (font, AFM_ENCODING_KOI8, enc_flags);
998 /* Let's use font's default encoding -- nothing here. */
1002 /* Put it to the AFM cache. */
1003 if (!strhash_put (afm_cache, Fname, strlen (Fname), font, NULL))
1007 font_info = (CachedFontInfo *) xcalloc (1, sizeof (*font_info));
1008 /* Read character widths and types. */
1009 for (i = 0; i < 256; i++)
1013 (void) afm_font_charwidth (font, Fpt.w, i, &w0x, &w0y);
1014 font_info->font_widths[i] = w0x;
1016 if (font->encoding[i] == AFM_ENC_NONE)
1017 font_info->font_ctype[i] = ' ';
1018 else if (font->encoding[i] == AFM_ENC_NON_EXISTENT)
1019 font_info->font_ctype[i] = '.';
1021 font_info->font_ctype[i] = '*';
1024 font_info->font_is_fixed
1025 = font->writing_direction_metrics[0].IsFixedPitch;
1026 font_info->font_bbox_lly = font->global_info.FontBBox_lly;
1029 (void) afm_close_font (font);
1031 /* Store font information to the AFM information cache. */
1032 if (!strhash_put (afm_info_cache, buffer_ptr (&fkey),
1033 strlen (buffer_ptr (&fkey)), font_info, NULL))
1034 font_info_cached = 0;
1037 /* Select character widths and types. */
1038 memcpy (font_widths, font_info->font_widths, 256 * sizeof (double));
1039 memcpy (font_ctype, font_info->font_ctype, 256);
1041 font_is_fixed = font_info->font_is_fixed;
1042 font_bbox_lly = font_info->font_bbox_lly;
1044 if (!font_info_cached)
1047 buffer_uninit (&fkey);
1052 download_font (char *name)
1056 struct stat stat_st;
1058 unsigned char buf[4096];
1063 /* Get font prefix. */
1064 error = afm_font_prefix (afm, name, &prefix);
1065 if (error != AFM_SUCCESS)
1066 /* Font is unknown, nothing to download. */
1069 /* Check if we have a font description file. */
1071 buffer_init (&fname);
1074 buffer_append (&fname, prefix);
1075 buffer_append (&fname, ".pfa");
1076 if (stat (buffer_ptr (&fname), &stat_st) != 0)
1079 buffer_clear (&fname);
1080 buffer_append (&fname, prefix);
1081 buffer_append (&fname, ".pfb");
1082 if (stat (buffer_ptr (&fname), &stat_st) != 0)
1084 /* Couldn't find font description file, nothing to download. */
1085 buffer_uninit (&fname);
1090 /* Ok, fine. Font was found. */
1092 MESSAGE (1, (stderr, _("downloading font \"%s\"\n"), name));
1093 fp = fopen (buffer_ptr (&fname), "rb");
1096 MESSAGE (0, (stderr,
1097 _("couldn't open font description file \"%s\": %s\n"),
1098 buffer_ptr (&fname), strerror (errno)));
1099 buffer_uninit (&fname);
1102 buffer_uninit (&fname);
1105 fprintf (ofp, "%%%%BeginResource: font %s\n", name);
1107 /* Check file type. */
1111 /* Not much to do here. */
1118 unsigned int to_read;
1128 /* Read 6-byte long header. */
1129 i = fread (buf, 1, 6, fp);
1133 chunk = buf[2] | (buf[3] << 8) | (buf[4] << 16) | (buf[5] << 24);
1135 /* Check chunk type. */
1142 to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
1143 i = fread (buf, 1, to_read, fp);
1150 /* Check and fix Mac-newlines. */
1151 for (j = 0; j < i; j++)
1153 if (j == 0 && last_was_cr && buf[0] != '\n')
1156 fputc (buf[0], ofp);
1158 else if (buf[j] == '\r' && j + 1 < i
1159 && buf[j + 1] != '\n')
1163 else if (buf[j] != '\r')
1164 fputc (buf[j], ofp);
1168 last_was_cr = (buf[i - 1] == '\r');
1172 case 2: /* binary data */
1175 to_read = sizeof (buf) < chunk ? sizeof (buf) : chunk;
1176 i = fread (buf, 1, to_read, fp);
1183 for (j = 0; j < i; j++)
1185 fprintf (ofp, "%02X", buf[j]);
1186 if ((j + 1) % 32 == 0)
1187 fprintf (ofp, "\n");
1198 /* Force a linebreak after each chunk. */
1199 fprintf (ofp, "\n");
1206 while ((i = fread (buf, 1, sizeof (buf), fp)) != 0)
1207 fwrite (buf, 1, i, ofp);
1210 fprintf (ofp, "%%%%EndResource\n");
1212 /* Remove font from needed resources. */
1213 (void) strhash_delete (res_fonts, name, strlen (name) + 1, (void **) &cp);
1220 escape_string (char *string)
1226 /* Count the length of the result string. */
1227 for (len = 0, i = 0; string[i]; i++)
1240 /* Create result. */
1241 cp = xmalloc (len + 1);
1242 for (i = 0, j = 0; string[i]; i++)
1252 cp[j++] = string[i];
1263 * Help macros for the format_user_string() function.
1266 #define NEED_NBYTES(n) \
1268 if (rbufpos + (n) >= rbuflen) \
1270 rbuflen += (n) + 1024; \
1271 rbuf = xrealloc (rbuf, rbuflen); \
1275 #define APPEND_CH(ch) \
1278 NEED_NBYTES (width); \
1279 if (width && justification < 0) \
1280 rbuf[rbufpos++] = (ch); \
1281 for (a = 0; a < width - 1; a++) \
1282 rbuf[rbufpos++] = ' '; \
1283 if (!width || justification > 0) \
1284 rbuf[rbufpos++] = (ch); \
1287 #define APPEND_STR(str) \
1289 int len = strlen ((str)); \
1295 nspace = width - len; \
1297 NEED_NBYTES (nspace + len); \
1298 if (width && justification > 0) \
1299 for (; nspace; nspace--) \
1300 rbuf[rbufpos++] = ' '; \
1302 memcpy (rbuf + rbufpos, str, len); \
1305 if (width && justification < 0) \
1306 for (; nspace; nspace--) \
1307 rbuf[rbufpos++] = ' '; \
1311 format_user_string (char *context_name, char *str)
1322 int justification = 1;
1324 /* Format string. */
1325 for (i = 0; str[i] != '\0'; i++)
1331 if (type == '%' || type == '$')
1337 /* Get optional width and justification. */
1343 while (isdigit (str[i]))
1344 width = width * 10 + str[i++] - '0';
1346 /* Handle escapes. */
1349 /* General state related %-escapes. */
1352 case '%': /* `%%' character `%' */
1356 case 'c': /* `%c' trailing component of pwd. */
1357 getcwd (buf, sizeof (buf));
1358 cp = strrchr (buf, '/');
1366 case 'C': /* `%C' runtime in `hh:mm:ss' format */
1367 sprintf (buf, "%02d:%02d:%02d", run_tm.tm_hour,
1368 run_tm.tm_min, run_tm.tm_sec);
1372 case 'd': /* `%d' current working directory */
1373 getcwd (buf, sizeof (buf));
1378 if (str[i + 1] == '{')
1380 /* `%D{}' format run date with strftime() */
1382 j < sizeof (buf2) && str[i] && str[i] != '}';
1387 _("%s: too long format for %%D{} escape"),
1391 strftime (buf, sizeof (buf), buf2, &run_tm);
1395 /* `%D' run date in `yy-mm-dd' format */
1396 sprintf (buf, "%02d-%02d-%02d", run_tm.tm_year % 100,
1397 run_tm.tm_mon + 1, run_tm.tm_mday);
1402 case 'E': /* `%E' run date in `yy/mm/dd' format */
1403 sprintf (buf, "%02d/%02d/%02d", run_tm.tm_year % 100,
1404 run_tm.tm_mon + 1, run_tm.tm_mday);
1408 case 'F': /* `%F' run date in `dd.mm.yyyy' format */
1409 sprintf (buf, "%d.%d.%d",
1412 run_tm.tm_year + 1900);
1416 case 'H': /* `%H' document title */
1420 case 'm': /* `%m' the hostname up to the first `.' */
1421 (void) gethostname (buf, sizeof (buf));
1422 cp = strchr (buf, '.');
1428 case 'M': /* `%M' the full hostname */
1429 (void) gethostname (buf, sizeof (buf));
1433 case 'n': /* `%n' username */
1434 APPEND_STR (passwd->pw_name);
1437 case 'N': /* `%N' pw_gecos up to the first `,' char */
1438 strcpy (buf, passwd->pw_gecos);
1439 cp = strchr (buf, ',');
1445 case 't': /* `%t' runtime in 12-hour am/pm format */
1446 sprintf (buf, "%d:%d%s",
1448 ? run_tm.tm_hour - 12 : run_tm.tm_hour,
1450 run_tm.tm_hour > 12 ? "pm" : "am");
1454 case 'T': /* `%T' runtime in 24-hour format */
1455 sprintf (buf, "%d:%d", run_tm.tm_hour, run_tm.tm_min);
1459 case '*': /* `%*' runtime in 24-hour format with secs */
1460 sprintf (buf, "%d:%d:%d", run_tm.tm_hour, run_tm.tm_min,
1465 case 'W': /* `%W' run date in `mm/dd/yy' format */
1466 sprintf (buf, "%02d/%02d/%02d", run_tm.tm_mon + 1,
1467 run_tm.tm_mday, run_tm.tm_year % 100);
1472 FATAL ((stderr, _("%s: unknown `%%' escape `%c' (%d)"),
1473 context_name, str[i], str[i]));
1479 /* Input file related $-escapes. */
1482 case '$': /* `$$' character `$' */
1486 case '%': /* `$%' current page number */
1488 sprintf (buf, "%d%c", current_pagenum, slice - 1 + 'A');
1490 sprintf (buf, "%d", current_pagenum);
1494 case '=': /* `$=' number of pages in this file */
1498 case 'p': /* `$p' number of pages processed so far */
1499 sprintf (buf, "%d", total_pages);
1503 case '(': /* $(ENVVAR) */
1505 str[i] && str[i] != ')' && j < sizeof (buf) - 1;
1510 FATAL ((stderr, _("%s: no closing ')' for $() escape"),
1513 FATAL ((stderr, _("%s: too long variable name for $() escape"),
1524 case 'C': /* `$C' modtime in `hh:mm:ss' format */
1525 sprintf (buf, "%02d:%02d:%02d", mod_tm.tm_hour,
1526 mod_tm.tm_min, mod_tm.tm_sec);
1531 if (str[i + 1] == '{')
1533 /* `$D{}' format modification date with strftime() */
1535 j < sizeof (buf2) && str[i] && str[i] != '}';
1540 _("%s: too long format for $D{} escape"),
1544 strftime (buf, sizeof (buf), buf2, &mod_tm);
1548 /* `$D' mod date in `yy-mm-dd' format */
1549 sprintf (buf, "%02d-%02d-%02d", mod_tm.tm_year % 100,
1550 mod_tm.tm_mon + 1, mod_tm.tm_mday);
1555 case 'E': /* `$E' mod date in `yy/mm/dd' format */
1556 sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_year % 100,
1557 mod_tm.tm_mon + 1, mod_tm.tm_mday);
1561 case 'F': /* `$F' run date in `dd.mm.yyyy' format */
1562 sprintf (buf, "%d.%d.%d",
1565 mod_tm.tm_year + 1900);
1569 case 't': /* `$t' runtime in 12-hour am/pm format */
1570 sprintf (buf, "%d:%d%s",
1572 ? mod_tm.tm_hour - 12 : mod_tm.tm_hour,
1574 mod_tm.tm_hour > 12 ? "pm" : "am");
1578 case 'T': /* `$T' runtime in 24-hour format */
1579 sprintf (buf, "%d:%d", mod_tm.tm_hour, mod_tm.tm_min);
1583 case '*': /* `$*' runtime in 24-hour format with secs */
1584 sprintf (buf, "%d:%d:%d", mod_tm.tm_hour, mod_tm.tm_min,
1589 case 'v': /* `$v': input file number */
1590 sprintf (buf, "%d", input_filenum);
1594 case 'V': /* `$V': input file number in --toc format */
1597 sprintf (buf, "%d-", input_filenum);
1602 case 'W': /* `$W' run date in `mm/dd/yy' format */
1603 sprintf (buf, "%02d/%02d/%02d", mod_tm.tm_mon + 1,
1604 mod_tm.tm_mday, mod_tm.tm_year % 100);
1608 case 'N': /* `$N' the full name of the printed file */
1612 case 'n': /* `$n' input file name without directory */
1613 cp = strrchr (fname, '/');
1621 case 'L': /* `$L' number of lines in this file. */
1622 /* This is valid only for TOC-strings. */
1623 sprintf (buf, "%d", current_file_linenum - 1);
1628 FATAL ((stderr, _("%s: unknown `$' escape `%c' (%d)"),
1629 context_name, str[i], str[i]));
1633 /* Reset width so the else-arm goes ok at the next round. */
1642 /* Escape PS specials. */
1643 cp = escape_string (rbuf);
1651 parse_key_value_pair (StringHashPtr set, char *kv)
1656 cp = strchr (kv, ':');
1659 if (strhash_delete (set, kv, strlen (kv) + 1, (void **) &cp))
1665 buffer_append_len (&key, kv, cp - kv);
1667 strhash_put (set, buffer_ptr (&key), strlen (buffer_ptr (&key)) + 1,
1668 xstrdup (cp + 1), (void **) &cp);
1672 buffer_uninit (&key);
1678 count_key_value_set (StringHashPtr set)
1684 for (got = strhash_get_first (set, &cp, &j, &value); got;
1685 got = strhash_get_next (set, &cp, &j, &value))
1693 pathwalk (char *path, PathWalkProc proc, void *context)
1700 for (cp = path; cp; cp = strchr (cp, PATH_SEPARATOR))
1705 cp2 = strchr (cp, PATH_SEPARATOR);
1711 memcpy (buf, cp, len);
1714 i = (*proc) (buf, context);
1724 file_lookup (char *path, void *context)
1727 FileLookupCtx *ctx = context;
1728 struct stat stat_st;
1731 MESSAGE (2, (stderr, "file_lookup(): %s/%s%s\t", path, ctx->name,
1734 len = strlen (path);
1735 if (len && path[len - 1] == '/')
1738 buffer_clear (ctx->fullname);
1739 buffer_append_len (ctx->fullname, path, len);
1740 buffer_append (ctx->fullname, "/");
1741 buffer_append (ctx->fullname, ctx->name);
1742 buffer_append (ctx->fullname, ctx->suffix);
1744 i = stat (buffer_ptr (ctx->fullname), &stat_st) == 0;
1746 MESSAGE (2, (stderr, "#%c\n", i ? 't' : 'f'));
1753 tilde_subst (char *fname)
1757 struct passwd *pswd;
1761 if (fname[0] != '~')
1762 return xstrdup (fname);
1764 if (fname[1] == '/' || fname[1] == '\0')
1766 /* The the user's home directory from the `HOME' environment
1768 cp = getenv ("HOME");
1770 return xstrdup (fname);
1772 buffer_init (&buffer);
1773 buffer_append (&buffer, cp);
1774 buffer_append (&buffer, fname + 1);
1776 result = buffer_copy (&buffer);
1777 buffer_uninit (&buffer);
1782 /* Get user's login name. */
1783 for (i = 1; fname[i] && fname[i] != '/'; i++)
1786 buffer_init (&buffer);
1787 buffer_append_len (&buffer, fname + 1, i - 1);
1789 pswd = getpwnam (buffer_ptr (&buffer));
1790 buffer_uninit (&buffer);
1794 /* Found passwd entry. */
1795 buffer_init (&buffer);
1796 buffer_append (&buffer, pswd->pw_dir);
1797 buffer_append (&buffer, fname + i);
1799 result = buffer_copy (&buffer);
1800 buffer_uninit (&buffer);
1805 /* No match found. */
1806 return xstrdup (fname);
1811 parse_float (char *string, int units, int horizontal)
1816 val = strtod (string, &end);
1819 ERROR ((stderr, _("malformed float dimension: \"%s\""), string));
1841 val *= CHAR_WIDTH ('m');
1847 goto malformed_float;
1854 goto malformed_float;
1862 * InputStream functions.
1866 is_open (InputStream *is, FILE *fp, char *fname, char *input_filter)
1868 /* Init stream variables. */
1869 is->data_in_buf = 0;
1872 is->unget_ch = NULL;
1874 is->unget_alloc = 0;
1886 fname = input_filter_stdin;
1889 * Count the initial command length, this will grow dynamically
1890 * when file specifier `%s' is encountered from <input_filter>.
1892 cmdlen = strlen (input_filter) + 1;
1893 cmd = xmalloc (cmdlen);
1895 /* Create filter command. */
1897 for (i = 0; input_filter[i]; i++)
1899 if (input_filter[i] == '%')
1901 switch (input_filter[i + 1])
1904 /* Expand cmd-buffer. */
1905 cmdlen += strlen (fname);
1906 cmd = xrealloc (cmd, cmdlen);
1908 /* Paste filename. */
1909 strcpy (cmd + pos, fname);
1910 pos += strlen (fname);
1921 cmd[pos++] = input_filter[i];
1926 cmd[pos++] = input_filter[i];
1930 is->fp = popen (cmd, "r");
1936 _("couldn't open input filter \"%s\" for file \"%s\": %s"),
1937 input_filter, fname ? fname : "(stdin)",
1944 /* Just open the stream. */
1950 is->fp = fopen (fname, "rb");
1953 ERROR ((stderr, _("couldn't open input file \"%s\": %s"), fname,
1965 is_close (InputStream *is)
1973 xfree (is->unget_ch);
1978 is_getc (InputStream *is)
1982 if (is->unget_pos > 0)
1984 ch = is->unget_ch[--is->unget_pos];
1990 /* Do we have any data left? */
1991 if (is->bufpos >= is->data_in_buf)
1994 if (is->nreads > 0 && is->data_in_buf < sizeof (is->buf))
1998 /* Read more data. */
1999 is->data_in_buf = fread (is->buf, 1, sizeof (is->buf), is->fp);
2006 return is->buf[is->bufpos++];
2011 is_ungetc (int ch, InputStream *is)
2013 if (is->unget_pos >= is->unget_alloc)
2015 is->unget_alloc += 1024;
2016 is->unget_ch = xrealloc (is->unget_ch, is->unget_alloc);
2019 is->unget_ch[is->unget_pos++] = ch;
2030 buffer_init (Buffer *buffer)
2032 buffer->allocated = 128;
2033 buffer->data = xmalloc (buffer->allocated);
2034 buffer->data[0] = '\0';
2040 buffer_uninit (Buffer *buffer)
2042 xfree (buffer->data);
2049 Buffer *buffer = (Buffer *) xcalloc (1, sizeof (Buffer));
2051 buffer_init (buffer);
2058 buffer_free (Buffer *buffer)
2060 buffer_uninit (buffer);
2066 buffer_append (Buffer *buffer, const char *data)
2068 buffer_append_len (buffer, data, strlen (data));
2073 buffer_append_len (Buffer *buffer, const char *data, size_t len)
2075 if (buffer->len + len + 1 >= buffer->allocated)
2077 buffer->allocated = buffer->len + len + 1024;
2078 buffer->data = xrealloc (buffer->data, buffer->allocated);
2081 memcpy (buffer->data + buffer->len, data, len);
2084 buffer->data[buffer->len] = '\0';
2089 buffer_copy (Buffer *buffer)
2091 char *copy = xmalloc (buffer->len + 1);
2093 memcpy (copy, buffer->data, buffer->len + 1);
2100 buffer_clear (Buffer *buffer)
2103 buffer->data[0] = '\0';
2108 buffer_ptr (Buffer *buffer)
2110 return buffer->data;
2115 buffer_len (Buffer *buffer)