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