jcs
/subtext
/amendments
/1
Initial import
Work on a functioning console
jcs made amendment 1 over 3 years ago
--- AddressXlation.h Fri Sep 18 08:57:06 2020
+++ AddressXlation.h Fri Sep 18 08:57:06 2020
@@ -0,0 +1,74 @@
+/*
+ AddressXlation.h
+ MacTCP name to address translation routines.
+
+ Copyright Apple Computer, Inc. 1988
+ All rights reserved
+
+*/
+
+#ifndef __ADDRESSXLATION__
+#define __ADDRESSXLATION__
+
+#include "MacTCP.h"
+
+#define NUM_ALT_ADDRS 4
+
+typedef struct hostInfo {
+ int _pad; /* XXX: i don't know why this is needed, but without it,
+ * StrToAddrProcPtr() returns everything shifted 2 bytes */
+ int rtnCode;
+ char cname[255];
+ unsigned long addr[NUM_ALT_ADDRS];
+};
+
+typedef enum AddrClasses {
+ A = 1,
+ NS,
+ CNAME = 5,
+ lastClass = 65535
+};
+
+typedef struct cacheEntryRecord {
+ char *cname;
+ unsigned short type;
+ enum AddrClasses class;
+ unsigned long ttl;
+ union {
+ char *name;
+ ip_addr addr;
+ } rdata;
+};
+
+typedef pascal void (*EnumResultProcPtr)(struct cacheEntryRecord *cacheEntryRecordPtr,
+ char *userDataPtr);
+typedef pascal void (*ResultProcPtr)(struct hostInfo *hostInfoPtr,
+ char *userDataPtr);
+
+extern OSErr OpenResolver(char *fileName);
+extern OSErr StrToAddr(char *hostName, struct hostInfo *hostInfoPtr,
+ ResultProcPtr resultProc, char *userDataPtr);
+extern OSErr AddrToStr(unsigned long addr, char *addrStr);
+extern OSErr EnumCache(EnumResultProcPtr enumResultProc, char *userDataPtr);
+extern OSErr AddrToName(ip_addr addr, struct hostInfo *hostInfoPtr,
+ ResultProcPtr resultProc, char *userDataPtr);
+extern OSErr CloseResolver(void);
+
+/*
+ * The above functions call into the dnr resource and pass the function id
+ * and function-specific arguments, so the compiler needs to know the types
+ */
+typedef OSErr (*OpenResolverProcPtr)(UInt32, char *);
+typedef OSErr (*CloseResolverProcPtr)(UInt32);
+typedef OSErr (*StrToAddrProcPtr)(UInt32, char *hostName,
+ struct hostInfo *hostInfoPtr, ResultProcPtr resultProc,
+ char *userDataPtr);
+typedef OSErr (*AddrToStrProcPtr)(UInt32, unsigned long addr,
+ char *addrStr);
+typedef OSErr (*EnumCacheProcPtr)(UInt32, EnumResultProcPtr enumResultProc,
+ char *userDataPtr);
+typedef OSErr (*AddrToNameProcPtr)(UInt32, ip_addr addr,
+ struct hostInfo *hostInfoPtr, ResultProcPtr resultProc,
+ char *userDataPtr);
+
+#endif
--- console.c Wed Nov 17 15:31:31 2021
+++ console.c Wed Nov 17 15:31:31 2021
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "subtext.h"
+#include "console.h"
+#include "session.h"
+#include "util.h"
+
+struct node_funcs console_node_funcs = {
+ console_output,
+ console_read,
+};
+
+void console_redraw(struct console *console);
+void console_cursor(struct console *console, short line, short column);
+
+struct console *
+console_init(void)
+{
+ char title[256] = { 0 };
+ struct console *console;
+ Rect bounds = { 0 };
+ Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */
+ short padding = 20;
+
+ console = xmalloczero(sizeof(struct console));
+
+ bounds.left = (padding / 2);
+ bounds.top = screenBits.bounds.top + (GetMBarHeight() * 2) - 1 +
+ (padding / 2);
+ bounds.right = screenBits.bounds.right - 1 - (padding / 2);
+ bounds.bottom = screenBits.bounds.bottom - 1 - (padding / 2);
+
+ if (sprintf(title, "%s: console", PROGRAM_NAME) > sizeof(title))
+ err(1, "sprintf overflow!");
+ CtoPstr(title);
+
+ console->win = NewWindow(0L, &bounds, title, false, noGrowDocProc,
+ (WindowPtr)-1L, true, 0);
+ if (!console->win)
+ err(1, "Can't create window");
+ SetPort(console->win);
+
+ bounds.right = console->win->portRect.right;
+ bounds.left = bounds.right - SCROLLBAR_WIDTH;
+ bounds.bottom = console->win->portRect.bottom + 1;
+ bounds.top = console->win->portRect.top - 1;
+ console->scroller = NewControl(console->win, &bounds, "\p", true,
+ 1, 1, 1, scrollBarProc, 0L);
+
+ ShowWindow(console->win);
+
+ console->session = session_create("console", &console_node_funcs);
+ console->session->cookie = (void *)console;
+ console->ncolumns = 80;
+ console->nlines = 24;
+ console->old_cursor_line = -1;
+
+ return console;
+}
+
+void
+console_idle(struct console *console)
+{
+}
+
+void
+console_suspend(struct console *console)
+{
+}
+
+void
+console_resume(struct console *console)
+{
+}
+
+void
+console_update(struct console *console, EventRecord *event)
+{
+ Str255 buf;
+ Rect r;
+ short what = -1;
+
+ if (event != NULL)
+ what = event->what;
+
+ switch (what) {
+ case -1:
+ case updateEvt:
+ console_redraw(console);
+ //browser_update_menu(browser);
+ UpdtControl(console->win, console->win->visRgn);
+
+ break;
+ case activateEvt:
+ break;
+ }
+}
+
+void
+console_mouse_down(struct console *console, EventRecord *event)
+{
+}
+
+short
+console_output(struct session *session)
+{
+ struct console *console = (struct console *)(session->cookie);
+ short n, len, cursor;
+
+ if (session->obuflen == 0)
+ return 0;
+
+ for (n = 0; n < session->obuflen; n++) {
+ if (console->cursor_column >= console->ncolumns &&
+ session->obuf[n] != '\n') {
+ console->cursor_column = 0;
+ console->cursor_line++;
+ }
+
+ switch (session->obuf[n]) {
+ case '\r':
+ console->cursor_column = 0;
+ break;
+ case '\n':
+ console->cursor_line++;
+ break;
+ default:
+ cursor = (console->cursor_line * console->ncolumns) +
+ console->cursor_column;
+ console->chars[cursor] = session->obuf[n];
+ console->attrs[cursor] |= ATTR_DIRTY;
+ console->cursor_column++;
+ break;
+ }
+ }
+
+ len = session->obuflen;
+ session->obuflen = 0;
+
+ console_redraw(console);
+
+ return len;
+}
+
+void
+console_redraw(struct console *console)
+{
+ short n, line = 0, column = 0;
+
+ TextFont(monaco);
+ TextSize(9);
+
+ if (console->old_cursor_line != -1)
+ console_cursor(console, console->old_cursor_line,
+ console->old_cursor_column);
+
+ for (n = 0; n < sizeof(console->chars); n++) {
+ if (!(console->attrs[n] & ATTR_DIRTY))
+ continue;
+
+ line = ((n + 1) / console->ncolumns);
+ column = n - (line * console->ncolumns);
+ MoveTo(6 + ((console->win->portRect.left + column) * 6),
+ 6 + ((console->win->portRect.top + line + 1) * 10));
+ DrawChar(console->chars[n]);
+ console->attrs[n] &= ~ATTR_DIRTY;
+ }
+
+ console_cursor(console, console->cursor_line, console->cursor_column);
+ console->old_cursor_line = console->cursor_line;
+ console->old_cursor_column = console->cursor_column;
+}
+
+void
+console_cursor(struct console *console, short line, short column)
+{
+ Rect cursor;
+
+ cursor.left = 6 + ((console->win->portRect.left + column) * 6);
+ cursor.top = 6 + ((console->win->portRect.top + line + 1) * 10);
+ cursor.right = cursor.left + 6;
+ cursor.bottom = cursor.top + 10;
+ InvertRect(&cursor);
+}
+
+short
+console_read(struct session *session)
+{
+ return 0;
+}
--- console.h Wed Nov 17 15:26:21 2021
+++ console.h Wed Nov 17 15:26:21 2021
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __CONSOLE_H__
+#define __CONSOLE_H__
+
+#include "session.h"
+
+struct console {
+ short state;
+ WindowPtr win;
+ ControlHandle scroller;
+ char chars[80 * 24];
+ char attrs[80 * 24];
+#define ATTR_DIRTY (1 << 0)
+#define ATTR_BOLD (1 << 1)
+ short nlines;
+ short ncolumns;
+ short cursor_line;
+ short cursor_column;
+ short old_cursor_line;
+ short old_cursor_column;
+ struct session *session;
+};
+
+struct console *console_init(void);
+void console_idle(struct console *console);
+void console_suspend(struct console *console);
+void console_resume(struct console *console);
+void console_update(struct console *console, EventRecord *event);
+void console_mouse_down(struct console *console, EventRecord *event);
+
+short console_output(struct session *session);
+short console_read(struct session *session);
+
+#endif /* __CONSOLE_H__ */
--- dnr.c Thu Sep 17 14:11:02 2020
+++ dnr.c Thu Sep 17 14:11:02 2020
@@ -0,0 +1,308 @@
+/*
+ * DNR.c - DNR library for MPW
+ *
+ * (c) Copyright 1988 by Apple Computer. All rights reserved
+ *
+ * Modifications by Jim Matthews, Dartmouth College, 5/91
+ */
+
+#include <OSUtils.h>
+#include <Files.h>
+#include <Folders.h>
+#include <GestaltEqu.h>
+#include <Traps.h>
+
+#include "AddressXlation.h"
+
+#define OPENRESOLVER 1
+#define CLOSERESOLVER 2
+#define STRTOADDR 3
+#define ADDRTOSTR 4
+#define ENUMCACHE 5
+#define ADDRTONAME 6
+#define HINFO 7
+#define MXINFO 8
+
+TrapType GetTrapType(unsigned long theTrap);
+Boolean TrapAvailable(unsigned long trap);
+void GetSystemFolder(short *vRefNumP, long *dirIDP);
+void GetCPanelFolder(short *vRefNumP, long *dirIDP);
+short SearchFolderForDNRP(long targetType, long targetCreator,
+ short vRefNum, long dirID);
+short OpenOurRF(void);
+
+Handle dnr = nil;
+
+TrapType
+GetTrapType(unsigned long theTrap)
+{
+ if (BitAnd(theTrap, 0x0800) > 0)
+ return (ToolTrap);
+
+ return (OSTrap);
+}
+
+Boolean
+TrapAvailable(unsigned long trap)
+{
+ TrapType trapType = ToolTrap;
+ unsigned long numToolBoxTraps;
+
+ if (NGetTrapAddress(_InitGraf, ToolTrap) ==
+ NGetTrapAddress(0xAA6E, ToolTrap))
+ numToolBoxTraps = 0x200;
+ else
+ numToolBoxTraps = 0x400;
+
+ trapType = GetTrapType(trap);
+ if (trapType == ToolTrap) {
+ trap = BitAnd(trap, 0x07FF);
+ if (trap >= numToolBoxTraps)
+ trap = _Unimplemented;
+ }
+
+ return (NGetTrapAddress(trap, trapType) !=
+ NGetTrapAddress(_Unimplemented, ToolTrap));
+}
+
+void
+GetSystemFolder(short *vRefNumP, long *dirIDP)
+{
+ SysEnvRec info;
+ long wdProcID;
+
+ SysEnvirons(1, &info);
+ if (GetWDInfo(info.sysVRefNum, vRefNumP, dirIDP, &wdProcID) != noErr) {
+ *vRefNumP = 0;
+ *dirIDP = 0;
+ }
+}
+
+void
+GetCPanelFolder(short *vRefNumP, long *dirIDP)
+{
+ Boolean hasFolderMgr = false;
+ long feature;
+
+ if (TrapAvailable(_GestaltDispatch) &&
+ Gestalt(gestaltFindFolderAttr, &feature) == noErr)
+ hasFolderMgr = true;
+
+ if (!hasFolderMgr) {
+ GetSystemFolder(vRefNumP, dirIDP);
+ return;
+ }
+
+ if (FindFolder(kOnSystemDisk, kControlPanelFolderType,
+ kDontCreateFolder, vRefNumP, dirIDP) != noErr) {
+ *vRefNumP = 0;
+ *dirIDP = 0;
+ }
+}
+
+/*
+ * SearchFolderForDNRP is called to search a folder for files that might
+ * contain the 'dnrp' resource
+ */
+short
+SearchFolderForDNRP(long targetType, long targetCreator, short vRefNum,
+ long dirID)
+{
+ HParamBlockRec fi;
+ Str255 filename;
+ short refnum;
+
+ fi.fileParam.ioCompletion = nil;
+ fi.fileParam.ioNamePtr = filename;
+ fi.fileParam.ioVRefNum = vRefNum;
+ fi.fileParam.ioDirID = dirID;
+ fi.fileParam.ioFDirIndex = 1;
+
+ while (PBHGetFInfo(&fi, false) == noErr) {
+ /* scan system folder for driver resource files of specific type & creator */
+ if (fi.fileParam.ioFlFndrInfo.fdType == targetType &&
+ fi.fileParam.ioFlFndrInfo.fdCreator == targetCreator) {
+ /* found the MacTCP driver file? */
+ refnum = HOpenResFile(vRefNum, dirID, filename, fsRdPerm);
+ if (Get1IndResource('dnrp', 1))
+ return refnum;
+ CloseResFile(refnum);
+ }
+
+ /* check next file in system folder */
+ fi.fileParam.ioFDirIndex++;
+ fi.fileParam.ioDirID = dirID; /* PBHGetFInfo() clobbers ioDirID */
+ }
+ return -1;
+}
+
+/* OpenOurRF is called to open the MacTCP driver resources */
+short
+OpenOurRF(void)
+{
+ short refnum;
+ short vRefNum;
+ long dirID;
+
+ /* first search Control Panels for MacTCP 1.1 */
+ GetCPanelFolder(&vRefNum, &dirID);
+ refnum = SearchFolderForDNRP('cdev', 'ztcp', vRefNum, dirID);
+ if (refnum != -1)
+ return (refnum);
+
+ /* next search System Folder for MacTCP 1.0.x */
+ GetSystemFolder(&vRefNum, &dirID);
+ refnum = SearchFolderForDNRP('cdev', 'mtcp', vRefNum, dirID);
+ if (refnum != -1)
+ return (refnum);
+
+ /* finally, search Control Panels for MacTCP 1.0.x */
+ GetCPanelFolder(&vRefNum, &dirID);
+ refnum = SearchFolderForDNRP('cdev', 'mtcp', vRefNum, dirID);
+ if (refnum != -1)
+ return (refnum);
+
+ return -1;
+}
+
+OSErr
+OpenResolver(char *fileName)
+{
+ short refnum;
+ OSErr rc;
+
+ if (dnr != nil)
+ /* resolver already loaded in */
+ return (noErr);
+
+ /*
+ * Open the MacTCP driver to get DNR resources. Search for it based on
+ * creator & type rather than simply file name.
+ */
+ refnum = OpenOurRF();
+
+ /*
+ * Ignore failures since the resource may have been installed in the
+ * System file if running on a Mac 512Ke.
+ */
+
+ /* load in the DNR resource package */
+ ResrvMem(16000L); /* s-dorner@uiuc.edu, 9/5/91 */
+ dnr = Get1IndResource('dnrp', 1);
+ if (dnr == nil) {
+ /* can't open DNR */
+ return (ResError());
+ }
+
+ DetachResource(dnr);
+ if (refnum != -1) {
+ CloseWD(refnum);
+ CloseResFile(refnum);
+ }
+
+ /* lock the DNR resource since it cannot be relocated while opened */
+ MoveHHi(dnr);
+ HLock(dnr);
+
+ /* call open resolver */
+ rc = ((OpenResolverProcPtr)*dnr)(OPENRESOLVER, fileName);
+ if (rc != noErr) {
+ /* problem with open resolver, flush it */
+ HUnlock(dnr);
+ DisposHandle(dnr);
+ dnr = nil;
+ }
+
+ return (rc);
+}
+
+OSErr
+CloseResolver(void)
+{
+ if (dnr == nil)
+ /* resolver not loaded error */
+ return (notOpenErr);
+
+ /* call close resolver */
+ (void)((CloseResolverProcPtr)*dnr)(CLOSERESOLVER);
+
+ /* release the DNR resource package */
+ HUnlock(dnr);
+ DisposHandle(dnr);
+ dnr = nil;
+
+ return (noErr);
+}
+
+OSErr
+StrToAddr(char *hostName, struct hostInfo *rtnStruct,
+ ResultProcPtr resultproc, char *userDataPtr)
+{
+ OSErr err;
+
+ if (dnr == nil)
+ /* resolver not loaded error */
+ return (notOpenErr);
+
+ err = ((StrToAddrProcPtr)*dnr)(STRTOADDR, hostName, rtnStruct,
+ resultproc, userDataPtr);
+ return err;
+}
+
+OSErr
+AddrToStr(unsigned long addr, char *addrStr)
+{
+ if (dnr == nil)
+ /* resolver not loaded error */
+ return (notOpenErr);
+
+ (void)((AddrToStrProcPtr)*dnr)(ADDRTOSTR, addr, addrStr);
+ return (noErr);
+}
+
+OSErr
+EnumCache(EnumResultProcPtr enumResultProc, char *userDataPtr)
+{
+ if (dnr == nil)
+ /* resolver not loaded error */
+ return (notOpenErr);
+
+ return ((EnumCacheProcPtr)*dnr)(ENUMCACHE, enumResultProc,
+ userDataPtr);
+}
+
+OSErr
+AddrToName(ip_addr addr, struct hostInfo *hostInfoPtr,
+ ResultProcPtr resultProc, char *userDataPtr)
+{
+ if (dnr == nil)
+ /* resolver not loaded error */
+ return (notOpenErr);
+
+ return ((AddrToNameProcPtr)*dnr)(ADDRTONAME, addr, hostInfoPtr,
+ resultProc, userDataPtr);
+}
+
+#if 0
+OSErr
+HInfo(char *hostName, struct returnRec *returnRecPtr, long resultProc,
+ char *userDataPtr)
+{
+ if (dnr == nil)
+ /* resolver not loaded error */
+ return (notOpenErr);
+
+ return ((*dnr)(HINFO, hostName, returnRecPtr, resultProc, userDataPtr));
+}
+
+OSErr
+MXInfo(char *hostName, struct returnRec *returnRecPtr, long resultProc,
+ char *userDataPtr)
+{
+ if (dnr == nil)
+ /* resolver not loaded error */
+ return (notOpenErr);
+
+ return ((*dnr)(MXINFO, hostName, returnRecPtr, resultProc, userDataPtr));
+}
+#endif
--- MacTCP.h Thu Sep 17 17:48:48 2020
+++ MacTCP.h Thu Sep 17 17:48:48 2020
@@ -0,0 +1,863 @@
+/*
+ File: MacTCP.h
+
+ Contains: TCP Manager Interfaces.
+
+ Version: Technology: MacTCP 2.0.6
+ Package: Universal Interfaces 2.1ß1 in “MPW Prerelease” on ETO #17
+
+ Copyright: © 1984-1995 by Apple Computer, Inc.
+ All rights reserved.
+
+ Bugs?: If you find a problem with this file, use the Apple Bug Reporter
+ stack. Include the file and version information (from above)
+ in the problem description and send to:
+ Internet: apple.bugs@applelink.apple.com
+ AppleLink: APPLE.BUGS
+
+*/
+
+#ifndef __MACTCP__
+#define __MACTCP__
+
+
+#ifndef __TYPES__
+#include <Types.h>
+#endif
+
+#if 0
+#ifndef __APPLETALK__
+#include <AppleTalk.h>
+#endif
+#endif
+
+enum {
+ kPascalStackBased = 0,
+ kCStackBased = 0,
+ kThinkCStackBased = 0
+};
+#define SIZE_CODE(size) (0)
+#define RESULT_SIZE(sizeCode) (0)
+#define STACK_ROUTINE_PARAMETER(whichParam, sizeCode) (0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if PRAGMA_ALIGN_SUPPORTED
+#pragma options align=mac68k
+#endif
+
+#if PRAGMA_IMPORT_SUPPORTED
+#pragma import on
+#endif
+
+/*
+Developer Notes:
+ 0. This MacTCP header replaces what used to be defined in the following header files
+ MacTCPCommonTypes.h
+ GetMyIPAddr.h
+ MiscIPPB.h
+ TCPPB.h
+ UDPPB.h
+
+ When the various control calls are made to the ip driver, you must set up a
+ NewRoutineDescriptor for every non-nil completion routine and/or notifyProc parameter.
+ Otherwise, the 68K driver code, will not correctly call your routine.
+ 1. For ipctlGetAddr Control calls, use NewGetIPIOCompletionProc
+ to set up a GetIPIOCompletionUPP universal procptr to pass as
+ the ioCompletion parameter.
+ 2. For the ipctlEchoICMP and ipctlLAPStats Control calls, use
+ NewIPIOCompletion to set up a IPIOCompletionUPP universal procptr
+ to pass in the ioCompletion field of the parameter block.
+ 3. For TCPCreatePB Control calls, use NewTCPNotifyProc to set up a
+ TCPNotifyUPP universal procptr to pass in the notifyProc field
+ of the parameter block
+ 4. For all of the TCP Control calls using the TCPiopb parameter block,
+ use NewTCPIOCompletionProc to set up a TCPIOCompletionUPP
+ universal procptr to pass in the ioCompletion field of the paramter
+ block.
+ 5. For UDBCreatePB Control calls, use NewUDPNotifyProc to set up a
+ UDPNotifyUPP universal procptr to pass in the notifyProc field
+ of the parameter block
+ 6. For all of the UDP Control calls using the UDPiopb parameter block,
+ use NewUDPIOCompletionProc to set up a UDPIOCompletionUPP
+ universal procptr to pass in the ioCompletion field of the paramter
+ block.
+ 7. For all calls implementing a notifyProc or ioCompletion routine
+ which was set up using a NewTCPRoutineProc call, do not call
+ DisposeRoutineSDescriptor on the universal procptr until
+ after the completion or notify proc has completed.
+*/
+/* MacTCP return Codes in the range -23000 through -23049 */
+
+enum {
+ inProgress = 1, /* I/O in progress */
+ ipBadLapErr = -23000, /* bad network configuration */
+ ipBadCnfgErr = -23001, /* bad IP configuration error */
+ ipNoCnfgErr = -23002, /* missing IP or LAP configuration error */
+ ipLoadErr = -23003, /* error in MacTCP load */
+ ipBadAddr = -23004, /* error in getting address */
+ connectionClosing = -23005, /* connection is closing */
+ invalidLength = -23006,
+ connectionExists = -23007, /* request conflicts with existing connection */
+ connectionDoesntExist = -23008, /* connection does not exist */
+ insufficientResources = -23009, /* insufficient resources to perform request */
+ invalidStreamPtr = -23010,
+ streamAlreadyOpen = -23011,
+ connectionTerminated = -23012,
+ invalidBufPtr = -23013,
+ invalidRDS = -23014,
+ invalidWDS = -23014,
+ openFailed = -23015,
+ commandTimeout = -23016,
+ duplicateSocket = -23017
+};
+
+/* Error codes from internal IP functions */
+enum {
+ ipDontFragErr = -23032, /* Packet too large to send w/o fragmenting */
+ ipDestDeadErr = -23033, /* destination not responding */
+ icmpEchoTimeoutErr = -23035, /* ICMP echo timed-out */
+ ipNoFragMemErr = -23036, /* no memory to send fragmented pkt */
+ ipRouteErr = -23037, /* can't route packet off-net */
+ nameSyntaxErr = -23041,
+ cacheFault = -23042,
+ noResultProc = -23043,
+ noNameServer = -23044,
+ authNameErr = -23045,
+ noAnsErr = -23046,
+ dnrErr = -23047,
+ outOfMemory = -23048
+};
+
+enum {
+ BYTES_16WORD = 2, /* bytes per = 16, bit ip word */
+ BYTES_32WORD = 4, /* bytes per = 32, bit ip word */
+ BYTES_64WORD = 8 /* bytes per = 64, bit ip word */
+};
+
+/* 8-bit quantity */
+typedef unsigned char UInt8;
+typedef signed char SInt8;
+typedef UInt8 b_8;
+
+/* 16-bit quantity */
+typedef unsigned short UInt16;
+typedef signed short SInt16;
+typedef UInt16 b_16;
+
+/* 32-bit quantity */
+typedef unsigned long UInt32;
+typedef signed long SInt32;
+typedef UInt32 b_32;
+
+/* IP address is 32-bits */
+typedef b_32 ip_addr;
+
+struct ip_addrbytes {
+ union {
+ b_32 addr;
+ UInt8 byte[4];
+ } a;
+};
+typedef struct ip_addrbytes ip_addrbytes;
+
+struct wdsEntry {
+ unsigned short length; /* length of buffer */
+ Ptr ptr; /* pointer to buffer */
+};
+typedef struct wdsEntry wdsEntry;
+
+struct rdsEntry {
+ unsigned short length; /* length of buffer */
+ Ptr ptr; /* pointer to buffer */
+};
+typedef struct rdsEntry rdsEntry;
+
+typedef unsigned long BufferPtr;
+
+typedef unsigned long StreamPtr;
+
+
+enum {
+ netUnreach = 0,
+ hostUnreach = 1,
+ protocolUnreach = 2,
+ portUnreach = 3,
+ fragReqd = 4,
+ sourceRouteFailed = 5,
+ timeExceeded = 6,
+ parmProblem = 7,
+ missingOption = 8,
+ lastICMPMsgType = 32767
+};
+
+typedef unsigned short ICMPMsgType;
+
+typedef b_16 ip_port;
+
+struct ICMPReport {
+ StreamPtr streamPtr;
+ ip_addr localHost;
+ ip_port localPort;
+ ip_addr remoteHost;
+ ip_port remotePort;
+ short reportType;
+ unsigned short optionalAddlInfo;
+ unsigned long optionalAddlInfoPtr;
+};
+typedef struct ICMPReport ICMPReport;
+
+/* csCode to get our IP address */
+
+enum {
+ ipctlGetAddr = 15
+};
+
+typedef void (*GetIPIOCompletionProcPtr)(struct GetAddrParamBlock *iopb);
+
+#if GENERATINGCFM
+typedef UniversalProcPtr GetIPIOCompletionUPP;
+#else
+typedef GetIPIOCompletionProcPtr GetIPIOCompletionUPP;
+#endif
+
+#define GetIPParamBlockHeader \
+ struct QElem* qLink; \
+ short qType; \
+ short ioTrap; \
+ Ptr ioCmdAddr; \
+ GetIPIOCompletionUPP ioCompletion; \
+ OSErr ioResult; \
+ StringPtr ioNamePtr; \
+ short ioVRefNum; \
+ short ioCRefNum; \
+ short csCode
+struct GetAddrParamBlock {
+ struct QElem *qLink;
+ short qType;
+ short ioTrap;
+ Ptr ioCmdAddr;
+ GetIPIOCompletionUPP ioCompletion;
+ OSErr ioResult;
+ StringPtr ioNamePtr;
+ short ioVRefNum;
+ short ioCRefNum;
+ short csCode; /* standard I/O header */
+ ip_addr ourAddress; /* our IP address */
+ long ourNetMask; /* our IP net mask */
+};
+typedef struct GetAddrParamBlock GetAddrParamBlock;
+
+/* control codes */
+
+enum {
+ ipctlEchoICMP = 17, /* send icmp echo */
+ ipctlLAPStats = 19 /* get lap stats */
+};
+
+typedef void (*IPIOCompletionProcPtr)(struct ICMPParamBlock *iopb);
+
+#if GENERATINGCFM
+typedef UniversalProcPtr IPIOCompletionUPP;
+#else
+typedef IPIOCompletionProcPtr IPIOCompletionUPP;
+#endif
+
+#define IPParamBlockHeader \
+ struct QElem* qLink; \
+ short qType; \
+ short ioTrap; \
+ Ptr ioCmdAddr; \
+ IPIOCompletionUPP ioCompletion; \
+ OSErr ioResult; \
+ StringPtr ioNamePtr; \
+ short ioVRefNum; \
+ short ioCRefNum; \
+ short csCode
+struct ICMPParamBlock {
+ struct QElem *qLink;
+ short qType;
+ short ioTrap;
+ Ptr ioCmdAddr;
+ IPIOCompletionUPP ioCompletion;
+ OSErr ioResult;
+ StringPtr ioNamePtr;
+ short ioVRefNum;
+ short ioCRefNum;
+ short csCode; /* standard I/O header */
+ short params[11];
+ struct {
+ unsigned long echoRequestOut; /* time in ticks of when the echo request went out */
+ unsigned long echoReplyIn; /* time in ticks of when the reply was received */
+ struct rdsEntry echoedData; /* data received in responce */
+ Ptr options;
+ unsigned long userDataPtr;
+ } icmpEchoInfo;
+};
+typedef pascal void (*ICMPEchoNotifyProcPtr)(struct ICMPParamBlock *iopb);
+
+#if GENERATINGCFM
+typedef UniversalProcPtr ICMPEchoNotifyUPP;
+#else
+typedef ICMPEchoNotifyProcPtr ICMPEchoNotifyUPP;
+#endif
+
+struct IPParamBlock {
+ struct QElem *qLink;
+ short qType;
+ short ioTrap;
+ Ptr ioCmdAddr;
+ IPIOCompletionUPP ioCompletion;
+ OSErr ioResult;
+ StringPtr ioNamePtr;
+ short ioVRefNum;
+ short ioCRefNum;
+ short csCode; /* standard I/O header */
+ union {
+ struct {
+ ip_addr dest; /* echo to IP address */
+ wdsEntry data;
+ short timeout;
+ Ptr options;
+ unsigned short optLength;
+ ICMPEchoNotifyUPP icmpCompletion;
+ unsigned long userDataPtr;
+ } IPEchoPB;
+ struct {
+ struct LAPStats *lapStatsPtr;
+ } LAPStatsPB;
+ } csParam;
+};
+union LAPStatsAddrXlation {
+ struct arp_entry *arp_table;
+ struct nbp_entry *nbp_table;
+};
+struct LAPStats {
+ short ifType;
+ char *ifString;
+ short ifMaxMTU;
+ long ifSpeed;
+ short ifPhyAddrLength;
+ char *ifPhysicalAddress;
+ union LAPStatsAddrXlation AddrXlation;
+ short slotNumber;
+};
+typedef struct LAPStats LAPStats;
+
+#ifdef __APPLETALK__
+struct nbp_entry {
+ ip_addr ip_address; /* IP address */
+ AddrBlock at_address; /* matching AppleTalk address */
+ Boolean gateway; /* TRUE if entry for a gateway */
+ Boolean valid; /* TRUE if LAP address is valid */
+ Boolean probing; /* TRUE if NBP lookup pending */
+ SInt8 afiller; /* Filler for proper byte alignment */
+ long age; /* ticks since cache entry verified */
+ long access; /* ticks since last access */
+ SInt8 filler[116]; /* for internal use only !!! */
+};
+#endif
+struct Enet_addr {
+ b_16 en_hi;
+ b_32 en_lo;
+};
+typedef struct Enet_addr Enet_addr;
+
+struct arp_entry {
+ short age; /* cache aging field */
+ b_16 protocol; /* Protocol type */
+ ip_addr ip_address; /* IP address */
+ Enet_addr en_address; /* matching Ethernet address */
+};
+typedef struct arp_entry arp_entry;
+
+/* number of ARP table entries */
+
+enum {
+ ARP_TABLE_SIZE = 20
+};
+
+enum {
+ NBP_TABLE_SIZE = 20, /* number of NBP table entries */
+ NBP_MAX_NAME_SIZE = 16 + 10 + 2
+};
+
+/* Command codes */
+enum {
+ TCPCreate = 30,
+ TCPPassiveOpen = 31,
+ TCPActiveOpen = 32,
+ TCPSend = 34,
+ TCPNoCopyRcv = 35,
+ TCPRcvBfrReturn = 36,
+ TCPRcv = 37,
+ TCPClose = 38,
+ TCPAbort = 39,
+ TCPStatus = 40,
+ TCPExtendedStat = 41,
+ TCPRelease = 42,
+ TCPGlobalInfo = 43,
+ TCPCtlMax = 49
+};
+
+enum {
+ TCPClosing = 1,
+ TCPULPTimeout = 2,
+ TCPTerminate = 3,
+ TCPDataArrival = 4,
+ TCPUrgent = 5,
+ TCPICMPReceived = 6,
+ lastEvent = 32767
+};
+
+typedef unsigned short TCPEventCode;
+
+
+enum {
+ TCPRemoteAbort = 2,
+ TCPNetworkFailure = 3,
+ TCPSecPrecMismatch = 4,
+ TCPULPTimeoutTerminate = 5,
+ TCPULPAbort = 6,
+ TCPULPClose = 7,
+ TCPServiceError = 8,
+ lastReason = 32767
+};
+
+typedef unsigned short TCPTerminationReason;
+
+typedef pascal void (*TCPNotifyProcPtr)(StreamPtr tcpStream, unsigned short eventCode, Ptr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg);
+
+#if GENERATINGCFM
+typedef UniversalProcPtr TCPNotifyUPP;
+#else
+typedef TCPNotifyProcPtr TCPNotifyUPP;
+#endif
+
+typedef unsigned short tcp_port;
+
+/* ValidityFlags */
+
+enum {
+ timeoutValue = 0x80,
+ timeoutAction = 0x40,
+ typeOfService = 0x20,
+ precedence = 0x10
+};
+
+/* TOSFlags */
+enum {
+ lowDelay = 0x01,
+ throughPut = 0x02,
+ reliability = 0x04
+};
+
+struct TCPCreatePB {
+ Ptr rcvBuff;
+ unsigned long rcvBuffLen;
+ TCPNotifyUPP notifyProc;
+ Ptr userDataPtr;
+};
+typedef struct TCPCreatePB TCPCreatePB;
+
+struct TCPOpenPB {
+ SInt8 ulpTimeoutValue;
+ SInt8 ulpTimeoutAction;
+ SInt8 validityFlags;
+ SInt8 commandTimeoutValue;
+ ip_addr remoteHost;
+ tcp_port remotePort;
+ ip_addr localHost;
+ tcp_port localPort;
+ SInt8 tosFlags;
+ SInt8 precedence;
+ Boolean dontFrag;
+ SInt8 timeToLive;
+ SInt8 security;
+ SInt8 optionCnt;
+ SInt8 options[40];
+ Ptr userDataPtr;
+};
+typedef struct TCPOpenPB TCPOpenPB;
+
+struct TCPSendPB {
+ SInt8 ulpTimeoutValue;
+ SInt8 ulpTimeoutAction;
+ SInt8 validityFlags;
+ Boolean pushFlag;
+ Boolean urgentFlag;
+ SInt8 filler; /* Filler for proper byte alignment */
+ Ptr wdsPtr;
+ unsigned long sendFree;
+ unsigned short sendLength;
+ Ptr userDataPtr;
+};
+typedef struct TCPSendPB TCPSendPB;
+
+/* for receive and return rcv buff calls */
+/* Note: the filler in the following structure is in a different location than */
+/* that specified in the Programmer's Guide. */
+struct TCPReceivePB {
+ SInt8 commandTimeoutValue;
+ Boolean markFlag;
+ Boolean urgentFlag;
+ SInt8 filler; /* Filler for proper byte alignment */
+ Ptr rcvBuff;
+ unsigned short rcvBuffLen;
+ Ptr rdsPtr;
+ unsigned short rdsLength;
+ unsigned short secondTimeStamp;
+ Ptr userDataPtr;
+};
+typedef struct TCPReceivePB TCPReceivePB;
+
+struct TCPClosePB {
+ SInt8 ulpTimeoutValue;
+ SInt8 ulpTimeoutAction;
+ SInt8 validityFlags;
+ SInt8 filler; /* Filler for proper byte alignment */
+ Ptr userDataPtr;
+};
+typedef struct TCPClosePB TCPClosePB;
+
+struct HistoBucket {
+ unsigned short value;
+ unsigned long counter;
+};
+typedef struct HistoBucket HistoBucket;
+
+
+enum {
+ NumOfHistoBuckets = 7
+};
+
+struct TCPConnectionStats {
+ unsigned long dataPktsRcvd;
+ unsigned long dataPktsSent;
+ unsigned long dataPktsResent;
+ unsigned long bytesRcvd;
+ unsigned long bytesRcvdDup;
+ unsigned long bytesRcvdPastWindow;
+ unsigned long bytesSent;
+ unsigned long bytesResent;
+ unsigned short numHistoBuckets;
+ struct HistoBucket sentSizeHisto[NumOfHistoBuckets];
+ unsigned short lastRTT;
+ unsigned short tmrSRTT;
+ unsigned short rttVariance;
+ unsigned short tmrRTO;
+ SInt8 sendTries;
+ SInt8 sourchQuenchRcvd;
+};
+typedef struct TCPConnectionStats TCPConnectionStats;
+
+struct TCPStatusPB {
+ SInt8 ulpTimeoutValue;
+ SInt8 ulpTimeoutAction;
+ long unused;
+ ip_addr remoteHost;
+ tcp_port remotePort;
+ ip_addr localHost;
+ tcp_port localPort;
+ SInt8 tosFlags;
+ SInt8 precedence;
+ SInt8 connectionState;
+ SInt8 filler; /* Filler for proper byte alignment */
+ unsigned short sendWindow;
+ unsigned short rcvWindow;
+ unsigned short amtUnackedData;
+ unsigned short amtUnreadData;
+ Ptr securityLevelPtr;
+ unsigned long sendUnacked;
+ unsigned long sendNext;
+ unsigned long congestionWindow;
+ unsigned long rcvNext;
+ unsigned long srtt;
+ unsigned long lastRTT;
+ unsigned long sendMaxSegSize;
+ struct TCPConnectionStats *connStatPtr;
+ Ptr userDataPtr;
+};
+typedef struct TCPStatusPB TCPStatusPB;
+
+struct TCPAbortPB {
+ Ptr userDataPtr;
+};
+typedef struct TCPAbortPB TCPAbortPB;
+
+struct TCPParam {
+ unsigned long tcpRtoA;
+ unsigned long tcpRtoMin;
+ unsigned long tcpRtoMax;
+ unsigned long tcpMaxSegSize;
+ unsigned long tcpMaxConn;
+ unsigned long tcpMaxWindow;
+};
+typedef struct TCPParam TCPParam;
+
+struct TCPStats {
+ unsigned long tcpConnAttempts;
+ unsigned long tcpConnOpened;
+ unsigned long tcpConnAccepted;
+ unsigned long tcpConnClosed;
+ unsigned long tcpConnAborted;
+ unsigned long tcpOctetsIn;
+ unsigned long tcpOctetsOut;
+ unsigned long tcpOctetsInDup;
+ unsigned long tcpOctetsRetrans;
+ unsigned long tcpInputPkts;
+ unsigned long tcpOutputPkts;
+ unsigned long tcpDupPkts;
+ unsigned long tcpRetransPkts;
+};
+typedef struct TCPStats TCPStats;
+
+typedef StreamPtr *StreamPPtr;
+
+struct TCPGlobalInfoPB {
+ struct TCPParam *tcpParamPtr;
+ struct TCPStats *tcpStatsPtr;
+ StreamPPtr tcpCDBTable[1];
+ Ptr userDataPtr;
+ unsigned short maxTCPConnections;
+};
+typedef struct TCPGlobalInfoPB TCPGlobalInfoPB;
+
+typedef void (*TCPIOCompletionProcPtr)(struct TCPiopb *iopb);
+
+#if GENERATINGCFM
+typedef UniversalProcPtr TCPIOCompletionUPP;
+#else
+typedef TCPIOCompletionProcPtr TCPIOCompletionUPP;
+#endif
+
+struct TCPiopb {
+ SInt8 fill12[12];
+ TCPIOCompletionUPP ioCompletion;
+ short ioResult;
+ Ptr ioNamePtr;
+ short ioVRefNum;
+ short ioCRefNum;
+ short csCode;
+ StreamPtr tcpStream;
+ union {
+ struct TCPCreatePB create;
+ struct TCPOpenPB open;
+ struct TCPSendPB send;
+ struct TCPReceivePB receive;
+ struct TCPClosePB close;
+ struct TCPAbortPB abort;
+ struct TCPStatusPB status;
+ struct TCPGlobalInfoPB globalInfo;
+ } csParam;
+};
+typedef struct TCPiopb TCPiopb;
+
+
+enum {
+ UDPCreate = 20,
+ UDPRead = 21,
+ UDPBfrReturn = 22,
+ UDPWrite = 23,
+ UDPRelease = 24,
+ UDPMaxMTUSize = 25,
+ UDPStatus = 26,
+ UDPMultiCreate = 27,
+ UDPMultiSend = 28,
+ UDPMultiRead = 29,
+ UDPCtlMax = 29
+};
+
+enum {
+ UDPDataArrival = 1,
+ UDPICMPReceived = 2,
+ lastUDPEvent = 32767
+};
+
+typedef unsigned short UDPEventCode;
+
+typedef pascal void (*UDPNotifyProcPtr)(StreamPtr udpStream, unsigned short eventCode, Ptr userDataPtr, struct ICMPReport *icmpMsg);
+
+#if GENERATINGCFM
+typedef UniversalProcPtr UDPNotifyUPP;
+#else
+typedef UDPNotifyProcPtr UDPNotifyUPP;
+#endif
+
+typedef unsigned short udp_port;
+
+/* for create and release calls */
+struct UDPCreatePB {
+ Ptr rcvBuff;
+ unsigned long rcvBuffLen;
+ UDPNotifyUPP notifyProc;
+ unsigned short localPort;
+ Ptr userDataPtr;
+ udp_port endingPort;
+};
+typedef struct UDPCreatePB UDPCreatePB;
+
+struct UDPSendPB {
+ unsigned short reserved;
+ ip_addr remoteHost;
+ udp_port remotePort;
+ Ptr wdsPtr;
+ Boolean checkSum;
+ SInt8 filler; /* Filler for proper byte alignment */
+ unsigned short sendLength;
+ Ptr userDataPtr;
+ udp_port localPort;
+};
+typedef struct UDPSendPB UDPSendPB;
+
+/* for receive and buffer return calls */
+struct UDPReceivePB {
+ unsigned short timeOut;
+ ip_addr remoteHost;
+ udp_port remotePort;
+ Ptr rcvBuff;
+ unsigned short rcvBuffLen;
+ unsigned short secondTimeStamp;
+ Ptr userDataPtr;
+ ip_addr destHost; /* only for use with multi rcv */
+ udp_port destPort; /* only for use with multi rcv */
+};
+typedef struct UDPReceivePB UDPReceivePB;
+
+struct UDPMTUPB {
+ unsigned short mtuSize;
+ ip_addr remoteHost;
+ Ptr userDataPtr;
+};
+typedef struct UDPMTUPB UDPMTUPB;
+
+typedef void (*UDPIOCompletionProcPtr)(struct UDPiopb *iopb);
+
+#if GENERATINGCFM
+typedef UniversalProcPtr UDPIOCompletionUPP;
+#else
+typedef UDPIOCompletionProcPtr UDPIOCompletionUPP;
+#endif
+
+struct UDPiopb {
+ SInt8 fill12[12];
+ UDPIOCompletionUPP ioCompletion;
+ short ioResult;
+ Ptr ioNamePtr;
+ short ioVRefNum;
+ short ioCRefNum;
+ short csCode;
+ StreamPtr udpStream;
+ union {
+ struct UDPCreatePB create;
+ struct UDPSendPB send;
+ struct UDPReceivePB receive;
+ struct UDPMTUPB mtu;
+ } csParam;
+};
+typedef struct UDPiopb UDPiopb;
+
+enum {
+ uppGetIPIOCompletionProcInfo = kCStackBased
+ | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(struct GetAddrParamBlock*))),
+ uppIPIOCompletionProcInfo = kCStackBased
+ | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(struct ICMPParamBlock*))),
+ uppICMPEchoNotifyProcInfo = kPascalStackBased
+ | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(struct ICMPParamBlock*))),
+ uppTCPNotifyProcInfo = kPascalStackBased
+ | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(StreamPtr)))
+ | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(unsigned short)))
+ | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(Ptr)))
+ | STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(unsigned short)))
+ | STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(struct ICMPReport*))),
+ uppTCPIOCompletionProcInfo = kCStackBased
+ | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(struct TCPiopb*))),
+ uppUDPNotifyProcInfo = kPascalStackBased
+ | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(StreamPtr)))
+ | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(unsigned short)))
+ | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(Ptr)))
+ | STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(struct ICMPReport*))),
+ uppUDPIOCompletionProcInfo = kCStackBased
+ | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(struct UDPiopb*)))
+};
+
+#if GENERATINGCFM
+#define NewGetIPIOCompletionProc(userRoutine) \
+ (GetIPIOCompletionUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppGetIPIOCompletionProcInfo, GetCurrentArchitecture())
+#define NewIPIOCompletionProc(userRoutine) \
+ (IPIOCompletionUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppIPIOCompletionProcInfo, GetCurrentArchitecture())
+#define NewICMPEchoNotifyProc(userRoutine) \
+ (ICMPEchoNotifyUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppICMPEchoNotifyProcInfo, GetCurrentArchitecture())
+#define NewTCPNotifyProc(userRoutine) \
+ (TCPNotifyUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppTCPNotifyProcInfo, GetCurrentArchitecture())
+#define NewTCPIOCompletionProc(userRoutine) \
+ (TCPIOCompletionUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppTCPIOCompletionProcInfo, GetCurrentArchitecture())
+#define NewUDPNotifyProc(userRoutine) \
+ (UDPNotifyUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppUDPNotifyProcInfo, GetCurrentArchitecture())
+#define NewUDPIOCompletionProc(userRoutine) \
+ (UDPIOCompletionUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppUDPIOCompletionProcInfo, GetCurrentArchitecture())
+#else
+#define NewGetIPIOCompletionProc(userRoutine) \
+ ((GetIPIOCompletionUPP) (userRoutine))
+#define NewIPIOCompletionProc(userRoutine) \
+ ((IPIOCompletionUPP) (userRoutine))
+#define NewICMPEchoNotifyProc(userRoutine) \
+ ((ICMPEchoNotifyUPP) (userRoutine))
+#define NewTCPNotifyProc(userRoutine) \
+ ((TCPNotifyUPP) (userRoutine))
+#define NewTCPIOCompletionProc(userRoutine) \
+ ((TCPIOCompletionUPP) (userRoutine))
+#define NewUDPNotifyProc(userRoutine) \
+ ((UDPNotifyUPP) (userRoutine))
+#define NewUDPIOCompletionProc(userRoutine) \
+ ((UDPIOCompletionUPP) (userRoutine))
+#endif
+
+#if GENERATINGCFM
+#define CallGetIPIOCompletionProc(userRoutine, iopb) \
+ CallUniversalProc((UniversalProcPtr)(userRoutine), uppGetIPIOCompletionProcInfo, (iopb))
+#define CallIPIOCompletionProc(userRoutine, iopb) \
+ CallUniversalProc((UniversalProcPtr)(userRoutine), uppIPIOCompletionProcInfo, (iopb))
+#define CallICMPEchoNotifyProc(userRoutine, iopb) \
+ CallUniversalProc((UniversalProcPtr)(userRoutine), uppICMPEchoNotifyProcInfo, (iopb))
+#define CallTCPNotifyProc(userRoutine, tcpStream, eventCode, userDataPtr, terminReason, icmpMsg) \
+ CallUniversalProc((UniversalProcPtr)(userRoutine), uppTCPNotifyProcInfo, (tcpStream), (eventCode), (userDataPtr), (terminReason), (icmpMsg))
+#define CallTCPIOCompletionProc(userRoutine, iopb) \
+ CallUniversalProc((UniversalProcPtr)(userRoutine), uppTCPIOCompletionProcInfo, (iopb))
+#define CallUDPNotifyProc(userRoutine, udpStream, eventCode, userDataPtr, icmpMsg) \
+ CallUniversalProc((UniversalProcPtr)(userRoutine), uppUDPNotifyProcInfo, (udpStream), (eventCode), (userDataPtr), (icmpMsg))
+#define CallUDPIOCompletionProc(userRoutine, iopb) \
+ CallUniversalProc((UniversalProcPtr)(userRoutine), uppUDPIOCompletionProcInfo, (iopb))
+#else
+#define CallGetIPIOCompletionProc(userRoutine, iopb) \
+ (*(userRoutine))((iopb))
+#define CallIPIOCompletionProc(userRoutine, iopb) \
+ (*(userRoutine))((iopb))
+#define CallICMPEchoNotifyProc(userRoutine, iopb) \
+ (*(userRoutine))((iopb))
+#define CallTCPNotifyProc(userRoutine, tcpStream, eventCode, userDataPtr, terminReason, icmpMsg) \
+ (*(userRoutine))((tcpStream), (eventCode), (userDataPtr), (terminReason), (icmpMsg))
+#define CallTCPIOCompletionProc(userRoutine, iopb) \
+ (*(userRoutine))((iopb))
+#define CallUDPNotifyProc(userRoutine, udpStream, eventCode, userDataPtr, icmpMsg) \
+ (*(userRoutine))((udpStream), (eventCode), (userDataPtr), (icmpMsg))
+#define CallUDPIOCompletionProc(userRoutine, iopb) \
+ (*(userRoutine))((iopb))
+#endif
+
+
+#if PRAGMA_IMPORT_SUPPORTED
+#pragma import off
+#endif
+
+#if PRAGMA_ALIGN_SUPPORTED
+#pragma options align=reset
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MACTCP__ */
--- main.c Wed Nov 17 15:36:46 2021
+++ main.c Wed Nov 17 15:36:46 2021
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2020-2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "subtext.h"
+#include "console.h"
+#include "session.h"
+#include "util.h"
+
+MenuHandle file_menu;
+short quitting = 0;
+struct console *cur_console = NULL;
+struct config config;
+
+void handle_menu(long menu_id);
+void update_menu(void);
+
+int
+main(void)
+{
+ Handle mbar;
+ MenuHandle apple_menu;
+ EventRecord event;
+ WindowPtr event_win;
+ GrafPtr old_port;
+ short event_in, n;
+ char key;
+
+ InitGraf(&thePort);
+ InitFonts();
+ FlushEvents(everyEvent, 0);
+ InitWindows();
+ InitMenus();
+ TEInit();
+ InitDialogs(0);
+ InitCursor();
+ MaxApplZone();
+
+ err_init();
+
+ mbar = GetNewMBar(MBAR_ID);
+ SetMenuBar(mbar);
+ apple_menu = GetMHandle(APPLE_MENU_ID);
+ AddResMenu(apple_menu, 'DRVR');
+ file_menu = GetMHandle(FILE_MENU_ID);
+ update_menu();
+ DrawMenuBar();
+
+ /* TODO: get these from resources */
+ memset(&config, 0, sizeof(config));
+ strcpy(config.name, "Kludge BBS");
+ strcpy(config.hostname, "klud.ge");
+
+ while (!quitting) {
+ WaitNextEvent(everyEvent, &event, 5L, 0L);
+
+ switch (event.what) {
+ case nullEvent:
+ if (!cur_console)
+ cur_console = console_init();
+ console_idle(cur_console);
+ for (n = 0; n < nsessions; n++)
+ session_idle(sessions[n]);
+ break;
+ case keyDown:
+ case autoKey:
+ key = event.message & charCodeMask;
+ if ((event.modifiers & cmdKey) != 0)
+ handle_menu(MenuKey(key));
+ break;
+ case mouseDown:
+ event_in = FindWindow(event.where, &event_win);
+
+ switch (event_in) {
+ case inMenuBar:
+ handle_menu(MenuSelect(event.where));
+ break;
+ case inSysWindow:
+ SystemClick(&event, event_win);
+ break;
+ case inDrag:
+ DragWindow(event_win, event.where, &screenBits.bounds);
+ break;
+ case inGoAway:
+ if (TrackGoAway(event_win, event.where)) {
+ /* TODO: close */
+ }
+ break;
+ case inContent:
+ if (event_win != FrontWindow())
+ SelectWindow(event_win);
+ break;
+ }
+ break;
+ case updateEvt:
+ case activateEvt:
+ event_win = (WindowPtr)event.message;
+
+ if (event.what == updateEvt) {
+ GetPort(&old_port);
+ SetPort(event_win);
+ BeginUpdate(event_win);
+ }
+
+ if (cur_console)
+ console_update(cur_console, &event);
+
+ if (event.what == updateEvt) {
+ EndUpdate(event_win);
+ SetPort(old_port);
+ }
+ break;
+ case app4Evt:
+ if (HiWord(event.message) & (1 << 8)) {
+ /* multifinder suspend/resume */
+ switch (event.message & (1 << 0)) {
+ case 0:
+ /* suspend */
+ if (cur_console)
+ console_suspend(cur_console);
+ break;
+ case 1:
+ /* resume */
+ if (cur_console)
+ console_resume(cur_console);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void
+handle_menu(long menu_id)
+{
+ switch (HiWord(menu_id)) {
+ case APPLE_MENU_ID:
+ switch (LoWord(menu_id)) {
+ case APPLE_MENU_ABOUT_ID: {
+ VersRecHndl vers;
+ char vers_s[255];
+ char short_vers[255] = { 0 };
+ short vlen;
+
+ if ((vers = (VersRecHndl)GetResource('vers', 1))) {
+ /*
+ * vers "long version string" is a pascal string after the
+ * short version pascal string
+ */
+ HLock(vers);
+ vlen = (*vers)->shortVersion[0];
+ memcpy(short_vers, (*vers)->shortVersion + vlen + 1,
+ sizeof((*vers)->shortVersion) - vlen - 1);
+ PtoCstr(short_vers);
+ sprintf(vers_s, "%s %s", PROGRAM_NAME, short_vers);
+ ReleaseResource(vers);
+ note("%s", vers_s);
+ } else
+ warnx("Can't find version number!");
+ break;
+ }
+ }
+ break;
+ case FILE_MENU_ID:
+ switch (LoWord(menu_id)) {
+ case FILE_MENU_QUIT_ID:
+ quitting = 1;
+ break;
+ }
+ break;
+ }
+
+ HiliteMenu(0);
+}
+
+void
+update_menu(void)
+{
+}
--- session.c Wed Nov 17 15:39:45 2021
+++ session.c Wed Nov 17 15:39:45 2021
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "subtext.h"
+#include "session.h"
+#include "util.h"
+
+struct session **sessions = NULL;
+short nsessions = 0;
+char session_tbuf[512];
+
+void
+session_idle(struct session *s)
+{
+ if (s->obuflen) {
+ s->node_funcs->output(s);
+ if (s->obuflen)
+ return;
+ }
+
+ switch (s->state) {
+ case SESSION_STATE_INIT:
+ session_output(s, "\r\nWelcome to %s (%s)\r\n", config.name,
+ s->node);
+ s->state = SESSION_STATE_LOGIN;
+ break;
+ }
+}
+
+struct session *
+session_create(char *node, struct node_funcs *node_funcs)
+{
+ struct session *session;
+
+ nsessions++;
+ sessions = xreallocarray(sessions, nsessions, sizeof(struct session));
+ session = xmalloczero(sizeof(struct session));
+ sessions[nsessions - 1] = session;
+
+ session->state = SESSION_STATE_INIT;
+ memcpy(session->node, node, sizeof(session->node));
+
+ session->node_funcs = node_funcs;
+
+ return session;
+}
+
+short
+session_output(struct session *session, const char *format, ...)
+{
+ va_list ap;
+ short len;
+
+ va_start(ap, format);
+ len = vsprintf(session_tbuf, format, ap);
+ va_end(ap);
+ if (len >= sizeof(session_tbuf))
+ err(1, "sprintf overflow in session_output!");
+
+ if (len > (sizeof(session->obuf) - session->obuflen)) {
+ warn("session_output is out of room!");
+ len = sizeof(session->obuf) - session->obuflen;
+ }
+
+ memcpy(session->obuf + session->obuflen, session_tbuf, len);
+ session->obuflen += len;
+
+ return len;
+}
--- session.h Wed Nov 17 13:31:32 2021
+++ session.h Wed Nov 17 13:31:32 2021
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SESSION_H__
+#define __SESSION_H__
+
+enum session_state {
+ SESSION_STATE_INIT,
+ SESSION_STATE_LOGIN,
+ SESSION_STATE_WELCOME
+};
+
+struct node_funcs {
+ short (*output)(struct session *session);
+ short (*read)(struct session *session);
+};
+
+struct session {
+ char node[32];
+ char obuf[64];
+ char ibuf[64];
+ short state;
+ short obuflen;
+ short ibuflen;
+ void *cookie;
+ struct node_funcs *node_funcs;
+};
+
+extern struct session **sessions;
+extern short nsessions;
+
+struct session *session_create(char *node, struct node_funcs *node_funcs);
+void session_idle(struct session *session);
+short session_output(struct session *session, const char *format, ...);
+
+#endif /* __SESSION_H__ */
--- subtext.π.r Mon Nov 22 10:07:07 2021
+++ subtext.π.r Mon Nov 22 10:07:07 2021
@@ -0,0 +1,56 @@
+data 'MENU' (128) {
+ $"0080 0000 0000 0000 0000 FFFF FFFB 0114" /* .Ä.............. */
+ $"1041 626F 7574 2053 7562 7465 7874 2E2E" /* .About Subtext.. */
+ $"2E00 0000 0001 2D00 0000 0000" /* ......-..... */
+};
+
+data 'MENU' (129) {
+ $"0081 0000 0000 0000 0000 FFFF FFFF 0446" /* .Å.............F */
+ $"696C 6504 5175 6974 0051 0000 00" /* ile.Quit.Q... */
+};
+
+data 'MBAR' (128) {
+ $"0002 0080 0081" /* ...Ä.Å */
+};
+
+data 'ALRT' (129) {
+ $"0078 006C 00DA 0196 0081 5555 300A" /* .x.l...ñ.ÅUU0. */
+};
+
+data 'DITL' (128) {
+ $"0001 0000 0000 0046 012C 005A 0166 0402" /* .......F.,.Z.f.. */
+ $"4F4B 0000 0000 0014 0023 004A 0143 0802" /* OK.......#.J.C.. */
+ $"5E30" /* ^0 */
+};
+
+data 'DITL' (129) {
+ $"0001 0000 0000 0046 00E6 005A 0120 0402" /* .......F...Z. .. */
+ $"4F4B 0000 0000 000A 003C 0040 011E 0802" /* OK.......<.@.... */
+ $"5E30" /* ^0 */
+};
+
+data 'WIND' (128) {
+ $"002B 0060 012B 01A0 0003 0100 0000 0000" /* .+.`.+.†........ */
+ $"0000 0A41 626F 7574 2043 6172 6C56 280A" /* ...About CarlV(. */
+};
+
+data 'vers' (1) {
+ $"0010 6000 0000 0330 2E31 1D30 2E31 20A9" /* ..`....0.1.0.1 © */
+ $"2032 3032 302D 3230 3231 2C20 6A6F 7368" /* 2020-2021, josh */
+ $"7561 2073 7465 696E" /* ua stein */
+};
+
+data 'DLOG' (128) {
+ $"0064 0083 00A0 017D 0001 0100 0100 0000" /* .d.É.†.}........ */
+ $"0000 0080 001A 280A" /* ...Ä..(. */
+};
+
+data 'TMPL' (128, "USER") {
+ $"0269 6444 5752 4408 7573 6572 6E61 6D65" /* .idDWRD.username */
+ $"4353 5452" /* CSTR */
+};
+
+data 'USER' (128) {
+ $"0001 6A63 7300" /* ..jcs. */
+};
+
--- subtext.h Wed Nov 17 15:40:42 2021
+++ subtext.h Wed Nov 17 15:40:42 2021
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SUBTEXT_H__
+#define __SUBTEXT_H__
+
+#define PROGRAM_NAME "Subtext"
+
+#define MBAR_ID 128
+
+#define APPLE_MENU_ID 128
+#define APPLE_MENU_ABOUT_ID 1
+
+#define FILE_MENU_ID 129
+#define FILE_MENU_QUIT_ID 1
+
+struct config {
+ char name[32];
+ char phone_number[16];
+ char hostname[32];
+};
+extern struct config config;
+
+extern MenuHandle file_menu;
+
+#endif /* __SUBTEXT_H__ */
--- tcp.c Tue Nov 16 12:53:10 2021
+++ tcp.c Tue Nov 16 12:53:10 2021
@@ -0,0 +1,527 @@
+#include <stdio.h>
+#include <string.h>
+#include "tcp.h"
+
+#define RCV_BUFFER_SIZE 1024
+#define TCP_BUFFER_SIZE 8192
+#define OPEN_TIMEOUT 60
+
+short gIPPDriverRefNum;
+
+pascal void StrToAddrMarkDone(struct hostInfo *hi, char *data);
+
+unsigned long resolved_ip;
+
+OSErr
+_TCPInit(void)
+{
+ ParamBlockRec pb;
+ OSErr osErr;
+
+ memset(&pb, 0, sizeof(pb));
+
+ gIPPDriverRefNum = -1;
+
+ pb.ioParam.ioCompletion = 0;
+ pb.ioParam.ioNamePtr = "\p.IPP";
+ pb.ioParam.ioPermssn = fsCurPerm;
+
+ osErr = PBOpen(&pb, false);
+ if (noErr == osErr) {
+ gIPPDriverRefNum = pb.ioParam.ioRefNum;
+ }
+
+ return osErr;
+}
+
+OSErr
+_TCPGetOurIP(ip_addr *ip, long *netMask)
+{
+ OSErr osErr;
+ GetAddrParamBlock pb;
+ int i;
+
+ memset(&pb, 0, sizeof(pb));
+
+ pb.csCode = ipctlGetAddr;
+ pb.ioCRefNum = gIPPDriverRefNum;
+
+ osErr = PBControl((ParmBlkPtr)&pb, true);
+ while (pb.ioResult > 0)
+ ;
+
+ if (pb.ioResult != noErr)
+ return pb.ioResult;
+
+ if (ip != NULL)
+ *ip = pb.ourAddress;
+ if (netMask != NULL)
+ *netMask = pb.ourNetMask;
+
+ return osErr;
+}
+
+OSErr
+_TCPCreate(TCPiopb *pb, StreamPtr *stream, Ptr rcvBufPtr, long rcvBufLen,
+ TCPNotifyProc aNotifyProc, Ptr userDataPtr,
+ TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ OSErr osErr;
+
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPCreate;
+
+ pb->csParam.create.rcvBuff = rcvBufPtr;
+ pb->csParam.create.rcvBuffLen = rcvBufLen;
+ pb->csParam.create.notifyProc = aNotifyProc;
+ pb->csParam.create.userDataPtr = userDataPtr;
+
+ osErr = PBControl((ParmBlkPtr)pb, async);
+ if (!async && (noErr == osErr)) {
+ *stream = pb->tcpStream;
+ }
+
+ return osErr;
+}
+
+/* listen for an incoming connection */
+OSErr
+_TCPPassiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr *remoteIP,
+ tcp_port *remotePort, ip_addr *localIP, tcp_port *localPort,
+ Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ OSErr osErr;
+ short index;
+
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPPassiveOpen;
+ pb->tcpStream = stream;
+
+ pb->csParam.open.ulpTimeoutValue = 0;
+ pb->csParam.open.ulpTimeoutAction = 1;
+ pb->csParam.open.validityFlags = 0xC0;
+ pb->csParam.open.commandTimeoutValue = 30;
+ pb->csParam.open.remoteHost = *remoteIP;
+ pb->csParam.open.remotePort = *remotePort;
+ pb->csParam.open.localHost = 0;
+ pb->csParam.open.localPort = *localPort;
+ pb->csParam.open.tosFlags = 0;
+ pb->csParam.open.precedence = 0;
+ pb->csParam.open.dontFrag = 0;
+ pb->csParam.open.timeToLive = 0;
+ pb->csParam.open.security = 0;
+ pb->csParam.open.optionCnt = 0;
+ for (index = 0; index < sizeof(pb->csParam.open.options); ++index)
+ pb->csParam.open.options[index] = 0;
+ pb->csParam.open.userDataPtr = userData;
+
+ osErr = PBControl((ParmBlkPtr) pb, async);
+ if (!async && (noErr == osErr)) {
+ *remoteIP = pb->csParam.open.remoteHost;
+ *remotePort = pb->csParam.open.remotePort;
+ *localIP = pb->csParam.open.localHost;
+ *localPort = pb->csParam.open.localPort;
+ }
+
+ return osErr;
+}
+
+/* make an outgoing connection */
+OSErr
+_TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr remoteIP,
+ tcp_port remotePort, ip_addr *localIP, tcp_port *localPort,
+ Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ OSErr osErr;
+ short index;
+
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPActiveOpen;
+ pb->tcpStream = stream;
+
+ pb->csParam.open.ulpTimeoutValue = 30;
+ pb->csParam.open.ulpTimeoutAction = 1;
+ pb->csParam.open.validityFlags = 0xC0;
+#if 0
+ /* not available with this csCode */
+ pb->csParam.open.commandTimeoutValue = 30;
+#endif
+ pb->csParam.open.remoteHost = remoteIP;
+ pb->csParam.open.remotePort = remotePort;
+ pb->csParam.open.localHost = 0;
+ pb->csParam.open.localPort = *localPort;
+ pb->csParam.open.tosFlags = 0;
+ pb->csParam.open.precedence = 0;
+ pb->csParam.open.dontFrag = 0;
+ pb->csParam.open.timeToLive = 0;
+ pb->csParam.open.security = 0;
+ pb->csParam.open.optionCnt = 0;
+ for (index = 0; index < sizeof(pb->csParam.open.options); ++index)
+ pb->csParam.open.options[index] = 0;
+ pb->csParam.open.userDataPtr = userData;
+
+ osErr = PBControl((ParmBlkPtr) pb, async);
+ if (!async && (noErr == osErr)) {
+ *localIP = pb->csParam.open.localHost;
+ *localPort = pb->csParam.open.localPort;
+ }
+
+ return osErr;
+}
+
+OSErr
+_TCPSend(TCPiopb *pb, StreamPtr stream, wdsEntry *wdsPtr, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPSend;
+ pb->tcpStream = stream;
+
+ pb->csParam.send.ulpTimeoutValue = 30;
+ pb->csParam.send.ulpTimeoutAction = 1;
+ pb->csParam.send.validityFlags = 0xC0;
+ pb->csParam.send.pushFlag = 1; /* XXX */
+ pb->csParam.send.urgentFlag = 0;
+ pb->csParam.send.wdsPtr = (Ptr)wdsPtr;
+ pb->csParam.send.sendFree = 0;
+ pb->csParam.send.sendLength = 0;
+ pb->csParam.send.userDataPtr = userData;
+
+ return PBControl((ParmBlkPtr)pb, async);
+}
+
+OSErr
+_TCPNoCopyRcv(TCPiopb *pb, StreamPtr stream, Ptr rdsPtr,
+ unsigned short rdsLength, Ptr userData, TCPIOCompletionProc ioCompletion,
+ Boolean async)
+{
+ OSErr osErr;
+
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPNoCopyRcv;
+ pb->tcpStream = stream;
+
+ pb->csParam.receive.commandTimeoutValue = 30;
+ pb->csParam.receive.urgentFlag = 0;
+ pb->csParam.receive.markFlag = 0;
+ pb->csParam.receive.rdsPtr = rdsPtr;
+ pb->csParam.receive.rdsLength = rdsLength;
+ pb->csParam.receive.userDataPtr = userData;
+
+ return PBControl((ParmBlkPtr)pb, async);
+}
+
+OSErr
+_TCPRcv(TCPiopb *pb, StreamPtr stream, Ptr rcvBufPtr,
+ unsigned short *rcvBufLen, Ptr userData, TCPIOCompletionProc ioCompletion,
+ Boolean async)
+{
+ OSErr osErr;
+
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPRcv;
+ pb->tcpStream = stream;
+
+ pb->csParam.receive.commandTimeoutValue = 30;
+ pb->csParam.receive.urgentFlag = 0;
+ pb->csParam.receive.markFlag = 0;
+ pb->csParam.receive.rcvBuff = rcvBufPtr;
+ pb->csParam.receive.rcvBuffLen = *rcvBufLen;
+ pb->csParam.receive.userDataPtr = userData;
+
+ osErr = PBControl((ParmBlkPtr)pb, async);
+ if (!async)
+ *rcvBufLen = pb->csParam.receive.rcvBuffLen;
+
+ return osErr;
+}
+
+OSErr
+_TCPBfrReturn(TCPiopb *pb, StreamPtr stream, Ptr rdsPtr, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPRcvBfrReturn;
+ pb->tcpStream = stream;
+
+ pb->csParam.receive.rdsPtr = rdsPtr;
+ pb->csParam.receive.userDataPtr = userData;
+
+ return PBControl((ParmBlkPtr)pb, async);
+}
+
+OSErr
+_TCPClose(TCPiopb *pb, StreamPtr stream, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPClose;
+ pb->tcpStream = stream;
+
+ pb->csParam.close.ulpTimeoutValue = 30;
+ pb->csParam.close.ulpTimeoutAction = 1;
+ pb->csParam.close.validityFlags = 0xC0;
+ pb->csParam.close.userDataPtr = userData;
+
+ return PBControl((ParmBlkPtr)pb, async);
+}
+
+OSErr
+_TCPAbort(TCPiopb *pb, StreamPtr stream, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPAbort;
+ pb->tcpStream = stream;
+
+ pb->csParam.abort.userDataPtr = userData;
+
+ return PBControl((ParmBlkPtr)pb, async);
+}
+
+OSErr
+_TCPStatus(TCPiopb *pb, StreamPtr stream, struct TCPStatusPB *status,
+ Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ OSErr osErr;
+
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPStatus;
+ pb->tcpStream = stream;
+ pb->csParam.status.userDataPtr = userData;
+
+ osErr = PBControl((ParmBlkPtr)pb, async);
+ if (!async && (noErr == osErr)) {
+ *status = pb->csParam.status;
+ }
+
+ return osErr;
+}
+
+OSErr
+_TCPRelease(TCPiopb *pb, StreamPtr stream, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async)
+{
+ OSErr osErr;
+
+ memset(pb, 0, sizeof(*pb));
+
+ pb->ioCompletion = ioCompletion;
+ pb->ioCRefNum = gIPPDriverRefNum;
+ pb->csCode = TCPRelease;
+ pb->tcpStream = stream;
+
+ pb->csParam.status.userDataPtr = userData;
+
+ osErr = PBControl((ParmBlkPtr)pb, async);
+
+ return osErr;
+}
+
+/* convenience functions */
+
+static pascal void
+StrToAddrMarkDone(struct hostInfo *hi, char *data)
+{
+ volatile int *done = (int *)data;
+ *done = 1;
+}
+
+OSErr
+TCPResolveName(char **name, unsigned long *ipAddress)
+{
+ OSErr osErr;
+ struct hostInfo aHostInfo;
+ volatile int done = 0;
+
+ osErr = OpenResolver(nil);
+ if (osErr)
+ return osErr;
+
+ osErr = StrToAddr(*name, &aHostInfo, (ResultProcPtr)StrToAddrMarkDone,
+ (char *)&done);
+
+ if (osErr == cacheFault) {
+ /* StrToAddrMarkDone will set done when DNS resolution finishes */
+ while (!done)
+ ;
+ }
+
+ if ((aHostInfo.rtnCode == noErr) || (aHostInfo.rtnCode == cacheFault)) {
+ /* use the first IP address for this host */
+ *ipAddress = aHostInfo.addr[0];
+ osErr = noErr;
+ } else
+ osErr = aHostInfo.rtnCode;
+
+ CloseResolver();
+ return osErr;
+}
+
+long
+ip2long(char *ip)
+{
+ long address = 0;
+ int dotcount = 0, i;
+ unsigned int b = 0;
+
+ for (i = 0; ip[i] != 0; i++) {
+ if (ip[i] == '.') {
+ if (++dotcount > 3)
+ return (0);
+ address <<= 8;
+ address |= b;
+ b = 0;
+ } else if (ip[i] >= '0' && ip[i] <= '9') {
+ b *= 10;
+ b += (ip[i] - '0');
+ if (b > 255)
+ return (0);
+ } else
+ return (0);
+ }
+
+ if (dotcount != 3)
+ return (0);
+ address <<= 8;
+ address |= b;
+ return address;
+}
+
+void
+long2ip(unsigned long num, char *ip)
+{
+ unsigned char *tmp = (unsigned char *)#
+ (void)sprintf(ip, (const char *)"%d.%d.%d.%d", tmp[0], tmp[1], tmp[2], tmp[3]);
+}
+
+#define SOCKS_VERSION_SOCKS5 0x5
+#define SOCKS_METHOD_AUTH_NONE 0x0
+#define SOCKS_REQUEST_CONNECT 0x1
+#define SOCKS_REQUEST_ATYP_DOMAINNAME 0x3
+#define SOCKS_REPLY_SUCCESS 0x0
+
+OSErr
+SOCKS5TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr socks_ip,
+ tcp_port socks_port, char *remote_host, tcp_port remote_port,
+ ip_addr *local_ip, tcp_port *local_port, Ptr user_data,
+ TCPIOCompletionProc io_completion, Boolean async)
+{
+ OSErr err;
+ TCPStatusPB status_pb;
+ wdsEntry wds[2];
+ char data[255] = { 0 };
+ unsigned short len, remote_host_len;
+
+ remote_host_len = strlen(remote_host);
+ if (remote_host_len + 7 > sizeof(data))
+ return -1;
+
+ err = _TCPActiveOpen(pb, stream, socks_ip, socks_port, local_ip,
+ local_port, user_data, io_completion, async);
+ if (err != noErr)
+ return err;
+
+ data[0] = SOCKS_VERSION_SOCKS5;
+ data[1] = 1; /* nmethods */
+ data[2] = SOCKS_METHOD_AUTH_NONE;
+
+ memset(&wds, 0, sizeof(wds));
+ wds[0].ptr = (Ptr)&data;
+ wds[0].length = 3;
+
+ err = _TCPSend(pb, stream, wds, nil, nil, false);
+ if (err)
+ goto fail;
+
+ for (;;) {
+ err = _TCPStatus(pb, stream, &status_pb, nil, nil, false);
+ if (err != noErr)
+ goto fail;
+
+ if (status_pb.amtUnreadData >= 2)
+ break;
+ }
+
+ len = 2;
+ err = _TCPRcv(pb, stream, (Ptr)&data, &len, nil, nil, false);
+ if (err != noErr)
+ goto fail;
+
+ if (data[0] != SOCKS_VERSION_SOCKS5 || data[1] != SOCKS_METHOD_AUTH_NONE)
+ goto fail;
+
+ len = 0;
+ data[len++] = SOCKS_VERSION_SOCKS5;
+ data[len++] = SOCKS_REQUEST_CONNECT;
+ data[len++] = 0; /* reserved */
+ data[len++] = SOCKS_REQUEST_ATYP_DOMAINNAME;
+ data[len++] = remote_host_len;
+ memcpy(data + len, remote_host, remote_host_len);
+ len += remote_host_len;
+ data[len++] = (remote_port >> 8);
+ data[len++] = (remote_port & 0xff);
+
+ memset(&wds, 0, sizeof(wds));
+ wds[0].ptr = (Ptr)&data;
+ wds[0].length = len;
+
+ err = _TCPSend(pb, stream, wds, nil, nil, false);
+ if (err)
+ goto fail;
+
+ for (;;) {
+ err = _TCPStatus(pb, stream, &status_pb, nil, nil, false);
+ if (err != noErr)
+ goto fail;
+
+ if (status_pb.amtUnreadData >= 7)
+ break;
+ }
+
+ len = status_pb.amtUnreadData;
+ if (len > sizeof(data))
+ len = sizeof(data);
+ err = _TCPRcv(pb, stream, (Ptr)&data, &len, nil, nil, false);
+ if (err != noErr)
+ goto fail;
+
+ if (data[0] != SOCKS_VERSION_SOCKS5 || data[1] != SOCKS_REPLY_SUCCESS)
+ goto fail;
+
+ return noErr;
+
+fail:
+ _TCPClose(pb, stream, nil, nil, false);
+ return err;
+}
--- tcp.h Fri Nov 13 14:05:09 2020
+++ tcp.h Fri Nov 13 14:05:09 2020
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1990-1992 by the University of Illinois Board of Trustees
+ */
+
+#include "MacTCP.h"
+#include "AddressXlation.h"
+
+#ifndef __TCP_H__
+#define __TCP_H__
+
+typedef struct
+{
+ Handle next;
+ struct hostInfo hi;
+} HostInfoQ, *HostInfoQPtr, **HostInfoQHandle;
+
+typedef ProcPtr TCPNotifyProc;
+typedef void (*TCPIOCompletionProc)(struct TCPiopb *iopb);
+
+OSErr _TCPInit(void);
+OSErr _TCPGetOurIP(ip_addr *ip, long *netMask);
+OSErr _TCPCreate(TCPiopb *pb, StreamPtr *stream, Ptr rcvBufPtr,
+ long rcvBufLen, TCPNotifyProc aNotifyProc, Ptr userDataPtr,
+ TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPPassiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr *remoteIP,
+ tcp_port *remotePort, ip_addr *localIP, tcp_port *localPort,
+ Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr remoteIP,
+ tcp_port remotePort, ip_addr *localIP, tcp_port *localPort,
+ Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPSend(TCPiopb *pb, StreamPtr stream, wdsEntry *wdsPtr,
+ Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPNoCopyRcv(TCPiopb *pb, StreamPtr stream, Ptr rdsPtr,
+ unsigned short rdsLength, Ptr userData, TCPIOCompletionProc ioCompletion,
+ Boolean async);
+OSErr _TCPRcv(TCPiopb *pb, StreamPtr stream, Ptr rcvBufPtr,
+ unsigned short *rcvBufLen, Ptr userData, TCPIOCompletionProc ioCompletion,
+ Boolean async);
+OSErr _TCPBfrReturn(TCPiopb *pb, StreamPtr stream, Ptr rdsPtr, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPClose(TCPiopb *pb, StreamPtr stream, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPAbort(TCPiopb *pb, StreamPtr stream, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPStatus(TCPiopb *pb, StreamPtr stream, struct TCPStatusPB *status,
+ Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async);
+OSErr _TCPRelease(TCPiopb *pb, StreamPtr stream, Ptr userData,
+ TCPIOCompletionProc ioCompletion, Boolean async);
+
+OSErr TCPResolveName(char **name, unsigned long *ipAddress);
+
+long ip2long(char *ip);
+void long2ip(unsigned long num, char *ip);
+
+OSErr SOCKS5TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr socks_ip,
+ tcp_port socks_port, char *remote_host, tcp_port remote_port,
+ ip_addr *local_ip, tcp_port *local_port, Ptr user_data,
+ TCPIOCompletionProc io_completion, Boolean async);
+
+#endif
--- util.c Thu Oct 28 17:01:17 2021
+++ util.c Thu Oct 28 17:01:17 2021
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2020-2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+#define ERROR_ALERT_ID 129
+#define ERROR_STRING_SIZE 1024
+
+enum {
+ STOP_ALERT,
+ CAUTION_ALERT,
+ NOTE_ALERT
+};
+
+Handle err_str;
+static TEHandle track_control_te = NULL;
+
+void vwarn(short alert_func, const char *format, va_list ap);
+
+/*
+ * Memory functions
+ */
+
+void *
+xmalloc(size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ err(2, "xmalloc: zero size");
+ ptr = malloc(size);
+ if (ptr == NULL)
+ err(2, "xmalloc: allocating %zu bytes", size);
+ return ptr;
+}
+
+void *
+xmalloczero(size_t size)
+{
+ void *ptr = xmalloc(size);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ ptr = calloc(nmemb, size);
+ if (ptr == NULL)
+ err(2, "xcalloc: allocating %zu * %zu bytes", nmemb, size);
+ return ptr;
+}
+
+void *
+xrealloc(void *src, size_t size)
+{
+ void *ret;
+
+ ret = realloc(src, size);
+ if (ret == NULL)
+ err(2, "Couldn't realloc %lu bytes of memory", size);
+ return ret;
+}
+
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+xreallocarray(void *optr, size_t nmemb, size_t size)
+{
+ void *new_ptr;
+
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
+ err(2, "reallocarray");
+ }
+ if ((new_ptr = realloc(optr, size * nmemb)) == NULL)
+ err(2, "realloc");
+
+ return new_ptr;
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *cp;
+ size_t len;
+
+ len = strlen(str);
+
+ cp = xmalloc(len + 1);
+ strncpy(cp, str, len);
+ cp[len] = '\0';
+
+ return cp;
+}
+
+/*
+ * String functions
+ */
+
+short
+getline(char *str, size_t len, char **ret)
+{
+ short i;
+
+ for (i = 0; i < len; i++) {
+ if (str[i] == '\r' || i == len - 1) {
+ if (*ret == NULL)
+ *ret = xmalloc(i + 1);
+ memcpy(*ret, str, i + 1);
+ (*ret)[i] = '\0';
+ return i + 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * BSD err(3) and warn(3) functions, must call err_init() before using
+ */
+
+void
+err_init(void)
+{
+ if (!(err_str = NewHandle(ERROR_STRING_SIZE))) {
+ SysBeep(20);
+ ExitToShell();
+ }
+}
+
+void
+vwarn(short alert_func, const char *format, va_list ap)
+{
+ size_t len;
+ short quit = 0;
+ WindowPtr win;
+
+ GetPort(&win);
+
+ HLock(err_str);
+ len = vsprintf(*err_str, format, ap);
+ if (len >= ERROR_STRING_SIZE) {
+ sprintf(*err_str, "raise_error string overflow!");
+ quit = 1;
+ }
+
+ ParamText(CtoPstr(*err_str), "\p", "\p", "\p");
+ switch (alert_func) {
+ case CAUTION_ALERT:
+ CautionAlert(ERROR_ALERT_ID, nil);
+ break;
+ case NOTE_ALERT:
+ NoteAlert(ERROR_ALERT_ID, nil);
+ break;
+ default:
+ StopAlert(ERROR_ALERT_ID, nil);
+ }
+ HUnlock(err_str);
+
+ SetPort(win);
+
+ if (quit)
+ ExitToShell();
+}
+
+void
+warn(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(CAUTION_ALERT, format, ap);
+ va_end(ap);
+}
+
+void
+warnx(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(CAUTION_ALERT, format, ap);
+ va_end(ap);
+}
+
+void
+err(short retcode, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(STOP_ALERT, format, ap);
+ va_end(ap);
+
+ ExitToShell();
+}
+
+void
+note(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarn(NOTE_ALERT, format, ap);
+ va_end(ap);
+}
+
+/*
+ * Error checking wrappers for Mac toolkit functions
+ */
+
+Handle
+xNewHandle(unsigned long size)
+{
+ Handle h;
+
+ h = NewHandle(size);
+ if (h == NULL)
+ err(1, "Failed to NewHandle(%lu)", size);
+
+ return h;
+}
+
+Handle
+xGetResource(ResType type, short id)
+{
+ Handle h;
+
+ h = GetResource(type, id);
+ if (h == NULL)
+ err(1, "Failed to find resource %d", id);
+
+ return h;
+}
+
+StringHandle
+xGetString(short id)
+{
+ StringHandle h;
+
+ h = GetString(id);
+ if (h == NULL)
+ err(1, "Failed to find STR resource %d", id);
+
+ return h;
+}
+
+/*
+ * Filesystem utilities
+ */
+
+/*
+ * With SFGetFile, when the user has a folder selected and clicks Open,
+ * File Manager will change to the folder and expect the user to select a
+ * file in it. This triggers sfHookOpenFolder, but when the user double-
+ * clicks on a folder to browse into it, the same result code is returned.
+ *
+ * To be able to return the folder itself, we need to know whether the user
+ * double-clicked a folder, or clicked Open. When we get our hook callback,
+ * we check where the mouse is in relation to the Open button rect. If
+ * the user clicked Open, we return the folder itself, otherwise we assume
+ * the user double-clicked a folder and we should browse into it.
+ *
+ * Finally, we return the full path of the file/folder as a char *, rather
+ * than just a ref id.
+ */
+char *
+askfileordirpath(void)
+{
+ Point pt = { 75, 75 };
+ SFReply reply;
+ HParamBlockRec hpbr;
+ Str63 fName = { 0 };
+ char *ret = NULL;
+
+ SFGetFile(pt, "\p", NULL, -1, 0, open_dialog_hook, &reply);
+ if (!reply.good)
+ return NULL;
+
+ if (reply.fName[0] == 0) {
+ /* selected a folder, look it up */
+ hpbr.fileParam.ioCompletion = 0;
+ hpbr.fileParam.ioNamePtr = (StringPtr)&fName;
+ hpbr.fileParam.ioVRefNum = reply.vRefNum;
+ hpbr.fileParam.ioDirID = reply.fType;
+ hpbr.fileParam.ioFDirIndex = -1;
+ PBGetCatInfo(&hpbr, false);
+ memcpy(reply.fName, fName, sizeof(fName));
+ }
+
+ getpath(reply.vRefNum, reply.fName, &ret, true);
+
+ return ret;
+}
+
+pascal short
+open_dialog_hook(short theItem, DialogPtr theDialog)
+{
+ short item_type;
+ Handle item;
+ Rect item_rect;
+ Point mouse;
+
+ if (theItem == sfHookOpenFolder) {
+ GetDItem(theDialog, getOpen, &item_type, &item, &item_rect);
+ GetMouse(&mouse);
+
+ if (PtInRect(mouse, &item_rect)) {
+ /* clicked open */
+ return getOpen;
+ }
+ }
+
+ return theItem;
+}
+
+short
+getpath(short vRefNum, Str255 fileName, char **ret, bool include_file)
+{
+ WDPBRec wdir;
+ HVolumeParam wvol;
+ DirInfo wcinfo;
+ Str255 name;
+ size_t retlen = 0;
+ char *tmp;
+
+ wdir.ioVRefNum = wdir.ioWDVRefNum = vRefNum;
+ wdir.ioWDIndex = 0;
+ wdir.ioWDProcID = 0;
+ wdir.ioNamePtr = (StringPtr)&name;
+ if (PBGetWDInfo(&wdir, 0) != noErr) {
+ warn("Failed looking up directory");
+ return 1;
+ }
+
+ wvol.ioNamePtr = (StringPtr)&name;
+ wvol.ioVRefNum = vRefNum;
+ wvol.ioVolIndex = 0;
+
+ if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) {
+ warn("Failed getting volume info");
+ return 1;
+ }
+
+ if (wvol.ioVSigWord != 0x4244) {
+ warn("Unknown filesystem type 0x%x", wvol.ioVSigWord);
+ return 1;
+ }
+
+ wcinfo.ioNamePtr = (StringPtr)&name;
+ wcinfo.ioVRefNum = vRefNum;
+ wcinfo.ioFDirIndex = -1;
+ wcinfo.ioDrParID = wdir.ioWDDirID;
+ wcinfo.ioDrDirID = wdir.ioWDDirID;
+
+ /* go backwards, prepending each folder's parent */
+ while (wcinfo.ioDrParID != 1) {
+ wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */
+
+ if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr)
+ break;
+
+ if (retlen == 0) {
+ retlen = name[0];
+ *ret = xmalloc(retlen + 1);
+ sprintf(*ret, "%s", PtoCstr(name));
+ } else {
+ tmp = xstrdup(*ret);
+ free(*ret);
+ *ret = xmalloc(retlen + 1 + name[0] + 1);
+ retlen += 1 + name[0];
+ sprintf(*ret, "%s:%s", PtoCstr(name), tmp);
+ free(tmp);
+ }
+ }
+
+ if (include_file) {
+ /* append the original path */
+ memcpy(name, fileName, sizeof(name));
+ PtoCstr(name);
+ if (retlen == 0)
+ *ret = xstrdup((char *)name);
+ else {
+ *ret = xrealloc(*ret, retlen + 1 + fileName[0] + 1);
+ sprintf(*ret + retlen, ":%s", (char *)name);
+ }
+ } else if (retlen == 0) {
+ *ret = NULL;
+ }
+
+ return 0;
+}
+
+short
+stat(const char *path, struct stat *sb)
+{
+ CInfoPBRec catblock = { 0 };
+ short ret;
+ char *tpath;
+
+ tpath = xstrdup(path);
+ CtoPstr(tpath);
+
+ catblock.hFileInfo.ioNamePtr = (StringPtr)tpath;
+ ret = PBGetCatInfo(&catblock, 0);
+ free(tpath);
+ if (ret != noErr)
+ return -1;
+
+ /* data fork logical length + resource fork logical length */
+ sb->st_size = catblock.hFileInfo.ioFlLgLen +
+ catblock.hFileInfo.ioFlRLgLen;
+ sb->st_ctime = catblock.hFileInfo.ioFlCrDat;
+ sb->st_mtime = catblock.hFileInfo.ioFlMdDat;
+ sb->st_flags = catblock.hFileInfo.ioFlAttrib;
+
+ return 0;
+}
+
+bool
+is_dir(char *path)
+{
+ struct stat st;
+ short ret;
+
+ if ((ret = stat(path, &st)) != 0)
+ return ret;
+
+ /* bit 4 is set in ioFlAttrib if the item is a directory */
+ if (st.st_flags & (1 << 4))
+ return true;
+
+ return false;
+}
+
+/* read a \r-terminated line or the final non-line bytes of an open file */
+OSErr
+FSReadLine(short frefnum, char *buf, size_t buflen)
+{
+ char tbuf;
+ size_t pos, fsize, rlen = 1, total_read = 0;
+ short error, found = -1, i;
+
+ GetFPos(frefnum, &pos);
+ GetEOF(frefnum, &fsize);
+
+ for (; pos <= fsize; pos++) {
+ if (total_read > buflen)
+ return -1;
+
+ error = FSRead(frefnum, &rlen, &tbuf);
+ if (error)
+ return -1;
+
+ if (tbuf == '\r')
+ return total_read;
+
+ buf[total_read++] = tbuf;
+ }
+
+ /* nothing found until the end of the file */
+ return total_read;
+}
+
+/*
+ * General Mac-specific GUI functions
+ */
+
+short
+FontHeight(short font_id, short size)
+{
+ FMInput f_in;
+ FMOutput *f_out;
+
+ if (font_id == -1)
+ font_id = thePort->txFont;
+ if (size == -1)
+ size = thePort->txSize;
+
+ f_in.family = font_id;
+ f_in.size = size;
+ f_in.face = 0;
+ f_in.needBits = false;
+ f_in.device = 0;
+ f_in.numer.h = f_in.numer.v = 1;
+ f_in.denom.h = f_in.denom.v = 1;
+
+ f_out = FMSwapFont(&f_in);
+
+ return (((f_out->leading + f_out->ascent + f_out->descent) *
+ f_out->numer.v) / f_out->denom.v);
+}
+
+void
+DrawGrowIconOnly(WindowPtr win)
+{
+ Rect r;
+ RgnHandle tmp;
+
+ GetClip(tmp = NewRgn());
+ r = win->portRect;
+ r.top = r.bottom - SCROLLBAR_WIDTH;
+ r.left = r.right - SCROLLBAR_WIDTH + 1;
+ ClipRect(&r);
+ DrawGrowIcon(win);
+ SetClip(tmp);
+ DisposeRgn(tmp);
+}
+
+void
+UpdateScrollbarForTE(ControlHandle scroller, TEHandle te, bool reset)
+{
+ size_t vlines, telines;
+ TERec *ter;
+ short vtop, lheight;
+ short max, val;
+
+ HLock(te);
+ ter = *te;
+
+#define ceildiv(a,b) ((a / b) + (!!(a % b)))
+
+ lheight = TEGetHeight(0, 0, te);
+ vlines = (ter->viewRect.bottom - ter->viewRect.top) / lheight;
+ telines = ter->nLines;
+ /* telines is inaccurate if the last line doesn't have any chars */
+ if (telines >= vlines)
+ telines++;
+ max = telines - vlines;
+ if (max < 1)
+ max = 1;
+
+ if (reset) {
+ val = 1;
+ vtop = (*te)->viewRect.top;
+ TESetSelect(0, 0, te);
+ } else {
+ val = (ter->viewRect.top - ter->destRect.top);
+ if (val < 0)
+ val = 1;
+ else {
+ val = ceildiv(val, lheight) + 1;
+ if (val > max)
+ max = val;
+ }
+ }
+ SetCtlMax(scroller, max);
+ SetCtlValue(scroller, val);
+
+ HUnlock(te);
+}
+
+void
+SetTrackControlTE(TEHandle te)
+{
+ track_control_te = te;
+}
+
+pascal void
+TrackMouseDownInControl(ControlHandle control, short part)
+{
+ short page, val, adj, lheight;
+
+ if (track_control_te == NULL)
+ err(1, "TrackMouseDownInControl without SetTrackControlTE");
+
+ lheight = TEGetHeight(0, 0, track_control_te);
+
+ /* keep 1 line of context between pages */
+ page = (((*track_control_te)->viewRect.bottom -
+ (*track_control_te)->viewRect.top) / lheight) - 1;
+
+ adj = 0;
+ switch (part) {
+ case inUpButton:
+ adj = -1;
+ break;
+ case inPageUp:
+ adj = -page;
+ break;
+ case inDownButton:
+ adj = 1;
+ break;
+ case inPageDown:
+ adj = page;
+ break;
+ }
+
+ val = GetCtlValue(control);
+ if (val + adj < GetCtlMin(control))
+ adj = -(val - GetCtlMin(control));
+ if (val + adj > GetCtlMax(control))
+ adj = (GetCtlMax(control) - val);
+ if (adj == 0)
+ return;
+
+ TEScroll(0, -adj * lheight, track_control_te);
+ SetCtlValue(control, val + adj);
+}
--- util.h Thu Oct 28 17:01:09 2021
+++ util.h Thu Oct 28 17:01:09 2021
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020-2021 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#include <stdlib.h>
+#include <limits.h>
+#include <time.h>
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ULONG_MAX
+#endif
+
+#define SCROLLBAR_WIDTH 16
+
+#define MAX_TEXTEDIT_SIZE 32767L
+
+#define nitems(what) (sizeof((what)) / sizeof((what)[0]))
+
+#ifndef bool
+typedef Boolean bool;
+#endif
+
+typedef signed long off_t;
+typedef signed long ssize_t;
+typedef unsigned char u_char;
+typedef unsigned long u_int;
+
+typedef struct {
+ short push[2], rts;
+ void *addr;
+} tCodeStub;
+
+typedef struct stat {
+ short st_mode;
+ ssize_t st_size;
+ time_t st_ctime;
+ time_t st_mtime;
+ unsigned char st_flags;
+};
+
+void *xmalloc(size_t);
+void *xmalloczero(size_t);
+void *xcalloc(size_t, size_t);
+void *xrealloc(void *src, size_t size);
+void *xreallocarray(void *, size_t, size_t);
+char *xstrdup(const char *);
+
+short getline(char *str, size_t len, char **ret);
+/* from strnatcmp.c */
+int strnatcmp(char const *a, char const *b);
+int strnatcasecmp(char const *a, char const *b);
+
+void err_init(void);
+void warnx(const char *format, ...);
+void warn(const char *format, ...);
+void err(short retcode, const char *format, ...);
+void note(const char *format, ...);
+
+Handle xNewHandle(unsigned long size);
+Handle xGetResource(ResType type, short id);
+StringHandle xGetString(short id);
+char *xGetStringAsChar(short id);
+long xGetStringAsLong(short id);
+
+char *askfileordirpath(void);
+short getpath(short vRefNum, Str255 fileName, char **ret,
+ bool include_file);
+pascal short open_dialog_hook(short theItem, DialogPtr theDialog);
+pascal Boolean open_dialog_filter(DialogPtr theDialog,
+ EventRecord *theEvent, short *itemHit);
+bool is_dir(char *path);
+short stat(const char *path, struct stat *sb);
+OSErr FSReadLine(short frefnum, char *buf, size_t buflen);
+
+short FontHeight(short font_id, short size);
+void DrawGrowIconOnly(WindowPtr win);
+void UpdateScrollbarForTE(ControlHandle scroller, TEHandle te, bool reset);
+void SetTrackControlTE(TEHandle te);
+pascal void TrackMouseDownInControl(ControlHandle control, short part);
+
+#endif