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