Index: trunk/launch/launch/main.c
===================================================================
--- trunk/launch/launch/main.c	(revision 148)
+++ trunk/launch/launch/main.c	(revision 166)
@@ -3,5 +3,5 @@
  Nicholas Riley <launchsw@sabi.net>
 
- Copyright (c) 2001-03, Nicholas Riley
+ Copyright (c) 2001-05, Nicholas Riley
  All rights reserved.
 
@@ -51,5 +51,5 @@
 const char *APP_NAME;
 
-#define VERSION "1.0"
+#define VERSION "1.0.1"
 
 #define STRBUF_LEN 1024
@@ -59,4 +59,5 @@
     OSType creator;
     CFStringRef bundleID, name;
+    Boolean forceURLs;
     enum { ACTION_FIND, ACTION_FIND_ITEMS,
 	   ACTION_OPEN, ACTION_OPEN_ITEMS, 
@@ -64,5 +65,5 @@
 } OPTS = 
 {
-    kLSUnknownCreator, NULL, NULL,
+    kLSUnknownCreator, NULL, NULL, false,
     ACTION_DEFAULT
 };
@@ -87,4 +88,5 @@
     { -10827, "application package contains no executable, or an unusable executable" }, /* kLSNoExecutableErr, not defined in 10.2 */
     { -10828, "Classic environment required but not available" }, /* kLSNoClassicEnvironmentErr, not defined in 10.2 */
+    { -10829, "unable to launch multiple instances of application" }, /* kLSMultipleSessionsNotSupportedErr, not defined in 10.2 */
 #ifndef BROKEN_AUTHORIZATION
     // Security framework errors
@@ -97,4 +99,5 @@
     { icInternalErr, "internal Internet Config error" },
     // Misc. errors
+    { nsvErr, "the volume cannot be found (buggy filesystem?)" },
     { procNotFound, "unable to connect to system service.\nAre you logged in?" },
     { kCGErrorIllegalArgument, "window server error.\nAre you logged in?" },
@@ -105,6 +108,6 @@
 
 void usage() {
-    fprintf(stderr, "usage: %s [-npswbmhCX] [-c creator] [-i bundleID] [-u URL] [-a name] [item ...] [-]\n"
-                    "   or: %s [-npflswbmhCX] item ...\n", APP_NAME, APP_NAME);
+    fprintf(stderr, "usage: %s [-npswbmhCXU] [-c creator] [-i bundleID] [-u URL] [-a name] [item ...] [-]\n"
+                    "   or: %s [-npflswbmhCXU] item ...\n", APP_NAME, APP_NAME);
     fprintf(stderr,
         "  -n            print matching paths/URLs instead of opening them\n"
@@ -121,4 +124,5 @@
         "  -C            force CFM/PEF Carbon application to launch in Classic\n"
         "  -X            don't start Classic for this app if Classic isn't running\n"
+	"  -U            interpret items as URLs, even if same-named files exist\n"
         "  -c creator    match application by four-character creator code ('ToyS')\n"
         "  -i bundle ID  match application by bundle identifier (com.apple.scripteditor)\n"
@@ -127,5 +131,5 @@
         "'document' may be a file, folder, or disk - whatever the application can open.\n"
         "'item' may be a file, folder, disk, or URL.\n\n");
-    fprintf(stderr, "launch "VERSION" (c) 2001-03 Nicholas Riley <http://web.sabi.net/nriley/software/>.\n"
+    fprintf(stderr, "launch "VERSION" (c) 2001-05 Nicholas Riley <http://web.sabi.net/nriley/software/>.\n"
 	            "Please send bugs, suggestions, etc. to <launchsw@sabi.net>.\n");
 
@@ -341,5 +345,5 @@
     if (argc == 1) usage();
     
-    while ( (ch = getopt(argc, argv, "npflswbmhCXc:i:u:a:")) != -1) {
+    while ( (ch = getopt(argc, argv, "npflswbmhCXUc:i:u:a:")) != -1) {
         switch (ch) {
         case 'n':
@@ -384,4 +388,5 @@
         case 'C': LSPEC.launchFlags |= kLSLaunchInClassic; break;  // force Classic
         case 'X': LSPEC.launchFlags ^= kLSLaunchStartClassic; break;// don't start Classic for app
+	case 'U': OPTS.forceURLs = true; break;
         case 'c':
             if (strlen(optarg) != 4) errexit("creator (argument of -c) must be four characters long");
@@ -390,13 +395,13 @@
             break;
         case 'i':
-            OPTS.bundleID = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());
+            OPTS.bundleID = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
 	    appSpecified = true;
             break;
         case 'a':
-            OPTS.name = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());
+            OPTS.name = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
 	    appSpecified = true;
             break;
 	case 'u':
-            { CFStringRef str = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());
+            { CFStringRef str = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
 	      LSPEC.appURL = CFURLCreateWithString(NULL, str, NULL);
               if (str != NULL) CFRelease(str);
@@ -471,18 +476,23 @@
                 LSPEC.launchFlags ^= kLSLaunchAsync;
             } else {
-                argStr = CFStringCreateWithCString(NULL, argv[i], CFStringGetSystemEncoding());
-                // check for URLs
-                itemURL = normalizedURLFromString(argStr);
-                if (itemURL == NULL && OPTS.action == ACTION_LAUNCH_URLS) {
-                    // check for email addresses
-                    if (strchr(argv[i], '@') != NULL && strchr(argv[i], '/') == NULL)
-                        itemURL = normalizedURLFromPrefixSlack(CFSTR("mailto:"), argStr);
-                    // check for "slack" URLs
-                    if (itemURL == NULL && strchr(argv[i], '.') != NULL && strchr(argv[i], '/') != argv[i])
-                        itemURL = normalizedURLFromPrefixSlack(CFSTR("http://"), argStr);
-                }
+		struct stat stat_buf;
+		if (!OPTS.forceURLs && stat(argv[i], &stat_buf) == 0) {
+		    itemURL = NULL;
+		} else {
+		    argStr = CFStringCreateWithCString(NULL, argv[i], kCFStringEncodingUTF8);
+		    // check for URLs
+		    itemURL = normalizedURLFromString(argStr);
+		    if (itemURL == NULL && OPTS.action == ACTION_LAUNCH_URLS) {
+			// check for email addresses
+			if (strchr(argv[i], '@') != NULL && strchr(argv[i], '/') == NULL)
+			    itemURL = normalizedURLFromPrefixSlack(CFSTR("mailto:"), argStr);
+			// check for "slack" URLs
+			if (itemURL == NULL && strchr(argv[i], '.') != NULL && strchr(argv[i], '/') != argv[i])
+			    itemURL = normalizedURLFromPrefixSlack(CFSTR("http://"), argStr);
+		    }
+		}
                 if (itemURL == NULL) {
                     // check for file paths
-                    itemURL = CFURLCreateWithFileSystemPath(NULL, argStr, kCFURLPOSIXPathStyle, false);
+                    itemURL = CFURLCreateFromFileSystemRepresentation(NULL, argv[i], strlen(argv[i]), false);
                     err = LSCopyItemInfoForURL(itemURL, kLSRequestExtensionFlagsOnly, &docInfo);
                     if (err != noErr) osstatusexit(err, "unable to locate '%s'", argv[i]);
@@ -496,22 +506,38 @@
 }
 
+Boolean stringFromURLIsRemote(CFURLRef url, char *strBuffer) {
+    CFStringRef scheme = CFURLCopyScheme(url);
+    Boolean isRemote = !CFEqual(scheme, CFSTR("file"));
+    CFRelease(scheme);
+    
+    strBuffer[0] = '\0';
+    if (isRemote) {
+        CFStringRef urlString = CFURLGetString(url);
+	CFStringGetCString(urlString, strBuffer, STRBUF_LEN, kCFStringEncodingUTF8);
+	CFRelease(urlString);
+    } else {
+	if (CFURLGetFileSystemRepresentation(url, false, strBuffer, STRBUF_LEN)) {
+	    if (strBuffer[0] == '.' && strBuffer[1] == '/') {
+		// remove the leading "./"
+		char *fromBufPtr = strBuffer + 2;
+		while (true) {
+		    *strBuffer = *fromBufPtr;
+		    if (*fromBufPtr == '\0') break;
+		    strBuffer++;
+		    fromBufPtr++;
+		}
+	    }
+	} else {
+	    strcpy(strBuffer, "[can't get path: CFURLGetFileSystemRepresentation failed]");
+	}
+    }
+    return isRemote;
+}
+
 void printPathFromURL(CFURLRef url, FILE *stream) {
-    CFStringRef scheme, pathOrURL;
     static char strBuffer[STRBUF_LEN];
-    
     check(url != NULL && stream != NULL);
-
-    scheme = CFURLCopyScheme(url);
-    
-    if (CFEqual(scheme, CFSTR("file")))
-        pathOrURL = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
-    else
-        pathOrURL = CFURLGetString(url);
-
-    strBuffer[0] = '\0';
-    CFStringGetCString(pathOrURL, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX buffer size issues?
+    stringFromURLIsRemote(url, strBuffer);
     fprintf(stream, "%s\n", strBuffer);
-    CFRelease(scheme);
-    CFRelease(pathOrURL);
 }
 
@@ -582,5 +608,10 @@
 
     if (fscInfo.nodeFlags & kFSNodeIsDirectoryMask) {
-        printf("\tcontents: %lu item%s\n", fscInfo.valence, fscInfo.valence != 1 ? "s" : "");
+        printf("\tcontents: ");
+	switch (fscInfo.valence) {
+	case 0: printf("zero items\n"); break;
+	case 1: printf("1 item\n"); break;
+	default: printf("%lu items\n", fscInfo.valence);
+	}
     } else {
         printSizes("data fork size", fscInfo.dataLogicalSize, fscInfo.dataPhysicalSize, true);
@@ -605,26 +636,37 @@
 }
 
+const char *utf8StringFromCFStringRef(CFStringRef cfStr) {
+    static char tmpBuffer[STRBUF_LEN];
+    CFStringGetCString(cfStr, tmpBuffer, STRBUF_LEN, kCFStringEncodingUTF8);
+    return tmpBuffer;
+}
+
+const char *utf8StringFromOSType(OSType osType) {
+    CFStringRef typeStr = CFStringCreateWithBytes(NULL, (const char *)&osType, 4, CFStringGetSystemEncoding(), false);
+    if (typeStr == NULL) {
+	// punt to displaying verbatim
+	static char tmpBuffer[4];
+	tmpBuffer[4] = '\0';
+	strncpy(tmpBuffer, (const char *)&osType, 4);
+	return tmpBuffer;
+    }
+    const char *buffer = utf8StringFromCFStringRef(typeStr);
+    CFRelease(typeStr);
+    return buffer;
+}
+
 // 'context' is to match prototype for CFArrayApplierFunction, it's unused
 void printInfoFromURL(CFURLRef url, void *context) {
-    CFStringRef scheme, pathOrURL, kind;
-    Boolean isRemote;
-    static char strBuffer[STRBUF_LEN], tmpBuffer[STRBUF_LEN];
+    CFStringRef kind;
+    static char strBuffer[STRBUF_LEN];
     
     check(url != NULL && context == NULL);
 
-    scheme = CFURLCopyScheme(url);
-    
-    isRemote = !CFEqual(scheme, CFSTR("file"));
-    if (isRemote)
-        pathOrURL = CFURLGetString(url);
-    else
-        pathOrURL = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
-
-    strBuffer[0] = '\0';
-    CFStringGetCString(pathOrURL, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX buffer size issues?
-    if (isRemote)
+    if (stringFromURLIsRemote(url, strBuffer))
         printf("<%s>: URL\n", strBuffer);
     else {
         static LSItemInfoRecord info;
+	CFStringRef version = NULL;
+	UInt32 intVersion = 0;
         OSStatus err = LSCopyItemInfoForURL(url, kLSRequestAllInfo, &info);
         if (err != noErr) osstatusexit(err, "unable to get information about '%s'", strBuffer);
@@ -655,7 +697,6 @@
         printf("\n");
         if (!(info.flags & kLSItemInfoIsContainer) || info.flags & kLSItemInfoIsPackage) {
-            tmpBuffer[4] = '\0';
-            strncpy(tmpBuffer, (char *)&info.filetype, 4); printf("\ttype: '%s'", tmpBuffer);
-            strncpy(tmpBuffer, (char *)&info.creator, 4); printf("\tcreator: '%s'\n", tmpBuffer);
+	    printf("\ttype: '%s'", utf8StringFromOSType(info.filetype));
+	    printf("\tcreator: '%s'\n", utf8StringFromOSType(info.creator));
         }
         if (info.flags & kLSItemInfoIsPackage || info.flags & kLSItemInfoIsApplication) {
@@ -663,6 +704,4 @@
             CFBundleRef bundle = CFBundleCreate(NULL, url);
             CFStringRef bundleID = NULL;
-            CFStringRef appVersion = NULL;
-            UInt32 intVersion = 0;
             if (bundle == NULL && (info.flags & kLSItemInfoIsApplication)) {
                 FSRef fsr;
@@ -683,10 +722,12 @@
                             CFPropertyListRef infoPlist = CFPropertyListCreateFromXMLData(NULL, plstData, kCFPropertyListImmutable, &error);
 			    if (plstData != NULL) {
-			        CFRelease(plstData);
-			        plstData = NULL;
+				CFRelease(plstData);
+				plstData = NULL;
+			    } else {
+				// 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
+				infoPlist = CFBundleCopyInfoDictionaryForURL(url);
 			    }
                             if (infoPlist == NULL) {
-                                CFStringGetCString(error, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding());
-                                printf("\t['plst' 0 resource invalid: %s]\n", tmpBuffer);
+                                printf("\t['plst' 0 resource invalid: %s]\n", utf8StringFromCFStringRef(error));
                                 CFRelease(error);
                             } else {
@@ -694,8 +735,8 @@
                                 bundleID = CFDictionaryGetValue(infoPlist, kCFBundleIdentifierKey);
                                 if (bundleID != NULL) CFRetain(bundleID);
-                                appVersion = CFDictionaryGetValue(infoPlist, CFSTR("CFBundleShortVersionString"));
-                                if (appVersion == NULL)
-                                    appVersion = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey);
-                                if (appVersion != NULL) CFRetain(appVersion);
+                                version = CFDictionaryGetValue(infoPlist, CFSTR("CFBundleShortVersionString"));
+                                if (version == NULL)
+                                    version = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey);
+                                if (version != NULL) CFRetain(version);
                                 CFRelease(infoPlist);
                             }
@@ -705,6 +746,6 @@
                             if (err != noErr && err != resNotFound) osstatusexit(err, "unable to read 'vers' 1 resource");
                         } else {
-                            if (appVersion == NULL) { // prefer 'plst' version
-                                appVersion = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead?
+                            if (version == NULL) { // prefer 'plst' version
+                                version = CFStringCreateWithPascalString(NULL, vers[0]->shortVersion, CFStringGetSystemEncoding()); // XXX use country code instead?
                             }
                             intVersion = ((NumVersionVariant)vers[0]->numericVersion).whole;
@@ -717,9 +758,9 @@
                 if (bundleID != NULL) CFRetain(bundleID);
 		// prefer a short version string, e.g. "1.0 Beta" instead of "51" for Safari
-                appVersion = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"));
-		if (appVersion == NULL)
-		    appVersion = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
-                if (appVersion != NULL) {
-                    CFRetain(appVersion);
+                version = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"));
+		if (version == NULL)
+		    version = CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
+                if (version != NULL) {
+                    CFRetain(version);
                     intVersion = CFBundleGetVersionNumber(bundle);
                 }
@@ -727,27 +768,37 @@
             }
             if (bundleID != NULL) {
-                CFStringGetCString(bundleID, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding());
-                printf("\tbundle ID: %s\n", tmpBuffer);
+                printf("\tbundle ID: %s\n", utf8StringFromCFStringRef(bundleID));
                 CFRelease(bundleID);
             }
-            if (appVersion != NULL) {
-                CFStringGetCString(appVersion, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding());
-                printf("\tversion: %s", tmpBuffer);
-                if (intVersion != 0) printf(" [0x%lx = %lu]", intVersion, intVersion);
-                putchar('\n');
-                CFRelease(appVersion);
-            }
-        }
-        
+        } else {
+	    // try to get a version if we can, but don't complain if we can't
+	    FSRef fsr;
+	    if (CFURLGetFSRef(url, &fsr)) {
+		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);
+	    }
+	}
+	
+	if (version != NULL) {
+	    printf("\tversion: %s", utf8StringFromCFStringRef(version));
+	    if (intVersion != 0) printf(" [0x%lx = %lu]", intVersion, intVersion);
+	    putchar('\n');
+	    CFRelease(version);
+	}
+
         // kind string
         err = LSCopyKindStringForURL(url, &kind);
         if (err != noErr) osstatusexit(err, "unable to get kind of '%s'", strBuffer);
-        CFStringGetCString(kind, tmpBuffer, STRBUF_LEN, CFStringGetSystemEncoding());
-        printf("\tkind: %s\n", tmpBuffer);
+        printf("\tkind: %s\n", utf8StringFromCFStringRef(kind));
 	CFRelease(kind);
         printMoreInfoFromURL(url);
     }
-    CFRelease(scheme);
-    CFRelease(pathOrURL);
 }
 
@@ -760,5 +811,5 @@
 
     strBuffer[0] = '\0';
-    CFStringGetCString(urlStr, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX buffer size issues?
+    CFStringGetCString(urlStr, strBuffer, STRBUF_LEN, CFStringGetSystemEncoding()); // XXX no idea what encoding ICLaunchURL is supposed to take; leave as is for now
     strStart = 0;
     strEnd = strlen(strBuffer);
