beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / lua / loslibext.c
blob76dd527c0bfb8182570d741f42f560cd16e01978
1 /* loslibext.c
3 Copyright 2006-2012 Taco Hoekwater <taco@luatex.org>
5 This file is part of LuaTeX.
7 LuaTeX is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
12 LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU General Public License along
18 with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
20 #include "ptexlib.h"
21 #include "lua/luatex-api.h"
23 #include <kpathsea/c-stat.h>
24 #include <kpathsea/c-dir.h>
25 #include <time.h>
28 #if defined(_WIN32) || defined(__NT__)
29 # define MKDIR(a,b) mkdir(a)
30 #else
31 # define MKDIR(a,b) mkdir(a,b)
32 #endif
34 /* An attempt to figure out the basic platform, does not
35 care about niceties like version numbers yet,
36 and ignores platforms where luatex is unlikely to
37 successfully compile without major prorting effort
38 (amiga|mac|os2|vms) */
40 #if defined(_WIN32) || defined(__NT__)
41 # define OS_PLATTYPE "windows"
42 # define OS_PLATNAME "windows"
43 #elif defined(__GO32__) || defined(__DJGPP__) || defined(__DOS__)
44 # define OS_PLATTYPE "msdos"
45 # define OS_PLATNAME "msdos"
46 #else /* assume everything else is unix-y */
47 # include <sys/utsname.h>
48 # define OS_PLATTYPE "unix"
49 /* this is just a first guess */
50 # if defined(__BSD__)
51 # define OS_PLATNAME "bsd"
52 # elif defined(__CYGWIN__)
53 # define OS_PLATNAME "cygwin"
54 # elif defined(__SYSV__)
55 # define OS_PLATNAME "sysv"
56 # else
57 # define OS_PLATNAME "generic"
58 # endif
59 /* attempt to be more precise */
60 # if defined(__LINUX__) || defined (__linux)
61 # undef OS_PLATNAME
62 # define OS_PLATNAME "linux"
63 # elif defined(__FREEBSD__) || defined(__FreeBSD__) || defined(__FreeBSD)
64 # undef OS_PLATNAME
65 # define OS_PLATNAME "freebsd"
66 # elif defined(__FreeBSD_kernel__)
67 # undef OS_PLATNAME
68 # define OS_PLATNAME "kfreebsd"
69 # elif defined(__OPENBSD__) || defined(__OpenBSD)
70 # undef OS_PLATNAME
71 # define OS_PLATNAME "openbsd"
72 # elif defined(__SOLARIS__)
73 # undef OS_PLATNAME
74 # define OS_PLATNAME "solaris"
75 # elif defined(__SUNOS__) || defined(__SUN__) || defined(sun)
76 # undef OS_PLATNAME
77 # define OS_PLATNAME "sunos"
78 # elif defined(HPUX) || defined(__hpux)
79 # undef OS_PLATNAME
80 # define OS_PLATNAME "hpux"
81 # elif defined(__sgi)
82 # undef OS_PLATNAME
83 # define OS_PLATNAME "irix"
84 # elif defined(__MACH__) && defined(__APPLE__)
85 # undef OS_PLATNAME
86 # define OS_PLATNAME "macosx"
87 # elif defined(__GNU__)
88 # undef OS_PLATNAME
89 # define OS_PLATNAME "gnu"
90 # endif
91 #endif
97 /* there could be more platforms that don't have these two,
98 * but win32 and sunos are for sure.
99 * gettimeofday() for win32 is using an alternative definition
102 #if (! defined(_WIN32)) && (! defined(__SUNOS__))
103 # include <sys/time.h> /* gettimeofday() */
104 # include <sys/times.h> /* times() */
105 # include <sys/wait.h>
106 #endif
108 /* set this to one for spawn instead of exec on windows */
110 #define DONT_REALLY_EXIT 1
112 /* Note: under WIN32, |environ| is nothing but a copy of the actual
113 environment as it was during program startup. That variable
114 can then be changed (which is a little odd), but such changes
115 do not survive in the actual environment *unless* they are
116 passed on down in e.g. |_execvpe|, when they would be inherited
117 by the child process.
119 This gives trouble because some parts of the texk code actually
120 change the contents of the |environ| variable as a side-effect
121 of other processing, and that explains why the current code does
122 not use |environ| at all for the moment.
124 The API is kept in place for now, just in case. Thanks to Tomek
125 for the patch and the persistence in tracking this down.
128 #ifdef _WIN32
129 # include <process.h>
130 # define spawn_command(a,b,c) c ? \
131 _spawnvpe(_P_WAIT,(const char *)a,(const char* const*)b,(const char* const*)c) : \
132 _spawnvp(_P_WAIT,(const char *)a,(const char* const*)b)
133 # if DONT_REALLY_EXIT
134 # define exec_command(a,b,c) exit(spawn_command((a),(b),(c)))
135 # else
136 # define exec_command(a,b,c) c ? \
137 _execvpe((const char *)a,(const char* const*)b,(const char* const*)c) : \
138 _execvp((const char *)a,(const char* const*)b)
139 # endif
140 #else
141 # include <unistd.h>
142 # define DEFAULT_PATH "/bin:/usr/bin:."
144 static int exec_command(const char *file, char *const *av, char *const *envp)
146 char *path;
147 const char *searchpath, *esp;
148 size_t prefixlen, filelen, totallen;
150 if (strchr(file, '/')) /* Specific path */
151 return envp ? execve(file, av, envp) : execv(file, av);
153 filelen = strlen(file);
154 path = NULL;
156 searchpath = getenv("PATH");
157 if (!searchpath)
158 searchpath = DEFAULT_PATH;
160 errno = ENOENT; /* Default errno, if execve() doesn't change it */
162 do {
163 esp = strchr(searchpath, ':');
164 if (esp)
165 prefixlen = (size_t) (esp - searchpath);
166 else
167 prefixlen = strlen(searchpath);
169 if (prefixlen == 0 || searchpath[prefixlen - 1] == '/') {
170 totallen = prefixlen + filelen;
171 # ifdef PATH_MAX
172 if (totallen >= PATH_MAX)
173 continue;
174 # endif
175 path = malloc(totallen + 1);
176 memcpy(path, searchpath, prefixlen);
177 memcpy(path + prefixlen, file, filelen);
178 } else {
179 totallen = prefixlen + filelen + 1;
180 # ifdef PATH_MAX
181 if (totallen >= PATH_MAX)
182 continue;
183 # endif
184 path = malloc(totallen + 1);
185 memcpy(path, searchpath, prefixlen);
186 path[prefixlen] = '/';
187 memcpy(path + prefixlen + 1, file, filelen);
189 path[totallen] = '\0';
191 if (envp) {
192 execve(path, av, envp);
193 } else {
194 execv(path, av);
196 free(path);
197 path = NULL;
198 if (errno == E2BIG || errno == ENOEXEC ||
199 errno == ENOMEM || errno == ETXTBSY)
200 break; /* Report this as an error, no more search */
202 searchpath = esp + 1;
203 } while (esp);
205 return -1;
209 It is not possible to mimic |spawnve()| completely. The main problem is
210 that the |fork|--|waitpid| combination cannot really do identical error
211 reporting to the parent process, because it has to pass all the possible
212 error conditions as well as the actual process return status through a
213 single 8-bit value.
215 The current implementation tries to give back meaningful results for |execve()|
216 errors in the child, for the cases that could also be returned by |spawnve()|,
217 and for |ETXTBSY|, because that can be triggered by our path searching routine.
219 This implementation does not differentiate abnormal status conditions reported
220 by |waitpid()|, but will simply return a single error indication value.
222 For all this, hyjacking a bunch of numbers in the range 1...255 is needed.
223 The chance of collisions is hopefully diminished by using a rather random
224 range in the 8-bit section.
227 # define INVALID_RET_E2BIG 143
228 # define INVALID_RET_ENOENT 144
229 # define INVALID_RET_ENOEXEC 145
230 # define INVALID_RET_ENOMEM 146
231 # define INVALID_RET_ETXTBSY 147
232 # define INVALID_RET_UNKNOWN 148
233 # define INVALID_RET_INTR 149
235 static int spawn_command(const char *file, char *const *av, char *const *envp)
237 pid_t pid, wait_pid;
238 int status;
239 pid = fork();
240 if (pid < 0) {
241 return -1; /* fork failed */
243 if (pid > 0) { /* parent */
244 status = 0;
245 wait_pid = waitpid(pid, &status, 0);
246 if (wait_pid == pid) {
247 if (WIFEXITED(status))
248 return WEXITSTATUS(status);
249 else
250 return INVALID_RET_INTR;
251 } else {
252 return -1; /* some waitpid error */
254 } else {
255 int f;
256 /* somewhat random upper limit. ignore errors on purpose */
257 for (f = 0; f < 256; f++)
258 (void) fsync(f);
260 if (exec_command(file, av, envp)) {
261 /* let's hope no-one uses these values */
262 switch (errno) {
263 case E2BIG:
264 exit(INVALID_RET_E2BIG);
265 case ETXTBSY:
266 exit(INVALID_RET_ETXTBSY);
267 case ENOENT:
268 exit(INVALID_RET_ENOENT);
269 case ENOEXEC:
270 exit(INVALID_RET_ENOEXEC);
271 case ENOMEM:
272 exit(INVALID_RET_ENOMEM);
273 default:
274 exit(INVALID_RET_UNKNOWN);
276 return -1;
279 return 0;
282 #endif
284 #ifdef _WIN32
285 static char *get_command_name(char *maincmd)
287 /* retrieve argv[0] part from the command string,
288 it will be truncated to MAX_PATH if it's too long */
289 char *cmdname = (char *) malloc(sizeof(char) * MAX_PATH);
290 int i, k, quoted;
291 quoted = k = 0;
292 for (i = 0; (i < MAX_PATH) && maincmd[i] &&
293 ((maincmd[i] != ' ' && maincmd[i] != '\t') || quoted); i++) {
294 if (maincmd[i] == '"') {
295 quoted = !quoted;
296 } else {
297 cmdname[k] = maincmd[i];
298 k++;
301 cmdname[k] = '\0';
302 return cmdname;
304 #endif
306 static char **do_split_command(const char *maincmd, char **runcmd)
308 char **cmdline = NULL;
309 #ifdef _WIN32
310 /* On WIN32 don't split anything, because
311 _spawnvpe can't put it back together properly
312 if there are quoted arguments with spaces.
313 Instead, dump everything into one argument
314 and it will be passed through as is */
315 cmdline = malloc(sizeof(char *) * 2);
316 cmdline[0] = xstrdup(maincmd);
317 cmdline[1] = NULL;
318 *runcmd = get_command_name(cmdline[0]);
319 #else
320 char *piece, *start_piece;
321 const char *cmd;
322 unsigned int i, j;
323 int ret = 0;
324 int in_string = 0;
325 int quoted = 0;
326 if (strlen(maincmd) == 0)
327 return NULL;
328 /* allocate the array of options first. it will probably be
329 be a little bit too big, but better too much than to little */
330 j = 2;
331 for (i = 0; i < strlen(maincmd); i++) {
332 if (maincmd[i] == ' ')
333 j++;
335 cmdline = malloc(sizeof(char *) * j);
336 for (i = 0; i < j; i++) {
337 cmdline[i] = NULL;
339 cmd = maincmd;
340 i = 0;
341 while (cmd[i] == ' ')
342 i++; /* skip leading spaces */
343 start_piece = malloc(strlen(cmd) + 1); /* a buffer */
344 piece = start_piece;
345 for (; i <= strlen(maincmd); i++) {
346 if (cmd[i] == '\\' &&
347 (cmd[i + 1] == '\\' || cmd[i + 1] == '\'' || cmd[i + 1] == '"')) {
348 quoted = 1;
349 continue;
351 if (in_string && cmd[i] == in_string && !quoted) {
352 in_string = 0;
353 continue;
355 if ((cmd[i] == '"' || cmd[i] == '\'') && !quoted) {
356 in_string = cmd[i];
357 continue;
359 if ((in_string == 0 && cmd[i] == ' ') || cmd[i] == 0) {
360 *piece = 0;
361 cmdline[ret++] = xstrdup(start_piece);
362 piece = start_piece;
363 while (i < strlen(maincmd) && cmd[(i + 1)] == ' ')
364 i++;
365 continue;
367 *piece++ = cmd[i];
368 quoted = 0;
370 xfree(start_piece);
371 *runcmd = cmdline[0];
372 #endif
373 return cmdline;
376 static char **do_flatten_command(lua_State * L, char **runcmd)
378 unsigned int i;
379 int j;
380 const char *s;
381 char **cmdline = NULL;
382 *runcmd = NULL;
384 for (j = 1;; j++) {
385 lua_rawgeti(L, -1, j);
386 if (lua_isnil(L, -1)) {
387 lua_pop(L, 1);
388 break;
390 lua_pop(L, 1);
392 if (j == 1)
393 return NULL;
394 cmdline = malloc(sizeof(char *) * (unsigned) (j + 1));
395 for (i = 1; i <= (unsigned) j; i++) {
396 cmdline[i] = NULL;
397 lua_rawgeti(L, -1, (int) i);
398 if (lua_isnil(L, -1) || (s = lua_tostring(L, -1)) == NULL) {
399 lua_pop(L, 1);
400 if (i == 1) {
401 xfree(cmdline);
402 return NULL;
403 } else {
404 break;
406 } else {
407 lua_pop(L, 1);
408 cmdline[(i - 1)] = xstrdup(s);
411 cmdline[i] = NULL;
413 lua_rawgeti(L, -1, 0);
414 if (lua_isnil(L, -1) || (s = lua_tostring(L, -1)) == NULL) {
415 #ifdef _WIN32
416 *runcmd = get_command_name(cmdline[0]);
417 #else
418 *runcmd = cmdline[0];
419 #endif
420 } else {
421 *runcmd = xstrdup(s);
423 lua_pop(L, 1);
425 return cmdline;
429 static int os_exec(lua_State * L)
431 int allow = 0;
432 const char *maincmd = NULL;
433 char *runcmd = NULL;
434 char *safecmd = NULL, *cmdname = NULL;
435 char **cmdline = NULL;
436 char **envblock = NULL;
438 if (lua_gettop(L) != 1) {
439 lua_pushnil(L);
440 lua_pushliteral(L, "invalid arguments passed");
441 return 2;
443 if (shellenabledp <= 0) {
444 lua_pushnil(L);
445 lua_pushliteral(L, "All command execution disabled.");
446 return 2;
448 if (lua_type(L, 1) == LUA_TSTRING) {
449 maincmd = lua_tostring(L, 1);
450 cmdline = do_split_command(maincmd, &runcmd);
451 } else if (lua_type(L, 1) == LUA_TTABLE) {
452 cmdline = do_flatten_command(L, &runcmd);
454 /* If restrictedshell == 0, any command is allowed. */
455 /* this is a little different from \write18/ os.execute processing
456 * because it does not test for commands with fixed arguments,
457 * but I am not so eager to attempt to fix that. Just document
458 * that os.exec() checks only the command name.
460 if (restrictedshell == 0) {
461 allow = 1;
462 } else {
463 const char *theruncmd = runcmd;
464 allow = shell_cmd_is_allowed(theruncmd, &safecmd, &cmdname);
467 if (allow > 0 && cmdline != NULL && runcmd != NULL) {
468 #if defined(_WIN32) && DONT_REALLY_EXIT
469 if (allow == 2)
470 exec_command(safecmd, cmdline, envblock);
471 else
472 exec_command(runcmd, cmdline, envblock);
473 #else
475 int r;
476 if (allow == 2)
477 r = exec_command(safecmd, cmdline, envblock);
478 else
479 r = exec_command(runcmd, cmdline, envblock);
480 if (r == -1) {
481 lua_pushnil(L);
482 lua_pushfstring(L, "%s: %s", runcmd, strerror(errno));
483 lua_pushinteger(L, errno);
484 return 3;
487 #endif
489 if (safecmd)
490 free(safecmd);
491 if (cmdname)
492 free(cmdname);
493 if (allow == 0) {
494 lua_pushnil(L);
495 lua_pushliteral(L, "Command execution disabled via shell_escape='p'");
496 return 2;
498 lua_pushnil(L);
499 lua_pushliteral(L, "invalid command line passed");
500 return 2;
503 #define do_error_return(A,B) do { \
504 lua_pushnil(L); \
505 lua_pushfstring(L,"%s: %s",runcmd,(A)); \
506 lua_pushinteger(L, B); \
507 return 3; \
508 } while (0)
510 static int os_spawn(lua_State * L)
512 int allow = 0;
513 const char *maincmd = NULL;
514 char *runcmd = NULL;
515 char *safecmd = NULL, *cmdname = NULL;
516 char **cmdline = NULL;
517 char **envblock = NULL;
518 int i;
520 if (lua_gettop(L) != 1) {
521 lua_pushnil(L);
522 lua_pushliteral(L, "invalid arguments passed");
523 return 2;
525 if (shellenabledp <= 0) {
526 lua_pushnil(L);
527 lua_pushliteral(L, "All command execution disabled.");
528 return 2;
530 if (lua_type(L, 1) == LUA_TSTRING) {
531 maincmd = lua_tostring(L, 1);
532 cmdline = do_split_command(maincmd, &runcmd);
533 } else if (lua_type(L, 1) == LUA_TTABLE) {
534 cmdline = do_flatten_command(L, &runcmd);
536 /* If restrictedshell == 0, any command is allowed. */
537 /* this is a little different from \write18/ os.execute processing
538 * because it does not test for commands with fixed arguments,
539 * but I am not so eager to attempt to fix that. Just document
540 * that os.exec() checks only the command name.
542 if (restrictedshell == 0) {
543 allow = 1;
544 } else {
545 const char *theruncmd = runcmd;
546 allow = shell_cmd_is_allowed(theruncmd, &safecmd, &cmdname);
548 if (allow > 0 && cmdline != NULL && runcmd != NULL) {
549 if (allow == 2)
550 i = spawn_command(safecmd, cmdline, envblock);
551 else
552 i = spawn_command(runcmd, cmdline, envblock);
553 if (safecmd)
554 free(safecmd);
555 if (cmdname)
556 free(cmdname);
557 if (i == 0) {
558 lua_pushinteger(L, i);
559 return 1;
560 } else if (i == -1) {
561 /* this branch covers WIN32 as well as fork() and waitpid() errors */
562 do_error_return(strerror(errno), errno);
563 #ifndef _WIN32
564 } else if (i == INVALID_RET_E2BIG) {
565 do_error_return(strerror(E2BIG), i);
566 } else if (i == INVALID_RET_ENOENT) {
567 do_error_return(strerror(ENOENT), i);
568 } else if (i == INVALID_RET_ENOEXEC) {
569 do_error_return(strerror(ENOEXEC), i);
570 } else if (i == INVALID_RET_ENOMEM) {
571 do_error_return(strerror(ENOMEM), i);
572 } else if (i == INVALID_RET_ETXTBSY) {
573 do_error_return(strerror(ETXTBSY), i);
574 } else if (i == INVALID_RET_UNKNOWN) {
575 do_error_return("execution failed", i);
576 } else if (i == INVALID_RET_INTR) {
577 do_error_return("execution interrupted", i);
578 #endif
579 } else {
580 lua_pushinteger(L, i);
581 return 1;
584 if (safecmd)
585 free(safecmd);
586 if (cmdname)
587 free(cmdname);
588 if (allow == 0) {
589 lua_pushnil(L);
590 lua_pushliteral(L, "Command execution disabled via shell_escape='p'");
591 return 2;
593 lua_pushnil(L);
594 lua_pushliteral(L, "invalid command line passed");
595 return 2;
598 /* Hans wants to set env values */
600 static int os_setenv(lua_State * L)
602 const char *key, *val;
603 char *value;
604 key = luaL_optstring(L, 1, NULL);
605 val = luaL_optstring(L, 2, NULL);
606 if (key) {
607 if (val) {
608 value = xmalloc((unsigned) (strlen(key) + strlen(val) + 2));
609 sprintf(value, "%s=%s", key, val);
610 if (putenv(value)) {
611 return luaL_error(L, "unable to change environment");
613 } else {
614 #if defined(_WIN32) || defined(__sun__) || defined(__sun) || defined(_AIX)
615 value = xmalloc(strlen(key) + 2);
616 sprintf(value, "%s=", key);
617 if (putenv(value)) {
618 return luaL_error(L, "unable to change environment");
620 #else
621 (void) unsetenv(key);
622 #endif
625 lua_pushboolean(L, 1);
626 return 1;
630 static void find_env(lua_State * L)
632 char *envitem, *envitem_orig;
633 char *envkey;
634 char **envpointer;
635 envpointer = environ;
636 lua_getglobal(L, "os");
637 if (envpointer != NULL && lua_istable(L, -1)) {
638 luaL_checkstack(L, 2, "out of stack space");
639 lua_pushstring(L, "env");
640 lua_newtable(L);
641 while (*envpointer) {
642 /* TODO: perhaps a memory leak here */
643 luaL_checkstack(L, 2, "out of stack space");
644 envitem = xstrdup(*envpointer);
645 envitem_orig = envitem;
646 envkey = envitem;
647 while (*envitem != '=') {
648 envitem++;
650 *envitem = 0;
651 envitem++;
652 lua_pushstring(L, envkey);
653 lua_pushstring(L, envitem);
654 lua_rawset(L, -3);
655 envpointer++;
656 free(envitem_orig);
658 lua_rawset(L, -3);
660 lua_pop(L, 1);
663 static int ex_sleep(lua_State * L)
665 lua_Number interval = luaL_checknumber(L, 1);
666 lua_Number units = luaL_optnumber(L, 2, 1);
667 #ifdef _WIN32
668 Sleep((DWORD) (1e3 * interval / units));
669 #else /* assumes posix or bsd */
670 usleep((unsigned) (1e6 * interval / units));
671 #endif
672 return 0;
677 #ifdef _WIN32
678 # define _UTSNAME_LENGTH 65
680 /* Structure describing the system and machine. */
681 struct utsname {
682 char sysname[_UTSNAME_LENGTH];
683 char nodename[_UTSNAME_LENGTH];
684 char release[_UTSNAME_LENGTH];
685 char version[_UTSNAME_LENGTH];
686 char machine[_UTSNAME_LENGTH];
690 * Get name and information about current kernel.
692 static int uname(struct utsname *uts)
694 enum { WinNT, Win95, Win98, WinUnknown };
695 OSVERSIONINFO osver;
696 SYSTEM_INFO sysinfo;
697 DWORD sLength;
698 DWORD os = WinUnknown;
700 memset(uts, 0, sizeof(*uts));
702 osver.dwOSVersionInfoSize = sizeof(osver);
703 GetVersionEx(&osver);
704 GetSystemInfo(&sysinfo);
706 switch (osver.dwPlatformId) {
707 case VER_PLATFORM_WIN32_NT: /* NT, Windows 2000 or Windows XP */
708 if (osver.dwMajorVersion == 4)
709 strcpy(uts->sysname, "Windows NT4x"); /* NT4x */
710 else if (osver.dwMajorVersion <= 3)
711 strcpy(uts->sysname, "Windows NT3x"); /* NT3x */
712 else if (osver.dwMajorVersion == 5) {
713 if (osver.dwMinorVersion == 0)
714 strcpy(uts->sysname, "Windows 2000"); /* 2k */
715 else if (osver.dwMinorVersion == 1)
716 strcpy(uts->sysname, "Windows XP"); /* XP */
717 else if (osver.dwMinorVersion == 2)
718 strcpy(uts->sysname, "Windows Server 2003"); /* Server 2003 */
719 } else if (osver.dwMajorVersion == 6) {
721 if( osver.wProductType == VER_NT_WORKSTATION )
723 strcpy(uts->sysname, "Windows Vista"); /* Vista */
725 else
726 strcpy (uts->sysname, "Windows Server 2008");
729 os = WinNT;
730 break;
732 case VER_PLATFORM_WIN32_WINDOWS: /* Win95, Win98 or WinME */
733 if ((osver.dwMajorVersion > 4) ||
734 ((osver.dwMajorVersion == 4) && (osver.dwMinorVersion > 0))) {
735 if (osver.dwMinorVersion >= 90)
736 strcpy(uts->sysname, "Windows ME"); /* ME */
737 else
738 strcpy(uts->sysname, "Windows 98"); /* 98 */
739 os = Win98;
740 } else {
741 strcpy(uts->sysname, "Windows 95"); /* 95 */
742 os = Win95;
744 break;
746 case VER_PLATFORM_WIN32s: /* Windows 3.x */
747 strcpy(uts->sysname, "Windows");
748 break;
750 default: /* anything else */
751 strcpy(uts->sysname, "Windows");
752 break;
755 sprintf(uts->version, "%ld.%02ld",
756 osver.dwMajorVersion, osver.dwMinorVersion);
758 if (osver.szCSDVersion[0] != '\0' &&
759 (strlen(osver.szCSDVersion) + strlen(uts->version) + 1) <
760 sizeof(uts->version)) {
761 strcat(uts->version, " ");
762 strcat(uts->version, osver.szCSDVersion);
765 sprintf(uts->release, "build %ld", osver.dwBuildNumber & 0xFFFF);
767 switch (sysinfo.wProcessorArchitecture) {
768 case PROCESSOR_ARCHITECTURE_PPC:
769 strcpy(uts->machine, "ppc");
770 break;
771 case PROCESSOR_ARCHITECTURE_ALPHA:
772 strcpy(uts->machine, "alpha");
773 break;
774 case PROCESSOR_ARCHITECTURE_MIPS:
775 strcpy(uts->machine, "mips");
776 break;
777 case PROCESSOR_ARCHITECTURE_INTEL:
779 * dwProcessorType is only valid in Win95 and Win98 and WinME
780 * wProcessorLevel is only valid in WinNT
782 switch (os) {
783 case Win95:
784 case Win98:
785 switch (sysinfo.dwProcessorType) {
786 case PROCESSOR_INTEL_386:
787 case PROCESSOR_INTEL_486:
788 case PROCESSOR_INTEL_PENTIUM:
789 sprintf(uts->machine, "i%ld", sysinfo.dwProcessorType);
790 break;
791 default:
792 strcpy(uts->machine, "i386");
793 break;
795 break;
796 case WinNT:
797 sprintf(uts->machine, "i%d86", sysinfo.wProcessorLevel);
798 break;
799 default:
800 strcpy(uts->machine, "unknown");
801 break;
803 break;
804 default:
805 strcpy(uts->machine, "unknown");
806 break;
809 sLength = sizeof(uts->nodename) - 1;
810 GetComputerName(uts->nodename, &sLength);
811 return 0;
813 #endif
816 static int ex_uname(lua_State * L)
818 struct utsname uts;
819 if (uname(&uts) >= 0) {
820 lua_newtable(L);
821 lua_pushstring(L, uts.sysname);
822 lua_setfield(L, -2, "sysname");
823 lua_pushstring(L, uts.machine);
824 lua_setfield(L, -2, "machine");
825 lua_pushstring(L, uts.release);
826 lua_setfield(L, -2, "release");
827 lua_pushstring(L, uts.version);
828 lua_setfield(L, -2, "version");
829 lua_pushstring(L, uts.nodename);
830 lua_setfield(L, -2, "nodename");
831 } else {
832 lua_pushnil(L);
834 return 1;
838 #if (! defined (_WIN32)) && (! defined (__SUNOS__))
839 static int os_times(lua_State * L)
841 struct tms r;
842 (void) times(&r);
843 lua_newtable(L);
844 lua_pushnumber(L, /* float */
845 ((lua_Number) (r.tms_utime)) /
846 (lua_Number) sysconf(_SC_CLK_TCK));
847 lua_setfield(L, -2, "utime");
848 lua_pushnumber(L, /* float */
849 ((lua_Number) (r.tms_stime)) /
850 (lua_Number) sysconf(_SC_CLK_TCK));
851 lua_setfield(L, -2, "stime");
852 lua_pushnumber(L, /* float */
853 ((lua_Number) (r.tms_cutime)) /
854 (lua_Number) sysconf(_SC_CLK_TCK));
855 lua_setfield(L, -2, "cutime");
856 lua_pushnumber(L, /* float */
857 ((lua_Number) (r.tms_cstime)) /
858 (lua_Number) sysconf(_SC_CLK_TCK));
859 lua_setfield(L, -2, "cstime");
860 return 1;
862 #endif
864 #if ! defined (__SUNOS__)
866 # if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
867 # define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
868 # else
869 # define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
870 # endif
872 static int os_gettimeofday(lua_State * L)
874 double v;
875 # ifndef _WIN32
876 struct timeval tv;
877 gettimeofday(&tv, NULL);
878 v = (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
879 # else
880 FILETIME ft;
881 int64_t tmpres = 0;
883 GetSystemTimeAsFileTime(&ft);
885 tmpres |= ft.dwHighDateTime;
886 tmpres <<= 32;
887 tmpres |= ft.dwLowDateTime;
888 tmpres /= 10;
889 tmpres -= DELTA_EPOCH_IN_MICROSECS; /*converting file time to unix epoch */
890 v = (double) tmpres / 1000000.0;
891 # endif
892 lua_pushnumber(L, v); /* float */
893 return 1;
895 #endif
897 static const char repl[] = "0123456789abcdefghijklmnopqrstuvwxyz";
899 #define MAXTRIES 36*36*36
901 #ifndef HAVE_MKDTEMP
902 static int dirs_made = 0;
904 static char *do_mkdtemp(char *tmpl)
906 int count;
907 int value;
908 char *xes = &tmpl[strlen(tmpl) - 6];
909 /* this is not really all that random, but it will do */
910 if (dirs_made == 0) {
911 srand((unsigned) time(NULL));
913 value = rand();
914 for (count = 0; count < MAXTRIES; value += 8413, ++count) {
915 int v = value;
916 xes[0] = repl[v % 36];
917 v /= 36;
918 xes[1] = repl[v % 36];
919 v /= 36;
920 xes[2] = repl[v % 36];
921 v /= 36;
922 xes[3] = repl[v % 36];
923 v /= 36;
924 xes[4] = repl[v % 36];
925 v /= 36;
926 xes[5] = repl[v % 36];
927 if (MKDIR(tmpl, S_IRUSR | S_IWUSR | S_IXUSR) >= 0) {
928 dirs_made++;
929 return tmpl;
930 } else if (errno != EEXIST) {
931 return NULL;
934 return NULL;
936 #endif
938 static int os_tmpdir(lua_State * L)
940 char *s, *tempdir;
941 const char *tmp = luaL_optstring(L, 1, "luatex.XXXXXX");
942 if (tmp == NULL ||
943 strlen(tmp) < 6 || (strcmp(tmp + strlen(tmp) - 6, "XXXXXX") != 0)) {
944 lua_pushnil(L);
945 lua_pushstring(L, "Invalid argument to os.tmpdir()");
946 return 2;
947 } else {
948 tempdir = xstrdup(tmp);
950 #ifdef HAVE_MKDTEMP
951 s = mkdtemp(tempdir);
952 #else
953 s = do_mkdtemp(tempdir);
954 #endif
955 if (s == NULL) {
956 int en = errno; /* calls to Lua API may change this value */
957 lua_pushnil(L);
958 lua_pushfstring(L, "%s", strerror(en));
959 return 2;
960 } else {
961 lua_pushstring(L, s);
962 return 1;
967 static int os_execute(lua_State * L)
969 int allow = 0;
970 int ret = 1;
971 char *safecmd = NULL;
972 char *cmdname = NULL;
973 const char *cmd = luaL_optstring(L, 1, NULL);
975 if (cmd == NULL) { /* pretend we are \.{\\pdfshellescape} */
976 if (shellenabledp <= 0) {
977 lua_pushinteger(L, 0);
978 } else if (restrictedshell == 0) {
979 lua_pushinteger(L, 1);
980 } else {
981 lua_pushinteger(L, 2);
983 return 1;
985 if (shellenabledp <= 0) {
986 lua_pushnil(L);
987 lua_pushstring(L, "All command execution disabled.");
988 return 2;
990 /* If restrictedshell == 0, any command is allowed. */
991 if (restrictedshell == 0)
992 allow = 1;
993 else
994 allow = shell_cmd_is_allowed(cmd, &safecmd, &cmdname);
996 if (allow == 1) {
997 lua_pushinteger(L, system(cmd));
998 } else if (allow == 2) {
999 lua_pushinteger(L, system(safecmd));
1000 } else {
1001 lua_pushnil(L);
1002 ret = 2;
1003 if (allow == 0)
1004 lua_pushstring(L,
1005 "Command execution disabled via shell_escape='p'");
1006 else /* allow == -1 */
1007 lua_pushstring(L, "Quoting error in system command line.");
1009 if (safecmd)
1010 free(safecmd);
1011 if (cmdname)
1012 free(cmdname);
1013 return ret;
1017 void open_oslibext(lua_State * L, int safer)
1020 find_env(L);
1022 lua_getglobal(L, "os");
1023 lua_pushcfunction(L, ex_sleep);
1024 lua_setfield(L, -2, "sleep");
1025 lua_pushliteral(L, OS_PLATTYPE);
1026 lua_setfield(L, -2, "type");
1027 lua_pushliteral(L, OS_PLATNAME);
1028 lua_setfield(L, -2, "name");
1029 lua_pushcfunction(L, ex_uname);
1030 lua_setfield(L, -2, "uname");
1031 #if (! defined (_WIN32)) && (! defined (__SUNOS__))
1032 lua_pushcfunction(L, os_times);
1033 lua_setfield(L, -2, "times");
1034 #endif
1035 #if ! defined (__SUNOS__)
1036 lua_pushcfunction(L, os_gettimeofday);
1037 lua_setfield(L, -2, "gettimeofday");
1038 #endif
1040 if (!safer) {
1041 lua_pushcfunction(L, os_setenv);
1042 lua_setfield(L, -2, "setenv");
1043 lua_pushcfunction(L, os_exec);
1044 lua_setfield(L, -2, "exec");
1045 lua_pushcfunction(L, os_spawn);
1046 lua_setfield(L, -2, "spawn");
1047 lua_pushcfunction(L, os_execute);
1048 lua_setfield(L, -2, "execute");
1049 lua_pushcfunction(L, os_tmpdir);
1050 lua_setfield(L, -2, "tmpdir");
1052 lua_pop(L, 1); /* pop the table */