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