From 1a9cb0d7669aaced5f57206de2dd4334d7061bf1 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Mon, 13 Jul 2020 22:52:44 -0400 Subject: [PATCH] Beef up the IUDateString/UIDateTime functions. Needed for a Date built-in replacement. --- toolbox/dispatch.cpp | 4 + toolbox/packages.cpp | 572 +++++++++++++++++++++++++++++++++++++++++-- toolbox/packages.h | 1 + 3 files changed, 558 insertions(+), 19 deletions(-) diff --git a/toolbox/dispatch.cpp b/toolbox/dispatch.cpp index e2a2901..58ea203 100644 --- a/toolbox/dispatch.cpp +++ b/toolbox/dispatch.cpp @@ -1095,6 +1095,10 @@ namespace ToolBox { d0 = OS::OSDispatch(trap); break; + case 0xa8b5: + d0 = Packages::ScriptUtil(trap); + break; + case 0xABFF: d0 = Debug::DebugStr(trap); break; diff --git a/toolbox/packages.cpp b/toolbox/packages.cpp index 9682342..8eb3405 100644 --- a/toolbox/packages.cpp +++ b/toolbox/packages.cpp @@ -52,14 +52,308 @@ using OS::MacToUnix; namespace Packages { + enum { + shortDate = 0, + longDate = 1, + abbrevDate = 2 + }; + + enum { + mdy = 0, + dmy = 1, + ymd = 2, + myd = 3, + dym = 4, + ydm = 5 + }; + + enum { + timeCycle24 = 0, /*time sequence 0:00 - 23:59*/ + timeCycleZero = 1, /*time sequence 0:00-11:59, 0:00 - 11:59*/ + timeCycle12 = 255, /*time sequence 12:00 - 11:59, 12:00 - 11:59*/ + zeroCycle = 1, /*old name for timeCycleZero*/ + longDay = 0, /*day of the month*/ + longWeek = 1, /*day of the week*/ + longMonth = 2, /*month of the year*/ + longYear = 3, /*year*/ + supDay = 1, /*suppress day of month*/ + supWeek = 2, /*suppress day of week*/ + supMonth = 4, /*suppress month*/ + supYear = 8, /*suppress year*/ + + dayLdingZ = 32, + mntLdingZ = 64, + century = 128, + secLeadingZ = 32, + minLeadingZ = 64, + hrLeadingZ = 128 + }; + + #if 0 +struct Intl0Rec { + char decimalPt; /*decimal point character*/ + char thousSep; /*thousands separator character*/ + char listSep; /*list separator character*/ + char currSym1; /*currency symbol*/ + char currSym2; + char currSym3; + UInt8 currFmt; /*currency format flags*/ + UInt8 dateOrder; /*order of short date elements: mdy, dmy, etc.*/ + UInt8 shrtDateFmt; /*format flags for each short date element*/ + char dateSep; /*date separator character*/ + UInt8 timeCycle; /*specifies time cycle: 0..23, 1..12, or 0..11*/ + UInt8 timeFmt; /*format flags for each time element*/ + char mornStr[4]; /*trailing string for AM if 12-hour cycle*/ + char eveStr[4]; /*trailing string for PM if 12-hour cycle*/ + char timeSep; /*time separator character*/ + char time1Suff; /*trailing string for AM if 24-hour cycle*/ + char time2Suff; + char time3Suff; + char time4Suff; + char time5Suff; /*trailing string for PM if 24-hour cycle*/ + char time6Suff; + char time7Suff; + char time8Suff; + UInt8 metricSys; /*255 if metric, 0 if inches etc.*/ + short intl0Vers; /*region code (hi byte) and version (lo byte)*/ +}; + #endif + enum { + o_decimalPt = 0, + o_thousSep = 1, + o_listSep = 2, + o_currSym1 = 3, + o_currSym2 = 4, + o_currSym3 = 5, + o_currFmt = 6, + o_dateOrder = 7, + o_shrtDateFmt = 8, + o_dateSep = 9, + o_timeCycle = 10, + o_timeFmt = 11, + o_mornStr = 12, + o_eveStr = 16, + o_timeSep = 20, + o_time1Suff = 21, + o_time2Suff = 22, + o_time3Suff = 23, + o_time4Suff = 24, + o_time5Suff = 25, + o_time6Suff = 26, + o_time7Suff = 27, + o_time8Suff = 28, + o_metricSys = 29, + o_intl0Vers = 30, /* 16-bit */ + + }; + + struct DateFormat { + uint8_t dateOrder = mdy; + uint8_t dateSep = '/'; + uint8_t shrtDateFmt = 0; + + void unpack(uint32_t intlPtr) { + if (!intlPtr) return; + + dateOrder = memoryReadByte(intlPtr + o_dateOrder); + shrtDateFmt = memoryReadByte(intlPtr + o_shrtDateFmt); + dateSep = memoryReadByte(intlPtr + o_dateSep); + } + }; + + struct TimeFormat { + uint8_t timeCycle = timeCycle12; + uint8_t timeSep = ':'; + uint8_t timeFmt = secLeadingZ + minLeadingZ; + + std::string mornStr = " AM"; + std::string eveStr = " PM"; + + void unpack(uint32_t intlPtr) { + if (!intlPtr) return; + + timeCycle = memoryReadByte(intlPtr + o_timeCycle); + timeFmt = memoryReadByte(intlPtr + o_timeFmt); + timeSep = memoryReadByte(intlPtr + o_timeSep); + + if (timeCycle == timeCycle24) { + mornStr = read_string(intlPtr + o_time1Suff); + eveStr = read_string(intlPtr + o_time5Suff); + } else { + mornStr = read_string(intlPtr + o_mornStr); + eveStr = read_string(intlPtr + o_eveStr); + } + } + + static std::string read_string(uint32_t address) { + std::string rv; + for (unsigned i = 0; i < 4; ++i, ++address) { + char c = memoryReadByte(address); + if (!c) break; + rv.push_back(c); + } + return rv; + } + }; + + + namespace { + + std::string FormatAbbrDate(uint32_t dateTime, uint32_t intlPtr) { + std::string rv; + + char dd[8]; + char mm[8]; + char yy[8]; + + std::string sep; + + DateFormat df; + + time_t t = MacToUnix(dateTime); + struct tm *tm = ::localtime(&t); + + if (intlPtr) df.unpack(intlPtr); + if (df.dateSep) sep.push_back(df.dateSep); + + if (df.shrtDateFmt & dayLdingZ) snprintf(dd, sizeof(dd), "%02u", tm->tm_mday); + else snprintf(dd, sizeof(dd), "%u", tm->tm_mday); + + if (df.shrtDateFmt & mntLdingZ) snprintf(mm, sizeof(mm), "%02u", tm->tm_mon + 1); + else snprintf(mm, sizeof(mm), "%u", tm->tm_mon + 1); + + if (df.shrtDateFmt & century) snprintf(yy, sizeof(yy), "%04u", tm->tm_year + 1900); + else snprintf(yy, sizeof(yy), "%02u", tm->tm_year % 100); + + + switch(df.dateOrder) { + default: + case mdy: + rv = mm + sep + dd + sep + yy; + break; + case dmy: + rv = dd + sep + mm + sep + yy; + break; + case ymd: + rv = yy + sep + mm + sep + dd; + break; + case myd: + rv = mm + sep + yy + sep + dd; + break; + case dym: + rv = dd + sep + yy + sep + mm; + break; + case ydm: + rv = yy + sep + dd + sep + mm; + break; + } + return rv; + } + + std::string FormatDate(uint32_t dateTime, unsigned form, uint32_t intlPtr) { + /* not localized. some foreign day/month strings include macroman chars */ + + if (form == abbrevDate) + return FormatAbbrDate(dateTime, intlPtr); + + char buffer[256]; + int length; + std::string rv; + + + + time_t t = MacToUnix(dateTime); + struct tm *tm = ::localtime(&t); + + + switch(form) { + case shortDate: + // Sat, Jul 11, 2020 + // length = std::strftime(buffer, sizeof(buffer), "%a, %b %e, %Y", tm); + length = std::strftime(buffer, sizeof(buffer), "%a, %b ", tm); + rv = std::string(buffer, buffer + length); + length = std::snprintf(buffer, sizeof(buffer), "%u, %04u", + tm->tm_mday, tm->tm_year + 1900); + rv.append(buffer, buffer + length); + break; + case longDate: + default: + // Saturday, July 11, 2020 + // length = std::strftime(buffer, sizeof(buffer), "%A, %B %e, %Y", tm); + length = std::strftime(buffer, sizeof(buffer), "%A, %B ", tm); + rv = std::string(buffer, buffer + length); + length = std::snprintf(buffer, sizeof(buffer), "%u, %04u", + tm->tm_mday, tm->tm_year + 1900); + rv.append(buffer, buffer + length); + break; + } + + return rv; + + } + + + std::string FormatTime(uint32_t dateTime, bool seconds, uint32_t intlPtr) { + + char hh[8]; + char mm[8]; + char ss[8]; + + std::string rv; + + std::string sep; + + TimeFormat tf; + + time_t t = MacToUnix(dateTime); + struct tm *tm = ::localtime(&t); + + if (intlPtr) tf.unpack(intlPtr); + if (tf.timeSep) sep.push_back(tf.timeSep); + + + if (tf.timeFmt & secLeadingZ) snprintf(ss, sizeof(ss), "%02u", tm->tm_sec); + else snprintf(ss, sizeof(ss), "%u", tm->tm_sec); + + if (tf.timeFmt & minLeadingZ) snprintf(mm, sizeof(mm), "%02u", tm->tm_min); + else snprintf(mm, sizeof(mm), "%u", tm->tm_min); + + + unsigned h = tm->tm_hour; + bool am = h < 12; + switch (tf.timeCycle) { + default: + case timeCycle12: + h %= 12; + if (!h) h = 12; + break; + case timeCycle24: + break; + case timeCycleZero: + h %= 12; + break; + } + + if (tf.timeFmt & hrLeadingZ) snprintf(hh, sizeof(hh), "%02u", h); + else snprintf(hh, sizeof(hh), "%u", h); + + + rv = hh + sep + mm; + if (seconds) { + rv += sep + ss; + } + + rv.append( am ? tf.mornStr : tf.eveStr); + + return rv; + } + + } uint16_t IUDateString() { - // void IUDateString(long dateTime,DateForm longFlag,Str255 result) - // DateForm doesn't seem to do anything. + // void IUDateString(long dateTime, DateForm longFlag, Str255 result) - char buffer[256]; - int length; std::string out; uint32_t dateTime; @@ -71,15 +365,35 @@ namespace Packages { Log(" IUDateString(%08x, %02x, %08x)\n", dateTime, flag, result); - time_t t = MacToUnix(dateTime); - struct tm *tm = ::localtime(&t); - - // not strictly correct -- uses %d/%d/%2d form. - length = std::strftime(buffer, sizeof(buffer), "%m/%d/%y", tm); - out.assign(buffer, length); - + out = FormatDate(dateTime, flag, 0); ToolBox::WritePString(result, out); return 0; + + } + + uint16_t IUDatePString() + { + // void IUDateString(long dateTime, DateForm longFlag, Str255 result, Handle intlHandle) + + std::string out; + + uint32_t dateTime; + uint8_t flag; + uint32_t result; + uint32_t intlHandle; + uint32_t intlPtr; + + + StackFrame<14>(dateTime, flag, result, intlHandle); + + Log(" IUDatePString(%08x, %02x, %08x, %08x)\n", dateTime, flag, result, intlHandle); + + intlPtr = intlHandle ? memoryReadLong(intlHandle) : 0; + + out = FormatDate(dateTime, flag, intlPtr); + ToolBox::WritePString(result, out); + return 0; + } uint16_t IUTimeString() @@ -87,8 +401,6 @@ namespace Packages { // void IUTimeString(long dateTime,Boolean wantSeconds,Str255 result) // output: 12:00:00 AM or 12:00 AM - char buffer[256]; - int length; std::string out; uint32_t dateTime; @@ -99,17 +411,210 @@ namespace Packages { StackFrame<10>(dateTime, wantSeconds, result); Log(" IUTimeString(%08x, %02x, %08x)\n", dateTime, wantSeconds, result); - time_t t = MacToUnix(dateTime); - struct tm *tm = ::localtime(&t); - - length = std::strftime(buffer, sizeof(buffer), wantSeconds ? "%I:%M:%S %p" : "%I:%M %p", tm); - - out.assign(buffer, length); + out = FormatTime(dateTime, wantSeconds, 0); ToolBox::WritePString(result, out); return 0; } + + uint16_t IUTimePString() { + // void IUTimePString(long dateTime, Boolean wantSeconds, Str255 result, Handle intlHandle) + + + std::string out; + + uint32_t dateTime; + uint8_t wantSeconds; + uint32_t result; + uint32_t intlHandle; + uint32_t intlPtr; + + StackFrame<14>(dateTime, wantSeconds, result, intlHandle); + Log(" IUTimePString(%08x, %02x, %08x, %08x)\n", dateTime, wantSeconds, result, intlHandle); + + intlPtr = intlHandle ? memoryReadLong(intlHandle) : 0; + + out = FormatTime(dateTime, wantSeconds, intlPtr); + ToolBox::WritePString(result, out); + return 0; + } + + uint16_t InitDateCache() + { + // OSErr InitDateCache(DateCachePtr theCache) + uint32_t theCache; + uint32_t sp; + sp = StackFrame<4>(theCache); + Log(" InitDateCache(%08x)\n", theCache); + /* cache not used */ + + ToolReturn<4>(sp, 0); + return 0; + } + + enum { + /* StringToDate status values */ + fatalDateTime = 0x8000, /* StringToDate and String2Time mask to a fatal error */ + longDateFound = 1, /* StringToDate mask to long date found */ + leftOverChars = 2, /* StringToDate & Time mask to warn of left over characters */ + sepNotIntlSep = 4, /* StringToDate & Time mask to warn of non-standard separators */ + fieldOrderNotIntl = 8, /* StringToDate & Time mask to warn of non-standard field order */ + extraneousStrings = 16, /* StringToDate & Time mask to warn of unparsable strings in text */ + tooManySeps = 32, /* StringToDate & Time mask to warn of too many separators */ + sepNotConsistent = 64, /* StringToDate & Time mask to warn of inconsistent separators */ + tokenErr = 0x8100, /* StringToDate & Time mask for 'tokenizer err encountered' */ + cantReadUtilities = 0x8200, + dateTimeNotFound = 0x8400, + dateTimeInvalid = 0x8800 + }; + +#if 0 +typedef short StringToDateStatus; +typedef StringToDateStatus String2DateStatus; +struct DateCacheRecord { + short hidden[256]; /* only for temporary use */ +}; +typedef struct DateCacheRecord DateCacheRecord; +typedef DateCacheRecord * DateCachePtr; +struct DateTimeRec { + short year; + short month; + short day; + short hour; + short minute; + short second; + short dayOfWeek; +}; +typedef struct DateTimeRec DateTimeRec; + +typedef SInt64 LongDateTime; +union LongDateCvt { + SInt64 c; + struct { + UInt32 lHigh; + UInt32 lLow; + } hl; +}; +typedef union LongDateCvt LongDateCvt; +union LongDateRec { + struct { + short era; + short year; + short month; + short day; + short hour; + short minute; + short second; + short dayOfWeek; + short dayOfYear; + short weekOfYear; + short pm; + short res1; + short res2; + short res3; + } ld; + short list[14]; /*Index by LongDateField!*/ + struct { + short eraAlt; + DateTimeRec oldDate; + } od; +}; +typedef union LongDateRec LongDateRec; +#endif + + enum { + + o_era = 0, /* 0 = AD, -1 = BC */ + o_year = 2, + o_month = 4, /* 1-12 */ + o_day = 6, /* 1-31 */ + o_hour = 8, + o_minute = 10, + o_second = 12, + o_dayOfWeek = 14, /*1-7, sun-sat */ + o_dayOfYear = 16, /* 1-365 (366) */ + o_weekOfYear = 18, /* 1- 52 */ + o_pm = 20, /* 0 = am, 1 = pm */ + o_res1 = 22, + o_res2 = 24, + o_res3 = 26, + }; + + uint16_t StringToDate() + { + // StringToDateStatus StringToDate(Ptr textPtr, long textLen, DateCachePtr theCache, long *lengthUsed, LongDateRec *dateTime) + uint32_t sp; + uint32_t textPtr; + uint32_t textLen; + uint32_t theCache; + uint32_t lengthUsed; + uint32_t dateTime; + + uint16_t rv = 0; + uint32_t used = 0; + + struct tm tm = {}; + + sp = StackFrame<20>(textPtr, textLen, theCache, lengthUsed, dateTime); + std::string s = ToolBox::ReadString(textPtr, textLen); + + Log(" StringToDate(%s, %08lx)\n", s.c_str(), dateTime); + + rv = dateTimeNotFound; + if (s.length()) { + /* only allow %Y-%m-%d for now */ + char *cp; + cp = strptime(s.c_str(), " %Y-%m-%d", &tm); + if (!cp) cp = strptime(s.c_str(), "%m/%d/%Y", &tm); + + if (cp) { + rv = longDateFound; + used = cp - s.c_str(); + + memoryWriteWord(0, dateTime+o_era); /* o = AD, -1 = BC */ + memoryWriteWord(1900 + tm.tm_year, dateTime+o_year); + memoryWriteWord(1 + tm.tm_mon, dateTime+o_month); + memoryWriteWord(0 + tm.tm_mday, dateTime+o_day); + /* not populated */ + //memoryWriteWord(1 + tm.tm_wday, dateTime+o_dayOfWeek); + //memoryWriteWord(1 + tm.tm_yday, dateTime+o_dayOfYear); + // unsupported - week of year.... + } + } + + + memoryWriteLong(used, lengthUsed); + ToolReturn<2>(sp, rv); + return 0; + } + + uint16_t StringToTime() + { + // StringToDateStatus StringToTime(Ptr textPtr, long textLen, DateCachePtr theCache, long *lengthUsed, LongDateRec *dateTime) + uint32_t sp; + uint32_t textPtr; + uint32_t textLen; + uint32_t theCache; + uint32_t lengthUsed; + uint32_t dateTime; + + uint16_t rv = 0; + uint32_t used = 0; + + sp = StackFrame<20>(textPtr, textLen, theCache, lengthUsed, dateTime); + std::string s = ToolBox::ReadString(textPtr, textLen); + + Log(" StringToTime(%s, %08lx)\n", s.c_str(), dateTime); + + + rv = dateTimeNotFound; + + memoryWriteLong(used, lengthUsed); + ToolReturn<2>(sp, rv); + return 0; + } + uint16_t GetIntlResource() { // FUNCTION GetIntlResource (theID: Integer) :Handle; @@ -147,8 +652,14 @@ namespace Packages { return IUDateString(); case 0x0002: return IUTimeString(); + // case 0x0004: return IsMetric(); case 0x0006: return GetIntlResource(); + // case 0x0008: return SetIntlResource(); + case 0x000e: + return IUDatePString(); + case 0x0010: + return IUTimePString(); default: fprintf(stderr, "Pack6: selector %04x not supported\n", selector); @@ -158,4 +669,27 @@ namespace Packages { } + uint16_t ScriptUtil(uint16_t trap) + { + uint32_t selector; + StackFrame<4>(selector); + Log("%04x ScriptUtil(%08x)\n", trap, selector); + + switch(selector) + { + case 0x8204fff8: + return InitDateCache(); + case 0x8214fff6: + return StringToDate(); + case 0x8214fff4: + return StringToTime(); + default: + fprintf(stderr, "ScriptUtil: selector %08x not supported\n", selector); + exit(1); + } + + + + } + } diff --git a/toolbox/packages.h b/toolbox/packages.h index 92538a0..13f4483 100644 --- a/toolbox/packages.h +++ b/toolbox/packages.h @@ -22,6 +22,7 @@ namespace Packages { uint16_t Pack14(uint16_t); uint16_t Pack15(uint16_t); + uint16_t ScriptUtil(uint16_t); } #endif