xref: /haiku/src/bin/xres.cpp (revision 5a86b40e33c129f20ba750a265bf0a327d327869)
1 /*
2  * Copyright 2005-2010, Ingo Weinhold, bonefish@users.sf.net.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include <string>
12 
13 #include <List.h>
14 #include <Resources.h>
15 #include <StorageDefs.h>
16 #include <TypeConstants.h>
17 
18 
19 using namespace std;
20 
21 
22 static const char *kCommandName = "xres";
23 static const char *kDefaultResourceName = NULL;
24 static const char *kDefaultOutputFile = "xres.output.rsrc";
25 static const int kMaxSaneResourceSize = 100 * 1024 * 1024;	// 100 MB
26 
27 static int kArgc;
28 static const char *const *kArgv;
29 
30 // usage
31 const char *kUsage =
32 "Usage: %s ( -h | --help )\n"
33 "       %s -l <file> ...\n"
34 "       %s <command> ...\n"
35 "\n"
36 "The first form prints this help text and exits.\n"
37 "\n"
38 "The second form lists the resources of all given files.\n"
39 "\n"
40 "The third form manipulates the resources of one or more files according to\n"
41 "the given commands.\n"
42 "\n"
43 "Valid commands are:\n"
44 "  <input file>\n"
45 "         - Add the resources read from file <input file> to the current\n"
46 "           output file. The file can either be a resource file or an\n"
47 "           executable file.\n"
48 "  -a <type>:<id>[:<name>] ( <file> |  -s <data> )\n"
49 "         - Add a resource to the current output file. The added resource is\n"
50 "           of type <type> and has the ID <id>. If given the resource will\n"
51 "           have name <name>, otherwise it won't have a name. The resource\n"
52 "           data will either be the string <data> provided on the command\n"
53 "           line or the data read from file <file> (the whole contents).\n"
54 "  -d <type>[:<id>]\n"
55 "         - Excludes resources with type <type> and, if given, ID <id> from\n"
56 "           being written to the output file. This applies to all resources\n"
57 "           read from input files or directly specified via command \"-a\"\n"
58 "           following this command until the next \"-d\" command.\n"
59 "  -o <output file>\n"
60 "         - Changes the output file to <output file>. All resources specified\n"
61 "           by subsequent <input file> or \"-a\" commands will be written\n"
62 "           to this file until the next output file is specified via the\n"
63 "           \"-o\" command. Resources specified later overwrite earlier ones\n"
64 "           with the same type and ID. If <output file> doesn't exist yet, \n"
65 "           a resource file with the name will be created. If it exists and\n"
66 "           is an executable file, the resources will be added to it (if the\n"
67 "           file already has resources, they will be removed before). If it\n"
68 "           is a resource file or a file of unknown type, it will be\n"
69 "           overwritten with a resource file containing the specified\n"
70 "           resources. The initial output file is \"xres.output.rsrc\".\n"
71 "           Note that an output file will only be created or modified, if at\n"
72 "           least one <input file> or \"-a\" command is given for it.\n"
73 "  -x <type>[:<id>]\n"
74 "         - Only resources with type <type> and, if given, ID <id> will be\n"
75 "           written to the output file. This applies to all resources\n"
76 "           read from input files or directly specified via command \"-a\"\n"
77 "           following this command until the next \"-x\" command.\n"
78 "  --     - All following arguments, even if starting with a \"-\" character,\n"
79 "           are treated as input file names.\n"
80 "\n"
81 "Parameters:\n"
82 "  <type> - A type constant consisting of exactly four characters.\n"
83 "  <id>   - A positive or negative integer.\n"
84 ;
85 
86 
87 // resource_type
88 static const char *
resource_type(type_code type)89 resource_type(type_code type)
90 {
91 	static char typeString[5];
92 
93 	typeString[0] = type >> 24;
94 	typeString[1] = (type >> 16) & 0xff;
95 	typeString[2] = (type >> 8) & 0xff;
96 	typeString[3] = type & 0xff;
97 	typeString[4] = '\0';
98 
99 	return typeString;
100 }
101 
102 
103 // ResourceID
104 struct ResourceID {
105 	type_code	type;
106 	int32		id;
107 	bool		wildcardID;
108 
ResourceIDResourceID109 	ResourceID(type_code type = B_ANY_TYPE, int32 id = 0,
110 			bool wildcardID = true)
111 		:
112 		type(type),
113 		id(id),
114 		wildcardID(wildcardID)
115 	{
116 	}
117 
ResourceIDResourceID118 	ResourceID(const ResourceID &other)
119 	{
120 		*this = other;
121 	}
122 
MatchesResourceID123 	bool Matches(const ResourceID &other) const
124 	{
125 		return ((type == other.type || type == B_ANY_TYPE)
126 			&& (wildcardID || id == other.id));
127 	}
128 
operator =ResourceID129 	ResourceID &operator=(const ResourceID &other)
130 	{
131 		type = other.type;
132 		id = other.id;
133 		wildcardID = other.wildcardID;
134 		return *this;
135 	}
136 };
137 
138 
139 // ResourceDataSource
140 struct ResourceDataSource {
ResourceDataSourceResourceDataSource141 	ResourceDataSource()
142 	{
143 	}
144 
~ResourceDataSourceResourceDataSource145 	virtual ~ResourceDataSource()
146 	{
147 	}
148 
149 	virtual void GetData(const void *&data, size_t &size) = 0;
150 
FlushResourceDataSource151 	virtual void Flush()
152 	{
153 	}
154 };
155 
156 
157 // MemoryResourceDataSource
158 struct MemoryResourceDataSource : ResourceDataSource {
MemoryResourceDataSourceMemoryResourceDataSource159 	MemoryResourceDataSource(const void *data, size_t size, bool clone)
160 	{
161 		_Init(data, size, clone);
162 	}
163 
MemoryResourceDataSourceMemoryResourceDataSource164 	MemoryResourceDataSource(const char *data, bool clone)
165 	{
166 		_Init(data, strlen(data) + 1, clone);
167 	}
168 
~MemoryResourceDataSourceMemoryResourceDataSource169 	virtual ~MemoryResourceDataSource()
170 	{
171 		if (fOwner)
172 			delete[] fData;
173 	}
174 
GetDataMemoryResourceDataSource175 	virtual void GetData(const void *&data, size_t &size)
176 	{
177 		data = fData;
178 		size = fSize;
179 	}
180 
181 private:
_InitMemoryResourceDataSource182 	void _Init(const void *data, size_t size, bool clone)
183 	{
184 		if (clone) {
185 			fData = new uint8[size];
186 			memcpy(fData, data, size);
187 			fSize = size;
188 			fOwner = true;
189 		} else {
190 			fData = (uint8*)data;
191 			fSize = size;
192 			fOwner = false;
193 		}
194 	}
195 
196 private:
197 	uint8	*fData;
198 	size_t	fSize;
199 	bool	fOwner;
200 };
201 
202 
203 // FileResourceDataSource
204 struct FileResourceDataSource : ResourceDataSource {
FileResourceDataSourceFileResourceDataSource205 	FileResourceDataSource(const char *path)
206 		:
207 		fPath(path),
208 		fData(NULL),
209 		fSize(0)
210 	{
211 	}
212 
~FileResourceDataSourceFileResourceDataSource213 	virtual ~FileResourceDataSource()
214 	{
215 		Flush();
216 	}
217 
GetDataFileResourceDataSource218 	virtual void GetData(const void *&_data, size_t &_size)
219 	{
220 		if (!fData) {
221 			// open the file for reading
222 			BFile file;
223 			status_t error = file.SetTo(fPath.c_str(), B_READ_ONLY);
224 			if (error != B_OK) {
225 				fprintf(stderr, "Error: Failed to open file \"%s\": %s\n",
226 					fPath.c_str(), strerror(error));
227 
228 				exit(1);
229 			}
230 
231 			// get size
232 			off_t size;
233 			error = file.GetSize(&size);
234 			if (error != B_OK) {
235 				fprintf(stderr, "Error: Failed to get size of file \"%s\": "
236 					"%s\n", fPath.c_str(), strerror(error));
237 
238 				exit(1);
239 			}
240 
241 			// check size
242 			if (size > kMaxSaneResourceSize) {
243 				fprintf(stderr, "Error: Resource data file \"%s\" is too big\n",
244 					fPath.c_str());
245 
246 				exit(1);
247 			}
248 
249 			// read the data
250 			fData = new uint8[size];
251 			fSize = size;
252 			ssize_t bytesRead = file.ReadAt(0, fData, fSize);
253 			if (bytesRead < 0) {
254 				fprintf(stderr, "Error: Failed to read data size from file "
255 					"\"%s\": %s\n", fPath.c_str(), strerror(bytesRead));
256 
257 				exit(1);
258 			}
259 		}
260 
261 		_data = fData;
262 		_size = fSize;
263 	}
264 
FlushFileResourceDataSource265 	virtual void Flush()
266 	{
267 		if (fData) {
268 			delete[] fData;
269 			fData = NULL;
270 		}
271 	}
272 
273 private:
274 	string	fPath;
275 	uint8	*fData;
276 	size_t	fSize;
277 };
278 
279 
280 // State
281 struct State {
StateState282 	State()
283 	{
284 	}
285 
~StateState286 	virtual ~State()
287 	{
288 	}
289 
SetOutputState290 	virtual void SetOutput(const char *path)
291 	{
292 		(void)path;
293 	}
294 
ProcessInputState295 	virtual void ProcessInput(const char *path)
296 	{
297 		(void)path;
298 	}
299 
SetInclusionPatternState300 	virtual void SetInclusionPattern(const ResourceID &pattern)
301 	{
302 		(void)pattern;
303 	}
304 
SetExclusionPatternState305 	virtual void SetExclusionPattern(const ResourceID &pattern)
306 	{
307 		(void)pattern;
308 	}
309 
AddResourceState310 	virtual void AddResource(const ResourceID &id, const char *name,
311 		ResourceDataSource *dataSource)
312 	{
313 		(void)id;
314 		(void)name;
315 		(void)dataSource;
316 	}
317 };
318 
319 
320 // ListState
321 struct ListState : State {
ListStateListState322 	ListState()
323 	{
324 	}
325 
~ListStateListState326 	virtual ~ListState()
327 	{
328 	}
329 
ProcessInputListState330 	virtual void ProcessInput(const char *path)
331 	{
332 		// open the file for reading
333 		BFile file;
334 		status_t error = file.SetTo(path, B_READ_ONLY);
335 		if (error != B_OK) {
336 			fprintf(stderr, "Error: Failed to open file \"%s\": %s\n", path,
337 				strerror(error));
338 			exit(1);
339 		}
340 
341 		// open the resources
342 		BResources resources;
343 		error = resources.SetTo(&file, false);
344 		if (error != B_OK) {
345 			if (error == B_ERROR) {
346 				fprintf(stderr, "Error: File \"%s\" is not a resource file.\n",
347 				path);
348 			} else {
349 				fprintf(stderr, "Error: Failed to read resources from file "
350 					"\"%s\": %s\n", path, strerror(error));
351 			}
352 
353 			exit(1);
354 		}
355 
356 		// print resources
357 		printf("\n%s resources:\n\n", path);
358 		printf(" type           ID        size  name\n");
359 		printf("------ ----------- -----------  --------------------\n");
360 
361 		type_code type;
362 		int32 id;
363 		const char *name;
364 		size_t size;
365 		for (int32 i = 0;
366 				resources.GetResourceInfo(i, &type, &id, &name, &size); i++) {
367 			printf("'%s' %11" B_PRId32 " %11" B_PRIuSIZE "  %s\n",
368 				resource_type(type), id, size,
369 				name != NULL && name[0] != '\0' ? name : "(no name)");
370 		}
371 	}
372 };
373 
374 
375 // WriteFileState
376 struct WriteFileState : State {
WriteFileStateWriteFileState377 	WriteFileState()
378 		:
379 		fOutputFilePath(kDefaultOutputFile),
380 		fResources(NULL),
381 		fInclusionPattern(NULL),
382 		fExclusionPattern(NULL)
383 	{
384 	}
385 
~WriteFileStateWriteFileState386 	virtual ~WriteFileState()
387 	{
388 		_FlushOutput();
389 	}
390 
SetOutputWriteFileState391 	virtual void SetOutput(const char *path)
392 	{
393 		_FlushOutput();
394 
395 		fOutputFilePath = path;
396 	}
397 
ProcessInputWriteFileState398 	virtual void ProcessInput(const char *path)
399 	{
400 		// open the file for reading
401 		BFile file;
402 		status_t error = file.SetTo(path, B_READ_ONLY);
403 		if (error != B_OK) {
404 			fprintf(stderr, "Error: Failed to open input file \"%s\": %s\n",
405 				path, strerror(error));
406 			exit(1);
407 		}
408 
409 		// open the resources
410 		BResources resources;
411 		error = resources.SetTo(&file, false);
412 		if (error != B_OK) {
413 			if (error == B_ERROR) {
414 				fprintf(stderr, "Error: Input file \"%s\" is not a resource "
415 				"file.\n", path);
416 			} else {
417 				fprintf(stderr, "Error: Failed to read resources from input "
418 					"file \"%s\": %s\n", path, strerror(error));
419 			}
420 
421 			exit(1);
422 		}
423 		resources.PreloadResourceType();
424 
425 		// add resources
426 		type_code type;
427 		int32 id;
428 		const char *name;
429 		size_t size;
430 		for (int32 i = 0;
431 			 resources.GetResourceInfo(i, &type, &id, &name, &size);
432 			 i++) {
433 			// load the resource
434 			const void *data = resources.LoadResource(type, id, &size);
435 			if (!data) {
436 				fprintf(stderr, "Error: Failed to read resources from input "
437 					"file \"%s\".\n", path);
438 
439 				exit(1);
440 			}
441 
442 			// add it
443 			MemoryResourceDataSource dataSource(data, size, false);
444 			AddResource(ResourceID(type, id), name, &dataSource);
445 		}
446 	}
447 
SetInclusionPatternWriteFileState448 	virtual void SetInclusionPattern(const ResourceID &pattern)
449 	{
450 		if (!fInclusionPattern)
451 			fInclusionPattern = new ResourceID;
452 		*fInclusionPattern = pattern;
453 	}
454 
SetExclusionPatternWriteFileState455 	virtual void SetExclusionPattern(const ResourceID &pattern)
456 	{
457 		if (!fExclusionPattern)
458 			fExclusionPattern = new ResourceID;
459 		*fExclusionPattern = pattern;
460 	}
461 
AddResourceWriteFileState462 	virtual void AddResource(const ResourceID &id, const char *name,
463 		ResourceDataSource *dataSource)
464 	{
465 		_PrepareOutput();
466 
467 		// filter resource
468 		if ((fInclusionPattern && !fInclusionPattern->Matches(id))
469 			|| (fExclusionPattern && fExclusionPattern->Matches(id))) {
470 			// not included or explicitly excluded
471 			return;
472 		}
473 
474 		// get resource data
475 		const void *data;
476 		size_t size;
477 		dataSource->GetData(data, size);
478 
479 		// add the resource
480 		status_t error = fResources->AddResource(id.type, id.id, data, size,
481 			name);
482 		if (error != B_OK) {
483 			fprintf(stderr, "Error: Failed to add resource type '%s', ID %"
484 				B_PRId32 " to output file \"%s\": %s\n", resource_type(id.type),
485 				id.id, fOutputFilePath.c_str(), strerror(error));
486 			exit(1);
487 		}
488 	}
489 
490 private:
_FlushOutputWriteFileState491 	void _FlushOutput()
492 	{
493 		if (fResources) {
494 			status_t error = fResources->Sync();
495 			if (error != B_OK) {
496 				fprintf(stderr, "Error: Failed to write resources to output "
497 					"file \"%s\": %s\n", fOutputFilePath.c_str(),
498 					strerror(error));
499 
500 				exit(1);
501 			}
502 
503 			delete fResources;
504 			fResources = NULL;
505 		}
506 	}
507 
_PrepareOutputWriteFileState508 	void _PrepareOutput()
509 	{
510 		if (fResources)
511 			return;
512 
513 		// open the file for writing
514 		BFile file;
515 		status_t error = file.SetTo(fOutputFilePath.c_str(),
516 			B_READ_WRITE | B_CREATE_FILE);
517 		if (error != B_OK) {
518 			fprintf(stderr, "Error: Failed to open output file \"%s\": %s\n",
519 				fOutputFilePath.c_str(), strerror(error));
520 			exit(1);
521 		}
522 
523 		// open the resources
524 		fResources = new BResources;
525 		error = fResources->SetTo(&file, true);
526 		if (error != B_OK) {
527 			fprintf(stderr, "Error: Failed to init resources for output "
528 				"file \"%s\": %s\n", fOutputFilePath.c_str(), strerror(error));
529 
530 			exit(1);
531 		}
532 	}
533 
534 private:
535 	string		fOutputFilePath;
536 	BResources	*fResources;
537 	ResourceID	*fInclusionPattern;
538 	ResourceID	*fExclusionPattern;
539 };
540 
541 
542 // Command
543 struct Command {
CommandCommand544 	Command()
545 	{
546 	}
547 
~CommandCommand548 	virtual ~Command()
549 	{
550 	}
551 
552 	virtual void Do(State *state) = 0;
553 };
554 
555 
556 // SetOutputCommand
557 struct SetOutputCommand : Command {
SetOutputCommandSetOutputCommand558 	SetOutputCommand(const char *path)
559 		:
560 		Command(),
561 		fPath(path)
562 	{
563 	}
564 
DoSetOutputCommand565 	virtual void Do(State *state)
566 	{
567 		state->SetOutput(fPath.c_str());
568 	}
569 
570 private:
571 	string	fPath;
572 };
573 
574 
575 // ProcessInputCommand
576 struct ProcessInputCommand : Command {
ProcessInputCommandProcessInputCommand577 	ProcessInputCommand(const char *path)
578 		:
579 		Command(),
580 		fPath(path)
581 	{
582 	}
583 
DoProcessInputCommand584 	virtual void Do(State *state)
585 	{
586 		state->ProcessInput(fPath.c_str());
587 	}
588 
589 private:
590 	string	fPath;
591 };
592 
593 
594 // SetResourcePatternCommand
595 struct SetResourcePatternCommand : Command {
SetResourcePatternCommandSetResourcePatternCommand596 	SetResourcePatternCommand(const ResourceID &pattern, bool inclusion)
597 		:
598 		Command(),
599 		fPattern(pattern),
600 		fInclusion(inclusion)
601 	{
602 	}
603 
DoSetResourcePatternCommand604 	virtual void Do(State *state)
605 	{
606 		if (fInclusion)
607 			state->SetInclusionPattern(fPattern);
608 		else
609 			state->SetExclusionPattern(fPattern);
610 	}
611 
612 private:
613 	ResourceID	fPattern;
614 	bool		fInclusion;
615 };
616 
617 
618 // AddResourceCommand
619 struct AddResourceCommand : Command {
AddResourceCommandAddResourceCommand620 	AddResourceCommand(const ResourceID &id, const char *name,
621 			ResourceDataSource *dataSource)
622 		:
623 		Command(),
624 		fID(id),
625 		fHasName(name),
626 		fDataSource(dataSource)
627 	{
628 		if (fHasName)
629 			fName = name;
630 	}
631 
~AddResourceCommandAddResourceCommand632 	virtual ~AddResourceCommand()
633 	{
634 		delete fDataSource;
635 	}
636 
DoAddResourceCommand637 	virtual void Do(State *state)
638 	{
639 		state->AddResource(fID, (fHasName ? fName.c_str() : NULL),
640 			fDataSource);
641 		fDataSource->Flush();
642 	}
643 
644 private:
645 	ResourceID			fID;
646 	string				fName;
647 	bool				fHasName;
648 	ResourceDataSource	*fDataSource;
649 };
650 
651 
652 // print_usage
653 static void
print_usage(bool error)654 print_usage(bool error)
655 {
656 	// get command name
657 	const char *commandName = NULL;
658 	if (kArgc > 0) {
659 		if (const char *lastSlash = strchr(kArgv[0], '/'))
660 			commandName = lastSlash + 1;
661 		else
662 			commandName = kArgv[0];
663 	}
664 
665 	if (!commandName || commandName[0] == '\0')
666 		commandName = kCommandName;
667 
668 	// print usage
669 	fprintf((error ? stderr : stdout), kUsage, commandName, commandName,
670 		commandName);
671 }
672 
673 
674 // print_usage_and_exit
675 static void
print_usage_and_exit(bool error)676 print_usage_and_exit(bool error)
677 {
678 	print_usage(error);
679 	exit(error ? 1 : 0);
680 }
681 
682 
683 // next_arg
684 static const char *
next_arg(int & argi,bool optional=false)685 next_arg(int &argi, bool optional = false)
686 {
687 	if (argi >= kArgc) {
688 		if (!optional)
689 			print_usage_and_exit(true);
690 		return NULL;
691 	}
692 
693 	return kArgv[argi++];
694 }
695 
696 
697 // parse_resource_id
698 static void
parse_resource_id(const char * toParse,ResourceID & resourceID,const char ** name=NULL)699 parse_resource_id(const char *toParse, ResourceID &resourceID,
700 	const char **name = NULL)
701 {
702 	int len = strlen(toParse);
703 
704 	// type
705 	if (len < 4)
706 		print_usage_and_exit(true);
707 
708 	resourceID.type = ((int32)toParse[0] << 24) | ((int32)toParse[1] << 16)
709 		| ((int32)toParse[2] << 8) | (int32)toParse[3];
710 
711 	if (toParse[4] == '\0') {
712 		// if a name can be provided, the ID is mandatory
713 		if (name)
714 			print_usage_and_exit(true);
715 
716 		resourceID.id = 0;
717 		resourceID.wildcardID = true;
718 		return;
719 	}
720 
721 	if (toParse[4] != ':')
722 		print_usage_and_exit(true);
723 
724 	toParse += 5;
725 	len -= 5;
726 
727 	// ID
728 	bool negative = false;
729 	if (*toParse == '-') {
730 		negative = true;
731 		toParse++;
732 		len--;
733 	}
734 
735 	if (*toParse < '0' || *toParse > '9')
736 		print_usage_and_exit(true);
737 
738 	int id = 0;
739 	while (*toParse >= '0' && *toParse <= '9') {
740 		id = 10 * id + (*toParse - '0');
741 		toParse++;
742 		len--;
743 	}
744 
745 	resourceID.wildcardID = false;
746 	resourceID.id = (negative ? -id : id);
747 
748 	if (*toParse == '\0') {
749 		if (name)
750 			*name = kDefaultResourceName;
751 		return;
752 	}
753 
754 	if (*toParse != ':')
755 		print_usage_and_exit(true);
756 
757 	// the remainder is name
758 	*name = toParse + 1;
759 }
760 
761 
762 // main
763 int
main(int argc,const char * const * argv)764 main(int argc, const char *const *argv)
765 {
766 	kArgc = argc;
767 	kArgv = argv;
768 
769 	if (argc < 2)
770 		print_usage_and_exit(true);
771 
772 	BList commandList;
773 
774 	// parse the arguments
775 	bool noMoreOptions = false;
776 	bool list = false;
777 	bool noList = false;
778 	bool hasInputFiles = false;
779 	for (int argi = 1; argi < argc; ) {
780 		const char *arg = argv[argi++];
781 		if (!noMoreOptions && arg[0] == '-') {
782 			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0)
783 				print_usage_and_exit(false);
784 
785 			if (strlen(arg) != 2)
786 				print_usage_and_exit(true);
787 
788 			switch (arg[1]) {
789 				case 'a':
790 				{
791 					noList = true;
792 
793 					// get id
794 					const char *typeString = next_arg(argi);
795 					ResourceID resourceID;
796 					const char *name = NULL;
797 					parse_resource_id(typeString, resourceID, &name);
798 
799 					// get data
800 					const char *file = next_arg(argi);
801 					ResourceDataSource *dataSource;
802 					if (strcmp(file, "-s") == 0) {
803 						const char *data = next_arg(argi);
804 						dataSource = new MemoryResourceDataSource(data, false);
805 
806 					} else {
807 						dataSource = new FileResourceDataSource(file);
808 					}
809 
810 					// add command
811 					Command *command = new AddResourceCommand(resourceID,
812 						name, dataSource);
813 					commandList.AddItem(command);
814 
815 					break;
816 				}
817 
818 				case 'd':
819 				{
820 					noList = true;
821 
822 					// get pattern
823 					const char *typeString = next_arg(argi);
824 					ResourceID pattern;
825 					parse_resource_id(typeString, pattern);
826 
827 					// add command
828 					Command *command = new SetResourcePatternCommand(pattern,
829 						false);
830 					commandList.AddItem(command);
831 
832 					break;
833 				}
834 
835 				case 'l':
836 				{
837 					list = true;
838 					break;
839 				}
840 
841 				case 'o':
842 				{
843 					noList = true;
844 
845 					// get file name
846 					const char *out = next_arg(argi);
847 
848 					// add command
849 					Command *command = new SetOutputCommand(out);
850 					commandList.AddItem(command);
851 
852 					break;
853 				}
854 
855 				case 'x':
856 				{
857 					noList = true;
858 
859 					// get pattern
860 					const char *typeString = next_arg(argi);
861 					ResourceID pattern;
862 					parse_resource_id(typeString, pattern);
863 
864 					// add command
865 					Command *command = new SetResourcePatternCommand(pattern,
866 						true);
867 					commandList.AddItem(command);
868 
869 					break;
870 				}
871 
872 				case '-':
873 					noMoreOptions = true;
874 					break;
875 
876 				default:
877 					print_usage_and_exit(true);
878 					break;
879 			}
880 
881 		} else {
882 			// input file
883 			hasInputFiles = true;
884 			Command *command = new ProcessInputCommand(arg);
885 			commandList.AddItem(command);
886 		}
887 	}
888 
889 	// don't allow "-l" together with other comands or without at least one
890 	// input file
891 	if ((list && noList) || (list && !hasInputFiles))
892 		print_usage_and_exit(true);
893 
894 	// create a state
895 	State *state;
896 	if (list)
897 		state = new ListState();
898 	else
899 		state = new WriteFileState();
900 
901 	// process commands
902 	for (int32 i = 0; Command *command = (Command*)commandList.ItemAt(i); i++)
903 		command->Do(state);
904 
905 	// delete state (will flush resources)
906 	delete state;
907 
908 	return 0;
909 }
910