xref: /haiku/src/bin/mimeset.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
1 /*
2  * Copyright 2005-2006, Axel Dörfler, axeld@pinc-software.de.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <getopt.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <Application.h>
13 #include <Mime.h>
14 #include <Path.h>
15 
16 #include <mime/AppMetaMimeCreator.h>
17 #include <mime/Database.h>
18 #include <mime/DatabaseLocation.h>
19 #include <mime/MimeInfoUpdater.h>
20 #include <mime/MimeSnifferAddonManager.h>
21 #include <mime/TextSnifferAddon.h>
22 
23 
24 using namespace BPrivate::Storage::Mime;
25 
26 
27 #ifdef HAIKU_HOST_PLATFORM_SUNOS
28 static const char* sProgramName = "mimeset";
29 #else
30 extern const char* __progname;
31 static const char* sProgramName = __progname;
32 #endif
33 
34 // options
35 bool gFiles = true;
36 bool gApps = false;
37 int gForce = B_UPDATE_MIME_INFO_NO_FORCE;
38 
39 static Database* sDatabase = NULL;
40 
41 
42 static void
43 usage(int status)
44 {
45 	printf("Usage: %s <options> <path> ...\n"
46 		"Recursively updates the MIME related attributes (e.g. file type) for\n"
47 		"the given files. Alternatively or additionally encountered\n"
48 		"applications are entered into the MIME database. When \"@\" is\n"
49 		"specified as <path>, file paths are read from stdin.\n"
50 		"\n"
51 		"Options:\n"
52 		"  -A, --all\n"
53 		"    Update the files' MIME information and enter applications into\n"
54 		"    the MIME database.\n"
55 		"  -a, --apps\n"
56 		"    Only enter applications into the MIME database.\n"
57 		"  -f\n"
58 		"    Force updating, even if previously updated, but do not overwrite\n"
59 		"    the type of a file.\n"
60 		"  -F\n"
61 		"    Force updating, even if previously updated. Also overwrite the\n"
62 		"    type of a file.\n"
63 		"  -h, --help\n"
64 		"    Display this help information.\n"
65 		"  -m, --mimedb <directory>\n"
66 		"    Instead of the system MIME DB use the given directory\n"
67 		"    <directory>. The option can occur multiple times to specify a\n"
68 		"    list of directories. MIME DB changes are written to the first\n"
69 		"    specified directory.\n"
70 		"\n"
71 		"Obsolete options:\n"
72 		"  -all  (synonymous with --all)\n"
73 		"  -apps (synonymous with --apps)\n"
74 		"\n",
75 		sProgramName);
76 
77 	exit(status);
78 }
79 
80 
81 static status_t
82 process_file_with_custom_mime_db(const BEntry& entry)
83 {
84 	AppMetaMimeCreator appMetaMimeCreator(sDatabase, NULL, gForce);
85 	MimeInfoUpdater mimeInfoUpdater(sDatabase, NULL, gForce);
86 
87 	entry_ref ref;
88 	status_t error = entry.GetRef(&ref);
89 
90 	if (gFiles && error == B_OK)
91 		error = mimeInfoUpdater.DoRecursively(ref);
92 	if (gApps && error == B_OK) {
93 		error = appMetaMimeCreator.DoRecursively(ref);
94 		if (error == B_BAD_TYPE) {
95 			// Ignore B_BAD_TYPE silently. The most likely cause is that the
96 			// file doesn't have a "BEOS:APP_SIG" attribute.
97 			error = B_OK;
98 		}
99 	}
100 
101 	if (error != B_OK) {
102 		BPath path;
103 		fprintf(stderr, "%s: \"%s\": %s\n",
104 			sProgramName,
105 			entry.GetPath(&path) == B_OK ? path.Path() : entry.Name(),
106 			strerror(error));
107 		return error;
108 	}
109 
110 	return B_OK;
111 }
112 
113 
114 static status_t
115 process_file(const char* path)
116 {
117 	status_t status = B_OK;
118 
119 	BEntry entry(path);
120 	if (!entry.Exists())
121 		status = B_ENTRY_NOT_FOUND;
122 
123 	if (sDatabase != NULL)
124 		return process_file_with_custom_mime_db(entry);
125 
126 	if (gFiles && status == B_OK)
127 		status = update_mime_info(path, true, true, gForce);
128 	if (gApps && status == B_OK)
129 		status = create_app_meta_mime(path, true, true, gForce);
130 
131 	if (status != B_OK) {
132 		fprintf(stderr, "%s: \"%s\": %s\n",
133 			sProgramName, path, strerror(status));
134 	}
135 	return status;
136 }
137 
138 
139 int
140 main(int argc, const char** argv)
141 {
142 	// parse arguments
143 
144 	// replace old-style options first
145 	for (int i = 1; i < argc; i++) {
146 		const char* arg = argv[i];
147 		if (*arg != '-')
148 			break;
149 		if (strcmp(arg, "-all") == 0)
150 			argv[i] = "--all";
151 		else if (strcmp(arg, "-apps") == 0)
152 			argv[i] = "--apps";
153 	}
154 
155 	BStringList databaseDirectories;
156 
157 	for (;;) {
158 		static struct option sLongOptions[] = {
159 			{ "all", no_argument, 0, 'A' },
160 			{ "apps", no_argument, 0, 'a' },
161 			{ "help", no_argument, 0, 'h' },
162 			{ "mimedb", required_argument, 0, 'm' },
163 			{ 0, 0, 0, 0 }
164 		};
165 
166 		opterr = 0; // don't print errors
167 		int c = getopt_long(argc, (char**)argv, "aAfFhm:", sLongOptions,
168 			NULL);
169 		if (c == -1)
170 			break;
171 
172 		switch (c) {
173 			case 'a':
174 				gApps = true;
175 				gFiles = false;
176 				break;
177 			case 'A':
178 				gApps = true;
179 				gFiles = true;
180 				break;
181 			case 'f':
182 				gForce = B_UPDATE_MIME_INFO_FORCE_KEEP_TYPE;
183 				break;
184 			case 'F':
185 				gForce = B_UPDATE_MIME_INFO_FORCE_UPDATE_ALL;
186 				break;
187 			case 'h':
188 				usage(0);
189 				break;
190 			case 'm':
191 				databaseDirectories.Add(optarg);
192 				break;
193 			default:
194 				usage(1);
195 				break;
196 		}
197 	}
198 
199 	if (argc - optind < 1)
200 		usage(1);
201 
202 	// set up custom MIME DB, if specified
203 	DatabaseLocation databaseLocation;
204 	if (!databaseDirectories.IsEmpty()) {
205 		int32 count = databaseDirectories.CountStrings();
206 		for (int32 i = 0; i < count; i++)
207 			databaseLocation.AddDirectory(databaseDirectories.StringAt(i));
208 
209 		status_t error = MimeSnifferAddonManager::CreateDefault();
210 		if (error != B_OK) {
211 			fprintf(stderr, "%s: Failed to create MIME sniffer add-on "
212 				"manager: %s\n", sProgramName, strerror(error));
213 			exit(1);
214 		}
215 		MimeSnifferAddonManager* manager = MimeSnifferAddonManager::Default();
216 		manager->AddMimeSnifferAddon(
217 			new(std::nothrow) TextSnifferAddon(&databaseLocation));
218 
219 		sDatabase = new(std::nothrow) Database(&databaseLocation, manager,
220 			NULL);
221 		if (sDatabase == NULL) {
222 			fprintf(stderr, "%s: Out of memory!\n", sProgramName);
223 			exit(1);
224 		}
225 
226 		error = sDatabase->InitCheck();
227 		if (error != B_OK) {
228 			fprintf(stderr, "%s: Failed to init MIME DB: %s\n", sProgramName,
229 				strerror(error));
230 			exit(1);
231 		}
232 	}
233 
234 	// process files
235 
236 	BApplication app("application/x-vnd.haiku.mimeset");
237 
238 	for (; optind < argc; optind++) {
239 		const char* arg = argv[optind];
240 
241 		if (strcmp(arg, "@") == 0) {
242 			// read file names from stdin
243 			char name[B_PATH_NAME_LENGTH];
244 			while (fgets(name, sizeof(name), stdin) != NULL) {
245 				name[strlen(name) - 1] = '\0';
246 					// remove trailing '\n'
247 				if (process_file(name) != B_OK)
248 					exit(1);
249 			}
250 		} else {
251 			if (process_file(arg) != B_OK)
252 				exit(1);
253 		}
254 	}
255 
256 	return 0;
257 }
258