xref: /netbsd-src/external/mit/lua/dist/src/loslib.c (revision a24efa7dea9f1f56c3bdb15a927d3516792ace1c)
1 /*	$NetBSD: loslib.c,v 1.5 2016/01/28 14:41:39 lneto Exp $	*/
2 
3 /*
4 ** Id: loslib.c,v 1.60 2015/11/19 19:16:22 roberto Exp
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 ** ===================================================================
31 */
32 #if !defined(LUA_STRFTIMEOPTIONS)	/* { */
33 
34 #if defined(LUA_USE_C89)
35 #define LUA_STRFTIMEOPTIONS	{ "aAbBcdHIjmMpSUwWxXyYz%", "" }
36 #else  /* C99 specification */
37 #define LUA_STRFTIMEOPTIONS \
38 	{ "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "", \
39 	  "E", "cCxXyY",  \
40 	  "O", "deHImMSuUVwWy" }
41 #endif
42 
43 #endif					/* } */
44 /* }================================================================== */
45 
46 
47 /*
48 ** {==================================================================
49 ** Configuration for time-related stuff
50 ** ===================================================================
51 */
52 
53 #if !defined(l_time_t)		/* { */
54 /*
55 ** type to represent time_t in Lua
56 */
57 #define l_timet			lua_Integer
58 #define l_pushtime(L,t)		lua_pushinteger(L,(lua_Integer)(t))
59 
60 static time_t l_checktime (lua_State *L, int arg) {
61   lua_Integer t = luaL_checkinteger(L, arg);
62   luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
63   return (time_t)t;
64 }
65 
66 #endif				/* } */
67 
68 
69 #if !defined(l_gmtime)		/* { */
70 /*
71 ** By default, Lua uses gmtime/localtime, except when POSIX is available,
72 ** where it uses gmtime_r/localtime_r
73 */
74 
75 #if defined(LUA_USE_POSIX)	/* { */
76 
77 #define l_gmtime(t,r)		gmtime_r(t,r)
78 #define l_localtime(t,r)	localtime_r(t,r)
79 
80 #else				/* }{ */
81 
82 /* ISO C definitions */
83 #define l_gmtime(t,r)		((void)(r)->tm_sec, gmtime(t))
84 #define l_localtime(t,r)  	((void)(r)->tm_sec, localtime(t))
85 
86 #endif				/* } */
87 
88 #endif				/* } */
89 
90 /* }================================================================== */
91 
92 
93 /*
94 ** {==================================================================
95 ** Configuration for 'tmpnam':
96 ** By default, Lua uses tmpnam except when POSIX is available, where
97 ** it uses mkstemp.
98 ** ===================================================================
99 */
100 #if !defined(lua_tmpnam)	/* { */
101 
102 #if defined(LUA_USE_POSIX)	/* { */
103 
104 #include <unistd.h>
105 
106 #define LUA_TMPNAMBUFSIZE	32
107 
108 #if !defined(LUA_TMPNAMTEMPLATE)
109 #define LUA_TMPNAMTEMPLATE	"/tmp/lua_XXXXXX"
110 #endif
111 
112 #define lua_tmpnam(b,e) { \
113         strcpy(b, LUA_TMPNAMTEMPLATE); \
114         e = mkstemp(b); \
115         if (e != -1) close(e); \
116         e = (e == -1); }
117 
118 #else				/* }{ */
119 
120 /* ISO C definitions */
121 #define LUA_TMPNAMBUFSIZE	L_tmpnam
122 #define lua_tmpnam(b,e)		{ e = (tmpnam(b) == NULL); }
123 
124 #endif				/* } */
125 
126 #endif				/* } */
127 /* }================================================================== */
128 
129 
130 
131 
132 static int os_execute (lua_State *L) {
133   const char *cmd = luaL_optstring(L, 1, NULL);
134   int stat = system(cmd);
135   if (cmd != NULL)
136     return luaL_execresult(L, stat);
137   else {
138     lua_pushboolean(L, stat);  /* true if there is a shell */
139     return 1;
140   }
141 }
142 
143 
144 static int os_remove (lua_State *L) {
145   const char *filename = luaL_checkstring(L, 1);
146   return luaL_fileresult(L, remove(filename) == 0, filename);
147 }
148 
149 
150 static int os_rename (lua_State *L) {
151   const char *fromname = luaL_checkstring(L, 1);
152   const char *toname = luaL_checkstring(L, 2);
153   return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
154 }
155 
156 
157 static int os_tmpname (lua_State *L) {
158   char buff[LUA_TMPNAMBUFSIZE];
159   int err;
160   lua_tmpnam(buff, err);
161   if (err)
162     return luaL_error(L, "unable to generate a unique filename");
163   lua_pushstring(L, buff);
164   return 1;
165 }
166 
167 
168 static int os_getenv (lua_State *L) {
169   lua_pushstring(L, getenv(luaL_checkstring(L, 1)));  /* if NULL push nil */
170   return 1;
171 }
172 
173 
174 static int os_clock (lua_State *L) {
175   lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
176   return 1;
177 }
178 
179 
180 /*
181 ** {======================================================
182 ** Time/Date operations
183 ** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
184 **   wday=%w+1, yday=%j, isdst=? }
185 ** =======================================================
186 */
187 
188 static void setfield (lua_State *L, const char *key, int value) {
189   lua_pushinteger(L, value);
190   lua_setfield(L, -2, key);
191 }
192 
193 static void setboolfield (lua_State *L, const char *key, int value) {
194   if (value < 0)  /* undefined? */
195     return;  /* does not set field */
196   lua_pushboolean(L, value);
197   lua_setfield(L, -2, key);
198 }
199 
200 static int getboolfield (lua_State *L, const char *key) {
201   int res;
202   res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1);
203   lua_pop(L, 1);
204   return res;
205 }
206 
207 
208 /* maximum value for date fields (to avoid arithmetic overflows with 'int') */
209 #if !defined(L_MAXDATEFIELD)
210 #define L_MAXDATEFIELD	(INT_MAX / 2)
211 #endif
212 
213 static int getfield (lua_State *L, const char *key, int d, int delta) {
214   int isnum;
215   int t = lua_getfield(L, -1, key);
216   lua_Integer res = lua_tointegerx(L, -1, &isnum);
217   if (!isnum) {  /* field is not a number? */
218     if (t != LUA_TNIL)  /* some other value? */
219       return luaL_error(L, "field '%s' not an integer", key);
220     else if (d < 0)  /* absent field; no default? */
221       return luaL_error(L, "field '%s' missing in date table", key);
222     res = d;
223   }
224   else {
225     if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD))
226       return luaL_error(L, "field '%s' out-of-bounds", key);
227     res -= delta;
228   }
229   lua_pop(L, 1);
230   return (int)res;
231 }
232 
233 
234 static const char *checkoption (lua_State *L, const char *conv, char *buff) {
235   static const char *const options[] = LUA_STRFTIMEOPTIONS;
236   unsigned int i;
237   for (i = 0; i < sizeof(options)/sizeof(options[0]); i += 2) {
238     if (*conv != '\0' && strchr(options[i], *conv) != NULL) {
239       buff[1] = *conv;
240       if (*options[i + 1] == '\0') {  /* one-char conversion specifier? */
241         buff[2] = '\0';  /* end buffer */
242         return conv + 1;
243       }
244       else if (*(conv + 1) != '\0' &&
245                strchr(options[i + 1], *(conv + 1)) != NULL) {
246         buff[2] = *(conv + 1);  /* valid two-char conversion specifier */
247         buff[3] = '\0';  /* end buffer */
248         return conv + 2;
249       }
250     }
251   }
252   luaL_argerror(L, 1,
253     lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv));
254   return conv;  /* to avoid warnings */
255 }
256 
257 
258 /* maximum size for an individual 'strftime' item */
259 #define SIZETIMEFMT	250
260 
261 
262 static int os_date (lua_State *L) {
263   const char *s = luaL_optstring(L, 1, "%c");
264   time_t t = luaL_opt(L, l_checktime, 2, time(NULL));
265   struct tm tmr, *stm;
266   if (*s == '!') {  /* UTC? */
267     stm = l_gmtime(&t, &tmr);
268     s++;  /* skip '!' */
269   }
270   else
271     stm = l_localtime(&t, &tmr);
272   if (stm == NULL)  /* invalid date? */
273     luaL_error(L, "time result cannot be represented in this installation");
274   if (strcmp(s, "*t") == 0) {
275     lua_createtable(L, 0, 9);  /* 9 = number of fields */
276     setfield(L, "sec", stm->tm_sec);
277     setfield(L, "min", stm->tm_min);
278     setfield(L, "hour", stm->tm_hour);
279     setfield(L, "day", stm->tm_mday);
280     setfield(L, "month", stm->tm_mon+1);
281     setfield(L, "year", stm->tm_year+1900);
282     setfield(L, "wday", stm->tm_wday+1);
283     setfield(L, "yday", stm->tm_yday+1);
284     setboolfield(L, "isdst", stm->tm_isdst);
285   }
286   else {
287     char cc[4];
288     luaL_Buffer b;
289     cc[0] = '%';
290     luaL_buffinit(L, &b);
291     while (*s) {
292       if (*s != '%')  /* not a conversion specifier? */
293         luaL_addchar(&b, *s++);
294       else {
295         size_t reslen;
296         char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
297         s = checkoption(L, s + 1, cc);
298         reslen = strftime(buff, SIZETIMEFMT, cc, stm);
299         luaL_addsize(&b, reslen);
300       }
301     }
302     luaL_pushresult(&b);
303   }
304   return 1;
305 }
306 
307 
308 static int os_time (lua_State *L) {
309   time_t t;
310   if (lua_isnoneornil(L, 1))  /* called without args? */
311     t = time(NULL);  /* get current time */
312   else {
313     struct tm ts;
314     luaL_checktype(L, 1, LUA_TTABLE);
315     lua_settop(L, 1);  /* make sure table is at the top */
316     ts.tm_sec = getfield(L, "sec", 0, 0);
317     ts.tm_min = getfield(L, "min", 0, 0);
318     ts.tm_hour = getfield(L, "hour", 12, 0);
319     ts.tm_mday = getfield(L, "day", -1, 0);
320     ts.tm_mon = getfield(L, "month", -1, 1);
321     ts.tm_year = getfield(L, "year", -1, 1900);
322     ts.tm_isdst = getboolfield(L, "isdst");
323     t = mktime(&ts);
324   }
325   if (t != (time_t)(l_timet)t || t == (time_t)(-1))
326     luaL_error(L, "time result cannot be represented in this installation");
327   l_pushtime(L, t);
328   return 1;
329 }
330 
331 
332 static int os_difftime (lua_State *L) {
333   time_t t1 = l_checktime(L, 1);
334   time_t t2 = l_checktime(L, 2);
335   lua_pushnumber(L, (lua_Number)difftime(t1, t2));
336   return 1;
337 }
338 
339 /* }====================================================== */
340 
341 
342 static int os_setlocale (lua_State *L) {
343   static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
344                       LC_NUMERIC, LC_TIME};
345   static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
346      "numeric", "time", NULL};
347   const char *l = luaL_optstring(L, 1, NULL);
348   int op = luaL_checkoption(L, 2, "all", catnames);
349   lua_pushstring(L, setlocale(cat[op], l));
350   return 1;
351 }
352 
353 
354 static int os_exit (lua_State *L) {
355   int status;
356   if (lua_isboolean(L, 1))
357     status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE);
358   else
359     status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS);
360   if (lua_toboolean(L, 2))
361     lua_close(L);
362   if (L) exit(status);  /* 'if' to avoid warnings for unreachable 'return' */
363   return 0;
364 }
365 
366 
367 static const luaL_Reg syslib[] = {
368   {"clock",     os_clock},
369   {"date",      os_date},
370   {"difftime",  os_difftime},
371   {"execute",   os_execute},
372   {"exit",      os_exit},
373   {"getenv",    os_getenv},
374   {"remove",    os_remove},
375   {"rename",    os_rename},
376   {"setlocale", os_setlocale},
377   {"time",      os_time},
378   {"tmpname",   os_tmpname},
379   {NULL, NULL}
380 };
381 
382 /* }====================================================== */
383 
384 
385 
386 LUAMOD_API int luaopen_os (lua_State *L) {
387   luaL_newlib(L, syslib);
388   return 1;
389 }
390 
391