Changeset 214 for trunk/launch/launch/main.c
- Timestamp:
- 03/12/06 12:44:00 (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/launch/launch/main.c
r166 r214 3 3 Nicholas Riley <launchsw@sabi.net> 4 4 5 Copyright (c) 2001-0 5, Nicholas Riley5 Copyright (c) 2001-06, Nicholas Riley 6 6 All rights reserved. 7 7 … … 16 16 */ 17 17 18 /* To do/think about:19 20 - Launching as root: use authentication framework - doesn't work.21 22 - launch URL with specified URL handler (done, except for IC)23 24 - launch apps by IC protocol handler (esp. editor)25 26 Thanks to:27 28 - Nat Irons, for encouragement and suggestions29 30 - Brian Hill, for the great Security.framework tutorial and sample code31 32 */33 34 /* #define DEBUG 1 */35 18 #define BROKEN_AUTHORIZATION 1 36 #define BROKEN_LSOPENFROMURLSPEC 137 19 #define kComponentSignatureString "launch" 38 20 … … 51 33 const char *APP_NAME; 52 34 53 #define VERSION "1. 0.1"35 #define VERSION "1.1d1" 54 36 55 37 #define STRBUF_LEN 1024 … … 71 53 #define DEFAULT_LAUNCH_FLAGS (kLSLaunchNoParams | kLSLaunchStartClassic | kLSLaunchAsync) 72 54 73 LSLaunchURLSpec LSPEC = {NULL, NULL, NULL, DEFAULT_LAUNCH_FLAGS, NULL}; 55 LSApplicationParameters LPARAMS = {0, DEFAULT_LAUNCH_FLAGS, NULL, NULL, NULL, NULL, NULL}; 56 CFArrayRef ITEMS = NULL; 57 FSRef APPLICATION; 74 58 75 59 char *TEMPFILE = NULL; … … 86 70 { kLSLaunchInProgressErr, "application is being opened; please try again after the application is open" }, 87 71 { kLSNotRegisteredErr, "application not registered in Launch Services database" }, 88 { -10827, "application package contains no executable, or an unusable executable" }, /* kLSNoExecutableErr, not defined in 10.2 */89 { -10828, "Classic environment required but not available" }, /* kLSNoClassicEnvironmentErr, not defined in 10.2 */90 { -10829, "unable to launch multiple instances of application" }, /* kLSMultipleSessionsNotSupportedErr, not defined in 10.2 */72 { kLSNoExecutableErr, "application package contains no executable, or an unusable executable" }, 73 { kLSNoClassicEnvironmentErr, "Classic environment required but not available" }, 74 { kLSMultipleSessionsNotSupportedErr, "unable to launch multiple instances of application" }, 91 75 #ifndef BROKEN_AUTHORIZATION 92 76 // Security framework errors … … 108 92 109 93 void usage() { 110 fprintf(stderr, "usage: %s [-npswbmhCXU] [-c creator] [-i bundleID] [-u URL] [-a name] [ item ...] [-]\n"111 " or: %s [-npflswbmhCXU] item ...\n", APP_NAME, APP_NAME);94 fprintf(stderr, "usage: %s [-npswbmhCXU] [-c creator] [-i bundleID] [-u URL] [-a name] [-o argument] [item ...] [-]\n" 95 " or: %s [-npflswbmhCXU] [-o argument] item ...\n", APP_NAME, APP_NAME); 112 96 fprintf(stderr, 113 97 " -n print matching paths/URLs instead of opening them\n" … … 128 112 " -i bundle ID match application by bundle identifier (com.apple.scripteditor)\n" 129 113 " -u URL open application at file:// URL (NOT RECOMMENDED for scripts)\n" 130 " -a name match application by name (NOT RECOMMENDED, very fragile)\n" 114 " -a name|path match application by name/path (NOT RECOMMENDED, very fragile)\n" 115 " -o argument pass argument to application (may be specified more than once)\n" 131 116 "'document' may be a file, folder, or disk - whatever the application can open.\n" 132 117 "'item' may be a file, folder, disk, or URL.\n\n"); 133 fprintf(stderr, "launch "VERSION" (c) 2001-0 5Nicholas Riley <http://web.sabi.net/nriley/software/>.\n"118 fprintf(stderr, "launch "VERSION" (c) 2001-06 Nicholas Riley <http://web.sabi.net/nriley/software/>.\n" 134 119 "Please send bugs, suggestions, etc. to <launchsw@sabi.net>.\n"); 135 120 … … 288 273 errexit("can't create temporary file '%s'", tempPath); 289 274 // mark file as stationery 290 err = FSPathMakeRef( tempPath, &fsr, NULL);275 err = FSPathMakeRef((UInt8 *)tempPath, &fsr, NULL); 291 276 if (err != noErr) osstatusexit(err, "can't find '%s'", tempPath); 292 277 err = FSGetCatalogInfo(&fsr, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL); … … 341 326 extern int optind; 342 327 int ch; 328 OSStatus err; 343 329 Boolean appSpecified = false; 344 330 345 331 if (argc == 1) usage(); 346 332 347 while ( (ch = getopt(argc, argv, "npflswbmhCXUc:i:u:a: ")) != -1) {333 while ( (ch = getopt(argc, argv, "npflswbmhCXUc:i:u:a:o:")) != -1) { 348 334 switch (ch) { 349 335 case 'n': … … 354 340 if (OPTS.action != ACTION_DEFAULT) errexit("choose only one of -n, -p, -f, -l options"); 355 341 OPTS.action = ACTION_OPEN; 356 L SPEC.launchFlags |= kLSLaunchAndPrint;342 LPARAMS.flags |= kLSLaunchAndPrint; 357 343 break; 358 344 case 'f': … … 382 368 } 383 369 #endif 384 case 'w': L SPEC.launchFlags ^= kLSLaunchAsync; break; // synchronous385 case 'b': L SPEC.launchFlags |= kLSLaunchDontSwitch; break; // open in background386 case 'm': L SPEC.launchFlags |= kLSLaunchNewInstance; break;// open multiple387 case 'h': L SPEC.launchFlags |= kLSLaunchAndHide; break; // hide once launched388 case 'C': L SPEC.launchFlags |= kLSLaunchInClassic; break; // force Classic389 case 'X': L SPEC.launchFlags ^= kLSLaunchStartClassic; break;// don't start Classic for app370 case 'w': LPARAMS.flags ^= kLSLaunchAsync; break; // synchronous 371 case 'b': LPARAMS.flags |= kLSLaunchDontSwitch; break; // open in background 372 case 'm': LPARAMS.flags |= kLSLaunchNewInstance; break;// open multiple 373 case 'h': LPARAMS.flags |= kLSLaunchAndHide; break; // hide once launched 374 case 'C': LPARAMS.flags |= kLSLaunchInClassic; break; // force Classic 375 case 'X': LPARAMS.flags ^= kLSLaunchStartClassic; break;// don't start Classic for app 390 376 case 'U': OPTS.forceURLs = true; break; 391 377 case 'c': … … 399 385 break; 400 386 case 'a': 401 OPTS.name = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8); 402 appSpecified = true; 387 err = FSPathMakeRef((UInt8 *)optarg, &APPLICATION, NULL); 388 if (err == noErr) { 389 LPARAMS.application = &APPLICATION; 390 } else { 391 OPTS.name = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8); 392 } 393 appSpecified = true; 403 394 break; 404 395 case 'u': 405 396 { CFStringRef str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8); 406 LSPEC.appURL = CFURLCreateWithString(NULL, str, NULL); 407 if (str != NULL) CFRelease(str); 397 CFURLRef appURL = CFURLCreateWithString(NULL, str, NULL); 398 if (appURL == NULL) 399 errexit("invalid URL (argument of -u)"); 400 err = CFURLGetFSRef(appURL, &APPLICATION); 401 if (err != noErr) 402 osstatusexit(err, "can't find application (argument of -u)"); 408 403 } 409 if (LSPEC.appURL == NULL) { 410 errexit("invalid URL (argument of -u)"); 411 } else { 412 CFURLRef absURL = CFURLCopyAbsoluteURL(LSPEC.appURL); 413 CFRelease(LSPEC.appURL); 414 LSPEC.appURL = NULL; 415 if (absURL != NULL) { 416 CFStringRef scheme = CFURLCopyScheme(absURL); 417 LSPEC.appURL = absURL; 418 if (scheme == NULL || !CFEqual(scheme, CFSTR("file"))) 419 errexit("invalid file:// URL (argument of -u)"); 420 CFRelease(scheme); 421 } 422 } 404 LPARAMS.application = &APPLICATION; 423 405 appSpecified = true; 424 406 break; 407 case 'o': 408 if (LPARAMS.argv == NULL) 409 LPARAMS.argv = CFArrayCreateMutable(NULL, 0, NULL); 410 CFArrayAppendValue((CFMutableArrayRef)LPARAMS.argv, 411 CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8)); 412 break; 425 413 default: usage(); 426 414 } … … 431 419 432 420 if ( (OPTS.action == ACTION_FIND || OPTS.action == ACTION_LAUNCH_URLS || 433 OPTS.action == ACTION_INFO_ITEMS) && L SPEC.launchFlags != DEFAULT_LAUNCH_FLAGS)421 OPTS.action == ACTION_INFO_ITEMS) && LPARAMS.flags != DEFAULT_LAUNCH_FLAGS) 434 422 errexit("options -s, -b, -m, -h, -C, -X apply to application launch (not -n, -f or -l)"); 435 423 436 424 if (OPTS.creator == kLSUnknownCreator && OPTS.bundleID == NULL && OPTS.name == NULL) { 437 if (argc == 0 && L SPEC.appURL== NULL)425 if (argc == 0 && LPARAMS.application == NULL) 438 426 errexit("must specify an application by -u, or one or more of -c, -i, -a"); 439 427 if (!appSpecified) { … … 444 432 } 445 433 } else { 446 if (L SPEC.appURL!= NULL)434 if (LPARAMS.application != NULL) 447 435 errexit("application URL (argument of -u) incompatible with matching by -c, -i, -a"); 448 436 } … … 454 442 errexit("can't get information (-f) on item(s) using an application (-u, -c, -i, -a)"); 455 443 456 if (argc == 0 && OPTS.action == ACTION_OPEN && L SPEC.launchFlags & kLSLaunchAndPrint)444 if (argc == 0 && OPTS.action == ACTION_OPEN && LPARAMS.flags & kLSLaunchAndPrint) 457 445 errexit("print option (-p) must be accompanied by document(s) to print"); 458 446 … … 468 456 469 457 // handle document/item/URL arguments 470 LSPEC.itemURLs= CFArrayCreateMutable(NULL, argc, NULL);458 ITEMS = CFArrayCreateMutable(NULL, argc, NULL); 471 459 for (i = 0 ; i < argc ; i++) { 472 460 argStr = NULL; 473 461 if (strcmp(argv[i], "-") == 0) { 474 462 TEMPFILE = stdinAsTempFile(); 475 itemURL = CFURLCreateFromFileSystemRepresentation(NULL, TEMPFILE, strlen(TEMPFILE), false);476 L SPEC.launchFlags ^= kLSLaunchAsync;463 itemURL = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8 *)TEMPFILE, strlen(TEMPFILE), false); 464 LPARAMS.flags ^= kLSLaunchAsync; 477 465 } else { 478 466 struct stat stat_buf; … … 494 482 if (itemURL == NULL) { 495 483 // check for file paths 496 itemURL = CFURLCreateFromFileSystemRepresentation(NULL, argv[i], strlen(argv[i]), false);484 itemURL = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8 *)argv[i], strlen(argv[i]), false); 497 485 err = LSCopyItemInfoForURL(itemURL, kLSRequestExtensionFlagsOnly, &docInfo); 498 486 if (err != noErr) osstatusexit(err, "unable to locate '%s'", argv[i]); 499 487 } 500 488 } 501 CFArrayAppendValue((CFMutableArrayRef) LSPEC.itemURLs, itemURL);489 CFArrayAppendValue((CFMutableArrayRef)ITEMS, itemURL); 502 490 // don't CFRelease the itemURL because CFArray doesn't retain it by default 503 491 if (argStr != NULL) CFRelease(argStr); … … 517 505 CFRelease(urlString); 518 506 } else { 519 if (CFURLGetFileSystemRepresentation(url, false, strBuffer, STRBUF_LEN)) {507 if (CFURLGetFileSystemRepresentation(url, false, (UInt8 *)strBuffer, STRBUF_LEN)) { 520 508 if (strBuffer[0] == '.' && strBuffer[1] == '/') { 521 509 // remove the leading "./" … … 598 586 } 599 587 600 void printMoreInfoFromURL(CFURLRef url) { 601 FSRef fsr; 588 void printMoreInfoForRef(FSRef fsr) { 602 589 OSStatus err; 603 590 FSCatalogInfo fscInfo; 604 591 605 if (!CFURLGetFSRef(url, &fsr)) return;606 592 err = FSGetCatalogInfo(&fsr, kFSCatInfoNodeFlags | kFSCatInfoAllDates | kFSCatInfoDataSizes | kFSCatInfoRsrcSizes | kFSCatInfoValence, &fscInfo, NULL, NULL, NULL); 607 593 if (err != noErr) osstatusexit(err, "unable to get catalog information for file"); … … 643 629 644 630 const char *utf8StringFromOSType(OSType osType) { 645 CFStringRef typeStr = CFStringCreateWithBytes(NULL, ( const char*)&osType, 4, CFStringGetSystemEncoding(), false);631 CFStringRef typeStr = CFStringCreateWithBytes(NULL, (UInt8 *)&osType, 4, CFStringGetSystemEncoding(), false); 646 632 if (typeStr == NULL) { 647 633 // punt to displaying verbatim … … 670 656 UInt32 intVersion = 0; 671 657 OSStatus err = LSCopyItemInfoForURL(url, kLSRequestAllInfo, &info); 658 Boolean haveFSRef; 659 FSRef fsr; 672 660 if (err != noErr) osstatusexit(err, "unable to get information about '%s'", strBuffer); 661 haveFSRef = CFURLGetFSRef(url, &fsr); 673 662 674 663 printf("%s: ", strBuffer); … … 705 694 CFStringRef bundleID = NULL; 706 695 if (bundle == NULL && (info.flags & kLSItemInfoIsApplication)) { 707 FSRef fsr; 708 if (info.flags & kLSItemInfoIsPackage || !CFURLGetFSRef(url, &fsr)) { 696 if (info.flags & kLSItemInfoIsPackage || !haveFSRef) { 709 697 printf("\t[can't access CFBundle for application]\n"); 710 698 } else { // OS X bug causes this to fail when it shouldn't, so fake it … … 718 706 if (err != noErr && err != resNotFound) osstatusexit(err, "unable to read 'plst' 0 resource"); 719 707 } else { 720 CFDataRef plstData = CFDataCreate(NULL, *h, GetHandleSize(h));708 CFDataRef plstData = CFDataCreate(NULL, (UInt8 *)*h, GetHandleSize(h)); 721 709 CFStringRef error; 722 710 CFPropertyListRef infoPlist = CFPropertyListCreateFromXMLData(NULL, plstData, kCFPropertyListImmutable, &error); … … 771 759 CFRelease(bundleID); 772 760 } 773 } else {761 } else if (haveFSRef) { 774 762 // try to get a version if we can, but don't complain if we can't 775 FSRef fsr; 776 if (CFURLGetFSRef(url, &fsr)) { 777 SInt16 resFork = FSOpenResFile(&fsr, fsRdPerm); 778 if (ResError() == noErr) { 779 VersRecHndl vers = (VersRecHndl)Get1Resource('vers', 1); 780 if (ResError() == noErr && vers != NULL) { 781 version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead? 782 intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole; 783 } 784 } 785 CloseResFile(resFork); 786 } 763 SInt16 resFork = FSOpenResFile(&fsr, fsRdPerm); 764 if (ResError() == noErr) { 765 VersRecHndl vers = (VersRecHndl)Get1Resource('vers', 1); 766 if (ResError() == noErr && vers != NULL) { 767 version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead? 768 intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole; 769 } 770 } 771 CloseResFile(resFork); 787 772 } 788 773 … … 799 784 printf("\tkind: %s\n", utf8StringFromCFStringRef(kind)); 800 785 CFRelease(kind); 801 printMoreInfoFromURL(url); 786 787 if (haveFSRef) { 788 // content type identifier (UTI) 789 err = LSCopyItemAttribute(&fsr, kLSRolesAll, kLSItemContentType, (CFTypeRef *)&kind); 790 if (err == noErr) { 791 printf("\tcontent type ID: %s\n", utf8StringFromCFStringRef(kind)); 792 CFRelease(kind); 793 } 794 printMoreInfoForRef(fsr); 795 } 802 796 } 803 797 } … … 822 816 } 823 817 824 OSStatus openFromURLSpec() { 825 #ifndef BROKEN_LSOPENFROMURLSPEC 826 return LSOpenFromURLSpec(&LSPEC, NULL); 827 #else 828 LSLaunchFSRefSpec spec = {NULL, 0, NULL, LSPEC.passThruParams, 829 LSPEC.launchFlags, LSPEC.asyncRefCon}; 830 CFIndex urlIndex, urlCount = LSPEC.itemURLs ? CFArrayGetCount(LSPEC.itemURLs) : 0; 831 FSRef *itemRefs = malloc(urlCount * sizeof(FSRef)); 832 CFURLRef url; 833 CFStringRef scheme, fileScheme = CFSTR("file"); 834 int itemIndex = 0; 835 OSStatus err; 836 837 for (urlIndex = 0 ; urlIndex < urlCount ; urlIndex++) { 838 url = CFArrayGetValueAtIndex(LSPEC.itemURLs, urlIndex); 839 scheme = CFURLCopyScheme(url); 840 if (CFEqual(scheme, fileScheme)) { 841 if (CFURLGetFSRef(url, &itemRefs[itemIndex])) { 842 itemIndex++; 843 CFArrayRemoveValueAtIndex((CFMutableArrayRef)LSPEC.itemURLs, urlIndex); 844 urlIndex--; 845 urlCount--; 846 } else { 847 fprintf(stderr, "%s: unable to locate: ", APP_NAME); 848 printPathFromURL(url, stderr); 849 } 850 } 851 CFRelease(scheme); 852 } 853 854 if (urlCount > 0 || itemIndex == 0) { /* URLs, or no items */ 855 err = LSOpenFromURLSpec(&LSPEC, NULL); 856 if (err != noErr) 857 return err; 858 } 859 if (itemIndex > 0) { 860 FSRef appRef; 861 spec.numDocs = itemIndex; 862 spec.itemRefs = itemRefs; 863 if (LSPEC.appURL != NULL) { 864 if (!CFURLGetFSRef(LSPEC.appURL, &appRef)) { 865 errexit("can't find application"); 866 } 867 spec.appRef = &appRef; 868 } 869 return LSOpenFromRefSpec(&spec, NULL); 870 } 871 return noErr; 872 #endif 818 OSStatus openItems(void) { 819 if (ITEMS == NULL) 820 ITEMS = CFArrayCreate(NULL, NULL, 0, NULL); 821 CFShow(LPARAMS.argv); 822 return LSOpenURLsWithRole(ITEMS, kLSRolesAll, NULL, &LPARAMS, NULL, 0); 873 823 } 874 824 … … 880 830 881 831 if (OPTS.action == ACTION_FIND || OPTS.action == ACTION_OPEN) { 882 if (LSPEC.appURL != NULL) goto findOK; // already have an application URL 883 err = LSFindApplicationForInfo(OPTS.creator, OPTS.bundleID, OPTS.name, NULL, &LSPEC.appURL); 832 if (LPARAMS.application != NULL) goto findOK; // already have an application FSRef 833 err = LSFindApplicationForInfo(OPTS.creator, OPTS.bundleID, OPTS.name, &APPLICATION, NULL); 834 LPARAMS.application = &APPLICATION; 884 835 885 836 if (err != noErr) { … … 887 838 OPTS.name = CFStringCreateMutableCopy(NULL, CFStringGetLength(OPTS.name) + 4, OPTS.name); 888 839 CFStringAppend((CFMutableStringRef)OPTS.name, CFSTR(".app")); 889 err = LSFindApplicationForInfo(OPTS.creator, OPTS.bundleID, OPTS.name, NULL, &LSPEC.appURL);840 err = LSFindApplicationForInfo(OPTS.creator, OPTS.bundleID, OPTS.name, &APPLICATION, NULL); 890 841 if (err == noErr) goto findOK; 891 842 } 892 osstatusexit(err, "can't locate application", argv[1]); 843 osstatusexit(err, "can't locate application"); 844 } 893 845 findOK: ; 894 }895 846 } 896 847 897 848 switch (OPTS.action) { 898 849 case ACTION_FIND: 899 printPathFromURL( LSPEC.appURL, stdout);850 printPathFromURL(CFURLCreateFromFSRef(NULL, LPARAMS.application), stdout); 900 851 break; 901 852 case ACTION_OPEN: 902 err = open FromURLSpec();903 if (err != noErr) osstatusexit(err, "can't open application" , argv[1]);853 err = openItems(); 854 if (err != noErr) osstatusexit(err, "can't open application"); 904 855 break; 905 856 case ACTION_FIND_ITEMS: 906 CFArrayApplyFunction( LSPEC.itemURLs, CFRangeMake(0, CFArrayGetCount(LSPEC.itemURLs)),857 CFArrayApplyFunction(ITEMS, CFRangeMake(0, CFArrayGetCount(ITEMS)), 907 858 (CFArrayApplierFunction) printPathFromURL, stdout); 908 859 break; 909 860 case ACTION_OPEN_ITEMS: 910 err = open FromURLSpec();911 if (err != noErr) osstatusexit(err, "can't open items" , argv[1]);861 err = openItems(); 862 if (err != noErr) osstatusexit(err, "can't open items"); 912 863 break; 913 864 case ACTION_INFO_ITEMS: 914 CFArrayApplyFunction( LSPEC.itemURLs, CFRangeMake(0, CFArrayGetCount(LSPEC.itemURLs)),865 CFArrayApplyFunction(ITEMS, CFRangeMake(0, CFArrayGetCount(ITEMS)), 915 866 (CFArrayApplierFunction) printInfoFromURL, NULL); 916 867 break; … … 920 871 err = ICStart(&icInst, '\?\?\?\?'); // in case GCC trigraph handling is enabled 921 872 if (err != noErr) osstatusexit(err, "can't initialize Internet Config", argv[1]); 922 CFArrayApplyFunction( LSPEC.itemURLs, CFRangeMake(0, CFArrayGetCount(LSPEC.itemURLs)),873 CFArrayApplyFunction(ITEMS, CFRangeMake(0, CFArrayGetCount(ITEMS)), 923 874 (CFArrayApplierFunction) launchURL, icInst); 924 875 ICStop(icInst);
Note:
See TracChangeset
for help on using the changeset viewer.