xref: /haiku/src/bin/rc/rc.cpp (revision 268f99dd7dc4bd7474a8bd2742d3f1ec1de6752a)
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 
32 extern const char *__progname;
33 
34 static const char *kTitle = "Haiku Resource Compiler 1.1";
35 static const char *kProgramName = __progname;
36 
37 
38 static bool sQuiet = false;
39 static bool sDecompile = false;
40 static uint32 sFlags = 0;
41 
42 static char sOutputFile[B_PATH_NAME_LENGTH] = { 0 };
43 static char *sFirstInputFile = NULL;
44 
45 
46 void
warn(const char * format,...)47 warn(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 
61 void
error(const char * format,...)62 error(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 
78 static void
usage()79 usage()
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 
101 static void
version()102 version()
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 
130 static bool
has_extension(char * name,const char * ext)131 has_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 
143 static void
cut_extension(char * name,const char * ext)144 cut_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 
153 static void
add_extension(char * name,const char * ext)154 add_extension(char *name, const char *ext)
155 {
156 	strlcat(name, ext, B_PATH_NAME_LENGTH);
157 }
158 
159 
160 static void
parse_options(int argc,char * argv[])161 parse_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 
249 static void
compile()250 compile()
251 {
252 	if (!has_extension(sOutputFile, ".rsrc"))
253 		add_extension(sOutputFile, ".rsrc");
254 
255 	rdef_compile(sOutputFile);
256 }
257 
258 
259 static void
decompile()260 decompile()
261 {
262 	if (!has_extension(sOutputFile, ".rdef"))
263 		add_extension(sOutputFile, ".rdef");
264 
265 	rdef_decompile(sOutputFile);
266 }
267 
268 
269 static void
report_error()270 report_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 
301 int
main(int argc,char * argv[])302 main(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