1// resattr.cpp
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7#include <Entry.h>
8#include <File.h>
9#include <fs_attr.h>
10#include <Resources.h>
11
12// usage
13static const char *kUsage =
14"Usage: %s [ <options> ] -o <outFile> [ <inFile> ... ]\n"
15"\n"
16"Reads resources from zero or more input files and adds them as attributes\n"
17"to the specified output file, or (in reverse mode) reads attributes from\n"
18"zero or more input files and adds them as resources to the specified output\n"
19"file. If not existent the output file is created as an empty file.\n"
20"\n"
21"Options:\n"
22"  -h, --help       - Print this text.\n"
23"  -o <outfile>     - Specifies the output file.\n"
24"  -O, --overwrite  - Overwrite existing attributes. regardless of whether\n"
25"                     an attribute does already exist, it is always written\n"
26"                     when a respective resource is encountered in an input\n"
27"                     file. The last input file specifying the attribute\n"
28"                     wins. If the options is not given, already existing\n"
29"                     attributes remain untouched. Otherwise the first input\n"
30"                     file specifying an attribute wins.\n"
31"  -r, --reverse    - Reverse mode: Reads attributes from input files and\n"
32"                     writes resources. A unique resource ID is assigned to\n"
33"                     each attribute, so there will be no conflicts if two\n"
34"                     input files feature the same attribute.\n"
35;
36
37// command line args
38static int sArgc;
39static const char *const *sArgv;
40
41// print_usage
42void
43print_usage(bool error)
44{
45	// get nice program name
46	const char *programName = (sArgc > 0 ? sArgv[0] : "resattr");
47	if (const char *lastSlash = strrchr(programName, '/'))
48		programName = lastSlash + 1;
49
50	// print usage
51	fprintf((error ? stderr : stdout), kUsage, programName);
52}
53
54// print_usage_and_exit
55static
56void
57print_usage_and_exit(bool error)
58{
59	print_usage(error);
60	exit(error ? 1 : 0);
61}
62
63// next_arg
64static
65const char*
66next_arg(int argc, const char* const* argv, int& argi, bool dontFail = false)
67{
68	if (argi + 1 >= argc) {
69		if (dontFail)
70			return NULL;
71		print_usage_and_exit(true);
72	}
73
74	return argv[++argi];
75}
76
77// write_attributes
78static
79void
80write_attributes(BNode &out, const char *inFileName, BResources &resources,
81	bool overwrite)
82{
83	// iterate through the resources
84	type_code type;
85	int32 id;
86	const char *name;
87	size_t size;
88	for (int resIndex = 0;
89		 resources.GetResourceInfo(resIndex, &type, &id, &name, &size);
90		 resIndex++) {
91		// if we shall not overwrite attributes, we skip the attribute, if it
92		// already exists
93		attr_info attrInfo;
94		if (!overwrite && out.GetAttrInfo(name, &attrInfo) == B_OK)
95			continue;
96
97		// get the resource
98		const void *data = resources.LoadResource(type, id, &size);
99		if (!data && size > 0) {
100			// should not happen
101			fprintf(stderr, "Failed to get resource `%s', type: %" B_PRIx32
102				", id: %" B_PRId32 " from input file `%s'\n", name, type, id,
103				inFileName);
104			exit(1);
105		}
106
107		// construct a name, if the resource doesn't have one
108		char nameBuffer[32];
109		if (!name) {
110			sprintf(nameBuffer, "unnamed_%d\n", resIndex);
111			name = nameBuffer;
112		}
113
114		// write the attribute
115		ssize_t bytesWritten = out.WriteAttr(name, type, 0LL, data, size);
116		if (bytesWritten < 0) {
117			fprintf(stderr, "Failed to write attribute `%s' to output file: "
118				"%s\n", name, strerror(bytesWritten));
119		}
120	}
121}
122
123// write_resources
124static
125void
126write_resources(BResources &resources, const char *inFileName, BNode &in,
127	int32 &resID)
128{
129	// iterate through the attributes
130	char name[B_ATTR_NAME_LENGTH];
131	while (in.GetNextAttrName(name) == B_OK) {
132		// get attribute info
133		attr_info attrInfo;
134		status_t error = in.GetAttrInfo(name, &attrInfo);
135		if (error != B_OK) {
136			fprintf(stderr, "Failed to get info for attribute `%s' of input "
137				"file `%s': %s\n", name, inFileName, strerror(error));
138			exit(1);
139		}
140
141		// read attribute
142		char *data = new char[attrInfo.size];
143		ssize_t bytesRead = in.ReadAttr(name, attrInfo.type, 0LL, data,
144			attrInfo.size);
145		if (bytesRead < 0) {
146			fprintf(stderr, "Failed to read attribute `%s' of input "
147				"file `%s': %s\n", name, inFileName, strerror(bytesRead));
148			delete[] data;
149			exit(1);
150		}
151
152		// find unique ID
153		const char *existingName;
154		size_t existingSize;
155		while (resources.GetResourceInfo(attrInfo.type, resID, &existingName,
156			&existingSize)) {
157			resID++;
158		}
159
160		// write resource
161		error = resources.AddResource(attrInfo.type, resID++, data,
162			attrInfo.size, name);
163		if (error != B_OK) {
164			fprintf(stderr, "Failed to write resource `%s' to output "
165				"file: %s\n", name, strerror(error));
166		}
167		delete[] data;
168	}
169}
170
171// resources_to_attributes
172static
173void
174resources_to_attributes(const char *outputFile, const char **inputFiles,
175	int inputFileCount, bool overwrite)
176{
177	// create output file, if it doesn't exist
178	if (!BEntry(outputFile).Exists()) {
179		BFile createdOut;
180		status_t error = createdOut.SetTo(outputFile,
181			B_READ_WRITE | B_CREATE_FILE);
182		if (error != B_OK) {
183			fprintf(stderr, "Failed to create output file `%s': %s\n",
184				outputFile, strerror(error));
185			exit(1);
186		}
187	}
188
189	// open output file
190	BNode out;
191	status_t error = out.SetTo(outputFile);
192	if (error != B_OK) {
193		fprintf(stderr, "Failed to open output file `%s': %s\n", outputFile,
194			strerror(error));
195		exit(1);
196	}
197
198	// iterate through the input files
199	for (int i = 0; i < inputFileCount; i++) {
200		// open input file
201		BFile in;
202		error = in.SetTo(inputFiles[i], B_READ_ONLY);
203		if (error != B_OK) {
204			fprintf(stderr, "Failed to open input file `%s': %s\n",
205				inputFiles[i], strerror(error));
206			exit(1);
207		}
208
209		// open resources
210		BResources resources;
211		error = resources.SetTo(&in, false);
212		if (error != B_OK) {
213			fprintf(stderr, "Failed to read resources of input file `%s': %s\n",
214				inputFiles[i], strerror(error));
215			exit(1);
216		}
217
218		// add the attributes
219		write_attributes(out, inputFiles[i], resources, overwrite);
220	}
221}
222
223// resources_to_attributes
224static
225void
226attributes_to_resources(const char *outputFile, const char **inputFiles,
227	int inputFileCount)
228{
229	// open output file
230	BFile out;
231	status_t error = out.SetTo(outputFile, B_READ_WRITE | B_CREATE_FILE);
232	if (error != B_OK) {
233		fprintf(stderr, "Failed to open output file `%s': %s\n", outputFile,
234			strerror(error));
235		exit(1);
236	}
237
238	// init output resources
239	BResources resources;
240	error = resources.SetTo(&out, false);
241	if (error != B_OK) {
242		fprintf(stderr, "Failed to init resources of output file `%s': %s\n",
243			outputFile, strerror(error));
244		exit(1);
245	}
246
247	int32 resID = 0;
248
249	// iterate through the input files
250	for (int i = 0; i < inputFileCount; i++) {
251		// open input file
252		BNode in;
253		error = in.SetTo(inputFiles[i]);
254		if (error != B_OK) {
255			fprintf(stderr, "Failed to open input file `%s': %s\n",
256				inputFiles[i], strerror(error));
257			exit(1);
258		}
259
260		// add the resources
261
262		write_resources(resources, inputFiles[i], in, resID);
263	}
264}
265
266// main
267int
268main(int argc, const char *const *argv)
269{
270	sArgc = argc;
271	sArgv = argv;
272
273	// parameters
274	const char *outputFile = NULL;
275	bool overwrite = false;
276	bool reverse = false;
277	const char **inputFiles = new const char*[argc];
278	int inputFileCount = 0;
279
280	// parse arguments
281	for (int argi = 1; argi < argc; argi++) {
282		const char *arg = argv[argi];
283		if (arg[0] == '-') {
284			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
285				print_usage_and_exit(false);
286			} else if (strcmp(arg, "-o") == 0) {
287				outputFile = next_arg(argc, argv, argi);
288			} else if (strcmp(arg, "-O") == 0) {
289				overwrite = true;
290			} else if (strcmp(arg, "-r") == 0
291				|| strcmp(arg, "--reverse") == 0) {
292				reverse = true;
293			} else {
294				print_usage_and_exit(true);
295			}
296		} else {
297			inputFiles[inputFileCount++] = arg;
298		}
299	}
300
301	// check parameters
302	if (!outputFile)
303		print_usage_and_exit(true);
304
305	if (reverse) {
306		attributes_to_resources(outputFile, inputFiles, inputFileCount);
307	} else {
308		resources_to_attributes(outputFile, inputFiles, inputFileCount,
309			overwrite);
310	}
311
312	return 0;
313}
314