xref: /haiku/src/bin/rc/rc.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
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 
232 	for (int i = 1; i < argc; ++i) {
233 		if (argv[i] == NULL)
234 			continue;
235 
236 		if (sFirstInputFile == NULL)
237 			sFirstInputFile = argv[i];
238 
239 		rdef_add_input_file(argv[i]);
240 	}
241 
242 	if (sOutputFile[0] == '\0') {
243 		// no output file name was given, use the name of the
244 		// first source file as base
245 		strlcpy(sOutputFile, sFirstInputFile, sizeof(sOutputFile));
246 
247 		cut_extension(sOutputFile, sDecompile ? ".rsrc" : ".rdef");
248 		add_extension(sOutputFile, sDecompile ? ".rdef" : ".rsrc");
249 	}
250 }
251 
252 
253 static void
254 compile()
255 {
256 	if (!has_extension(sOutputFile, ".rsrc"))
257 		add_extension(sOutputFile, ".rsrc");
258 
259 	rdef_compile(sOutputFile);
260 }
261 
262 
263 static void
264 decompile()
265 {
266 	if (!has_extension(sOutputFile, ".rdef"))
267 		add_extension(sOutputFile, ".rdef");
268 
269 	rdef_decompile(sOutputFile);
270 }
271 
272 
273 static void
274 report_error()
275 {
276 	switch (rdef_err) {
277 		case RDEF_COMPILE_ERR:
278 			error("%s:%ld %s", rdef_err_file, rdef_err_line, rdef_err_msg);
279 			break;
280 
281 		case RDEF_FILE_NOT_FOUND:
282 			error("%s not found", rdef_err_file);
283 			break;
284 
285 		case RDEF_NO_RESOURCES:
286 			error("%s is not a resource file", rdef_err_file);
287 			break;
288 
289 		case RDEF_WRITE_ERR:
290 			error("error writing to %s (%s)", rdef_err_file, rdef_err_msg);
291 			break;
292 
293 		case B_NO_MEMORY:
294 			error("out of memory");
295 			break;
296 
297 		case B_ERROR:
298 		default:
299 			error("unknown error: %lx (%s)", rdef_err, strerror(rdef_err));
300 			break;
301 	}
302 }
303 
304 
305 int
306 main(int argc, char *argv[])
307 {
308 	parse_options(argc, argv);
309 
310 	rdef_set_flags(sFlags);
311 
312 	if (sDecompile)
313 		decompile();
314 	else
315 		compile();
316 
317 	rdef_free_input_files();
318 	rdef_free_include_dirs();
319 
320 	if (rdef_err != B_OK)
321 		report_error();
322 
323 	return EXIT_SUCCESS;
324 }
325 
326