xref: /haiku/src/bin/resattr.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
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
13 static 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
38 static int sArgc;
39 static const char *const *sArgv;
40 
41 // print_usage
42 void
43 print_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
55 static
56 void
57 print_usage_and_exit(bool error)
58 {
59 	print_usage(error);
60 	exit(error ? 1 : 0);
61 }
62 
63 // next_arg
64 static
65 const char*
66 next_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
78 static
79 void
80 write_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
124 static
125 void
126 write_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
172 static
173 void
174 resources_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
224 static
225 void
226 attributes_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
267 int
268 main(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