LCOV - code coverage report
Current view: top level - utils/pfbtops - pfbtops.c (source / functions) Hit Total Coverage
Test: GNU roff Lines: 83 152 54.6 %
Date: 2026-01-16 17:51:41 Functions: 3 6 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1992-2025 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : /* This translates ps fonts in .pfb format to ASCII ps files. */
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include <config.h>
      23             : #endif
      24             : 
      25             : #include <assert.h> // assert()
      26             : #include <errno.h> // errno
      27             : #include <stdio.h>
      28             : #include <stdlib.h> // exit(), EXIT_FAILURE, EXIT_SUCCESS
      29             : #include <string.h> // strerror()
      30             : #include <limits.h>
      31             : 
      32             : #include <getopt.h>
      33             : 
      34             : #include "nonposix.h"
      35             : 
      36             : /* Binary bytes per output line. */
      37             : #define BYTES_PER_LINE (64/2)
      38             : #define MAX_LINE_LENGTH 78
      39             : #define HEX_DIGITS "0123456789abcdef"
      40             : 
      41             : extern const char *Version_string;
      42             : 
      43             : static char *program_name;
      44             : 
      45           0 : static void error(const char *s)
      46             : {
      47           0 :   (void) fprintf(stderr, "%s: error: %s\n", program_name, s);
      48           0 : }
      49             : 
      50           0 : static void die(const char *s)
      51             : {
      52           0 :   error(s);
      53           0 :   exit(EXIT_FAILURE);
      54             : }
      55             : 
      56           0 : static void usage(FILE *stream)
      57             : {
      58           0 :   (void) fprintf(stream, "usage: %s [pfb-file]\n"
      59             :                  "usage: %s {-v | --version}\n"
      60             :                  "usage: %s --help\n",
      61             :           program_name, program_name, program_name);
      62           0 :   if (stdout == stream)
      63           0 :     fputs("\n"
      64             : "Translate a PostScript Type 1 font in Printer Font Binary (PFB) format"
      65             : "\n"
      66             : "to Printer Font ASCII (PFA) format.  See the pfbtops(1) manual page."
      67             : "\n",
      68             :           stream);
      69           0 : }
      70             : 
      71           2 : static void get_text(int n)
      72             : {
      73           2 :   int c = 0, c1;
      74           2 :   int in_string = 0;
      75           2 :   int is_comment = 0;
      76           2 :   int count = 0;
      77             : 
      78        2215 :   while (--n >= 0) {
      79        2213 :     c = getchar();
      80        2213 :     if (c == '(' && !is_comment)
      81           5 :       in_string++;
      82        2208 :     else if (c == ')' && !is_comment)
      83           5 :       in_string--;
      84        2203 :     else if (c == '%' && !in_string)
      85          19 :       is_comment = 1;
      86        2184 :     else if (c == '\\' && in_string) {
      87           1 :       count++;
      88           1 :       putchar(c);
      89           1 :       if (n-- == 0)
      90           0 :         break;
      91           1 :       c = getchar();
      92             :       /* don't split octal character representations */
      93           1 :       if (c >= '0' && c <= '7') {
      94           1 :         count++;
      95           1 :         putchar(c);
      96           1 :         if (n-- == 0)
      97           0 :           break;
      98           1 :         c = getchar();
      99           1 :         if (c >= '0' && c <= '7') {
     100           1 :           count++;
     101           1 :           putchar(c);
     102           1 :           if (n-- == 0)
     103           0 :             break;
     104           1 :           c = getchar();
     105           1 :           if (c >= '0' && c <= '7') {
     106           1 :             count++;
     107           1 :             putchar(c);
     108           1 :             if (n-- == 0)
     109           0 :               break;
     110           1 :             c = getchar();
     111             :           }
     112             :         }
     113             :       }
     114             :     }
     115        2213 :     if (c == EOF)
     116           0 :       die("end of file in text packet");
     117        2213 :     else if (c == '\r') {
     118           0 :       if (n-- == 0)
     119           0 :         break;
     120           0 :       c1 = getchar();
     121           0 :       if (c1 != '\n') {
     122           0 :         ungetc(c1, stdin);
     123           0 :         n++;
     124             :       }
     125           0 :       c = '\n';
     126             :     }
     127        2213 :     if (c == '\n') {
     128          73 :       count = 0;
     129          73 :       is_comment = 0;
     130             :     }
     131        2140 :     else if (count >= MAX_LINE_LENGTH) {
     132           1 :       if (in_string > 0) {
     133           1 :         count = 1;
     134           1 :         putchar('\\');
     135           1 :         putchar('\n');
     136             :       }
     137           0 :       else if (is_comment) {
     138           0 :         count = 2;
     139           0 :         putchar('\n');
     140           0 :         putchar('%');
     141             :       }
     142             :       else {
     143             :         /* split at the next whitespace character */
     144           0 :         while (c != ' ' && c != '\t' && c != '\f') {
     145           0 :           putchar(c);
     146           0 :           if (n-- == 0)
     147           0 :             break;  
     148           0 :           c = getchar();
     149             :         }
     150           0 :         count = 0;
     151           0 :         putchar('\n');
     152           0 :         continue;
     153             :       }
     154             :     }
     155        2213 :     count++;
     156        2213 :     putchar(c);
     157             :   }
     158           2 :   if (c != '\n')
     159           0 :     putchar('\n');
     160           2 : }
     161             : 
     162           1 : static void get_binary(int n)
     163             : {
     164             :   int c;
     165           1 :   int count = 0;
     166             : 
     167       12157 :   while (--n >= 0) {
     168       12156 :     c = getchar();
     169       12156 :     if (c == EOF)
     170           0 :       die("end of file in binary packet");
     171       12156 :     if (count >= BYTES_PER_LINE) {
     172         379 :       putchar('\n');
     173         379 :       count = 0;
     174             :     }
     175       12156 :     count++;
     176       12156 :     putchar(HEX_DIGITS[(c >> 4) & 0xf]);
     177       12156 :     putchar(HEX_DIGITS[c & 0xf]);
     178             :   }
     179           1 :   putchar('\n');
     180           1 : }
     181             : 
     182           1 : int main(int argc, char **argv)
     183             : {
     184             :   int opt;
     185             :   static const struct option long_options[] = {
     186             :     { "help", no_argument, NULL, CHAR_MAX + 1 },
     187             :     { "version", no_argument, NULL, 'v' },
     188             :     { NULL, 0, NULL, 0 }
     189             :   };
     190             : 
     191           1 :   program_name = argv[0];
     192             : 
     193           1 :   while ((opt = getopt_long(argc, argv, ":v", long_options, NULL))
     194             :          != EOF) {
     195           0 :     switch (opt) {
     196           0 :     case 'v':
     197           0 :       printf("GNU pfbtops (groff) version %s\n", Version_string);
     198           0 :       exit(EXIT_SUCCESS);
     199             :       break;
     200           0 :     case CHAR_MAX + 1: /* --help */
     201           0 :       usage(stdout);
     202           0 :       exit(EXIT_SUCCESS);
     203             :       break;
     204           0 :     case '?':
     205           0 :       if (optopt != 0) {
     206           0 :         char errbuf[] = "unrecognized command-line option 'X'";
     207           0 :         errbuf[(strchr(errbuf, 'X') - errbuf)] = optopt;
     208           0 :         error(errbuf);
     209             :       }
     210             :       else
     211             :         // We can't use `error()` because we have no idea how big the
     212             :         // user-specified long option is.
     213           0 :         (void) fprintf(stderr, "%s: error: unrecognized command-line"
     214             :                        " option '%s'\n", program_name,
     215           0 :                        argv[(optind - 1)]);
     216           0 :       usage(stderr);
     217           0 :       exit(2);
     218             :       break;
     219             :     // in case we ever accept options that take arguments
     220           0 :     case ':':
     221           0 :       fprintf(stderr, "%s: error: command-line option '%c' requires an"
     222           0 :               " argument\n", program_name, (char) optopt);
     223           0 :       usage(stderr);
     224           0 :       exit(2);
     225             :       break;
     226           0 :     default:
     227           0 :       assert(0 == "unhandled case of command-line option");
     228             :     }
     229             :   }
     230             : 
     231           1 :   if (argc - optind > 1) {
     232           0 :     usage(stderr);
     233           0 :     exit(2);
     234             :   }
     235           1 :   const char *file = argv[optind];
     236           1 :   if (argc > optind && !freopen(file, "r", stdin)) {
     237           0 :     fprintf(stderr, "%s: error: unable to open file '%s': %s\n",
     238           0 :             program_name, file, strerror(errno));
     239           0 :     exit(EXIT_FAILURE);
     240             :   }
     241             :   SET_BINARY(fileno(stdin));
     242           3 :   for (;;) {
     243             :     int type, c, i;
     244             :     long n;
     245             : 
     246           4 :     c = getchar();
     247           4 :     if (c != 0x80)
     248           0 :       die("first byte of packet not 0x80");
     249           4 :     type = getchar();
     250           4 :     if (type == 3)
     251           1 :       break;
     252           3 :     if (type != 1 && type != 2)
     253           0 :       die("bad packet type");
     254           3 :     n = 0;
     255          15 :     for (i = 0; i < 4; i++) {
     256          12 :       c = getchar();
     257          12 :       if (c == EOF)
     258           0 :         die("end of file in packet header");
     259          12 :       n |= (long)c << (i << 3);
     260             :     }
     261           3 :     if (n < 0)
     262           0 :       die("negative packet length");
     263           3 :     if (type == 1)
     264           2 :       get_text(n);
     265             :     else
     266           1 :       get_binary(n);
     267             :   }
     268           1 :   exit(EXIT_SUCCESS);
     269             : }
     270             : 
     271             : // Local Variables:
     272             : // fill-column: 72
     273             : // mode: C
     274             : // End:
     275             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14