lmtoslibext.c /size: 12 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7# if defined (_WIN32)
8#   define MKDIR(a,b) mkdir(a)
9# else
10#   define MKDIR(a,b) mkdir(a,b)
11# endif
12
13/*tex
14
15    An attempt to figure out the basic platform, does not care about niceties like version numbers
16    yet, and ignores platforms where \LUATEX\ is unlikely to successfully compile without major
17    porting effort (amiga,mac,os2,vms). We dropped solaris, cygwin, hpux, iris, sysv, dos, djgpp
18    etc. Basically we have either a windows or some kind of unix brand.
19
20*/
21
22# ifdef _WIN32
23#   define OSLIB_PLATTYPE "windows"
24#   define OSLIB_PLATNAME "windows"
25# else
26#   include <sys/param.h>
27#   include <sys/utsname.h>
28#   if defined(__linux__) || defined (__gnu_linux__)
29#     define OSLIB_PLATNAME "linux"
30#   elif defined(__MACH__) && defined(__APPLE__)
31#     define OSLIB_PLATNAME "macosx"
32#   elif defined(__FreeBSD__)
33#     define OSLIB_PLATNAME "freebsd"
34#   elif defined(__OpenBSD__)
35#     define OSLIB_PLATNAME "openbsd"
36#   elif defined(__BSD__)
37#     define OSLIB_PLATNAME "bsd"
38#   elif defined(__GNU__)
39#     define OSLIB_PLATNAME "gnu"
40#   else
41#     define OSLIB_PLATNAME "generic"
42#   endif
43#   define OSLIB_PLATTYPE "unix"
44# endif
45
46/*tex
47
48    There could be more platforms that don't have these two, but win32 and sunos are for sure.
49    |gettimeofday()| for win32 is using an alternative definition
50
51*/
52
53# ifndef _WIN32
54#   include <sys/time.h>  /*tex for |gettimeofday()| */
55#   include <sys/times.h> /*tex for |times()| */
56#   include <sys/wait.h>
57# endif
58
59static int oslib_sleep(lua_State *L)
60{
61    lua_Number interval = luaL_checknumber(L, 1);
62    lua_Number units = luaL_optnumber(L, 2, 1);
63# ifdef _WIN32
64    Sleep((DWORD) (1e3 * interval / units));
65# else                           /* assumes posix or bsd */
66    usleep((unsigned) (1e6 * interval / units));
67# endif
68    return 0;
69}
70
71# ifdef _WIN32
72
73    # define _UTSNAME_LENGTH 65
74
75    /*tex Structure describing the system and machine. */
76
77    typedef struct utsname {
78        char sysname [_UTSNAME_LENGTH];
79        char nodename[_UTSNAME_LENGTH];
80        char release [_UTSNAME_LENGTH];
81        char version [_UTSNAME_LENGTH];
82        char machine [_UTSNAME_LENGTH];
83    } utsname;
84
85    /*tex Get name and information about current kernel. */
86
87    /*tex
88
89        \starttabulate[|T|r|]
90        \NC Windows 10                \NC 10.0 \NC \NR
91        \NC Windows Server 2016       \NC 10.0 \NC \NR
92        \NC Windows 8.1               \NC  6.3 \NC \NR
93        \NC Windows Server 2012 R2    \NC  6.3 \NC \NR
94        \NC Windows 8                 \NC  6.2 \NC \NR
95        \NC Windows Server 2012       \NC  6.2 \NC \NR
96        \NC Windows 7                 \NC  6.1 \NC \NR
97        \NC Windows Server 2008 R2    \NC  6.1 \NC \NR
98        \NC Windows Server 2008       \NC  6.0 \NC \NR
99        \NC Windows Vista             \NC  6.0 \NC \NR
100        \NC Windows Server 2003 R2    \NC  5.2 \NC \NR
101        \NC Windows Server 2003       \NC  5.2 \NC \NR
102        \NC Windows XP 64-Bit Edition \NC  5.2 \NC \NR
103        \NC Windows XP                \NC  5.1 \NC \NR
104        \NC Windows 2000              \NC  5.0 \NC \NR
105        \stoptabulate
106
107    */
108
109    static int uname(struct utsname *uts)
110    {
111        OSVERSIONINFO osver;
112        SYSTEM_INFO sysinfo;
113        DWORD sLength;
114        memset(uts, 0, sizeof(*uts));
115        osver.dwOSVersionInfoSize = sizeof(osver);
116        GetSystemInfo(&sysinfo);
117        strcpy(uts->sysname, "Windows");
118        /*tex When |GetVersionEx| becomes obsolete the version and release fields will be set to "". */
119     // if (0) {
120     //     GetVersionEx(&osver);
121     //     sprintf(uts->version, "%ld.%02ld", osver.dwMajorVersion, osver.dwMinorVersion);
122     //     if (osver.szCSDVersion[0] != '\0' && (strlen(osver.szCSDVersion) + strlen(uts->version) + 1) < sizeof(uts->version)) {
123     //         strcat(uts->version, " ");
124     //         strcat(uts->version, osver.szCSDVersion);
125     //     }
126     //     sprintf(uts->release, "build %ld", osver.dwBuildNumber & 0xFFFF);
127     // } else { 
128            /*tex I can't motivate myself to figure this out. */
129            strcpy(uts->version, "");
130            strcpy(uts->release, "");
131     // }
132        /*tex So far for the fragile and actually not that relevant part of |uts|. */
133        switch (sysinfo.wProcessorArchitecture) {
134            case PROCESSOR_ARCHITECTURE_AMD64:
135                strcpy(uts->machine, "x86_64");
136                break;
137# ifdef PROCESSOR_ARCHITECTURE_ARM64
138            case PROCESSOR_ARCHITECTURE_ARM64:
139                strcpy(uts->machine, "arm64");
140                break;
141# endif
142            case PROCESSOR_ARCHITECTURE_INTEL:
143                strcpy(uts->machine, "i386");
144                break;
145            default:
146                strcpy(uts->machine, "unknown");
147                break;
148        }
149        sLength = sizeof(uts->nodename) - 1;
150        GetComputerName(uts->nodename, &sLength);
151        return 0;
152    }
153
154# endif
155
156static int oslib_uname(lua_State *L)
157{
158    struct utsname uts;
159    if (uname(&uts) >= 0) {
160        lua_createtable(L,0,5);
161        lua_pushstring(L, uts.sysname);
162        lua_setfield(L, -2, "sysname");
163        lua_pushstring(L, uts.machine);
164        lua_setfield(L, -2, "machine");
165        lua_pushstring(L, uts.release);
166        lua_setfield(L, -2, "release");
167        lua_pushstring(L, uts.version);
168        lua_setfield(L, -2, "version");
169        lua_pushstring(L, uts.nodename);
170        lua_setfield(L, -2, "nodename");
171    } else {
172        lua_pushnil(L);
173    }
174    return 1;
175}
176
177# if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
178    # define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
179# else
180    # define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
181# endif
182
183# ifdef _WIN32
184
185    # ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
186        # define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x04
187    # endif
188
189    static int oslib_gettimeofday(lua_State *L)
190    {
191        FILETIME ft;
192        __int64 tmpres = 0;
193        GetSystemTimeAsFileTime(&ft);
194        tmpres |= ft.dwHighDateTime;
195        tmpres <<= 32;
196        tmpres |= ft.dwLowDateTime;
197        tmpres /= 10;
198        /*tex Convert file time to unix epoch: */
199        tmpres -= DELTA_EPOCH_IN_MICROSECS;
200        /*tex Float: */
201        lua_pushnumber(L, (double) tmpres / 1000000.0);
202        return 1;
203    }
204
205    static int oslib_enableansi(lua_State *L)
206    {
207        HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
208        DWORD mode = 0;
209        int done = 0;
210        if (GetConsoleMode(handle, &mode)) {
211            mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
212            if (SetConsoleMode(handle, mode)) {
213                done = 1;
214            } else {
215                /* bad */
216            }
217        }
218        lua_pushboolean(L, done);
219        return 1;
220    }
221
222# else
223
224    static int oslib_gettimeofday(lua_State *L)
225    {
226        double v;
227        struct timeval tv;
228        gettimeofday(&tv, NULL);
229        v = (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
230        /*tex Float: */
231        lua_pushnumber(L, v);
232        return 1;
233    }
234
235    static int oslib_enableansi(lua_State *L)
236    {
237        lua_pushboolean(L, 1);
238        return 1;
239    }
240
241# endif
242
243/*tex Historically we have a different os.execute than Lua! */
244
245static int oslib_execute(lua_State *L)
246{
247    const char *cmd = luaL_optstring(L, 1, NULL);
248    if (cmd) {
249        lua_pushinteger(L, aux_utf8_system(cmd) || lmt_error_state.default_exit_code);
250    } else {
251        lua_pushinteger(L, 0);
252    }
253    return 1;
254}
255
256# ifdef _WIN32
257
258    static int oslib_remove (lua_State *L)
259    {
260        const char *filename = luaL_checkstring(L, 1);
261        return luaL_fileresult(L, aux_utf8_remove(filename) == 0, filename);
262    }
263
264    static int oslib_rename (lua_State *L)
265    {
266        const char *fromname = luaL_checkstring(L, 1);
267        const char *toname = luaL_checkstring(L, 2);
268        return luaL_fileresult(L, aux_utf8_rename(fromname, toname) == 0, NULL);
269    }
270
271    static int oslib_getcodepage(lua_State *L)
272    {
273        lua_pushinteger(L, (int) GetOEMCP());
274        lua_pushinteger(L, (int) GetACP());
275        return 2;
276    }
277
278    /*
279    static int oslib_getenv(lua_State *L) {
280        LPWSTR wkey = utf8_to_wide(luaL_checkstring(L, 1));
281        char * val = wide_to_utf8(_wgetenv(wkey));
282        lmt_memory_free(wkey);
283        lua_pushstring(L, val);
284        lmt_memory_free(val);
285        return 1;
286    }
287    */
288
289    static int oslib_getenv(lua_State *L)
290    {
291        const char *key = luaL_checkstring(L, 1);
292        char *val = NULL;
293        if (key) {
294            size_t wlen = 0;
295            LPWSTR wkey = aux_utf8_to_wide(key);
296            _wgetenv_s(&wlen, NULL, 0, wkey);
297            if (wlen) {
298                LPWSTR wval = (LPWSTR) lmt_memory_malloc(wlen * sizeof(WCHAR));
299                if (! _wgetenv_s(&wlen, wval, wlen, wkey)) {
300                    val = aux_utf8_from_wide(wval);
301                }
302            }
303        }
304        if (val) {
305            lua_pushstring(L, val);
306        } else {
307            lua_pushnil(L);
308        }
309        return 1;
310    }
311
312    static int oslib_setenv(lua_State *L)
313    {
314        const char *key = luaL_optstring(L, 1, NULL);
315        if (key) {
316            const char *val = luaL_optstring(L, 2, NULL);
317            LPWSTR wkey = aux_utf8_to_wide(key);
318            LPWSTR wval = aux_utf8_to_wide(val ? val : "");
319            int bad = _wputenv_s(wkey, wval); 
320            lmt_memory_free(wval);
321            lmt_memory_free(wkey);
322            if (bad) {
323                return luaL_error(L, "unable to change environment");
324            }
325        }
326        lua_pushboolean(L, 1);
327        return 1;
328    }
329
330# else
331
332    static int oslib_getcodepage(lua_State *L)
333    {
334        lua_pushboolean(L,0);
335        lua_pushboolean(L,0);
336        return 2;
337    }
338
339    static int oslib_setenv(lua_State *L)
340    {
341        const char *key = luaL_optstring(L, 1, NULL);
342        if (key) {
343            const char *val = luaL_optstring(L, 2, NULL);
344            if (val) {
345                char *value = lmt_memory_malloc((unsigned) (strlen(key) + strlen(val) + 2));
346                sprintf(value, "%s=%s", key, val);
347                if (putenv(value)) {
348                 /* lmt_memory_free(value); */ /* valgrind reports some issue otherwise */
349                    return luaL_error(L, "unable to change environment");
350                } else {
351                 /* lmt_memory_free(value); */ /* valgrind reports some issue otherwise */
352                }
353            } else {
354                (void) unsetenv(key);
355            }
356        }
357        lua_pushboolean(L, 1);
358        return 1;
359    }
360
361# endif
362
363static const luaL_Reg oslib_function_list[] = {
364    { "sleep",        oslib_sleep        },
365    { "uname",        oslib_uname        },
366    { "gettimeofday", oslib_gettimeofday },
367    { "setenv",       oslib_setenv       },
368    { "execute",      oslib_execute      },
369# ifdef _WIN32
370    { "rename",       oslib_rename       },
371    { "remove",       oslib_remove       },
372    { "getenv",       oslib_getenv       },
373# endif
374    { "enableansi",   oslib_enableansi   },
375    { "getcodepage",  oslib_getcodepage  },
376    { NULL,           NULL               },
377};
378
379
380/*tex
381    The |environ| variable is depricated on windows so it made sense to just drop this old \LUATEX\
382    feature.
383*/
384
385# ifndef _WIN32
386    extern char **environ;
387# else
388    # define environ _environ
389# endif
390
391int luaextend_os(lua_State *L)
392{
393    /*tex We locate the library: */
394    lua_getglobal(L, "os");
395    /*tex A few constant strings: */
396    lua_pushliteral(L, OSLIB_PLATTYPE);
397    lua_setfield(L, -2, "type");
398    lua_pushliteral(L, OSLIB_PLATNAME);
399    lua_setfield(L, -2, "name");
400    /*tex The extra functions: */
401    for (const luaL_Reg *lib = oslib_function_list; lib->name; lib++) {
402        lua_pushcfunction(L, lib->func);
403        lua_setfield(L, -2, lib->name);
404    }
405    /*tex Environment variables: */
406    if (0) {
407        char **envpointer = environ; /*tex Provided by the standard library. */
408        if (envpointer) {
409            lua_pushstring(L, "env");
410            lua_newtable(L);
411            while (*envpointer) {
412                /* TODO: perhaps a memory leak here  */
413                char *envitem = lmt_memory_strdup(*envpointer);
414                char *envitem_orig = envitem;
415                char *envkey = envitem;
416                while (*envitem != '=') {
417                    envitem++;
418                }
419                *envitem = 0;
420                envitem++;
421                lua_pushstring(L, envkey);
422                lua_pushstring(L, envitem);
423                lua_rawset(L, -3);
424                envpointer++;
425                lmt_memory_free(envitem_orig);
426            }
427            lua_rawset(L, -3);
428        }
429    }
430    /*tex Done. */
431    lua_pop(L, 1);
432    return 1;
433}
434