Index: /trunk/launch/launch/README
===================================================================
--- /trunk/launch/launch/README	(revision 307)
+++ /trunk/launch/launch/README	(revision 308)
@@ -61,8 +61,8 @@
 -----------
 
-An Xcode project, 'launch.xcodeproj', is included.  A precompiled Universal
-binary is also provided.  'launch' was developed and tested under Mac OS X 
-10.4.7 with Xcode 2.3, and does not require any additional software to
-build.
+An Xcode project, 'launch.xcodeproj', is included.  A precompiled
+Universal binary is also provided.  'launch' was developed and tested
+under Mac OS X 10.4.8 with Xcode 2.4, and does not require any
+additional software to build.
 
 USAGE
@@ -89,9 +89,11 @@
 
 % launch -f /Applications/Preview.app
-/Applications/Preview.app: Mac OS X application package 
+/Applications/Preview.app: Mac OS X application package
         type: 'APPL'    creator: 'prvw'
+        architecture: Intel 80x86, PowerPC
         bundle ID: com.apple.Preview
-        version: 2.1.0
+        version: 3.0.8
         kind: Application
+        content type ID: com.apple.application-bundle
         contents: 1 item
 [...]
@@ -120,9 +122,8 @@
 
 % launch -f /Applications/Internet/OmniWeb.app
-/Applications/Internet/OmniWeb.app: Mac OS X application package 
+/Applications/Internet/OmniWeb.app: scriptable Mac OS X application package 
         type: 'APPL'    creator: 'OWEB'
-        bundle ID: com.omnigroup.OmniWeb
-        version: 4.5
-        kind: Application
+        architecture: PowerPC, Intel 80x86
+        bundle ID: com.omnigroup.OmniWeb5
 
 Note that bundle IDs are not case-sensitive: com.barebones.BBEdit is
@@ -227,4 +228,5 @@
  - -o: pass command-line arguments (broken, r. 4474993)
  - display content type ID (UTI)    
+ - display architecture of Mach-O files
  - switched to new LSOpen APIs (now requires Mac OS X 10.4 or later)
  - switched to new date formatting APIs (the old ones are deprecated)
Index: /trunk/launch/launch/launch.xcodeproj/project.pbxproj
===================================================================
--- /trunk/launch/launch/launch.xcodeproj/project.pbxproj	(revision 307)
+++ /trunk/launch/launch/launch.xcodeproj/project.pbxproj	(revision 308)
@@ -134,4 +134,5 @@
 					i386,
 				);
+				GCC_C_LANGUAGE_STANDARD = c99;
 				MACOSX_DEPLOYMENT_TARGET = 10.4;
 				PREBINDING = NO;
@@ -147,4 +148,5 @@
 					i386,
 				);
+				GCC_C_LANGUAGE_STANDARD = c99;
 				MACOSX_DEPLOYMENT_TARGET = 10.4;
 				PREBINDING = NO;
Index: /trunk/launch/launch/main.c
===================================================================
--- /trunk/launch/launch/main.c	(revision 307)
+++ /trunk/launch/launch/main.c	(revision 308)
@@ -21,4 +21,7 @@
 #include <unistd.h>
 #include <sys/stat.h>
+#include <mach-o/fat.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
 #include <Carbon/Carbon.h>
 #include <CoreServices/CoreServices.h>
@@ -648,4 +651,83 @@
     CFRelease(typeStr);
     return buffer;
+}
+
+// based on Apple's "CheckExecutableArchitecture" sample code
+
+#define MAX_HEADER_BYTES 512
+
+void swapHeader(uint8_t *bytes, ssize_t length) {
+    for (ssize_t i = 0 ; i < length ; i += 4)
+        *(uint32_t *)(bytes + i) = OSSwapInt32(*(uint32_t *)(bytes + i));
+}
+
+void printExecutableArchitectures(CFURLRef url, bool printOnFailure) {
+    uint8_t path[PATH_MAX];
+    if (printOnFailure)
+        printf("\tarchitecture: ");
+        
+    if (!CFURLGetFileSystemRepresentation(url, true, path, PATH_MAX)) {
+        if (printOnFailure) printf("(can't get executable)\n");
+        return;
+    }
+    
+    int fd = open((const char *)path, O_RDONLY, 0777);
+    if (fd <= 0) {
+        if (printOnFailure) printf("(can't read)\n");
+        return;
+    }
+    
+    uint8_t bytes[MAX_HEADER_BYTES];
+    ssize_t length = read(fd, bytes, MAX_HEADER_BYTES);
+    close(fd);
+
+    if (length < sizeof(struct mach_header_64)) {
+        if (printOnFailure) printf("(can't read Mach-O header)\n");
+        return;
+    }
+
+    // Look for any of the six magic numbers relevant to Mach-O executables, and swap the header if necessary.
+    uint32_t num_fat = 0, magic = *((uint32_t *)bytes);
+    uint32_t max_fat = (length - sizeof(struct fat_header)) / sizeof(struct fat_arch);
+    struct fat_arch one_fat = {0}, *fat;
+    if (MH_MAGIC == magic || MH_CIGAM == magic) {
+        struct mach_header *mh = (struct mach_header *)bytes;
+        if (MH_CIGAM == magic) swapHeader(bytes, length);
+        one_fat.cputype = mh->cputype;
+        one_fat.cpusubtype = mh->cpusubtype;
+        fat = &one_fat;
+        num_fat = 1;
+    } else if (MH_MAGIC_64 == magic || MH_CIGAM_64 == magic) {
+        struct mach_header_64 *mh = (struct mach_header_64 *)bytes;
+        if (MH_CIGAM_64 == magic) swapHeader(bytes, length);
+        one_fat.cputype = mh->cputype;
+        one_fat.cpusubtype = mh->cpusubtype;
+        fat = &one_fat;
+        num_fat = 1;
+    } else if (FAT_MAGIC == magic || FAT_CIGAM == magic) {
+        fat = (struct fat_arch *)(bytes + sizeof(struct fat_header));
+        if (FAT_CIGAM == magic) swapHeader(bytes, length);
+        num_fat = ((struct fat_header *)bytes)->nfat_arch;
+        if (num_fat > max_fat) num_fat = max_fat;
+    }
+    
+    if (num_fat == 0) {
+        if (printOnFailure) printf("(none found)\n");
+        return;
+    }
+    
+    if (!printOnFailure)
+        printf("\tarchitecture: ");
+        
+    for (int i = 0 ; i < num_fat ; i++) {
+        if (i != 0) printf(", ");
+        const NXArchInfo *arch = NXGetArchInfoFromCpuType(fat[i].cputype, fat[i].cpusubtype);
+        if (arch == NULL) {
+            printf("unknown (cputype %d, subtype %d)", fat[i].cputype, fat[i].cpusubtype);
+            continue;
+        }
+        printf(arch->description);
+    }
+    printf("\n");
 }
 
@@ -761,4 +843,9 @@
                     intVersion = CFBundleGetVersionNumber(bundle);
                 }
+                CFURLRef executable = CFBundleCopyExecutableURL(bundle);
+                if (executable != NULL) {
+                    printExecutableArchitectures(executable, true);
+                    CFRelease(executable);
+                }
                 CFRelease(bundle);
             }
@@ -767,17 +854,20 @@
                 CFRelease(bundleID);
             }
-        } else if (haveFSRef) {
-	    // try to get a version if we can, but don't complain if we can't
-            SInt16 resFork = FSOpenResFile(&fsr, fsRdPerm);
-            if (ResError() == noErr) {
-                VersRecHndl vers = (VersRecHndl)Get1Resource('vers', 1);
-                if (ResError() == noErr && vers != NULL) {
-                    version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead?
-                    intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole;
+        } else {
+            printExecutableArchitectures(url, false);
+            if (haveFSRef) {
+                // try to get a version if we can, but don't complain if we can't
+                SInt16 resFork = FSOpenResFile(&fsr, fsRdPerm);
+                if (ResError() == noErr) {
+                    VersRecHndl vers = (VersRecHndl)Get1Resource('vers', 1);
+                    if (ResError() == noErr && vers != NULL) {
+                        version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead?
+                        intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole;
+                    }
                 }
+                CloseResFile(resFork);
             }
-            CloseResFile(resFork);
 	}
-	
+
 	if (version != NULL) {
 	    printf("\tversion: %s", utf8StringFromCFStringRef(version));
