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 }