Now looks at multiple XML tags to determine product version in MacOS's
authorRyan C. Gordon <icculus@icculus.org>
Tue, 25 May 2004 12:22:09 +0000
changeset 8 013c7889f09e
parent 7 d9d436b50ca7
child 9 4a96fabb7b79
Now looks at multiple XML tags to determine product version in MacOS's Info.plist. Furthermore, if MacOS fails to find the package, it can prompt the user to locate the product, so there're some new entry points in the UI abstraction. Also added a cheezy means to check multiple versions, such as: "1.0 or 1.1 or 1.2" Obviously, that needs more robustness.
mojopatch.c
platform.h
platform_unix.c
ui.h
ui_carbon.c
--- a/mojopatch.c	Tue May 25 11:23:33 2004 +0000
+++ b/mojopatch.c	Tue May 25 12:22:09 2004 +0000
@@ -538,6 +538,26 @@
 } /* _current_operation */
 
 
+/* don't taunt this function. */
+int version_ok(const char *ver, const char *allowed_ver)
+{
+    char *ptr;
+    char *buf = (char *) alloca(strlen(allowed_ver) + 1);
+    strcpy(buf, allowed_ver);
+
+    while ((ptr = strstr(buf, " or ")) != NULL)
+    {
+        *ptr = '\0';
+        if (strcmp(ver, buf) == 0)
+            return(1);
+
+        buf = ptr + 4;
+    } /* while */
+
+    return(strcmp(ver, buf) == 0);
+} /* version_ok */
+
+
 static int _do_xdelta(const char *fmt, ...)
 {
     char buf[512];
--- a/platform.h	Tue May 25 11:23:33 2004 +0000
+++ b/platform.h	Tue May 25 12:22:09 2004 +0000
@@ -46,6 +46,8 @@
 /* Call this for logging (debug info). */
 void _dlog(const char *fmt, ...);
 
+int version_ok(const char *ver, const char *allowed);
+
 /* platform-specific stuff you implement. */
 int file_exists(const char *fname);
 int file_is_directory(const char *fname);
--- a/platform_unix.c	Tue May 25 11:23:33 2004 +0000
+++ b/platform_unix.c	Tue May 25 12:22:09 2004 +0000
@@ -220,8 +220,17 @@
             continue;
         } /* if */
 
-        if ((strcasecmp(tag,"key")==0)&&(strcasecmp(val,"CFBundleVersion")==0))
-            have_key = 1;
+        /* You should only use CFBundleShortVersionString, for various
+         *  reasons not worth explaining here. CFBundleVersion is here
+         *  for older products that need to update to the other tag.
+         */
+        if (strcasecmp(tag,"key") == 0)
+        {
+            if (strcasecmp(val,"CFBundleVersion") == 0)
+                have_key = 1;
+            if (strcasecmp(val,"CFBundleShortVersionString") == 0)
+                have_key = 1;
+        } /* if */
     } /* while */
     
     return(NULL);
@@ -245,10 +254,28 @@
     io = NULL;
     mem[fsize] = '\0';
 
+    ptr = find_info_plist_bundle_id(mem);
+    if ((ptr == NULL) || (strcasecmp(ptr, ident) != 0))
+    {
+        int yes = ui_prompt_ny("We don't think we're looking at the right directory!"
+                               " Are you SURE this is the right place?"
+                               " If you aren't sure, clicking 'Yes' can destroy unrelated files!");
+        if (!yes)
+        {
+            _fatal("Stopping at user's request.");
+            free(mem);
+            return(0);
+        } /* if */
+    } /* if */
+
+    if ( (io = fopen(fname, "r")) == NULL ) goto parse_info_plist_bailed;
+    if ( (fread(mem, fsize, 1, io)) != 1 ) goto parse_info_plist_bailed;
+    fclose(io);
+
     ptr = find_info_plist_version(mem);
     if (ptr != NULL)
     {
-        if (strcmp(version, ptr) == 0)
+        if (version_ok(ptr, version))
             retval = 1;
         else
         {
@@ -311,6 +338,8 @@
 } /* update_version */
 
 
+int manually_locate_product(char *buf, size_t bufsize);
+
 int chdir_by_identifier(const char *str, const char *version)
 {
     char buf[MAXPATHLEN];
@@ -322,18 +351,24 @@
 
     rc = LSFindApplicationForInfo(kLSUnknownCreator, id, NULL, NULL, &url);
     CFRelease(id);
-    if (rc != noErr)
+    if (rc == noErr)
     {
-        _fatal("Couldn't find product. Perhaps it isn't installed?");
-        return(0);
+        b = CFURLGetFileSystemRepresentation(url, TRUE, buf, sizeof (buf));
+        CFRelease(url);
+        if (!b)
+        {
+            _fatal("Internal error.");
+            return(0);
+        } /* if */
     } /* if */
-
-    b = CFURLGetFileSystemRepresentation(url, TRUE, buf, sizeof (buf));
-    CFRelease(url);
-    if (!b)
+    else
     {
-        _fatal("Internal error.");
-        return(0);
+        _log("Couldn't find product. Perhaps it isn't installed?");
+        if (!manually_locate_product(buf, sizeof (buf)))
+        {
+            _fatal("We can't patch the product if we can't find it!");
+            return(0);
+        } /* if */
     } /* if */
 
     _log("I think the product is installed at [%s].", buf);
@@ -344,10 +379,7 @@
         return(0);
     } /* if */
 
-    if (strcmp(version, "") != 0)
-        return(parse_info_dot_plist(version));
-
-    return(1);
+    return(parse_info_dot_plist(str, version));
 } /* chdir_by_identifier */
 
 
--- a/ui.h	Tue May 25 11:23:33 2004 +0000
+++ b/ui.h	Tue May 25 12:22:09 2004 +0000
@@ -16,6 +16,8 @@
 void ui_total_progress(int percent);
 void ui_status(const char *str);
 void ui_title(const char *str);
+int ui_prompt_yn(const char *question);
+int ui_prompt_ny(const char *question);
 
 #ifdef __cplusplus
 }
--- a/ui_carbon.c	Tue May 25 11:23:33 2004 +0000
+++ b/ui_carbon.c	Tue May 25 12:22:09 2004 +0000
@@ -68,20 +68,26 @@
 } /* ui_add_to_log */
 
 
-static void do_msgbox(const char *str, AlertType alert_type)
+static int do_msgbox(const char *str, AlertType alert_type,
+                     AlertStdCFStringAlertParamRec *param,
+                     DialogItemIndex *idx)
 {
     const char *_title = "MojoPatch";
+    int retval = 0;
+    DialogItemIndex val = 0;
     CFStringRef title = CFStringCreateWithBytes(NULL, _title, strlen(_title),
                                                 kCFStringEncodingISOLatin1, 0);
     CFStringRef msg = CFStringCreateWithBytes(NULL, str, strlen(str),
                                                 kCFStringEncodingISOLatin1, 0);
     if ((msg != NULL) && (title != NULL))
     {
-        DialogItemIndex val = 0;
         DialogRef dlg = NULL;
 
-        if (CreateStandardAlert(alert_type, title, msg, NULL, &dlg) == noErr)
-            RunStandardAlert(dlg, NULL, &val);
+        if (CreateStandardAlert(alert_type, title, msg, param, &dlg) == noErr)
+        {
+            RunStandardAlert(dlg, NULL, (idx) ? idx : &val);
+            retval = 1;
+        } /* if */
     } /* if */
 
     if (msg != NULL)
@@ -89,18 +95,114 @@
 
     if (title != NULL)
         CFRelease(title);
+
+    return(retval);
 } /* do_msgbox */
 
 
+static int ui_prompt_yes_or_no(const char *question, int yes)
+{
+    OSStatus err;
+    DialogItemIndex item;
+    AlertStdCFStringAlertParamRec params;
+    err = GetStandardAlertDefaultParams(&params, kStdCFStringAlertVersionOne);
+    if (err != noErr)
+        return(0);
+
+    params.movable = TRUE;
+    params.helpButton = FALSE;
+    params.defaultText = CFSTR("Yes");
+    params.cancelText = CFSTR("No");
+    params.defaultButton = (yes) ? kAlertStdAlertOKButton :
+                                   kAlertStdAlertCancelButton;
+    params.cancelButton = kAlertStdAlertCancelButton;
+    if (!do_msgbox(question, kAlertCautionAlert, &params, &item))
+        return(0); /* oh well. */
+
+    return(item == kAlertStdAlertOKButton);
+} /* ui_prompt_yes_or_no */
+
+int ui_prompt_yn(const char *question)
+{
+    return(ui_prompt_yes_or_no(question, 1));
+} /* ui_prompt_yn */
+
+int ui_prompt_ny(const char *question)
+{
+    return(ui_prompt_yes_or_no(question, 1));  /* !!! FIXME! should be zero. */
+} /* ui_prompt_ny */
+
+
+int manually_locate_product(char *buf, size_t bufsize)
+{
+    NavDialogCreationOptions dlgopt;
+    NavDialogRef dlg;
+    NavReplyRecord reply;
+    NavUserAction action;
+    AEKeyword keyword;
+    AEDesc desc;
+    FSRef fsref;
+    OSStatus rc;
+    int retval = 0;
+    int yn;
+
+    yn = ui_prompt_yn("We can't find your installation."
+                      " Would you like to show us where it is?");
+    if (!yn)
+    {
+        _log("User chose not to manually locate installation");
+        return(0);
+    } /* if */
+
+    _log("Creating file selector dialog...");
+    NavGetDefaultDialogCreationOptions(&dlgopt);
+    dlgopt.optionFlags |= kNavSupportPackages;
+    dlgopt.optionFlags |= kNavAllowOpenPackages;
+    dlgopt.optionFlags &= ~kNavAllowMultipleFiles;
+    dlgopt.windowTitle = CFSTR("Please select the product's icon and click 'OK'.");
+    dlgopt.actionButtonLabel = CFSTR("OK");
+    NavCreateChooseFolderDialog(&dlgopt, NULL, NULL, NULL, &dlg);
+    NavDialogRun(dlg);
+    action = NavDialogGetUserAction(dlg);
+    if (action == kNavUserActionCancel)
+        _log("User cancelled file selector!");
+    else
+    {
+        NavDialogGetReply(dlg, &reply);
+        rc = AEGetNthDesc(&reply.selection, 1, typeFSRef, &keyword, &desc);
+        if (rc != noErr)
+            _fatal("Unexpected error in AEGetNthDesc: %d\n", (int) rc);
+        else
+        {
+            /* !!! FIXME: Check return values here! */
+            BlockMoveData(*desc.dataHandle, &fsref, sizeof (fsref));
+            FSRefMakePath(&fsref, buf, bufsize - 1);
+            buf[bufsize - 1] = '\0';
+            AEDisposeDesc(&desc);
+            retval = 1;
+        } /* if */
+
+        NavDisposeReply(&reply);
+    } /* else */
+
+    NavDialogDispose(dlg);
+
+    _log("File selector complete. User %s path.",
+            retval ? "selected" : "did NOT select");
+
+    return(retval);
+}
+
+
 void ui_fatal(const char *str)
 {
-    do_msgbox(str, kAlertStopAlert);
+    do_msgbox(str, kAlertStopAlert, NULL, NULL);
 } /* ui_fatal */
 
 
 void ui_success(const char *str)
 {
-    do_msgbox(str, kAlertNoteAlert);
+    do_msgbox(str, kAlertNoteAlert, NULL, NULL);
 } /* ui_success */