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
usage(int status)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
process_file_with_custom_mime_db(const BEntry & entry)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
process_file(const char * path)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
main(int argc,const char ** argv)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 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