Changeset 477

Show
Ignore:
Timestamp:
05/11/08 10:47:36 PM (7 months ago)
Author:
nicholas
Message:

read Info.plists embedded in all Mach-O binaries (e.g. hdiutil); simplify printInfoFromURL

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/launch/launch/README

    r314 r477  
    6363An Xcode project, 'launch.xcodeproj', is included.  A precompiled 
    6464Universal binary is also provided.  'launch' was developed and tested 
    65 under Mac OS X 10.4.8 with Xcode 2.4, and does not require any 
     65under Mac OS X 10.5.2 with Xcode 3.1, and does not require any 
    6666additional software to build. 
    6767 
     
    2252251.1.1 - unreleased 
    226226 - updated error codes for Mac OS X 10.4 
     227 - read Info.plists embedded in all Mach-O binaries (e.g. hdiutil) 
    2272281.1 - 30 October 2006 
    228229 - -L: send "launch" (ascr/noop) event to app, bypasses automatic 
  • trunk/launch/launch/VERSION

    r314 r477  
    1 1.1.1d1 
     11.1.1d2 
  • trunk/launch/launch/launch.xcodeproj/project.pbxproj

    r308 r477  
    106106                        isa = PBXProject; 
    107107                        buildConfigurationList = E194195609C40AD400266191 /* Build configuration list for PBXProject "launch" */; 
     108                        compatibilityVersion = "Xcode 2.4"; 
    108109                        hasScannedForEncodings = 1; 
    109110                        mainGroup = 08FB7794FE84155DC02AAC07 /* launch */; 
    110111                        projectDirPath = ""; 
     112                        projectRoot = ""; 
    111113                        targets = ( 
    112114                                E194196209C40AFB00266191 /* launch */, 
  • trunk/launch/launch/main.c

    r314 r477  
    33 Nicholas Riley <launchsw@sabi.net> 
    44 
    5  Copyright (c) 2001-06, Nicholas Riley 
     5 Copyright (c) 2001-08, Nicholas Riley 
    66 All rights reserved. 
    77 
     
    3737const char *APP_NAME; 
    3838 
    39 #define VERSION "1.1
     39#define VERSION "1.1.1d2
    4040 
    4141#define STRBUF_LEN 1024 
     
    9999    { kCGErrorApplicationRequiresNewerSystem, "application requires a newer Mac OS X version" }, 
    100100    { fnfErr, "file not found" }, 
     101    { eofErr, "data not found" }, 
    101102    { 0, NULL } 
    102103}; 
     
    755756    check(url != NULL && context == NULL); 
    756757 
    757     if (stringFromURLIsRemote(url, strBuffer)) 
     758    if (stringFromURLIsRemote(url, strBuffer)) { 
    758759        printf("<%s>: URL\n", strBuffer); 
    759     else { 
    760         static LSItemInfoRecord info; 
    761         CFStringRef version = NULL; 
    762         UInt32 intVersion = 0; 
    763         OSStatus err = LSCopyItemInfoForURL(url, kLSRequestAllInfo, &info); 
    764         Boolean haveFSRef; 
    765         FSRef fsr; 
    766         if (err != noErr) osstatusexit(err, "unable to get information about '%s'", strBuffer); 
    767         haveFSRef = CFURLGetFSRef(url, &fsr); 
    768          
    769         printf("%s: ", strBuffer); 
    770          
    771         // modifiers 
    772         if (info.flags & kLSItemInfoIsInvisible) printf("invisible "); 
    773         if (info.flags & kLSItemInfoAppIsScriptable) printf("scriptable "); 
    774         if (info.flags & kLSItemInfoIsNativeApp) printf("Mac OS X "); 
    775         if (info.flags & kLSItemInfoIsClassicApp) printf("Classic "); 
    776          
    777         // kind 
    778         if (info.flags & kLSItemInfoIsVolume) printf("volume"); 
    779         else if (info.flags & kLSItemInfoIsApplication) printf("application "); 
    780         else if (info.flags & kLSItemInfoIsPackage) printf("non-application "); 
    781         else if (info.flags & kLSItemInfoIsContainer) printf("folder"); 
    782         else if (info.flags & kLSItemInfoIsAliasFile) printf("alias"); 
    783         else if (info.flags & kLSItemInfoIsSymlink) printf("symbolic link"); 
    784         else if (info.flags & kLSItemInfoIsPlainFile) printf("document"); 
    785         else printf("unknown file system entity"); 
    786  
    787         if (info.flags & kLSItemInfoIsPackage) printf("package "); 
    788  
    789         if (info.flags & kLSItemInfoAppPrefersNative) printf("[Carbon, prefers native OS X]"); 
    790         else if (info.flags & kLSItemInfoAppPrefersClassic) printf("[Carbon, prefers Classic]"); 
    791  
    792         printf("\n"); 
    793         if (!(info.flags & kLSItemInfoIsContainer) || info.flags & kLSItemInfoIsPackage) { 
    794             printf("\ttype: '%s'", utf8StringFromOSType(info.filetype)); 
    795             printf("\tcreator: '%s'\n", utf8StringFromOSType(info.creator)); 
    796         } 
    797         if (info.flags & kLSItemInfoIsPackage || info.flags & kLSItemInfoIsApplication) { 
    798                 // a package, or possibly a native app with a 'plst' resource 
    799             CFBundleRef bundle = CFBundleCreate(NULL, url); 
    800             CFStringRef bundleID = NULL; 
    801             if (bundle == NULL && (info.flags & kLSItemInfoIsApplication)) { 
    802                 if (info.flags & kLSItemInfoIsPackage || !haveFSRef) { 
    803                     printf("\t[can't access CFBundle for application]\n"); 
    804                 } else { // OS X bug causes this to fail when it shouldn't, so fake it 
    805                     SInt16 resFork = FSOpenResFile(&fsr, fsRdPerm); 
    806                     OSStatus err = ResError(); 
    807                     if (err != noErr) { 
    808                         printf("\t[can't open resource fork: %s]\n", osstatusstr(err)); 
    809                     } else { 
    810                         Handle h = Get1Resource('plst', 0); 
    811                         if ( (err = ResError()) != noErr || h == NULL) { 
    812                             if (err != noErr && err != resNotFound) osstatusexit(err, "unable to read 'plst' 0 resource"); 
    813                         } else { 
    814                             CFDataRef plstData = CFDataCreate(NULL, (UInt8 *)*h, GetHandleSize(h)); 
    815                             CFStringRef error; 
    816                             CFPropertyListRef infoPlist = CFPropertyListCreateFromXMLData(NULL, plstData, kCFPropertyListImmutable, &error); 
    817                             if (plstData != NULL) { 
    818                                 CFRelease(plstData); 
    819                                 plstData = NULL; 
    820                             } else { 
    821                                 // 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 
    822                                 infoPlist = CFBundleCopyInfoDictionaryForURL(url); 
    823                             } 
    824                             if (infoPlist == NULL) { 
    825                                 printf("\t['plst' 0 resource invalid: %s]\n", utf8StringFromCFStringRef(error)); 
    826                                 CFRelease(error); 
    827                             } else { 
    828                                 // mimic CFBundle logic below 
    829                                 bundleID = CFDictionaryGetValue(infoPlist, kCFBundleIdentifierKey); 
    830                                 if (bundleID != NULL) CFRetain(bundleID); 
    831                                 version = CFDictionaryGetValue(infoPlist, CFSTR("CFBundleShortVersionString")); 
    832                                 if (version == NULL) 
    833                                     version = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey); 
    834                                 if (version != NULL) CFRetain(version); 
    835                                 CFRelease(infoPlist); 
    836                             } 
    837                         } 
    838                         VersRecHndl vers = (VersRecHndl)Get1Resource('vers', 1); 
    839                         if ( (err = ResError()) != noErr || vers == NULL) { 
    840                             if (err != noErr && err != resNotFound) osstatusexit(err, "unable to read 'vers' 1 resource"); 
    841                         } else { 
    842                             if (version == NULL) { // prefer 'plst' version 
    843                                 version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead? 
    844                             } 
    845                             intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole; 
    846                         } 
    847                         CloseResFile(resFork); 
    848                     } 
    849                 } 
    850             } else { 
    851                 bundleID = CFBundleGetIdentifier(bundle); 
    852                 if (bundleID != NULL) CFRetain(bundleID); 
    853                 // prefer a short version string, e.g. "1.0 Beta" instead of "51" for Safari 
    854                 version = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")); 
    855                 if (version == NULL) 
    856                     version = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey); 
    857                 if (version != NULL) { 
    858                     CFRetain(version); 
    859                     intVersion = CFBundleGetVersionNumber(bundle); 
    860                 } 
    861                 CFURLRef executable = CFBundleCopyExecutableURL(bundle); 
    862                 if (executable != NULL) { 
    863                     printExecutableArchitectures(executable, true); 
    864                     CFRelease(executable); 
    865                 } 
    866                 CFRelease(bundle); 
    867             } 
    868             if (bundleID != NULL) { 
    869                 printf("\tbundle ID: %s\n", utf8StringFromCFStringRef(bundleID)); 
    870                 CFRelease(bundleID); 
    871             } 
    872         } else { 
    873             printExecutableArchitectures(url, false); 
    874             if (haveFSRef) { 
    875                 // try to get a version if we can, but don't complain if we can't 
    876                 SInt16 resFork = FSOpenResFile(&fsr, fsRdPerm); 
    877                 if (ResError() == noErr) { 
    878                     VersRecHndl vers = (VersRecHndl)Get1Resource('vers', 1); 
    879                     if (ResError() == noErr && vers != NULL) { 
    880                         version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead? 
    881                         intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole; 
    882                     } 
    883                 } 
    884                 CloseResFile(resFork); 
    885             } 
    886         } 
    887  
     760        return; 
     761    } 
     762     
     763    static LSItemInfoRecord info; 
     764    OSStatus err; 
     765    if ( (err = LSCopyItemInfoForURL(url, kLSRequestAllInfo, &info)) != noErr) 
     766        osstatusexit(err, "unable to get information about '%s'", strBuffer); 
     767     
     768    printf("%s: ", strBuffer); 
     769     
     770    // modifiers 
     771    if (info.flags & kLSItemInfoIsInvisible) printf("invisible "); 
     772    if (info.flags & kLSItemInfoAppIsScriptable) printf("scriptable "); 
     773    if (info.flags & kLSItemInfoIsNativeApp) printf("Mac OS X "); 
     774    if (info.flags & kLSItemInfoIsClassicApp) printf("Classic "); 
     775     
     776    // kind 
     777    if (info.flags & kLSItemInfoIsVolume) printf("volume"); 
     778    else if (info.flags & kLSItemInfoIsApplication) printf("application "); 
     779    else if (info.flags & kLSItemInfoIsPackage) printf("non-application "); 
     780    else if (info.flags & kLSItemInfoIsContainer) printf("folder"); 
     781    else if (info.flags & kLSItemInfoIsAliasFile) printf("alias"); 
     782    else if (info.flags & kLSItemInfoIsSymlink) printf("symbolic link"); 
     783    else if (info.flags & kLSItemInfoIsPlainFile) printf("document"); 
     784    else printf("unknown file system entity"); 
     785 
     786    if (info.flags & kLSItemInfoIsPackage) printf("package "); 
     787 
     788    if (info.flags & kLSItemInfoAppPrefersNative) printf("[Carbon, prefers native OS X]"); 
     789    else if (info.flags & kLSItemInfoAppPrefersClassic) printf("[Carbon, prefers Classic]"); 
     790 
     791    printf("\n"); 
     792    if (!(info.flags & kLSItemInfoIsContainer) || info.flags & kLSItemInfoIsPackage) { 
     793        printf("\ttype: '%s'", utf8StringFromOSType(info.filetype)); 
     794        printf("\tcreator: '%s'\n", utf8StringFromOSType(info.creator)); 
     795    } 
     796 
     797    CFStringRef bundleID = NULL; 
     798    CFStringRef version = NULL; 
     799    UInt32 intVersion = 0; 
     800    FSRef fsr; 
     801    Boolean haveFSRef = CFURLGetFSRef(url, &fsr); 
     802    CFBundleRef bundle = NULL; 
     803     
     804    if ((info.flags & kLSItemInfoIsPackage || info.flags & kLSItemInfoIsApplication) && 
     805        ( (bundle = CFBundleCreate(NULL, url)) != NULL)) { 
     806        bundleID = CFBundleGetIdentifier(bundle); 
     807        if (bundleID != NULL) CFRetain(bundleID); 
     808        // prefer a short version string, e.g. "1.0 Beta" instead of "51" for Safari 
     809        version = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")); 
     810        if (version == NULL) 
     811            version = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey); 
    888812        if (version != NULL) { 
    889             printf("\tversion: %s", utf8StringFromCFStringRef(version)); 
    890             if (intVersion != 0) printf(" [0x%lx = %lu]", intVersion, intVersion); 
    891             putchar('\n'); 
    892             CFRelease(version); 
    893         } 
    894  
    895         // kind string 
    896         err = LSCopyKindStringForURL(url, &kind); 
    897         if (err != noErr) osstatusexit(err, "unable to get kind of '%s'", strBuffer); 
    898         printf("\tkind: %s\n", utf8StringFromCFStringRef(kind)); 
    899         CFRelease(kind); 
    900          
    901         if (haveFSRef) { 
    902             // content type identifier (UTI) 
    903             err = LSCopyItemAttribute(&fsr, kLSRolesAll, kLSItemContentType, (CFTypeRef *)&kind); 
    904             if (err == noErr) { 
    905                 printf("\tcontent type ID: %s\n", utf8StringFromCFStringRef(kind)); 
    906                 CFRelease(kind); 
    907             } 
    908             printMoreInfoForRef(fsr); 
    909         } 
     813            CFRetain(version); 
     814            intVersion = CFBundleGetVersionNumber(bundle); 
     815        } 
     816        CFURLRef executable = CFBundleCopyExecutableURL(bundle); 
     817        if (executable != NULL) { 
     818            printExecutableArchitectures(executable, true); 
     819            CFRelease(executable); 
     820        } 
     821        CFRelease(bundle); 
     822    } else if (info.flags & kLSItemInfoIsPackage || !haveFSRef) { 
     823        printf("\t[can't access package contents]\n"); 
     824    } else if (haveFSRef) { 
     825        SInt16 resFork = FSOpenResFile(&fsr, fsRdPerm); 
     826        CFPropertyListRef infoPlist = NULL; 
     827        if ( (err = ResError()) == noErr) { 
     828            Handle h = Get1Resource('plst', 0); 
     829            if (h == NULL) { 
     830                err = ResError(); 
     831                if (err != noErr && err != resNotFound) 
     832                    osstatusexit(err, "unable to read 'plst' 0 resource"); 
     833            } else { 
     834                CFDataRef plstData = CFDataCreate(NULL, (UInt8 *)*h, GetHandleSize(h)); 
     835                CFStringRef error = NULL; 
     836                infoPlist = CFPropertyListCreateFromXMLData(NULL, plstData, kCFPropertyListImmutable, &error); 
     837                if (plstData != NULL) CFRelease(plstData); 
     838                if (infoPlist == NULL) { 
     839                    printf("\t['plst' 0 resource invalid: %s]\n", utf8StringFromCFStringRef(error)); 
     840                    CFRelease(error); 
     841                } 
     842            } 
     843        } 
     844        if (infoPlist == NULL) { 
     845            // 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 
     846            infoPlist = CFBundleCopyInfoDictionaryForURL(url); 
     847            if (infoPlist == NULL && info.flags & kLSItemInfoIsApplication && resFork == -1) 
     848                printf("\t[can't open resource fork: %s]\n", osstatusstr(err)); 
     849        } 
     850        if (infoPlist != NULL) { 
     851            // mimic CFBundle logic above 
     852            bundleID = CFDictionaryGetValue(infoPlist, kCFBundleIdentifierKey); 
     853            if (bundleID != NULL) CFRetain(bundleID); 
     854            version = CFDictionaryGetValue(infoPlist, CFSTR("CFBundleShortVersionString")); 
     855            if (version == NULL) 
     856                version = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey); 
     857            if (version != NULL) CFRetain(version); 
     858            CFRelease(infoPlist); 
     859        } 
     860        if (resFork != -1) { 
     861            VersRecHndl vers = (VersRecHndl)Get1Resource('vers', 1); 
     862            if (vers == NULL) { 
     863                err = ResError(); 
     864                if (err != noErr && err != resNotFound) 
     865                    osstatusexit(err, "unable to read 'vers' 1 resource"); 
     866            } else { 
     867                if (version == NULL) { // prefer 'plst' version 
     868                    version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead? 
     869                } 
     870                intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole; 
     871            } 
     872            CloseResFile(resFork); 
     873        } 
     874        printExecutableArchitectures(url, false); 
     875    } 
     876 
     877    if (bundleID != NULL) { 
     878        printf("\tbundle ID: %s\n", utf8StringFromCFStringRef(bundleID)); 
     879        CFRelease(bundleID); 
     880    } 
     881    if (version != NULL) { 
     882        printf("\tversion: %s", utf8StringFromCFStringRef(version)); 
     883        if (intVersion != 0) printf(" [0x%lx = %lu]", intVersion, intVersion); 
     884        putchar('\n'); 
     885        CFRelease(version); 
     886    } 
     887 
     888    // kind string 
     889    err = LSCopyKindStringForURL(url, &kind); 
     890    if (err != noErr) osstatusexit(err, "unable to get kind of '%s'", strBuffer); 
     891    printf("\tkind: %s\n", utf8StringFromCFStringRef(kind)); 
     892    CFRelease(kind); 
     893     
     894    if (haveFSRef) { 
     895        // content type identifier (UTI) 
     896        err = LSCopyItemAttribute(&fsr, kLSRolesAll, kLSItemContentType, (CFTypeRef *)&kind); 
     897        if (err == noErr) { 
     898            printf("\tcontent type ID: %s\n", utf8StringFromCFStringRef(kind)); 
     899            CFRelease(kind); 
     900        } 
     901        printMoreInfoForRef(fsr); 
    910902    } 
    911903}