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
24using namespace BPrivate::Storage::Mime;
25
26
27extern const char* __progname;
28static const char* sProgramName = __progname;
29
30// options
31bool gFiles = true;
32bool gApps = false;
33int gForce = B_UPDATE_MIME_INFO_NO_FORCE;
34
35static Database* sDatabase = NULL;
36
37
38static void
39usage(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
77static status_t
78process_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
110static status_t
111process_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
135int
136main(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				if (name[0] != '\0') {
242					name[strlen(name) - 1] = '\0';
243						// remove trailing '\n'
244				}
245				if (process_file(name) != B_OK)
246					exit(1);
247			}
248		} else {
249			if (process_file(arg) != B_OK)
250				exit(1);
251		}
252	}
253
254	return 0;
255}
256