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