1 /* Copyright (C) 2001-2006 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, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: genconf.c 8250 2007-09-25 13:31:24Z giles $ */
15 /* Generate configuration files */
16 #include "stdpre.h"
17 #include <assert.h>
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdlib.h>		/* for calloc */
21 #include <string.h>
22 
23 /*
24  * This program reads .dev files, which contain definitions of modules,
25  * and generates merged configuration files.
26  *
27  * A .dev file specifies a list of "resources" of various kinds (.obj/.o
28  * files, other .dev files to be merged in, PostScript files, driver names,
29  * compiled fonts, etc.)  that make up the module.  (This is similar in
30  * spirit to the Macintosh resource fork, and to PostScript resources, but
31  * has nothing to do directly with the latter.)
32  *
33  * A .dev file consists of a series of switches and resource names.  Most
34  * switches specifies the type of the following resources, until the next
35  * type switch; there are also a few other switches.
36  *
37  * genconf recognizes the following resource type switches in .dev files.
38  * See the next section on command line switches for the meaning of
39  * <name_prefix>.
40  *
41  *    -comp <name>
42  *
43  *	Adds compositor_(<name_prefix>_composite_<name>_type) to <gconfig.h>.
44  *	Used for gs_composite_type_t structures, whose identity may need to
45  *	passed through the command list.
46  *
47  *    -dev <device>
48  *
49  *	Adds device_(<name_prefix><device>_device) to <gconfig.h>.
50  *	Used for ordinary devices.
51  *
52  *    -dev2 <device>
53  *
54  *	Adds device2_(<name_prefix><device>_device) to <gconfig.h>.
55  *	Used for some newer devices with potentially different structures
56  *	or conventions.
57  *
58  *    -emulator <emulator>
59  *
60  *	Adds emulator_("<emulator>",<len>), where len is the number of
61  *	characters in <emulator>, to <gconfig.h>.
62  *	Used for names that should appear as instances of the PostScript
63  *	Emulator resource category.
64  *
65  *    -font <font>
66  *
67  *	Adds an entry to <gconfigf.h>, as described below.
68  *
69  *    -functiontype <fntype>
70  *
71  *	Adds function_type_(<fntype>,<name_prefix>build_function_<fntype>)
72  *	to <gconfig.h>.
73  *	Used for instances of the PostScript FunctionType resource category,
74  *	and also to generate a table of procedures for processing
75  *	Function dictionaries.
76  *
77  *    -halftone <htname>
78  *
79  *	Adds halftone_(<name_prefix>dht_<htname>) to <gconfig.h>.
80  *	Used for halftones that will be compiled into the executable.
81  *
82  *    -imageclass <iclass>
83  *
84  *	Adds image_class_(<name_prefix>image_class_<iclass>) to <gconfig.h>.
85  *	Used internally for the various cases of rendering images.
86  *
87  *    -imagetype <itype>
88  *
89  *	Adds image_type_(<itype>,<name_prefix>image_type_<itype>) to
90  *	<gconfig.h>.
91  *	Used for instances of the PostScript ImageType resource category,
92  *	and also to generate a table of procedures for handling the various
93  *	types of images.
94  *
95  *    -include <module>
96  *
97  *	Reads the file named <module> (or <module>.dev if <module> doesn't
98  *	end with ".dev") as though it were part of the current .dev file.
99  *	Used when one module requires the presence of another.
100  *
101  *    -init <initname>
102  *
103  *	Adds init_(<name_prefix><initname>_init) to <gconfig.h>.
104  *	Used for initialization procedures to be called when Ghostscript
105  *	is started.  By convention, <initname> is (almost always) the name
106  *	of the source file in which the procedure is defined.
107  *
108  *    -iodev <iodev>
109  *
110  *	Adds io_device_(<name_prefix>iodev_<iodev>) to <gconfig.h>.
111  *	Used for instances of the PostScript IODevice resource category.
112  *
113  *    -lib <lib>
114  *
115  *	Adds <lib> to the list of libraries to be written by -l.
116  *
117  *    -libpath <libpath>
118  *
119  *	Adds <libpath> to the list of library paths to be written by -l.
120  *
121  *    -link <link>
122  *
123  *	Adds <link> to the list of switches to be passed to the linker,
124  *	to be written by -l.
125  *
126  *    -obj <obj>
127  *
128  *	Adds <obj> to the list of files to be linked, to be written by -o.
129  *
130  *    -oper <opername>
131  *
132  *	Adds oper_(<opername>_op_defs) to <gconfig.h>.
133  *	Used for tables of PostScript operators.  By convention,
134  *	<opername> is (usually) the name of the source file in which the
135  *	table appears.
136  *
137  *    -plugin <plugin_name>
138  *
139  *	Adds plugin_(<name_prefix><plugin_name>_instantiate) to <gconfig.h>.
140  *	Used for plugins to be instantiated when Ghostscript
141  *	is started.  By convention, <plugin_name> is (almost always) the name
142  *	of the source file in which the plugin is defined.
143  *
144  *    -ps <psname>
145  *
146  *	Adds psfile_("<psname>.ps",<len+3>), where <len> is the number of
147  *	character in <psname>, to <gconfig.h>.
148  *	Used for PostScript files that should be read at initialization.
149  *
150  * genconf recognizes the following other switches in .dev files:
151  *
152  *    -replace <module>
153  *
154  *	This switch declares that <module> has been replaced by another
155  *	module (presumably the one defined by the .dev file in which the
156  *	switch appears), and should be removed from consideration.
157  *	Modules that can be -replaced should not -include other modules.
158  *
159  * genconf writes the following files if the corresponding switch is used:
160  *
161  *    -h <gconfig.h>
162  *
163  *	Writes a list of all the resources as described above.  Each
164  *	sublist is surrounded by an #ifdef/#endif in case the
165  *	referenced macro (e.g., device_, oper_) is undefined.
166  *	Duplicates are eliminated.
167  *
168  *    -f <gconfigf.h>
169  *
170  *	Writes a list of all the -font resources, in the form
171  *		font_("0.font_<name>",<name_prefix>f_<name>,zf_<name)
172  *
173  * Other switches specify various options and parameters:
174  *
175  *    -Z
176  *
177  *	Turns on debugging output.
178  *
179  *    -C [<file_prefix>]
180  *
181  *	Prefixes <file_prefix> to the names of all .dev files read in,
182  *	and to the names of all .obj/.o files written in <gconfig.h>.
183  *	The default file_prefix is the empty string.
184  *	This switch should probably be removed, since nothing in the
185  *	current Ghostscript makefiles uses it.
186  *
187  *    -e <escapechar>
188  *
189  *	Sets the escape character for patterns.  See below.
190  *
191  *    -n [<name_prefix> | -]
192  *
193  *	Prefixes <name_prefix>, or the empty string, to certain items in
194  *	the output, as indicated above.
195  *	The default name_prefix is "gs_".
196  *
197  *    -p[l|L][u][e] <pattern>
198  *
199  *	Sets the pattern (format string) used for writing certain items in
200  *	the output, as indicated above.  '%' in the pattern indicates
201  *	substitution of the variable data, as for printf except that the
202  *	'%' is not followed by a format character -- the data are always
203  *	inserted literally.  See below for more information about patterns.
204  *
205  *    -l[o] <lib.tr>
206  *
207  *	Writes the list of library paths, links, and library file names
208  *	on <lib.tr>.  -lo also writes the list of object file names,
209  *	as for -o. This feature is obsolete and is not longer in use.
210  *      Use -ol instead.
211  *
212  *    -o[l] <obj.tr>
213  *
214  *	Writes the list object file names on <obj.tr>.  -ol also writes
215  *	the list of library paths, links, and library file names as for -l.
216  *
217  * Usage summary:
218  *
219  *      genconf [-Z] [-C prefix] [-e escapechar] [-n [name_prefix | -]]
220  *	  [@]xxx.dev* [-f gconfigf.h] [-h gconfig.h]
221  *        [-p[l|L][u][e] pattern] [-l|o|lo|ol lib.tr]
222  *
223  * Patterns:
224  *
225  *   The default escape character is &.  When this character appears in a
226  *   pattern, it acts as follows:
227  *	&p produces a %;
228  *	&s produces a space;
229  *	&& (i.e., the escape character twice) produces a \;
230  *	&- produces a -;
231  *	&x, for any other character x, is an error.
232  */
233 
234 #define DEFAULT_FILE_PREFIX ""
235 #define DEFAULT_NAME_PREFIX "gs_"
236 
237 #define MAX_STR 120
238 
239 /* Structures for accumulating information. */
240 typedef struct string_item_s {
241     const char *str;
242     int file_index;		/* index of file containing this item */
243     int index;
244 } string_item_t;
245 
246 /* The values of uniq_mode are bit masks. */
247 typedef enum {
248     uniq_all = 1,		/* keep all occurrences (default) */
249     uniq_first = 2,		/* keep only first occurrence */
250     uniq_last = 4		/* keep only last occurrence */
251 } uniq_mode_t;
252 typedef struct string_list_s {
253     /* The following are set at creation time. */
254     const char *list_name;	/* only for debugging */
255     int max_count;
256     uniq_mode_t mode;
257     /* The following are updated dynamically. */
258     int count;
259     string_item_t *items;
260 } string_list_t;
261 
262 #define MAX_PATTERN 60
263 typedef struct string_pattern_s {
264     bool upper_case;
265     bool drop_extn;
266     char pattern[MAX_PATTERN + 1];
267 } string_pattern_t;
268 typedef struct config_s {
269     int debug;
270     const char *name_prefix;
271     const char *file_prefix;
272     /* Special "resources" */
273     string_list_t file_names;
274     string_list_t file_contents;
275     string_list_t replaces;
276     /* Real resources */
277     union ru_ {
278 	struct nu_ {
279 	    /* These names must parallel config_lists below. */
280 	    string_list_t sorted_resources;
281 	    string_list_t resources;
282 	    string_list_t devs;	/* also includes devs2 */
283 	    string_list_t compositors;
284 	    string_list_t fonts;
285 	    string_list_t libs;
286 	    string_list_t libpaths;
287 	    string_list_t links;
288 	    string_list_t objs;
289 	} named;
290 #define NUM_RESOURCE_LISTS 9
291 	string_list_t indexed[NUM_RESOURCE_LISTS];
292     } lists;
293     string_pattern_t lib_p;
294     string_pattern_t libpath_p;
295     string_pattern_t obj_p;
296 } config_t;
297 
298 /*
299  * These lists grow automatically if needed, so we could start out with
300  * small allocations.
301  */
302 static const config_t init_config = {
303     0,				/* debug */
304     DEFAULT_NAME_PREFIX,	/* name_prefix */
305     DEFAULT_FILE_PREFIX,	/* file_prefix */
306     {"file name", 200},		/* file_names */
307     {"file contents", 200},	/* file_contents */
308     {"-replace", 50}
309 };
310 static const string_list_t init_config_lists[] = {
311     {"resource", 100, uniq_first},
312     {"sorted_resource", 20, uniq_first},
313     {"-comp", 10, uniq_first},
314     {"-dev", 100, uniq_first},
315     {"-font", 50, uniq_first},
316     {"-lib", 20, uniq_last},
317     {"-libpath", 10, uniq_first},
318     {"-link", 10, uniq_first},
319     {"-obj", 500, uniq_first}
320 };
321 
322 /* Forward definitions */
323 static void *mrealloc(void *, size_t, size_t);
324 int alloc_list(string_list_t *);
325 void dev_file_name(char *);
326 int process_replaces(config_t *);
327 int read_dev(config_t *, const char *);
328 int read_token(char *, int, const char **);
329 int add_entry(config_t *, char *, const char *, int);
330 string_item_t *add_item(string_list_t *, const char *, int);
331 void sort_uniq(string_list_t *, bool);
332 void write_list(FILE *, const string_list_t *, const char *);
333 void write_list_pattern(FILE *, const string_list_t *, const string_pattern_t *);
334 bool var_expand(char *, char [MAX_STR], const config_t *);
335 void add_definition(const char *, const char *, string_list_t *, bool);
336 string_item_t *lookup(const char *, const string_list_t *);
337 
338 int
main(int argc,char * argv[])339 main(int argc, char *argv[])
340 {
341     config_t conf;
342     char escape = '&';
343     int i;
344 
345     /* Allocate string lists. */
346     conf = init_config;
347     memcpy(conf.lists.indexed, init_config_lists,
348 	   sizeof(conf.lists.indexed));
349     alloc_list(&conf.file_names);
350     alloc_list(&conf.file_contents);
351     alloc_list(&conf.replaces);
352     for (i = 0; i < NUM_RESOURCE_LISTS; ++i)
353 	alloc_list(&conf.lists.indexed[i]);
354 
355     /* Initialize patterns. */
356     conf.lib_p.upper_case = false;
357     conf.lib_p.drop_extn = false;
358     strcpy(conf.lib_p.pattern, "%s\n");
359     conf.libpath_p = conf.lib_p;
360     conf.obj_p = conf.lib_p;
361 
362     /* Process command line arguments. */
363     for (i = 1; i < argc; i++) {
364 	const char *arg = argv[i];
365 	FILE *out;
366 	int lib = 0, obj = 0;
367 
368 	if (*arg != '-') {
369 	    read_dev(&conf, arg);
370 	    continue;
371 	}
372 	if (i == argc - 1) {
373 	    fprintf(stderr, "Missing argument after %s.\n",
374 		    arg);
375 	    exit(1);
376 	}
377 	switch (arg[1]) {
378 	    case 'C':		/* change directory, by analogy with make */
379 		conf.file_prefix =
380 		    (argv[i + 1][0] == '-' ? "" : argv[i + 1]);
381 		++i;
382 		continue;
383 	    case 'e':
384 		escape = argv[i + 1][0];
385 		++i;
386 		continue;
387 	    case 'n':
388 		conf.name_prefix =
389 		    (argv[i + 1][0] == '-' ? "" : argv[i + 1]);
390 		++i;
391 		continue;
392 	    case 'p':
393 		{
394 		    string_pattern_t *pat;
395 
396 		    switch (*(arg += 2)) {
397 			case 'l':
398 			    pat = &conf.lib_p;
399 			    break;
400 			case 'L':
401 			    pat = &conf.libpath_p;
402 			    break;
403 			default:
404 			    pat = &conf.obj_p;
405 			    arg--;
406 		    }
407 		    pat->upper_case = false;
408 		    pat->drop_extn = false;
409 		    if (argv[i + 1][0] == '-')
410 			strcpy(pat->pattern, "%s\n");
411 		    else {
412 			char *p, *q;
413 
414 			for (p = pat->pattern, q = argv[++i];
415 			     (*p++ = *q++) != 0;
416 			    )
417 			    if (p[-1] == escape)
418 				switch (*q) {
419 				    case 'p':
420 					p[-1] = '%'; q++; break;
421 				    case 's':
422 					p[-1] = ' '; q++; break;
423 				    case '-':
424 					p[-1] = '-'; q++; break;
425 				    default:
426 					if (*q == escape) {
427 					    p[-1] = '\\'; q++; break;
428 					}
429 					fprintf(stderr,
430 					  "%c not followed by p|s|%c|-: &%c\n",
431 						escape, escape, *q);
432 					exit(1);
433 				}
434 			p[-1] = '\n';
435 			*p = 0;
436 		    }
437 		    for (;;) {
438 			switch (*++arg) {
439 			    case 'u':
440 				pat->upper_case = true;
441 				break;
442 			    case 'e':
443 				pat->drop_extn = true;
444 				break;
445 			    case 0:
446 				goto pbreak;
447 			    default:
448 				fprintf(stderr, "Unknown switch %s.\n", arg);
449 				exit(1);
450 			}
451 		    }
452 		  pbreak:if (pat == &conf.obj_p) {
453 			conf.lib_p = *pat;
454 			conf.libpath_p = *pat;
455 		    }
456 		    continue;
457 		}
458 	    case 'Z':
459 		conf.debug = 1;
460 		continue;
461 	}
462 	/* Must be an output file. */
463 	out = fopen(argv[++i], "w");
464 	if (out == 0) {
465 	    fprintf(stderr, "Can't open %s for output.\n",
466 		    argv[i]);
467 	    exit(1);
468 	}
469 	switch (arg[1]) {
470 	    case 'f':
471 		process_replaces(&conf);
472 		fputs("/* This file was generated automatically by genconf.c. */\n", out);
473 		fputs("/* For documentation, see gsconfig.c. */\n", out);
474 		{
475 		    char template[80];
476 
477 		    sprintf(template,
478 			    "font_(\"0.font_%%s\",%sf_%%s,zf_%%s)\n",
479 			    conf.name_prefix);
480 		    write_list(out, &conf.lists.named.fonts, template);
481 		}
482 		break;
483 	    case 'h':
484 		process_replaces(&conf);
485 		fputs("/* This file was generated automatically by genconf.c. */\n", out);
486 		write_list(out, &conf.lists.named.compositors, "%s\n");
487 		write_list(out, &conf.lists.named.devs, "%s\n");
488 		sort_uniq(&conf.lists.named.resources, true);
489 		write_list(out, &conf.lists.named.resources, "%s\n");
490 		sort_uniq(&conf.lists.named.sorted_resources, false);
491 		write_list(out, &conf.lists.named.sorted_resources, "%s\n");
492 		break;
493 	    case 'l':
494 		lib = 1;
495 		obj = arg[2] == 'o';
496 		goto lo;
497 	    case 'o':
498 		obj = 1;
499 		lib = arg[2] == 'l';
500 	      lo:process_replaces(&conf);
501 		if (obj) {
502 		    sort_uniq(&conf.lists.named.objs, true);
503 		    write_list_pattern(out, &conf.lists.named.objs, &conf.obj_p);
504 		}
505 		if (lib) {
506 		    sort_uniq(&conf.lists.named.libs, true);
507 		    sort_uniq(&conf.lists.named.links, true);
508 		    write_list_pattern(out, &conf.lists.named.libpaths, &conf.libpath_p);
509 		    write_list_pattern(out, &conf.lists.named.links, &conf.obj_p);
510 		    write_list_pattern(out, &conf.lists.named.libs, &conf.lib_p);
511 		}
512 		break;
513 	    default:
514 		fclose(out);
515 		fprintf(stderr, "Unknown switch %s.\n", argv[i]);
516 		exit(1);
517 	}
518 	fclose(out);
519     }
520 
521     return 0;
522 }
523 
524 /*
525  * We would like to use the real realloc, but it doesn't work on all systems
526  * (e.g., some Linux versions).  Also, this procedure does the right thing
527  * if old_ptr = NULL.
528  */
529 static void *
mrealloc(void * old_ptr,size_t old_size,size_t new_size)530 mrealloc(void *old_ptr, size_t old_size, size_t new_size)
531 {
532     void *new_ptr = malloc(new_size);
533 
534     if (new_ptr == NULL)
535 	return NULL;
536     /* We have to pass in the old size, since we have no way to */
537     /* determine it otherwise. */
538     if (old_ptr)
539 	memcpy(new_ptr, old_ptr, min(old_size, new_size));
540     return new_ptr;
541 }
542 
543 /* Allocate and initialize a string list. */
544 int
alloc_list(string_list_t * list)545 alloc_list(string_list_t * list)
546 {
547     list->count = 0;
548     list->items =
549 	(string_item_t *) calloc(list->max_count, sizeof(string_item_t));
550     assert(list->items != NULL);
551     return 0;
552 }
553 
554 /* If necessary, convert a .dev name to its file name. */
555 void
dev_file_name(char * str)556 dev_file_name(char *str)
557 {
558     int len = strlen(str);
559 
560     if (len <= 4 || strcmp(".dev", str + len - 4))
561 	strcat(str, ".dev");
562 }
563 
564 /* Delete any files that are named as -replace "resources". */
565 int
process_replaces(config_t * pconf)566 process_replaces(config_t * pconf)
567 {
568     char bufname[MAX_STR];
569     int i;
570 
571     for (i = 0; i < pconf->replaces.count; ++i) {
572 	int j;
573 
574 	strcpy(bufname, pconf->replaces.items[i].str);
575 	/* See if the file being replaced was included. */
576 	dev_file_name(bufname);
577 	for (j = 0; j < pconf->file_names.count; ++j) {
578 	    const char *fname = pconf->file_names.items[j].str;
579 
580 	    if (!strcmp(fname, bufname)) {
581 		if (pconf->debug)
582 		    printf("Replacing file %s.\n", fname);
583 		/* Delete all resources associated with this file. */
584 		{
585 		    int rn;
586 
587 		    for (rn = 0; rn < NUM_RESOURCE_LISTS; ++rn) {
588 			string_item_t *items = pconf->lists.indexed[rn].items;
589 			int count = pconf->lists.indexed[rn].count;
590 			int tn;
591 
592 			for (tn = 0; tn < count; ++tn) {
593 			    if (items[tn].file_index == j) {
594 				/*
595 				 * Delete the item.  Since we haven't sorted
596 				 * the items yet, just replace this item
597 				 * with the last one, but make sure we don't
598 				 * miss scanning it.
599 				 */
600 				if (pconf->debug)
601 				    printf("Replacing %s %s.\n",
602 					 pconf->lists.indexed[rn].list_name,
603 					   items[tn].str);
604 				items[tn--] = items[--count];
605 			    }
606 			}
607 			pconf->lists.indexed[rn].count = count;
608 		    }
609 		}
610 		pconf->file_names.items[j].str = "";
611 		break;
612 	    }
613 	}
614     }
615     /* Don't process the replaces again. */
616     pconf->replaces.count = 0;
617     return 0;
618 }
619 
620 /*
621  * Read an entire file into memory.
622  * We use the 'index' of the file_contents string_item_t to record the union
623  * of the uniq_mode_ts of all (direct and indirect) items in the file.
624  * Return the file_contents item for the file.
625  */
626 static string_item_t *
read_file(config_t * pconf,const char * fname)627 read_file(config_t * pconf, const char *fname)
628 {
629     char *cname = malloc(strlen(fname) + strlen(pconf->file_prefix) + 1);
630     int i;
631     FILE *in;
632     int end, nread;
633     char *cont;
634     string_item_t *item;
635 
636     if (cname == 0) {
637 	fprintf(stderr, "Can't allocate space for file name %s%s.\n",
638 		pconf->file_prefix, fname);
639 	exit(1);
640     }
641     strcpy(cname, pconf->file_prefix);
642     strcat(cname, fname);
643     for (i = 0; i < pconf->file_names.count; ++i)
644 	if (!strcmp(pconf->file_names.items[i].str, cname)) {
645 	    free(cname);
646 	    return &pconf->file_contents.items[i];
647 	}
648     /* Try to open the file in binary mode, to avoid the overhead */
649     /* of unnecessary EOL conversion in the C library. */
650     in = fopen(cname, "rb");
651     if (in == 0) {
652 	in = fopen(cname, "r");
653 	if (in == 0) {
654 	    fprintf(stderr, "Can't read %s.\n", cname);
655 	    exit(1);
656 	}
657     }
658     fseek(in, 0L, 2 /*SEEK_END */ );
659     end = ftell(in);
660     cont = malloc(end + 1);
661     if (cont == 0) {
662 	fprintf(stderr, "Can't allocate %d bytes to read %s.\n",
663 		end + 1, cname);
664 	exit(1);
665     }
666     rewind(in);
667     nread = fread(cont, 1, end, in);
668     fclose(in);
669     cont[nread] = 0;
670     if (pconf->debug)
671 	printf("File %s = %d bytes.\n", cname, nread);
672     add_item(&pconf->file_names, cname, -1);
673     item = add_item(&pconf->file_contents, cont, -1);
674     item->index = 0;		/* union of uniq_mode_ts */
675     return item;
676 }
677 
678 /* Read and parse a .dev file.  Return the union of all its uniq_mode_ts. */
679 int
read_dev(config_t * pconf,const char * arg)680 read_dev(config_t * pconf, const char *arg)
681 {
682     string_item_t *item;
683     const char *in;
684 
685 #define MAX_TOKEN 256
686     char *token = malloc(MAX_TOKEN + 1);
687     char *category = malloc(MAX_TOKEN + 1);
688     int file_index;
689     int len;
690 
691     if (pconf->debug)
692 	printf("Reading %s;\n", arg);
693     item = read_file(pconf, arg);
694     if (item->index == uniq_first) {	/* Don't need to read the file again. */
695 	if (pconf->debug)
696 	    printf("Skipping duplicate file.\n");
697 	return uniq_first;
698     }
699     in = item->str;
700     file_index = item - pconf->file_contents.items;
701     strcpy(category, "obj");
702     while ((len = read_token(token, MAX_TOKEN, &in)) > 0)
703 	item->index |= add_entry(pconf, category, token, file_index);
704     free(category);
705 #undef MAX_TOKEN
706     if (len < 0) {
707 	fprintf(stderr, "Token too long: %s.\n", token);
708 	exit(1);
709     }
710     if (pconf->debug)
711 	printf("Finished %s.\n", arg);
712     free(token);
713     return item->index;
714 }
715 
716 /* Read a token from a string that contains the contents of a file. */
717 int
read_token(char * token,int max_len,const char ** pin)718 read_token(char *token, int max_len, const char **pin)
719 {
720     const char *in = *pin;
721     int len = 0;
722 
723     while (len < max_len) {
724 	char ch = *in;
725 
726 	if (ch == 0)
727 	    break;
728 	++in;
729 	if (isspace(ch)) {
730 	    if (len > 0)
731 		break;
732 	    continue;
733 	}
734 	token[len++] = ch;
735     }
736     token[len] = 0;
737     *pin = in;
738     return (len >= max_len ? -1 /* token too long */ : len);
739 }
740 
741 /* Add an entry to a configuration.  Return its uniq_mode_t. */
742 int
add_entry(config_t * pconf,char * category,const char * item,int file_index)743 add_entry(config_t * pconf, char *category, const char *item, int file_index)
744 {
745     if (item[0] == '-' && islower(item[1])) {	/* set category */
746 	strcpy(category, item + 1);
747 	return 0;
748     } else {			/* add to current category */
749 	char str[MAX_STR];
750 	char template[80];
751 	const char *pat = 0;
752 	string_list_t *list = &pconf->lists.named.resources;
753 
754 	if (pconf->debug)
755 	    printf("Adding %s %s;\n", category, item);
756 	/* Handle a few resources specially; just queue the rest. */
757 	switch (category[0]) {
758 #define IS_CAT(str) !strcmp(category, str)
759 	    case 'c':
760 		if (!IS_CAT("comp"))
761 		    goto err;
762 		pat = "compositor_(%scomposite_%%s_type)";
763 		list = &pconf->lists.named.compositors;
764 		goto pre;
765 	    case 'd':
766 		if (IS_CAT("dev"))
767 		    pat = "device_(%s%%s_device)";
768 		else if (IS_CAT("dev2"))
769 		    pat = "device2_(%s%%s_device)";
770 		else
771 		    goto err;
772 		list = &pconf->lists.named.devs;
773 pre:		sprintf(template, pat, pconf->name_prefix);
774 		pat = template;
775 		break;
776 	    case 'e':
777 		if (IS_CAT("emulator")) {
778 		    sprintf(str, "emulator_(\"%s\",%u)",
779 			    item, (uint)strlen(item));
780 		    item = str;
781 		    break;
782 		}
783 		goto err;
784 	    case 'f':
785 		if (IS_CAT("font")) {
786 		    list = &pconf->lists.named.fonts;
787 		    break;
788 		} else if (IS_CAT("functiontype")) {
789 		    pat = "function_type_(%%s,%sbuild_function_%%s)";
790 		} else
791 		    goto err;
792 		goto pre;
793 	    case 'h':
794 		if (IS_CAT("halftone")) {
795 		    pat = "halftone_(%sdht_%%s)";
796 		} else
797 		    goto err;
798 		goto pre;
799 	    case 'i':
800 		if (IS_CAT("imageclass")) {
801 		    list = &pconf->lists.named.sorted_resources;
802 		    pat = "image_class_(%simage_class_%%s)";
803 		} else if (IS_CAT("imagetype")) {
804 		    pat = "image_type_(%%s,%simage_type_%%s)";
805 		} else if (IS_CAT("include")) {
806 		    strcpy(str, item);
807 		    dev_file_name(str);
808 		    return read_dev(pconf, str);
809 		} else if (IS_CAT("init")) {
810 		    pat = "init_(%s%%s_init)";
811 		} else if (IS_CAT("iodev")) {
812 		    pat = "io_device_(%siodev_%%s)";
813 		} else
814 		    goto err;
815 		goto pre;
816 	    case 'l':
817 		if (IS_CAT("lib")) {
818 		    list = &pconf->lists.named.libs;
819 		    break;
820 		} else if (IS_CAT("libpath")) {
821 		    list = &pconf->lists.named.libpaths;
822 		    break;
823 		} else if (IS_CAT("link")) {
824 		    list = &pconf->lists.named.links;
825 		    break;
826 		}
827 		goto err;
828 	    case 'o':
829 		if (IS_CAT("obj")) {
830 		    list = &pconf->lists.named.objs;
831 		    strcpy(template, pconf->file_prefix);
832 		    strcat(template, "%s");
833 		    pat = template;
834 		    break;
835 		}
836 		if (IS_CAT("oper")) {
837 		    pat = "oper_(%s_op_defs)";
838 		    break;
839 		}
840 		goto err;
841 	    case 'p':
842 		if (IS_CAT("ps")) {
843 		    sprintf(str, "psfile_(\"%s.ps\",%u)",
844 			    item, (uint)(strlen(item) + 3));
845 		    item = str;
846 		    break;
847 		} else if (IS_CAT("plugin")) {
848 		    pat = "plugin_(%s%%s_instantiate)";
849                     goto pre;
850                 }
851 		goto err;
852 	    case 'r':
853 		if (IS_CAT("replace")) {
854 		    list = &pconf->replaces;
855 		    break;
856 		}
857 		goto err;
858 #undef IS_CAT
859 	    default:
860 err:		fprintf(stderr, "Definition not recognized: %s %s.\n",
861 			category, item);
862 		exit(1);
863 	}
864 	if (pat) {
865 	    sprintf(str, pat, item, item);
866 	    assert(strlen(str) < MAX_STR);
867 	    add_item(list, str, file_index);
868 	} else
869 	    add_item(list, item, file_index);
870 	return list->mode;
871     }
872 }
873 
874 /* Add an item to a list. */
875 string_item_t *
add_item(string_list_t * list,const char * str,int file_index)876 add_item(string_list_t * list, const char *str, int file_index)
877 {
878     char *rstr = malloc(strlen(str) + 1);
879     int count = list->count;
880     string_item_t *item;
881 
882     if (count >= list->max_count) {
883 	list->max_count <<= 1;
884 	if (list->max_count < 20)
885 	    list->max_count = 20;
886 	list->items =
887 	    (string_item_t *) mrealloc(list->items,
888 				     (list->max_count >> 1) *
889 				     sizeof(string_item_t),
890 				     list->max_count *
891 				     sizeof(string_item_t));
892 	assert(list->items != NULL);
893     }
894     item = &list->items[count];
895     item->str = rstr;
896     item->index = count;
897     item->file_index = file_index;
898     strcpy(rstr, str);
899     list->count++;
900     return item;
901 }
902 
903 /*
904  * Remove duplicates from a list of string_item_ts.
905  * In case of duplicates, remove all but the earliest (if last = false)
906  * or the latest (if last = true).
907  */
908 static int
cmp_index(const void * p1,const void * p2)909 cmp_index(const void *p1, const void *p2)
910 {
911     const string_item_t *const psi1 = (const string_item_t *)p1;
912     const string_item_t *const psi2 = (const string_item_t *)p2;
913     int cmp = psi1->index - psi2->index;
914 
915     return (cmp < 0 ? -1 : cmp > 0 ? 1 : 0);
916 }
917 static int
cmp_str(const void * p1,const void * p2)918 cmp_str(const void *p1, const void *p2)
919 {
920     const string_item_t *const psi1 = (const string_item_t *)p1;
921     const string_item_t *const psi2 = (const string_item_t *)p2;
922 
923     return strcmp(psi1->str, psi2->str);
924 }
925 void
sort_uniq(string_list_t * list,bool by_index)926 sort_uniq(string_list_t * list, bool by_index)
927 {
928     string_item_t *strlist = list->items;
929     int count = list->count;
930     const string_item_t *from;
931     string_item_t *to;
932     int i;
933     bool last = list->mode == uniq_last;
934 
935     if (count == 0)
936 	return;
937     qsort((char *)strlist, count, sizeof(string_item_t), cmp_str);
938     for (from = to = strlist + 1, i = 1; i < count; from++, i++)
939 	if (strcmp(from->str, to[-1].str))
940 	    *to++ = *from;
941 	else if ((last ? from->index > to[-1].index :
942 		  from->index < to[-1].index)
943 	    )
944 	    to[-1] = *from;
945     count = to - strlist;
946     list->count = count;
947     if (by_index)
948 	qsort((char *)strlist, count, sizeof(string_item_t), cmp_index);
949 }
950 
951 /* Write a list of strings using a template. */
952 void
write_list(FILE * out,const string_list_t * list,const char * pstr)953 write_list(FILE * out, const string_list_t * list, const char *pstr)
954 {
955     string_pattern_t pat;
956 
957     pat.upper_case = false;
958     pat.drop_extn = false;
959     strcpy(pat.pattern, pstr);
960     write_list_pattern(out, list, &pat);
961 }
962 void
write_list_pattern(FILE * out,const string_list_t * list,const string_pattern_t * pat)963 write_list_pattern(FILE * out, const string_list_t * list,
964 		   const string_pattern_t * pat)
965 {
966     int i;
967     char macname[40];
968     int plen = strlen(pat->pattern);
969 
970     *macname = 0;
971     for (i = 0; i < list->count; i++) {
972 	const char *lstr = list->items[i].str;
973 	int len = strlen(lstr);
974 	char *str = malloc(len + 1);
975 	int xlen = plen + len * 3;
976 	char *xstr = malloc(xlen + 1);
977 	char *alist;
978 
979 	strcpy(str, lstr);
980 	if (pat->drop_extn) {
981 	    char *dot = str + len;
982 
983 	    while (dot > str && *dot != '.')
984 		dot--;
985 	    if (dot > str)
986 		*dot = 0, len = dot - str;
987 	}
988 	if (pat->upper_case) {
989 	    char *ptr = str;
990 
991 	    for (; *ptr; ptr++)
992 		if (islower(*ptr))
993 		    *ptr = toupper(*ptr);
994 	}
995 	/* We repeat str for the benefit of patterns that */
996 	/* need the argument substituted in more than one place. */
997 	sprintf(xstr, pat->pattern, str, str, str);
998 	/* Check to make sure the item is within the scope of */
999 	/* an appropriate #ifdef, if necessary. */
1000 	alist = strchr(xstr, '(');
1001 	if (alist != 0 && alist != xstr && alist[-1] == '_') {
1002 	    *alist = 0;
1003 	    if (strcmp(xstr, macname)) {
1004 		if (*macname)
1005 		    fputs("#endif\n", out);
1006 		fprintf(out, "#ifdef %s\n", xstr);
1007 		strcpy(macname, xstr);
1008 	    }
1009 	    *alist = '(';
1010 	} else {
1011 	    if (*macname) {
1012 		fputs("#endif\n", out);
1013 		*macname = 0;
1014 	    }
1015 	}
1016 	fputs(xstr, out);
1017 	free(xstr);
1018 	free(str);
1019     }
1020     if (*macname)
1021 	fputs("#endif\n", out);
1022 }
1023