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