AmendHub

Download:

jcs

/

subtext

/

amendments

/

1

Initial import

Work on a functioning console

jcs made amendment 1 over 2 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 *)&num; + (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