1 /* Copyright (C) 1998 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: gendev.c,v 1.7 2004/12/08 21:35:13 stefan Exp $ */
18 /* Generate .dev 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 generates .dev configuration files.
28 *
29 * Usage:
30 * gendev [-Z] [-n [<name_prefix> | -]] [-C [<dir> | -]]
31 * (-d <devfile> | -m <modfile> | -a <modfile>)
32 * (-<category> | <item>)*
33 *
34 * The name_prefix is for device[2], font, init, and iodev resources.
35 * ****** DOESN'T HANDLE -replace YET ******
36 * ****** DOESN'T MERGE device AND device2 ******
37 */
38
39 #define DEFAULT_NAME_PREFIX "gs_"
40 #define INITIAL_CATEGORY "obj"
41
42 typedef struct config_s {
43 /* Set once */
44 FILE *out;
45 bool debug;
46 const char *name_prefix;
47 const char *file_prefix;
48 ulong file_id; /* for uniq_last detection */
49 /* Updated dynamically */
50 #define ITEM_ID_BITS 5
51 uint item_id;
52 bool any_scan_items;
53 bool in_res_scan;
54 bool in_category; /* in_category implies in_res_scan */
55 const char *current_category;
56 } config;
57 static const config init_config =
58 {
59 0, /* out */
60 0, /* debug */
61 DEFAULT_NAME_PREFIX, /* name_prefix */
62 "", /* file_prefix */
63 0, /* file_id */
64 0, /* item_id */
65 0, /* any_scan_items */
66 0, /* in_res_scan */
67 0, /* in_category */
68 "" /* current_category */
69 };
70
71 static const char *indent_INCLUDED = "\t\t\t\t";
72 static const char *indent_RES_SCAN = " ";
73 static const char *indent_category = "\t";
74 static const char *indent_SEEN = "\t ";
75 static const char *indent_include = " ";
76 static const char *indent_scan_item = "\t";
77 static const char *indent_item = "";
78
79 /* Forward definitions */
80 void add_entry(config *, const char *, const char *, bool);
81
main(int argc,char * argv[])82 main(int argc, char *argv[])
83 {
84 config conf;
85 int i, j;
86 bool dev, append;
87 const char *category = INITIAL_CATEGORY;
88 const char *fnarg;
89 FILE *out;
90 long pos;
91
92 conf = init_config;
93 /* Process command line arguments. */
94 for (i = 1; i < argc; i++) {
95 const char *arg = argv[i];
96
97 if (*arg != '-') {
98 fprintf(stderr, "-d|m|a must precede non-switches.\n", arg);
99 exit(1);
100 }
101 switch (arg[1]) {
102 case 'C': /* change directory, by analogy with make */
103 conf.file_prefix =
104 (argv[i + 1][0] == '-' ? "" : argv[i + 1]);
105 ++i;
106 continue;
107 case 'n':
108 conf.name_prefix =
109 (argv[i + 1][0] == '-' ? "" : argv[i + 1]);
110 ++i;
111 continue;
112 case 'a':
113 dev = false, append = true;
114 break;
115 case 'd':
116 dev = true, append = false;
117 break;
118 case 'm':
119 dev = false, append = false;
120 break;
121 case 'Z':
122 conf.debug = true;
123 continue;
124 default:
125 fprintf(stderr, "Unknown switch %s.\n", argv[i]);
126 exit(1);
127 }
128 break;
129 }
130 if (i == argc - 1) {
131 fprintf(stderr, "No output file name given, last argument is %s.\n",
132 argv[i]);
133 exit(1);
134 }
135 /* Must be the output file. */
136 fnarg = argv[++i];
137 {
138 char fname[100];
139
140 strcpy(fname, fnarg);
141 strcat(fname, ".dev");
142 out = fopen(fname, (append ? "a" : "w"));
143 if (out == 0) {
144 fprintf(stderr, "Can't open %s for output.\n", fname);
145 exit(1);
146 }
147 if (!append)
148 fprintf(out,
149 "/*\n * File %s created automatically by gendev.\n */\n",
150 fname);
151 }
152 conf.out = out;
153 pos = ftell(out);
154 /* We need a separate _INCLUDED flag for each batch of definitions. */
155 fprintf(out, "\n#%sifndef %s_%ld_INCLUDED\n",
156 indent_INCLUDED, fnarg, pos);
157 fprintf(out, "#%s define %s_%ld_INCLUDED\n",
158 indent_INCLUDED, fnarg, pos);
159 /* Create a "unique" hash for the output file. */
160 for (j = 0; fnarg[j] != 0; ++j)
161 conf.file_id = conf.file_id * 41 + fnarg[j];
162 conf.item_id <<= ITEM_ID_BITS;
163 /* Add the real entries. */
164 if (dev)
165 add_entry(&conf, "dev", fnarg, false);
166 for (j = i + 1; j < argc; ++j) {
167 const char *arg = argv[j];
168
169 if (arg[0] == '-')
170 category = arg + 1;
171 else
172 add_entry(&conf, category, arg, false);
173 }
174 if (conf.in_category)
175 fprintf(out, "#%sendif /* -%s */\n",
176 indent_category, conf.current_category);
177 /* Add the scanning entries, if any. */
178 if (conf.any_scan_items) {
179 if (conf.in_res_scan)
180 fprintf(out, "#%selse /* RES_SCAN */\n", indent_RES_SCAN);
181 else
182 fprintf(out, "#%sifdef RES_SCAN\n", indent_RES_SCAN);
183 conf.in_res_scan = true;
184 category = INITIAL_CATEGORY;
185 conf.item_id = 0;
186 for (j = i + 1; j < argc; ++j) {
187 const char *arg = argv[j];
188
189 if (arg[0] == '-')
190 category = arg + 1;
191 else
192 add_entry(&conf, category, arg, true);
193 }
194 }
195 if (conf.in_res_scan)
196 fprintf(out, "#%sendif /* RES_SCAN */\n", indent_RES_SCAN);
197 fprintf(out, "#%sendif /* !%s_%ld_INCLUDED */\n",
198 indent_INCLUDED, fnarg, pos);
199 fclose(out);
200 exit(0);
201 }
202
203 /* Add an entry to the output. */
204 typedef enum {
205 uniq_none = 0,
206 uniq_first,
207 uniq_last
208 } uniq_mode;
209 void
write_item(config * pconf,const char * str,const char * category,const char * item,uniq_mode mode)210 write_item(config * pconf, const char *str, const char *category,
211 const char *item, uniq_mode mode)
212 {
213 FILE *out = pconf->out;
214 char cati[80];
215
216 if (!pconf->in_res_scan) {
217 fprintf(out, "#%sifndef RES_SCAN\n", indent_RES_SCAN);
218 pconf->in_res_scan = true;
219 }
220 if (strcmp(pconf->current_category, category)) {
221 const char *paren = strchr(str, '(');
222
223 if (pconf->in_category)
224 fprintf(out, "#%sendif /* -%s */\n",
225 indent_category, pconf->current_category);
226 fprintf(out, "#%sifdef ", indent_category);
227 fwrite(str, sizeof(char), paren - str, out);
228
229 fprintf(out, "\n");
230 pconf->current_category = category;
231 pconf->in_category = true;
232 }
233 sprintf(cati, "%s_%s_", category, item);
234 switch (mode) {
235 case uniq_none:
236 fprintf(out, "%s%s\n", indent_item, str);
237 break;
238 case uniq_first:
239 fprintf(out, "#%sifndef %sSEEN\n", indent_SEEN, cati);
240 fprintf(out, "#%s define %sSEEN\n", indent_SEEN, cati);
241 write_item(pconf, str, category, item, uniq_none);
242 fprintf(out, "#%sendif\n", indent_SEEN, cati);
243 break;
244 case uniq_last:
245 fprintf(out, "#%sif %sSEEN == %lu\n", indent_SEEN, cati,
246 pconf->file_id + pconf->item_id++);
247 write_item(pconf, str, category, item, uniq_none);
248 fprintf(out, "#%sendif\n", indent_SEEN, cati);
249 pconf->any_scan_items = true;
250 break;
251 }
252 }
253 void
write_scan_item(config * pconf,const char * str,const char * category,const char * item,uniq_mode mode)254 write_scan_item(config * pconf, const char *str, const char *category,
255 const char *item, uniq_mode mode)
256 {
257 FILE *out = pconf->out;
258 char cati[80];
259
260 sprintf(cati, "%s_%s_", category, item);
261 switch (mode) {
262 case uniq_none:
263 break;
264 case uniq_first:
265 break;
266 case uniq_last:
267 fprintf(out, "#%sundef %sSEEN\n", indent_scan_item, cati);
268 fprintf(out, "#%s define %sSEEN %lu\n", indent_scan_item, cati,
269 pconf->file_id + pconf->item_id++);
270 }
271 }
272 void
add_entry(config * pconf,const char * category,const char * item,bool scan)273 add_entry(config * pconf, const char *category, const char *item, bool scan)
274 {
275 char str[80];
276 uniq_mode mode = uniq_first;
277
278 if (pconf->debug && !scan)
279 printf("Adding %s %s;\n", category, item);
280 str[0] = 0;
281 switch (category[0]) { /* just an accelerator */
282 #define is_cat(str) !strcmp(category, str)
283 case 'd':
284 if (is_cat("dev"))
285 sprintf(str, "device_(%s%s_device)\n",
286 pconf->name_prefix, item);
287 else if (is_cat("dev2"))
288 sprintf(str, "device2_(%s%s_device)\n",
289 pconf->name_prefix, item);
290 break;
291 case 'e':
292 if (is_cat("emulator"))
293 sprintf(str, "emulator_(\"%s\",%d)",
294 item, strlen(item));
295 break;
296 case 'f':
297 if (is_cat("font"))
298 sprintf(str, "font_(\"0.font_%s\",%sf_%s,zf_%s)",
299 item, pconf->name_prefix, item, item);
300 break;
301 case 'i':
302 if (is_cat("include")) {
303 int len = strlen(item);
304
305 if (scan)
306 return;
307 if (strcmp(pconf->current_category, category)) {
308 if (pconf->in_category) {
309 fprintf(pconf->out, "#%sendif /* -%s */\n",
310 indent_category, pconf->current_category);
311 pconf->in_category = false;
312 }
313 pconf->current_category = category;
314 }
315 if (pconf->in_res_scan) {
316 fprintf(pconf->out, "#%sendif /* RES_SCAN */\n",
317 indent_RES_SCAN);
318 pconf->in_res_scan = false;
319 }
320 if (len < 5 || strcmp(item + len - 4, ".dev"))
321 fprintf(pconf->out, "#%sinclude \"%s.dev\"\n",
322 indent_include, item);
323 else
324 fprintf(pconf->out, "#%sinclude \"%s\"\n",
325 indent_include, item);
326 return;
327 } else if (is_cat("init"))
328 sprintf(str, "init_(%s%s_init)", pconf->name_prefix, item);
329 else if (is_cat("iodev"))
330 sprintf(str, "io_device_(%siodev_%s)", pconf->name_prefix, item);
331 break;
332 case 'l':
333 if (is_cat("lib")) {
334 sprintf(str, "lib_(%s)", item);
335 mode = uniq_last;
336 }
337 break;
338 case 'o':
339 if (is_cat("obj"))
340 sprintf(str, "obj_(%s%s)", pconf->file_prefix, item);
341 else if (is_cat("oper"))
342 sprintf(str, "oper_(%s_op_defs)", item);
343 break;
344 case 'p':
345 if (is_cat("ps"))
346 sprintf(str, "psfile_(\"%s.ps\",%d)",
347 item, strlen(item) + 3);
348 break;
349 #undef is_cat
350 default:
351 ;
352 }
353 if (str[0] == 0) {
354 fprintf(stderr, "Unknown category %s.\n", category);
355 exit(1);
356 }
357 if (scan)
358 write_scan_item(pconf, str, category, item, mode);
359 else
360 write_item(pconf, str, category, item, mode);
361 }
362