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