Ignore:
Timestamp:
05/11/08 22:47:36 (16 years ago)
Author:
Nicholas Riley
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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}
Note: See TracChangeset for help on using the changeset viewer.