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