1/*
2 * Copyright (c) 2003 Matthijs Hollemans
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23
24#include <stdarg.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include "rdef.h"
30
31
32extern const char *__progname;
33
34static const char *kTitle = "Haiku Resource Compiler 1.1";
35static const char *kProgramName = __progname;
36
37
38static bool sQuiet = false;
39static bool sDecompile = false;
40static uint32 sFlags = 0;
41
42static char sOutputFile[B_PATH_NAME_LENGTH] = { 0 };
43static char *sFirstInputFile = NULL;
44
45
46void
47warn(const char *format, ...)
48{
49	va_list ap;
50
51	if (!sQuiet) {
52		fprintf(stderr, "%s: Warning! ", kProgramName);
53		va_start(ap, format);
54		vfprintf(stderr, format, ap);
55		va_end(ap);
56		fprintf(stderr, "\n");
57	}
58}
59
60
61void
62error(const char *format, ...)
63{
64	va_list ap;
65
66	if (!sQuiet) {
67		fprintf(stderr, "%s: Error! ", kProgramName);
68		va_start(ap, format);
69		vfprintf(stderr, format, ap);
70		va_end(ap);
71		fprintf(stderr, "\n");
72	}
73
74	exit(EXIT_FAILURE);
75}
76
77
78static void
79usage()
80{
81	printf("%s\n\n"
82		"To compile an rdef script into a resource file:\n"
83		"    %s [options] [-o <file>] <file>...\n\n"
84		"To convert a resource file back into an rdef script:\n"
85		"    %s [options] [-o <file>] -d <file>...\n\n"
86		"Options:\n"
87		"    -d --decompile       create an rdef script from a resource file\n"
88		"       --auto-names      construct resource names from ID symbols\n"
89		"    -h --help            show this message\n"
90		"    -I --include <dir>   add <dir> to the list of include paths\n"
91		"    -m --merge           do not erase existing contents of output file\n"
92		"    -o --output          specify output file name, default is out.xxx\n"
93		"    -q --quiet           do not display any error messages\n"
94		"    -V --version         show software version and license\n",
95		kTitle, kProgramName, kProgramName);
96
97	exit(EXIT_SUCCESS);
98}
99
100
101static void
102version()
103{
104	printf("%s\n\n"
105		"Copyright (c) 2003 Matthijs Hollemans\n\n"
106
107		"Permission is hereby granted, free of charge, to any person obtaining a\n"
108		"copy of this software and associated documentation files (the \"Software\"),\n"
109		"to deal in the Software without restriction, including without limitation\n"
110		"the rights to use, copy, modify, merge, publish, distribute, sublicense,\n"
111		"and/or sell copies of the Software, and to permit persons to whom the\n"
112		"Software is furnished to do so, subject to the following conditions:\n\n"
113
114		"The above copyright notice and this permission notice shall be included in\n"
115		"all copies or substantial portions of the Software.\n\n"
116
117		"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
118		"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
119		"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
120		"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
121		"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n"
122		"FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n"
123		"DEALINGS IN THE SOFTWARE.\n",
124		kTitle);
125
126	exit(EXIT_SUCCESS);
127}
128
129
130static bool
131has_extension(char *name, const char *ext)
132{
133	size_t nameLength = strlen(name);
134	size_t extLength = strlen(ext);
135
136	if (nameLength > extLength)
137		return strcmp(name + nameLength - extLength, ext) == 0;
138
139	return false;
140}
141
142
143static void
144cut_extension(char *name, const char *ext)
145{
146	if (!has_extension(name, ext))
147		return;
148
149	name[strlen(name) - strlen(ext)] = '\0';
150}
151
152
153static void
154add_extension(char *name, const char *ext)
155{
156	strlcat(name, ext, B_PATH_NAME_LENGTH);
157}
158
159
160static void
161parse_options(int argc, char *argv[])
162{
163	int32 args_left = argc - 1;
164
165	// ToDo: use getopt_long()
166
167	for (int32 i = 1; i < argc; ++i) {
168		if (strcmp(argv[i], "-o") == 0
169				|| strcmp(argv[i], "--output") == 0) {
170			if (i + 1 >= argc)
171				error("%s should be followed by a file name", argv[i]);
172
173			strcpy(sOutputFile, argv[i + 1]);
174
175			argv[i] = NULL;
176			argv[i + 1] = NULL;
177			++i; args_left -= 2;
178		} else if (strcmp(argv[i], "-I") == 0
179				|| strcmp(argv[i], "--include") == 0) {
180			if (i + 1 >= argc)
181				error("%s should be followed by a directory name", argv[i]);
182
183			rdef_add_include_dir(argv[i + 1], true);
184
185			argv[i] = NULL;
186			argv[i + 1] = NULL;
187			++i; args_left -= 2;
188		} else if (strcmp(argv[i], "-d") == 0
189				|| strcmp(argv[i], "--decompile") == 0) {
190			sDecompile = true;
191			argv[i] = NULL;
192			--args_left;
193		} else if (strcmp(argv[i], "-m") == 0
194				|| strcmp(argv[i], "--merge") == 0) {
195			sFlags |= RDEF_MERGE_RESOURCES;
196			argv[i] = NULL;
197			--args_left;
198		} else if (strcmp(argv[i], "--auto-names") == 0) {
199			sFlags |= RDEF_AUTO_NAMES;
200			argv[i] = NULL;
201			--args_left;
202		} else if (strcmp(argv[i], "-q") == 0
203				|| strcmp(argv[i], "--quiet") == 0) {
204			sQuiet = true;
205			argv[i] = NULL;
206			--args_left;
207		} else if (strcmp(argv[i], "-h") == 0
208				|| strcmp(argv[i], "--help") == 0) {
209			usage();
210		} else if (strcmp(argv[i], "-V") == 0
211				|| strcmp(argv[i], "--version") == 0) {
212			version();
213		} else if (!strcmp(argv[i], "-")) {
214			// stdin input file
215			break;
216		} else if (argv[i][0] == '-') {
217			error("unknown option %s", argv[i]);
218			argv[i] = NULL;
219			--args_left;
220		}
221	}
222
223	if (args_left < 1) {
224		error("no input files");
225		usage();
226	}
227
228	for (int i = 1; i < argc; ++i) {
229		if (argv[i] == NULL)
230			continue;
231
232		if (sFirstInputFile == NULL)
233			sFirstInputFile = argv[i];
234
235		rdef_add_input_file(argv[i]);
236	}
237
238	if (sOutputFile[0] == '\0') {
239		// no output file name was given, use the name of the
240		// first source file as base
241		strlcpy(sOutputFile, sFirstInputFile, sizeof(sOutputFile));
242
243		cut_extension(sOutputFile, sDecompile ? ".rsrc" : ".rdef");
244		add_extension(sOutputFile, sDecompile ? ".rdef" : ".rsrc");
245	}
246}
247
248
249static void
250compile()
251{
252	if (!has_extension(sOutputFile, ".rsrc"))
253		add_extension(sOutputFile, ".rsrc");
254
255	rdef_compile(sOutputFile);
256}
257
258
259static void
260decompile()
261{
262	if (!has_extension(sOutputFile, ".rdef"))
263		add_extension(sOutputFile, ".rdef");
264
265	rdef_decompile(sOutputFile);
266}
267
268
269static void
270report_error()
271{
272	switch (rdef_err) {
273		case RDEF_COMPILE_ERR:
274			error("%s:%" B_PRId32 " %s", rdef_err_file, rdef_err_line, rdef_err_msg);
275			break;
276
277		case RDEF_FILE_NOT_FOUND:
278			error("%s not found", rdef_err_file);
279			break;
280
281		case RDEF_NO_RESOURCES:
282			error("%s is not a resource file", rdef_err_file);
283			break;
284
285		case RDEF_WRITE_ERR:
286			error("error writing to %s (%s)", rdef_err_file, rdef_err_msg);
287			break;
288
289		case B_NO_MEMORY:
290			error("out of memory");
291			break;
292
293		case B_ERROR:
294		default:
295			error("unknown error: %" B_PRIx32 " (%s)", rdef_err, strerror(rdef_err));
296			break;
297	}
298}
299
300
301int
302main(int argc, char *argv[])
303{
304	parse_options(argc, argv);
305
306	rdef_set_flags(sFlags);
307
308	if (sDecompile)
309		decompile();
310	else
311		compile();
312
313	rdef_free_input_files();
314	rdef_free_include_dirs();
315
316	if (rdef_err != B_OK)
317		report_error();
318
319	return EXIT_SUCCESS;
320}
321
322