1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* Generate source data for the %rom% IODevice */
18 /*
19  * For reasons of convenience and footprint reduction, the various postscript
20  * source files, resources and fonts required by Ghostscript can be compiled
21  * into the executable.
22  *
23  * This file takes a set of directories, and creates a compressed filesystem
24  * image that can be compiled into the executable as static data and accessed
25  * through the %rom% iodevice prefix
26  *
27  */
28 /*
29  *	Usage: mkromfs [-o outputfile] [options ...] path path ...
30  *
31  *	    mkromfs no longer performs implicit enumeration of paths, so if a
32  *	    path is a directory to be enumerated trailing '<slash><star>' must be present.
33  *	    gcc complains about comments inside comments so we use '<slash>=/' and '<star>=*' here.
34  *	    Note: one should avoid bare "<slash><star>" which may be expanded by the shell
35  *	    to "/bin /boot /dev ...". e.g. use "<slash><star>.ttf" or "<slash><star>.ps" instead of "<slash><star>".
36  *
37  *	    options and paths can be interspersed and are processed in order
38  *
39  *	    options:
40  *		-o outputfile	default: obj/gsromfs.c if this option present, must be first.
41  *		-P prefix	use prefix to find path. prefix not included in %rom%
42  *		-X path		exclude the path/file from further processing.
43  *			  Note:	The tail of any path encountered will be tested so .svn on the
44  *				-X list will exclude that path in all subsequent paths enumerated.
45  *
46  *              -d romprefix    directory in %rom% file system (a prefix string on filename)
47  *		-c		compression on
48  *		-b		compression off (binary).
49  *		-C		postscript 'compaction' on
50  *		-B		postscript 'compaction' off
51  *		-g initfile gconfig_h
52  *				special handling to read the 'gs_init.ps' file (from
53  *				the current -P prefix path), and read the gconfig.h for
54  *				psfile_ entries and combines them into a whitespace
55  *				optimized and no comments form and writes this 'gs_init.ps'
56  *				to the current -d destination path. This is a space and
57  *				startup performance improvement, so also this should be
58  *				BEFORE other stuff in the %rom% list of files (currently
59  *				we do a sequential search in the %rom% directory).
60  *
61  *				For performance reasons, it is best to turn off compression
62  *				for the init file. Less frequently accessed files, if they
63  *				are large should still be compressed.
64  *              -s num_files    split the output into <num_files> subfiles.
65  *
66  */
67 
68 /* prevent gp.h redefining fopen */
69 #define fopen fopen
70 /* prevent gp.h redefining sprintf */
71 #define sprintf sprintf
72 
73 #include "stdpre.h"
74 #include "stdint_.h"
75 #include "time_.h"
76 #include "gsiorom.h"
77 #include "gsmemret.h" /* for gs_memory_type_ptr_t */
78 #include "gsmalloc.h"
79 #include "gsstype.h"
80 #include "gp.h"
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <ctype.h>
85 #include <memory.h>
86 
87 #include <zlib.h>
88 
89 int gs_log_error(int err, const char *file, int line);
90 
91 /*
92  * The rom file system is an array of pointers to nodes, terminated by a NULL
93  */
94 /*
95  * in memory structure of each node is:
96  *
97  *	length_of_uncompressed_file		[31-bit big-endian]
98  *						high bit is compression flag
99  *	data_block_struct[]
100  *	padded_file_name (char *)	includes as least one terminating <nul>
101  *	padded_data_blocks
102  */
103 /*
104  *	data_block_struct:
105  *	    data_length			(not including pad)	[32-bit big-endian]
106  *	    data_block_offset		(start of each block)	[32-bit big-endian]
107  */
108 
109 typedef struct romfs_inode_s {
110     unsigned long disc_length;		/* length of file on disc */
111     unsigned long length;		/* blocks is (length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE */
112     char *name;				/* nul terminated */
113     unsigned long *data_lengths;	/* this could be short if ROMFS_BLOCKSIZE */
114                                         /* is < 64k, but the cost is small to use int */
115     unsigned char **data;
116 } romfs_inode;
117 
118 typedef struct Xlist_element_s {
119         void *next;
120         char *path;
121     } Xlist_element;
122 
123 typedef struct {
124     int num_splits;
125     int max_splits;
126     unsigned long *sizes;
127     char *outname;
128     char *outname_formatted;
129 } split_data;
130 
131 #define PATH_STR_LEN 1024
132 
133 #ifndef ARCH_IS_BIG_ENDIAN
134 #define ARCH_IS_BIG_ENDIAN 0
135 #endif
136 /* This gives what we're running on,
137  * whilst ARCH_IS_BIG_ENDIAN gives what we're
138  * building for.
139  */
isbigendian(void)140 static inline int isbigendian(void)
141 {
142     union {
143         int i;
144         char c[sizeof(int)];
145     } u = {1};
146 
147     return u.c[0] != 1;
148 }
149 
150 /* mkromfs doesn't use gp_stat, but it does link gp_misc.c which includes
151    call to gp_stat_impl(). Rather than major build upheaval for something not
152    used, just define a dummy here for Windows.
153  */
154 #ifdef _WIN32
gp_stat_impl(const gs_memory_t * mem,const char * path,struct _stat64 * buf)155 int gp_stat_impl(const gs_memory_t *mem, const char *path, struct _stat64 *buf)
156 {
157     (void)mem;
158     (void)path;
159     (void)buf;
160     return 0;
161 }
162 #endif
163 
164 /*******************************************************************************
165  * The following are non-redirected printing functions to avoid the need for
166  * these included from gsmisc.c (unix gp_ functions, among others, use if_debug).
167  *******************************************************************************/
168 #include <stdarg.h>
169 #define PRINTF_BUF_LENGTH 1024
170 
171 static const char msg_truncated[] = "\n*** Previous line has been truncated.\n";
172 
outprintf(const gs_memory_t * mem,const char * fmt,...)173 int outprintf(const gs_memory_t *mem, const char *fmt, ...)
174 {
175     int count;
176     char buf[PRINTF_BUF_LENGTH];
177     va_list args;
178 
179     va_start(args, fmt);
180     count = vsnprintf(buf, sizeof(buf), fmt, args);
181     if (count >= sizeof(buf) || count < 0)  { /* C99 || MSVC */
182         fwrite(buf, 1, sizeof(buf) - 1, stdout);
183         fwrite(msg_truncated, 1, sizeof(msg_truncated) - 1, stdout);
184     } else {
185         fwrite(buf, 1, count, stdout);
186     }
187     va_end(args);
188     return count;
189 }
190 
191 #ifndef GS_THREADSAFE
errprintf_nomem(const char * fmt,...)192 int errprintf_nomem(const char *fmt, ...)
193 {
194     int count;
195     char buf[PRINTF_BUF_LENGTH];
196     va_list args;
197 
198     va_start(args, fmt);
199     count = vsnprintf(buf, sizeof(buf), fmt, args);
200     if (count >= sizeof(buf) || count < 0)  { /* C99 || MSVC */
201         fwrite(buf, 1, sizeof(buf) - 1, stderr);
202         fwrite(msg_truncated, 1, sizeof(msg_truncated) - 1, stderr);
203     } else {
204         fwrite(buf, 1, count, stderr);
205     }
206     va_end(args);
207     return count;
208 }
209 #endif
210 
errprintf(const gs_memory_t * mem,const char * fmt,...)211 int errprintf(const gs_memory_t *mem, const char *fmt, ...)
212 {
213     int count;
214     char buf[PRINTF_BUF_LENGTH];
215     va_list args;
216 
217     va_start(args, fmt);
218     count = vsnprintf(buf, sizeof(buf), fmt, args);
219     if (count >= sizeof(buf) || count < 0)  { /* C99 || MSVC */
220         fwrite(buf, 1, sizeof(buf) - 1, stderr);
221         fwrite(msg_truncated, 1, sizeof(msg_truncated) - 1, stderr);
222     } else {
223         fwrite(buf, 1, count, stderr);
224     }
225     va_end(args);
226     return count;
227 }
228 
229 
230 #ifndef GS_THREADSAFE
231 #if __LINE__                    /* compiler provides it */
232 void
lprintf_file_and_line(const char * file,int line)233 lprintf_file_and_line(const char *file, int line)
234 {
235     epf("%s(%d): ", file, line);
236 }
237 #else
238 void
lprintf_file_only(FILE * f,const char * file)239 lprintf_file_only(FILE * f, const char *file)
240 {
241     epf("%s(?): ", file);
242 }
243 #endif
244 
245 void
eprintf_program_ident(const char * program_name,long revision_number)246 eprintf_program_ident(const char *program_name,
247                       long revision_number)
248 {
249     if (program_name) {
250         epf((revision_number ? "%s " : "%s"), program_name);
251         if (revision_number) {
252             int fpart = revision_number % 100;
253 
254             epf("%d.%02d", (int)(revision_number / 100), fpart);
255         }
256         epf(": ");
257     }
258 }
259 #endif
260 
261 void
emprintf_program_ident(const gs_memory_t * mem,const char * program_name,long revision_number)262 emprintf_program_ident(const gs_memory_t *mem,
263                        const char *program_name,
264                        long revision_number)
265 {
266     if (program_name) {
267         epfm(mem, (revision_number ? "%s " : "%s"), program_name);
268         if (revision_number) {
269             int fpart = revision_number % 100;
270 
271             epfm(mem, "%d.%02d", (int)(revision_number / 100), fpart);
272         }
273         epfm(mem, ": ");
274     }
275 }
276 
277 int
gs_log_error(int err,const char * file,int line)278 gs_log_error(int err, const char *file, int line)
279 {
280     if (file == NULL)
281         errprintf_nomem("Returning error %d.\n", err);
282     else
283         errprintf_nomem("%s(%d): Returning error %d.\n",
284                  (const char *)file, line, err);
285     return err;
286 }
287 
288 /*******************************************************************************
289  * The following is a REALLY minimal gs_memory_t for use by the gp_ functions
290  *******************************************************************************/
291 
292 byte *minimal_alloc_bytes(gs_memory_t * mem, size_t size, client_name_t cname);
293 byte *minimal_alloc_byte_array(gs_memory_t * mem, size_t num_elements,
294                                size_t elt_size, client_name_t cname);
295 void *minimal_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
296                            client_name_t cname);
297 void minimal_free_object(gs_memory_t * mem, void * data, client_name_t cname);
298 void minimal_free_string(gs_memory_t * mem, byte * data, size_t nbytes, client_name_t cname);
299 
300 byte *
minimal_alloc_bytes(gs_memory_t * mem,size_t size,client_name_t cname)301 minimal_alloc_bytes(gs_memory_t * mem, size_t size, client_name_t cname)
302 {
303     return malloc(size);
304 }
305 
306 byte *
minimal_alloc_byte_array(gs_memory_t * mem,size_t num_elements,size_t elt_size,client_name_t cname)307 minimal_alloc_byte_array(gs_memory_t * mem, size_t num_elements,
308                          size_t elt_size, client_name_t cname)
309 {
310     return malloc(num_elements * elt_size);
311 }
312 
313 void *
minimal_alloc_struct(gs_memory_t * mem,gs_memory_type_ptr_t pstype,client_name_t cname)314 minimal_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
315                      client_name_t cname)
316 {
317     return malloc(pstype->ssize);
318 }
319 
320 void
minimal_free_object(gs_memory_t * mem,void * data,client_name_t cname)321 minimal_free_object(gs_memory_t * mem, void * data, client_name_t cname)
322 {
323     free(data);
324     return;
325 }
326 
327 void
minimal_free_string(gs_memory_t * mem,byte * data,size_t nbytes,client_name_t cname)328 minimal_free_string(gs_memory_t * mem, byte * data, size_t nbytes, client_name_t cname)
329 {
330     free(data);
331     return;
332 }
333 
334 void basic_enum_ptrs(void);
335 void basic_reloc_ptrs(void);
336 
basic_enum_ptrs()337 void basic_enum_ptrs() {
338     printf("basic_enum_ptrs is only called by a GC. Abort.\n");
339     exit(1);
340 }
341 
basic_reloc_ptrs()342 void basic_reloc_ptrs() {
343     printf("basic_reloc_ptrs is only called by a GC. Abort.\n");
344     exit(1);
345 }
346 
347 const gs_malloc_memory_t minimal_memory = {
348     (gs_memory_t *)&minimal_memory, /* stable */
349     { minimal_alloc_bytes, /* alloc_bytes_immovable */
350       NULL, /* resize_object */
351       minimal_free_object, /* free_object */
352       NULL, /* stable */
353       NULL, /* status */
354       NULL, /* free_all */
355       NULL, /* consolidate_free */
356       minimal_alloc_bytes, /* alloc_bytes */
357       minimal_alloc_struct, /* alloc_struct */
358       minimal_alloc_struct, /* alloc_struct_immovable */
359       minimal_alloc_byte_array, /* alloc_byte_array */
360       minimal_alloc_byte_array, /* alloc_byte_array_immovable */
361       NULL, /* alloc_struct_array */
362       NULL, /* alloc_struct_array_immovable */
363       NULL, /* object_size */
364       NULL, /* object_type */
365       minimal_alloc_bytes, /* alloc_string */
366       minimal_alloc_bytes, /* alloc_string_immovable */
367       NULL, /* resize_string */
368       minimal_free_string, /* free_string */
369       NULL, /* register_root */
370       NULL, /* unregister_root */
371       NULL /* enable_free */
372     },
373     NULL, /* gs_lib_ctx */
374     NULL, /* head */
375     NULL, /* non_gc_memory */
376     0, /* allocated */
377     0, /* limit */
378     0, /* used */
379     0  /* max used */
380 };
381 
382 int cmpstringp(const void *p1, const void *p2);
383 void put_uint32(FILE *out, const unsigned int q);
384 void put_bytes_padded(FILE *out, unsigned char *p, unsigned int len);
385 void inode_clear(romfs_inode* node);
386 void inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int*totlen, split_data *splits, int verbose);
387 void process_path(char *path, const char *os_prefix, const char *rom_prefix,
388                   Xlist_element *Xlist_head, int compression,
389                   int compaction, int *inode_count, int *totlen, FILE *out,
390                   split_data *splits, int verbose);
391 FILE *prefix_open(const char *os_prefix, const char *inname, int verbose);
392 void prefix_add(const char *prefix, const char *filename, char *prefixed_path);
393 
394 /* put 4 byte integer, big endian */
put_uint32(FILE * out,const unsigned int q)395 void put_uint32(FILE *out, const unsigned int q)
396 {
397 #if ARCH_IS_BIG_ENDIAN
398     fprintf (out, "0x%08x,", q);
399 #else
400     fprintf (out, "0x%02x%02x%02x%02x,", q & 0xff, (q>>8) & 0xff, (q>>16) & 0xff, (q>>24) & 0xff);
401 #endif
402 }
403 
404 /* write string as 4 character chunks, padded to 4 byte words. */
put_bytes_padded(FILE * out,unsigned char * p,unsigned int len)405 void put_bytes_padded(FILE *out, unsigned char *p, unsigned int len)
406 {
407     int i, j=0;
408     union {
409         uint32_t w;
410         struct {
411             unsigned char c1;
412             unsigned char c2;
413             unsigned char c3;
414             unsigned char c4;
415         } c;
416     } w2c;
417 
418     for (i=0; i<(len/4); i++) {
419         j = i*4;
420         w2c.c.c1 = p[j++];
421         w2c.c.c2 = p[j++];
422         w2c.c.c3 = p[j++];
423         w2c.c.c4 = p[j++];
424         if (isbigendian() != ARCH_IS_BIG_ENDIAN) {
425             fprintf (out, "0x%02x%02x%02x%02x,", (w2c.w) & 0xff, (w2c.w>>8) & 0xff, (w2c.w>>16) & 0xff, (w2c.w >>24) & 0xff);
426         }
427         else {
428             fprintf(out, "0x%08x,", w2c.w);
429         }
430         if ((i & 7) == 7)
431             fprintf(out, "\n\t");
432     }
433     w2c.w = 0;
434     switch (len - j) {
435       case 3:
436         w2c.c.c3 = p[j+2];
437       case 2:
438         w2c.c.c2 = p[j+1];
439       case 1:
440         w2c.c.c1 = p[j];
441         if (isbigendian() != ARCH_IS_BIG_ENDIAN) {
442             fprintf (out, "0x%02x%02x%02x%02x,", (w2c.w) & 0xff, (w2c.w>>8) & 0xff, (w2c.w>>16) & 0xff, (w2c.w >>24) & 0xff);
443         }
444         else {
445             fprintf(out, "0x%08x,", w2c.w);
446         }
447       default: ;
448     }
449     fprintf(out, "\n\t");
450 }
451 
452 /* clear the internal memory of an inode */
inode_clear(romfs_inode * node)453 void inode_clear(romfs_inode* node)
454 {
455     int i, blocks;
456 
457     if (node) {
458         blocks = (node->disc_length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE;
459         if (node->data) {
460             for (i = 0; i < blocks; i++) {
461                 if (node->data[i]) free(node->data[i]);
462             }
463             free(node->data);
464         }
465         if (node->data_lengths) free(node->data_lengths);
466     }
467 }
468 
469 static unsigned long
do_inode_write(FILE * out,romfs_inode * node,int compression,int inode_count,int * totlen,int split,int verbose)470 do_inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int *totlen, int split, int verbose)
471 {
472     int i, offset;
473     int blocks = (node->length+ROMFS_BLOCKSIZE-1)/ROMFS_BLOCKSIZE;
474     int namelen = strlen(node->name) + 1;	/* include terminating <nul> */
475     int clen = 0;			/* compressed length */
476 
477     /* write the node header */
478     fprintf(out,"    %s const uint32_t %snode_%d[] = {\n\t",
479             split ? "" : "static",
480             split ? "mkromfs_" : "",
481             inode_count);
482     /* 4 byte file length + compression flag in high bit */
483     put_uint32(out, node->length | (compression ? 0x80000000 : 0));
484     fprintf(out, "\t/* compression_flag_bit + file length */\n\t");
485 
486     /* write out data block structures */
487     offset = 4 + (8*blocks) + ((namelen+3) & ~3);
488     *totlen += offset;			/* add in the header size */
489     for (i = 0; i < blocks; i++) {
490         put_uint32(out, node->data_lengths[i]);
491         put_uint32(out, offset);
492         offset += (node->data_lengths[i]+3) & ~3;
493         fprintf(out, "\t/* data_block_length, offset to data_block */\n\t");
494     }
495     /* write file name (path) padded to 4 byte multiple */
496     fprintf(out, "\t/* file name '%s' */\n\t", node->name);
497     put_bytes_padded(out, (unsigned char *)node->name, namelen);
498 
499     /* write out data */
500     for (i = 0; i < blocks; i++) {
501         put_bytes_padded(out, node->data[i], node->data_lengths[i]);
502         clen += node->data_lengths[i];
503         *totlen += (node->data_lengths[i]+3) & ~3;	/* padded block length */
504     }
505     fprintf(out, "\t0 };\t/* end-of-node */\n");
506 
507     if (verbose) {
508         printf("node '%s' len=%ld", node->name, node->length);
509         printf(" %d blocks", blocks);
510         if (compression) {
511             printf(", compressed size=%d", clen);
512         }
513         printf("\n");
514     }
515     if (compression)
516         return clen;
517     return node->length;
518 }
519 
520 static void
prepare_splits(split_data * splits)521 prepare_splits(split_data *splits)
522 {
523     if (splits->num_splits) {
524         /* Make sure we have a properly sized size array. */
525         if (splits->num_splits > splits->max_splits) {
526             unsigned long *sizes = realloc(splits->sizes, sizeof(unsigned long) * splits->num_splits);
527             if (sizes == NULL) {
528                 fprintf(stderr, "Failed to allocate split data array\n");
529                 exit(1);
530             }
531             memset(&sizes[splits->max_splits], 0, sizeof(unsigned long) * (splits->num_splits - splits->max_splits));
532             splits->sizes = sizes;
533             splits->max_splits = splits->num_splits;
534         }
535     }
536 }
537 
538 static void
start_file(FILE * out)539 start_file(FILE *out)
540 {
541     fprintf(out,"\t/* Generated data for %%rom%% device, see mkromfs.c */\n");
542 #if ARCH_IS_BIG_ENDIAN
543     fprintf(out,"\t/* this code assumes a big endian target platform */\n");
544 #else
545     fprintf(out,"\t/* this code assumes a little endian target platform */\n");
546 #endif
547     fprintf(out,"\n#include \"stdint_.h\"\n");
548     fprintf(out,"\n#include \"time_.h\"\n\n");
549 }
550 
551 /* write out an inode and its file data */
552 void
inode_write(FILE * out,romfs_inode * node,int compression,int inode_count,int * totlen,split_data * splits,int verbose)553 inode_write(FILE *out, romfs_inode *node, int compression, int inode_count, int *totlen, split_data *splits, int verbose)
554 {
555     prepare_splits(splits);
556     if (splits->max_splits) {
557         /* Find the smallest bin to add this to. */
558         FILE *out2;
559         int which = 0;
560         int i;
561         for (i = 1; i < splits->max_splits; i++) {
562             if (splits->sizes[which] > splits->sizes[i])
563                 which = i;
564         }
565 
566         sprintf(splits->outname_formatted, splits->outname, which);
567         if (splits->sizes[which] == 0) {
568             out2 = fopen(splits->outname_formatted, "w");
569             start_file(out2);
570         } else {
571             out2 = fopen(splits->outname_formatted, "a");
572         }
573         splits->sizes[which] += do_inode_write(out2, node, compression, inode_count, totlen, 1, verbose);
574         fclose(out2);
575     } else
576         (void)do_inode_write(out, node, compression, inode_count, totlen, 0, verbose);
577 }
578 
579 void
prefix_add(const char * prefix,const char * filename,char * prefixed_path)580 prefix_add(const char *prefix, const char *filename, char *prefixed_path)
581 {
582     prefixed_path[0] = 0;	/* empty string */
583     strcat(prefixed_path, prefix);
584     strcat(prefixed_path, filename);
585 #if defined(__WIN32__) || defined(__OS2__)
586     /* On Windows, the paths may (will) have '\' instead of '/' so we translate them */
587     {
588         int i;
589 
590         for (i=0; i<strlen(prefixed_path); i++)
591             if (prefixed_path[i] == '\\')
592                 prefixed_path[i] = '/';
593     }
594 #endif
595 }
596 
597 /* Simple ps compaction routines; strip comments, compact whitespace */
598 typedef enum {
599     PSC_BufferIn = 0,
600     PSC_InComment,
601     PSC_InString,
602     PSC_InHexString,
603     PSC_BufferOut,
604     PSC_BufferCopy,
605 } psc_state;
606 
607 typedef int (psc_getc)(void *);
608 typedef void (psc_ungetc)(int, void *);
609 typedef int (psc_feof)(void *);
610 
611 typedef struct {
612     psc_state state;
613     int inpos;
614     int inmax;
615     int outpos;
616     int outend;
617     int outmax;
618     int buffercopy;
619     int wasascii;
620     unsigned char *bufferin;
621     unsigned char *bufferout;
622     psc_getc *pgetc;
623     psc_ungetc *unpgetc;
624     psc_feof *peof;
625     void *file;
626     int names;
627     int binary;
628     int noescape;
629     int escaping;
630     int paren;
631     int firstnum;
632 } pscompstate;
633 
pscompact_start(pscompstate * psc,psc_getc * myfgetc,psc_ungetc * myungetc,psc_feof * myfeof,void * myfile,int names,int binary,int firstnum)634 static void pscompact_start(pscompstate *psc, psc_getc *myfgetc, psc_ungetc *myungetc, psc_feof *myfeof, void *myfile, int names, int binary, int firstnum)
635 {
636     psc->state = PSC_BufferIn;
637     psc->bufferin = malloc(80);
638     psc->bufferout = malloc(80);
639     if ((psc->bufferin == NULL) || (psc->bufferout == NULL)) {
640         fprintf(stderr, "Malloc failed in ps compaction\n");
641         exit(1);
642     }
643     psc->inmax = 80;
644     psc->outmax = 80;
645     psc->inpos = 0;
646     psc->wasascii = 0;
647     psc->pgetc = myfgetc;
648     psc->unpgetc = myungetc;
649     psc->peof = myfeof;
650     psc->file = myfile;
651     psc->names = names;
652     psc->binary = binary;
653     psc->noescape = 0;
654     psc->escaping = 0;
655     psc->paren = 0;
656     psc->firstnum = firstnum;
657 }
658 
pscompact_end(pscompstate * psc)659 static void pscompact_end(pscompstate *psc)
660 {
661     free(psc->bufferin);
662     free(psc->bufferout);
663 }
664 
pscompact_copy(pscompstate * psc,int c,int n)665 static void pscompact_copy(pscompstate *psc, int c, int n)
666 {
667     psc->bufferout[0] = c;
668     psc->outend = 1;
669     psc->outpos = 0;
670     psc->buffercopy = n;
671     if (n == 0)
672         psc->state = PSC_BufferOut;
673     else
674         psc->state = PSC_BufferCopy;
675 }
676 
pscompact_copy2(pscompstate * psc,int c1,int c2,int n)677 static void pscompact_copy2(pscompstate *psc, int c1, int c2, int n)
678 {
679     psc->bufferout[0] = c1;
680     psc->bufferout[1] = c2;
681     psc->outend = 2;
682     psc->outpos = 0;
683     psc->buffercopy = n;
684     if (n == 0)
685         psc->state = PSC_BufferOut;
686     else
687         psc->state = PSC_BufferCopy;
688 }
689 
pscompact_copy3(pscompstate * psc,int c1,int c2,int c3,int n)690 static void pscompact_copy3(pscompstate *psc, int c1, int c2, int c3, int n)
691 {
692     psc->bufferout[0] = c1;
693     psc->bufferout[1] = c2;
694     psc->bufferout[2] = c3;
695     psc->outend = 3;
696     psc->outpos = 0;
697     psc->buffercopy = n;
698     if (n == 0)
699         psc->state = PSC_BufferOut;
700     else
701         psc->state = PSC_BufferCopy;
702 }
703 
pscompact_buffer(pscompstate * psc,int c)704 static void pscompact_buffer(pscompstate *psc, int c)
705 {
706     if (psc->inpos == psc->inmax) {
707         psc->inmax *= 2;
708         psc->bufferin = realloc(psc->bufferin, psc->inmax * 2);
709         if (psc->bufferin == NULL) {
710             fprintf(stderr, "Realloc failed in pscompaction\n");
711             exit(1);
712         }
713     }
714     psc->bufferin[psc->inpos++] = c;
715 }
716 
pscompact_bufferatstart(pscompstate * psc,int c)717 static void pscompact_bufferatstart(pscompstate *psc, int c)
718 {
719     if (psc->inpos == psc->inmax) {
720         psc->inmax *= 2;
721         psc->bufferin = realloc(psc->bufferin, psc->inmax * 2);
722         if (psc->bufferin == NULL) {
723             fprintf(stderr, "Realloc failed in pscompaction\n");
724             exit(1);
725         }
726     }
727     memmove(psc->bufferin+1, psc->bufferin, psc->inpos);
728     psc->inpos++;
729     psc->bufferin[0] = c;
730 }
731 
pscompact_copyinout(pscompstate * psc)732 static void pscompact_copyinout(pscompstate *psc)
733 {
734     if (psc->outmax < psc->inpos) {
735         psc->outmax = psc->inmax;
736         psc->bufferout = realloc(psc->bufferout, psc->outmax);
737         if (psc->bufferout == NULL) {
738             fprintf(stderr, "Realloc failed in pscompaction\n");
739             exit(1);
740         }
741     }
742     memcpy(psc->bufferout, psc->bufferin, psc->inpos);
743     psc->outpos = 0;
744     psc->outend = psc->inpos;
745     psc->state = PSC_BufferOut;
746     psc->inpos = 0;
747 }
748 
pscompact_copyinout_bin(pscompstate * psc)749 static void pscompact_copyinout_bin(pscompstate *psc)
750 {
751     pscompact_copyinout(psc);
752     psc->noescape = 1;
753 }
754 
pscompact_hex2ascii(pscompstate * psc)755 static void pscompact_hex2ascii(pscompstate *psc)
756 {
757     int i = 0;
758     int o = 0;
759 
760     while (i < psc->inpos) {
761         int v = 0;
762 
763         if ((psc->bufferin[i] >= '0') && (psc->bufferin[i] <= '9')) {
764             v = (psc->bufferin[i] - '0')<<4;
765         } else if ((psc->bufferin[i] >= 'a') && (psc->bufferin[i] <= 'f')) {
766             v = (psc->bufferin[i] - 'a' + 10)<<4;
767         } else if ((psc->bufferin[i] >= 'A') && (psc->bufferin[i] <= 'F')) {
768             v = (psc->bufferin[i] - 'A' + 10)<<4;
769         } else {
770             fprintf(stderr, "Malformed hexstring in pscompaction!\n");
771             exit(1);
772         }
773         i++;
774 
775         if (i == psc->inpos) {
776             /* End of string */
777         } else if ((psc->bufferin[i] >= '0') && (psc->bufferin[i] <= '9')) {
778             v += psc->bufferin[i] - '0';
779         } else if ((psc->bufferin[i] >= 'a') && (psc->bufferin[i] <= 'f')) {
780             v += psc->bufferin[i] - 'a' + 10;
781         } else if ((psc->bufferin[i] >= 'A') && (psc->bufferin[i] <= 'F')) {
782             v += psc->bufferin[i] - 'A' + 10;
783         } else {
784             fprintf(stderr, "Malformed hexstring in pscompaction!\n");
785             exit(1);
786         }
787         i++;
788         psc->bufferin[o++] = v;
789     }
790     psc->inpos = o;
791 }
792 
793 static const char *pscompact_names[] =
794 {
795   "abs",
796   "add",
797   "aload",
798   "anchorsearch",
799   "and",
800   "arc",
801   "arcn",
802   "arct",
803   "arcto",
804   "array",
805   "ashow",
806   "astore",
807   "awidthshow",
808   "begin",
809   "bind",
810   "bitshift",
811   "ceiling",
812   "charpath",
813   "clear",
814   "cleartomark",
815   "clip",
816   "clippath",
817   "closepath",
818   "concat",
819   "concatmatrix",
820   "copy",
821   "count",
822   "counttomark",
823   "currentcmykcolor",
824   "currentdash",
825   "currentdict",
826   "currentfile",
827   "currentfont",
828   "currentgray",
829   "currentgstate",
830   "currenthsbcolor",
831   "currentlinecap",
832   "currentlinejoin",
833   "currentlinewidth",
834   "currentmatrix",
835   "currentpoint",
836   "currentrgbcolor",
837   "currentshared",
838   "curveto",
839   "cvi",
840   "cvlit",
841   "cvn",
842   "cvr",
843   "cvrs",
844   "cvs",
845   "cvx",
846   "def",
847   "defineusername",
848   "dict",
849   "div",
850   "dtransform",
851   "dup",
852   "end",
853   "eoclip",
854   "eofill",
855   "eoviewclip",
856   "eq",
857   "exch",
858   "exec",
859   "exit",
860   "file",
861   "fill",
862   "findfont",
863   "flattenpath",
864   "floor",
865   "flush",
866   "flushfile",
867   "for",
868   "forall",
869   "ge",
870   "get",
871   "getinterval",
872   "grestore",
873   "gsave",
874   "gstate",
875   "gt",
876   "identmatrix",
877   "idiv",
878   "idtransform",
879   "if",
880   "ifelse",
881   "image",
882   "imagemask",
883   "index",
884   "ineofill",
885   "infill",
886   "initviewclip",
887   "inueofill",
888   "inufill",
889   "invertmatrix",
890   "itransform",
891   "known",
892   "le",
893   "length",
894   "lineto",
895   "load",
896   "loop",
897   "lt",
898   "makefont",
899   "matrix",
900   "maxlength",
901   "mod",
902   "moveto",
903   "mul",
904   "ne",
905   "neg",
906   "newpath",
907   "not",
908   "null",
909   "or",
910   "pathbbox",
911   "pathforall",
912   "pop",
913   "print",
914   "printobject",
915   "put",
916   "putinterval",
917   "rcurveto",
918   "read",
919   "readhexstring",
920   "readline",
921   "readstring",
922   "rectclip",
923   "rectfill",
924   "rectstroke",
925   "rectviewclip",
926   "repeat",
927   "restore",
928   "rlineto",
929   "rmoveto",
930   "roll",
931   "rotate",
932   "round",
933   "save",
934   "scale",
935   "scalefont",
936   "search",
937   "selectfont",
938   "setbbox",
939   "setcachedevice",
940   "setcachedevice2",
941   "setcharwidth",
942   "setcmykcolor",
943   "setdash",
944   "setfont",
945   "setgray",
946   "setgstate",
947   "sethsbcolor",
948   "setlinecap",
949   "setlinejoin",
950   "setlinewidth",
951   "setmatrix",
952   "setrgbcolor",
953   "setshared",
954   "shareddict",
955   "show",
956   "showpage",
957   "stop",
958   "stopped",
959   "store",
960   "string",
961   "stringwidth",
962   "stroke",
963   "strokepath",
964   "sub",
965   "systemdict",
966   "token",
967   "transform",
968   "translate",
969   "truncate",
970   "type",
971   "uappend",
972   "ucache",
973   "ueofill",
974   "ufill",
975   "undef",
976   "upath",
977   "userdict",
978   "ustroke",
979   "viewclip",
980   "viewclippath",
981   "where",
982   "widthshow",
983   "write",
984   "writehexstring",
985   "writeobject",
986   "writestring",
987   "wtranslation",
988   "xor",
989   "xshow",
990   "xyshow",
991   "yshow",
992   "FontDirectory",
993   "SharedFontDirectory",
994   "Courier%",
995   "Courier-Bold",
996   "Courier-BoldOblique",
997   "Courier-Oblique",
998   "Helvetica",
999   "Helvetica-Bold",
1000   "Helvetica-BoldOblique",
1001   "Helvetica-Oblique",
1002   "Symbol",
1003   "Times-Bold",
1004   "Times-BoldItalic",
1005   "Times-Italic",
1006   "Times-Roman",
1007   "execuserobject",
1008   "currentcolor",
1009   "currentcolorspace",
1010   "currentglobal",
1011   "execform",
1012   "filter",
1013   "findresource",
1014   "globaldict",
1015   "makepattern",
1016   "setcolor",
1017   "setcolorspace",
1018   "setglobal",
1019   "setpagedevice",
1020   "setpattern"
1021 };
1022 
pscompact_isname(pscompstate * psc,int * i)1023 static int pscompact_isname(pscompstate *psc, int *i)
1024 {
1025     int off = 0;
1026     int n;
1027 
1028     if (psc->bufferin[0] == '/')
1029         off = 1;
1030     for (n = 0; n < sizeof(pscompact_names)/sizeof(char *); n++) {
1031         if (strncmp(pscompact_names[n], (const char *)&psc->bufferin[off], psc->inpos-off) == 0) {
1032             /* Match! */
1033             if (off)
1034                 *i = -1-n;
1035             else
1036                 *i = n;
1037             return 1;
1038         }
1039     }
1040     return 0;
1041 }
1042 
pscompact_isint(pscompstate * psc,int * i)1043 static int pscompact_isint(pscompstate *psc, int *i)
1044 {
1045     int pos = 0;
1046 
1047     if ((psc->bufferin[0] == '+') || (psc->bufferin[0] == '-')) {
1048         pos = 1;
1049     }
1050     if (pos >= psc->inpos)
1051         return 0;
1052     if ((psc->inpos > pos+3) &&
1053         (strncmp((const char *)&psc->bufferin[pos], "16#", 3) == 0)) {
1054         /* hex */
1055         int v = 0;
1056         pos += 3;
1057         while (pos < psc->inpos) {
1058             if ((psc->bufferin[pos] >= '0') && (psc->bufferin[pos] <= '9'))
1059                 v = v*16 + psc->bufferin[pos] - '0';
1060             else if ((psc->bufferin[pos] >= 'a') && (psc->bufferin[pos] <= 'f'))
1061                 v = v*16 + psc->bufferin[pos] - 'a' + 10;
1062             else if ((psc->bufferin[pos] >= 'A') && (psc->bufferin[pos] <= 'F'))
1063                 v = v*16 + psc->bufferin[pos] - 'A' + 10;
1064             else
1065                 return 0;
1066             pos++;
1067         }
1068         if (psc->bufferin[0] == '-')
1069             v = -v;
1070         *i = v;
1071         return 1;
1072     }
1073 
1074     do {
1075         if ((psc->bufferin[pos] < '0') || (psc->bufferin[pos] > '9'))
1076             return 0;
1077         pos++;
1078     } while (pos < psc->inpos);
1079 
1080     if (psc->inpos == psc->inmax) {
1081         psc->inmax *= 2;
1082         psc->bufferin = realloc(psc->bufferin, psc->inmax);
1083     }
1084     psc->bufferin[psc->inpos] = 0;
1085     *i = atoi((const char *)psc->bufferin);
1086 
1087     /* Check for 32bit overflow */
1088     if (psc->inpos > 9) {
1089         char *end;
1090         double d = strtod((const char *)psc->bufferin, &end);
1091         if (d != (double)(int)*i)
1092             return 0;
1093     }
1094 
1095     return 1;
1096 }
1097 
pscompact_isfloat(pscompstate * psc,float * f)1098 static int pscompact_isfloat(pscompstate *psc, float *f)
1099 {
1100     int pos = 0;
1101     int point = 0;
1102 
1103     if ((psc->bufferin[0] == '+') || (psc->bufferin[0] == '-')) {
1104         pos = 1;
1105     }
1106     if (pos >= psc->inpos)
1107         return 0;
1108     do {
1109         if ((psc->bufferin[pos] >= '0') && (psc->bufferin[pos] <= '9')) {
1110             /* Digits are OK */
1111         } else if ((psc->bufferin[pos] == '.') && (point == 0)) {
1112             /* as are points, but only the first one */
1113             point = 1;
1114         } else {
1115             /* Anything else is a failure */
1116             return 0;
1117         }
1118         pos++;
1119     } while (pos < psc->inpos);
1120 
1121     if (psc->inpos == psc->inmax) {
1122         psc->inmax *= 2;
1123         psc->bufferin = realloc(psc->bufferin, psc->inmax);
1124     }
1125     psc->bufferin[psc->inpos] = 0;
1126     *f = atof((const char *)psc->bufferin);
1127     return 1;
1128 }
1129 
pscompact_getcompactedblock(pscompstate * psc,unsigned char * ubuf,unsigned long ulen)1130 static unsigned long pscompact_getcompactedblock(pscompstate *psc, unsigned char *ubuf, unsigned long ulen)
1131 {
1132     unsigned char *out;
1133     int c;
1134 
1135     if (ulen == 0)
1136         return 0;
1137     out = ubuf;
1138     do {
1139         switch (psc->state) {
1140             case PSC_BufferIn:
1141                 c = psc->pgetc(psc->file);
1142                 if ((c <= 32) || (c == EOF)) {
1143                     /* Whitespace */
1144                     if (psc->inpos == 0) {
1145                         /* Leading whitespace, just bin it */
1146                         break;
1147                     }
1148                 } else if (c == '(') {
1149                     /* Start of a string */
1150                     if (psc->inpos == 0) {
1151                         /* Go into string state */
1152                         psc->state = PSC_InString;
1153                         psc->paren = 1;
1154                         break;
1155                     }
1156                 } else if (c == '>') {
1157                     /* End of a dictionary */
1158                     if (psc->inpos == 0) {
1159                         /* Just output it (with no whitespace) */
1160                         *out++ = c;
1161                         break;
1162                     }
1163                 } else if ((c == '{') || (c == '}') ||
1164                            (c == '[') || (c == ']')) {
1165                     /* Stand alone token bytes */
1166                     if (psc->inpos == 0) {
1167                         /* Process now and be done with it */
1168                         *out++ = c;
1169                         psc->wasascii = 0;
1170                         break;
1171                     }
1172                 } else if ((c >= 128) && (c <= 131)) {
1173                     fprintf(stderr, "Can't compact files with binary object sequences in!");
1174                     exit(1);
1175                 } else if ((c == 132) || (c == 133) || (c == 138) || (c == 139) || (c == 140)) {
1176                     /* 32 bit integers or reals */
1177                     if (psc->inpos == 0) {
1178                         pscompact_copy(psc, c, 4);
1179                         break;
1180                     }
1181                 } else if ((c == 134) || (c == 135)) {
1182                     /* 16 bit integers */
1183                     if (psc->inpos == 0) {
1184                         pscompact_copy(psc, c, 2);
1185                         break;
1186                     }
1187                 } else if ((c == 136) || (c == 141) || (c == 145) ||
1188                            (c == 146) || (c == 147) || (c == 148)) {
1189                     /* 8 bit integers or bools or pool name */
1190                     if (psc->inpos == 0) {
1191                         pscompact_copy(psc, c, 1);
1192                         break;
1193                     }
1194                 } else if (c == 137) {
1195                     /* fixed point */
1196                     if (psc->inpos == 0) {
1197                         int r = psc->pgetc(psc->file);
1198                         if (r & 32) {
1199                             pscompact_copy2(psc, c, r, 2);
1200                         } else {
1201                             pscompact_copy2(psc, c, r, 4);
1202                         }
1203                         break;
1204                     }
1205                 } else if (c == 142) {
1206                     /* short string */
1207                     if (psc->inpos == 0) {
1208                         int n = psc->pgetc(psc->file);
1209                         pscompact_copy2(psc, c, n, n);
1210                         break;
1211                     }
1212                 } else if (c == 143) {
1213                     /* long string */
1214                     if (psc->inpos == 0) {
1215                         int n1 = psc->pgetc(psc->file);
1216                         int n2 = psc->pgetc(psc->file);
1217                         pscompact_copy3(psc, c, n1, n2, (n1<<8) + n2);
1218                         break;
1219                     }
1220                 } else if (c == 144) {
1221                     /* long string */
1222                     if (psc->inpos == 0) {
1223                         int n1 = psc->pgetc(psc->file);
1224                         int n2 = psc->pgetc(psc->file);
1225                         pscompact_copy3(psc, c, n1, n2, n1 + (n2<<8));
1226                         break;
1227                     }
1228                 } else if ((c >= 149) && (c <= 159)) {
1229                     fprintf(stderr, "Can't compact files with binary postscript byte %d in!", c);
1230                     exit(1);
1231                 } else if (c == '%') {
1232                     if (psc->inpos == 0) {
1233                         psc->state = PSC_InComment;
1234                         break;
1235                     }
1236                 } else if (c == '<') {
1237                     if (psc->inpos == 0) {
1238                         psc->state = PSC_InHexString;
1239                         break;
1240                     }
1241                 } else if ((c == '/') &&
1242                            (psc->inpos > 0) &&
1243                            (psc->bufferin[psc->inpos-1] != '/')) {
1244                     /* We hit a / while not in a prefix of them - stop the
1245                      * buffering. */
1246                 } else {
1247                     /* Stick c into the buffer and continue */
1248                     pscompact_buffer(psc, c);
1249                     break;
1250                 }
1251                 /* If we reach here, we have a complete buffer full. We need
1252                  * to write it (or something equivalent to it) out. */
1253                 if (c > 32) {
1254                     /* Put c back into the file to process next time */
1255                     psc->unpgetc(c, psc->file);
1256                 }
1257                 if (psc->binary) {
1258                     int i;
1259                     float f;
1260                     /* Is it a number? */
1261                     if (pscompact_isint(psc, &i)) {
1262                         if (psc->firstnum) {
1263                             /* Don't alter the first number in a file */
1264                             psc->firstnum = 0;
1265                         } else if ((i >= -128) && (i <= 127)) {
1266                             /* Encode as a small integer */
1267                             psc->bufferout[0] = 136;
1268                             psc->bufferout[1] = i & 255;
1269                             psc->inpos = 0;
1270                             psc->outend = 2;
1271                             psc->wasascii = 0;
1272                             psc->noescape = 1;
1273                             psc->state = PSC_BufferOut;
1274                             break;
1275                         } else if ((i >= -0x8000) && (i <= 0x7FFF)) {
1276                             /* Encode as a 16 bit integer */
1277                             psc->bufferout[0] = 135;
1278                             psc->bufferout[1] = i & 255;
1279                             psc->bufferout[2] = (i>>8) & 255;
1280                             psc->inpos = 0;
1281                             psc->outpos = 0;
1282                             psc->outend = 3;
1283                             psc->wasascii = 0;
1284                             psc->noescape = 1;
1285                             psc->state = PSC_BufferOut;
1286                             break;
1287                         } else {
1288                             /* Encode as a 32 bit integer */
1289                             psc->bufferout[0] = 133;
1290                             psc->bufferout[1] = i & 255;
1291                             psc->bufferout[2] = (i>>8) & 255;
1292                             psc->bufferout[3] = (i>>16) & 255;
1293                             psc->bufferout[4] = (i>>24) & 255;
1294                             psc->inpos = 0;
1295                             psc->outpos = 0;
1296                             psc->outend = 5;
1297                             psc->wasascii = 0;
1298                             psc->noescape = 1;
1299                             psc->state = PSC_BufferOut;
1300                             break;
1301                         }
1302                     } else if ((sizeof(float) == 4) && pscompact_isfloat(psc, &f)) {
1303                         /* Encode as a 32 bit float */
1304                         union {
1305                             float f;
1306                             unsigned char c[4];
1307                         } fc;
1308                         fc.f = 1.0;
1309                         if ((fc.c[0] == 0) && (fc.c[1] == 0) &&
1310                             (fc.c[2] == 0x80) && (fc.c[3] == 0x3f)) {
1311                             fc.f = f;
1312                             psc->bufferout[0] = 139;
1313                             psc->bufferout[1] = (char)fc.c[0];
1314                             psc->bufferout[2] = (char)fc.c[1];
1315                             psc->bufferout[3] = (char)fc.c[2];
1316                             psc->bufferout[4] = (char)fc.c[3];
1317                             psc->inpos = 0;
1318                             psc->outpos = 0;
1319                             psc->outend = 5;
1320                             psc->wasascii = 0;
1321                             psc->noescape = 1;
1322                             psc->state = PSC_BufferOut;
1323                             break;
1324                         } else if ((fc.c[0] == 0x3f) && (fc.c[1] == 0x80) &&
1325                                    (fc.c[2] == 0) && (fc.c[3] == 0)) {
1326                             fc.f = f;
1327                             psc->bufferout[0] = 139;
1328                             psc->bufferout[1] = (char)fc.c[3];
1329                             psc->bufferout[2] = (char)fc.c[2];
1330                             psc->bufferout[3] = (char)fc.c[1];
1331                             psc->bufferout[4] = (char)fc.c[0];
1332                             psc->inpos = 0;
1333                             psc->outpos = 0;
1334                             psc->outend = 5;
1335                             psc->wasascii = 0;
1336                             psc->noescape = 1;
1337                             psc->state = PSC_BufferOut;
1338                             break;
1339                         }
1340                     }
1341                     if ((psc->inpos == 4) &&
1342                         (strncmp((const char *)psc->bufferin, "true", 4) == 0)) {
1343                         /* Encode as a 32 bit integer */
1344                         psc->bufferout[0] = 141;
1345                         psc->bufferout[1] = 1;
1346                         psc->inpos = 0;
1347                         psc->outpos = 0;
1348                         psc->outend = 2;
1349                         psc->wasascii = 0;
1350                         psc->noescape = 1;
1351                         psc->state = PSC_BufferOut;
1352                         break;
1353                     }
1354                     if ((psc->inpos == 5) &&
1355                         (strncmp((const char *)psc->bufferin, "false", 5) == 0)) {
1356                         /* Encode as a 32 bit integer */
1357                         psc->bufferout[0] = 141;
1358                         psc->bufferout[1] = 0;
1359                         psc->inpos = 0;
1360                         psc->outpos = 0;
1361                         psc->outend = 2;
1362                         psc->wasascii = 0;
1363                         psc->noescape = 1;
1364                         psc->state = PSC_BufferOut;
1365                         break;
1366                     }
1367                     if (psc->names && pscompact_isname(psc, &i)) {
1368                         /* Encode as a name lookup */
1369                         if (i >= 0) {
1370                             /* Executable */
1371                             psc->bufferout[0] = 146;
1372                             psc->bufferout[1] = i;
1373                         } else {
1374                             /* Literal */
1375                             psc->bufferout[0] = 145;
1376                             psc->bufferout[1] = 1-i;
1377                         }
1378                         psc->inpos = 0;
1379                         psc->outpos = 0;
1380                         psc->outend = 2;
1381                         psc->wasascii = 0;
1382                         psc->noescape = 1;
1383                         psc->state = PSC_BufferOut;
1384                         break;
1385                     }
1386                 }
1387                 if ((psc->wasascii) && (psc->bufferin[0]!='/'))
1388                     *out++ = 32;
1389                 pscompact_copyinout(psc);
1390                 psc->wasascii = 1;
1391                 break;
1392             case PSC_BufferOut:
1393             {
1394                 unsigned char c = psc->bufferout[psc->outpos++];
1395                 if (psc->noescape) {
1396                 } else if ((c == 10) && (psc->outpos < psc->outend)) {
1397                     if (!psc->escaping) {
1398                         c = '\\';
1399                         psc->outpos--;
1400                         psc->escaping = 1;
1401                     } else {
1402                         c = 'n';
1403                         psc->escaping = 0;
1404                     }
1405                 } else if (c == 9) {
1406                     if (!psc->escaping) {
1407                         c = '\\';
1408                         psc->outpos--;
1409                         psc->escaping = 1;
1410                     } else {
1411                         c = 't';
1412                         psc->escaping = 0;
1413                     }
1414                 } else if (c == 8) {
1415                     if (!psc->escaping) {
1416                         c = '\\';
1417                         psc->outpos--;
1418                         psc->escaping = 1;
1419                     } else {
1420                         c = 'b';
1421                         psc->escaping = 0;
1422                     }
1423                 } else if (c == 12) {
1424                     if (!psc->escaping) {
1425                         c = '\\';
1426                         psc->outpos--;
1427                         psc->escaping = 1;
1428                     } else {
1429                         c = 'f';
1430                         psc->escaping = 0;
1431                     }
1432                 } else if (c == 13) {
1433                     if (!psc->escaping) {
1434                         c = '\\';
1435                         psc->outpos--;
1436                         psc->escaping = 1;
1437                     } else {
1438                         c = 'r';
1439                         psc->escaping = 0;
1440                     }
1441                 } else if (c == '\\') {
1442                     if (!psc->escaping) {
1443                         psc->outpos--;
1444                         psc->escaping = 1;
1445                     } else {
1446                         psc->escaping = 0;
1447                     }
1448                 } else if ((c == ')') && (psc->outpos < psc->outend)) {
1449                     if (!psc->escaping) {
1450                         c = '\\';
1451                         psc->outpos--;
1452                         psc->escaping = 1;
1453                     } else {
1454                         c = ')';
1455                         psc->escaping = 0;
1456                     }
1457                 } else if ((c == '(') && (psc->outpos > 1)) {
1458                     if (!psc->escaping) {
1459                         c = '\\';
1460                         psc->outpos--;
1461                         psc->escaping = 1;
1462                     } else {
1463                         c = '(';
1464                         psc->escaping = 0;
1465                     }
1466                 } else if (((c < 32) && (c != 10)) || (c >= 128)) {
1467                     if (psc->escaping == 0) {
1468                         c = '\\';
1469                         psc->outpos--;
1470                         psc->escaping = 1;
1471                     } else if (psc->escaping == 1) {
1472                         c = '0' + ((c >> 6)&3);
1473                         psc->outpos--;
1474                         psc->escaping = 2;
1475                     } else if (psc->escaping == 2) {
1476                         c = '0' + ((c >> 3)&7);
1477                         psc->outpos--;
1478                         psc->escaping = 3;
1479                     } else if (psc->escaping == 3) {
1480                         c = '0' + (c&7);
1481                         psc->escaping = 0;
1482                     }
1483                 }
1484                 *out++ = c;
1485                 if (psc->outpos == psc->outend) {
1486                     psc->outpos = 0;
1487                     psc->outend = 0;
1488                     psc->noescape = 0;
1489                     psc->state = PSC_BufferIn;
1490                 }
1491                 break;
1492             }
1493             case PSC_BufferCopy:
1494                 if (psc->outpos < psc->outend) {
1495                     *out++ = psc->bufferout[psc->outpos++];
1496                     break;
1497                 }
1498                 *out++ = psc->pgetc(psc->file);
1499                 psc->buffercopy--;
1500                 if (psc->buffercopy == 0) {
1501                     psc->outpos = 0;
1502                     psc->outend = 0;
1503                     psc->state = PSC_BufferIn;
1504                 }
1505                 break;
1506             case PSC_InString:
1507                 c = psc->pgetc(psc->file);
1508                 if ((c == ')') && (--psc->paren == 0)) {
1509                     psc->wasascii = 0;
1510                     if (psc->binary) {
1511                         /* Write out the string as binary */
1512                         if (psc->inpos < 256) {
1513                             pscompact_bufferatstart(psc, psc->inpos);
1514                             pscompact_bufferatstart(psc, 142);
1515                             pscompact_copyinout_bin(psc);
1516                             break;
1517                         } else if (psc->inpos < 65536) {
1518                             int count = psc->inpos;
1519                             pscompact_bufferatstart(psc, count>>8);
1520                             pscompact_bufferatstart(psc, count & 255);
1521                             pscompact_bufferatstart(psc, 144);
1522                             pscompact_copyinout_bin(psc);
1523                             break;
1524                         }
1525                     }
1526                     /* if all else fails, just write it out as an ascii
1527                      * string. */
1528                     pscompact_bufferatstart(psc, '(');
1529                     pscompact_buffer(psc, ')');
1530                     pscompact_copyinout(psc);
1531                     break;
1532                 } else if (c == '\\') {
1533                     c = psc->pgetc(psc->file);
1534                     if (c == 10)
1535                         break;
1536                     else if (c == 'b')
1537                         c = 8;
1538                     else if (c == 't')
1539                         c = 9;
1540                     else if (c == 'n')
1541                         c = 10;
1542                     else if (c == 'f')
1543                         c = 12;
1544                     else if (c == 'r')
1545                         c = 13;
1546                     else if ((c >= '0') && (c <= '7')) {
1547                         int d;
1548                         c = (c - '0');
1549                         d = psc->pgetc(psc->file);
1550                         if ((d >= '0') && (d <= '7')) {
1551                             c = (c<<3) + (d-'0');
1552                             d = psc->pgetc(psc->file);
1553                             if ((d >= '0') && (d <= '7')) {
1554                                 c = (c<<3) + (d-'0');
1555                             } else {
1556                                 psc->unpgetc(d, psc->file);
1557                             }
1558                         } else {
1559                             psc->unpgetc(d, psc->file);
1560                         }
1561                         c &= 0xFF;
1562                     }
1563                 } else if (c == '(') {
1564                     psc->paren++;
1565                 }
1566                 pscompact_buffer(psc, c);
1567                 break;
1568             case PSC_InComment:
1569                 /* Watch for an EOL, otherwise swallow */
1570                 c = psc->pgetc(psc->file);
1571                 if ((c == 13) || (c == 10)) {
1572                     if ((psc->inpos >= 3) &&
1573                         (strncmp((const char *)psc->bufferin, "END", 3) == 0)) {
1574                         /* Special comment to retain */
1575                         pscompact_bufferatstart(psc, '%');
1576                         pscompact_buffer(psc, 10);
1577                         pscompact_copyinout(psc);
1578                         break;
1579                     }
1580                     if ((psc->inpos >= 7) &&
1581                         (strncmp((const char *)psc->bufferin, "NAMESOK", 7) == 0)) {
1582                         psc->names = 1;
1583                         pscompact_bufferatstart(psc, '%');
1584                         pscompact_buffer(psc, 10);
1585                         pscompact_copyinout(psc);
1586                         break;
1587                     }
1588                     if ((psc->inpos >= 8) &&
1589                         (strncmp((const char *)psc->bufferin, "BINARYOK", 8) == 0)) {
1590                         psc->binary = 1;
1591                         pscompact_bufferatstart(psc, '%');
1592                         pscompact_buffer(psc, 10);
1593                         pscompact_copyinout(psc);
1594                         break;
1595                     }
1596                     /* Throw the buffered line away, and go back to buffering */
1597                     psc->inpos = 0;
1598                     psc->state = PSC_BufferIn;
1599                     break;
1600                 }
1601                 pscompact_buffer(psc, c);
1602                 break;
1603             case PSC_InHexString:
1604                 c = psc->pgetc(psc->file);
1605                 if (c == '<') {
1606                     /* Dictionary */
1607                     pscompact_copy2(psc, '<', '<', 0);
1608                     break;
1609                 } else if (c == '~') {
1610                     /* FIXME: ASCII85 encoded! */
1611                     fprintf(stderr, "ASCII85 encoded strings unsupported in pscompaction\n");
1612                     exit(1);
1613                 } else if (c == '>') {
1614                     psc->wasascii = 0;
1615                     if (psc->binary) {
1616                         pscompact_hex2ascii(psc);
1617                         if (psc->inpos < 256) {
1618                             pscompact_bufferatstart(psc, psc->inpos);
1619                             pscompact_bufferatstart(psc, 142);
1620                             pscompact_copyinout_bin(psc);
1621                         } else if (psc->inpos < 65536) {
1622                             int count = psc->inpos;
1623                             pscompact_bufferatstart(psc, count>>8);
1624                             pscompact_bufferatstart(psc, count & 255);
1625                             pscompact_bufferatstart(psc, 144);
1626                             pscompact_copyinout_bin(psc);
1627                         } else {
1628                             fprintf(stderr, "HexString more than 64K in pscompaction\n");
1629                             exit(1);
1630                         }
1631                         break;
1632                     }
1633                     /* If all else fails, write it out as an ascii hexstring
1634                      * again */
1635                     pscompact_bufferatstart(psc, '<');
1636                     pscompact_buffer(psc, '>');
1637                     pscompact_copyinout(psc);
1638                     break;
1639                 } else if (c <= 32) {
1640                     /* Swallow whitespace */
1641                     break;
1642                 } else if (((c >= 'A') && (c <= 'Z')) ||
1643                            ((c >= 'a') && (c <= 'z')) ||
1644                            ((c >= '0') && (c <= '9'))) {
1645                     pscompact_buffer(psc, c);
1646                 } else {
1647                     fprintf(stderr, "Unexpected char when parsing hexstring in pscompaction\n");
1648                     exit(1);
1649                 }
1650                 break;
1651         }
1652     } while ((out-ubuf != ulen) && (!psc->peof(psc->file)));
1653     return out-ubuf;
1654 }
1655 
cmpstringp(const void * p1,const void * p2)1656 int cmpstringp(const void *p1, const void *p2)
1657 {
1658    /* The actual arguments to this function are "pointers to
1659       pointers to char", but strcmp(3) arguments are "pointers
1660       to char", hence the following cast plus dereference */
1661 
1662    return strcmp(* (char * const *) p1, * (char * const *) p2);
1663 }
1664 
1665 /* This relies on the gp_enumerate_* which should not return directories, nor	*/
1666 /* should it recurse into directories (unlike Adobe's implementation)		*/
1667 /* paths are checked to see if they are an ordinary file or a path		*/
process_path(char * path,const char * os_prefix,const char * rom_prefix,Xlist_element * Xlist_head,int compression,int compaction,int * inode_count,int * totlen,FILE * out,split_data * splits,int verbose)1668 void process_path(char *path, const char *os_prefix, const char *rom_prefix,
1669                   Xlist_element *Xlist_head, int compression,
1670                   int compaction, int *inode_count, int *totlen, FILE *out,
1671                   split_data *splits, int verbose)
1672 {
1673     int i, namelen, excluded, save_count=*inode_count;
1674     Xlist_element *Xlist_scan;
1675     char *prefixed_path;
1676     char *found_path, *rom_filename;
1677     file_enum *pfenum;
1678     int ret, block, blocks;
1679     romfs_inode *node;
1680     unsigned char *ubuf, *cbuf;
1681     unsigned long ulen, clen;
1682     FILE *in;
1683     unsigned long psc_len;
1684     pscompstate psc = { 0 };
1685     unsigned long numfiles = 0;
1686     char **foundfiles = NULL, *temp;
1687 
1688     prefixed_path = malloc(PATH_STR_LEN);
1689     found_path = malloc(PATH_STR_LEN);
1690     rom_filename = malloc(PATH_STR_LEN);
1691     ubuf = malloc(ROMFS_BLOCKSIZE);
1692     cbuf = malloc(ROMFS_CBUFSIZE);
1693     if (ubuf == NULL || cbuf == NULL || prefixed_path == NULL ||
1694                 found_path == NULL || rom_filename == NULL) {
1695         printf("malloc fail in process_path\n");
1696         exit(1);
1697     }
1698     prefix_add(os_prefix, path, prefixed_path);
1699     prefix_add(rom_prefix, "", rom_filename);
1700     strcpy(rom_filename, rom_prefix);
1701 
1702     /* check for the file on the Xlist */
1703     pfenum = gp_enumerate_files_init((gs_memory_t *)&minimal_memory, prefixed_path,
1704                          strlen(prefixed_path));
1705     if (pfenum == NULL) {
1706         printf("gp_enumerate_files_init failed.\n");
1707         exit(1);
1708     }
1709     while ((namelen=gp_enumerate_files_next((gs_memory_t *)&minimal_memory, pfenum, found_path, 1024)) >= 0) {
1710         excluded = 0;
1711         found_path[namelen] = 0;		/* terminate the string */
1712         /* check to see if the tail of the path we found matches one on the exclusion list */
1713         for (Xlist_scan = Xlist_head; Xlist_scan != NULL; Xlist_scan = Xlist_scan->next) {
1714             if (strlen(found_path) >= strlen(Xlist_scan->path)) {
1715                 if (strcmp(Xlist_scan->path, found_path+strlen(found_path)-strlen(Xlist_scan->path)) == 0) {
1716                     excluded = 1;
1717                     break;
1718                 }
1719             }
1720         }
1721         if (excluded)
1722             continue;
1723 
1724         numfiles++;
1725         temp = realloc(foundfiles, sizeof(char *) * numfiles);
1726         if (temp == NULL) {
1727             free(cbuf);
1728             free(ubuf);
1729             free(found_path);
1730             free(foundfiles);
1731             free(prefixed_path);
1732             free(rom_filename);
1733             printf("realloc failed in process_path.\n");
1734             exit(1);
1735         }
1736         foundfiles = (char **)temp;
1737         foundfiles[numfiles - 1] = strdup(found_path);
1738     }
1739 
1740     qsort(foundfiles, numfiles, sizeof(char *), cmpstringp);
1741 
1742     for (i = 0; i < numfiles; i++) {
1743         char *fpath = foundfiles[i];
1744 
1745         /* process a file */
1746         node = calloc(1, sizeof(romfs_inode));
1747         /* get info for this file */
1748         in = fopen(fpath, "rb");
1749         if (in == NULL) {
1750             printf("unable to open file for processing: %s\n", fpath);
1751             continue;
1752         }
1753         /* printf("compacting %s\n", fpath); */
1754         /* rom_filename + strlen(rom_prefix) is first char after the new prefix we want to add */
1755         /* fpath + strlen(os_prefix) is the file name after the -P prefix */
1756         rom_filename[strlen(rom_prefix)] = 0;		/* truncate afater prefix */
1757         strcat(rom_filename, fpath + strlen(os_prefix));
1758         node->name = rom_filename;	/* without -P prefix, with -d rom_prefix */
1759         fseek(in, 0, SEEK_END);
1760         node->disc_length = node->length = ftell(in);
1761         blocks = (node->length+ROMFS_BLOCKSIZE-1) / ROMFS_BLOCKSIZE + 1;
1762         node->data_lengths = calloc(blocks, sizeof(*node->data_lengths));
1763         node->data = calloc(blocks, sizeof(*node->data));
1764         fclose(in);
1765         in = fopen(fpath, "rb");
1766         ulen = strlen(fpath);
1767         block = 0;
1768         psc_len = 0;
1769         if (compaction)
1770             pscompact_start(&psc, (psc_getc*)&fgetc, (psc_ungetc*)&ungetc, (psc_feof*)&feof, in, 1, 0, 0);
1771         while (!feof(in)) {
1772             if (compaction)
1773                 ulen = pscompact_getcompactedblock(&psc, ubuf, ROMFS_BLOCKSIZE);
1774             else
1775                 ulen = fread(ubuf, 1, ROMFS_BLOCKSIZE, in);
1776             psc_len += ulen;
1777             if (!ulen) break;
1778             clen = ROMFS_CBUFSIZE;
1779             if (compression) {
1780                 /* compress data here */
1781                 ret = compress(cbuf, &clen, ubuf, ulen);
1782                 if (ret != Z_OK) {
1783                     printf("error compressing data block!\n");
1784                     exit(1);
1785                 }
1786             } else {
1787                 memcpy(cbuf, ubuf, ulen);
1788                 clen = ulen;
1789             }
1790             node->data_lengths[block] = clen;
1791             node->data[block] = malloc(clen);
1792             memcpy(node->data[block], cbuf, clen);
1793             block++;
1794         }
1795         fclose(in);
1796         if (compaction) {
1797             /* printf("%s: Compaction saved %d bytes (before compression)\n",
1798              *        fpath, node->length - psc_len); */
1799             pscompact_end(&psc);
1800             node->length = psc_len;
1801         }
1802 
1803         /* write out data for this file */
1804         inode_write(out, node, compression, *inode_count, totlen, splits, verbose);
1805         /* clean up */
1806         inode_clear(node);
1807         free(node);
1808         (*inode_count)++;
1809         free(fpath);
1810     }
1811     free(cbuf);
1812     free(ubuf);
1813     free(found_path);
1814     free(foundfiles);
1815     free(prefixed_path);
1816     free(rom_filename);
1817     if (save_count == *inode_count) {
1818         printf("warning: no files found from path '%s%s'\n", os_prefix, path);
1819     }
1820 }
1821 
1822 /*
1823  * Utility for merging all the Ghostscript initialization files (gs_*.ps)
1824  * into a single file.
1825  *
1826  * The following special constructs are recognized in the input files:
1827  *	%% Replace[%| ]<#lines> (<psfile>)
1828  *	%% Replace[%| ]<#lines> INITFILES
1829  * '%' after Replace means that the file to be included intact.
1830  * If the first non-comment, non-blank line in a file ends with the
1831  * LanguageLevel 2 constructs << or <~, its section of the merged file
1832  * will begin with
1833  *	currentobjectformat 1 setobjectformat
1834  * and end with
1835  *	setobjectformat
1836  * and if any line then ends with with <, the following ASCIIHex string
1837  * will be converted to a binary token.
1838  */
1839 /* Forward references */
1840 void merge_to_ps(const char *os_prefix, const char *inname, FILE * in, FILE * config, int verbose);
1841 int write_init(char *);
1842 bool rl(FILE * in, char *str, int len);
1843 void wsc(const byte *str, int len);
1844 void ws(const byte *str, int len);
1845 void wl(const char *str);
1846 char *doit(char *line, bool intact);
1847 void hex_string_to_binary(FILE *in);
1848 void flush_buf(char *buf);
1849 void mergefile(const char *os_prefix, const char *inname, FILE * in, FILE * config,
1850                bool intact, int verbose);
1851 void flush_line_buf(int len);
1852 
1853 typedef struct in_block_s in_block_t;
1854 struct in_block_s {
1855     struct in_block_s *next;
1856     unsigned char data[ROMFS_BLOCKSIZE];
1857 };
1858 
1859 #define LINE_SIZE 1024
1860 
1861 /* Globals used for gs_init processing */
1862 char linebuf[LINE_SIZE * 2];		/* make it plenty long to avoid overflow */
1863 in_block_t *in_block_head = NULL;
1864 in_block_t *in_block_tail = NULL;
1865 unsigned char *curr_block_p, *curr_block_end;
1866 
1867 typedef struct {
1868     in_block_t *block;
1869     int pos;
1870     int eof;
1871 } in_block_file;
1872 
ib_getc(in_block_file * ibf)1873 static int ib_getc(in_block_file *ibf) {
1874     if ((ibf->block == in_block_tail) &&
1875         (ibf->pos == curr_block_p - in_block_tail->data)) {
1876         ibf->eof = 1;
1877         return -1;
1878     }
1879     if (ibf->pos == ROMFS_BLOCKSIZE) {
1880         ibf->block = ibf->block->next;
1881         ibf->pos = 0;
1882     }
1883     return ibf->block->data[ibf->pos++];
1884 }
1885 
ib_ungetc(int c,in_block_file * ibf)1886 static void ib_ungetc(int c, in_block_file *ibf)
1887 {
1888     ibf->pos--;
1889 }
1890 
ib_feof(in_block_file * ibf)1891 static int ib_feof(in_block_file *ibf)
1892 {
1893     return ibf->eof;
1894 }
1895 
1896 static int
process_initfile(char * initfile,char * gconfig_h,const char * os_prefix,const char * rom_prefix,int compression,int * inode_count,int * totlen,FILE * out,split_data * splits,int verbose)1897 process_initfile(char *initfile, char *gconfig_h, const char *os_prefix,
1898                  const char *rom_prefix, int compression, int *inode_count,
1899                  int *totlen, FILE *out, split_data *splits, int verbose)
1900 {
1901     int ret, block, blocks;
1902     romfs_inode *node = NULL;
1903     unsigned char *ubuf = NULL, *cbuf = NULL;
1904     char *prefixed_path = NULL, *rom_filename = NULL;
1905     unsigned long clen;
1906     FILE *in;
1907     FILE *config;
1908     in_block_t *in_block = NULL;
1909     int compaction = 1;
1910     int code = 0;
1911 
1912     ubuf = malloc(ROMFS_BLOCKSIZE);
1913     cbuf = malloc(ROMFS_CBUFSIZE);
1914     prefixed_path = malloc(1024);
1915     rom_filename = malloc(1024);
1916     if (ubuf == NULL || cbuf == NULL || prefixed_path == NULL ||
1917         rom_filename == NULL) {
1918         printf("malloc fail in process_initfile\n");
1919         /* should free whichever buffers got allocated, but don't bother */
1920         code = -1;
1921         goto done;
1922     }
1923 
1924     prefix_add(os_prefix, initfile, prefixed_path);
1925     prefix_add(rom_prefix, initfile, rom_filename);
1926 
1927     in = fopen(prefixed_path, "r");
1928     if (in == 0) {
1929         printf("cannot open initfile at: %s\n", prefixed_path);
1930         code = -1;
1931         goto done;
1932     }
1933     config = fopen(gconfig_h, "r");
1934     if (config == 0) {
1935         printf("Cannot open gconfig file %s\n", gconfig_h);
1936         fclose(in);
1937         code = -1;
1938         goto done;
1939     }
1940     memset(linebuf, 0, sizeof(linebuf));
1941     node = calloc(1, sizeof(romfs_inode));
1942     node->name = rom_filename;	/* without -P prefix, with -d rom_prefix */
1943 
1944     merge_to_ps(os_prefix, initfile, in, config, verbose);
1945 
1946     fclose(in);
1947     fclose(config);
1948 
1949     if (compaction)
1950     {
1951         in_block_t *comp_block_head;
1952         in_block_t *comp_block;
1953         pscompstate psc = {0};
1954         in_block_file ibf;
1955         int ulen;
1956 
1957         ibf.block = in_block_head;
1958         ibf.pos = 0;
1959         ibf.eof = 0;
1960 
1961         comp_block = malloc(sizeof(*comp_block));
1962         comp_block_head = comp_block;
1963         pscompact_start(&psc, (psc_getc*)&ib_getc, (psc_ungetc*)&ib_ungetc, (psc_feof*)&ib_feof, &ibf, 0, 0, 1);
1964         do {
1965             ulen = pscompact_getcompactedblock(&psc, comp_block->data, ROMFS_BLOCKSIZE);
1966             comp_block->next = NULL;
1967             if (ulen == ROMFS_BLOCKSIZE) {
1968                 comp_block->next = malloc(sizeof(*comp_block));
1969                 comp_block = comp_block->next;
1970             }
1971         } while (ulen == ROMFS_BLOCKSIZE);
1972         pscompact_end(&psc);
1973         while (in_block_head != NULL) {
1974             in_block = in_block_head->next;
1975             free(in_block_head);
1976             in_block_head = in_block;
1977         }
1978         in_block_head = comp_block_head;
1979         in_block_tail = comp_block;
1980         curr_block_p = in_block_tail->data + ulen;
1981     }
1982 
1983     node->length = 0;
1984     in_block = in_block_head;
1985     while (in_block != NULL) {
1986         node->length +=
1987             in_block != in_block_tail ? ROMFS_BLOCKSIZE : curr_block_p - in_block->data;
1988         in_block = in_block->next;
1989     }
1990     node->disc_length = node->length;
1991 
1992     blocks = (node->length+ROMFS_BLOCKSIZE-1) / ROMFS_BLOCKSIZE + 1;
1993     node->data_lengths = calloc(blocks, sizeof(*node->data_lengths));
1994     node->data = calloc(blocks, sizeof(*node->data));
1995     block = 0;
1996 
1997     in_block = in_block_head;
1998     while (in_block != NULL) {
1999         int block_len =
2000                 in_block != in_block_tail ? ROMFS_BLOCKSIZE : curr_block_p - in_block->data;
2001 
2002         clen = ROMFS_CBUFSIZE;
2003         if (compression) {
2004             /* compress data here */
2005             ret = compress(cbuf, &clen, in_block->data, block_len);
2006             if (ret != Z_OK) {
2007                 printf("error compressing data block!\n");
2008                 code = -1;
2009                 goto done;
2010             }
2011         } else {
2012             memcpy(cbuf, in_block->data, block_len);
2013             clen = block_len;
2014         }
2015         node->data_lengths[block] = clen;
2016         node->data[block] = malloc(clen);
2017         memcpy(node->data[block], cbuf, clen);
2018         block++;
2019         in_block = in_block->next;
2020     }
2021 
2022     /* write data for this file */
2023     inode_write(out, node, compression, *inode_count, totlen, splits, verbose);
2024     /* clean up */
2025     inode_clear(node);
2026     (*inode_count)++;
2027 done:
2028     free(node);
2029     free(cbuf);
2030     free(ubuf);
2031     free(prefixed_path);
2032     free(rom_filename);
2033     return code;
2034 }
2035 
2036 void
flush_line_buf(int len)2037 flush_line_buf(int len) {
2038     int remaining_len = len;
2039     int move_len;
2040     int line_offset = 0;
2041 
2042     if (len > LINE_SIZE) {
2043         printf("*** warning, flush_line called with len (%d) > LINE_SIZE (%d)\n",
2044                 len, LINE_SIZE);
2045         return;
2046     }
2047     /* check for empty list and allocate the first block if needed */
2048     if (in_block_tail == NULL) {
2049         in_block_head = in_block_tail = calloc(1, sizeof(in_block_t));
2050         in_block_tail->next = NULL;	/* calloc really already does this */
2051         curr_block_p = in_block_head->data;
2052         curr_block_end = curr_block_p + ROMFS_BLOCKSIZE;
2053     }
2054     /* move the data into the in_block buffer */
2055     do {
2056         move_len = min(remaining_len, curr_block_end - curr_block_p);
2057         memcpy(curr_block_p, linebuf + line_offset, move_len);
2058         curr_block_p += move_len;
2059         line_offset += move_len;
2060         if (curr_block_p == curr_block_end) {
2061             /* start a new data block appended to the list of blocks */
2062             in_block_tail->next =  calloc(1, sizeof(in_block_t));
2063             in_block_tail = in_block_tail->next;
2064             in_block_tail->next = NULL;	/* calloc really already does this */
2065             curr_block_p = in_block_tail->data;
2066             curr_block_end = curr_block_p + ROMFS_BLOCKSIZE;
2067         }
2068         remaining_len = max(0, remaining_len - move_len);
2069     } while (remaining_len > 0);
2070 
2071     /* clear the line (to allow 'strlen' to work if the data is not binary */
2072     memset(linebuf, 0, sizeof(linebuf));
2073 }
2074 
2075 /* Read a line from the input. */
2076 bool
rl(FILE * in,char * str,int len)2077 rl(FILE * in, char *str, int len)
2078 {
2079     /*
2080      * Unfortunately, we can't use fgets here, because the typical
2081      * implementation only recognizes the EOL convention of the current
2082      * platform.
2083      */
2084     int i = 0, c = getc(in);
2085 
2086     if (c < 0)
2087         return false;
2088     while (i < len - 1) {
2089         if (c < 0 || c == 10)		/* ^J, Unix EOL */
2090             break;
2091         if (c == 13) {		/* ^M, Mac EOL */
2092             c = getc(in);
2093             if (c != 10 && c >= 0)	/* ^M^J, PC EOL */
2094                 ungetc(c, in);
2095             break;
2096         }
2097         str[i++] = c;
2098         c = getc(in);
2099     }
2100     str[i] = 0;
2101     return true;
2102 }
2103 
2104 /* Write a string or a line on the output. */
2105 void
wsc(const byte * str,int len)2106 wsc(const byte *str, int len)
2107 {
2108     int n = 0;
2109     int i;
2110 
2111     if (len >= LINE_SIZE)
2112         exit(1);
2113 
2114     for (i = 0; i < len; ++i) {
2115         int c = str[i];
2116 
2117         sprintf(linebuf,
2118                 (c < 32 || c >= 127 ? "%d," :
2119                  c == '\'' || c == '\\' ? "'\\%c'," : "'%c',"),
2120                 c);
2121         flush_line_buf(strlen(linebuf));
2122         if (++n == 15) {
2123             linebuf[0] = '\n';
2124             flush_line_buf(1);
2125             n = 0;
2126         }
2127     }
2128     if (n != 0) {
2129         flush_line_buf(strlen(linebuf));
2130         linebuf[0] = '\n';
2131         flush_line_buf(1);
2132     }
2133 }
2134 void
ws(const byte * str,int len)2135 ws(const byte *str, int len)
2136 {
2137     if (len >= LINE_SIZE)
2138         exit(1);
2139 
2140     memcpy(linebuf, str, len);
2141     flush_line_buf(len);
2142 }
2143 
2144 void
wl(const char * str)2145 wl(const char *str)
2146 {
2147     ws((const byte *)str, strlen(str));
2148     ws((const byte *)"\n", 1);
2149 }
2150 
2151 /*
2152  * Strip whitespace and comments from a line of PostScript code as possible.
2153  * Return a pointer to any string that remains, or NULL if none.
2154  * Note that this may store into the string.
2155  */
2156 char *
doit(char * line,bool intact)2157 doit(char *line, bool intact)
2158 {
2159     char *str = line;
2160     char *from;
2161     char *to;
2162     int in_string = 0;
2163 
2164     if (intact)
2165         return str;
2166     while (*str == ' ' || *str == '\t')		/* strip leading whitespace */
2167         ++str;
2168     if (*str == 0)		/* all whitespace */
2169         return NULL;
2170     if (!strncmp(str, "%END", 4))	/* keep these for .skipeof */
2171         return str;
2172     if (str[0] == '%')    /* comment line */
2173         return NULL;
2174     /*
2175      * Copy the string over itself removing:
2176      *  - All comments not within string literals;
2177      *  - Whitespace adjacent to '[' ']' '{' '}';
2178      *  - Whitespace before '/' '(' '<';
2179      *  - Whitespace after ')' '>'.
2180      */
2181     for (to = from = str; (*to = *from) != 0; ++from, ++to) {
2182         switch (*from) {
2183             case '%':
2184                 if (!in_string)
2185                     break;
2186                 continue;
2187             case ' ':
2188             case '\t':
2189                 if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
2190                     --to;
2191                 continue;
2192             case '(':
2193             case '<':
2194             case '/':
2195             case '[':
2196             case ']':
2197             case '{':
2198             case '}':
2199                 if (to > str && !in_string && strchr(" \t", to[-1]))
2200                     *--to = *from;
2201                 if (*from == '(')
2202                     ++in_string;
2203                 continue;
2204             case ')':
2205                 --in_string;
2206                 continue;
2207             case '\\':
2208                 if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
2209                     *++to = *++from;
2210                 continue;
2211             default:
2212                 continue;
2213         }
2214         break;
2215     }
2216     /* Strip trailing whitespace. */
2217     while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
2218         --to;
2219     *to = 0;
2220     return str;
2221 }
2222 
2223 /* Copy a hex string to the output as a binary token. */
2224 void
hex_string_to_binary(FILE * in)2225 hex_string_to_binary(FILE *in)
2226 {
2227 #define MAX_STR 0xffff	/* longest possible PostScript string token */
2228     byte *strbuf = (byte *)malloc(MAX_STR);
2229     byte *q = strbuf;
2230     int c;
2231     bool which = false;
2232     int len;
2233     byte prefix[3];
2234 
2235     if (strbuf == 0) {
2236         printf("Unable to allocate string token buffer.\n");
2237         exit(1);
2238     }
2239     while ((c = getc(in)) >= 0) {
2240         if (isxdigit(c)) {
2241             c -= (isdigit(c) ? '0' : islower(c) ? 'a' : 'A');
2242             if ((which = !which)) {
2243                 if (q == strbuf + MAX_STR) {
2244                     printf("Can't handle string token > %d bytes.\n",
2245                             MAX_STR);
2246                     exit(1);
2247                 }
2248                 *q++ = c << 4;
2249             } else
2250                 q[-1] += c;
2251         } else if (isspace(c))
2252             continue;
2253         else if (c == '>')
2254             break;
2255         else
2256             printf("Unknown character in ASCIIHex string: %c\n", c);
2257     }
2258     len = q - strbuf;
2259     if (len <= 255) {
2260         prefix[0] = 142;
2261         prefix[1] = (byte)len;
2262         ws(prefix, 2);
2263     } else {
2264         prefix[0] = 143;
2265         prefix[1] = (byte)(len >> 8);
2266         prefix[2] = (byte)len;
2267         ws(prefix, 3);
2268     }
2269     ws(strbuf, len);
2270     free((char *)strbuf);
2271 #undef MAX_STR
2272 }
2273 
2274 /* Merge a file from input to output. */
2275 void
flush_buf(char * buf)2276 flush_buf(char *buf)
2277 {
2278     if (buf[0] != 0) {
2279         wl(buf);
2280         buf[0] = 0;
2281     }
2282 }
2283 
2284 FILE *
prefix_open(const char * os_prefix,const char * filename,int verbose)2285 prefix_open(const char *os_prefix, const char *filename, int verbose)
2286 {
2287     char *prefixed_path;
2288     FILE *filep;
2289 
2290     prefixed_path = malloc(1024);
2291     if (prefixed_path == NULL) {
2292         printf("malloc problem in prefix_open\n");
2293         return NULL;
2294     }
2295     prefix_add(os_prefix, filename, prefixed_path);
2296     if (verbose) {
2297         printf("including: '%s'\n", prefixed_path);
2298     }
2299     filep = fopen(prefixed_path, "rb");
2300     free(prefixed_path);
2301     return filep;
2302 }
2303 
2304 void
mergefile(const char * os_prefix,const char * inname,FILE * in,FILE * config,bool intact,int verbose)2305 mergefile(const char *os_prefix, const char *inname, FILE * in, FILE * config,
2306           bool intact, int verbose)
2307 {
2308     char line[LINE_SIZE + 1];
2309     char buf[LINE_SIZE + 1];
2310     char *str;
2311     int level = 1;
2312     bool first = true;
2313 
2314     buf[0] = 0;
2315     while (rl(in, line, LINE_SIZE)) {
2316         char psname[LINE_SIZE + 1];
2317         int nlines;
2318 
2319         if (!strncmp(line, "%% Replace", 10) &&
2320             sscanf(line + 11, "%d %s", &nlines, psname) == 2
2321             ) {
2322             bool do_intact = (line[10] == '%');
2323 
2324             flush_buf(buf);
2325             while (nlines-- > 0)
2326                 rl(in, line, LINE_SIZE);
2327             if (psname[0] == '(') {
2328                 FILE *ps;
2329 
2330                 psname[strlen(psname) - 1] = 0;
2331                 ps = prefix_open(os_prefix, psname + 1, verbose);
2332                 if (ps == 0) {
2333                     fprintf(stderr, "Failed to open '%s' - aborting\n", psname+1);
2334                     exit(1);
2335                 }
2336                 mergefile(os_prefix, psname + 1, ps, config, intact || do_intact, verbose);
2337             } else if (!strcmp(psname, "INITFILES")) {
2338                 /*
2339                  * We don't want to bind config.h into geninit, so
2340                  * we parse it ourselves at execution time instead.
2341                  */
2342                 rewind(config);
2343                 while (rl(config, psname, LINE_SIZE))
2344                     if (!strncmp(psname, "psfile_(\"", 9)) {
2345                         FILE *ps;
2346                         char *quote = strchr(psname + 9, '"');
2347                         if (quote == NULL)
2348                             exit(1);
2349 
2350                         *quote = 0;
2351                         ps = prefix_open(os_prefix, psname + 9, verbose);
2352                         if (ps == 0)
2353                             exit(1);
2354                         mergefile(os_prefix, psname + 9, ps, config, false, verbose);
2355                     }
2356             } else {
2357                 printf("Unknown %%%% Replace %d %s\n",
2358                         nlines, psname);
2359                 exit(1);
2360             }
2361         } else if (!strcmp(line, "currentfile closefile")) {
2362             /* The rest of the file is debugging code, stop here. */
2363             break;
2364         } else {
2365             int len;
2366 
2367             str = doit(line, intact);
2368             if (str == 0)
2369                 continue;
2370             len = strlen(str);
2371             if (first && len >= 2 && str[len - 2] == '<' &&
2372                 (str[len - 1] == '<' || str[len - 1] == '~')
2373                 ) {
2374                 wl("currentobjectformat 1 setobjectformat\n");
2375                 level = 2;
2376             }
2377             if (level > 1 && len > 0 && str[len - 1] == '<' &&
2378                 (len < 2 || str[len - 2] != '<')
2379                 ) {
2380                 /* Convert a hex string to a binary token. */
2381                 flush_buf(buf);
2382                 str[len - 1] = 0;
2383                 wl(str);
2384                 hex_string_to_binary(in);
2385                 continue;
2386             }
2387             if (buf[0] != '%' &&	/* i.e. not special retained comment */
2388                 strlen(buf) + len < LINE_SIZE &&
2389                 (strchr("(/[]{}", str[0]) ||
2390                  (buf[0] != 0 && strchr(")[]{}", buf[strlen(buf) - 1])))
2391                 )
2392                 strcat(buf, str);
2393             else {
2394                 flush_buf(buf);
2395                 strcpy(buf, str);
2396             }
2397             first = false;
2398         }
2399     }
2400     flush_buf(buf);
2401     if (level > 1)
2402         wl("setobjectformat");
2403 }
2404 
2405 /* Merge and produce a PostScript file. */
2406 void
merge_to_ps(const char * os_prefix,const char * inname,FILE * in,FILE * config,int verbose)2407 merge_to_ps(const char *os_prefix, const char *inname, FILE * in, FILE * config, int verbose)
2408 {
2409     char line[LINE_SIZE + 1];
2410 
2411     while ((rl(in, line, LINE_SIZE), line[0])) {
2412         sprintf(linebuf, "%s", line );
2413         wl(linebuf);
2414     }
2415     mergefile(os_prefix, inname, in, config, false, verbose);
2416 }
2417 
2418 static void
make_split_name(split_data * splits,const char * filename)2419 make_split_name(split_data *splits, const char *filename)
2420 {
2421   const char *s = filename;
2422   const char *t = NULL;
2423   char *u;
2424 
2425   while (*s) {
2426     if (*s == '.')
2427       t = s;
2428     s++;
2429   }
2430   if (t == NULL)
2431     t = s;
2432 
2433   free(splits->outname);
2434   splits->outname = u = malloc(s-filename+4);
2435   if (u == NULL) {
2436     fprintf(stderr, "malloc failure while constructing split filename\n");
2437     exit(1);
2438   }
2439   memcpy(u, filename, t-filename);
2440   u[t-filename] = 'c';
2441   u[t-filename+1] = '%';
2442   u[t-filename+2] = 'd';
2443   if (s-t)
2444      memcpy(u+(t-filename)+3, t, s-t);
2445   u[s-filename+3] = 0;
2446 
2447   free(splits->outname_formatted);
2448   splits->outname_formatted = malloc(s-filename+4+32);
2449   if (splits->outname_formatted == NULL) {
2450     fprintf(stderr, "malloc failure while constructing split filename\n");
2451     exit(1);
2452   }
2453 }
2454 
2455 int
main(int argc,char * argv[])2456 main(int argc, char *argv[])
2457 {
2458     int i;
2459     int inode_count = 0, totlen = 0;
2460     FILE *out;
2461     const char *outfilename = "obj/gsromfs.c";
2462     const char *os_prefix = "";
2463     const char *rom_prefix = "";
2464     int atarg = 1;
2465     int compression = 1;			/* default to doing compression */
2466     int compaction = 0;
2467     int verbose = 1;
2468     Xlist_element *Xlist_scan = NULL, *Xlist_head = NULL;
2469     char pa[PATH_STR_LEN];
2470     time_t buildtime = 0;
2471     char* env_source_date_epoch;
2472     split_data splits = { 0 } ;
2473 
2474     memset(pa, 0x00, PATH_STR_LEN);
2475 
2476     if (argc < 2) {
2477         printf("\n"
2478                 "       Usage: mkromfs [-o outputfile] [options ...] paths\n"
2479                 "           options and paths can be interspersed and are processed in order\n"
2480                 "           options:\n"
2481                 "               -o outputfile   default: obj/gsromfs.c if this option present, must be first.\n"
2482                 "               -P prefix       use prefix to find path. prefix not included in %%rom%%\n"
2483                 "               -q              reduce diagnostics\n"
2484                 "               -X path         exclude the path from further processing.\n"
2485                 "                         Note: The tail of any path encountered will be tested so .svn on the -X\n"
2486                 "                               list will exclude that path in all subsequent paths enumerated.\n"
2487                 "\n"
2488                 "               -d romprefix    directory in %%rom file system (just a prefix string on filename)\n"
2489                 "               -c              compression on\n"
2490                 "               -b              compression off (binary).\n"
2491                 "               -C              postscript 'compaction' on\n"
2492                 "               -B              postscript 'compaction' off\n"
2493                 "               -g initfile gconfig_h \n"
2494                 "                       special handling to read the 'gs_init.ps' file (from\n"
2495                 "                       the current -P prefix path), and read the gconfig.h for\n"
2496                 "                       psfile_ entries and combines them into a whitespace\n"
2497                 "                       optimized and no comments form and writes this 'gs_init.ps'\n"
2498                 "                       to the current -d destination path. This is a space and\n"
2499                 "                       startup performance improvement, so also this should be\n"
2500                 "                       BEFORE other stuff in the %%rom%% list of files (currently\n"
2501                 "                       we do a sequential search in the %%rom%% directory).\n"
2502                 "\n"
2503                 "                       For performance reasons, it is best to turn off compression\n"
2504                 "                       for the init file. Less frequently accessed files, if they\n"
2505                 "                       are large should still be compressed.\n"
2506                 "\n"
2507             );
2508         exit(1);
2509     }
2510 
2511     printf("compression will use %d byte blocksize (zlib output buffer %d bytes)\n",
2512         ROMFS_BLOCKSIZE, ROMFS_CBUFSIZE);
2513 
2514     if (argc > 3 && argv[1][0] == '-' && argv[1][1] == 'o') {
2515         /* process -o option for outputfile */
2516         outfilename = argv[2];
2517         atarg += 2;
2518     }
2519     printf("   writing romfs data to '%s'\n", outfilename);
2520     out = fopen(outfilename, "w");
2521 
2522     start_file(out);
2523 
2524     if ((env_source_date_epoch = getenv("SOURCE_DATE_EPOCH"))) {
2525         buildtime = strtoul(env_source_date_epoch, NULL, 10);
2526     }
2527     if (!buildtime)
2528         buildtime = time(NULL);
2529     fprintf(out,"    time_t gs_romfs_buildtime = %ld;\n\n", (long)buildtime);
2530 
2531     /* process the remaining arguments (options interspersed with paths) */
2532     for (; atarg < argc; atarg++) {
2533         if (argv[atarg][0] == '-') {
2534             /* process an option */
2535             switch (argv[atarg][1]) {
2536               case 'b':
2537                 compression = 0;
2538                 break;
2539               case 'c':
2540                 compression = 1;
2541                 break;
2542               case 'B':
2543                 compaction = 0;
2544                 break;
2545               case 'C':
2546                 compaction = 1;
2547                 break;
2548               case 'q':
2549                 verbose = 0;
2550                 break;
2551               case 'd':
2552                 if (++atarg == argc) {
2553                     printf("   option %s missing required argument\n", argv[atarg-1]);
2554                     exit(1);
2555                 }
2556                 rom_prefix = argv[atarg];
2557                 break;
2558               case 's':
2559                 if (++atarg == argc) {
2560                     printf("   option %s missing required argument\n", argv[atarg-1]);
2561                     exit(1);
2562                 }
2563                 splits.num_splits = atoi(argv[atarg]);
2564                 if (splits.num_splits <= 0) {
2565                     printf("   Invalid number of files to split to: %s\n", argv[atarg]);
2566                     exit(1);
2567                 }
2568                 make_split_name(&splits, outfilename);
2569                 break;
2570               case 'g':
2571                 {
2572                     char initfile[PATH_STR_LEN] = {0};
2573                     char gconfig_h[PATH_STR_LEN] = {0};
2574                     if ((++atarg) + 1 == argc) {
2575                         printf("   option %s missing required arguments\n", argv[atarg-1]);
2576                         exit(1);
2577                     }
2578                     strncpy(initfile, argv[atarg], PATH_STR_LEN - 1);
2579                     atarg++;
2580                     strncpy(gconfig_h, argv[atarg], PATH_STR_LEN - 1);
2581                     process_initfile(initfile, gconfig_h, os_prefix, rom_prefix, compression,
2582                                     &inode_count, &totlen, out, &splits, verbose);
2583                 }
2584                 break;
2585               case 'P':
2586                 if (++atarg == argc) {
2587                     printf("   option %s missing required argument\n", argv[atarg-1]);
2588                     exit(1);
2589                 }
2590                 os_prefix = argv[atarg];
2591                 break;
2592               case 'X':
2593                 if (++atarg == argc) {
2594                     printf("   option %s missing required argument\n", argv[atarg-1]);
2595                     exit(1);
2596                 }
2597                 Xlist_scan = malloc(sizeof(Xlist_element));
2598                 if (Xlist_scan == NULL) {
2599                     exit(1);
2600                 }
2601                 Xlist_scan->next = Xlist_head;
2602                 Xlist_head = Xlist_scan;
2603                 Xlist_head->path = argv[atarg];
2604                 break;
2605               default:
2606                 printf("  unknown option: %s \n", argv[atarg]);
2607             }
2608             continue;
2609         }
2610         /* process a path or file */
2611         strncpy(pa, argv[atarg], PATH_STR_LEN - (strlen(os_prefix) < strlen(rom_prefix) ? strlen(rom_prefix) : strlen(os_prefix)));
2612         process_path(pa, os_prefix, rom_prefix, Xlist_head,
2613                      compression, compaction, &inode_count, &totlen, out, &splits, verbose);
2614     }
2615 
2616     /* Now allow for the (probably never happening) case where we are splitting, but haven't written anything to one of the files */
2617     prepare_splits(&splits);
2618     for (i = 0; i < splits.max_splits; i++) {
2619         if (splits.sizes[i] == 0) {
2620             FILE *out2;
2621             sprintf(splits.outname_formatted, splits.outname, i);
2622             out2 = fopen(splits.outname_formatted, "w");
2623             fprintf(out2, "const int mkromfs_dummy_chunk%d;\n", i);
2624             fclose(out2);
2625         }
2626     }
2627 
2628     if (splits.max_splits) {
2629         for (i=0; i<inode_count; i++)
2630             fprintf(out, "\t extern const uint32_t mkromfs_node_%d[];\n", i);
2631     }
2632 
2633     /* now write out the array of nodes */
2634     fprintf(out, "    const uint32_t *gs_romfs[] = {\n");
2635     for (i=0; i<inode_count; i++)
2636         fprintf(out, "\t%snode_%d,\n", splits.max_splits ? "mkromfs_" : "", i);
2637     fprintf(out, "\t0 };\n");
2638     fclose(out);
2639     while (Xlist_head) {
2640         Xlist_scan = Xlist_head->next;
2641         free(Xlist_head);
2642         Xlist_head = Xlist_scan;
2643     }
2644     printf("Total %%rom%% structure size is %d bytes.\n", totlen);
2645 
2646     return 0;
2647 }
2648