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