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