1/*
2 * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10#include <AppFileInfo.h>
11#include <Mime.h>
12
13static const char *kCommandName = "settype";
14
15static int kArgc;
16static const char *const *kArgv;
17
18// usage
19const char *kUsage =
20"Usage: %s <options> <file> ...\n"
21"\n"
22"Sets the MIME type, signature, and/or preferred application of one or more\n"
23"files.\n"
24"\n"
25"Options:\n"
26"  -h, --help\n"
27"         - Print this help text and exit.\n"
28"  -preferredAppSig <signature>\n"
29"         - Set the preferred application of the given files to\n"
30"           <signature>.\n"
31"  -s <signature>\n"
32"         - Set the application signature of the given files to\n"
33"           <signature>.\n"
34"  -t <type>\n"
35"         - Set the MIME type of the given files to <type>.\n"
36;
37
38// print_usage
39static void
40print_usage(bool error)
41{
42	// get command name
43	const char *commandName = NULL;
44	if (kArgc > 0) {
45		if (const char *lastSlash = strchr(kArgv[0], '/'))
46			commandName = lastSlash + 1;
47		else
48			commandName = kArgv[0];
49	}
50
51	if (!commandName || strlen(commandName) == 0)
52		commandName = kCommandName;
53
54	// print usage
55	fprintf((error ? stderr : stdout), kUsage, commandName, commandName,
56		commandName);
57}
58
59// print_usage_and_exit
60static void
61print_usage_and_exit(bool error)
62{
63	print_usage(error);
64	exit(error ? 1 : 0);
65}
66
67// next_arg
68static const char *
69next_arg(int &argi, bool optional = false)
70{
71	if (argi >= kArgc) {
72		if (!optional)
73			print_usage_and_exit(true);
74		return NULL;
75	}
76
77	return kArgv[argi++];
78}
79
80// check_mime_type
81static void
82check_mime_type(const char *type)
83{
84	// check type
85	if (type) {
86		if (!BMimeType::IsValid(type)) {
87			fprintf(stderr, "\"%s\" is no valid MIME type.\n", type);
88
89			exit(1);
90		}
91	}
92}
93
94// main
95int
96main(int argc, const char *const *argv)
97{
98	kArgc = argc;
99	kArgv = argv;
100
101	// parameters
102	const char **files = new const char*[argc];
103	int fileCount = 0;
104	const char *type = NULL;
105	const char *signature = NULL;
106	const char *preferredApp = NULL;
107
108	// parse the arguments
109	for (int argi = 1; argi < argc; ) {
110		const char *arg = argv[argi++];
111		if (arg[0] == '-') {
112			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
113				print_usage_and_exit(false);
114
115			} else if (strcmp(arg, "-preferredAppSig") == 0) {
116				preferredApp = next_arg(argi);
117
118			} else if (strcmp(arg, "-s") == 0) {
119				signature = next_arg(argi);
120
121			} else if (strcmp(arg, "-t") == 0) {
122				type = next_arg(argi);
123
124			} else {
125				fprintf(stderr, "Error: Invalid option: \"%s\"\n", arg);
126				print_usage_and_exit(true);
127			}
128		} else {
129			// file
130			files[fileCount++] = arg;
131		}
132	}
133
134	// check parameters
135	if (!preferredApp && !signature && !type) {
136		fprintf(stderr, "Error: At least one option of \"-preferredAppSig\", "
137			"\"-s\", and \"-t\" must be given.\n");
138		print_usage_and_exit(true);
139	}
140
141	if (fileCount == 0) {
142		fprintf(stderr, "Error: No file specified.\n");
143		print_usage_and_exit(true);
144	}
145
146	// check for valid MIME types
147	check_mime_type(preferredApp);
148	check_mime_type(type);
149	check_mime_type(signature);
150
151	// iterate through the files
152	for (int i = 0; i < fileCount; i++) {
153		const char *fileName = files[i];
154
155		// check, whether the file exists
156		BEntry entry;
157		status_t error = entry.SetTo(fileName, false);
158		if (error != B_OK) {
159			fprintf(stderr, "Error: Can't access file \"%s\": %s\n",
160				fileName, strerror(error));
161
162			exit(1);
163		}
164
165		if (!entry.Exists()) {
166			fprintf(stderr, "Error: \"%s\": No such file or directory.\n",
167				fileName);
168
169			exit(1);
170		}
171
172		// ... and has the right type
173		if (signature && !entry.IsFile()) {
174			fprintf(stderr, "Error: \"%s\" is not a file. Signatures can only "
175				"be set for executable files.\n", fileName);
176
177			exit(1);
178		}
179
180		// open the file
181		BFile file;
182		BNode _node;
183		BNode &node = (signature ? file : _node);
184		error = (signature ? file.SetTo(fileName, B_READ_WRITE)
185			: node.SetTo(fileName));
186		if (error != B_OK) {
187			fprintf(stderr, "Error: Failed to open file \"%s\": %s\n",
188				fileName, strerror(error));
189
190			exit(1);
191		}
192
193		// prepare an node/app info object
194		BAppFileInfo appInfo;
195		BNodeInfo _nodeInfo;
196		BNodeInfo &nodeInfo = (signature ? appInfo : _nodeInfo);
197		error = (signature ? appInfo.SetTo(&file) : nodeInfo.SetTo(&node));
198		if (error != B_OK) {
199			fprintf(stderr, "Error: Failed to open file \"%s\": %s\n",
200				fileName, strerror(error));
201
202			exit(1);
203		}
204
205		// set preferred app
206		if (preferredApp) {
207			error = nodeInfo.SetPreferredApp(preferredApp);
208			if (error != B_OK) {
209				fprintf(stderr, "Error: Failed to set the preferred "
210					"application of file \"%s\": %s\n", fileName,
211					strerror(error));
212
213				exit(1);
214			}
215		}
216
217		// set type
218		if (type) {
219			error = nodeInfo.SetType(type);
220			if (error != B_OK) {
221				fprintf(stderr, "Error: Failed to set the MIME type of file "
222					"\"%s\": %s\n", fileName, strerror(error));
223
224				exit(1);
225			}
226		}
227
228		// set signature
229		if (signature) {
230			error = appInfo.SetSignature(signature);
231			if (error != B_OK) {
232				fprintf(stderr, "Error: Failed to set the signature of file "
233					"\"%s\": %s\n", fileName, strerror(error));
234
235				exit(1);
236			}
237		}
238	}
239
240	delete[] files;
241
242	return 0;
243}
244