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