1/* $NetBSD: loslib.c,v 1.13 2023/06/08 21:12:08 nikita Exp $ */ 2 3/* 4** Id: loslib.c 5** Standard Operating System library 6** See Copyright Notice in lua.h 7*/ 8 9#define loslib_c 10#define LUA_LIB 11 12#include "lprefix.h" 13 14 15#include <errno.h> 16#include <locale.h> 17#include <stdlib.h> 18#include <string.h> 19#include <time.h> 20 21#include "lua.h" 22 23#include "lauxlib.h" 24#include "lualib.h" 25 26 27/* 28** {================================================================== 29** List of valid conversion specifiers for the 'strftime' function; 30** options are grouped by length; group of length 2 start with '||'. 31** =================================================================== 32*/ 33#if !defined(LUA_STRFTIMEOPTIONS) /* { */ 34 35#if defined(LUA_USE_WINDOWS) 36#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ 37 "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ 38#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ 39#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" 40#else /* C99 specification */ 41#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ 42 "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ 43#endif 44 45#endif /* } */ 46/* }================================================================== */ 47 48 49/* 50** {================================================================== 51** Configuration for time-related stuff 52** =================================================================== 53*/ 54 55/* 56** type to represent time_t in Lua 57*/ 58#if !defined(LUA_NUMTIME) /* { */ 59 60#define l_timet lua_Integer 61#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) 62#define l_gettime(L,arg) luaL_checkinteger(L, arg) 63 64#else /* }{ */ 65 66#define l_timet lua_Number 67#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) 68#define l_gettime(L,arg) luaL_checknumber(L, arg) 69 70#endif /* } */ 71 72 73#if !defined(l_gmtime) /* { */ 74/* 75** By default, Lua uses gmtime/localtime, except when POSIX is available, 76** where it uses gmtime_r/localtime_r 77*/ 78 79#if defined(LUA_USE_POSIX) /* { */ 80 81#define l_gmtime(t,r) gmtime_r(t,r) 82#define l_localtime(t,r) localtime_r(t,r) 83 84#else /* }{ */ 85 86/* ISO C definitions */ 87#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) 88#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) 89 90#endif /* } */ 91 92#endif /* } */ 93 94/* }================================================================== */ 95 96 97/* 98** {================================================================== 99** Configuration for 'tmpnam': 100** By default, Lua uses tmpnam except when POSIX is available, where 101** it uses mkstemp. 102** =================================================================== 103*/ 104#if !defined(lua_tmpnam) /* { */ 105 106#if defined(LUA_USE_POSIX) /* { */ 107 108#include <unistd.h> 109 110#define LUA_TMPNAMBUFSIZE 32 111 112#if !defined(LUA_TMPNAMTEMPLATE) 113#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" 114#endif 115 116#define lua_tmpnam(b,e) { \ 117 strcpy(b, LUA_TMPNAMTEMPLATE); \ 118 e = mkstemp(b); \ 119 if (e != -1) close(e); \ 120 e = (e == -1); } 121 122#else /* }{ */ 123 124/* ISO C definitions */ 125#define LUA_TMPNAMBUFSIZE L_tmpnam 126#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } 127 128#endif /* } */ 129 130#endif /* } */ 131/* }================================================================== */ 132 133 134#if !defined(l_system) 135#if defined(LUA_USE_IOS) 136/* Despite claiming to be ISO C, iOS does not implement 'system'. */ 137#define l_system(cmd) ((cmd) == NULL ? 0 : -1) 138#else 139#define l_system(cmd) system(cmd) /* default definition */ 140#endif 141#endif 142 143 144static int os_execute (lua_State *L) { 145 const char *cmd = luaL_optstring(L, 1, NULL); 146 int stat; 147 errno = 0; 148 stat = l_system(cmd); 149 if (cmd != NULL) 150 return luaL_execresult(L, stat); 151 else { 152 lua_pushboolean(L, stat); /* true if there is a shell */ 153 return 1; 154 } 155} 156 157 158static int os_remove (lua_State *L) { 159 const char *filename = luaL_checkstring(L, 1); 160 return luaL_fileresult(L, remove(filename) == 0, filename); 161} 162 163 164static int os_rename (lua_State *L) { 165 const char *fromname = luaL_checkstring(L, 1); 166 const char *toname = luaL_checkstring(L, 2); 167 return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); 168} 169 170 171static int os_tmpname (lua_State *L) { 172 char buff[LUA_TMPNAMBUFSIZE]; 173 int err; 174 lua_tmpnam(buff, err); 175 if (l_unlikely(err)) 176 return luaL_error(L, "unable to generate a unique filename"); 177 lua_pushstring(L, buff); 178 return 1; 179} 180 181 182static int os_getenv (lua_State *L) { 183 lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ 184 return 1; 185} 186 187 188static int os_clock (lua_State *L) { 189 lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); 190 return 1; 191} 192 193 194/* 195** {====================================================== 196** Time/Date operations 197** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, 198** wday=%w+1, yday=%j, isdst=? } 199** ======================================================= 200*/ 201 202/* 203** About the overflow check: an overflow cannot occur when time 204** is represented by a lua_Integer, because either lua_Integer is 205** large enough to represent all int fields or it is not large enough 206** to represent a time that cause a field to overflow. However, if 207** times are represented as doubles and lua_Integer is int, then the 208** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 209** to compute the year. 210*/ 211static void setfield (lua_State *L, const char *key, int value, int delta) { 212 #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) 213 if (l_unlikely(value > LUA_MAXINTEGER - delta)) 214 luaL_error(L, "field '%s' is out-of-bound", key); 215 #endif 216 lua_pushinteger(L, (lua_Integer)value + delta); 217 lua_setfield(L, -2, key); 218} 219 220 221static void setboolfield (lua_State *L, const char *key, int value) { 222 if (value < 0) /* undefined? */ 223 return; /* does not set field */ 224 lua_pushboolean(L, value); 225 lua_setfield(L, -2, key); 226} 227 228 229/* 230** Set all fields from structure 'tm' in the table on top of the stack 231*/ 232static void setallfields (lua_State *L, struct tm *stm) { 233 setfield(L, "year", stm->tm_year, 1900); 234 setfield(L, "month", stm->tm_mon, 1); 235 setfield(L, "day", stm->tm_mday, 0); 236 setfield(L, "hour", stm->tm_hour, 0); 237 setfield(L, "min", stm->tm_min, 0); 238 setfield(L, "sec", stm->tm_sec, 0); 239 setfield(L, "yday", stm->tm_yday, 1); 240 setfield(L, "wday", stm->tm_wday, 1); 241 setboolfield(L, "isdst", stm->tm_isdst); 242} 243 244 245static int getboolfield (lua_State *L, const char *key) { 246 int res; 247 res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); 248 lua_pop(L, 1); 249 return res; 250} 251 252 253static int getfield (lua_State *L, const char *key, int d, int delta) { 254 int isnum; 255 int t = lua_getfield(L, -1, key); /* get field and its type */ 256 lua_Integer res = lua_tointegerx(L, -1, &isnum); 257 if (!isnum) { /* field is not an integer? */ 258 if (l_unlikely(t != LUA_TNIL)) /* some other value? */ 259 return luaL_error(L, "field '%s' is not an integer", key); 260 else if (l_unlikely(d < 0)) /* absent field; no default? */ 261 return luaL_error(L, "field '%s' missing in date table", key); 262 res = d; 263 } 264 else { 265 if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) 266 return luaL_error(L, "field '%s' is out-of-bound", key); 267 res -= delta; 268 } 269 lua_pop(L, 1); 270 return (int)res; 271} 272 273 274static const char *checkoption (lua_State *L, const char *conv, 275 ptrdiff_t convlen, char *buff) { 276 const char *option = LUA_STRFTIMEOPTIONS; 277 int oplen = 1; /* length of options being checked */ 278 for (; *option != '\0' && oplen <= convlen; option += oplen) { 279 if (*option == '|') /* next block? */ 280 oplen++; /* will check options with next length (+1) */ 281 else if (memcmp(conv, option, oplen) == 0) { /* match? */ 282 memcpy(buff, conv, oplen); /* copy valid option to buffer */ 283 buff[oplen] = '\0'; 284 return conv + oplen; /* return next item */ 285 } 286 } 287 luaL_argerror(L, 1, 288 lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); 289 return conv; /* to avoid warnings */ 290} 291 292 293static time_t l_checktime (lua_State *L, int arg) { 294 l_timet t = l_gettime(L, arg); 295 luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); 296 return (time_t)t; 297} 298 299 300/* maximum size for an individual 'strftime' item */ 301#define SIZETIMEFMT 250 302 303 304static int os_date (lua_State *L) { 305 size_t slen; 306 const char *s = luaL_optlstring(L, 1, "%c", &slen); 307 time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); 308 const char *se = s + slen; /* 's' end */ 309 struct tm tmr, *stm; 310 if (*s == '!') { /* UTC? */ 311 stm = l_gmtime(&t, &tmr); 312 s++; /* skip '!' */ 313 } 314 else 315 stm = l_localtime(&t, &tmr); 316 if (stm == NULL) /* invalid date? */ 317 return luaL_error(L, 318 "date result cannot be represented in this installation"); 319 if (strcmp(s, "*t") == 0) { 320 lua_createtable(L, 0, 9); /* 9 = number of fields */ 321 setallfields(L, stm); 322 } 323 else { 324 char cc[4]; /* buffer for individual conversion specifiers */ 325 luaL_Buffer b; 326 cc[0] = '%'; 327 luaL_buffinit(L, &b); 328 while (s < se) { 329 if (*s != '%') /* not a conversion specifier? */ 330 luaL_addchar(&b, *s++); 331 else { 332 size_t reslen; 333 char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); 334 s++; /* skip '%' */ 335 s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ 336 reslen = strftime(buff, SIZETIMEFMT, cc, stm); 337 luaL_addsize(&b, reslen); 338 } 339 } 340 luaL_pushresult(&b); 341 } 342 return 1; 343} 344 345 346static int os_time (lua_State *L) { 347 time_t t; 348 if (lua_isnoneornil(L, 1)) /* called without args? */ 349 t = time(NULL); /* get current time */ 350 else { 351 struct tm ts; 352 luaL_checktype(L, 1, LUA_TTABLE); 353 lua_settop(L, 1); /* make sure table is at the top */ 354 ts.tm_year = getfield(L, "year", -1, 1900); 355 ts.tm_mon = getfield(L, "month", -1, 1); 356 ts.tm_mday = getfield(L, "day", -1, 0); 357 ts.tm_hour = getfield(L, "hour", 12, 0); 358 ts.tm_min = getfield(L, "min", 0, 0); 359 ts.tm_sec = getfield(L, "sec", 0, 0); 360 ts.tm_isdst = getboolfield(L, "isdst"); 361 t = mktime(&ts); 362 setallfields(L, &ts); /* update fields with normalized values */ 363 } 364 if (t != (time_t)(l_timet)t || t == (time_t)(-1)) 365 return luaL_error(L, 366 "time result cannot be represented in this installation"); 367 l_pushtime(L, t); 368 return 1; 369} 370 371 372static int os_difftime (lua_State *L) { 373 time_t t1 = l_checktime(L, 1); 374 time_t t2 = l_checktime(L, 2); 375 lua_pushnumber(L, (lua_Number)difftime(t1, t2)); 376 return 1; 377} 378 379/* }====================================================== */ 380 381 382static int os_setlocale (lua_State *L) { 383 static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, 384 LC_NUMERIC, LC_TIME}; 385 static const char *const catnames[] = {"all", "collate", "ctype", "monetary", 386 "numeric", "time", NULL}; 387 const char *l = luaL_optstring(L, 1, NULL); 388 int op = luaL_checkoption(L, 2, "all", catnames); 389 lua_pushstring(L, setlocale(cat[op], l)); 390 return 1; 391} 392 393 394static int os_exit (lua_State *L) { 395 int status; 396 if (lua_isboolean(L, 1)) 397 status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); 398 else 399 status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); 400 if (lua_toboolean(L, 2)) 401 lua_close(L); 402 if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ 403 return 0; 404} 405 406 407static const luaL_Reg syslib[] = { 408 {"clock", os_clock}, 409 {"date", os_date}, 410 {"difftime", os_difftime}, 411 {"execute", os_execute}, 412 {"exit", os_exit}, 413 {"getenv", os_getenv}, 414 {"remove", os_remove}, 415 {"rename", os_rename}, 416 {"setlocale", os_setlocale}, 417 {"time", os_time}, 418 {"tmpname", os_tmpname}, 419 {NULL, NULL} 420}; 421 422/* }====================================================== */ 423 424 425 426LUAMOD_API int luaopen_os (lua_State *L) { 427 luaL_newlib(L, syslib); 428 return 1; 429} 430 431