From 84194191c49e9aa2375633061343960f71d7555e Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 22 Apr 2018 23:29:03 +1000 Subject: src/loslib.js: Use our own strftime implementation; removes dependency --- NEWS | 1 + package.json | 1 - src/loslib.js | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 283 insertions(+), 21 deletions(-) diff --git a/NEWS b/NEWS index fbb983b..2e13a83 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ UNRELEASED - More node 6 compatibility fixes + - Use own strftime code (drops strftime npm package dependency) 0.1.1 - 2018-04-03 diff --git a/package.json b/package.json index 9988883..ca4c18f 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "dependencies": { "readline-sync": "^1.4.9", "sprintf-js": "^1.1.1", - "strftime": "^0.10.0", "tmp": "^0.0.33" }, "sideEffects": false, diff --git a/src/loslib.js b/src/loslib.js index 6240ed9..5768113 100644 --- a/src/loslib.js +++ b/src/loslib.js @@ -42,16 +42,13 @@ const { } = require('./lauxlib.js'); const { luastring_eq, - luastring_indexOf, to_jsstring, to_luastring } = require("./fengaricore.js"); -const strftime = require('strftime'); - /* options for ANSI C 89 (only 1-char options) */ -const L_STRFTIMEC89 = to_luastring("aAbBcdHIjmMpSUwWxXyYZ%"); -const LUA_STRFTIMEOPTIONS = L_STRFTIMEC89; +// const L_STRFTIMEC89 = to_luastring("aAbBcdHIjmMpSUwWxXyYZ%"); +// const LUA_STRFTIMEOPTIONS = L_STRFTIMEC89; /* options for ISO C 99 and POSIX */ // const L_STRFTIMEC99 = to_luastring("aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%||EcECExEXEyEYOdOeOHOIOmOMOSOuOUOVOwOWOy"); /* two-char options */ @@ -61,6 +58,18 @@ const LUA_STRFTIMEOPTIONS = L_STRFTIMEC89; // const L_STRFTIMEWIN = to_luastring("aAbBcdHIjmMpSUwWxXyYzZ%||#c#x#d#H#I#j#m#M#S#U#w#W#y#Y"); /* two-char options */ // const LUA_STRFTIMEOPTIONS = L_STRFTIMEWIN; +/* options for our own strftime implementation + - should be superset of C89 options for compat + - missing from C99: + - ISO 8601 week specifiers: gGV + - > single char specifiers + - beyond C99: + - %k: TZ extension: space-padded 24-hour + - %l: TZ extension: space-padded 12-hour + - %P: GNU extension: lower-case am/pm +*/ +const LUA_STRFTIMEOPTIONS = to_luastring("aAbBcCdDeFhHIjklmMnpPrRStTuUwWxXyYzZ%"); + const setfield = function(L, key, value) { lua_pushinteger(L, value); @@ -100,7 +109,272 @@ const getfield = function(L, key, d, delta) { return res; }; -const checkoption = function(L, conv, i, buff) { + +const locale = { + days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ].map((s) => to_luastring(s)), + shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((s) => to_luastring(s)), + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"].map((s) => to_luastring(s)), + shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].map((s) => to_luastring(s)), + AM: to_luastring("AM"), + PM: to_luastring("PM"), + am: to_luastring("am"), + pm: to_luastring("pm"), + formats: { + c: to_luastring("%a %b %e %H:%M:%S %Y"), + D: to_luastring("%m/%d/%y"), + F: to_luastring("%Y-%m-%d"), + R: to_luastring("%H:%M"), + r: to_luastring("%I:%M:%S %p"), + T: to_luastring("%H:%M:%S"), + X: to_luastring("%T"), + x: to_luastring("%D") + } +}; + +const week_number = function(date, start_of_week) { + // This works by shifting the weekday back by one day if we + // are treating Monday as the first day of the week. + let weekday = date.getDay(); + if (start_of_week === 'monday') { + if (weekday === 0) // Sunday + weekday = 6; + else + weekday--; + } + let yday = (date - new Date(date.getFullYear(), 0, 1)) / 86400000; + return Math.floor((yday + 7 - weekday) / 7); +}; + +const push_pad_2 = function(b, n, pad) { + if (n < 10) + luaL_addchar(b, pad); + luaL_addstring(b, to_luastring(String(n))); +}; + +const strftime = function(L, b, s, date) { + let i = 0; + while (i < s.length) { + if (s[i] !== 37 /* % */) { /* not a conversion specifier? */ + luaL_addchar(b, s[i++]); + } else { + i++; /* skip '%' */ + let len = checkoption(L, s, i); + /* each `case` has an example output above it for the UTC epoch */ + switch(s[i]) { + // '%' + case 37 /* % */: + luaL_addchar(b, 37); + break; + + // 'Thursday' + case 65 /* A */: + luaL_addstring(b, locale.days[date.getDay()]); + break; + + // 'January' + case 66 /* B */: + luaL_addstring(b, locale.months[date.getMonth()]); + break; + + // '19' + case 67 /* C */: + push_pad_2(b, Math.floor(date.getFullYear() / 100), 48 /* 0 */); + break; + + // '01/01/70' + case 68 /* D */: + strftime(L, b, locale.formats.D, date); + break; + + // '1970-01-01' + case 70 /* F */: + strftime(L, b, locale.formats.F, date); + break; + + // '00' + case 72 /* H */: + push_pad_2(b, date.getHours(), 48 /* 0 */); + break; + + // '12' + case 73 /* I */: + push_pad_2(b, (date.getHours() + 11) % 12 + 1, 48 /* 0 */); + break; + + // '00' + case 77 /* M */: + push_pad_2(b, date.getMinutes(), 48 /* 0 */); + break; + + // 'am' + case 80 /* P */: + luaL_addstring(b, date.getHours() < 12 ? locale.am : locale.pm); + break; + + // '00:00' + case 82 /* R */: + strftime(L, b, locale.formats.R, date); + break; + + // '00' + case 83 /* S */: + push_pad_2(b, date.getSeconds(), 48 /* 0 */); + break; + + // '00:00:00' + case 84 /* T */: + strftime(L, b, locale.formats.T, date); + break; + + // '00' + case 85 /* U */: + push_pad_2(b, week_number(date, "sunday"), 48 /* 0 */); + break; + + // '00' + case 87 /* W */: + push_pad_2(b, week_number(date, "monday"), 48 /* 0 */); + break; + + // '16:00:00' + case 88 /* X */: + strftime(L, b, locale.formats.X, date); + break; + + // '1970' + case 89 /* Y */: + luaL_addstring(b, to_luastring(String(date.getFullYear()))); + break; + + // 'GMT' + case 90 /* Z */: { + let tzString = date.toString().match(/\(([\w\s]+)\)/); + if (tzString) + luaL_addstring(b, to_luastring(tzString[1])); + break; + } + + // 'Thu' + case 97 /* a */: + luaL_addstring(b, locale.shortDays[date.getDay()]); + break; + + // 'Jan' + case 98 /* b */: + case 104 /* h */: + luaL_addstring(b, locale.shortMonths[date.getMonth()]); + break; + + // '' + case 99 /* c */: + strftime(L, b, locale.formats.c, date); + break; + + // '01' + case 100 /* d */: + push_pad_2(b, date.getDate(), 48 /* 0 */); + break; + + // ' 1' + case 101 /* e */: + push_pad_2(b, date.getDate(), 32 /* space */); + break; + + // '000' + case 106 /* j */: { + let yday = Math.floor((date - new Date(date.getFullYear(), 0, 1)) / 86400000); + if (yday < 100) { + if (yday < 10) + luaL_addchar(b, 48 /* 0 */); + luaL_addchar(b, 48 /* 0 */); + } + luaL_addstring(b, to_luastring(String(yday))); + break; + } + + // ' 0' + case 107 /* k */: + push_pad_2(b, date.getHours(), 32 /* space */); + break; + + // '12' + case 108 /* l */: + push_pad_2(b, (date.getHours() + 11) % 12 + 1, 32 /* space */); + break; + + // '01' + case 109 /* m */: + push_pad_2(b, date.getMonth() + 1, 48 /* 0 */); + break; + + // '\n' + case 110 /* n */: + luaL_addchar(b, 10); + break; + + // 'AM' + case 112 /* p */: + luaL_addstring(b, date.getHours() < 12 ? locale.AM : locale.PM); + break; + + // '12:00:00 AM' + case 114 /* r */: + strftime(L, b, locale.formats.r, date); + break; + + // '0' + case 115 /* s */: + luaL_addstring(b, to_luastring(String(Math.floor(date / 1000)))); + break; + + // '\t' + case 116 /* t */: + luaL_addchar(b, 8); + break; + + // '4' + case 117 /* u */: { + let day = date.getDay(); + luaL_addstring(b, to_luastring(String(day === 0 ? 7 : day))); + break; + } + + // '4' + case 119 /* w */: + luaL_addstring(b, to_luastring(String(date.getDay()))); + break; + + // '12/31/69' + case 120 /* x */: + strftime(L, b, locale.formats.x, date); + break; + + // '70' + case 121 /* y */: + push_pad_2(b, date.getFullYear() % 100, 48 /* 0 */); + break; + + // '+0000' + case 122 /* z */: { + let off = date.getTimezoneOffset(); + if (off > 0) { + luaL_addchar(b, 45 /* - */); + } else { + off = -off; + luaL_addchar(b, 43 /* + */); + } + push_pad_2(b, Math.floor(off/60), 48 /* 0 */); + push_pad_2(b, off % 60, 48 /* 0 */); + break; + } + } + i += len; + } + } +}; + + +const checkoption = function(L, conv, i) { let option = LUA_STRFTIMEOPTIONS; let o = 0; let oplen = 1; /* length of options being checked */ @@ -108,8 +382,7 @@ const checkoption = function(L, conv, i, buff) { if (option[o] === '|'.charCodeAt(0)) /* next block? */ oplen++; /* will check options with next length (+1) */ else if (luastring_eq(conv.subarray(i, i+oplen), option.subarray(o, o+oplen))) { /* match? */ - buff.set(conv.subarray(i, i+oplen)); /* copy valid option to buffer */ - return i + oplen; /* return next item */ + return oplen; /* return length */ } } luaL_argerror(L, 1, @@ -137,18 +410,7 @@ const os_date = function(L) { cc[0] = "%".charCodeAt(0); let b = new luaL_Buffer(); luaL_buffinit(L, b); - while (i < s.length) { - if (s[i] !== '%'.charCodeAt(0)) { /* not a conversion specifier? */ - luaL_addchar(b, s[i++]); - } else { - i++; /* skip '%' */ - i = checkoption(L, s, i, cc.subarray(1)); /* copy specifier to 'cc' */ - let len = luastring_indexOf(cc, 0); - if (len === -1) len = void 0; - let buff = strftime(to_jsstring(cc, 0, len), stm); - luaL_addstring(b, to_luastring(buff)); - } - } + strftime(L, b, s, stm); luaL_pushresult(b); } return 1; -- cgit v1.2.3-54-g00ecf