AmendHub

Download:

vkoskiv

/

MacNTP

/

amendments

/

11

Large rewrite, restructure the project into an INIT

* Moved NTP client to MacNTP.{c,h}
* Rewrite main to work as an INIT
* Load NTP url and utc offset from resourcs
* Fix many bugs
* Add many new bugs
* Remove all ANSI dependencies, it's a big library
* Remove util.{c,h}
* Add basic debugging, uncomment DEBUGGING define in main.c to enable

vkoskiv made amendment 11 8 months ago
--- MacNTP.π.r Thu Sep 7 20:07:34 2023 +++ MacNTP.π.r Thu Sep 7 20:07:34 2023 @@ -0,0 +1,47 @@ +data 'STR ' (129, "UTCOFFSETMINUTES", sysheap) { + $"0130" /* .0 */ +}; + +data 'STR ' (128, "NTPSERVER", sysheap) { + $"0F74 696D 652E 676F 6F67 6C65 2E63 6F6D" /* .time.google.com */ +}; + +data 'ICN#' (-4064) { + $"FFFF FFFF 8080 0001 8080 7C01 8881 8301" /* ....ÄÄ..ÄÄ|.àÅÉ. */ + $"9482 1081 9484 1041 9484 1041 9488 1021" /* îÇ.ÅîÑ.AîÑ.Aîà.! */ + $"9488 1021 9488 1021 9488 0821 9489 C421" /* îà.!îà.!îà.!îâƒ! */ + $"9485 8241 BE85 4441 A282 3881 A281 8301" /* îÖÇAæÖDA¢Ç8Å¢ÅÉ. */ + $"BE80 7C01 9480 2801 9480 2801 9480 2801" /* æÄ|.îÄ(.îÄ(.îÄ(. */ + $"9480 2801 9480 2801 9480 7C01 9480 4401" /* îÄ(.îÄ(.îÄ|.îÄD. */ + $"9480 4401 9480 7C01 9480 5401 94AF 93F5" /* îÄD.îÄ|.îÄT.îØì. */ + $"8880 2801 80AF C7F5 8080 0001 FFFF FFFF" /* àÄ(.ÄØ«.ÄÄ...... */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" /* ................ */ +}; + +data 'PICT' (-4041, purgeable) { + $"0085 00B7 00DF 00C9 00F0 1101 A030 3901" /* .Ö.∑...…....†09. */ + $"000A 00B7 00DF 00C9 00F0 3200 C800 DF00" /* ...∑...…..2.»... */ + $"C900 F090 0004 00B7 00D8 00C8 00F0 00B7" /* …..ê...∑.ÿ.»...∑ */ + $"00DF 00C8 00F0 00B7 00DF 00C8 00F0 0000" /* ...»...∑...».... */ + $"0007 C000 0018 3000 0021 0800 0041 0400" /* ..¿...0..!...A.. */ + $"0081 0200 0081 0200 0101 0100 0101 0100" /* .Å...Å.......... */ + $"0101 0100 0100 8140 0100 4100 3880 22DC" /* ......Å@..A.8Ä". */ + $"A480 1252 A440 0452 2420 0852 2418 3052" /* §Ä.R§@.R$ .R$.0R */ + $"0007 C000 FF" /* ..¿.. */ +}; + +data 'mach' (-4064, "init", purgeable) { + $"FFFF 0000" /* .... */ +}; + +data 'sysz' (0) { + $"0002 7100" /* ..q. */ +}; + --- MacNTP.c Wed Sep 6 23:33:40 2023 +++ MacNTP.c Wed Sep 6 23:33:40 2023 @@ -0,0 +1,189 @@ +#include <OSUtils.h> +#include "tcp.h" +#include "dnr.h" +#include "MacNTP.h" + +/* +TODO: +rename tcp.c/h to udp.c/h and rename _TCPInit to UDPInit +*/ + +void on_udp_receive(struct UDPiopb *iopb); + +// We already have this from dnr.h above, but Think C ignores it. +OSErr ResolveName(char *name, unsigned long *ipAddress); + +enum MacNTPError MacNTPSetSystemTime(struct ntp_packet *ntp, minutes_t utc_offset) { + /* + The NTP protocol expresses time as seconds from 01-01-1900. + The Macintosh expresses them as seconds from 01-01-1904. + This first step converts the two. + Seconds between 01-01-1900 and 01-01-1904 (none were leap years): + 4 * 365 * 24 * 60 * 60 = 126144000 + */ + //FIXME: We seem to be behind about 10 seconds after update. + OSErr err; + long mac_seconds; + long old_time; + Str255 date_string; + Str255 time_string; + long ntp_seconds = ntp->reference_timestamp.upper; + ntp_seconds += utc_offset * 60; + mac_seconds = ntp_seconds - 126144000; + GetDateTime(&old_time); + err = SetDateTime(mac_seconds); + if (err) { + if (err == -86) { // clkWrErr + return ClockWriteFailed; + } else if (err == -85) { // clkRdErr + return ClockReadFailed; + } + } + return Success; +} + +enum RequestState { + AwaitingResponse = 0, + GotResponse, + ResponseFailed +}; + +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]; + enum RequestState state; + + struct ntp_packet payload; +}; + +void on_udp_receive(struct UDPiopb *iopb) { + struct ntp_request *req; + req = (struct ntp_request *)iopb->csParam.receive.userDataPtr; + if (iopb->csParam.receive.rcvBuffLen != sizeof(struct ntp_packet)) { + req->state = ResponseFailed; + } else { + req->payload = *(struct ntp_packet *)iopb->csParam.receive.rcvBuff; + } + + req->state = GotResponse; +} + +enum MacNTPError MacNTPFetchTime(char *ntp_url, struct ntp_packet *packet, OSErr *supplemental) { + struct ntp_request req; + OSErr err; + UDPIOCompletionProc completion; + long i; + long retries = 40000; // Arbitrary + enum MacNTPError current_error = Success; + // I tried other values, but they all seem to make UDPCreate fail with + // the undocumented error code -23006 + unsigned char udp_buf[3000]; + + completion = NewUDPIOCompletionProc(on_udp_receive); + + if (packet == NULL) return PacketParamNull; + if (ntp_url == NULL) return InvalidURL; + if (sizeof(struct ntp_packet) != 48lu) return BadNtpStructSize; + err = _TCPInit(); + if (err != 0) { + if (supplemental) *supplemental = err; + // error goto tears down UDP, which we don't want here, so return + return MacTCPInitFailed; + } + + mntp_memset(&req, 0, sizeof(req)); + req.url = ntp_url; + + mntp_memset(&udp_buf, 0, sizeof(udp_buf)); + req.udp_buf_size = sizeof(udp_buf); + req.udp_buf = (unsigned char *)&udp_buf; + err = _UDPCreate(&req.udp_iopb, &req.udp_stream, (Ptr)req.udp_buf, + req.udp_buf_size, nil, nil, completion, false); + if (err) { + if (supplemental) *supplemental = err; + current_error = UDPCreateFailed; + goto error; + } + err = ResolveName(req.url, &req.host_ip); + if (err) { + if (supplemental) *supplemental = err; + current_error = DNSResolveFailed; + goto error; + } + //req.host_ip = 3639550720; + + // Set up NTP payload + mntp_memset(&req.payload, 0, sizeof(req.payload)); + // Set the mode to tell the server we're a client + req.payload.li_vn_mode = (4 << 3) | 3; + + // TODO: This should be fine, our time is always in 1904 anyway + // Just verify when receiving + mntp_memset(&req.payload.transmit_timestamp, 0x41, sizeof(req.payload.transmit_timestamp)); + + // And prepare for sending + mntp_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); + + err = _UDPSend(&req.udp_iopb, req.udp_stream, req.udp_wds, + req.host_ip, 123, nil, nil, false); + + if (err) { + if (supplemental) *supplemental = err; + current_error = UDPSendFailed; + goto error; + } + + req.state = AwaitingResponse; + + //FIXME: Only works asynchronously + err = _UDPRcv(&req.udp_iopb, + req.udp_stream, + nil, + nil, + req.host_ip, + 123, + (Ptr)&req, + completion, + true); + + if (err) { + if (supplemental) *supplemental = err; + current_error = UDPRcvFailed; + goto error; + } + + // Wait for event handler to fire + //FIXME: This needs a timeout, or we might be able to set it for udp + while (req.state == AwaitingResponse) { + if (retries <= 0) { + current_error = UDPRcvTimedOut; + goto error; + } + retries--; + } + if (req.state == ResponseFailed) { + current_error = InvalidNTPResponse; + goto error; + } + + // Verify received origin_ts is all 0x41s + for (i = 0; i < sizeof(struct ntp_ts); ++i) { + if (((unsigned char *)&req.payload.origin_timestamp)[i] != 0x41) { + current_error = OriginTimestampMismatch; + goto error; + } + } + + *packet = req.payload; + +error: + _UDPRelease(&req.udp_iopb, req.udp_stream, nil, nil, false); + return current_error; +} --- MacNTP.h Sun Sep 3 22:31:19 2023 +++ MacNTP.h Sun Sep 3 22:31:19 2023 @@ -0,0 +1,51 @@ +#ifndef _MACNTP_H +#define _MACNTP_H + +enum MacNTPError { + Success = 0, + MacTCPInitFailed, + InvalidURL, + BadNtpStructSize, + UDPCreateFailed, + UDPSendFailed, + UDPRcvFailed, + InvalidNTPResponse, + OriginTimestampMismatch, + DNSResolveFailed, + // Below only for SetSystemTime + ClockWriteFailed, + ClockReadFailed, + UDPRcvTimedOut, + PacketParamNull +}; + +struct ntp_ts { + unsigned long upper; /* Seconds since 1900-01-01 */ + unsigned long lower; /* FIXME: These values seem to be wrong */ +}; + +struct ntp_packet { + unsigned char li_vn_mode; + unsigned char stratum; + unsigned char poll; + char precision; + + unsigned long root_delay; + unsigned long root_dispersion; + unsigned long reference_id; + + struct ntp_ts reference_timestamp; + struct ntp_ts origin_timestamp; + struct ntp_ts receive_timestamp; + struct ntp_ts transmit_timestamp; +}; + +typedef long minutes_t; + +enum MacNTPError MacNTPFetchTime(char *ntp_url, struct ntp_packet *packet, OSErr *supplemental); + +// For convenience, you can just pass ntp_packet from above to set the system time +// Optionally, you can care about the contents of ntp_packet and do things with it. +enum MacNTPError MacNTPSetSystemTime(struct ntp_packet *ntp, minutes_t utc_offset); + +#endif --- main.c Thu Aug 24 16:56:44 2023 +++ main.c Thu Sep 7 20:19:27 2023 @@ -1,294 +1,175 @@ -#include <stdio.h> -#include <string.h> +#include <SetUpA4.h> #include <OSUtils.h> -#include <Packages.h> -#include "util.h" +#include "MacNTP.h" #include "tcp.h" -#include "dnr.h" +#include "prefs.h" -#define SLOW_DEBUG_PRINTF +// This enables DebugStr() printouts to aid development. +//#define DEBUGGING -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); -void set_system_time(struct ntp_ts ts); +pascal void NewInitGraf(Ptr globalPtr); +void dump_error(enum MacNTPError error, OSErr oserror); +int TryMacNTP(void); +void GetOurFilename(Str255 name); -static global_received = 0; -static global_err = 0; +#define InitGrafTrapnum 0xA86E +#define NTP_URL_STR_ID 128 +#define UTC_OFFSET_STR_ID 129 -// We already have this from dnr.h above, but Think C ignores it. -OSErr ResolveName(char *name, unsigned long *ipAddress); +long g_old_initgraf_address; +int g_ntp_success; +int g_ntp_error; +Handle g_mtcp_init_handle; -struct ntp_ts { - u_int32_t upper; /* Seconds since 1900-01-01 */ - u_int32_t lower; /* FIXME: These values seem to be wrong */ -}; +// NTP URL and UTC offset strings +// These are loaded into sys heap on boot +StringHandle g_url; +StringHandle g_utc; -struct ntp_packet { - u_int8_t li_vn_mode; - u_int8_t stratum; - u_int8_t poll; - 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; -} - -#define offset -void set_system_time(struct ntp_ts ts) { - /* - The NTP protocol expresses time as seconds from 01-01-1900. - The Macintosh expresses them as seconds from 01-01-1904. - This first step converts the two. - Seconds between 01-01-1900 and 01-01-1904 (none were leap years): - 4 * 365 * 24 * 60 * 60 = 126144000 - */ - //FIXME: Find out how timezone offsets are done on Sys6, if at all. - //FIXME: We seem to be behind about 10 seconds after update. - OSErr err; - long mac_seconds; - long old_time; - Str255 date_string; - Str255 time_string; - short utc_offset = 3; - long ntp_seconds = ts.upper; - ntp_seconds += utc_offset * 60 * 60; - mac_seconds = ntp_seconds - 126144000; - GetDateTime(&old_time); - err = SetDateTime(mac_seconds); - if (err) { - if (err == -86) { // clkWrErr - #ifdef SLOW_DEBUG_PRINTF - printf("clkWrErr encountered\n"); - #endif - return; - } else if (err == -85) { // clkRdErr - #ifdef SLOW_DEBUG_PRINTF - printf("clkRdErr encountered\n"); - #endif - return; - } +#ifdef DEBUGGING +void dump_error(enum MacNTPError error, OSErr oserror) { + Str255 numstr; + NumToString(oserror, numstr); + switch (error) { + case Success: + DebugStr("\pSuccess"); + break; + case MacTCPInitFailed: + DebugStr(numstr); + break; + case InvalidURL: + DebugStr("\pInvalidURL"); + break; + case BadNtpStructSize: + DebugStr("\pBadNtpStructSize"); + break; + case UDPCreateFailed: + DebugStr("\pUDPCreateFailed"); + break; + case UDPSendFailed: + DebugStr("\pUDPSendFailed"); + break; + case UDPRcvFailed: + DebugStr("\pUDPRcvFailed"); + break; + case InvalidNTPResponse: + DebugStr("\pInvalidNTPResponse"); + break; + case OriginTimestampMismatch: + DebugStr("\pOriginTimestampMismatch"); + break; + case DNSResolveFailed: + DebugStr("\pDNSResolveFailed"); + DebugStr(numstr); + break; + case ClockWriteFailed: + DebugStr("\pClockWriteFailed"); + break; + case ClockReadFailed: + DebugStr("\pClockReadFailed"); + break; + case UDPRcvTimedOut: + DebugStr("\pUDPRcvTimedOut"); + break; + case PacketParamNull: + DebugStr("\pPacketParamNull"); + break; } - #ifdef SLOW_DEBUG_PRINTF - printf("System clock set successfully!\n"); - IUDateString(old_time, abbrevDate, date_string); - IUTimeString(old_time, 1, time_string); - PtoCstr(date_string); - PtoCstr(time_string); - printf("Old: %s %s\n", date_string, time_string); - - IUDateString(mac_seconds, abbrevDate, date_string); - IUTimeString(mac_seconds, 1, time_string); - PtoCstr(date_string); - PtoCstr(time_string); - printf("New: %s %s\n", date_string, time_string); - #endif } +#endif -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]; - +int TryMacNTP(void) { struct ntp_packet payload; -}; - -//#define HOST_URL "time.google.com"; -#define HOST_URL "time.apple.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; + enum MacNTPError error; + OSErr oserr = 0; + long utc_offset_minutes; - printf("li_vn_mode: %u\n", received_ntp->li_vn_mode); - printf("stratum: %u\n", received_ntp->stratum); - printf("poll: %u\n", received_ntp->poll); - printf("precision: %i\n", received_ntp->precision); - printf("root_delay: %lu\n", received_ntp->root_delay); - printf("root_dispersion: %lu\n", received_ntp->root_dispersion); - printf("reference_id: %s\n", ref_id); + if (!g_url) return 1; + PtoCstr(*g_url); + error = MacNTPFetchTime((char *)(*g_url), &payload, &oserr); + HUnlock(g_url); + DisposHandle(g_url); + g_url = nil; - printf("reference_ts(hi): %lu\n", received_ntp->reference_timestamp.upper); - printf("reference_ts(lo): %lu\n", received_ntp->reference_timestamp.lower); + if (error) { +#ifdef DEBUGGING + dump_error(error, oserr); +#endif + return 1; + } - printf("origin_ts(hi): %lu\n", received_ntp->origin_timestamp.upper); - printf("origin_ts(lo): %lu\n", received_ntp->origin_timestamp.lower); + if (!g_utc) return 1; + StringToNum(*g_utc, &utc_offset_minutes); + HUnlock(g_utc); + DisposHandle(g_utc); + g_utc = nil; - printf("receive_ts(hi): %lu\n", received_ntp->receive_timestamp.upper); - printf("receive_ts(lo): %lu\n", received_ntp->receive_timestamp.lower); - - printf("transmit_ts(hi): %lu\n", received_ntp->transmit_timestamp.upper); - printf("transmit_ts(lo): %lu\n", received_ntp->transmit_timestamp.lower); - - printf("leap_information: %u, version_number: %u, mode: %u\n", - leap_information(*received_ntp), - version_number(*received_ntp), - mode(*received_ntp)); + error = MacNTPSetSystemTime(&payload, utc_offset_minutes); + if (error) { +#ifdef DEBUGGING + dump_error(error, oserr); +#endif + return 1; + } + return 0; } -void on_udp_receive(struct UDPiopb *iopb) { - struct ntp_request *req; - if (iopb->csParam.receive.rcvBuffLen != sizeof(struct ntp_packet)) { - #ifdef SLOW_DEBUG_PRINTF - printf("Unexpected payload size received!\n"); - #endif - global_err = 1; +#define SECONDS_TO_02_09_1996 2924510400ul + +pascal void NewInitGraf(Ptr globalPtr) { + unsigned long systime; + SetUpA4(); + if (g_ntp_success || g_ntp_error) goto skip; // Pretend we weren't here! + //ReadDateTime((unsigned long *)&systime); + // We only try to update the clock if we're well into the 20th century + /*if (systime > SECONDS_TO_02_09_1996) { + g_ntp_success = 1; + goto skip; + }*/ + + if (!TryMacNTP()) { + g_ntp_success = 1; } else { - req = (struct ntp_request *)iopb->csParam.receive.userDataPtr; - req->payload = *(struct ntp_packet *)iopb->csParam.receive.rcvBuff; + g_ntp_error = 1; } - global_received = 1; +skip: + CallPascal(globalPtr, g_old_initgraf_address); + RestoreA4(); } -int main(int argc, char **argv) { - struct ntp_request req; - char ip_s[16]; - short err; - UDPIOCompletionProc completion; - size_t i; - // I tried other values, but they all seem to make UDPCreate fail with - // the undocumented error code -23006 - unsigned char udp_buf[3000]; - completion = NewUDPIOCompletionProc(on_udp_receive); - - //TODO: Might need to call util_init() here, it sets up UI alert things - MaxApplZone(); - if (sizeof(struct ntp_packet) != 48lu) { - #ifdef SLOW_DEBUG_PRINTF - printf("sizeof(struct ntp_packet) != 48, exiting\n"); - #endif - return 1; +void main() { + Ptr mtcp_init_ptr; + asm { + move.L A0, mtcp_init_ptr; } - if (_TCPInit() != 0) { - #ifdef SLOW_DEBUG_PRINTF - printf("Failed to init MacTCP, exiting.\n"); - #endif - return 1; - } + RememberA0(); + SetUpA4(); + g_mtcp_init_handle = RecoverHandle(mtcp_init_ptr); + DetachResource(g_mtcp_init_handle); - memset(&req, 0, sizeof(req)); - req.url = HOST_URL; - - memset(&udp_buf, 0, sizeof(udp_buf)); - req.udp_buf_size = sizeof(udp_buf); - req.udp_buf = (unsigned char *)&udp_buf; - err = _UDPCreate(&req.udp_iopb, &req.udp_stream, (Ptr)req.udp_buf, - req.udp_buf_size, nil, nil, completion, false); - if (err) { - #ifdef SLOW_DEBUG_PRINTF - printf("UDPCreate failed: %d\n", err); - #endif - goto error; + //TODO: Load STR resources, movhhi and lock. Maybe Unlock+dispos in + // trap ? + g_url = GetString(NTP_URL_STR_ID); + g_utc = GetString(UTC_OFFSET_STR_ID); + if (!g_url || !g_utc) { +#ifdef DEBUGGING + DebugStr("\pSTR rsrcs missing"); +#endif } - err = ResolveName(req.url, &req.host_ip); - if (err) { - #ifdef SLOW_DEBUG_PRINTF - printf("Couldn't resolve host %s (%d)\n", req.url, err); - #endif - goto error; - } + DetachResource(g_url); + MoveHHi(g_url); + HLock(g_url); + DetachResource(g_utc); + MoveHHi(g_utc); + HLock(g_utc); - long2ip(req.host_ip, (char *)&ip_s); + g_old_initgraf_address = NGetTrapAddress(InitGrafTrapnum, ToolTrap); + NSetTrapAddress((long)&NewInitGraf, InitGrafTrapnum, ToolTrap); - // Set up NTP payload - memset(&req.payload, 0, sizeof(req.payload)); - // Set the mode to tell the server we're a client - req.payload.li_vn_mode = (4 << 3) | 3; + g_ntp_success = 0; + g_ntp_error = 0; - // 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)); - - // And prepare for sending - 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); - - #ifdef SLOW_DEBUG_PRINTF - printf("Sending NTP query to %s (%s)...\n", ip_s, req.url); - #endif - err = _UDPSend(&req.udp_iopb, req.udp_stream, req.udp_wds, - req.host_ip, 123, nil, nil, false); - - //FIXME: Only works asynchronously - err = _UDPRcv(&req.udp_iopb, - req.udp_stream, - nil, - nil, - req.host_ip, - 123, - (Ptr)&req, - completion, - true); - - if (err) { - #ifdef SLOW_DEBUG_PRINTF - printf("UDPRcv failed: %d\n", err); - #endif - goto error; - } - - // Wait for event handler to fire (not sure if needed) - //FIXME: This needs a timeout, or we might be able to set it for udp - while (!global_received) {} - if (global_err) goto error; - - #ifdef SLOW_DEBUG_PRINTF - printf("Got response:\n"); - #endif - - // Verify received origin_ts is all 0x41s - for (i = 0; i < sizeof(struct ntp_ts); ++i) { - if (((unsigned char *)&req.payload.origin_timestamp)[i] != 0x41) { - #ifdef SLOW_DEBUG_PRINTF - printf("origin_timestamp byte %lu != 0x41!\n", i); - #endif - goto error; - } - } - -#ifdef SLOW_DEBUG_PRINTF - dump_ntp_packet(&req.payload); -#endif - set_system_time(req.payload.reference_timestamp); - -#ifdef SLOW_DEBUG_PRINTF - printf("Goodbye.\n"); -#endif error: - _UDPRelease(&req.udp_iopb, req.udp_stream, nil, nil, false); - return 0; + RestoreA4(); } --- prefs.c Wed Sep 6 20:31:22 2023 +++ prefs.c Thu Sep 7 20:18:33 2023 @@ -0,0 +1,113 @@ +/* + Prefs storage for MacNTP. + The prefs are just two STR resources with the following IDs: + 128 - NTP server URL as a Pascal string + 129 - UTC offset as a Pascal String +*/ + +#include <OSUtils.h> +#include "prefs.h" + +// Yoink from dnr.c +void GetSystemFolder(short *vRefNumP, long *dirIDP); + +short SearchForVKOS(short vRefNum, long dirID); +short OpenMacNTP_RF(void); + +short SearchForVKOS(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 */ + //TODO: Maybe also check for xNIT? Is it a problem if this file is in use? + if ((fi.fileParam.ioFlFndrInfo.fdType == 'INIT' || + fi.fileParam.ioFlFndrInfo.fdType == 'xNIT') && + fi.fileParam.ioFlFndrInfo.fdCreator == 'VKOS') { + /* found the MacNTP file? */ + refnum = HOpenResFile(vRefNum, dirID, filename, fsRdPerm); + return refnum; + } + + /* check next file in system folder */ + fi.fileParam.ioFDirIndex++; + fi.fileParam.ioDirID = dirID; /* PBHGetFInfo() clobbers ioDirID */ + } + return -1; +} + +short OpenMacNTP_RF(void) { + short refnum; + short vRefNum; + long dirID; + + vRefNum = 0; + dirID = 0; + + GetSystemFolder(&vRefNum, &dirID); + + // VKOS is the creator ID for MacNTP. + // It used to be that Apple wanted developers to officially register + // these creator IDs before using them in production, but I doubt they + // accept registrations these days! + refnum = SearchForVKOS(vRefNum, dirID); + if (refnum = -1) { + return -1; + } + + return refnum; +} + +/* It might be that these will only be useful for the cdev, I'm having + a hard time trying to get the INIT to look in the system folder. + I think it makes sense that only the cdev cares about these to update + the rsrc file on disk, and we just load it while we load everything else + in the init, and keep those in the system heap. It will only ever + query the time on boot anyway. + */ + +// This should hopefully maybe maybe return a C string of the URL maybe +// Written at 1AM on a wednesday though, so buyer beware. +short get_url_param(Str255 url) { + StringHandle url_handle; + short refnum; + refnum = OpenMacNTP_RF(); + if (refnum == -1) { + return -1; + } + + url_handle = GetString(128); + HLock(url_handle); + if (url_handle == NULL) { + return -1; + } + BlockMove(url_handle, &url, 1 + *(char *)(url_handle)); //sus + HUnlock(url_handle); + DisposHandle(url_handle); // Needed?? + CloseResFile(refnum); + + PtoCstr(url); + return 0; +} + +short set_url_param(Str255 url) { + //TODO + return -1; +} + +short get_utc_param(Str255 url) { + //TODO + return -1; +} + +short set_utc_param(Str255 url) { + //TODO + return -1; +} --- prefs.h Wed Sep 6 01:25:31 2023 +++ prefs.h Wed Sep 6 01:25:31 2023 @@ -0,0 +1,11 @@ +/* + Prefs storage for MacNTP. + These are shared between the INIT and cdev +*/ + +// This should hopefully maybe maybe return a C string of the URL maybe +// Written at 1AM on a wednesday though, so buyer beware. +short get_url_param(Str255 url); +short set_url_param(Str255 url); +short get_utc_param(Str255 url); +short set_utc_param(Str255 url); --- README Tue Aug 22 22:09:33 2023 +++ README Thu Sep 7 21:05:05 2023 @@ -1,14 +1,33 @@ +MacNTP is the premier network time solution for System 6 users running MacTCP on their 68k Macs. It pretty much just does what it says on the tin - seamlessly updates the system clock of your Mac via NTP at boot time, so you don't need to put a leak-prone clock battery in your precious computer. + +MacNTP is © Valtteri Koskivuori (vkoskiv), and MIT-licensed. +If you find it useful, please don't hesitate to reach out with feedback or feature suggestions. You can reach me by mailing a letter to: + +- Valtteri Koskivuori, P.O. Box 347, Helsinki, Finland +- Just kidding, my details are at the end of this document + +More details: + +MacNTP works by patching the InitGraf trap. The patched trap (in main.c) checks to see if MacNTP has already run, and if it hasn't, it then checks the system time. If the system time is obviously incorrect, it will only then try to fetch the current time from the NTP server. + +If you want to compile it yourself: + Prerequisites: - System 6 (I haven't tested on other systems) -- MacTCP installed and properly configured (connection verified to work) +- MacTCP installed and properly configured (check that your connection works before trying this!) - Think C 5.0.1 Setup: -- Create new Think C project -- Add all .c files, and the following libraries: MacTraps, MacTraps2 and ANSI -- Drag ANSI all the way to the bottom of the list in the project window. This should put it in a separate code segment, as indicated by the dotted line separating ANSI from the rest of the sources/libs. +- Create new Think C project, name it "MacNTP.π" (option+p gets you π) +- Add all .c files, as well as MacTraps and MacTraps2 from Think C's Mac Libraries +- Use the SARez tool to compile "MacNTP.π.r" into a resource file, name it "MacNTP.π.rsrc" - It's important you name it exactly that, otherwise Think C won't find it. It also has to be in the same directory as your project file. - Ensure "Generate 68020 instructions" is disabled under Edit->Options->Compiler options (Unless your system has a 68020!) +- Select Project->Set Project Type, select Code Resource, Set File Type to "INIT", Creator to "VKOS", Name to whatever you want, Type to "INIT", ID to "0" (zero), Attrs to 70. Leave Custom Header deselected, and hit OK. -Select "Run" or "Build Application" under "Project" to run it or build a standalone program. +Select "Build Code Resource" under "Project" to build the INIT document. Think C will then prompt you for a file name for the INIT document. INITs are loaded in alphabetical order, so it is important you choose a name that comes after "MacTCP" alphabetically, as MacNTP depends on MacTCP being present. I usually name it "zMacNTP" to have it load last. You can then save this file in your System Folder and give it a go. -Don't hesitate to contact me, vkoskiv, on IRC if you run into any issues. +Caveat: Upon boot, MacNTP will check the system clock, and won't try updating it if the date is later than September 2nd, 1996. If you edit the UTC offset setting directly with ResEdit, you will need to set your clock back to before that date for it to update the system clock with the new offset. + +Don't hesitate to contact me, vkoskiv, on IRC (libera) if you run into any issues. I'm also @vkoskiv on Twitter and Discord. Also vkoskiv@gmail.com + +"Mac" is a trademark of Apple Inc., registered in the U.S. and other countries and regions. --- tcp.c Mon Aug 21 21:43:32 2023 +++ tcp.c Thu Aug 31 21:41:25 2023 @@ -1,4 +1,3 @@ -#include <stdio.h> #include <string.h> #include "tcp.h" @@ -13,13 +12,23 @@ short gIPPDriverRefNum; unsigned long resolved_ip; +// Invent our own memset, to avoid need to pull in all of ANSI-A4 +void mntp_memset(void *ptr, int c, long n) { + size_t i; + if (!ptr) return; + for (i = 0; i < n; ++i) { + // This feels weird + ((char *)ptr)[i] = (char)c; + } +} + OSErr _TCPInit(void) { ParamBlockRec pb; OSErr osErr; - memset(&pb, 0, sizeof(pb)); + mntp_memset(&pb, 0, sizeof(pb)); gIPPDriverRefNum = -1; @@ -40,7 +49,7 @@ _TCPGetOurIP(ip_addr *ip, long *netMask) OSErr osErr; GetAddrParamBlock pb; - memset(&pb, 0, sizeof(pb)); + mntp_memset(&pb, 0, sizeof(pb)); pb.csCode = ipctlGetAddr; pb.ioCRefNum = gIPPDriverRefNum; @@ -68,7 +77,7 @@ _TCPCreate(TCPiopb *pb, StreamPtr *stream, Ptr rcvBufP { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPCreate; pb->ioCompletion = ioCompletion; @@ -97,7 +106,7 @@ _TCPPassiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr OSErr osErr; short index; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPPassiveOpen; pb->ioCompletion = ioCompletion; @@ -144,7 +153,7 @@ _TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr OSErr osErr; short index; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPActiveOpen; pb->ioCompletion = ioCompletion; @@ -186,7 +195,7 @@ OSErr _TCPSend(TCPiopb *pb, StreamPtr stream, wdsEntry *wdsPtr, Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) { - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPSend; pb->ioCompletion = ioCompletion; @@ -212,7 +221,7 @@ _TCPNoCopyRcv(TCPiopb *pb, StreamPtr stream, Ptr rdsPt unsigned short rdsLength, Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) { - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPNoCopyRcv; pb->ioCompletion = ioCompletion; @@ -237,7 +246,7 @@ _TCPRcv(TCPiopb *pb, StreamPtr stream, Ptr rcvBufPtr, { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPRcv; pb->ioCompletion = ioCompletion; @@ -263,7 +272,7 @@ OSErr _TCPBfrReturn(TCPiopb *pb, StreamPtr stream, Ptr rdsPtr, Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) { - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPRcvBfrReturn; pb->ioCompletion = ioCompletion; @@ -281,7 +290,7 @@ OSErr _TCPClose(TCPiopb *pb, StreamPtr stream, Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) { - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPClose; pb->ioCompletion = ioCompletion; @@ -301,7 +310,7 @@ OSErr _TCPAbort(TCPiopb *pb, StreamPtr stream, Ptr userData, TCPIOCompletionProc ioCompletion, Boolean async) { - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPAbort; pb->ioCompletion = ioCompletion; @@ -320,7 +329,7 @@ _TCPStatus(TCPiopb *pb, StreamPtr stream, struct TCPSt { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPStatus; pb->ioCompletion = ioCompletion; @@ -343,7 +352,7 @@ _TCPRelease(TCPiopb *pb, StreamPtr stream, Ptr userDat { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = TCPRelease; pb->ioCompletion = ioCompletion; @@ -363,7 +372,7 @@ _UDPMaxMTUSize(UDPiopb *pb, short *mtu) { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = UDPMaxMTUSize; pb->ioCRefNum = gIPPDriverRefNum; @@ -386,7 +395,7 @@ _UDPCreate(UDPiopb *pb, StreamPtr *stream, Ptr rcvBufP { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = UDPCreate; pb->ioCompletion = ioCompletion; @@ -411,7 +420,7 @@ _UDPSend(UDPiopb *pb, StreamPtr stream, wdsEntry *wdsP udp_port remotePort, Ptr userData, UDPIOCompletionProc ioCompletion, Boolean async) { - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = UDPWrite; pb->ioCompletion = ioCompletion; @@ -435,7 +444,7 @@ OSErr _UDPRcv(UDPiopb *pb, StreamPtr stream, Ptr rcvBu { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = UDPRead; pb->ioCompletion = ioCompletion; @@ -463,7 +472,7 @@ _UDPRelease(UDPiopb *pb, StreamPtr stream, Ptr userDat { OSErr osErr; - memset(pb, 0, sizeof(*pb)); + mntp_memset(pb, 0, sizeof(*pb)); pb->csCode = UDPRelease; pb->ioCompletion = ioCompletion; @@ -511,13 +520,6 @@ ip2long(char *ip) 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 @@ -550,7 +552,7 @@ SOCKS5TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_ data[1] = 1; /* nmethods */ data[2] = SOCKS_METHOD_AUTH_NONE; - memset(&wds, 0, sizeof(wds)); + mntp_memset(&wds, 0, sizeof(wds)); wds[0].ptr = (Ptr)&data; wds[0].length = 3; @@ -586,7 +588,7 @@ SOCKS5TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_ data[len++] = (remote_port >> 8); data[len++] = (remote_port & 0xff); - memset(&wds, 0, sizeof(wds)); + mntp_memset(&wds, 0, sizeof(wds)); wds[0].ptr = (Ptr)&data; wds[0].length = len; --- tcp.h Mon Aug 21 21:11:34 2023 +++ tcp.h Sun Sep 3 23:11:42 2023 @@ -19,6 +19,8 @@ typedef ProcPtr UDPNotifyProc; typedef void (*TCPIOCompletionProc)(struct TCPiopb *iopb); typedef void (*UDPIOCompletionProc)(struct UDPiopb *iopb); +void mntp_memset(void *ptr, int c, long n); + OSErr _TCPInit(void); OSErr _TCPGetOurIP(ip_addr *ip, long *netMask); OSErr _TCPCreate(TCPiopb *pb, StreamPtr *stream, Ptr rcvBufPtr, --- util.c Tue Aug 22 20:34:58 2023 +++ util.c Sun Sep 3 23:11:42 2023 @@ -1,2072 +0,0 @@ -/* - * 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 Tue Aug 22 18:00:58 2023 +++ util.h Sun Sep 3 23:11:42 2023 @@ -1,169 +0,0 @@ -/* - * 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 char 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