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