From 999b6130a432eec11ea9d2f91747dc418285f0dc Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Thu, 18 Apr 2019 01:02:40 -0500 Subject: [PATCH] Rearrange/refactor DiskBrowser code into smaller, more focused files. There are only a few minor code changes, and nothing that should have an effect on functionality. This is basically just rearranging the code. --- Makefile | 2 +- asprintf.c | 1 + browserevents.c | 146 ++++++++++ browserevents.h | 6 + browserutil.c | 77 ++++++ browserutil.h | 7 + browserwindow.c | 211 +++++++++++++++ browserwindow.h | 8 + diskbrowser.c | 707 +++--------------------------------------------- diskbrowser.h | 53 ++++ diskmount.c | 147 ++++++++++ diskmount.h | 7 + disksearch.c | 166 ++++++++++++ disksearch.h | 6 + json.c | 4 + jsonutil.c | 4 + 16 files changed, 888 insertions(+), 664 deletions(-) create mode 100644 browserevents.c create mode 100644 browserevents.h create mode 100644 browserutil.c create mode 100644 browserutil.h create mode 100644 browserwindow.c create mode 100644 browserwindow.h create mode 100644 diskbrowser.h create mode 100644 diskmount.c create mode 100644 diskmount.h create mode 100644 disksearch.c create mode 100644 disksearch.h diff --git a/Makefile b/Makefile index 25026dc..4ac90a7 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ JSONTEST_PROG = jsontest HTTPTEST_OBJS = httptest.a hostname.a http.a readtcp.a seturl.a strcasecmp.a tcpconnection.a urlparser.a HTTPTEST_PROG = httptest -DISKBROWSER_OBJS = diskbrowser.a hostname.a http.a json.a jsonutil.a readtcp.a seturl.a strcasecmp.a tcpconnection.a urlparser.a asprintf.a +DISKBROWSER_OBJS = diskbrowser.a browserevents.a browserwindow.a browserutil.a diskmount.a disksearch.a hostname.a http.a json.a jsonutil.a readtcp.a seturl.a strcasecmp.a tcpconnection.a urlparser.a asprintf.a DISKBROWSER_RSRC = diskbrowser.rez DISKBROWSER_PROG = DiskBrowser diff --git a/asprintf.c b/asprintf.c index 3afec30..78efca8 100644 --- a/asprintf.c +++ b/asprintf.c @@ -1,5 +1,6 @@ #ifdef __ORCAC__ #pragma optimize -1 /* must be at least 8 */ +#pragma noroot #endif #include diff --git a/browserevents.c b/browserevents.c new file mode 100644 index 0000000..450b27d --- /dev/null +++ b/browserevents.c @@ -0,0 +1,146 @@ +/********************************************************************* + * Event handling + *********************************************************************/ + +#ifdef __ORCAC__ +#pragma noroot +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "json.h" + +#include "diskbrowser.h" +#include "browserevents.h" +#include "browserwindow.h" +#include "disksearch.h" +#include "diskmount.h" + +static void HandleEvent(int eventCode, WmTaskRec *taskRec); +static boolean DoLEEdit (int editAction); + +/* NDA-style action routine for our window */ +#pragma databank 1 +static int ActionProc(EventRecord *eventRec, int actionCode) { + static WmTaskRec taskRec; + int handledAction = 0; + + if (!windowOpened) + return 0; + + GrafPortPtr port = GetPort(); + SetPort(window); + + switch (actionCode) { + case eventAction: + /* Copy basic event rec & use our own wmTaskMask, as per IIgs TN 84 */ + memset(&taskRec, sizeof(taskRec), 0); + memmove(&taskRec, eventRec, 16); + taskRec.wmTaskMask = 0x1F7FFF; /* everything except tmInfo */ + + HandleEvent(TaskMasterDA(0, &taskRec), &taskRec); + UpdateControlState(); + break; + + case cursorAction: + break; + + case cutAction: + case copyAction: + case pasteAction: + case clearAction: + handledAction = DoLEEdit(actionCode); + break; + } + + SetPort(port); + + return handledAction; +} +#pragma databank 0 + +asm void actionProcWrapper(void) { + pha + phy + phx + jsl ActionProc + rtl +} + +/* Handle an event after TaskMasterDA processing */ +static void HandleEvent(int eventCode, WmTaskRec *taskRec) { + switch (eventCode) { + case wInControl: + switch (taskRec->wmTaskData4) { + case searchButton: + DoSearch(); + break; + + case forIIGSRadio: + gsDisksOnly = true; + break; + case forAnyAppleIIRadio: + gsDisksOnly = false; + break; + + case mountDiskButton: + DoMount(); + break; + } + break; + + case keyDownEvt: + case autoKeyEvt: + /* Handle keyboard shortcuts for cut/copy/paste */ + if (taskRec->modifiers & appleKey) { + switch (taskRec->message & 0x000000FF) { + case 'x': case 'X': + DoLEEdit(cutAction); + break; + case 'c': case 'C': + DoLEEdit(copyAction); + break; + case 'v': case 'V': + DoLEEdit(pasteAction); + break; + } + } + break; + } +} + +static boolean DoLEEdit (int editAction) { + CtlRecHndl ctl; /* target control handle */ + unsigned long id; /* control ID */ + GrafPortPtr port; /* caller's GrafPort */ + + ctl = FindTargetCtl(); + id = GetCtlID(ctl); + if (id == searchLine) { + LEFromScrap(); + switch (editAction) { + case cutAction: + LECut((LERecHndl) GetCtlTitle(ctl)); + LEToScrap(); + break; + case copyAction: + LECopy((LERecHndl) GetCtlTitle(ctl)); + LEToScrap(); + break; + case pasteAction: + LEPaste((LERecHndl) GetCtlTitle(ctl)); + break; + case clearAction: + LEDelete((LERecHndl) GetCtlTitle(ctl)); + break; + }; + }; + return (id == searchLine); +} diff --git a/browserevents.h b/browserevents.h new file mode 100644 index 0000000..897da10 --- /dev/null +++ b/browserevents.h @@ -0,0 +1,6 @@ +#ifndef BROWSEREVENTS_H +#define BROWSEREVENTS_H + +asm void actionProcWrapper(void); + +#endif diff --git a/browserutil.c b/browserutil.c new file mode 100644 index 0000000..6240a23 --- /dev/null +++ b/browserutil.c @@ -0,0 +1,77 @@ +/********************************************************************* + * Utilities for disk mounting & searching + *********************************************************************/ + +#ifdef __ORCAC__ +#pragma noroot +#endif + +#include + +#include + +#include "session.h" +#include "seturl.h" +#include "http.h" +#include "readtcp.h" +#include "tcpconnection.h" +#include "json.h" + + +void ShowErrorAlert(enum NetDiskError error, int alertNumber) { + char numStr[6] = ""; + char *subs[1] = {numStr}; + + snprintf(numStr, sizeof(numStr), "%u", error); + + AlertWindow(awResource+awCString+awButtonLayout, + (Pointer)subs, alertNumber); +} + +enum NetDiskError ReadJSONFromURL(char *url, json_value** jsonResult) { + static Session sess = {0}; + + enum NetDiskError result = OPERATION_SUCCESSFUL; + char *netBuf = NULL; /* Buffer for data received from network */ + + *jsonResult = NULL; + + result = SetURL(&sess, url, FALSE, FALSE); + if (result != OPERATION_SUCCESSFUL) + goto errorReturn; + + result = DoHTTPRequest(&sess); + if (result != OPERATION_SUCCESSFUL) + goto errorReturn; + + /* Limit content to <64k, to avoid any problems with 16-bit computations */ + if (sess.contentLength == 0 || sess.contentLength > 0xffff) + sess.contentLength = 0xffff; + + netBuf = malloc(sess.contentLength + 1); + if (netBuf == NULL) { + result = OUT_OF_MEMORY; + goto errorReturn; + } + + InitReadTCP(&sess, sess.contentLength, netBuf); + while (TryReadTCP(&sess) == rsWaiting) + /* keep reading */ ; + sess.contentLength -= sess.readCount; + *(netBuf + sess.contentLength) = 0; + if (sess.contentLength == 0) { + result = NO_RESPONSE; + goto errorReturn; + } + + *jsonResult = json_parse(netBuf, sess.contentLength); + if (*jsonResult == NULL) { + result = JSON_PARSING_ERROR; + goto errorReturn; + } + +errorReturn: + free(netBuf); + EndTCPConnection(&sess); + return result; +} diff --git a/browserutil.h b/browserutil.h new file mode 100644 index 0000000..51c3347 --- /dev/null +++ b/browserutil.h @@ -0,0 +1,7 @@ +#ifndef BROWSERUTIL_H +#define BROWSERUTIL_H + +void ShowErrorAlert(enum NetDiskError error, int alertNumber); +enum NetDiskError ReadJSONFromURL(char *url, json_value** jsonResult); + +#endif diff --git a/browserwindow.c b/browserwindow.c new file mode 100644 index 0000000..bf67699 --- /dev/null +++ b/browserwindow.c @@ -0,0 +1,211 @@ +/********************************************************************* + * Disk browser window handling + *********************************************************************/ + +#ifdef __ORCAC__ +#pragma noroot +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "json.h" + +#include "browserwindow.h" +#include "diskbrowser.h" +#include "browserevents.h" + +/* Title of browser window */ +static char windowTitle[] = "\p Archive.org Disk Browser "; + +/* Rectangles outlining the buttons in the style of "default" buttons */ +static Rect searchRect = {8, 305, 26, 414}; +static Rect mountRect = {150, 301, 168, 414}; + +/* Has the resource file been opened? (True while window is displayed.) */ +static Boolean resourceFileOpened; + +/* ID of our resource file */ +static Word resourceFileID; + +/* Record about our system window, to support processing by Desk Manager. */ +/* See Prog. Ref. for System 6.0, page 20. */ +static NDASysWindRecord sysWindRecord; + +/* Is the search button (as opposed to mount) the current default? */ +static Boolean defaultButtonIsSearch; + +/* Search and mount button controls */ +static CtlRecHndl mountButtonHandle; +static CtlRecHndl searchButtonHandle; + +/* Target control ID last time we checked */ +static unsigned long lastTargetCtlID = 0; + + +static void DrawContents(void); +static void DrawButtonOutlines(boolean justChanged); + + +void ShowBrowserWindow(void) { + if (windowOpened) { + BringToFront(window); + return; + } + + Word origResourceApp = GetCurResourceApp(); + SetCurResourceApp(myUserID); + + resourceFileID = + OpenResourceFile(readEnable, NULL, LGetPathname2(myUserID, 1)); + if (toolerror()) { + resourceFileID = 0; + resourceFileOpened = false; + goto cleanup; + } + resourceFileOpened = true; + + window = NewWindow2(windowTitle, 0, DrawContents, NULL, + refIsResource, winDiskBrowser, rWindParam1); + if (toolerror()) { + window = NULL; + windowOpened = false; + goto cleanup; + } + windowOpened = true; + + SetSysWindow(window); + + AuxWindInfoRecord *auxWindInfo = GetAuxWindInfo(window); + if (toolerror()) { + CloseWindow(window); + windowOpened = false; + window = NULL; + goto cleanup; + } + + memset(&sysWindRecord, sizeof(sysWindRecord), 0); + sysWindRecord.closeProc = (ProcPtr)CloseBrowserWindow; + sysWindRecord.actionProc = (ProcPtr)actionProcWrapper; + sysWindRecord.eventMask = 0xFFFF; //0x03FF; + sysWindRecord.memoryID = myUserID; + auxWindInfo->NDASysWindPtr = (Ptr)&sysWindRecord; + + disksListHandle = GetCtlHandleFromID(window, disksList); + mountButtonHandle = GetCtlHandleFromID(window, mountDiskButton); + searchButtonHandle = GetCtlHandleFromID(window, searchButton); + + HiliteControl(inactiveHilite, disksListHandle); + HiliteControl(inactiveHilite, mountButtonHandle); + + lastTargetCtlID = 0; + defaultButtonIsSearch = true; + + wantToOpenWindow = 0; + +cleanup: + if (resourceFileOpened && !windowOpened) { + CloseResourceFile(resourceFileID); + resourceFileOpened = false; + } + + SetCurResourceApp(origResourceApp); +} + + +#pragma databank 1 +void CloseBrowserWindow(void) { + if (windowOpened) { + CloseWindow(window); + windowOpened = false; + window = NULL; + + /* reset state */ + gsDisksOnly = true; + } + + if (resourceFileOpened) { + CloseResourceFile(resourceFileID); + resourceFileOpened = false; + } + + if (json) { + json_value_free(json); + json = NULL; + } +} +#pragma databank 0 + + +#pragma databank 1 +static void DrawContents(void) { + Word origResourceApp = GetCurResourceApp(); + SetCurResourceApp(myUserID); + + PenNormal(); /* use a "normal" pen */ + DrawControls(GetPort()); /* draw controls in window */ + + DrawButtonOutlines(false); + + SetCurResourceApp(origResourceApp); +} +#pragma databank 0 + + +/* Draw outlines of buttons as default or not, as appropriate. */ +static void DrawButtonOutlines(boolean justChanged) { + PenNormal(); + SetPenSize(3, 1); + if (defaultButtonIsSearch) { + FrameRRect(&searchRect, 36, 12); + if (justChanged) { + SetSolidPenPat(white); + FrameRRect(&mountRect, 36, 12); + } + } else { + FrameRRect(&mountRect, 36, 12); + if (justChanged) { + SetSolidPenPat(white); + FrameRRect(&searchRect, 36, 12); + } + } +} + +/* Update state of controls based on user input */ +void UpdateControlState(void) { + unsigned long targetCtlID = GetCtlID(FindTargetCtl()); + if (targetCtlID != lastTargetCtlID) { + /* Make appropriate button respond to the "return" key */ + lastTargetCtlID = targetCtlID; + if (targetCtlID == searchLine) { + SetCtlMoreFlags(GetCtlMoreFlags(mountButtonHandle) & 0x1FFF, + mountButtonHandle); + SetCtlMoreFlags(GetCtlMoreFlags(searchButtonHandle) | fCtlWantEvents, + searchButtonHandle); + defaultButtonIsSearch = true; + } else if (targetCtlID == disksList) { + SetCtlMoreFlags(GetCtlMoreFlags(searchButtonHandle) & 0x1FFF, + searchButtonHandle); + SetCtlMoreFlags(GetCtlMoreFlags(mountButtonHandle) | fCtlWantEvents, + mountButtonHandle); + defaultButtonIsSearch = false; + } + DrawButtonOutlines(true); + } + + /* Only allow "Mount Disk" to be clicked if there is a disk selected */ + if (NextMember2(0, (Handle)disksListHandle) != 0) { + HiliteControl(noHilite, mountButtonHandle); + } else { + HiliteControl(inactiveHilite, mountButtonHandle); + } +} diff --git a/browserwindow.h b/browserwindow.h new file mode 100644 index 0000000..d89eda0 --- /dev/null +++ b/browserwindow.h @@ -0,0 +1,8 @@ +#ifndef BROWSERWINDOW_H +#define BROWSERWINDOW_H + +void UpdateControlState(void); +void ShowBrowserWindow(void); +void CloseBrowserWindow(void); + +#endif diff --git a/diskbrowser.c b/diskbrowser.c index 7690f1f..daed6b6 100644 --- a/diskbrowser.c +++ b/diskbrowser.c @@ -1,115 +1,63 @@ +/********************************************************************* + * Disk browser main program & initialization + *********************************************************************/ + #pragma rtl -#include +#include #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include "session.h" + +#include + #include "mounturl.h" -#include "seturl.h" -#include "http.h" -#include "readtcp.h" -#include "tcpconnection.h" -#include "asprintf.h" #include "json.h" -#include "jsonutil.h" -static char menuTitle[] = "\pArchive.org Disk Browser\xC9"; -static char windowTitle[] = "\p Archive.org Disk Browser "; - -char diskbrowserRequestName[] = "\pSTH~DiskBrowser~"; -char finderRequestName[] = "\pApple~Finder~"; - -#define winDiskBrowser 1001 - -#define searchLine 1002 -#define searchButton 1003 -#define findDisksForText 1004 -#define forIIGSRadio 1005 -#define forAnyAppleIIRadio 1006 -#define disksList 1007 -#define mountDiskButton 1008 - -#define searchErrorAlert 3000 -#define mountErrorAlert 3001 - -Word resourceFileID; - -/* ID of our menu item in the Finder (or 0 if we're not active) */ -Word menuItemID = 0; - -Word myUserID; +#include "diskbrowser.h" +#include "browserevents.h" +#include "browserwindow.h" +#include "diskmount.h" +/* The browser window */ GrafPortPtr window; -CtlRecHndl disksListHandle; -CtlRecHndl mountButtonHandle; -CtlRecHndl searchButtonHandle; +/* Is the window open? */ +Boolean windowOpened; -/* Rectangles outlining the buttons in the style of "default" buttons */ -static Rect searchRect = {8, 305, 26, 414}; -static Rect mountRect = {150, 301, 168, 414}; - -Boolean defaultButtonIsSearch; - -unsigned long lastTargetCtlID = 0; - -Boolean resourceFileOpened, windowOpened; - -/* User preference */ +/* User preference: IIGS disks or all Apple II disks? */ boolean gsDisksOnly = true; -char queryBuf[257]; - -int pageNum = 0; - -#define DISK_LIST_LENGTH 30 - -struct diskListEntry { - char *memPtr; - Byte memFlag; - char *idPtr; - char *extPtr; -}; +/* Disks list control */ +CtlRecHndl disksListHandle; +/* List of disks, used for list control & mounting disks */ struct diskListEntry diskList[DISK_LIST_LENGTH]; -int diskListPos = 0; - -char *wantedExt; -char *wantedItemID; - -static struct MountURLRec mountURLRec = {sizeof(struct MountURLRec)}; - -Session sess = {0}; - -/* Record about our system window, to support processing by Desk Manager. */ -/* See Prog. Ref. for System 6.0, page 20. */ -static NDASysWindRecord sysWindRecord; - -/* Buffer for data received from network */ -char *netBuf = NULL; +/* Do we want to open a window with disk contents? Counts down until ready. */ +int wantToOpenWindow = 0; +/* JSON for current disk list (needs to be kept in memory while it's shown) */ json_value *json = NULL; -int wantToOpenWindow = 0; -GSString32 devName; -struct tellFinderOpenWindowOut tfowOut; +/* User ID of this program */ +Word myUserID; + +/***/ + +static char menuTitle[] = "\pArchive.org Disk Browser\xC9"; + +static char diskbrowserRequestName[] = "\pSTH~DiskBrowser~"; + +/* ID of our menu item in the Finder (or 0 if we're not active) */ +static Word menuItemID = 0; + + +/********************************************************************* + * Window handling and initialization + *********************************************************************/ void InstallMenuItem(void) { static MenuItemTemplate menuItem = { @@ -125,7 +73,7 @@ void InstallMenuItem(void) { tellFinderAddToExtrasOut dataOutRec; SendRequest(tellFinderAddToExtras, sendToName|stopAfterOne, - (Long)&finderRequestName, + (Long)NAME_OF_FINDER, (Long)&menuItem, (Ptr)&dataOutRec); @@ -136,569 +84,6 @@ void InstallMenuItem(void) { } } -/* Draw outlines of buttons as default or not, as appropriate. */ -void DrawButtonOutlines(boolean justChanged) { - PenNormal(); - SetPenSize(3, 1); - if (defaultButtonIsSearch) { - FrameRRect(&searchRect, 36, 12); - if (justChanged) { - SetSolidPenPat(white); - FrameRRect(&mountRect, 36, 12); - } - } else { - FrameRRect(&mountRect, 36, 12); - if (justChanged) { - SetSolidPenPat(white); - FrameRRect(&searchRect, 36, 12); - } - } -} - -#pragma databank 1 -void DrawContents(void) { - Word origResourceApp = GetCurResourceApp(); - SetCurResourceApp(myUserID); - - PenNormal(); /* use a "normal" pen */ - DrawControls(GetPort()); /* draw controls in window */ - - DrawButtonOutlines(false); - - SetCurResourceApp(origResourceApp); -} -#pragma databank 0 - -#pragma databank 1 -void CloseBrowserWindow(void) { - if (windowOpened) { - CloseWindow(window); - windowOpened = false; - window = NULL; - - /* reset state */ - gsDisksOnly = true; - } - - if (resourceFileOpened && !windowOpened) { - CloseResourceFile(resourceFileID); - resourceFileOpened = false; - } - - free(netBuf); - netBuf = NULL; - - if (json) { - json_value_free(json); - json = NULL; - } - - EndTCPConnection(&sess); -} -#pragma databank 0 - -boolean DoLEEdit (int editAction) { - CtlRecHndl ctl; /* target control handle */ - unsigned long id; /* control ID */ - GrafPortPtr port; /* caller's GrafPort */ - - ctl = FindTargetCtl(); - id = GetCtlID(ctl); - if (id == searchLine) { - LEFromScrap(); - switch (editAction) { - case cutAction: - LECut((LERecHndl) GetCtlTitle(ctl)); - LEToScrap(); - break; - case copyAction: - LECopy((LERecHndl) GetCtlTitle(ctl)); - LEToScrap(); - break; - case pasteAction: - LEPaste((LERecHndl) GetCtlTitle(ctl)); - break; - case clearAction: - LEDelete((LERecHndl) GetCtlTitle(ctl)); - break; - }; - }; - return (id == searchLine); -} - -boolean processDoc(json_value *docObj) { - if (diskListPos >= DISK_LIST_LENGTH) - return false; - - if (docObj == NULL || docObj->type != json_object) - return false; - json_value *id = findEntryInObject(docObj, "identifier", json_string); - json_value *title = findEntryInObject(docObj, "title", json_string); - json_value *ext = findEntryInObject(docObj, "emulator_ext", json_string); - if (id == NULL || title == NULL || ext == NULL) - return true; - diskList[diskListPos].idPtr = id->u.string.ptr; - // TODO character set translation - diskList[diskListPos].memPtr = title->u.string.ptr; - diskList[diskListPos++].extPtr = ext->u.string.ptr; - return true; -} - -char *EncodeQueryString(char *queryString) { - long sizeNeeded; - - char *encodedStr = NULL; - sizeNeeded = strlen(queryString); - if (sizeNeeded > 10000) - return NULL; - encodedStr = malloc(sizeNeeded*3 + 1); - if (encodedStr == NULL) - return NULL; - - char *s = encodedStr; - char c; - while ((c = *queryString++) != '\0') { - if (c > 0x7F) { - //TODO character set translation - c = '*'; - } - if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9')) { - *s++ = c; - } else { - snprintf(s, 4, "%%%02X", (unsigned char)c); - s+= 3; - } - } - *s = '\0'; - return encodedStr; -} - -void ShowErrorAlert(enum NetDiskError error, int alertNumber) { - char numStr[6] = ""; - char *subs[1] = {numStr}; - - snprintf(numStr, sizeof(numStr), "%u", error); - - AlertWindow(awResource+awCString+awButtonLayout, - (Pointer)subs, alertNumber); -} - - -enum NetDiskError ReadJSONFromURL(char *url, json_value** jsonResult) { - enum NetDiskError result = OPERATION_SUCCESSFUL; - - *jsonResult = NULL; - - result = SetURL(&sess, url, FALSE, FALSE); - if (result != OPERATION_SUCCESSFUL) - goto errorReturn; - - result = DoHTTPRequest(&sess); - if (result != OPERATION_SUCCESSFUL) - goto errorReturn; - - /* Limit content to <64k, to avoid any problems with 16-bit computations */ - if (sess.contentLength == 0 || sess.contentLength > 0xffff) - sess.contentLength = 0xffff; - - free(netBuf); - netBuf = malloc(sess.contentLength + 1); - if (netBuf == NULL) { - result = OUT_OF_MEMORY; - goto errorReturn; - } - - InitReadTCP(&sess, sess.contentLength, netBuf); - while (TryReadTCP(&sess) == rsWaiting) - /* keep reading */ ; - sess.contentLength -= sess.readCount; - *(netBuf + sess.contentLength) = 0; - if (sess.contentLength == 0) { - result = NO_RESPONSE; - goto errorReturn; - } - - *jsonResult = json_parse(netBuf, sess.contentLength); - if (*jsonResult == NULL) { - result = JSON_PARSING_ERROR; - goto errorReturn; - } - -errorReturn: - free(netBuf); - netBuf = NULL; - EndTCPConnection(&sess); - return result; -} - -/* Do a search */ -void DoSearch(void) { - char *searchURL = NULL; - enum NetDiskError result = 0; - - WaitCursor(); - - GetLETextByID(window, searchLine, (StringPtr)&queryBuf); - - char *queryString = EncodeQueryString(queryBuf+1); - if (queryString == NULL) { - result = OUT_OF_MEMORY; - goto errorReturn; - } - - asprintf(&searchURL, - "http://archive.org/advancedsearch.php?" - "q=emulator%%3A%s%%20%s" - "&fl%%5B%%5D=identifier&fl%%5B%%5D=title" - "&fl%%5B%%5D=emulator_ext" - "&sort%%5B%%5D=titleSorterRaw+asc" - "&rows=%i&page=%i&output=json", - gsDisksOnly ? "apple2gs" : "apple2%2A", - queryString, - DISK_LIST_LENGTH, - pageNum); - if (searchURL == NULL) { - result = URL_TOO_LONG; - goto errorReturn; - } - free(queryString); - queryString = NULL; - - if (json) - json_value_free(json); - result = ReadJSONFromURL(searchURL, &json); - if (result != OPERATION_SUCCESSFUL) - goto errorReturn; - - json_value *response = findEntryInObject(json, "response", json_object); - if (response == NULL) { - result = NOT_EXPECTED_CONTENTS; - goto errorReturn; - } - - diskListPos = 0; - json_value *docs = findEntryInObject(response, "docs", json_array); - if (docs == NULL) { - result = NOT_EXPECTED_CONTENTS; - goto errorReturn; - } - processArray(docs, json_object, processDoc); - - diskList[0].memFlag = 0x80; - for (int i = 1; i < DISK_LIST_LENGTH; i++) { - diskList[i].memFlag = 0; - } - - /* Update state of controls once disk list is available */ - HiliteControl(noHilite, disksListHandle); - SetCtlMoreFlags( - GetCtlMoreFlags(disksListHandle) | fCtlCanBeTarget | fCtlWantEvents, - disksListHandle); - - NewList2(NULL, 1, (Ref) diskList, refIsPointer, - diskListPos, (Handle)disksListHandle); - - if (diskListPos > 0) { - if (FindTargetCtl() != disksListHandle) { - MakeThisCtlTarget(disksListHandle); - CallCtlDefProc(disksListHandle, ctlChangeTarget, 0); - } - } - - free(searchURL); - InitCursor(); - return; - -errorReturn: - NewList2(NULL, 1, (Ref) diskList, refIsPointer, 0, (Handle)disksListHandle); - free(queryString); - free(searchURL); - InitCursor(); - ShowErrorAlert(result, searchErrorAlert); -} - - -void MountFile(char *itemID, char *fileName) { - char *fileURL = NULL; - asprintf(&fileURL, "http://archive.org/download/%s/%s", itemID, fileName); - if (fileURL == NULL) { - SysBeep(); - return; - } - - mountURLRec.result = NETDISK_NOT_PRESENT; - mountURLRec.url = fileURL; - mountURLRec.flags = flgUseCache; - - SendRequest(MountURL, sendToName|stopAfterOne, (Long)NETDISK_REQUEST_NAME, - (Long)&mountURLRec, NULL); - - if (mountURLRec.result == OPERATION_SUCCESSFUL) { - devName.length = snprintf(devName.text, sizeof(devName.text), ".D%u", - mountURLRec.devNum); - wantToOpenWindow = 2; - } - - free(fileURL); -} - -boolean processFile(json_value *fileObj) { - if (fileObj == NULL || fileObj->type != json_object) - return false; - - json_value *name = findEntryInObject(fileObj, "name", json_string); - if (name == NULL) - return true; - - char *nameExt = strrchr(name->u.string.ptr, '.'); - if (nameExt == NULL) - return true; - if (strcmp(nameExt + 1, wantedExt) == 0) { - MountFile(wantedItemID, name->u.string.ptr); - return false; - } - - return true; -} - -void DoMount(void) { - unsigned int itemNumber = NextMember2(0, (Handle)disksListHandle); - char *filesURL = NULL; - enum NetDiskError result = 0; - json_value *filesJSON = NULL; - - WaitCursor(); - - if (itemNumber == 0) { - // shouldn't happen - result = 1; - goto errorReturn; - } - itemNumber--; - - if (diskList[itemNumber].idPtr == NULL) { - result = 2; - goto errorReturn; - } - - asprintf(&filesURL, - "http://archive.org/metadata/%s/files", - diskList[itemNumber].idPtr); - if (filesURL == NULL) { - result = 3; - goto errorReturn; - } - - result = ReadJSONFromURL(filesURL, &filesJSON); - if (result != OPERATION_SUCCESSFUL) - goto errorReturn; - - json_value *resultJSON = findEntryInObject(filesJSON, "result", json_array); - if (resultJSON == NULL) { - result = NOT_EXPECTED_CONTENTS; - goto errorReturn; - } - - wantedExt = diskList[itemNumber].extPtr; - wantedItemID = diskList[itemNumber].idPtr; - processArray(resultJSON, json_object, processFile); - -errorReturn: - if (filesJSON != NULL) - json_value_free(filesJSON); - free(filesURL); - - InitCursor(); - if (result != 0) - ShowErrorAlert(result, mountErrorAlert); -} - - -/* Update state of controls based on user input */ -void UpdateControlState(void) { - unsigned long targetCtlID = GetCtlID(FindTargetCtl()); - if (targetCtlID != lastTargetCtlID) { - /* Make appropriate button respond to the "return" key */ - lastTargetCtlID = targetCtlID; - if (targetCtlID == searchLine) { - SetCtlMoreFlags(GetCtlMoreFlags(mountButtonHandle) & 0x1FFF, - mountButtonHandle); - SetCtlMoreFlags(GetCtlMoreFlags(searchButtonHandle) | fCtlWantEvents, - searchButtonHandle); - defaultButtonIsSearch = true; - } else if (targetCtlID == disksList) { - SetCtlMoreFlags(GetCtlMoreFlags(searchButtonHandle) & 0x1FFF, - searchButtonHandle); - SetCtlMoreFlags(GetCtlMoreFlags(mountButtonHandle) | fCtlWantEvents, - mountButtonHandle); - defaultButtonIsSearch = false; - } - DrawButtonOutlines(true); - } - - /* Only allow "Mount Disk" to be clicked if there is a disk selected */ - if (NextMember2(0, (Handle)disksListHandle) != 0) { - HiliteControl(noHilite, mountButtonHandle); - } else { - HiliteControl(inactiveHilite, mountButtonHandle); - } -} - -/* Handle an event after TaskMasterDA processing */ -void HandleEvent(int eventCode, WmTaskRec *taskRec) { - switch (eventCode) { - case wInControl: - switch (taskRec->wmTaskData4) { - case searchButton: - DoSearch(); - break; - - case forIIGSRadio: - gsDisksOnly = true; - break; - case forAnyAppleIIRadio: - gsDisksOnly = false; - break; - - case mountDiskButton: - DoMount(); - break; - } - break; - - case keyDownEvt: - case autoKeyEvt: - /* Handle keyboard shortcuts for cut/copy/paste */ - if (taskRec->modifiers & appleKey) { - switch (taskRec->message & 0x000000FF) { - case 'x': case 'X': - DoLEEdit(cutAction); - break; - case 'c': case 'C': - DoLEEdit(copyAction); - break; - case 'v': case 'V': - DoLEEdit(pasteAction); - break; - } - } - break; - } -} - -/* NDA-style action routine for our window */ -#pragma databank 1 -int ActionProc(EventRecord *eventRec, int actionCode) { - static WmTaskRec taskRec; - int handledAction = 0; - - if (!windowOpened) - return 0; - - GrafPortPtr port = GetPort(); - SetPort(window); - - switch (actionCode) { - case eventAction: - /* Copy basic event rec & use our own wmTaskMask, as per IIgs TN 84 */ - memset(&taskRec, sizeof(taskRec), 0); - memmove(&taskRec, eventRec, 16); - taskRec.wmTaskMask = 0x1F7FFF; /* everything except tmInfo */ - - HandleEvent(TaskMasterDA(0, &taskRec), &taskRec); - UpdateControlState(); - break; - - case cursorAction: - break; - - case cutAction: - case copyAction: - case pasteAction: - case clearAction: - handledAction = DoLEEdit(actionCode); - break; - } - - SetPort(port); - - return handledAction; -} -#pragma databank 0 - -asm void actionProcWrapper(void) { - pha - phy - phx - jsl ActionProc - rtl -} - -void ShowBrowserWindow(void) { - if (windowOpened) { - BringToFront(window); - return; - } - - Word origResourceApp = GetCurResourceApp(); - SetCurResourceApp(myUserID); - - resourceFileID = - OpenResourceFile(readEnable, NULL, LGetPathname2(myUserID, 1)); - if (toolerror()) { - resourceFileID = 0; - resourceFileOpened = false; - goto cleanup; - } - resourceFileOpened = true; - - window = NewWindow2(windowTitle, 0, DrawContents, NULL, - refIsResource, winDiskBrowser, rWindParam1); - if (toolerror()) { - window = NULL; - windowOpened = false; - goto cleanup; - } - windowOpened = true; - - SetSysWindow(window); - - AuxWindInfoRecord *auxWindInfo = GetAuxWindInfo(window); - if (toolerror()) { - CloseWindow(window); - windowOpened = false; - window = NULL; - goto cleanup; - } - - memset(&sysWindRecord, sizeof(sysWindRecord), 0); - sysWindRecord.closeProc = (ProcPtr)CloseBrowserWindow; - sysWindRecord.actionProc = (ProcPtr)actionProcWrapper; - sysWindRecord.eventMask = 0xFFFF; //0x03FF; - sysWindRecord.memoryID = myUserID; - auxWindInfo->NDASysWindPtr = (Ptr)&sysWindRecord; - - disksListHandle = GetCtlHandleFromID(window, disksList); - mountButtonHandle = GetCtlHandleFromID(window, mountDiskButton); - searchButtonHandle = GetCtlHandleFromID(window, searchButton); - - HiliteControl(inactiveHilite, disksListHandle); - HiliteControl(inactiveHilite, mountButtonHandle); - - lastTargetCtlID = 0; - defaultButtonIsSearch = true; - - wantToOpenWindow = 0; - -cleanup: - if (resourceFileOpened && !windowOpened) { - CloseResourceFile(resourceFileID); - resourceFileOpened = false; - } - - SetCurResourceApp(origResourceApp); -} - void DoGoAway(void) { CloseBrowserWindow(); @@ -726,13 +111,8 @@ static pascal Word requestProc(Word reqCode, Long dataIn, void *dataOut) { break; case finderSaysIdle: - /* - * Wait till the Finder has had time to recognize the new disk - * before opening the window for it. Otherwise, it may crash. - */ - if (wantToOpenWindow && --wantToOpenWindow == 0) { - SendRequest(tellFinderOpenWindow, sendToName|stopAfterOne, - (Long)NAME_OF_FINDER, (Long)&devName, (Ptr)&tfowOut); + if (wantToOpenWindow) { + ShowDiskWindow(); } break; @@ -751,6 +131,7 @@ static pascal Word requestProc(Word reqCode, Long dataIn, void *dataOut) { boolean NetDiskPresent(void) { + static struct MountURLRec mountURLRec = {sizeof(struct MountURLRec)}; mountURLRec.result = NETDISK_NOT_PRESENT; mountURLRec.url = ""; mountURLRec.flags = flgUseCache; @@ -766,7 +147,7 @@ boolean NetDiskPresent(void) } int main(void) { - myUserID = MMStartUp(); //userid() & 0xF0FF; + myUserID = MMStartUp(); Word origResourceApp = GetCurResourceApp(); ResourceStartUp(myUserID); diff --git a/diskbrowser.h b/diskbrowser.h new file mode 100644 index 0000000..fdf8e33 --- /dev/null +++ b/diskbrowser.h @@ -0,0 +1,53 @@ +#ifndef DISKBROWSER_H +#define DISKBROWSER_H + +/* Resource/control IDs */ +#define winDiskBrowser 1001 + +#define searchLine 1002 +#define searchButton 1003 +#define findDisksForText 1004 +#define forIIGSRadio 1005 +#define forAnyAppleIIRadio 1006 +#define disksList 1007 +#define mountDiskButton 1008 + +#define searchErrorAlert 3000 +#define mountErrorAlert 3001 + + +/* The browser window */ +extern GrafPortPtr window; + +/* Is the window open? */ +extern Boolean windowOpened; + +/* User preference: IIGS disks or all Apple II disks? */ +extern boolean gsDisksOnly; + +/* Disks list control */ +extern CtlRecHndl disksListHandle; + +/* Length of disks list */ +#define DISK_LIST_LENGTH 30 + +struct diskListEntry { + char *memPtr; + Byte memFlag; + char *idPtr; + char *extPtr; +}; + +/* List of disks, used for list control & mounting disks */ +extern struct diskListEntry diskList[DISK_LIST_LENGTH]; + +/* Do we want to open a window with disk contents? Counts down until ready. */ +extern int wantToOpenWindow; + +/* JSON for current disk list (needs to be kept in memory while it's shown) */ +extern json_value *json; + +/* User ID of this program */ +extern Word myUserID; + +#endif diff --git a/diskmount.c b/diskmount.c new file mode 100644 index 0000000..e67396c --- /dev/null +++ b/diskmount.c @@ -0,0 +1,147 @@ +/********************************************************************* + * Mounting disks + *********************************************************************/ + +#ifdef __ORCAC__ +#pragma noroot +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mounturl.h" +#include "asprintf.h" +#include "json.h" +#include "jsonutil.h" + +#include "diskbrowser.h" +#include "diskmount.h" +#include "browserutil.h" + +/* Item ID and file extension for the disk image we want to open */ +static char *wantedExt; +static char *wantedItemID; + +/* Device name of mounted to disk (to give to Finder) */ +static GSString32 devName; + +static boolean processFile(json_value *fileObj); +static void MountFile(char *itemID, char *fileName); + +void DoMount(void) { + unsigned int itemNumber = NextMember2(0, (Handle)disksListHandle); + char *filesURL = NULL; + enum NetDiskError result = 0; + json_value *filesJSON = NULL; + + WaitCursor(); + + if (itemNumber == 0) { + // shouldn't happen + result = 1; + goto errorReturn; + } + itemNumber--; + + if (diskList[itemNumber].idPtr == NULL) { + result = 2; + goto errorReturn; + } + + asprintf(&filesURL, + "http://archive.org/metadata/%s/files", + diskList[itemNumber].idPtr); + if (filesURL == NULL) { + result = 3; + goto errorReturn; + } + + result = ReadJSONFromURL(filesURL, &filesJSON); + if (result != OPERATION_SUCCESSFUL) + goto errorReturn; + + json_value *resultJSON = findEntryInObject(filesJSON, "result", json_array); + if (resultJSON == NULL) { + result = NOT_EXPECTED_CONTENTS; + goto errorReturn; + } + + wantedExt = diskList[itemNumber].extPtr; + wantedItemID = diskList[itemNumber].idPtr; + processArray(resultJSON, json_object, processFile); + +errorReturn: + if (filesJSON != NULL) + json_value_free(filesJSON); + free(filesURL); + + InitCursor(); + if (result != 0) + ShowErrorAlert(result, mountErrorAlert); +} + +static boolean processFile(json_value *fileObj) { + if (fileObj == NULL || fileObj->type != json_object) + return false; + + json_value *name = findEntryInObject(fileObj, "name", json_string); + if (name == NULL) + return true; + + char *nameExt = strrchr(name->u.string.ptr, '.'); + if (nameExt == NULL) + return true; + if (strcmp(nameExt + 1, wantedExt) == 0) { + MountFile(wantedItemID, name->u.string.ptr); + return false; + } + + return true; +} + +static void MountFile(char *itemID, char *fileName) { + static struct MountURLRec mountURLRec = {sizeof(struct MountURLRec)}; + + char *fileURL = NULL; + asprintf(&fileURL, "http://archive.org/download/%s/%s", itemID, fileName); + if (fileURL == NULL) { + SysBeep(); + return; + } + + mountURLRec.result = NETDISK_NOT_PRESENT; + mountURLRec.url = fileURL; + mountURLRec.flags = flgUseCache; + + SendRequest(MountURL, sendToName|stopAfterOne, (Long)NETDISK_REQUEST_NAME, + (Long)&mountURLRec, NULL); + + if (mountURLRec.result == OPERATION_SUCCESSFUL) { + devName.length = snprintf(devName.text, sizeof(devName.text), ".D%u", + mountURLRec.devNum); + wantToOpenWindow = 2; + } + + free(fileURL); +} + +/* Show Finder window for newly-mounted disk (once Finder is ready) */ +void ShowDiskWindow(void) { + static struct tellFinderOpenWindowOut tfowOut; + + /* + * Wait till the Finder has had time to recognize the new disk + * before opening the window for it. Otherwise, it may crash. + */ + if (wantToOpenWindow && --wantToOpenWindow == 0) { + SendRequest(tellFinderOpenWindow, sendToName|stopAfterOne, + (Long)NAME_OF_FINDER, (Long)&devName, (Ptr)&tfowOut); + } +} diff --git a/diskmount.h b/diskmount.h new file mode 100644 index 0000000..da7508a --- /dev/null +++ b/diskmount.h @@ -0,0 +1,7 @@ +#ifndef DISKMOUNT_H +#define DISKMOUNT_H + +void DoMount(void); +void ShowDiskWindow(void); + +#endif diff --git a/disksearch.c b/disksearch.c new file mode 100644 index 0000000..16130c7 --- /dev/null +++ b/disksearch.c @@ -0,0 +1,166 @@ +/********************************************************************* + * Searching for disks + *********************************************************************/ + +#ifdef __ORCAC__ +#pragma noroot +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include "netdiskerror.h" +#include "asprintf.h" +#include "json.h" +#include "jsonutil.h" + +#include "diskbrowser.h" +#include "disksearch.h" +#include "browserutil.h" + +/* Current position in disk list */ +static int diskListPos = 0; + +static boolean processDoc(json_value *docObj); +static char *EncodeQueryString(char *queryString); + +/* Do a search */ +void DoSearch(void) { + static char queryBuf[257]; + + char *searchURL = NULL; + enum NetDiskError result = 0; + + WaitCursor(); + + GetLETextByID(window, searchLine, (StringPtr)&queryBuf); + + char *queryString = EncodeQueryString(queryBuf+1); + if (queryString == NULL) { + result = OUT_OF_MEMORY; + goto errorReturn; + } + + asprintf(&searchURL, + "http://archive.org/advancedsearch.php?" + "q=emulator%%3A%s%%20%s" + "&fl%%5B%%5D=identifier&fl%%5B%%5D=title" + "&fl%%5B%%5D=emulator_ext" + "&sort%%5B%%5D=titleSorterRaw+asc" + "&rows=%i&page=%i&output=json", + gsDisksOnly ? "apple2gs" : "apple2%2A", + queryString, + DISK_LIST_LENGTH, + /*pageNum*/ 0); + if (searchURL == NULL) { + result = URL_TOO_LONG; + goto errorReturn; + } + free(queryString); + queryString = NULL; + + if (json) + json_value_free(json); + result = ReadJSONFromURL(searchURL, &json); + if (result != OPERATION_SUCCESSFUL) + goto errorReturn; + + json_value *response = findEntryInObject(json, "response", json_object); + if (response == NULL) { + result = NOT_EXPECTED_CONTENTS; + goto errorReturn; + } + + diskListPos = 0; + json_value *docs = findEntryInObject(response, "docs", json_array); + if (docs == NULL) { + result = NOT_EXPECTED_CONTENTS; + goto errorReturn; + } + processArray(docs, json_object, processDoc); + + diskList[0].memFlag = 0x80; + for (int i = 1; i < DISK_LIST_LENGTH; i++) { + diskList[i].memFlag = 0; + } + + /* Update state of controls once disk list is available */ + HiliteControl(noHilite, disksListHandle); + SetCtlMoreFlags( + GetCtlMoreFlags(disksListHandle) | fCtlCanBeTarget | fCtlWantEvents, + disksListHandle); + + NewList2(NULL, 1, (Ref) diskList, refIsPointer, + diskListPos, (Handle)disksListHandle); + + if (diskListPos > 0) { + if (FindTargetCtl() != disksListHandle) { + MakeThisCtlTarget(disksListHandle); + CallCtlDefProc(disksListHandle, ctlChangeTarget, 0); + } + } + + free(searchURL); + InitCursor(); + return; + +errorReturn: + NewList2(NULL, 1, (Ref) diskList, refIsPointer, 0, (Handle)disksListHandle); + free(queryString); + free(searchURL); + InitCursor(); + ShowErrorAlert(result, searchErrorAlert); +} + +static boolean processDoc(json_value *docObj) { + if (diskListPos >= DISK_LIST_LENGTH) + return false; + + if (docObj == NULL || docObj->type != json_object) + return false; + json_value *id = findEntryInObject(docObj, "identifier", json_string); + json_value *title = findEntryInObject(docObj, "title", json_string); + json_value *ext = findEntryInObject(docObj, "emulator_ext", json_string); + if (id == NULL || title == NULL || ext == NULL) + return true; + diskList[diskListPos].idPtr = id->u.string.ptr; + // TODO character set translation + diskList[diskListPos].memPtr = title->u.string.ptr; + diskList[diskListPos++].extPtr = ext->u.string.ptr; + return true; +} + +static char *EncodeQueryString(char *queryString) { + long sizeNeeded; + + char *encodedStr = NULL; + sizeNeeded = strlen(queryString); + if (sizeNeeded > 10000) + return NULL; + encodedStr = malloc(sizeNeeded*3 + 1); + if (encodedStr == NULL) + return NULL; + + char *s = encodedStr; + char c; + while ((c = *queryString++) != '\0') { + if (c > 0x7F) { + //TODO character set translation + c = '*'; + } + if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9')) { + *s++ = c; + } else { + snprintf(s, 4, "%%%02X", (unsigned char)c); + s+= 3; + } + } + *s = '\0'; + return encodedStr; +} diff --git a/disksearch.h b/disksearch.h new file mode 100644 index 0000000..7c31fab --- /dev/null +++ b/disksearch.h @@ -0,0 +1,6 @@ +#ifndef DISKSEARCH_H +#define DISKSEARCH_H + +void DoSearch(void); + +#endif diff --git a/json.c b/json.c index c0aa685..396b87f 100644 --- a/json.c +++ b/json.c @@ -27,6 +27,10 @@ * SUCH DAMAGE. */ +#ifdef __ORCAC__ +#pragma noroot +#endif + #include "json.h" #ifdef _MSC_VER diff --git a/jsonutil.c b/jsonutil.c index e56e37f..b49f0b1 100644 --- a/jsonutil.c +++ b/jsonutil.c @@ -1,3 +1,7 @@ +#ifdef __ORCAC__ +#pragma noroot +#endif + #include #include "jsonutil.h"