/* * Copyright (c) 2015, Kelvin W Sherlock * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "toolbox.h" #include "stackframe.h" #include "os.h" #include "packages.h" using ToolBox::Log; 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) std::string out; uint32_t dateTime; uint8_t flag; uint32_t result; StackFrame<10>(dateTime, flag, result); Log(" IUDateString(%08x, %02x, %08x)\n", dateTime, flag, result); 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() { // void IUTimeString(long dateTime,Boolean wantSeconds,Str255 result) // output: 12:00:00 AM or 12:00 AM std::string out; uint32_t dateTime; uint8_t wantSeconds; uint32_t result; StackFrame<10>(dateTime, wantSeconds, result); Log(" IUTimeString(%08x, %02x, %08x)\n", dateTime, wantSeconds, result); 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; // todo -- actually load the resource /* * theID Contains an integer (0, 1, 2, 4, or 5 respectively for * the 'itl0', 'itl1', 'itl2', 'itl4', and 'itl5'resources) to * identify the type of the desired international resource. */ uint16_t theID; uint32_t sp = StackFrame<2>(theID); Log(" GetIntlResource(%04x)\n", theID); ToolReturn<4>(sp, 0); return 0; // should set res error. } uint16_t Pack6(uint16_t trap) { uint16_t selector; StackFrame<2>(selector); Log("%04x Pack6(%04x)\n", trap, selector); switch(selector) { case 0x0000: 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); exit(1); } return 0; } 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); } } }