Changeset 166 for trunk/launch/launch/main.c
- Timestamp:
- 04/04/05 00:22:09 (19 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/launch/launch/main.c
r148 r166 3 3 Nicholas Riley <launchsw@sabi.net> 4 4 5 Copyright (c) 2001-0 3, Nicholas Riley5 Copyright (c) 2001-05, Nicholas Riley 6 6 All rights reserved. 7 7 … … 51 51 const char *APP_NAME; 52 52 53 #define VERSION "1.0 "53 #define VERSION "1.0.1" 54 54 55 55 #define STRBUF_LEN 1024 … … 59 59 OSType creator; 60 60 CFStringRef bundleID, name; 61 Boolean forceURLs; 61 62 enum { ACTION_FIND, ACTION_FIND_ITEMS, 62 63 ACTION_OPEN, ACTION_OPEN_ITEMS, … … 64 65 } OPTS = 65 66 { 66 kLSUnknownCreator, NULL, NULL, 67 kLSUnknownCreator, NULL, NULL, false, 67 68 ACTION_DEFAULT 68 69 }; … … 87 88 { -10827, "application package contains no executable, or an unusable executable" }, /* kLSNoExecutableErr, not defined in 10.2 */ 88 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 */ 89 91 #ifndef BROKEN_AUTHORIZATION 90 92 // Security framework errors … … 97 99 { icInternalErr, "internal Internet Config error" }, 98 100 // Misc. errors 101 { nsvErr, "the volume cannot be found (buggy filesystem?)" }, 99 102 { procNotFound, "unable to connect to system service.\nAre you logged in?" }, 100 103 { kCGErrorIllegalArgument, "window server error.\nAre you logged in?" }, … … 105 108 106 109 void usage() { 107 fprintf(stderr, "usage: %s [-npswbmhCX ] [-c creator] [-i bundleID] [-u URL] [-a name] [item ...] [-]\n"108 " or: %s [-npflswbmhCX ] item ...\n", APP_NAME, APP_NAME);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); 109 112 fprintf(stderr, 110 113 " -n print matching paths/URLs instead of opening them\n" … … 121 124 " -C force CFM/PEF Carbon application to launch in Classic\n" 122 125 " -X don't start Classic for this app if Classic isn't running\n" 126 " -U interpret items as URLs, even if same-named files exist\n" 123 127 " -c creator match application by four-character creator code ('ToyS')\n" 124 128 " -i bundle ID match application by bundle identifier (com.apple.scripteditor)\n" … … 127 131 "'document' may be a file, folder, or disk - whatever the application can open.\n" 128 132 "'item' may be a file, folder, disk, or URL.\n\n"); 129 fprintf(stderr, "launch "VERSION" (c) 2001-0 3Nicholas Riley <http://web.sabi.net/nriley/software/>.\n"133 fprintf(stderr, "launch "VERSION" (c) 2001-05 Nicholas Riley <http://web.sabi.net/nriley/software/>.\n" 130 134 "Please send bugs, suggestions, etc. to <launchsw@sabi.net>.\n"); 131 135 … … 341 345 if (argc == 1) usage(); 342 346 343 while ( (ch = getopt(argc, argv, "npflswbmhCX c:i:u:a:")) != -1) {347 while ( (ch = getopt(argc, argv, "npflswbmhCXUc:i:u:a:")) != -1) { 344 348 switch (ch) { 345 349 case 'n': … … 384 388 case 'C': LSPEC.launchFlags |= kLSLaunchInClassic; break; // force Classic 385 389 case 'X': LSPEC.launchFlags ^= kLSLaunchStartClassic; break;// don't start Classic for app 390 case 'U': OPTS.forceURLs = true; break; 386 391 case 'c': 387 392 if (strlen(optarg) != 4) errexit("creator (argument of -c) must be four characters long"); … … 390 395 break; 391 396 case 'i': 392 OPTS.bundleID = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());397 OPTS.bundleID = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8); 393 398 appSpecified = true; 394 399 break; 395 400 case 'a': 396 OPTS.name = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());401 OPTS.name = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8); 397 402 appSpecified = true; 398 403 break; 399 404 case 'u': 400 { CFStringRef str = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());405 { CFStringRef str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8); 401 406 LSPEC.appURL = CFURLCreateWithString(NULL, str, NULL); 402 407 if (str != NULL) CFRelease(str); … … 471 476 LSPEC.launchFlags ^= kLSLaunchAsync; 472 477 } else { 473 argStr = CFStringCreateWithCString(NULL, argv[i], CFStringGetSystemEncoding()); 474 // check for URLs 475 itemURL = normalizedURLFromString(argStr); 476 if (itemURL == NULL && OPTS.action == ACTION_LAUNCH_URLS) { 477 // check for email addresses 478 if (strchr(argv[i], '@') != NULL && strchr(argv[i], '/') == NULL) 479 itemURL = normalizedURLFromPrefixSlack(CFSTR("mailto:"), argStr); 480 // check for "slack" URLs 481 if (itemURL == NULL && strchr(argv[i], '.') != NULL && strchr(argv[i], '/') != argv[i]) 482 itemURL = normalizedURLFromPrefixSlack(CFSTR("http://"), argStr); 483 } 478 struct stat stat_buf; 479 if (!OPTS.forceURLs && stat(argv[i], &stat_buf) == 0) { 480 itemURL = NULL; 481 } else { 482 argStr = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8); 483 // check for URLs 484 itemURL = normalizedURLFromString(argStr); 485 if (itemURL == NULL && OPTS.action == ACTION_LAUNCH_URLS) { 486 // check for email addresses 487 if (strchr(argv[i], '@') != NULL && strchr(argv[i], '/') == NULL) 488 itemURL = normalizedURLFromPrefixSlack(CFSTR("mailto:"), argStr); 489 // check for "slack" URLs 490 if (itemURL == NULL && strchr(argv[i], '.') != NULL && strchr(argv[i], '/') != argv[i]) 491 itemURL = normalizedURLFromPrefixSlack(CFSTR("http://"), argStr); 492 } 493 } 484 494 if (itemURL == NULL) { 485 495 // check for file paths 486 itemURL = CFURLCreate WithFileSystemPath(NULL, argStr, kCFURLPOSIXPathStyle, false);496 itemURL = CFURLCreateFromFileSystemRepresentation(NULL, argv[i], strlen(argv[i]), false); 487 497 err = LSCopyItemInfoForURL(itemURL, kLSRequestExtensionFlagsOnly, &docInfo); 488 498 if (err != noErr) osstatusexit(err, "unable to locate '%s'", argv[i]); … … 496 506 } 497 507 508 Boolean stringFromURLIsRemote(CFURLRef url, char *strBuffer) { 509 CFStringRef scheme = CFURLCopyScheme(url); 510 Boolean isRemote = !CFEqual(scheme, CFSTR("file")); 511 CFRelease(scheme); 512 513 strBuffer[0] = '\0'; 514 if (isRemote) { 515 CFStringRef urlString = CFURLGetString(url); 516 CFStringGetCString(urlString, strBuffer, STRBUF_LEN, kCFStringEncodingUTF8); 517 CFRelease(urlString); 518 } else { 519 if (CFURLGetFileSystemRepresentation(url, false, strBuffer, STRBUF_LEN)) { 520 if (strBuffer[0] == '.' && strBuffer[1] == '/') { 521 // remove the leading "./" 522 char *fromBufPtr = strBuffer + 2; 523 while (true) { 524 *strBuffer = *fromBufPtr; 525 if (*fromBufPtr == '\0') break; 526 strBuffer++; 527 fromBufPtr++; 528 } 529 } 530 } else { 531 strcpy(strBuffer, "[can't get path: CFURLGetFileSystemRepresentation failed]"); 532 } 533 } 534 return isRemote; 535 } 536 498 537 void printPathFromURL(CFURLRef url, FILE *stream) { 499 CFStringRef scheme, pathOrURL;500 538 static char strBuffer[STRBUF_LEN]; 501 502 539 check(url != NULL && stream != NULL); 503 504 scheme = CFURLCopyScheme(url); 505 506 if (CFEqual(scheme, CFSTR("file"))) 507 pathOrURL = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); 508 else 509 pathOrURL = CFURLGetString(url); 510 511 strBuffer[0] = '\0'; 512 CFStringGetCString(pathOrURL, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX buffer size issues? 540 stringFromURLIsRemote(url, strBuffer); 513 541 fprintf(stream, "%s\n", strBuffer); 514 CFRelease(scheme);515 CFRelease(pathOrURL);516 542 } 517 543 … … 582 608 583 609 if (fscInfo.nodeFlags & kFSNodeIsDirectoryMask) { 584 printf("\tcontents: %lu item%s\n", fscInfo.valence, fscInfo.valence != 1 ? "s" : ""); 610 printf("\tcontents: "); 611 switch (fscInfo.valence) { 612 case 0: printf("zero items\n"); break; 613 case 1: printf("1 item\n"); break; 614 default: printf("%lu items\n", fscInfo.valence); 615 } 585 616 } else { 586 617 printSizes("data fork size", fscInfo.dataLogicalSize, fscInfo.dataPhysicalSize, true); … … 605 636 } 606 637 638 const char *utf8StringFromCFStringRef(CFStringRef cfStr) { 639 static char tmpBuffer[STRBUF_LEN]; 640 CFStringGetCString(cfStr, tmpBuffer, STRBUF_LEN, kCFStringEncodingUTF8); 641 return tmpBuffer; 642 } 643 644 const char *utf8StringFromOSType(OSType osType) { 645 CFStringRef typeStr = CFStringCreateWithBytes(NULL, (const char *)&osType, 4, CFStringGetSystemEncoding(), false); 646 if (typeStr == NULL) { 647 // punt to displaying verbatim 648 static char tmpBuffer[4]; 649 tmpBuffer[4] = '\0'; 650 strncpy(tmpBuffer, (const char *)&osType, 4); 651 return tmpBuffer; 652 } 653 const char *buffer = utf8StringFromCFStringRef(typeStr); 654 CFRelease(typeStr); 655 return buffer; 656 } 657 607 658 // 'context' is to match prototype for CFArrayApplierFunction, it's unused 608 659 void printInfoFromURL(CFURLRef url, void *context) { 609 CFStringRef scheme, pathOrURL, kind; 610 Boolean isRemote; 611 static char strBuffer[STRBUF_LEN], tmpBuffer[STRBUF_LEN]; 660 CFStringRef kind; 661 static char strBuffer[STRBUF_LEN]; 612 662 613 663 check(url != NULL && context == NULL); 614 664 615 scheme = CFURLCopyScheme(url); 616 617 isRemote = !CFEqual(scheme, CFSTR("file")); 618 if (isRemote) 619 pathOrURL = CFURLGetString(url); 620 else 621 pathOrURL = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); 622 623 strBuffer[0] = '\0'; 624 CFStringGetCString(pathOrURL, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX buffer size issues? 625 if (isRemote) 665 if (stringFromURLIsRemote(url, strBuffer)) 626 666 printf("<%s>: URL\n", strBuffer); 627 667 else { 628 668 static LSItemInfoRecord info; 669 CFStringRef version = NULL; 670 UInt32 intVersion = 0; 629 671 OSStatus err = LSCopyItemInfoForURL(url, kLSRequestAllInfo, &info); 630 672 if (err != noErr) osstatusexit(err, "unable to get information about '%s'", strBuffer); … … 655 697 printf("\n"); 656 698 if (!(info.flags & kLSItemInfoIsContainer) || info.flags & kLSItemInfoIsPackage) { 657 tmpBuffer[4] = '\0'; 658 strncpy(tmpBuffer, (char *)&info.filetype, 4); printf("\ttype: '%s'", tmpBuffer); 659 strncpy(tmpBuffer, (char *)&info.creator, 4); printf("\tcreator: '%s'\n", tmpBuffer); 699 printf("\ttype: '%s'", utf8StringFromOSType(info.filetype)); 700 printf("\tcreator: '%s'\n", utf8StringFromOSType(info.creator)); 660 701 } 661 702 if (info.flags & kLSItemInfoIsPackage || info.flags & kLSItemInfoIsApplication) { … … 663 704 CFBundleRef bundle = CFBundleCreate(NULL, url); 664 705 CFStringRef bundleID = NULL; 665 CFStringRef appVersion = NULL;666 UInt32 intVersion = 0;667 706 if (bundle == NULL && (info.flags & kLSItemInfoIsApplication)) { 668 707 FSRef fsr; … … 683 722 CFPropertyListRef infoPlist = CFPropertyListCreateFromXMLData(NULL, plstData, kCFPropertyListImmutable, &error); 684 723 if (plstData != NULL) { 685 CFRelease(plstData); 686 plstData = NULL; 724 CFRelease(plstData); 725 plstData = NULL; 726 } else { 727 // this function should handle the 'plst' 0 case too, but it doesn't provide error messages; however, it handles the case of an unbundled Mach-O binary, so it is useful as a fallback 728 infoPlist = CFBundleCopyInfoDictionaryForURL(url); 687 729 } 688 730 if (infoPlist == NULL) { 689 CFStringGetCString(error, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); 690 printf("\t['plst' 0 resource invalid: %s]\n", tmpBuffer); 731 printf("\t['plst' 0 resource invalid: %s]\n", utf8StringFromCFStringRef(error)); 691 732 CFRelease(error); 692 733 } else { … … 694 735 bundleID = CFDictionaryGetValue(infoPlist, kCFBundleIdentifierKey); 695 736 if (bundleID != NULL) CFRetain(bundleID); 696 appVersion = CFDictionaryGetValue(infoPlist, CFSTR("CFBundleShortVersionString"));697 if ( appVersion == NULL)698 appVersion = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey);699 if ( appVersion != NULL) CFRetain(appVersion);737 version = CFDictionaryGetValue(infoPlist, CFSTR("CFBundleShortVersionString")); 738 if (version == NULL) 739 version = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey); 740 if (version != NULL) CFRetain(version); 700 741 CFRelease(infoPlist); 701 742 } … … 705 746 if (err != noErr && err != resNotFound) osstatusexit(err, "unable to read 'vers' 1 resource"); 706 747 } else { 707 if ( appVersion == NULL) { // prefer 'plst' version708 appVersion = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead?748 if (version == NULL) { // prefer 'plst' version 749 version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead? 709 750 } 710 751 intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole; … … 717 758 if (bundleID != NULL) CFRetain(bundleID); 718 759 // prefer a short version string, e.g. "1.0 Beta" instead of "51" for Safari 719 appVersion = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"));720 if ( appVersion == NULL)721 appVersion = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);722 if ( appVersion != NULL) {723 CFRetain( appVersion);760 version = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")); 761 if (version == NULL) 762 version = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey); 763 if (version != NULL) { 764 CFRetain(version); 724 765 intVersion = CFBundleGetVersionNumber(bundle); 725 766 } … … 727 768 } 728 769 if (bundleID != NULL) { 729 CFStringGetCString(bundleID, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); 730 printf("\tbundle ID: %s\n", tmpBuffer); 770 printf("\tbundle ID: %s\n", utf8StringFromCFStringRef(bundleID)); 731 771 CFRelease(bundleID); 732 772 } 733 if (appVersion != NULL) { 734 CFStringGetCString(appVersion, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); 735 printf("\tversion: %s", tmpBuffer); 736 if (intVersion != 0) printf(" [0x%lx = %lu]", intVersion, intVersion); 737 putchar('\n'); 738 CFRelease(appVersion); 739 } 740 } 741 773 } else { 774 // 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 } 787 } 788 789 if (version != NULL) { 790 printf("\tversion: %s", utf8StringFromCFStringRef(version)); 791 if (intVersion != 0) printf(" [0x%lx = %lu]", intVersion, intVersion); 792 putchar('\n'); 793 CFRelease(version); 794 } 795 742 796 // kind string 743 797 err = LSCopyKindStringForURL(url, &kind); 744 798 if (err != noErr) osstatusexit(err, "unable to get kind of '%s'", strBuffer); 745 CFStringGetCString(kind, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); 746 printf("\tkind: %s\n", tmpBuffer); 799 printf("\tkind: %s\n", utf8StringFromCFStringRef(kind)); 747 800 CFRelease(kind); 748 801 printMoreInfoFromURL(url); 749 802 } 750 CFRelease(scheme);751 CFRelease(pathOrURL);752 803 } 753 804 … … 760 811 761 812 strBuffer[0] = '\0'; 762 CFStringGetCString(urlStr, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX buffer size issues?813 CFStringGetCString(urlStr, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX no idea what encoding ICLaunchURL is supposed to take; leave as is for now 763 814 strStart = 0; 764 815 strEnd = strlen(strBuffer);
Note:
See TracChangeset
for help on using the changeset viewer.