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