xref: /haiku/src/bin/xres.cpp (revision 45b01eb84189a623981270a02d21c7a8cb93a9e1)
1 /*
2  * Copyright 2005-2009, 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 *
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 
109 	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 
118 	ResourceID(const ResourceID &other)
119 	{
120 		*this = other;
121 	}
122 
123 	bool Matches(const ResourceID &other) const
124 	{
125 		return ((type == other.type || type == B_ANY_TYPE)
126 			&& (wildcardID || id == other.id));
127 	}
128 
129 	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 {
141 	ResourceDataSource()
142 	{
143 	}
144 
145 	virtual ~ResourceDataSource()
146 	{
147 	}
148 
149 	virtual void GetData(const void *&data, size_t &size) = 0;
150 
151 	virtual void Flush()
152 	{
153 	}
154 };
155 
156 
157 // MemoryResourceDataSource
158 struct MemoryResourceDataSource : ResourceDataSource {
159 	MemoryResourceDataSource(const void *data, size_t size, bool clone)
160 	{
161 		_Init(data, size, clone);
162 	}
163 
164 	MemoryResourceDataSource(const char *data, bool clone)
165 	{
166 		_Init(data, strlen(data) + 1, clone);
167 	}
168 
169 	virtual ~MemoryResourceDataSource()
170 	{
171 		if (fOwner)
172 			delete[] fData;
173 	}
174 
175 	virtual void GetData(const void *&data, size_t &size)
176 	{
177 		data = fData;
178 		size = fSize;
179 	}
180 
181 private:
182 	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 {
205 	FileResourceDataSource(const char *path)
206 		:
207 		fPath(path),
208 		fData(NULL),
209 		fSize(0)
210 	{
211 	}
212 
213 	virtual ~FileResourceDataSource()
214 	{
215 		Flush();
216 	}
217 
218 	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 
265 	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 {
282 	State()
283 	{
284 	}
285 
286 	virtual ~State()
287 	{
288 	}
289 
290 	virtual void SetOutput(const char *path)
291 	{
292 		(void)path;
293 	}
294 
295 	virtual void ProcessInput(const char *path)
296 	{
297 		(void)path;
298 	}
299 
300 	virtual void SetInclusionPattern(const ResourceID &pattern)
301 	{
302 		(void)pattern;
303 	}
304 
305 	virtual void SetExclusionPattern(const ResourceID &pattern)
306 	{
307 		(void)pattern;
308 	}
309 
310 	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 {
322 	ListState()
323 	{
324 	}
325 
326 	virtual ~ListState()
327 	{
328 	}
329 
330 	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 " %11lu  %s\n", resource_type(type), id,
368 				size, (name && strlen(name) > 0 ? name : "(no name)"));
369 		}
370 	}
371 };
372 
373 
374 // WriteFileState
375 struct WriteFileState : State {
376 	WriteFileState()
377 		:
378 		fOutputFilePath(kDefaultOutputFile),
379 		fResources(NULL),
380 		fInclusionPattern(NULL),
381 		fExclusionPattern(NULL)
382 	{
383 	}
384 
385 	virtual ~WriteFileState()
386 	{
387 		_FlushOutput();
388 	}
389 
390 	virtual void SetOutput(const char *path)
391 	{
392 		_FlushOutput();
393 
394 		fOutputFilePath = path;
395 	}
396 
397 	virtual void ProcessInput(const char *path)
398 	{
399 		// open the file for reading
400 		BFile file;
401 		status_t error = file.SetTo(path, B_READ_ONLY);
402 		if (error != B_OK) {
403 			fprintf(stderr, "Error: Failed to open input file \"%s\": %s\n",
404 				path, strerror(error));
405 			exit(1);
406 		}
407 
408 		// open the resources
409 		BResources resources;
410 		error = resources.SetTo(&file, false);
411 		if (error != B_OK) {
412 			if (error == B_ERROR) {
413 				fprintf(stderr, "Error: Input file \"%s\" is not a resource "
414 				"file.\n", path);
415 			} else {
416 				fprintf(stderr, "Error: Failed to read resources from input "
417 					"file \"%s\": %s\n", path, strerror(error));
418 			}
419 
420 			exit(1);
421 		}
422 		resources.PreloadResourceType();
423 
424 		// add resources
425 		type_code type;
426 		int32 id;
427 		const char *name;
428 		size_t size;
429 		for (int32 i = 0;
430 			 resources.GetResourceInfo(i, &type, &id, &name, &size);
431 			 i++) {
432 			// load the resource
433 			const void *data = resources.LoadResource(type, id, &size);
434 			if (!data) {
435 				fprintf(stderr, "Error: Failed to read resources from input "
436 					"file \"%s\".\n", path);
437 
438 				exit(1);
439 			}
440 
441 			// add it
442 			MemoryResourceDataSource dataSource(data, size, false);
443 			AddResource(ResourceID(type, id), name, &dataSource);
444 		}
445 	}
446 
447 	virtual void SetInclusionPattern(const ResourceID &pattern)
448 	{
449 		if (!fInclusionPattern)
450 			fInclusionPattern = new ResourceID;
451 		*fInclusionPattern = pattern;
452 	}
453 
454 	virtual void SetExclusionPattern(const ResourceID &pattern)
455 	{
456 		if (!fExclusionPattern)
457 			fExclusionPattern = new ResourceID;
458 		*fExclusionPattern = pattern;
459 	}
460 
461 	virtual void AddResource(const ResourceID &id, const char *name,
462 		ResourceDataSource *dataSource)
463 	{
464 		_PrepareOutput();
465 
466 		// filter resource
467 		if ((fInclusionPattern && !fInclusionPattern->Matches(id))
468 			|| (fExclusionPattern && fExclusionPattern->Matches(id))) {
469 			// not included or explicitly excluded
470 			return;
471 		}
472 
473 		// get resource data
474 		const void *data;
475 		size_t size;
476 		dataSource->GetData(data, size);
477 
478 		// add the resource
479 		status_t error = fResources->AddResource(id.type, id.id, data, size,
480 			name);
481 		if (error != B_OK) {
482 			fprintf(stderr, "Error: Failed to add resource type '%s', ID %"
483 				B_PRId32 " to output file \"%s\": %s\n", resource_type(id.type),
484 				id.id, fOutputFilePath.c_str(), strerror(error));
485 			exit(1);
486 		}
487 	}
488 
489 private:
490 	void _FlushOutput()
491 	{
492 		if (fResources) {
493 			status_t error = fResources->Sync();
494 			if (error != B_OK) {
495 				fprintf(stderr, "Error: Failed to write resources to output "
496 					"file \"%s\": %s\n", fOutputFilePath.c_str(),
497 					strerror(error));
498 
499 				exit(1);
500 			}
501 
502 			delete fResources;
503 			fResources = NULL;
504 		}
505 	}
506 
507 	void _PrepareOutput()
508 	{
509 		if (fResources)
510 			return;
511 
512 		// open the file for writing
513 		BFile file;
514 		status_t error = file.SetTo(fOutputFilePath.c_str(),
515 			B_READ_WRITE | B_CREATE_FILE);
516 		if (error != B_OK) {
517 			fprintf(stderr, "Error: Failed to open output file \"%s\": %s\n",
518 				fOutputFilePath.c_str(), strerror(error));
519 			exit(1);
520 		}
521 
522 		// open the resources
523 		fResources = new BResources;
524 		error = fResources->SetTo(&file, true);
525 		if (error != B_OK) {
526 			fprintf(stderr, "Error: Failed to init resources for output "
527 				"file \"%s\": %s\n", fOutputFilePath.c_str(), strerror(error));
528 
529 			exit(1);
530 		}
531 	}
532 
533 private:
534 	string		fOutputFilePath;
535 	BResources	*fResources;
536 	ResourceID	*fInclusionPattern;
537 	ResourceID	*fExclusionPattern;
538 };
539 
540 
541 // Command
542 struct Command {
543 	Command()
544 	{
545 	}
546 
547 	virtual ~Command()
548 	{
549 	}
550 
551 	virtual void Do(State *state) = 0;
552 };
553 
554 
555 // SetOutputCommand
556 struct SetOutputCommand : Command {
557 	SetOutputCommand(const char *path)
558 		:
559 		Command(),
560 		fPath(path)
561 	{
562 	}
563 
564 	virtual void Do(State *state)
565 	{
566 		state->SetOutput(fPath.c_str());
567 	}
568 
569 private:
570 	string	fPath;
571 };
572 
573 
574 // ProcessInputCommand
575 struct ProcessInputCommand : Command {
576 	ProcessInputCommand(const char *path)
577 		:
578 		Command(),
579 		fPath(path)
580 	{
581 	}
582 
583 	virtual void Do(State *state)
584 	{
585 		state->ProcessInput(fPath.c_str());
586 	}
587 
588 private:
589 	string	fPath;
590 };
591 
592 
593 // SetResourcePatternCommand
594 struct SetResourcePatternCommand : Command {
595 	SetResourcePatternCommand(const ResourceID &pattern, bool inclusion)
596 		:
597 		Command(),
598 		fPattern(pattern),
599 		fInclusion(inclusion)
600 	{
601 	}
602 
603 	virtual void Do(State *state)
604 	{
605 		if (fInclusion)
606 			state->SetInclusionPattern(fPattern);
607 		else
608 			state->SetExclusionPattern(fPattern);
609 	}
610 
611 private:
612 	ResourceID	fPattern;
613 	bool		fInclusion;
614 };
615 
616 
617 // AddResourceCommand
618 struct AddResourceCommand : Command {
619 	AddResourceCommand(const ResourceID &id, const char *name,
620 			ResourceDataSource *dataSource)
621 		:
622 		Command(),
623 		fID(id),
624 		fHasName(name),
625 		fDataSource(dataSource)
626 	{
627 		if (fHasName)
628 			fName = name;
629 	}
630 
631 	virtual ~AddResourceCommand()
632 	{
633 		delete fDataSource;
634 	}
635 
636 	virtual void Do(State *state)
637 	{
638 		state->AddResource(fID, (fHasName ? fName.c_str() : NULL),
639 			fDataSource);
640 		fDataSource->Flush();
641 	}
642 
643 private:
644 	ResourceID			fID;
645 	string				fName;
646 	bool				fHasName;
647 	ResourceDataSource	*fDataSource;
648 };
649 
650 
651 // print_usage
652 static void
653 print_usage(bool error)
654 {
655 	// get command name
656 	const char *commandName = NULL;
657 	if (kArgc > 0) {
658 		if (const char *lastSlash = strchr(kArgv[0], '/'))
659 			commandName = lastSlash + 1;
660 		else
661 			commandName = kArgv[0];
662 	}
663 
664 	if (!commandName || strlen(commandName) == 0)
665 		commandName = kCommandName;
666 
667 	// print usage
668 	fprintf((error ? stderr : stdout), kUsage, commandName, commandName,
669 		commandName);
670 }
671 
672 
673 // print_usage_and_exit
674 static void
675 print_usage_and_exit(bool error)
676 {
677 	print_usage(error);
678 	exit(error ? 1 : 0);
679 }
680 
681 
682 // next_arg
683 static const char *
684 next_arg(int &argi, bool optional = false)
685 {
686 	if (argi >= kArgc) {
687 		if (!optional)
688 			print_usage_and_exit(true);
689 		return NULL;
690 	}
691 
692 	return kArgv[argi++];
693 }
694 
695 
696 // parse_resource_id
697 static void
698 parse_resource_id(const char *toParse, ResourceID &resourceID,
699 	const char **name = NULL)
700 {
701 	int len = strlen(toParse);
702 
703 	// type
704 	if (len < 4)
705 		print_usage_and_exit(true);
706 
707 	resourceID.type = ((int32)toParse[0] << 24) | ((int32)toParse[1] << 16)
708 		| ((int32)toParse[2] << 8) | (int32)toParse[3];
709 
710 	if (toParse[4] == '\0') {
711 		// if a name can be provided, the ID is mandatory
712 		if (name)
713 			print_usage_and_exit(true);
714 
715 		resourceID.id = 0;
716 		resourceID.wildcardID = true;
717 		return;
718 	}
719 
720 	if (toParse[4] != ':')
721 		print_usage_and_exit(true);
722 
723 	toParse += 5;
724 	len -= 5;
725 
726 	// ID
727 	bool negative = false;
728 	if (*toParse == '-') {
729 		negative = true;
730 		toParse++;
731 		len--;
732 	}
733 
734 	if (*toParse < '0' || *toParse > '9')
735 		print_usage_and_exit(true);
736 
737 	int id = 0;
738 	while (*toParse >= '0' && *toParse <= '9') {
739 		id = 10 * id + (*toParse - '0');
740 		toParse++;
741 		len--;
742 	}
743 
744 	resourceID.wildcardID = false;
745 	resourceID.id = (negative ? -id : id);
746 
747 	if (*toParse == '\0') {
748 		if (name)
749 			*name = kDefaultResourceName;
750 			return;
751 	}
752 
753 	if (*toParse != ':')
754 		print_usage_and_exit(true);
755 
756 	// the remainder is name
757 	*name = toParse + 1;
758 }
759 
760 
761 // main
762 int
763 main(int argc, const char *const *argv)
764 {
765 	kArgc = argc;
766 	kArgv = argv;
767 
768 	if (argc < 2)
769 		print_usage_and_exit(true);
770 
771 	BList commandList;
772 
773 	// parse the arguments
774 	bool noMoreOptions = false;
775 	bool list = false;
776 	bool noList = false;
777 	bool hasInputFiles = false;
778 	for (int argi = 1; argi < argc; ) {
779 		const char *arg = argv[argi++];
780 		if (!noMoreOptions && arg[0] == '-') {
781 			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0)
782 				print_usage_and_exit(false);
783 
784 			if (strlen(arg) != 2)
785 				print_usage_and_exit(true);
786 
787 			switch (arg[1]) {
788 				case 'a':
789 				{
790 					noList = true;
791 
792 					// get id
793 					const char *typeString = next_arg(argi);
794 					ResourceID resourceID;
795 					const char *name = NULL;
796 					parse_resource_id(typeString, resourceID, &name);
797 
798 					// get data
799 					const char *file = next_arg(argi);
800 					ResourceDataSource *dataSource;
801 					if (strcmp(file, "-s") == 0) {
802 						const char *data = next_arg(argi);
803 						dataSource = new MemoryResourceDataSource(data, false);
804 
805 					} else {
806 						dataSource = new FileResourceDataSource(file);
807 					}
808 
809 					// add command
810 					Command *command = new AddResourceCommand(resourceID,
811 						name, dataSource);
812 					commandList.AddItem(command);
813 
814 					break;
815 				}
816 
817 				case 'd':
818 				{
819 					noList = true;
820 
821 					// get pattern
822 					const char *typeString = next_arg(argi);
823 					ResourceID pattern;
824 					parse_resource_id(typeString, pattern);
825 
826 					// add command
827 					Command *command = new SetResourcePatternCommand(pattern,
828 						false);
829 					commandList.AddItem(command);
830 
831 					break;
832 				}
833 
834 				case 'l':
835 				{
836 					list = true;
837 					break;
838 				}
839 
840 				case 'o':
841 				{
842 					noList = true;
843 
844 					// get file name
845 					const char *out = next_arg(argi);
846 
847 					// add command
848 					Command *command = new SetOutputCommand(out);
849 					commandList.AddItem(command);
850 
851 					break;
852 				}
853 
854 				case 'x':
855 				{
856 					noList = true;
857 
858 					// get pattern
859 					const char *typeString = next_arg(argi);
860 					ResourceID pattern;
861 					parse_resource_id(typeString, pattern);
862 
863 					// add command
864 					Command *command = new SetResourcePatternCommand(pattern,
865 						true);
866 					commandList.AddItem(command);
867 
868 					break;
869 				}
870 
871 				case '-':
872 					noMoreOptions = true;
873 					break;
874 
875 				default:
876 					print_usage_and_exit(true);
877 					break;
878 			}
879 
880 		} else {
881 			// input file
882 			hasInputFiles = true;
883 			Command *command = new ProcessInputCommand(arg);
884 			commandList.AddItem(command);
885 		}
886 	}
887 
888 	// don't allow "-l" together with other comands or without at least one
889 	// input file
890 	if ((list && noList) || (list && !hasInputFiles))
891 		print_usage_and_exit(true);
892 
893 	// create a state
894 	State *state;
895 	if (list)
896 		state = new ListState();
897 	else
898 		state = new WriteFileState();
899 
900 	// process commands
901 	for (int32 i = 0; Command *command = (Command*)commandList.ItemAt(i); i++)
902 		command->Do(state);
903 
904 	// delete state (will flush resources)
905 	delete state;
906 
907 	return 0;
908 }
909