diff options
| -rw-r--r-- | loslib.c | 76 | ||||
| -rw-r--r-- | testes/files.lua | 65 |
2 files changed, 100 insertions, 41 deletions
| @@ -58,18 +58,20 @@ | |||
| 58 | ** =================================================================== | 58 | ** =================================================================== |
| 59 | */ | 59 | */ |
| 60 | 60 | ||
| 61 | #if !defined(l_time_t) /* { */ | ||
| 62 | /* | 61 | /* |
| 63 | ** type to represent time_t in Lua | 62 | ** type to represent time_t in Lua |
| 64 | */ | 63 | */ |
| 64 | #if !defined(LUA_NUMTIME) /* { */ | ||
| 65 | |||
| 65 | #define l_timet lua_Integer | 66 | #define l_timet lua_Integer |
| 66 | #define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) | 67 | #define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) |
| 68 | #define l_gettime(L,arg) luaL_checkinteger(L, arg) | ||
| 67 | 69 | ||
| 68 | static time_t l_checktime (lua_State *L, int arg) { | 70 | #else /* }{ */ |
| 69 | lua_Integer t = luaL_checkinteger(L, arg); | 71 | |
| 70 | luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); | 72 | #define l_timet lua_Number |
| 71 | return (time_t)t; | 73 | #define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) |
| 72 | } | 74 | #define l_gettime(L,arg) luaL_checknumber(L, arg) |
| 73 | 75 | ||
| 74 | #endif /* } */ | 76 | #endif /* } */ |
| 75 | 77 | ||
| @@ -193,11 +195,25 @@ static int os_clock (lua_State *L) { | |||
| 193 | ** ======================================================= | 195 | ** ======================================================= |
| 194 | */ | 196 | */ |
| 195 | 197 | ||
| 196 | static void setfield (lua_State *L, const char *key, int value) { | 198 | /* |
| 197 | lua_pushinteger(L, value); | 199 | ** About the overflow check: an overflow cannot occurr when time |
| 200 | ** is represented by a lua_Integer, because either lua_Integer is | ||
| 201 | ** large enough to represent all int fields or it is not large enough | ||
| 202 | ** to represent a time that cause a field to overflow. However, if | ||
| 203 | ** times are represented as doubles and lua_Integer is int, then the | ||
| 204 | ** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 | ||
| 205 | ** to compute the year. | ||
| 206 | */ | ||
| 207 | static void setfield (lua_State *L, const char *key, int value, int delta) { | ||
| 208 | #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) | ||
| 209 | if (value > LUA_MAXINTEGER - delta) | ||
| 210 | luaL_error(L, "field '%s' is out-of-bound", key); | ||
| 211 | #endif | ||
| 212 | lua_pushinteger(L, (lua_Integer)value + delta); | ||
| 198 | lua_setfield(L, -2, key); | 213 | lua_setfield(L, -2, key); |
| 199 | } | 214 | } |
| 200 | 215 | ||
| 216 | |||
| 201 | static void setboolfield (lua_State *L, const char *key, int value) { | 217 | static void setboolfield (lua_State *L, const char *key, int value) { |
| 202 | if (value < 0) /* undefined? */ | 218 | if (value < 0) /* undefined? */ |
| 203 | return; /* does not set field */ | 219 | return; /* does not set field */ |
| @@ -210,14 +226,14 @@ static void setboolfield (lua_State *L, const char *key, int value) { | |||
| 210 | ** Set all fields from structure 'tm' in the table on top of the stack | 226 | ** Set all fields from structure 'tm' in the table on top of the stack |
| 211 | */ | 227 | */ |
| 212 | static void setallfields (lua_State *L, struct tm *stm) { | 228 | static void setallfields (lua_State *L, struct tm *stm) { |
| 213 | setfield(L, "sec", stm->tm_sec); | 229 | setfield(L, "year", stm->tm_year, 1900); |
| 214 | setfield(L, "min", stm->tm_min); | 230 | setfield(L, "month", stm->tm_mon, 1); |
| 215 | setfield(L, "hour", stm->tm_hour); | 231 | setfield(L, "day", stm->tm_mday, 0); |
| 216 | setfield(L, "day", stm->tm_mday); | 232 | setfield(L, "hour", stm->tm_hour, 0); |
| 217 | setfield(L, "month", stm->tm_mon + 1); | 233 | setfield(L, "min", stm->tm_min, 0); |
| 218 | setfield(L, "year", stm->tm_year + 1900); | 234 | setfield(L, "sec", stm->tm_sec, 0); |
| 219 | setfield(L, "wday", stm->tm_wday + 1); | 235 | setfield(L, "yday", stm->tm_yday, 1); |
| 220 | setfield(L, "yday", stm->tm_yday + 1); | 236 | setfield(L, "wday", stm->tm_wday, 1); |
| 221 | setboolfield(L, "isdst", stm->tm_isdst); | 237 | setboolfield(L, "isdst", stm->tm_isdst); |
| 222 | } | 238 | } |
| 223 | 239 | ||
| @@ -230,11 +246,6 @@ static int getboolfield (lua_State *L, const char *key) { | |||
| 230 | } | 246 | } |
| 231 | 247 | ||
| 232 | 248 | ||
| 233 | /* maximum value for date fields (to avoid arithmetic overflows with 'int') */ | ||
| 234 | #if !defined(L_MAXDATEFIELD) | ||
| 235 | #define L_MAXDATEFIELD (INT_MAX / 2) | ||
| 236 | #endif | ||
| 237 | |||
| 238 | static int getfield (lua_State *L, const char *key, int d, int delta) { | 249 | static int getfield (lua_State *L, const char *key, int d, int delta) { |
| 239 | int isnum; | 250 | int isnum; |
| 240 | int t = lua_getfield(L, -1, key); /* get field and its type */ | 251 | int t = lua_getfield(L, -1, key); /* get field and its type */ |
| @@ -247,7 +258,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { | |||
| 247 | res = d; | 258 | res = d; |
| 248 | } | 259 | } |
| 249 | else { | 260 | else { |
| 250 | if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) | 261 | /* unsigned avoids overflow when lua_Integer has 32 bits */ |
| 262 | if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta | ||
| 263 | : (lua_Integer)INT_MIN + delta <= res)) | ||
| 251 | return luaL_error(L, "field '%s' is out-of-bound", key); | 264 | return luaL_error(L, "field '%s' is out-of-bound", key); |
| 252 | res -= delta; | 265 | res -= delta; |
| 253 | } | 266 | } |
| @@ -275,6 +288,13 @@ static const char *checkoption (lua_State *L, const char *conv, | |||
| 275 | } | 288 | } |
| 276 | 289 | ||
| 277 | 290 | ||
| 291 | static time_t l_checktime (lua_State *L, int arg) { | ||
| 292 | l_timet t = l_gettime(L, arg); | ||
| 293 | luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); | ||
| 294 | return (time_t)t; | ||
| 295 | } | ||
| 296 | |||
| 297 | |||
| 278 | /* maximum size for an individual 'strftime' item */ | 298 | /* maximum size for an individual 'strftime' item */ |
| 279 | #define SIZETIMEFMT 250 | 299 | #define SIZETIMEFMT 250 |
| 280 | 300 | ||
| @@ -293,7 +313,7 @@ static int os_date (lua_State *L) { | |||
| 293 | stm = l_localtime(&t, &tmr); | 313 | stm = l_localtime(&t, &tmr); |
| 294 | if (stm == NULL) /* invalid date? */ | 314 | if (stm == NULL) /* invalid date? */ |
| 295 | return luaL_error(L, | 315 | return luaL_error(L, |
| 296 | "time result cannot be represented in this installation"); | 316 | "date result cannot be represented in this installation"); |
| 297 | if (strcmp(s, "*t") == 0) { | 317 | if (strcmp(s, "*t") == 0) { |
| 298 | lua_createtable(L, 0, 9); /* 9 = number of fields */ | 318 | lua_createtable(L, 0, 9); /* 9 = number of fields */ |
| 299 | setallfields(L, stm); | 319 | setallfields(L, stm); |
| @@ -329,12 +349,12 @@ static int os_time (lua_State *L) { | |||
| 329 | struct tm ts; | 349 | struct tm ts; |
| 330 | luaL_checktype(L, 1, LUA_TTABLE); | 350 | luaL_checktype(L, 1, LUA_TTABLE); |
| 331 | lua_settop(L, 1); /* make sure table is at the top */ | 351 | lua_settop(L, 1); /* make sure table is at the top */ |
| 332 | ts.tm_sec = getfield(L, "sec", 0, 0); | ||
| 333 | ts.tm_min = getfield(L, "min", 0, 0); | ||
| 334 | ts.tm_hour = getfield(L, "hour", 12, 0); | ||
| 335 | ts.tm_mday = getfield(L, "day", -1, 0); | ||
| 336 | ts.tm_mon = getfield(L, "month", -1, 1); | ||
| 337 | ts.tm_year = getfield(L, "year", -1, 1900); | 352 | ts.tm_year = getfield(L, "year", -1, 1900); |
| 353 | ts.tm_mon = getfield(L, "month", -1, 1); | ||
| 354 | ts.tm_mday = getfield(L, "day", -1, 0); | ||
| 355 | ts.tm_hour = getfield(L, "hour", 12, 0); | ||
| 356 | ts.tm_min = getfield(L, "min", 0, 0); | ||
| 357 | ts.tm_sec = getfield(L, "sec", 0, 0); | ||
| 338 | ts.tm_isdst = getboolfield(L, "isdst"); | 358 | ts.tm_isdst = getboolfield(L, "isdst"); |
| 339 | t = mktime(&ts); | 359 | t = mktime(&ts); |
| 340 | setallfields(L, &ts); /* update fields with normalized values */ | 360 | setallfields(L, &ts); /* update fields with normalized values */ |
diff --git a/testes/files.lua b/testes/files.lua index c8f23d18..6e7bd9e2 100644 --- a/testes/files.lua +++ b/testes/files.lua | |||
| @@ -775,11 +775,24 @@ assert(os.date(string.rep("%d", 1000), t) == | |||
| 775 | string.rep(os.date("%d", t), 1000)) | 775 | string.rep(os.date("%d", t), 1000)) |
| 776 | assert(os.date(string.rep("%", 200)) == string.rep("%", 100)) | 776 | assert(os.date(string.rep("%", 200)) == string.rep("%", 100)) |
| 777 | 777 | ||
| 778 | local t = os.time() | 778 | local function checkDateTable (t) |
| 779 | D = os.date("*t", t) | 779 | _G.D = os.date("*t", t) |
| 780 | load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and | 780 | assert(os.time(D) == t) |
| 781 | D.hour==%H and D.min==%M and D.sec==%S and | 781 | load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and |
| 782 | D.wday==%w+1 and D.yday==%j)]], t))() | 782 | D.hour==%H and D.min==%M and D.sec==%S and |
| 783 | D.wday==%w+1 and D.yday==%j)]], t))() | ||
| 784 | _G.D = nil | ||
| 785 | end | ||
| 786 | |||
| 787 | checkDateTable(os.time()) | ||
| 788 | if not _port then | ||
| 789 | -- assume that time_t can represent these values | ||
| 790 | checkDateTable(0) | ||
| 791 | checkDateTable(1) | ||
| 792 | checkDateTable(1000) | ||
| 793 | checkDateTable(0x7fffffff) | ||
| 794 | checkDateTable(0x80000000) | ||
| 795 | end | ||
| 783 | 796 | ||
| 784 | checkerr("invalid conversion specifier", os.date, "%") | 797 | checkerr("invalid conversion specifier", os.date, "%") |
| 785 | checkerr("invalid conversion specifier", os.date, "%9") | 798 | checkerr("invalid conversion specifier", os.date, "%9") |
| @@ -793,11 +806,24 @@ checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour=1.5}) | |||
| 793 | 806 | ||
| 794 | checkerr("missing", os.time, {hour = 12}) -- missing date | 807 | checkerr("missing", os.time, {hour = 12}) -- missing date |
| 795 | 808 | ||
| 809 | |||
| 810 | if string.packsize("i") == 4 then -- 4-byte ints | ||
| 811 | checkerr("field 'year' is out-of-bound", os.time, | ||
| 812 | {year = -(1 << 31) + 1899, month = 1, day = 1}) | ||
| 813 | end | ||
| 814 | |||
| 796 | if not _port then | 815 | if not _port then |
| 797 | -- test Posix-specific modifiers | 816 | -- test Posix-specific modifiers |
| 798 | assert(type(os.date("%Ex")) == 'string') | 817 | assert(type(os.date("%Ex")) == 'string') |
| 799 | assert(type(os.date("%Oy")) == 'string') | 818 | assert(type(os.date("%Oy")) == 'string') |
| 800 | 819 | ||
| 820 | -- test large dates (assume at least 4-byte ints and time_t) | ||
| 821 | local t0 = os.time{year = 1970, month = 1, day = 0} | ||
| 822 | local t1 = os.time{year = 1970, month = 1, day = 0, sec = (1 << 31) - 1} | ||
| 823 | assert(t1 - t0 == (1 << 31) - 1) | ||
| 824 | t0 = os.time{year = 1970, month = 1, day = 1} | ||
| 825 | t1 = os.time{year = 1970, month = 1, day = 1, sec = -(1 << 31)} | ||
| 826 | assert(t1 - t0 == -(1 << 31)) | ||
| 801 | 827 | ||
| 802 | -- test out-of-range dates (at least for Unix) | 828 | -- test out-of-range dates (at least for Unix) |
| 803 | if maxint >= 2^62 then -- cannot do these tests in Small Lua | 829 | if maxint >= 2^62 then -- cannot do these tests in Small Lua |
| @@ -812,25 +838,37 @@ if not _port then | |||
| 812 | -- time_t has 8 bytes; an int year cannot represent a huge time | 838 | -- time_t has 8 bytes; an int year cannot represent a huge time |
| 813 | print(" 8-byte time_t") | 839 | print(" 8-byte time_t") |
| 814 | checkerr("cannot be represented", os.date, "%Y", 2^60) | 840 | checkerr("cannot be represented", os.date, "%Y", 2^60) |
| 815 | -- it should have no problems with year 4000 | 841 | |
| 816 | assert(tonumber(os.time{year=4000, month=1, day=1})) | 842 | -- this is the maximum year |
| 843 | assert(tonumber(os.time | ||
| 844 | {year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=59})) | ||
| 845 | |||
| 846 | -- this is too much | ||
| 847 | checkerr("represented", os.time, | ||
| 848 | {year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=60}) | ||
| 817 | end | 849 | end |
| 850 | |||
| 851 | -- internal 'int' fields cannot hold these values | ||
| 852 | checkerr("field 'day' is out-of-bound", os.time, | ||
| 853 | {year = 0, month = 1, day = 2^32}) | ||
| 854 | |||
| 855 | checkerr("field 'month' is out-of-bound", os.time, | ||
| 856 | {year = 0, month = -((1 << 31) + 1), day = 1}) | ||
| 857 | |||
| 858 | checkerr("field 'year' is out-of-bound", os.time, | ||
| 859 | {year = (1 << 31) + 1900, month = 1, day = 1}) | ||
| 860 | |||
| 818 | else -- 8-byte ints | 861 | else -- 8-byte ints |
| 819 | -- assume time_t has 8 bytes too | 862 | -- assume time_t has 8 bytes too |
| 820 | print(" 8-byte time_t") | 863 | print(" 8-byte time_t") |
| 821 | assert(tonumber(os.date("%Y", 2^60))) | 864 | assert(tonumber(os.date("%Y", 2^60))) |
| 865 | |||
| 822 | -- but still cannot represent a huge year | 866 | -- but still cannot represent a huge year |
| 823 | checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1}) | 867 | checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1}) |
| 824 | end | 868 | end |
| 825 | end | 869 | end |
| 826 | end | 870 | end |
| 827 | 871 | ||
| 828 | |||
| 829 | D = os.date("!*t", t) | ||
| 830 | load(os.date([[!assert(D.year==%Y and D.month==%m and D.day==%d and | ||
| 831 | D.hour==%H and D.min==%M and D.sec==%S and | ||
| 832 | D.wday==%w+1 and D.yday==%j)]], t))() | ||
| 833 | |||
| 834 | do | 872 | do |
| 835 | local D = os.date("*t") | 873 | local D = os.date("*t") |
| 836 | local t = os.time(D) | 874 | local t = os.time(D) |
| @@ -844,6 +882,7 @@ do | |||
| 844 | assert(t == t1) -- if isdst is absent uses correct default | 882 | assert(t == t1) -- if isdst is absent uses correct default |
| 845 | end | 883 | end |
| 846 | 884 | ||
| 885 | local D = os.date("*t") | ||
| 847 | t = os.time(D) | 886 | t = os.time(D) |
| 848 | D.year = D.year-1; | 887 | D.year = D.year-1; |
| 849 | local t1 = os.time(D) | 888 | local t1 = os.time(D) |
