/* Copyright (C) 1995, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: geninit.c,v 1.3.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* * Utility for merging all the Ghostscript initialization files (gs_*.ps) * into a single file, optionally converting them to C data. Usage: * geninit [-(I|i) ] * geninit [-(I|i) ] -c * * The following special constructs are recognized in the input files: * %% Replace <#lines> () * %% Replace <#lines> INITFILES * If the first non-comment, non-blank line in a file ends with the * LanguageLevel 2 constructs << or <~, its section of the merged file * will begin with * currentobjectformat 1 setobjectformat * and end with * setobjectformat * and if any line then ends with with <, the following ASCIIHex string * will be converted to a binary token. */ #include "stdpre.h" #include #include #include /* for exit() */ #include /* Forward references */ private FILE *prefix_open(P2(const char *prefix, const char *inname)); private void merge_to_c(P5(const char *prefix, const char *inname, FILE * in, FILE * config, FILE * out)); private void merge_to_ps(P5(const char *prefix, const char *inname, FILE * in, FILE * config, FILE * out)); #define LINE_SIZE 128 int main(int argc, char *argv[]) { int arg_c = argc; char **arg_v = argv; const char *fin; FILE *in; const char *fconfig; FILE *config; const char *fout; FILE *out; const char *prefix = ""; bool to_c = false; if (arg_c >= 2 && (!strcmp(arg_v[1], "-I") || !strcmp(arg_v[1], "-i"))) { prefix = arg_v[2]; arg_c -= 2; arg_v += 2; } if (arg_c == 4) fin = arg_v[1], fconfig = arg_v[2], fout = arg_v[3]; else if (arg_c == 5 && !strcmp(arg_v[3], "-c")) fin = arg_v[1], fconfig = arg_v[2], fout = arg_v[4], to_c = true; else { fprintf(stderr, "\ Usage: geninit [-(I|i) lib/] gs_init.ps gconfig.h gs_xinit.ps\n\ or geninit [-(I|i) lib/] gs_init.ps gconfig.h -c gs_init.c\n"); exit(1); } in = prefix_open(prefix, fin); if (in == 0) { fprintf(stderr, "Cannot open %s for reading.\n", fin); exit(1); } config = fopen(fconfig, "r"); if (config == 0) { fprintf(stderr, "Cannot open %s for reading.\n", fconfig); fclose(in); exit(1); } out = fopen(fout, "w"); if (out == 0) { fprintf(stderr, "Cannot open %s for writing.\n", fout); fclose(config); fclose(in); exit(1); } if (to_c) merge_to_c(prefix, fin, in, config, out); else merge_to_ps(prefix, fin, in, config, out); fclose(out); return 0; } /* Open a file with a name prefix. */ private FILE * prefix_open(const char *prefix, const char *inname) { char fname[LINE_SIZE + 1]; if (strlen(prefix) + strlen(inname) > LINE_SIZE) { fprintf(stderr, "File name > %d characters, too long.\n", LINE_SIZE); exit(1); } strcpy(fname, prefix); strcat(fname, inname); return fopen(fname, "r"); } /* Read a line from the input. */ private bool rl(FILE * in, char *str, int len) { /* * Unfortunately, we can't use fgets here, because the typical * implementation only recognizes the EOL convention of the current * platform. */ int i = 0, c = getc(in); if (c < 0) return false; while (i < len - 1) { if (c < 0 || c == 10) /* ^J, Unix EOL */ break; if (c == 13) { /* ^M, Mac EOL */ c = getc(in); if (c != 10 && c >= 0) /* ^M^J, PC EOL */ ungetc(c, in); break; } str[i++] = c; c = getc(in); } str[i] = 0; return true; } /* Write a string or a line on the output. */ private void wsc(FILE * out, const byte *str, int len) { int n = 0; const byte *p = str; int i; for (i = 0; i < len; ++i) { int c = str[i]; fprintf(out, (c < 32 || c >= 127 ? "%d," : c == '\'' || c == '\\' ? "'\\%c'," : "'%c',"), c); if (++n == 15) { fputs("\n", out); n = 0; } } if (n != 0) fputc('\n', out); } private void ws(FILE * out, const byte *str, int len, bool to_c) { if (to_c) wsc(out, str, len); else fwrite(str, 1, len, out); } private void wl(FILE * out, const char *str, bool to_c) { ws(out, (const byte *)str, strlen(str), to_c); ws(out, (const byte *)"\n", 1, to_c); } /* * Strip whitespace and comments from a line of PostScript code as possible. * Return a pointer to any string that remains, or NULL if none. * Note that this may store into the string. */ private char * doit(char *line) { char *str = line; char *from; char *to; int in_string = 0; while (*str == ' ' || *str == '\t') /* strip leading whitespace */ ++str; if (*str == 0) /* all whitespace */ return NULL; if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */ return str; if (str[0] == '%') /* comment line */ return NULL; /* * Copy the string over itself removing: * - All comments not within string literals; * - Whitespace adjacent to []{}; * - Whitespace before /(<; * - Whitespace after )>. */ for (to = from = str; (*to = *from) != 0; ++from, ++to) { switch (*from) { case '%': if (!in_string) break; continue; case ' ': case '\t': if (to > str && !in_string && strchr(" \t>[]{})", to[-1])) --to; continue; case '(': ++in_string; case '<': case '/': case '[': case ']': case '{': case '}': if (to > str && !in_string && strchr(" \t", to[-1])) *--to = *from; continue; case ')': --in_string; continue; case '\\': if (from[1] == '\\' || from[1] == '(' || from[1] == ')') *++to = *++from; continue; default: continue; } break; } /* Strip trailing whitespace. */ while (to > str && (to[-1] == ' ' || to[-1] == '\t')) --to; *to = 0; return str; } /* Copy a hex string to the output as a binary token. */ private void hex_string_to_binary(FILE *out, FILE *in, bool to_c) { #define MAX_STR 0xffff /* longest possible PostScript string token */ byte *strbuf = (byte *)malloc(MAX_STR); byte *q = strbuf; int c, digit; bool which = false; int len; byte prefix[3]; if (strbuf == 0) { fprintf(stderr, "Unable to allocate string token buffer.\n"); exit(1); } while ((c = getc(in)) >= 0) { if (isxdigit(c)) { c -= (isdigit(c) ? '0' : islower(c) ? 'a' : 'A'); if ((which = !which)) { if (q == strbuf + MAX_STR) { fprintf(stderr, "Can't handle string token > %d bytes.\n", MAX_STR); exit(1); } *q++ = c << 4; } else q[-1] += c; } else if (isspace(c)) continue; else if (c == '>') break; else fprintf(stderr, "Unknown character in ASCIIHex string: %c\n", c); } len = q - strbuf; if (len <= 255) { prefix[0] = 142; prefix[1] = (byte)len; ws(out, prefix, 2, to_c); } else { prefix[0] = 143; prefix[1] = (byte)(len >> 8); prefix[2] = (byte)len; ws(out, prefix, 3, to_c); } ws(out, strbuf, len, to_c); free((char *)strbuf); #undef MAX_STR } /* Merge a file from input to output. */ private void flush_buf(FILE * out, char *buf, bool to_c) { if (buf[0] != 0) { wl(out, buf, to_c); buf[0] = 0; } } private void mergefile(const char *prefix, const char *inname, FILE * in, FILE * config, FILE * out, bool to_c) { char line[LINE_SIZE + 1]; char buf[LINE_SIZE + 1]; char *str; int level = 1; bool first = true; buf[0] = 0; while (rl(in, line, LINE_SIZE)) { char psname[LINE_SIZE + 1]; int nlines; if (!strncmp(line, "%% Replace ", 11) && sscanf(line + 11, "%d %s", &nlines, psname) == 2 ) { flush_buf(out, buf, to_c); while (nlines-- > 0) rl(in, line, LINE_SIZE); if (psname[0] == '(') { FILE *ps; psname[strlen(psname) - 1] = 0; ps = prefix_open(prefix, psname + 1); if (ps == 0) { fprintf(stderr, "Cannot open %s for reading.\n", psname + 1); exit(1); } mergefile(prefix, psname + 1, ps, config, out, to_c); } else if (!strcmp(psname, "INITFILES")) { /* * We don't want to bind config.h into geninit, so * we parse it ourselves at execution time instead. */ rewind(config); while (rl(config, psname, LINE_SIZE)) if (!strncmp(psname, "psfile_(\"", 9)) { FILE *ps; char *quote = strchr(psname + 9, '"'); *quote = 0; ps = prefix_open(prefix, psname + 9); if (ps == 0) { fprintf(stderr, "Cannot open %s for reading.\n", psname + 9); exit(1); } mergefile(prefix, psname + 9, ps, config, out, to_c); } } else { fprintf(stderr, "Unknown %%%% Replace %d %s\n", nlines, psname); exit(1); } } else if (!strcmp(line, "currentfile closefile")) { /* The rest of the file is debugging code, stop here. */ break; } else { int len; str = doit(line); if (str == 0) continue; len = strlen(str); if (first && len >= 2 && str[len - 2] == '<' && (str[len - 1] == '<' || str[len - 1] == '~') ) { wl(out, "currentobjectformat 1 setobjectformat\n", to_c); level = 2; } if (level > 1 && len > 0 && str[len - 1] == '<' && (len < 2 || str[len - 2] != '<') ) { /* Convert a hex string to a binary token. */ flush_buf(out, buf, to_c); str[len - 1] = 0; wl(out, str, to_c); hex_string_to_binary(out, in, to_c); continue; } if (buf[0] != '%' && /* i.e. not special retained comment */ strlen(buf) + len < LINE_SIZE && (strchr("(/[]{}", str[0]) || (buf[0] != 0 && strchr(")[]{}", buf[strlen(buf) - 1]))) ) strcat(buf, str); else { flush_buf(out, buf, to_c); strcpy(buf, str); } first = false; } } flush_buf(out, buf, to_c); if (level > 1) wl(out, "setobjectformat", to_c); fprintf(stderr, "%s: %ld bytes, output pos = %ld\n", inname, ftell(in), ftell(out)); fclose(in); } /* Merge and produce a C file. */ private void merge_to_c(const char *prefix, const char *inname, FILE * in, FILE * config, FILE * out) { char line[LINE_SIZE + 1]; fputs("/*\n", out); while ((rl(in, line, LINE_SIZE), line[0])) fprintf(out, "%s\n", line); fputs("*/\n", out); fputs("\n", out); fputs("/* Pre-compiled interpreter initialization string. */\n", out); fputs("\n", out); fputs("const unsigned char gs_init_string[] = {\n", out); mergefile(prefix, inname, in, config, out, true); fputs("10};\n", out); fputs("const unsigned int gs_init_string_sizeof = sizeof(gs_init_string);\n", out); } /* Merge and produce a PostScript file. */ private void merge_to_ps(const char *prefix, const char *inname, FILE * in, FILE * config, FILE * out) { char line[LINE_SIZE + 1]; while ((rl(in, line, LINE_SIZE), line[0])) fprintf(out, "%s\n", line); mergefile(prefix, inname, in, config, out, false); }