xref: /plan9/sys/src/cmd/gs/src/gendev.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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