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