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"