1171095Ssam/* $NetBSD: loslib.c,v 1.13 2023/06/08 21:12:08 nikita Exp $ */ 2171095Ssam 3171095Ssam/* 4171095Ssam** Id: loslib.c 5171095Ssam** Standard Operating System library 6171095Ssam** See Copyright Notice in lua.h 7171095Ssam*/ 8171095Ssam 9171095Ssam#define loslib_c 10171095Ssam#define LUA_LIB 11171095Ssam 12171095Ssam#include "lprefix.h" 13171095Ssam 14171095Ssam 15171095Ssam#include <errno.h> 16171095Ssam#include <locale.h> 17171095Ssam#include <stdlib.h> 18171095Ssam#include <string.h> 19171095Ssam#include <time.h> 20171095Ssam 21171095Ssam#include "lua.h" 22171095Ssam 23171095Ssam#include "lauxlib.h" 24171095Ssam#include "lualib.h" 25171095Ssam 26171095Ssam 27171095Ssam/* 28171095Ssam** {================================================================== 29171095Ssam** List of valid conversion specifiers for the 'strftime' function; 30171095Ssam** options are grouped by length; group of length 2 start with '||'. 31171095Ssam** =================================================================== 32171095Ssam*/ 33171095Ssam#if !defined(LUA_STRFTIMEOPTIONS) /* { */ 34171095Ssam 35171095Ssam#if defined(LUA_USE_WINDOWS) 36171095Ssam#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ 37171095Ssam "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ 38171095Ssam#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ 39173139Srwatson#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" 40171095Ssam#else /* C99 specification */ 41173139Srwatson#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ 42173139Srwatson "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ 43173139Srwatson#endif 44173139Srwatson 45173139Srwatson#endif /* } */ 46171095Ssam/* }================================================================== */ 47173139Srwatson 48173139Srwatson 49173139Srwatson/* 50173139Srwatson** {================================================================== 51173139Srwatson** Configuration for time-related stuff 52171095Ssam** =================================================================== 53173139Srwatson*/ 54171095Ssam 55171095Ssam/* 56171095Ssam** type to represent time_t in Lua 57171095Ssam*/ 58171095Ssam#if !defined(LUA_NUMTIME) /* { */ 59173139Srwatson 60171095Ssam#define l_timet lua_Integer 61173139Srwatson#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) 62171095Ssam#define l_gettime(L,arg) luaL_checkinteger(L, arg) 63173139Srwatson 64171095Ssam#else /* }{ */ 65173139Srwatson 66173139Srwatson#define l_timet lua_Number 67173139Srwatson#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) 68171095Ssam#define l_gettime(L,arg) luaL_checknumber(L, arg) 69173139Srwatson 70171095Ssam#endif /* } */ 71173139Srwatson 72173139Srwatson 73173139Srwatson#if !defined(l_gmtime) /* { */ 74173139Srwatson/* 75173139Srwatson** By default, Lua uses gmtime/localtime, except when POSIX is available, 76173139Srwatson** where it uses gmtime_r/localtime_r 77173139Srwatson*/ 78173139Srwatson 79173139Srwatson#if defined(LUA_USE_POSIX) /* { */ 80173139Srwatson 81173139Srwatson#define l_gmtime(t,r) gmtime_r(t,r) 82173139Srwatson#define l_localtime(t,r) localtime_r(t,r) 83171095Ssam 84173139Srwatson#else /* }{ */ 85171095Ssam 86171095Ssam/* ISO C definitions */ 87171095Ssam#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) 88171095Ssam#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) 89171095Ssam 90171095Ssam#endif /* } */ 91171095Ssam 92171095Ssam#endif /* } */ 93171095Ssam 94171095Ssam/* }================================================================== */ 95171095Ssam 96173139Srwatson 97173139Srwatson/* 98173139Srwatson** {================================================================== 99173139Srwatson** Configuration for 'tmpnam': 100171095Ssam** By default, Lua uses tmpnam except when POSIX is available, where 101171095Ssam** it uses mkstemp. 102171095Ssam** =================================================================== 103171095Ssam*/ 104171095Ssam#if !defined(lua_tmpnam) /* { */ 105171095Ssam 106171095Ssam#if defined(LUA_USE_POSIX) /* { */ 107171095Ssam 108171095Ssam#include <unistd.h> 109171095Ssam 110171095Ssam#define LUA_TMPNAMBUFSIZE 32 111171095Ssam 112171095Ssam#if !defined(LUA_TMPNAMTEMPLATE) 113171095Ssam#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" 114171095Ssam#endif 115173139Srwatson 116173139Srwatson#define lua_tmpnam(b,e) { \ 117173139Srwatson strcpy(b, LUA_TMPNAMTEMPLATE); \ 118173139Srwatson e = mkstemp(b); \ 119173139Srwatson if (e != -1) close(e); \ 120173139Srwatson e = (e == -1); } 121173139Srwatson 122173139Srwatson#else /* }{ */ 123171095Ssam 124171095Ssam/* ISO C definitions */ 125171095Ssam#define LUA_TMPNAMBUFSIZE L_tmpnam 126171095Ssam#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } 127171095Ssam 128171095Ssam#endif /* } */ 129171095Ssam 130171095Ssam#endif /* } */ 131171095Ssam/* }================================================================== */ 132173139Srwatson 133173139Srwatson 134171095Ssam#if !defined(l_system) 135173139Srwatson#if defined(LUA_USE_IOS) 136171095Ssam/* Despite claiming to be ISO C, iOS does not implement 'system'. */ 137173139Srwatson#define l_system(cmd) ((cmd) == NULL ? 0 : -1) 138171095Ssam#else 139171095Ssam#define l_system(cmd) system(cmd) /* default definition */ 140171095Ssam#endif 141171095Ssam#endif 142171095Ssam 143171095Ssam 144171095Ssamstatic int os_execute (lua_State *L) { 145171095Ssam const char *cmd = luaL_optstring(L, 1, NULL); 146171095Ssam int stat; 147171095Ssam errno = 0; 148171095Ssam stat = l_system(cmd); 149173139Srwatson if (cmd != NULL) 150173139Srwatson return luaL_execresult(L, stat); 151173139Srwatson else { 152173139Srwatson lua_pushboolean(L, stat); /* true if there is a shell */ 153173139Srwatson return 1; 154173139Srwatson } 155171095Ssam} 156171095Ssam 157171095Ssam 158173139Srwatsonstatic int os_remove (lua_State *L) { 159171095Ssam const char *filename = luaL_checkstring(L, 1); 160173139Srwatson return luaL_fileresult(L, remove(filename) == 0, filename); 161171095Ssam} 162173139Srwatson 163171095Ssam 164171095Ssamstatic int os_rename (lua_State *L) { 165171095Ssam const char *fromname = luaL_checkstring(L, 1); 166171095Ssam const char *toname = luaL_checkstring(L, 2); 167171095Ssam return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); 168171095Ssam} 169171095Ssam 170171095Ssam 171171095Ssamstatic int os_tmpname (lua_State *L) { 172171095Ssam char buff[LUA_TMPNAMBUFSIZE]; 173171095Ssam int err; 174171095Ssam lua_tmpnam(buff, err); 175171095Ssam if (l_unlikely(err)) 176171095Ssam return luaL_error(L, "unable to generate a unique filename"); 177171095Ssam lua_pushstring(L, buff); 178171095Ssam return 1; 179171095Ssam} 180171095Ssam 181171095Ssam 182173139Srwatsonstatic int os_getenv (lua_State *L) { 183173139Srwatson lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ 184173139Srwatson return 1; 185173139Srwatson} 186171095Ssam 187171095Ssam 188173139Srwatsonstatic int os_clock (lua_State *L) { 189171095Ssam lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); 190173139Srwatson return 1; 191171095Ssam} 192171095Ssam 193171095Ssam 194171095Ssam/* 195173139Srwatson** {====================================================== 196173139Srwatson** Time/Date operations 197173139Srwatson** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, 198173139Srwatson** wday=%w+1, yday=%j, isdst=? } 199173139Srwatson** ======================================================= 200173139Srwatson*/ 201171095Ssam 202171095Ssam/* 203171095Ssam** About the overflow check: an overflow cannot occur when time 204173139Srwatson** is represented by a lua_Integer, because either lua_Integer is 205171095Ssam** large enough to represent all int fields or it is not large enough 206173139Srwatson** to represent a time that cause a field to overflow. However, if 207171095Ssam** times are represented as doubles and lua_Integer is int, then the 208173139Srwatson** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 209171095Ssam** to compute the year. 210171095Ssam*/ 211171095Ssamstatic void setfield (lua_State *L, const char *key, int value, int delta) { 212171095Ssam #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) 213171095Ssam if (l_unlikely(value > LUA_MAXINTEGER - delta)) 214171095Ssam luaL_error(L, "field '%s' is out-of-bound", key); 215171095Ssam #endif 216171095Ssam lua_pushinteger(L, (lua_Integer)value + delta); 217173139Srwatson lua_setfield(L, -2, key); 218171095Ssam} 219173139Srwatson 220171095Ssam 221173139Srwatsonstatic void setboolfield (lua_State *L, const char *key, int value) { 222171095Ssam if (value < 0) /* undefined? */ 223173139Srwatson return; /* does not set field */ 224173139Srwatson lua_pushboolean(L, value); 225173139Srwatson lua_setfield(L, -2, key); 226173139Srwatson} 227173139Srwatson 228173139Srwatson 229173139Srwatson/* 230173139Srwatson** Set all fields from structure 'tm' in the table on top of the stack 231173139Srwatson*/ 232173139Srwatsonstatic void setallfields (lua_State *L, struct tm *stm) { 233171095Ssam setfield(L, "year", stm->tm_year, 1900); 234171095Ssam setfield(L, "month", stm->tm_mon, 1); 235171095Ssam setfield(L, "day", stm->tm_mday, 0); 236173139Srwatson setfield(L, "hour", stm->tm_hour, 0); 237173139Srwatson setfield(L, "min", stm->tm_min, 0); 238171095Ssam setfield(L, "sec", stm->tm_sec, 0); 239171095Ssam setfield(L, "yday", stm->tm_yday, 1); 240171095Ssam setfield(L, "wday", stm->tm_wday, 1); 241173139Srwatson setboolfield(L, "isdst", stm->tm_isdst); 242173139Srwatson} 243171095Ssam 244171095Ssam 245171095Ssamstatic int getboolfield (lua_State *L, const char *key) { 246171095Ssam int res; 247171095Ssam res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); 248171095Ssam lua_pop(L, 1); 249171095Ssam return res; 250171095Ssam} 251171095Ssam 252171095Ssam 253171095Ssamstatic int getfield (lua_State *L, const char *key, int d, int delta) { 254171095Ssam int isnum; 255171095Ssam int t = lua_getfield(L, -1, key); /* get field and its type */ 256171095Ssam lua_Integer res = lua_tointegerx(L, -1, &isnum); 257171095Ssam if (!isnum) { /* field is not an integer? */ 258171095Ssam if (l_unlikely(t != LUA_TNIL)) /* some other value? */ 259171095Ssam return luaL_error(L, "field '%s' is not an integer", key); 260171095Ssam else if (l_unlikely(d < 0)) /* absent field; no default? */ 261171095Ssam return luaL_error(L, "field '%s' missing in date table", key); 262171095Ssam res = d; 263171095Ssam } 264171095Ssam else { 265171095Ssam if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) 266171095Ssam return luaL_error(L, "field '%s' is out-of-bound", key); 267171095Ssam res -= delta; 268171095Ssam } 269173139Srwatson lua_pop(L, 1); 270173139Srwatson return (int)res; 271173139Srwatson} 272171095Ssam 273173139Srwatson 274171095Ssamstatic const char *checkoption (lua_State *L, const char *conv, 275171095Ssam ptrdiff_t convlen, char *buff) { 276173139Srwatson const char *option = LUA_STRFTIMEOPTIONS; 277171095Ssam int oplen = 1; /* length of options being checked */ 278171095Ssam for (; *option != '\0' && oplen <= convlen; option += oplen) { 279171095Ssam if (*option == '|') /* next block? */ 280171095Ssam oplen++; /* will check options with next length (+1) */ 281171095Ssam else if (memcmp(conv, option, oplen) == 0) { /* match? */ 282171095Ssam memcpy(buff, conv, oplen); /* copy valid option to buffer */ 283171095Ssam buff[oplen] = '\0'; 284171095Ssam return conv + oplen; /* return next item */ 285171095Ssam } 286171095Ssam } 287171095Ssam luaL_argerror(L, 1, 288171095Ssam lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); 289171095Ssam return conv; /* to avoid warnings */ 290171095Ssam} 291171095Ssam 292171095Ssam 293171095Ssamstatic time_t l_checktime (lua_State *L, int arg) { 294171095Ssam l_timet t = l_gettime(L, arg); 295171095Ssam luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); 296171095Ssam return (time_t)t; 297171095Ssam} 298171095Ssam 299171095Ssam 300171095Ssam/* maximum size for an individual 'strftime' item */ 301171095Ssam#define SIZETIMEFMT 250 302171095Ssam 303171095Ssam 304171095Ssamstatic int os_date (lua_State *L) { 305171095Ssam size_t slen; 306171095Ssam const char *s = luaL_optlstring(L, 1, "%c", &slen); 307171095Ssam time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); 308171095Ssam const char *se = s + slen; /* 's' end */ 309171095Ssam struct tm tmr, *stm; 310171095Ssam if (*s == '!') { /* UTC? */ 311171095Ssam stm = l_gmtime(&t, &tmr); 312173139Srwatson s++; /* skip '!' */ 313173139Srwatson } 314173139Srwatson else 315173139Srwatson stm = l_localtime(&t, &tmr); 316173139Srwatson if (stm == NULL) /* invalid date? */ 317173139Srwatson return luaL_error(L, 318173139Srwatson "date result cannot be represented in this installation"); 319173139Srwatson if (strcmp(s, "*t") == 0) { 320173139Srwatson lua_createtable(L, 0, 9); /* 9 = number of fields */ 321173139Srwatson setallfields(L, stm); 322173139Srwatson } 323171095Ssam else { 324171095Ssam char cc[4]; /* buffer for individual conversion specifiers */ 325171095Ssam luaL_Buffer b; 326171095Ssam cc[0] = '%'; 327171095Ssam luaL_buffinit(L, &b); 328171095Ssam while (s < se) { 329171095Ssam if (*s != '%') /* not a conversion specifier? */ 330171095Ssam luaL_addchar(&b, *s++); 331171095Ssam else { 332171095Ssam size_t reslen; 333171095Ssam char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); 334171095Ssam s++; /* skip '%' */ 335171095Ssam s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ 336171095Ssam reslen = strftime(buff, SIZETIMEFMT, cc, stm); 337171095Ssam luaL_addsize(&b, reslen); 338173139Srwatson } 339171095Ssam } 340171095Ssam luaL_pushresult(&b); 341171095Ssam } 342171095Ssam return 1; 343171095Ssam} 344171095Ssam 345171095Ssam 346171095Ssamstatic int os_time (lua_State *L) { 347171095Ssam time_t t; 348171095Ssam if (lua_isnoneornil(L, 1)) /* called without args? */ 349171095Ssam t = time(NULL); /* get current time */ 350173139Srwatson else { 351173139Srwatson struct tm ts; 352173139Srwatson luaL_checktype(L, 1, LUA_TTABLE); 353173139Srwatson lua_settop(L, 1); /* make sure table is at the top */ 354173139Srwatson ts.tm_year = getfield(L, "year", -1, 1900); 355173139Srwatson ts.tm_mon = getfield(L, "month", -1, 1); 356173139Srwatson ts.tm_mday = getfield(L, "day", -1, 0); 357173139Srwatson ts.tm_hour = getfield(L, "hour", 12, 0); 358173139Srwatson ts.tm_min = getfield(L, "min", 0, 0); 359173139Srwatson ts.tm_sec = getfield(L, "sec", 0, 0); 360171095Ssam ts.tm_isdst = getboolfield(L, "isdst"); 361171095Ssam t = mktime(&ts); 362171095Ssam setallfields(L, &ts); /* update fields with normalized values */ 363171095Ssam } 364171095Ssam if (t != (time_t)(l_timet)t || t == (time_t)(-1)) 365173139Srwatson return luaL_error(L, 366171095Ssam "time result cannot be represented in this installation"); 367171095Ssam l_pushtime(L, t); 368171095Ssam return 1; 369171095Ssam} 370171095Ssam 371171095Ssam 372171095Ssamstatic int os_difftime (lua_State *L) { 373171095Ssam time_t t1 = l_checktime(L, 1); 374171095Ssam time_t t2 = l_checktime(L, 2); 375171095Ssam lua_pushnumber(L, (lua_Number)difftime(t1, t2)); 376171095Ssam return 1; 377171095Ssam} 378173139Srwatson 379171095Ssam/* }====================================================== */ 380171095Ssam 381171095Ssam 382171095Ssamstatic int os_setlocale (lua_State *L) { 383171095Ssam static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, 384171095Ssam LC_NUMERIC, LC_TIME}; 385171095Ssam static const char *const catnames[] = {"all", "collate", "ctype", "monetary", 386171095Ssam "numeric", "time", NULL}; 387171095Ssam const char *l = luaL_optstring(L, 1, NULL); 388171095Ssam int op = luaL_checkoption(L, 2, "all", catnames); 389171095Ssam lua_pushstring(L, setlocale(cat[op], l)); 390171095Ssam return 1; 391171095Ssam} 392171095Ssam 393171095Ssam 394171095Ssamstatic int os_exit (lua_State *L) { 395173139Srwatson int status; 396171095Ssam if (lua_isboolean(L, 1)) 397171095Ssam status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); 398171095Ssam else 399171095Ssam status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); 400171095Ssam if (lua_toboolean(L, 2)) 401171095Ssam lua_close(L); 402171095Ssam if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ 403171095Ssam return 0; 404171095Ssam} 405171095Ssam 406171095Ssam 407171095Ssamstatic const luaL_Reg syslib[] = { 408171095Ssam {"clock", os_clock}, 409173139Srwatson {"date", os_date}, 410171095Ssam {"difftime", os_difftime}, 411171095Ssam {"execute", os_execute}, 412171095Ssam {"exit", os_exit}, 413173139Srwatson {"getenv", os_getenv}, 414171095Ssam {"remove", os_remove}, 415171095Ssam {"rename", os_rename}, 416171095Ssam {"setlocale", os_setlocale}, 417173139Srwatson {"time", os_time}, 418171095Ssam {"tmpname", os_tmpname}, 419171095Ssam {NULL, NULL} 420171095Ssam}; 421173139Srwatson 422171095Ssam/* }====================================================== */ 423171095Ssam 424171095Ssam 425173139Srwatson 426171095SsamLUAMOD_API int luaopen_os (lua_State *L) { 427171095Ssam luaL_newlib(L, syslib); 428171095Ssam return 1; 429173139Srwatson} 430171095Ssam 431171095Ssam