diff options
author | Peter Breitenlohner <peb@mppmu.mpg.de> | 2009-11-10 10:22:31 +0000 |
---|---|---|
committer | Peter Breitenlohner <peb@mppmu.mpg.de> | 2009-11-10 10:22:31 +0000 |
commit | 25078167f4333fa5acc0aa69a98490913be13433 (patch) | |
tree | 9e3035926ca53ebde75ffba4dd486411c13e81bb /Build/source/utils/t1utils/t1utils-1.34/t1asm.c | |
parent | 2caa63d64f99a64f64c8c4a5260477f761052eb4 (diff) |
towards TL2010: utils/t1utils
git-svn-id: svn://tug.org/texlive/trunk@15958 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/utils/t1utils/t1utils-1.34/t1asm.c')
-rw-r--r-- | Build/source/utils/t1utils/t1utils-1.34/t1asm.c | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/Build/source/utils/t1utils/t1utils-1.34/t1asm.c b/Build/source/utils/t1utils/t1utils-1.34/t1asm.c new file mode 100644 index 00000000000..eeb12127a2a --- /dev/null +++ b/Build/source/utils/t1utils/t1utils-1.34/t1asm.c @@ -0,0 +1,808 @@ +/* t1asm -*- c-basic-offset: 2 -*- + * + * This program `assembles' Adobe Type-1 font programs in pseudo-PostScript + * form into either PFB or PFA format. The human readable/editable input is + * charstring- and eexec-encrypted as specified in the `Adobe Type 1 Font + * Format' version 1.1 (the `black book'). There is a companion program, + * t1disasm, which `disassembles' PFB and PFA files into a pseudo-PostScript + * file. + * + * Copyright (c) 1992 by I. Lee Hetherington, all rights reserved. + * + * Permission is hereby granted to use, modify, and distribute this program + * for any purpose provided this copyright notice and the one below remain + * intact. + * + * I. Lee Hetherington (ilh@lcs.mit.edu) + * + * 1.5 and later versions contain changes by, and are maintained by, + * Eddie Kohler <ekohler@gmail.com>. + * + * New change log in `NEWS'. Old change log: + * + * Revision 1.4 92/07/10 10:53:09 ilh + * Added support for additional PostScript after the closefile command + * (ie., some fonts have {restore}if after the cleartomark). + * + * Revision 1.3 92/06/23 10:58:25 ilh + * MSDOS porting by Kai-Uwe Herbing (herbing@netmbx.netmbx.de) + * incoporated. + * + * Revision 1.2 92/05/22 11:54:45 ilh + * Fixed bug where integers larger than 32000 could not be encoded in + * charstrings. Now integer range is correct for four-byte + * twos-complement integers: -(1<<31) <= i <= (1<<31)-1. Bug detected by + * Piet Tutelaers (rcpt@urc.tue.nl). + * + * Revision 1.1 92/05/22 11:48:46 ilh + * initial version + * + * Ported to Microsoft C/C++ Compiler and MS-DOS operating system by + * Kai-Uwe Herbing (herbing@netmbx.netmbx.de) on June 12, 1992. Code + * specific to the MS-DOS version is encapsulated with #ifdef _MSDOS + * ... #endif, where _MSDOS is an identifier, which is automatically + * defined, if you compile with the Microsoft C/C++ Compiler. + * + */ + +/* Note: this is ANSI C. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#if defined(_MSDOS) || defined(_WIN32) +# include <fcntl.h> +# include <io.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <stdarg.h> +#include <errno.h> +#include <lcdf/clp.h> +#include "t1lib.h" + +#define LINESIZE 512 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char byte; + +static FILE *ifp; +static FILE *ofp; +static struct pfb_writer w; +static int blocklen = -1; + +/* flags */ +static int pfb = 1; +static int active = 0; +static int ever_active = 0; +static int start_charstring = 0; +static int in_eexec = 0; + +/* need to add 1 as space for \0 */ +static char line[LINESIZE + 1]; + +/* lenIV and charstring start command */ +static int lenIV = 4; +static char cs_start[10]; + +/* for charstring buffering */ +static byte charstring_buf[65535]; +static byte *charstring_bp; + +/* decryption stuff */ +static uint16_t er, cr; +static uint16_t c1 = 52845, c2 = 22719; + +/* table of charstring commands */ +static struct command { + const char *name; + int one, two; +} command_table[] = { + { "abs", 12, 9 }, /* Type 2 */ + { "add", 12, 10 }, /* Type 2 */ + { "and", 12, 3 }, /* Type 2 */ + { "blend", 16, -1 }, /* Type 2 */ + { "callgsubr", 29, -1 }, /* Type 2 */ + { "callothersubr", 12, 16 }, /* Type 1 ONLY */ + { "callsubr", 10, -1 }, + { "closepath", 9, -1 }, /* Type 1 ONLY */ + { "cntrmask", 20, -1 }, /* Type 2 */ + { "div", 12, 12 }, + { "dotsection", 12, 0 }, /* Type 1 ONLY */ + { "drop", 12, 18 }, /* Type 2 */ + { "dup", 12, 27 }, /* Type 2 */ + { "endchar", 14, -1 }, + { "eq", 12, 15 }, /* Type 2 */ + { "error", 0, -1 }, /* special */ + { "escape", 12, -1 }, /* special */ + { "exch", 12, 28 }, /* Type 2 */ + { "flex", 12, 35 }, /* Type 2 */ + { "flex1", 12, 37 }, /* Type 2 */ + { "get", 12, 21 }, /* Type 2 */ + { "hflex", 12, 34 }, /* Type 2 */ + { "hflex1", 12, 36 }, /* Type 2 */ + { "hhcurveto", 27, -1 }, /* Type 2 */ + { "hintmask", 19, -1 }, /* Type 2 */ + { "hlineto", 6, -1 }, + { "hmoveto", 22, -1 }, + { "hsbw", 13, -1 }, /* Type 1 ONLY */ + { "hstem", 1, -1 }, + { "hstem3", 12, 2 }, /* Type 1 ONLY */ + { "hstemhm", 18, -1 }, /* Type 2 */ + { "hvcurveto", 31, -1 }, + { "ifelse", 12, 22 }, /* Type 2 */ + { "index", 12, 29 }, /* Type 2 */ + { "load", 12, 13 }, /* Type 2 */ + { "mul", 12, 24 }, /* Type 2 */ + { "neg", 12, 14 }, /* Type 2 */ + { "not", 12, 5 }, /* Type 2 */ + { "or", 12, 4 }, /* Type 2 */ + { "pop", 12, 17 }, /* Type 1 ONLY */ + { "put", 12, 20 }, /* Type 2 */ + { "random", 12, 23 }, /* Type 2 */ + { "rcurveline", 24, -1 }, /* Type 2 */ + { "return", 11, -1 }, + { "rlinecurve", 25, -1 }, /* Type 2 */ + { "rlineto", 5, -1 }, + { "rmoveto", 21, -1 }, + { "roll", 12, 30 }, /* Type 2 */ + { "rrcurveto", 8, -1 }, + { "sbw", 12, 7 }, /* Type 1 ONLY */ + { "seac", 12, 6 }, /* Type 1 ONLY */ + { "setcurrentpoint", 12, 33 }, /* Type 1 ONLY */ + { "sqrt", 12, 26 }, /* Type 2 */ + { "store", 12, 8 }, /* Type 2 */ + { "sub", 12, 11 }, /* Type 2 */ + { "vhcurveto", 30, -1 }, + { "vlineto", 7, -1 }, + { "vmoveto", 4, -1 }, + { "vstem", 3, -1 }, + { "vstem3", 12, 1 }, /* Type 1 ONLY */ + { "vstemhm", 23, -1 }, /* Type 2 */ + { "vvcurveto", 26, -1 }, /* Type 2 */ +}; /* alphabetical */ + +/* Two separate encryption functions because eexec and charstring encryption + must proceed in parallel. */ + +static byte eencrypt(byte plain) +{ + byte cipher; + + cipher = (byte)(plain ^ (er >> 8)); + er = (uint16_t)((cipher + er) * c1 + c2); + return cipher; +} + +static byte cencrypt(byte plain) +{ + byte cipher; + + /* Thanks to Tom Kacvinsky <tjk@ams.org> who reported that lenIV == -1 means + unencrypted charstrings. */ + if (lenIV < 0) return plain; + + cipher = (byte)(plain ^ (cr >> 8)); + cr = (uint16_t)((cipher + cr) * c1 + c2); + return cipher; +} + +/* This function outputs a single byte. If output is in PFB format then output + is buffered through blockbuf[]. If output is in PFA format, then output + will be hexadecimal if in_eexec is set, ASCII otherwise. */ + +static void output_byte(byte b) +{ + static const char *hexchar = "0123456789abcdef"; + static int hexcol = 0; + + if (pfb) { + /* PFB */ + PFB_OUTPUT_BYTE(&w, b); + } else { + /* PFA */ + if (in_eexec) { + /* trim hexadecimal lines to `blocklen' columns */ + if (hexcol >= blocklen) { + putc('\n', ofp); + hexcol = 0; + } + putc(hexchar[(b >> 4) & 0xf], ofp); + putc(hexchar[b & 0xf], ofp); + hexcol += 2; + } else { + putc(b, ofp); + } + } +} + +/* This function outputs a byte through possible eexec encryption. */ + +static void eexec_byte(byte b) +{ + if (in_eexec) + output_byte(eencrypt(b)); + else + output_byte(b); +} + +/* This function outputs a null-terminated string through possible eexec + encryption. */ + +static void eexec_string(const char *string) +{ + while (*string) + eexec_byte(*string++); +} + +/* This function gets ready for the eexec-encrypted data. If output is in + PFB format then flush current ASCII block and get ready for binary block. + We start encryption with four random (zero) bytes. */ + +static void eexec_start(char *string) +{ + eexec_string("currentfile eexec\n"); + if (pfb && w.blocktyp != PFB_BINARY) { + pfb_writer_output_block(&w); + w.blocktyp = PFB_BINARY; + } + + in_eexec = 1; + er = 55665; + eexec_byte(0); + eexec_byte(0); + eexec_byte(0); + eexec_byte(0); + eexec_string(string); +} + +/* 25.Aug.1999 -- Return 1 if this line actually looks like the start of a + charstring. We use the heuristic that it should start with `/' (a name) or + `dup' (a subroutine). Previous heuristic caused killa bad output. */ + +static int check_line_charstring(void) +{ + char *p = line; + while (isspace(*p)) + p++; + return (*p == '/' || (p[0] == 'd' && p[1] == 'u' && p[2] == 'p')); +} + +/* This function returns an input line of characters. A line is terminated by + length (including terminating null) greater than LINESIZE, \r, \n, \r\n, or + when active (looking for charstrings) by '{'. When terminated by a newline + the newline is put into line[]. When terminated by '{', the '{' is not put + into line[], and the flag start_charstring is set to 1. */ + +static void texlive_getline(void) +{ + int c; + char *p = line; + int comment = 0; + start_charstring = 0; + + while (p < line + LINESIZE) { + c = getc(ifp); + + if (c == EOF) + break; + else if (c == '%') + comment = 1; + else if (active && !comment && c == '{') { + /* 25.Aug.1999 -- new check for whether we should stop be active */ + if (check_line_charstring()) { + start_charstring = 1; + break; + } else + active = 0; + } + + *p++ = (char) c; + + /* end of line processing: change CR or CRLF into LF, and exit */ + if (c == '\r') { + c = getc(ifp); + if (c != '\n') + ungetc(c, ifp); + p[-1] = '\n'; + break; + } else if (c == '\n') + break; + } + + *p = '\0'; +} + +/* This function wraps-up the eexec-encrypted data and writes ASCII trailer. + If output is in PFB format then this entails flushing binary block and + starting an ASCII block. */ + +static void eexec_end(void) +{ + int i, j; + + if (!pfb) + putc('\n', ofp); + else if (w.blocktyp != PFB_ASCII) { + pfb_writer_output_block(&w); + w.blocktyp = PFB_ASCII; + } + + in_eexec = active = 0; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 64; j++) + eexec_byte('0'); + eexec_byte('\n'); + } +} + +/* This function is used by the binary search, bsearch(), for command names in + the command table. */ + +static int CDECL command_compare(const void *key, const void *item) +{ + return strcmp((const char *) key, ((const struct command *) item)->name); +} + +/* This function returns 1 if the string is an integer and 0 otherwise. */ + +static int is_integer(char *string) +{ + if (isdigit(string[0]) || string[0] == '-' || string[0] == '+') { + while (*++string && isdigit(*string)) + ; /* deliberately empty */ + if (!*string) + return 1; + } + return 0; +} + +/* This function initializes charstring encryption. Note that this is called + at the beginning of every charstring. */ + +static void charstring_start(void) +{ + int i; + + charstring_bp = charstring_buf; + cr = 4330; + for (i = 0; i < lenIV; i++) + *charstring_bp++ = cencrypt((byte) 0); +} + +/* This function encrypts and buffers a single byte of charstring data. */ + +static void charstring_byte(int v) +{ + byte b = (byte)(v & 0xff); + if (charstring_bp - charstring_buf > (int)sizeof(charstring_buf)) + fatal_error("charstring buffer overflow"); + *charstring_bp++ = cencrypt(b); +} + +/* This function outputs buffered, encrypted charstring data through possible + eexec encryption. */ + +static void charstring_end(void) +{ + byte *bp; + + sprintf(line, "%d ", (int) (charstring_bp - charstring_buf)); + eexec_string(line); + sprintf(line, "%s ", cs_start); + eexec_string(line); + for (bp = charstring_buf; bp < charstring_bp; bp++) + eexec_byte(*bp); +} + +/* This function generates the charstring representation of an integer. */ + +static void charstring_int(int num) +{ + int x; + + if (num >= -107 && num <= 107) { + charstring_byte(num + 139); + } else if (num >= 108 && num <= 1131) { + x = num - 108; + charstring_byte(x / 256 + 247); + charstring_byte(x % 256); + } else if (num >= -1131 && num <= -108) { + x = abs(num) - 108; + charstring_byte(x / 256 + 251); + charstring_byte(x % 256); + } else if (num >= (-2147483647-1) && num <= 2147483647) { + charstring_byte(255); + charstring_byte(num >> 24); + charstring_byte(num >> 16); + charstring_byte(num >> 8); + charstring_byte(num); + } else { + error("can't format huge number `%d'", num); + /* output 0 instead */ + charstring_byte(139); + } +} + +/* This function returns one charstring token. It ignores comments. */ + +static void get_charstring_token(void) +{ + int c = getc(ifp); + while (isspace(c)) + c = getc(ifp); + + if (c == '%') { + while (c != EOF && c != '\r' && c != '\n') + c = getc(ifp); + get_charstring_token(); + + } else if (c == '}') { + line[0] = '}'; + line[1] = 0; + + } else { + char *p = line; + while (p < line + LINESIZE) { + *p++ = c; + c = getc(ifp); + if (c == EOF || isspace(c) || c == '%' || c == '}') { + ungetc(c, ifp); + break; + } + } + *p = 0; + } +} + + +/* This function parses an entire charstring into integers and commands, + outputting bytes through the charstring buffer. */ + +static void parse_charstring(void) +{ + struct command *cp; + + charstring_start(); + while (!feof(ifp)) { + get_charstring_token(); + if (line[0] == '}') + break; + if (is_integer(line)) { + charstring_int(atoi(line)); + } else { + int one; + int two; + int ok = 0; + + cp = (struct command *) + bsearch((void *) line, (void *) command_table, + sizeof(command_table) / sizeof(struct command), + sizeof(struct command), + command_compare); + + if (cp) { + one = cp->one; + two = cp->two; + ok = 1; + + } else if (strncmp(line, "escape_", 7) == 0) { + /* Parse the `escape' keyword requested by Lee Chun-Yu and Werner + Lemberg */ + one = 12; + if (sscanf(line + 7, "%d", &two) == 1) + ok = 1; + + } else if (strncmp(line, "UNKNOWN_", 8) == 0) { + /* Allow unanticipated UNKNOWN commands. */ + one = 12; + if (sscanf(line + 8, "12_%d", &two) == 1) + ok = 1; + else if (sscanf(line + 8, "%d", &one) == 1) { + two = -1; + ok = 1; + } + } + + if (!ok) + error("unknown charstring command `%s'", line); + else if (one < 0 || one > 255) + error("bad charstring command number `%d'", one); + else if (two > 255) + error("bad charstring command number `%d'", two); + else if (two < 0) + charstring_byte(one); + else { + charstring_byte(one); + charstring_byte(two); + } + } + } + charstring_end(); +} + + +/***** + * Command line + **/ + +#define BLOCK_LEN_OPT 300 +#define OUTPUT_OPT 301 +#define VERSION_OPT 302 +#define HELP_OPT 303 +#define PFB_OPT 304 +#define PFA_OPT 305 + +static Clp_Option options[] = { + { "block-length", 'l', BLOCK_LEN_OPT, Clp_ArgInt, 0 }, + { "help", 0, HELP_OPT, 0, 0 }, + { "line-length", 0, BLOCK_LEN_OPT, Clp_ArgInt, 0 }, + { "output", 'o', OUTPUT_OPT, Clp_ArgString, 0 }, + { "pfa", 'a', PFA_OPT, 0, 0 }, + { "pfb", 'b', PFB_OPT, 0, 0 }, + { "version", 0, VERSION_OPT, 0, 0 }, +}; +static const char *program_name; + +void +fatal_error(const char *message, ...) +{ + va_list val; + va_start(val, message); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, message, val); + putc('\n', stderr); + exit(1); +} + +void +error(const char *message, ...) +{ + va_list val; + va_start(val, message); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, message, val); + putc('\n', stderr); +} + +static void +short_usage(void) +{ + fprintf(stderr, "Usage: %s [OPTION]... [INPUT [OUTPUT]]\n\ +Try `%s --help' for more information.\n", + program_name, program_name); +} + +static void +usage(void) +{ + printf("\ +`T1asm' translates a human-readable version of a PostScript Type 1 font into\n\ +standard PFB or PFA format. The result is written to the standard output\n\ +unless an OUTPUT file is given.\n\ +\n\ +Usage: %s [OPTION]... [INPUT [OUTPUT]]\n\ +\n\ +Options:\n\ + -a, --pfa Output font in ASCII (PFA) format.\n\ + -b, --pfb Output font in binary (PFB) format. This is\n\ + the default.\n\ + -l, --block-length NUM Set max block length for PFB output.\n\ + -l, --line-length NUM Set max encrypted line length for PFA output.\n\ + -o, --output=FILE Write output to FILE.\n\ + -h, --help Print this message and exit.\n\ + --version Print version number and warranty and exit.\n\ +\n\ +Report bugs to <ekohler@gmail.com>.\n", program_name); +} + +#ifdef __cplusplus +} +#endif + + +int main(int argc, char *argv[]) +{ + char *p, *q, *r; + + Clp_Parser *clp = + Clp_NewParser(argc, (const char * const *)argv, sizeof(options) / sizeof(options[0]), options); + program_name = Clp_ProgramName(clp); + + /* interpret command line arguments using CLP */ + while (1) { + int opt = Clp_Next(clp); + switch (opt) { + + case BLOCK_LEN_OPT: + blocklen = clp->val.i; + break; + + output_file: + case OUTPUT_OPT: + if (ofp) + fatal_error("output file already specified"); + if (strcmp(clp->arg, "-") == 0) + ofp = stdout; + else if (!(ofp = fopen(clp->arg, "w"))) + fatal_error("%s: %s", clp->arg, strerror(errno)); + break; + + case PFB_OPT: + pfb = 1; + break; + + case PFA_OPT: + pfb = 0; + break; + + case HELP_OPT: + usage(); + exit(0); + break; + + case VERSION_OPT: + printf("t1asm (LCDF t1utils) %s\n", VERSION); + printf("Copyright (C) 1992-2003 I. Lee Hetherington, Eddie Kohler et al.\n\ +This is free software; see the source for copying conditions.\n\ +There is NO warranty, not even for merchantability or fitness for a\n\ +particular purpose.\n"); + exit(0); + break; + + case Clp_NotOption: + if (ifp && ofp) + fatal_error("too many arguments"); + else if (ifp) + goto output_file; + if (strcmp(clp->arg, "-") == 0) + ifp = stdin; + else if (!(ifp = fopen(clp->arg, "r"))) + fatal_error("%s: %s", clp->arg, strerror(errno)); + break; + + case Clp_Done: + goto done; + + case Clp_BadOption: + short_usage(); + exit(1); + break; + + } + } + + done: + if (!pfb) { + if (blocklen == -1) + blocklen = 64; + else if (blocklen < 8) { + blocklen = 8; + error("warning: line length raised to %d", blocklen); + } else if (blocklen > 1024) { + blocklen = 1024; + error("warning: line length lowered to %d", blocklen); + } + } + + if (!ifp) ifp = stdin; + if (!ofp) ofp = stdout; + + if (pfb) + init_pfb_writer(&w, blocklen, ofp); + +#if defined(_MSDOS) || defined(_WIN32) + /* If we are processing a PFB (binary) output */ + /* file, we must set its file mode to binary. */ + if (pfb) + _setmode(_fileno(ofp), _O_BINARY); +#endif + + /* Finally, we loop until no more input. Some special things to look for are + the `currentfile eexec' line, the beginning of the `/Subrs' or + `/CharStrings' definition, the definition of `/lenIV', and the definition + of the charstring start command which has `...string currentfile...' in + it. + + Being careful: Check with `/Subrs' and `/CharStrings' to see that a + number follows the token -- otherwise, the token is probably nested in a + subroutine a la Adobe Jenson, and we shouldn't pay attention to it. + + Bugs: Occurrence of `/Subrs 9' in a comment will fool t1asm. + + Thanks to Tom Kacvinsky <tjk@ams.org> who reported that some fonts come + without /Subrs sections and provided a patch. */ + + while (!feof(ifp) && !ferror(ifp)) { + texlive_getline(); + + if (!ever_active) { + if (strncmp(line, "currentfile eexec", 17) == 0 && isspace(line[17])) { + /* Allow arbitrary whitespace after "currentfile eexec". + Thanks to Tom Kacvinsky <tjk@ams.org> for reporting this. + Note: strlen("currentfile eexec") == 17. */ + for (p = line + 18; isspace(*p); p++) + ; + eexec_start(p); + continue; + } else if (strncmp(line, "/lenIV", 6) == 0) { + lenIV = atoi(line + 6); + } else if ((p = strstr(line, "string currentfile")) + && strstr(line, "readstring")) { /* enforce `readstring' */ + /* locate the name of the charstring start command */ + *p = '\0'; /* damage line[] */ + q = strrchr(line, '/'); + if (q) { + r = cs_start; + ++q; + while (!isspace(*q) && *q != '{') + *r++ = *q++; + *r = '\0'; + } + *p = 's'; /* repair line[] */ + } + } + + if (!active) { + if ((p = strstr(line, "/Subrs")) && isdigit(p[7])) + ever_active = active = 1; + else if ((p = strstr(line, "/CharStrings")) && isdigit(p[13])) + ever_active = active = 1; + } + if ((p = strstr(line, "currentfile closefile"))) { + /* 2/14/99 -- happy Valentine's day! -- don't look for `mark + currentfile closefile'; the `mark' might be on a different line */ + /* 1/3/2002 -- happy new year! -- Luc Devroye reports a failure with + some printers when `currentfile closefile' is followed by space */ + p += sizeof("currentfile closefile") - 1; + for (q = p; isspace(*q) && *q != '\n'; q++) + /* nada */; + if (q == p && !*q) + error("warning: `currentfile closefile' line too long"); + else if (q != p) { + if (*q != '\n') + error("text after `currentfile closefile' ignored"); + *p++ = '\n'; + *p++ = '\0'; + } + eexec_string(line); + break; + } + + eexec_string(line); + + /* output line data */ + if (start_charstring) { + if (!cs_start[0]) + fatal_error("couldn't find charstring start command"); + parse_charstring(); + } + } + + /* Handle remaining PostScript after the eexec section */ + if (in_eexec) + eexec_end(); + + /* There may be additional code. */ + while (!feof(ifp) && !ferror(ifp)) { + texlive_getline(); + eexec_string(line); + } + + if (pfb) + pfb_writer_end(&w); + + /* the end! */ + if (!ever_active) + error("warning: no charstrings found in input file"); + fclose(ifp); + fclose(ofp); + return 0; +} |