AmendHub

Download:

vkoskiv

/

MacNTP

/

amendments

/

1

Initial amendment

After a few afternoons of hacking, I now have a barely working
proof-of-concept NTP client that works on System 6.
(I added _UDPRcv() to tcp.{c,h})
Most of the rest were lifted from jcs's wikipedia Amend repo, and I
wrote main.c that implements the actual client.
My aim is to iron out bugs in this, and then turn it into an INIT
that syncs the clock in my Mac Plus at bootup.

vkoskiv made amendment 1 8 months 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 --- dnr.c Mon Apr 24 17:24:01 2023 +++ dnr.c Mon Apr 24 17:24:01 2023 @@ -0,0 +1,355 @@ +/* + * DNR.c - DNR library for MPW + * + * (c) Copyright 1988 by Apple Computer. All rights reserved + * + * Modifications by Jim Matthews, Dartmouth College, 5/91 + */ + +/* + * TODO: update to avoid having to include MacTraps2 for: + * CloseWD + * HOpenResFile + * FindFolder + * GetWDInfo + */ + +#include <OSUtils.h> +#include <Files.h> +#include <Folders.h> +#include <GestaltEqu.h> +#include <Traps.h> + +#include "dnr.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); +pascal void StrToAddrMarkDone(struct hostInfo *hi, char *data); + +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 + +pascal void +StrToAddrMarkDone(struct hostInfo *hi, char *data) +{ + volatile int *done = (int *)data; + *done = 1; +} + +OSErr +ResolveName(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; +} --- dnr.h Mon Apr 24 17:24:03 2023 +++ dnr.h Mon Apr 24 17:24:03 2023 @@ -0,0 +1,76 @@ +/* + dnr.h (formerly 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 + +struct hostInfo { + short _pad; /* XXX: i don't know why this is needed, but without it, + * StrToAddrProcPtr() returns everything shifted 2 bytes */ + short rtnCode; + char cname[255]; + unsigned long addr[NUM_ALT_ADDRS]; +}; + +enum AddrClasses { + A = 1, + NS, + CNAME = 5, + lastClass = 65535 +}; + +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); + +OSErr ResolveName(char *name, unsigned long *ipAddress); + +#endif --- MacTCP.h Thu Aug 17 02:40:59 2023 +++ MacTCP.h Thu Aug 17 02:40:59 2023 @@ -0,0 +1,877 @@ +/* + 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 +}; + +enum { + ConnectionStateClosed = 0, + ConnectionStateListening = 2, + ConnectionStateSYNReceived = 4, + ConnectionStateSYNSent = 6, + ConnectionStateEstablished = 8, + ConnectionStateFINWait1 = 10, + ConnectionStateFINWait2 = 12, + ConnectionStateCloseWait = 14, + ConnectionStateClosing = 16, + ConnectionStateLastAck = 18, + ConnectionStateTimeWait = 20 +}; + +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 struct UDPiopb UDPiopb; +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; +}; + +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 Tue Aug 22 00:00:40 2023 +++ main.c Tue Aug 22 00:00:40 2023 @@ -0,0 +1,210 @@ +#include <stdio.h> +#include <string.h> +#include "util.h" +#include "tcp.h" +#include "dnr.h" + +void ntp_req_free(void *); +void on_udp_receive(struct UDPiopb *iopb); +void dump_ntp_packet(struct ntp_packet *packet); +u_int8_t leap_information(struct ntp_packet pkt); +u_int8_t version_number(struct ntp_packet pkt); +u_int8_t mode(struct ntp_packet pkt); + +static global_received = 0; + +// We already have this from dnr.h above, but Think C ignores it. +OSErr ResolveName(char *name, unsigned long *ipAddress); + +struct ntp_ts { + /* FIXME: These may be swapped*/ + u_int32_t upper; /* Maybe seconds since 1900-01-01 */ + u_int32_t lower; /* Maybe fractional bits? Probably not needed */ +}; + +struct ntp_packet { + u_int8_t li_vn_mode; + u_int8_t stratum; + u_int8_t poll; + u_int8_t precision; + + u_int32_t root_delay; + u_int32_t root_dispersion; + u_int32_t reference_id; + + struct ntp_ts reference_timestamp; + struct ntp_ts origin_timestamp; + struct ntp_ts receive_timestamp; + struct ntp_ts transmit_timestamp; +}; + +u_int8_t leap_information(struct ntp_packet pkt) { + return pkt.li_vn_mode >> 6; +} + +u_int8_t version_number(struct ntp_packet pkt) { + return (pkt.li_vn_mode >> 3) & 7; +} + +u_int8_t mode(struct ntp_packet pkt) { + return pkt.li_vn_mode & 7; +} + +struct ntp_request { + ip_addr host_ip; + char *url; + UDPiopb udp_iopb; + StreamPtr udp_stream; + unsigned char *udp_buf; + unsigned short udp_buf_size; + wdsEntry udp_wds[2]; + //TCPStatusPB tcp_status_pb; + + struct ntp_packet payload; +}; + +void ntp_req_free(void *reqptr) { + unsigned long *addr = (unsigned long *)reqptr; + void *ptr = (void *)*addr; + struct ntp_request *req = (struct ntp_request *)ptr; + if (req == NULL) + return; + _UDPRelease(&req->udp_iopb, req->udp_stream, nil, nil, false); + xfree(req->udp_buf); + xfree(reqptr); +} + +#define HOST_URL "time.google.com"; + +void dump_ntp_packet(struct ntp_packet *packet) { + struct ntp_packet *received_ntp; + u_int32_t t; + char ref_id[5]; + received_ntp = packet; //FIXME just change the names below + t = received_ntp->reference_id; + ref_id[0] = (t & 0xFF000000) >> 24; + ref_id[1] = (t & 0x00FF0000) >> 16; + ref_id[2] = (t & 0x0000FF00) >> 8; + ref_id[3] = (t & 0x000000FF) >> 0; + ref_id[4] = 0; + + printf("struct ntp_packet received = {\n"); + printf("\tli_vn_mode: %u\n", received_ntp->li_vn_mode); + printf("\tstratum: %u\n", received_ntp->stratum); + printf("\tpoll: %u\n", received_ntp->poll); + printf("\tprecision: %u\n", received_ntp->precision); + printf("\troot_delay: %lu\n", received_ntp->root_delay); + printf("\troot_dispersion: %lu\n", received_ntp->root_dispersion); + printf("\treference_id: %s\n", ref_id); + + printf("\treference_ts(hi): %lu\n", received_ntp->reference_timestamp.upper); + printf("\treference_ts(lo): %lu\n", received_ntp->reference_timestamp.lower); + + printf("\torigin_ts(hi): %lu\n", received_ntp->origin_timestamp.upper); + printf("\torigin_ts(lo): %lu\n", received_ntp->origin_timestamp.lower); + + printf("\treceive_ts(hi): %lu\n", received_ntp->receive_timestamp.upper); + printf("\treceive_ts(lo): %lu\n", received_ntp->receive_timestamp.lower); + + printf("\ttransmit_ts(hi): %lu\n", received_ntp->transmit_timestamp.upper); + printf("\ttransmit_ts(lo): %lu\n", received_ntp->transmit_timestamp.lower); + + printf("}\nWoo, success(?)\n"); + + printf("leap_information: %u\n", leap_information(*received_ntp)); + printf("version_number: %u\n", version_number(*received_ntp)); + printf("mode: %u\n", mode(*received_ntp)); +} + +void on_udp_receive(struct UDPiopb *iopb) { + printf("on_udp_receive:\n"); + if (iopb->csParam.receive.rcvBuffLen != sizeof(struct ntp_packet)) { + printf("Unexpected payload size received!\n"); + //printf("%u != 48\n", iopb->csParam.receive.rcvBuffLen); + } else { + dump_ntp_packet((struct ntp_packet *)iopb->csParam.receive.rcvBuff); + } + + global_received = 1; +} + +int main(int argc, char **argv) { + struct ntp_request *req; + char ip_s[16]; + ip_addr local_ip; + tcp_port local_port; + short err; + size_t len; + char rcvbuf[64]; + unsigned short rcvbuflen; + UDPIOCompletionProc completion; + completion = NewUDPIOCompletionProc(on_udp_receive); + + //TODO: Might need to call util_init() here, it sets up UI alert things + MaxApplZone(); + if (_TCPInit() != 0) { + printf("Failed to init MacTCP, exiting.\n"); + return 1; + } + if (sizeof(struct ntp_packet) != 48lu) { + printf("sizeof(struct ntp_packet) != 48, exiting\n"); + return 1; + } + + req = xmalloczero(sizeof(struct ntp_request), "ntp_request"); + req->url = HOST_URL; + req->udp_buf_size = (2 * 1500); + req->udp_buf = xmalloc(req->udp_buf_size, "ntp_req buf"); + err = _UDPCreate(&req->udp_iopb, &req->udp_stream, (Ptr)req->udp_buf, + req->udp_buf_size, nil, nil, completion, false); + if (err) { + printf("UDPCreate failed: %d\n", err); + goto error; + } + err = ResolveName(req->url, &req->host_ip); + if (err) { + printf("Couldn't resolve host %s (%d)\n", req->url, err); + goto error; + } + + long2ip(req->host_ip, (char *)&ip_s); + printf("%s => %s\n", req->url, ip_s); + + // Set up NTP payload + memset(&req->payload, 0, sizeof(req->payload)); + req->payload.li_vn_mode = (4 << 3) | 3; + + // TODO: This should be fine, our time is always in 1904 anyway + // Just verify when receiving + memset(&req->payload.transmit_timestamp, 0x41, sizeof(req->payload.transmit_timestamp)); + + memset(&req->udp_wds, 0, sizeof(req->udp_wds)); + req->udp_wds[0].ptr = (void *)&req->payload; + req->udp_wds[0].length = sizeof(req->payload); + + memset(&rcvbuf, 0, sizeof(rcvbuf)); + rcvbuflen = 0; + + printf("Sending NTP query to %s...\n", ip_s); + err = _UDPSend(&req->udp_iopb, req->udp_stream, req->udp_wds, + req->host_ip, 123, nil, nil, false); + + printf("Waiting for response...\n"); + //FIXME: Probably broken, I wrote UDPRcv + err = _UDPRcv(&req->udp_iopb, req->udp_stream, (Ptr)&rcvbuf, + &rcvbuflen, req->host_ip, 123, nil, completion, true); + + if (err) { + printf("UDPRcv failed: %d\n", err); + goto error; + } + + // Wait for event handler to fire (not sure if needed) + while (!global_received) {} + printf("global_received set, goodbye\n"); + +error: + _UDPRelease(&req->udp_iopb, req->udp_stream, nil, nil, false); + //ntp_req_free(&req); + return 0; +} --- tcp.c Mon Aug 21 21:43:32 2023 +++ tcp.c Mon Aug 21 21:43:32 2023 @@ -0,0 +1,622 @@ +#include <stdio.h> +#include <string.h> +#include "tcp.h" + +#define RCV_BUFFER_SIZE 1024 +#define TCP_BUFFER_SIZE 8192 +#define OPEN_TIMEOUT 60 + +/* define to enable SOCKS5 code */ +#undef SOCKS + +short gIPPDriverRefNum; + +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; + + memset(&pb, 0, sizeof(pb)); + + pb.csCode = ipctlGetAddr; + pb.ioCRefNum = gIPPDriverRefNum; + pb.ioResult = 1; + + 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->csCode = TCPCreate; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->ioResult = 1; + + 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->csCode = TCPPassiveOpen; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + pb->csParam.open.ulpTimeoutAction = 1; /* abort half-open connection */ + pb->csParam.open.ulpTimeoutValue = 5; /* after 5 seconds */ + pb->csParam.open.validityFlags = 0xC0; + pb->csParam.open.commandTimeoutValue = 0; + pb->csParam.open.remoteHost = 0; + pb->csParam.open.remotePort = 0; + pb->csParam.open.localHost = 0; + pb->csParam.open.localPort = *localPort; + pb->csParam.open.tosFlags = 0x1; /* low delay */ + 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; + pb->csParam.open.userDataPtr = userData; + + osErr = PBControl((ParmBlkPtr) pb, async); + if (!async && (osErr == noErr)) { + if (remoteIP) + *remoteIP = pb->csParam.open.remoteHost; + if (remotePort) + *remotePort = pb->csParam.open.remotePort; + if (localIP) + *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->csCode = TCPActiveOpen; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + 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->csCode = TCPSend; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + 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) +{ + memset(pb, 0, sizeof(*pb)); + + pb->csCode = TCPNoCopyRcv; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + 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->csCode = TCPRcv; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + 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->csCode = TCPRcvBfrReturn; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + 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->csCode = TCPClose; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + 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->csCode = TCPAbort; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + 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->csCode = TCPStatus; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->csParam.status.userDataPtr = userData; + pb->ioResult = 1; + + 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->csCode = TCPRelease; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->tcpStream = stream; + pb->ioResult = 1; + + pb->csParam.status.userDataPtr = userData; + + osErr = PBControl((ParmBlkPtr)pb, async); + + return osErr; +} + +OSErr +_UDPMaxMTUSize(UDPiopb *pb, short *mtu) +{ + OSErr osErr; + + memset(pb, 0, sizeof(*pb)); + + pb->csCode = UDPMaxMTUSize; + pb->ioCRefNum = gIPPDriverRefNum; + pb->ioResult = 1; + + pb->csParam.mtu.remoteHost = (ip_addr)0; + + osErr = PBControl((ParmBlkPtr)pb, false); + + if (osErr == noErr) + *mtu = pb->csParam.mtu.mtuSize; + + return osErr; +} + +OSErr +_UDPCreate(UDPiopb *pb, StreamPtr *stream, Ptr rcvBufPtr, long rcvBufLen, + UDPNotifyProc aNotifyProc, Ptr userDataPtr, + UDPIOCompletionProc ioCompletion, Boolean async) +{ + OSErr osErr; + + memset(pb, 0, sizeof(*pb)); + + pb->csCode = UDPCreate; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->ioResult = 1; + + 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->udpStream; + } + + return osErr; +} + +OSErr +_UDPSend(UDPiopb *pb, StreamPtr stream, wdsEntry *wdsPtr, ip_addr remoteIP, + udp_port remotePort, Ptr userData, UDPIOCompletionProc ioCompletion, + Boolean async) +{ + memset(pb, 0, sizeof(*pb)); + + pb->csCode = UDPWrite; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->udpStream = stream; + pb->ioResult = 1; + + pb->csParam.send.remoteHost = remoteIP; + pb->csParam.send.remotePort = remotePort; + pb->csParam.send.wdsPtr = (Ptr)wdsPtr; + pb->csParam.send.checkSum = 0; + pb->csParam.send.sendLength = 0; + pb->csParam.send.userDataPtr = userData; + + return PBControl((ParmBlkPtr)pb, async); +} + +OSErr _UDPRcv(UDPiopb *pb, StreamPtr stream, Ptr rcvBufPtr, + unsigned short *rcvBufLen, ip_addr remoteIP, udp_port remotePort, Ptr userData, UDPIOCompletionProc ioCompletion, + Boolean async) +{ + OSErr osErr; + + memset(pb, 0, sizeof(*pb)); + + pb->csCode = UDPRead; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->udpStream = stream; + pb->ioResult = 1; + + pb->csParam.receive.remoteHost = remoteIP; + pb->csParam.receive.remotePort = remotePort; + pb->csParam.receive.timeOut = 5; + 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 +_UDPRelease(UDPiopb *pb, StreamPtr stream, Ptr userData, + UDPIOCompletionProc ioCompletion, Boolean async) +{ + OSErr osErr; + + memset(pb, 0, sizeof(*pb)); + + pb->csCode = UDPRelease; + pb->ioCompletion = ioCompletion; + pb->ioCRefNum = gIPPDriverRefNum; + pb->udpStream = stream; + pb->ioResult = 1; + + //pb->csParam.status.userDataPtr = userData; + + osErr = PBControl((ParmBlkPtr)pb, async); + + return osErr; +} + + +/* convenience functions */ + +unsigned long +ip2long(char *ip) +{ + unsigned long address = 0; + short dotcount = 0, i; + unsigned short 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; + sprintf(ip, "%d.%d.%d.%d", tmp[0], tmp[1], tmp[2], tmp[3]); +} + +#ifdef SOCKS +#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; +} +#endif /* SOCKS */ --- tcp.h Mon Aug 21 21:11:34 2023 +++ tcp.h Mon Aug 21 21:11:34 2023 @@ -0,0 +1,73 @@ +/* + * 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 ProcPtr UDPNotifyProc; +typedef void (*TCPIOCompletionProc)(struct TCPiopb *iopb); +typedef void (*UDPIOCompletionProc)(struct UDPiopb *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 _UDPMaxMTUSize(UDPiopb *pb, short *mtu); +OSErr _UDPCreate(UDPiopb *pb, StreamPtr *stream, Ptr rcvBufPtr, + long rcvBufLen, UDPNotifyProc aNotifyProc, Ptr userDataPtr, + UDPIOCompletionProc ioCompletion, Boolean async); +OSErr _UDPSend(UDPiopb *pb, StreamPtr stream, wdsEntry *wdsPtr, + ip_addr remoteIP, udp_port remotePort, Ptr userData, + UDPIOCompletionProc ioCompletion, Boolean async); +OSErr _UDPRcv(UDPiopb *pb, StreamPtr stream, Ptr rcvBufPtr, + unsigned short *rcvBufLen, ip_addr remoteHost, udp_port remotePort, Ptr userData, UDPIOCompletionProc ioCompletion, + Boolean async); +OSErr _UDPRelease(UDPiopb *pb, StreamPtr stream, Ptr userData, + UDPIOCompletionProc ioCompletion, Boolean async); + +unsigned 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 Mon Apr 24 17:24:32 2023 +++ util.c Mon Apr 24 17:24:32 2023 @@ -0,0 +1,2072 @@ +/* + * Copyright (c) 2020-2022 joshua stein <jcs@jcs.org> + * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org> + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + */ + +/* THINK C Project must have a header of "#include <MacHeaders>" */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <GestaltEqu.h> +#include <Script.h> +#include <SetUpA4.h> +#include "util.h" + +/* ALRT resources */ +#define ASK_ALERT_ID 130 + +#define ERROR_STRING_SIZE 1024 +static char err_str[ERROR_STRING_SIZE]; + +/* basic DITL with an ok button (1), text area (2), and icon (3) */ +#define ALERT_DITL_OK 1 +#define ALERT_DITL_ICON 3 +static const char alert_ditl[] = { + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, + 0x00, 0xFA, 0x00, 0x64, 0x01, 0x34, 0x04, 0x02, + 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x4E, 0x00, 0x41, 0x01, 0x36, 0x08, 0x02, + 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x17, 0x00, 0x2D, 0x00, 0x37, 0xA0, 0x02, + 0x00, 0x01 +}; +static Handle alert_ditl_h = NULL; + +/* ICN# to show for APPICON_ALERT */ +#define APPICON_ICN_ID 128 + +/* DITL with a Yes button (1), No button (2), text (3), and icon (4) */ +static const char ask_ditl[] = { + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, + 0x00, 0xFA, 0x00, 0x64, 0x01, 0x34, 0x04, 0x02, + 0x4F, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x4E, 0x00, 0x41, 0x01, 0x36, 0x08, 0x02, + 0x5E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, + 0x00, 0x17, 0x00, 0x2D, 0x00, 0x37, 0xA0, 0x02, + 0x00, 0x01 +}; +static Handle ask_ditl_h = NULL; + +/* DITL with just a text view */ +static const char progress_ditl[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x1E, 0x00, 0x32, 0x01, 0x3B, 0x08, 0x02, + 0x5E, 0x30 +}; +static Handle progress_ditl_h = NULL; +static DialogPtr progress_dialog = NULL; + +static TEHandle track_control_te = NULL; + +enum { + STOP_ALERT, + CAUTION_ALERT, + NOTE_ALERT, + APPICON_ALERT +}; + +/* + * Define to audit each malloc and free and verify that a pointer isn't + * double-freed. The list of outstanding allocations can be checked by + * looking through malloc_map. + */ +//#define MALLOC_DEBUG + +#ifdef MALLOC_DEBUG +/* + * List of allocations, updated at xmalloc() and xfree(). If an address + * passed to xfree() isn't in the list, it indicates a double-free. + */ +#define MALLOC_MAP_CHUNK_SIZE 1024 +struct malloc_map_e { + unsigned long addr; + unsigned long size; + char note[MALLOC_NOTE_SIZE]; +} *malloc_map = NULL; +unsigned long malloc_map_size = 0; +static bool malloc_map_compact = false; +#endif + +void vwarn(short alert_func, const char *format, va_list ap); + +/* + * Util helper needed to be called at program startup, to pre-allocate + * some things that we can't do during errors. + */ + +void +util_init(void) +{ + alert_ditl_h = xNewHandle(sizeof(alert_ditl)); + HLock(alert_ditl_h); + memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl)); + HUnlock(alert_ditl_h); + +#ifdef MALLOC_DEBUG + malloc_map_size = MALLOC_MAP_CHUNK_SIZE; + malloc_map = (struct malloc_map_e *)NewPtr(malloc_map_size * + sizeof(struct malloc_map_e)); + if (malloc_map == NULL) + panic("NewPtr(%lu) failed", MALLOC_MAP_CHUNK_SIZE); + memset(malloc_map, 0, malloc_map_size); +#endif +} + +/* + * Memory functions + */ + +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +xmalloc(size_t size, char *note) +{ + void *ptr; +#ifdef MALLOC_DEBUG + struct malloc_map_e *new_malloc_map; + unsigned short n, j; +#endif + + if (size == 0) + panic("xmalloc: zero size"); + + ptr = NewPtr(size); + if (ptr == NULL) + panic("xmalloc(%lu) failed", size); + +#ifdef MALLOC_DEBUG + if (malloc_map_compact) { + for (n = 0; n < malloc_map_size; n++) { + if (malloc_map[n].addr != 0) + continue; + + for (j = n + 1; j < malloc_map_size; j++) { + if (malloc_map[j].addr == 0) + continue; + + malloc_map[n] = malloc_map[j]; + memset(&malloc_map[j], 0, sizeof(struct malloc_map_e)); + break; + } + } + + malloc_map_compact = false; + } + + for (n = 0; n <= malloc_map_size; n++) { + if (n == malloc_map_size) { + malloc_map_size += MALLOC_MAP_CHUNK_SIZE; + warn("xmalloc(%lu): out of malloc map entries, maybe a " + "memory leak, resizing to %ld", size, malloc_map_size); + new_malloc_map = (struct malloc_map_e *)NewPtr( + malloc_map_size * sizeof(struct malloc_map_e)); + if (new_malloc_map == NULL) + panic("out of memory resizing malloc map"); + memcpy(new_malloc_map, malloc_map, + (malloc_map_size - MALLOC_MAP_CHUNK_SIZE) * + sizeof(struct malloc_map_e)); + DisposePtr(malloc_map); + malloc_map = new_malloc_map; + } + if (malloc_map[n].addr == 0) { + malloc_map[n].addr = (unsigned long)ptr; + malloc_map[n].size = size; + strlcpy(malloc_map[n].note, note, sizeof(malloc_map[n].note)); + break; + } + n = n; + } +#endif + + return ptr; +} + +void +xfree(void *ptrptr) +{ + unsigned long *addr = (unsigned long *)ptrptr; + void *ptr = (void *)*addr; +#ifdef MALLOC_DEBUG + unsigned long n; + + for (n = 0; n <= malloc_map_size; n++) { + if (n == malloc_map_size) + panic("xfree(0x%lx): can't find in alloc map, likely " + "double free()", *addr); + if (malloc_map[n].addr == *addr) { + malloc_map[n].addr = 0; + malloc_map[n].size = 0; + malloc_map[n].note[0] = '\0'; + break; + } + } +#endif + + DisposePtr(ptr); + + *addr = 0L; +} + +void * +xmalloczero(size_t size, char *note) +{ + void *ptr; + + ptr = xmalloc(size, note); + memset(ptr, 0, size); + + return ptr; +} + +void * +xcalloc(size_t nmemb, size_t size, char *note) +{ + void *ptr; + + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) + panic("xcalloc(%lu, %lu) overflow", nmemb, size); + ptr = xmalloczero(nmemb * size, note); + if (ptr == NULL) + panic("xcalloc(%lu, %lu) failed", nmemb, size); + + return ptr; +} + +void * +xrealloc(void *src, size_t size) +{ + void *ptr, *tsrc; + unsigned long n; + char note[MALLOC_NOTE_SIZE] = "realloc from null"; + +#ifdef MALLOC_DEBUG + if (src != NULL) { + for (n = 0; n <= malloc_map_size; n++) { + if (n == malloc_map_size) { + panic("xrealloc(%lu): can't find in alloc map, likely " + "double free()", (unsigned long)src); + return NULL; + } + if (malloc_map[n].addr == (unsigned long)src) { + strlcpy(note, malloc_map[n].note, sizeof(note)); + break; + } + } + } +#endif + + ptr = xmalloc(size, note); + if (src != NULL) { + memcpy(ptr, src, size); + tsrc = src; + xfree(&tsrc); + } + + return ptr; +} + +void * +xreallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) + panic("xreallocarray(%lu, %lu) failed", nmemb, size); + return xrealloc(optr, size * nmemb); +} + +char * +xstrdup(const char *str, char *note) +{ + char *cp; + size_t len; + + len = strlen(str); + + cp = xmalloc(len + 1, note); + strlcpy(cp, str, len + 1); + + return cp; +} + +char * +xstrndup(const char *str, size_t maxlen, char *note) +{ + char *copy; + const char *cp; + size_t len; + + /* strnlen */ + for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) + ; + + len = (size_t)(cp - str); + copy = xmalloc(len + 1, note); + (void)memcpy(copy, str, len); + copy[len] = '\0'; + + return copy; +} + + +/* + * 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, "getline"); + memcpy(*ret, str, i + 1); + (*ret)[i] = '\0'; + return i + 1; + } + } + + return 0; +} + +const char * +ordinal(unsigned short n) +{ + switch (n % 100) { + case 11: + case 12: + case 13: + return "th"; + default: + switch (n % 10) { + case 1: + return "st"; + case 2: + return "nd"; + case 3: + return "rd"; + default: + return "th"; + } + } +} + +size_t +rtrim(char *str, char *chars) +{ + size_t len, rlen, n, j; + + rlen = len = strlen(str); + + for (n = len; n > 0; n--) { + for (j = 0; chars[j] != '\0'; j++) { + if (str[n - 1] == chars[j]) { + rlen--; + str[n - 1] = '\0'; + goto next_in_str; + } + } + + break; +next_in_str: + continue; + } + + return rlen; +} + +long +strpos_quoted(char *str, char c) +{ + long pos; + unsigned char quot = 0; + + for (pos = 0; str[pos] != '\0'; pos++) { + if (quot) { + if (str[pos] == '\\') { + pos++; + continue; + } + if (str[pos] == quot) + quot = 0; + continue; + } else { + if (str[pos] == '"') { + quot = str[pos]; + continue; + } + if (str[pos] == c) + return pos; + } + } + + return -1; +} + +char * +OSTypeToString(OSType type) +{ + static char ostype_s[5]; + + ostype_s[0] = (unsigned char)((type >> 24) & 0xff); + ostype_s[1] = (unsigned char)((type >> 16) & 0xff); + ostype_s[2] = (unsigned char)((type >> 8) & 0xff); + ostype_s[3] = (unsigned char)(type & 0xff); + ostype_s[4] = 0; + + return ostype_s; +} + +/* + * BSD err(3) and warn(3) functions + */ + +void +vwarn(short alert_func, const char *format, va_list ap) +{ + Rect bounds; + short hit, itype; + WindowPtr win, dialog; + Handle ihandle; + Rect irect; + + GetPort(&win); + + vsnprintf(err_str, ERROR_STRING_SIZE, format, ap); + + ParamText(CtoPstr(err_str), "\p", "\p", "\p"); + + center_in_screen(320, 110, false, &bounds); + dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc, + (WindowPtr)-1L, false, 0, alert_ditl_h); + + GetDItem(dialog, ALERT_DITL_ICON, &itype, &ihandle, &irect); + switch (alert_func) { + case CAUTION_ALERT: + ihandle = GetIcon(cautionIcon); + break; + case NOTE_ALERT: + ihandle = GetIcon(noteIcon); + break; + case APPICON_ALERT: + ihandle = GetResource('ICN#', APPICON_ICN_ID); + if (ihandle) + break; + default: + ihandle = GetIcon(stopIcon); + } + SetDItem(dialog, ALERT_DITL_ICON, itype, ihandle, &irect); + + ShowWindow(dialog); + + for (;;) { + ModalDialog(0, &hit); + if (hit == ok) + break; + } + ReleaseResource(ihandle); + DisposDialog(dialog); + + /* + * Pre-allocate for the next one while we have memory. We can't use + * xNewHandle because we might already be alerting about an OOM + * condition. + */ + DisposHandle(alert_ditl_h); + alert_ditl_h = NewHandle(sizeof(alert_ditl)); + if (alert_ditl_h == NULL) + ExitToShell(); + HLock(alert_ditl_h); + memcpy(*alert_ditl_h, alert_ditl, sizeof(alert_ditl)); + HUnlock(alert_ditl_h); + + SetPort(win); +} + +void +panic(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vwarn(STOP_ALERT, format, ap); + va_end(ap); + + ExitToShell(); +} + +void +err(short ret, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vwarn(STOP_ALERT, format, ap); + va_end(ap); + + 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 +note(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vwarn(NOTE_ALERT, format, ap); + va_end(ap); +} + +void +appicon_note(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vwarn(APPICON_ALERT, format, ap); + va_end(ap); +} + +short +ask(const char *format, ...) +{ + Rect bounds; + short hit; + WindowPtr win, dialog; + va_list ap; + + GetPort(&win); + + va_start(ap, format); + vsnprintf(err_str, ERROR_STRING_SIZE, format, ap); + va_end(ap); + + ParamText(CtoPstr(err_str), "\p", "\p", "\p"); + + ask_ditl_h = xNewHandle(sizeof(ask_ditl)); + HLock(ask_ditl_h); + memcpy(*ask_ditl_h, ask_ditl, sizeof(ask_ditl)); + HUnlock(ask_ditl_h); + + center_in_screen(300, 100, false, &bounds); + dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc, + (WindowPtr)-1L, false, 0, ask_ditl_h); + + ShowWindow(dialog); + for (;;) { + ModalDialog(0, &hit); + if (hit == 1 || hit == 2) + break; + } + DisposDialog(dialog); + DisposHandle(ask_ditl_h); + + SetPort(win); + + return (hit == 1); +} + +void +about(char *program_name) +{ + VersRecHndl vers; + char vers_s[255]; + char short_vers[255] = { 0 }; + short vlen, n; + + 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); + snprintf(vers_s, sizeof(vers_s), "%s %s", program_name, + short_vers); + for (n = 0; n < sizeof(vers_s); n++) { + if (vers_s[n] == '©') { + vers_s[n - 1] = '\r'; + break; + } + } + ReleaseResource(vers); + appicon_note("%s", vers_s); + } else + warnx("Can't find version number!"); +} + +void +progress(char *format, ...) +{ + static Str255 progress_s; + Handle thandle; + va_list argptr; + Rect bounds; + Rect trect; + short ttype; + + if (format == NULL) { + if (progress_dialog != NULL) { + DisposDialog(progress_dialog); + DisposHandle(progress_ditl_h); + progress_dialog = NULL; + } + return; + } + + va_start(argptr, format); + vsnprintf((char *)progress_s, 256, format, argptr); + va_end(argptr); + CtoPstr(progress_s); + + if (progress_dialog == NULL) { + progress_ditl_h = xNewHandle(sizeof(progress_ditl)); + HLock(progress_ditl_h); + memcpy(*progress_ditl_h, progress_ditl, sizeof(progress_ditl)); + HUnlock(progress_ditl_h); + + center_in_screen(330, 60, false, &bounds); + progress_dialog = NewDialog(nil, &bounds, "\p", false, dBoxProc, + (WindowPtr)-1L, false, 0, progress_ditl_h); + } + + GetDItem(progress_dialog, 1, &ttype, &thandle, &trect); + SetIText(thandle, progress_s); + + ShowWindow(progress_dialog); + DrawDialog(progress_dialog); +} + +void +window_rect(WindowPtr win, Rect *ret) +{ + *ret = (*(((WindowPeek)win)->strucRgn))->rgnBBox; + ret->left += 1; + ret->right -= 2; + ret->top -= 1; + ret->bottom -= 1; +} + +void +center_in_screen(short width, short height, bool titlebar, Rect *b) +{ + b->left = ((screenBits.bounds.right - screenBits.bounds.left) / 2) - + (width / 2); + b->top = ((screenBits.bounds.bottom - screenBits.bounds.top) / 2) - + (height / 2) + (titlebar ? GetMBarHeight() : 0); + b->right = b->left + width; + b->bottom = b->top + height; +} + +Point +centered_sf_dialog(void) +{ + Point p; + Rect r; + + center_in_screen(364, 216, false, &r); + p.h = r.left; + p.v = r.top; + + return p; +} + + +/* + * General Mac-specific non-GUI functions + */ + +static unsigned long _xorshift_state = 0; +unsigned long +xorshift32(void) +{ + unsigned long x = _xorshift_state; + if (x == 0) + x = Ticks; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return _xorshift_state = x; +} + + +/* + * Error checking wrappers for Mac toolkit functions + */ + +Handle +xNewHandle(size_t size) +{ + Handle h; + + if (size == 0) + panic("Zero xNewHandle size"); + + h = NewHandle(size); + if (h == NULL) + panic("Failed to NewHandle(%lu)", size); + + return h; +} + +Handle +xGetResource(ResType type, short id) +{ + Handle h; + + h = GetResource(type, id); + if (h == NULL) + panic("Failed to find resource %d", id); + + return h; +} + +StringHandle +xGetString(short id) +{ + StringHandle h; + + h = GetString(id); + if (h == NULL) + panic("Failed to find STR resource %d", id); + + return h; +} + +char * +xGetStringAsChar(short id) +{ + StringHandle h; + char *out; + size_t l; + + h = xGetString(id); + HLock(h); + l = (*h)[0]; + out = xmalloc(l + 1, "xGetStringAsChar"); + memcpy((void *)out, (void *)(*h + 1), l); + out[l] = '\0'; + ReleaseResource(h); + + return out; +} + +long +xGetStringAsLong(short id) +{ + char *c; + long r; + + c = xGetStringAsChar(id); + r = atol(c); + xfree(&c); + return r; +} + +void +xSetHandleSize(Handle h, Size s) +{ + SetHandleSize(h, s); + if (MemError()) + panic("Failed to SetHandleSize to %ld", s); +} + +/* + * Filesystem utilities + */ + +short +getpath(short vRefNum, Str255 fileName, Str255 *ret, bool include_file) +{ + WDPBRec wdir; + HVolumeParam wvol; + DirInfo wcinfo; + char *name = NULL; + size_t retlen = 0, len; + char *tmpret = NULL, *tmp = NULL; + char *lastcolon; + + if (strchr((char *)fileName + 1, ':') != NULL) { + /* already a full path */ + memcpy(*ret, fileName, 256); + if (!include_file) { + PtoCstr(*ret); + lastcolon = strrchr((char *)*ret, ':'); + lastcolon[0] = '\0'; + CtoPstr(*ret); + } + return 0; + } + + name = xmalloc(FILENAME_MAX, "getpath"); + + 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"); + xfree(&name); + return 1; + } + + wvol.ioNamePtr = (StringPtr)name; + wvol.ioVRefNum = vRefNum; + wvol.ioVolIndex = 0; + + if (PBHGetVInfoSync((HParmBlkPtr)&wvol) != noErr) { + warn("Failed getting volume info"); + xfree(&name); + return 1; + } + + if (wvol.ioVSigWord != 0x4244) { + warn("Unknown filesystem type 0x%x", wvol.ioVSigWord); + xfree(&name); + return 1; + } + + wcinfo.ioVRefNum = vRefNum; + wcinfo.ioNamePtr = (StringPtr)name; + wcinfo.ioFDirIndex = -1; + wcinfo.ioDrParID = wdir.ioWDDirID; + wcinfo.ioDrDirID = wdir.ioWDDirID; + + tmp = xmalloc(FILENAME_MAX, "getpath"); + tmpret = xmalloc(FILENAME_MAX, "getpath"); + + /* go backwards, prepending each folder's parent */ + while (wcinfo.ioDrParID != 1) { + wcinfo.ioDrDirID = wcinfo.ioDrParID; /* .. */ + + if (PBGetCatInfo((CInfoPBPtr)&wcinfo, 0) != noErr) + break; + + len = name[0]; + PtoCstr(name); + if (retlen == 0) { + retlen = len; + strlcpy(tmpret, (char *)name, FILENAME_MAX); + } else { + strlcpy(tmp, tmpret, FILENAME_MAX); + snprintf(tmpret, FILENAME_MAX, "%s:%s", name, tmp); + } + } + + if (include_file) { + /* append the original path */ + memcpy(name, fileName, FILENAME_MAX); + PtoCstr(name); + if (retlen == 0) + strlcpy(tmpret, name, FILENAME_MAX); + else { + strlcat(tmpret, ":", FILENAME_MAX); + strlcat(tmpret, name, FILENAME_MAX); + } + } else if (retlen == 0) { + (*ret)[0] = 0; + xfree(&tmp); + xfree(&tmpret); + xfree(&name); + return 0; + } + + CtoPstr(tmpret); + memcpy(*ret, tmpret, FILENAME_MAX); + xfree(&tmp); + xfree(&tmpret); + xfree(&name); + + return 0; +} + +short +stat(char *path, struct stat *sb) +{ + char *ppath; + short ret; + + ppath = xstrdup(path, "stat"); + CtoPstr(ppath); + ret = FStat((unsigned char *)ppath, sb); + xfree(&ppath); + + return ret; +} + +short +FStat(Str255 path, struct stat *sb) +{ + CInfoPBRec catblock = { 0 }; + short ret; + + catblock.hFileInfo.ioNamePtr = path; + ret = PBGetCatInfo(&catblock, 0); + 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 +FIsDir(Str255 path) +{ + struct stat st; + short ret; + + if (FStat(path, &st) != 0) + return false; + + /* bit 4 is set in ioFlAttrib if the item is a directory */ + if (st.st_flags & (1 << 4)) + return true; + + return false; +} + +OSErr +copy_file(Str255 source, Str255 dest, bool overwrite) +{ + FInfo fi; + IOParam pb; + short error, source_ref, dest_ref; + + /* copy data fork */ + + error = GetFInfo(source, 0, &fi); + if (error) + return error; + + error = Create(dest, 0, fi.fdCreator, fi.fdType); + if (error == dupFNErr && overwrite) { + error = FSDelete(dest, 0); + if (error) + return error; + error = Create(dest, 0, fi.fdCreator, fi.fdType); + } + if (error) + return error; + + memset(&pb, 0, sizeof(pb)); + pb.ioNamePtr = source; + pb.ioPermssn = fsRdPerm; + error = PBOpen(&pb, false); + if (error) + return error; + source_ref = pb.ioRefNum; + + error = FSOpen(dest, 0, &dest_ref); + if (error) { + FSClose(source_ref); + return error; + } + + error = copy_file_contents(source_ref, dest_ref); + + FSClose(source_ref); + FSClose(dest_ref); + + if (error) + return error; + + /* + * Copy resource fork, open source as shared read/write in case it's + * an open resource file. + */ + source_ref = OpenRFPerm(source, 0, fsRdWrShPerm); + + if (source_ref == -1 && ResError() == eofErr) { + /* no resource fork */ + FSClose(source_ref); + FSClose(dest_ref); + return 0; + } + + if (source_ref == -1) + return ResError(); + + CreateResFile(dest); + if (ResError()) { + FSClose(source_ref); + return ResError(); + } + error = OpenRF(dest, 0, &dest_ref); + if (error) { + FSClose(source_ref); + return error; + } + + error = copy_file_contents(source_ref, dest_ref); + + FSClose(source_ref); + FSClose(dest_ref); + + return error; +} + +OSErr +copy_file_contents(short source_ref, short dest_ref) +{ + char *buf; + short error; + long source_size, count; + + GetEOF(source_ref, &source_size); + error = SetFPos(source_ref, fsFromStart, 0); + if (error) + return error; + error = SetEOF(dest_ref, source_size); + if (error) + return error; + error = SetFPos(dest_ref, fsFromStart, 0); + if (error) + return error; + + buf = xmalloc(1024, "copy_file_contents"); + + while (source_size > 0) { + count = 1024; + if (count > source_size) + count = source_size; + error = FSRead(source_ref, &count, buf); + if (error && error != eofErr) + break; + source_size -= count; + error = FSWrite(dest_ref, &count, buf); + if (error && error != eofErr) + break; + } + + xfree(&buf); + + if (error && error != eofErr) + return error; + + return 0; +} + +/* 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; + + 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; +} + + +/* + * Gestalt functions + */ +char * +gestalt_machine_type(void) +{ + short error; + long resp; + + error = Gestalt(gestaltMachineType, &resp); + if (error) + return NULL; + switch (resp) { + case gestaltClassic: + return "Macintosh 128K"; + case gestaltMacXL: + return "Macintosh XL"; + case gestaltMac512KE: + return "Macintosh 512Ke"; + case gestaltMacPlus: + return "Macintosh Plus"; + case gestaltMacSE: + return "Macintosh SE"; + case gestaltMacII: + return "Macintosh II"; + case gestaltMacIIx: + return "Macintosh IIx"; + case gestaltMacIIcx: + return "Macintosh IIcx"; + case gestaltMacSE030: + return "Macintosh SE/30"; + case gestaltPortable: + return "Macintosh Portable"; + case gestaltMacIIci: + return "Macintosh IIci"; + case gestaltMacIIfx: + return "Macintosh IIfx"; + case gestaltMacClassic: + return "Macintosh Classic"; + case gestaltMacIIsi: + return "Macintosh IIsi"; + case gestaltMacLC: + return "Macintosh LC"; + } + + return NULL; +} + +char * +get_version(bool long_version) +{ + static char vers_s[256] = { 0 }; + char *v; + VersRecHndl vers; + short len; + + vers = (VersRecHndl)GetResource('vers', 1); + if (!vers) + return "?.?"; + + HLock(vers); + v = (char *)&(*vers)->shortVersion; + len = v[0]; + if (long_version) { + v += len + 1; + len = v[0]; + } + memcpy(vers_s, v, len + 1); + ReleaseResource(vers); + + PtoCstr(vers_s); + return vers_s; +} + +/* + * 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); +} + +/* assumes a fixed-width style */ +short +TEGetWidth(short off, TEHandle te) +{ + TextStyle style; + short fheight, fwidth, ascent, old_font, old_size; + + TEGetStyle(off, &style, &fheight, &ascent, te); + + old_font = thePort->txFace; + old_size = thePort->txSize; + thePort->txFace = style.tsFont; + thePort->txSize = style.tsSize; + fwidth = CharWidth('m'); + thePort->txFace = old_font; + thePort->txSize = old_size; + + return fwidth; +} + +#define ceildiv(a,b) ((a / b) + (!!(a % b))) + +void +UpdateScrollbarForTE(GrafPtr win, ControlHandle control, TEHandle te, bool reset) +{ + size_t vlines, telines; + TERec *ter; + RgnHandle rgn; + short fheight, fwidth, max, val, per_line, horiz, max_chars, n; + + HLock(control); + HLock(te); + ter = *te; + + horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) < + ((*control)->contrlRect.right - (*control)->contrlRect.left)); + + if (horiz) { + fwidth = TEGetWidth(0, te); + per_line = ((ter->viewRect.right - ter->viewRect.left) / + fwidth) - 1; + for (max_chars = 0, n = 1; n < ter->nLines; n++) { + if (ter->lineStarts[n] - ter->lineStarts[n - 1] > max_chars) + max_chars = ter->lineStarts[n] - ter->lineStarts[n - 1]; + } + + if (max_chars <= per_line) + /* don't enable the scrollbar */ + max = 1; + else + max = max_chars - per_line + 1; + + if (reset) { + val = 1; + TESetSelect(0, 0, te); + } else { + val = (ter->viewRect.left - ter->destRect.left); + + if (val < 0) + val = 1; + else { + val = ceildiv(val, fwidth) + 1; + if (val > max) + max = val; + } + } + } else { + fheight = TEGetHeight(0, 0, te); + vlines = (ter->viewRect.bottom - ter->viewRect.top) / fheight; + telines = ter->nLines; + /* telines is inaccurate if the last line doesn't have any chars */ + //if (telines >= vlines) + // telines++; + max = telines - vlines + 1; + if (max < 1) + max = 1; + + if (reset) { + val = 1; + TESetSelect(0, 0, te); + } else { + val = (ter->viewRect.top - ter->destRect.top); + if (val < 0) + val = 1; + else { + val = ceildiv(val, fheight) + 1; + if (val > max) + max = val; + } + } + } + + /* + * Avoid SetCtlMax because it will redraw and then SetCtlValue will + * redraw again, which can cause a jump if we're trying to keep the + * scrollbar position in the same place (like at the bottom). + */ + (*control)->contrlMax = max; + SetCtlValue(control, val); + + rgn = NewRgn(); + RectRgn(rgn, &(*control)->contrlRect); + UpdtControl(win, rgn); + CloseRgn(rgn); + + HUnlock(te); + HUnlock(control); +} + +void +SetTrackControlTE(TEHandle te) +{ + track_control_te = te; +} + +pascal void +TrackMouseDownInControl(ControlHandle control, short part) +{ + TERec *ter; + short page, val, adj, fheight, fwidth, horiz, per_line; + + if (track_control_te == NULL) + panic("TrackMouseDownInControl without SetTrackControlTE"); + + HLock(track_control_te); + ter = *track_control_te; + + horiz = (((*control)->contrlRect.bottom - (*control)->contrlRect.top) < + ((*control)->contrlRect.right - (*control)->contrlRect.left)); + + if (horiz) { + fwidth = TEGetWidth(0, track_control_te); + per_line = ((ter->viewRect.right - ter->viewRect.left) / + fwidth) - 1; + page = ceildiv(GetCtlMax(control) + per_line, + ((per_line * 75) / 100)) + 1; + } else { + /* keep 1 line of context between pages */ + fheight = TEGetHeight(0, 0, track_control_te); + page = ((ter->viewRect.bottom - ter->viewRect.top) / fheight) - 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; + + if (horiz) + TEScroll(-adj * fwidth, 0, track_control_te); + else + TEScroll(0, -adj * fheight, track_control_te); + + SetCtlValue(control, val + adj); + + HUnlock(track_control_te); +} + +pascal bool +ModalDialogFilter(DialogPtr dlg, EventRecord *event, short *hit) +{ + WindowPtr event_win; + short event_in; + char key; + + switch (event->what) { + case mouseDown: + event_in = FindWindow(event->where, &event_win); + + switch (event_in) { + case inGoAway: + if (TrackGoAway(dlg, event->where)) { + *hit = -1; + return true; + } + } + break; + case keyDown: + key = event->message & charCodeMask; + + if (event->modifiers & cmdKey) { + switch (key) { + case 'x': + ZeroScrap(); + DlgCut(dlg); + break; + case 'c': + ZeroScrap(); + DlgCopy(dlg); + break; + case 'v': + if (TEFromScrap() == noErr) + DlgPaste(dlg); + break; + } + event->what = nullEvent; + return false; + } else if (key == 13 || key == 3) { + /* OK button */ + *hit = OK; + return true; + } + + break; + } + + return false; +} + +static short _password_dialog_ditl_id = -1; +static char *_password_dialog_storage = NULL; +static size_t _password_dialog_storage_len = 0; + +void +PasswordDialogFieldFilterSetup(short ditl_id, char *storage, size_t len) +{ + _password_dialog_ditl_id = ditl_id; + _password_dialog_storage = storage; + _password_dialog_storage_len = len; + + memset(_password_dialog_storage, 0, len); +} + +pascal bool +PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event, short *hit) +{ + DialogPeek dlgp; + short sel_start, sel_end; + char key; + + dlgp = (DialogPeek)dlg; + if (dlgp->editField == _password_dialog_ditl_id - 1) { + sel_start = (*(dlgp->textH))->selStart; + sel_end = (*(dlgp->textH))->selEnd; + + switch (event->what) { + case keyDown: + case autoKey: + key = event->message & charCodeMask; + if (event->modifiers & cmdKey) { + /* TODO: implement DlgPaste for cmd+v? */ + event->what = nullEvent; + return false; + } + + if (key == 8) { + /* backspace */ + if (sel_start == sel_end && sel_start > 0) + memmove(_password_dialog_storage + sel_start - 1, + _password_dialog_storage + sel_start, + _password_dialog_storage_len - sel_start - 1); + else if (sel_start != sel_end) + memmove(_password_dialog_storage + sel_start, + _password_dialog_storage + sel_end, + _password_dialog_storage_len - sel_end - 1); + } else if (sel_start >= _password_dialog_storage_len) { + event->what = nullEvent; + return false; + } else if (key >= ' ' && key <= '~') { + if (sel_start != sel_end) + /* delete selection before making space for new char */ + memmove(_password_dialog_storage + sel_start, + _password_dialog_storage + sel_end, + _password_dialog_storage_len - sel_end - 1); + memmove(_password_dialog_storage + sel_start + 1, + _password_dialog_storage + sel_start, + _password_dialog_storage_len - sel_start - 1); + _password_dialog_storage[sel_start] = key; + event->message = '•'; + } + _password_dialog_storage[(*(dlgp->textH))->teLength + 1] = '\0'; + sel_start = 0; + break; + } + } + + return ModalDialogFilter(dlg, event, hit); +} + +void +PasswordDialogFieldFinish(void) +{ + _password_dialog_ditl_id = -1; + _password_dialog_storage = NULL; + _password_dialog_storage_len = 0; +} + +/* (*(some_te))->caretHook = NullCaretHook; */ +pascal void +NullCaretHook(void) +{ + asm { + move.l (a7)+,d0 + rts + } +} + +static bool menu_bar_hidden = false; +static short old_mbar_height; +static Rect mbar_rect; +static RgnHandle mbar_save_region; + +void +HideMenuBar(void) +{ + RgnHandle mbar_region; + WindowPeek win; + + old_mbar_height = GetMBarHeight(); + SetRect(&mbar_rect, screenBits.bounds.left, screenBits.bounds.top, + screenBits.bounds.right, screenBits.bounds.top + old_mbar_height); + + mbar_save_region = NewRgn(); + mbar_region = NewRgn(); + + MBarHeight = 0; + CopyRgn(GetGrayRgn(), mbar_save_region); + RectRgn(mbar_region, &mbar_rect); + UnionRgn(GetGrayRgn(), mbar_region, GetGrayRgn()); + + win = (WindowPeek)FrontWindow(); + PaintOne(win, mbar_region); + PaintBehind(win, mbar_region); + CalcVis(win); + CalcVisBehind(win, mbar_region); + DisposeRgn(mbar_region); + + menu_bar_hidden = true; +} + +void +RestoreHiddenMenuBar(void) +{ + WindowPeek win; + + if (!menu_bar_hidden) + return; + + CopyRgn(mbar_save_region, GetGrayRgn()); + MBarHeight = old_mbar_height; + + RectRgn(mbar_save_region, &mbar_rect); + win = (WindowPeek)FrontWindow(); + CalcVis(win); + CalcVisBehind(win, mbar_save_region); + DisposeRgn(mbar_save_region); + + menu_bar_hidden = false; + + HiliteMenu(0); + DrawMenuBar(); +} + +/* C Extensions */ + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} + +char * +strndup(const char *str, size_t maxlen) +{ + char *copy; + const char *cp; + size_t len; + + /* strnlen */ + for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) + ; + + len = (size_t)(cp - str); + copy = malloc(len + 1); + if (copy != NULL) { + (void)memcpy(copy, str, len); + copy[len] = '\0'; + } + + return copy; +} + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + return (NULL); +} + +static struct format { + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned altForm : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + unsigned lSize : 1; + unsigned LSize : 1; + char sign; + char exponent; + int fieldWidth; + int precision; +} default_format; + +int bounded_vfprintf(FILE *fp, const char *fmt, va_list arg); +static int nullio(FILE *fp, int i); + +int +bounded_vfprintf(FILE *fp, const char *fmt, va_list arg) +{ + register int c, i, j, nwritten = 0; + register unsigned long n; + long double x; + register char *s; +#define VFPRINTF_BUFLEN 512 + char buf[VFPRINTF_BUFLEN], *digits, *t; + struct format F; + + for (c = *fmt; c; c = *++fmt) { + if (c != '%') + goto copy1; + F = default_format; + + /* decode flags */ + + for (;;) { + c = *++fmt; + if (c == '-') + F.leftJustify = TRUE; + else if (c == '+') + F.forceSign = TRUE; + else if (c == ' ') + F.sign = ' '; + else if (c == '#') + F.altForm = TRUE; + else if (c == '0') + F.zeroPad = TRUE; + else + break; + } + + /* decode field width */ + + if (c == '*') { + if ((F.fieldWidth = va_arg(arg, int)) < 0) { + F.leftJustify = TRUE; + F.fieldWidth = -F.fieldWidth; + } + c = *++fmt; + } + else { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); + } + + /* decode precision */ + + if (c == '.') { + if ((c = *++fmt) == '*') { + F.precision = va_arg(arg, int); + c = *++fmt; + } + else { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.precision = (10 * F.precision) + (c - '0'); + } + if (F.precision >= 0) + F.havePrecision = TRUE; + } + + /* perform appropriate conversion */ + + s = &buf[VFPRINTF_BUFLEN]; + if (F.leftJustify) + F.zeroPad = FALSE; +conv: switch (c) { + + /* 'h' size modifier */ + + case 'h': + F.hSize = TRUE; + c = *++fmt; + goto conv; + + /* 'l' size modifier */ + + case 'l': + F.lSize = TRUE; + c = *++fmt; + goto conv; + + /* 'L' size modifier */ + + case 'L': + F.LSize = TRUE; + c = *++fmt; + goto conv; + + /* decimal (signed) */ + + case 'd': + case 'i': + if (F.lSize) + n = va_arg(arg, long); + else + n = va_arg(arg, int); + if (F.hSize) + n = (short) n; + if ((long) n < 0) { + n = -n; + F.sign = '-'; + } + else if (F.forceSign) + F.sign = '+'; + goto decimal; + + /* decimal (unsigned) */ + + case 'u': + if (F.lSize) + n = va_arg(arg, unsigned long); + else + n = va_arg(arg, unsigned int); + if (F.hSize) + n = (unsigned short) n; + F.sign = 0; + goto decimal; + + /* decimal (common code) */ + + decimal: + if (!F.havePrecision) { + if (F.zeroPad) { + F.precision = F.fieldWidth; + if (F.sign) + --F.precision; + } + if (F.precision < 1) + F.precision = 1; + } + for (i = 0; n; n /= 10, i++) + *--s = n % 10 + '0'; + for (; i < F.precision; i++) + *--s = '0'; + if (F.sign) { + *--s = F.sign; + i++; + } + break; + + /* octal (unsigned) */ + + case 'o': + if (F.lSize) + n = va_arg(arg, unsigned long); + else + n = va_arg(arg, unsigned int); + if (F.hSize) + n = (unsigned short) n; + if (!F.havePrecision) { + if (F.zeroPad) + F.precision = F.fieldWidth; + if (F.precision < 1) + F.precision = 1; + } + for (i = 0; n; n /= 8, i++) + *--s = n % 8 + '0'; + if (F.altForm && i && *s != '0') { + *--s = '0'; + i++; + } + for (; i < F.precision; i++) + *--s = '0'; + break; + + /* hexadecimal (unsigned) */ + + case 'p': + F.havePrecision = F.lSize = TRUE; + F.precision = 8; + /* ... */ + case 'X': + digits = "0123456789ABCDEF"; + goto hexadecimal; + case 'x': + digits = "0123456789abcdef"; + /* ... */ + hexadecimal: + if (F.lSize) + n = va_arg(arg, unsigned long); + else + n = va_arg(arg, unsigned int); + if (F.hSize) + n = (unsigned short) n; + if (!F.havePrecision) { + if (F.zeroPad) { + F.precision = F.fieldWidth; + if (F.altForm) + F.precision -= 2; + } + if (F.precision < 1) + F.precision = 1; + } + for (i = 0; n; n /= 16, i++) + *--s = digits[n % 16]; + for (; i < F.precision; i++) + *--s = '0'; + if (F.altForm) { + *--s = c; + *--s = '0'; + i += 2; + } + break; + + /* character */ + + case 'c': + *--s = va_arg(arg, int); + i = 1; + break; + + /* string */ + + case 's': + s = va_arg(arg, char *); + if (F.altForm) { + i = (unsigned char) *s++; + if (F.havePrecision && i > F.precision) + i = F.precision; + } + else { + if (!F.havePrecision) + i = strlen(s); + else if (t = memchr(s, '\0', F.precision)) + i = t - s; + else + i = F.precision; + } + break; + + /* store # bytes written so far */ + + case 'n': + s = va_arg(arg, void *); + if (F.hSize) + * (short *) s = nwritten; + else if (F.lSize) + * (long *) s = nwritten; + else + * (int *) s = nwritten; + continue; + + /* oops - unknown conversion, abort */ + + default: + if (c != '%') + goto done; + copy1: + putc(c, fp); /* disregard EOF */ + ++nwritten; + continue; + } + + /* pad on the left */ + + if (i < F.fieldWidth && !F.leftJustify) { + do { + putc(' ', fp); /* disregard EOF */ + ++nwritten; + } while (i < --F.fieldWidth); + } + + /* write the converted result */ + + fwrite(s, 1, i, fp); /* disregard EOF */ + nwritten += i; + + /* pad on the right */ + + for (; i < F.fieldWidth; i++) { + putc(' ', fp); /* disregard EOF */ + ++nwritten; + } + } + + /* all done! */ + +done: + return(nwritten); +} + +int +snprintf(char *s, size_t size, const char *fmt, ...) +{ + return(vsnprintf(s, size, fmt, __va(fmt))); +} + +static int +nullio(FILE *fp, int i) +{ + return(EOF); +} + +int +vsnprintf(char *s, size_t size, const char *fmt, void *p) +{ + FILE f; + int n; + + memset(&f, 0, sizeof(f)); + f.refnum = -1; + f.ptr = (unsigned char *) s; + f.cnt = size; + f.proc = nullio; + f.dirty = 1; + + if ((n = bounded_vfprintf(&f, fmt, p)) >= 0) + s[n] = 0; + return(n); +} --- util.h Thu Aug 17 01:05:28 2023 +++ util.h Thu Aug 17 01:05:28 2023 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020-2022 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 nitems(what) (sizeof((what)) / sizeof((what)[0])) +#define member_size(type, member) sizeof(((type *)0)->member) + +#define MALLOC_NOTE_SIZE 32 + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define BOUND(a, min, max) ((a) > (max) ? (max) : ((a) < (min) ? (min) : (a))) + +#define EXPAND_TO_FIT(var, var_size, used_size, add, grow_amount) { \ + if ((used_size) + (add) >= (var_size)) { \ + while ((used_size) + (add) >= (var_size)) { \ + (var_size) += (grow_amount); \ + } \ + (var) = xrealloc((var), (var_size)); \ + } \ +} + +#define CHARS_TO_LONG(a,b,c,d) (unsigned long)(\ + ((unsigned long)((unsigned char)(a)) << 24) | \ + ((unsigned long)((unsigned char)(b)) << 16) | \ + ((unsigned long)((unsigned char)(c)) << 8) | \ + (unsigned long)((unsigned char)(d)) ) +#define CHARS_TO_SHORT(a,b) (unsigned short)(\ + ((unsigned short)((unsigned char)(a)) << 8) | \ + (unsigned short)((unsigned char)(b)) ) + +#define SCROLLBAR_WIDTH 16 + +/* GetMBarHeight() is not very useful */ +#define MENUBAR_HEIGHT 20 + +#define TICKS_PER_SEC 60L + +#define MAX_TEXTEDIT_SIZE 32767L + +#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 unsigned char u_int8_t; +typedef unsigned short u_int16_t; +typedef unsigned long u_int32_t; + +#define BYTE_ORDER BIG_ENDIAN + +typedef struct { + short push[2], rts; + void *addr; +} tCodeStub; + +struct stat { + short st_mode; + ssize_t st_size; + time_t st_ctime; + time_t st_mtime; + unsigned char st_flags; +}; + +void util_init(void); + +void * xmalloc(size_t, char *note); +void xfree(void *ptrptr); +void xfree_verify(void); +void * xmalloczero(size_t, char *note); +void * xcalloc(size_t, size_t, char *note); +void * xrealloc(void *src, size_t size); +void * xreallocarray(void *, size_t, size_t); +char * xstrdup(const char *, char *note); +char * xstrndup(const char *str, size_t maxlen, char *note); + +short getline(char *str, size_t len, char **ret); +const char * ordinal(unsigned short n); +size_t rtrim(char *str, char *chars); +long strpos_quoted(char *str, char c); +char * OSTypeToString(OSType type); + +unsigned long xorshift32(void); + +void panic(const char *format, ...); +void err(short ret, const char *format, ...); +void warnx(const char *format, ...); +void warn(const char *format, ...); +void note(const char *format, ...); +void appicon_note(const char *format, ...); +short ask(const char *format, ...); +#define ASK_YES 1 +#define ASK_NO 2 +void about(char *program_name); +void progress(char *format, ...); +void window_rect(WindowPtr win, Rect *ret); +void center_in_screen(short width, short height, bool titlebar, Rect *b); +Point centered_sf_dialog(void); + +Handle xNewHandle(size_t size); +Handle xGetResource(ResType type, short id); +StringHandle xGetString(short id); +char * xGetStringAsChar(short id); +long xGetStringAsLong(short id); +void xSetHandleSize(Handle h, Size s); + +short getpath(short vRefNum, Str255 fileName, Str255 *ret, bool include_file); +bool FIsDir(Str255 path); +short stat(char *path, struct stat *sb); +short FStat(Str255 path, struct stat *sb); +OSErr copy_file(Str255 source, Str255 dest, bool overwrite); +OSErr copy_file_contents(short source_ref, short dest_ref); +OSErr FSReadLine(short frefnum, char *buf, size_t buflen); + +char * gestalt_machine_type(void); +char * get_version(bool long_version); + +short FontHeight(short font_id, short size); +void DrawGrowIconOnly(WindowPtr win); +short TEGetWidth(short off, TEHandle te); +void UpdateScrollbarForTE(GrafPtr win, ControlHandle scroller, TEHandle te, + bool reset); +void SetTrackControlTE(TEHandle te); +pascal void TrackMouseDownInControl(ControlHandle control, short part); +pascal bool ModalDialogFilter(DialogPtr dlg, EventRecord *event, + short *hit); +void PasswordDialogFieldFilterSetup(short ditl_id, char *storage, + size_t len); +pascal bool PasswordDialogFieldFilter(DialogPtr dlg, EventRecord *event, + short *hit); +void PasswordDialogFieldFinish(void); +pascal void NullCaretHook(void); +void HideMenuBar(void); +void RestoreHiddenMenuBar(void); + +size_t strlcat(char *dst, const char *src, size_t dsize); +size_t strlcpy(char *dst, const char *src, size_t dsize); +char * strndup(const char *str, size_t maxlen); +char * strsep(char **stringp, const char *delim); +int snprintf(char *s, size_t size, const char *fmt, ...); +int vsnprintf(char *s, size_t size, const char *fmt, void *p); + +#endif