/* M2make OS helpers (C). * * Dual-ABI file/time/process primitives for the M2make front-end. * * Two string ABIs exist and both are served here: * - V3 (m2compiler-V3): ARRAY OF CHAR is a descriptor (LONGCARD * count + bytes (see runtime/syslib/shim.c). V3 buffers handed * to M2make are NUL-padded with count == capacity (verified by * probe), so the _d variants scan for NUL bounded by the count. * - GNU Modula-2: ARRAY OF CHAR is a NUL-terminated C buffer; * the _s variants use it directly. * * Handle-only calls (close) are ABI-neutral and shared. * Only the string-taking calls need _d/_s pairs. * Readline never overruns: Modula-2 passes cap (HIGH(buf)+1) and at * most cap-1 bytes plus NUL are stored. Physical lines longer than * the C temp (2048) continue on the next call; the Modula-2 scanner * keeps comment state across lines, so this only splits very long * lines (documented in the README). */ #include #include #include #include #define M2OS_TMP 2048 /* Copy a V3 descriptor to a NUL-terminated buffer (NUL scan bounded by the descriptor count, then by cap). */ static void desc2c(long *desc, char *buf, unsigned long cap) { long count = desc[0]; unsigned long bound; unsigned long i = 0; if (count < 0) count = 0; bound = (unsigned long)count; if (bound >= cap) bound = cap - 1; while (i < bound && ((char *)(desc + 1))[i] != 0) { buf[i] = ((char *)(desc + 1))[i]; i++; } buf[i] = 0; } /* ---------------- existence ---------------- */ long m2osExistD(long *desc) { char buf[4096]; struct stat st; desc2c(desc, buf, sizeof buf); return (stat(buf, &st) == 0); } long m2osExistS(char *s) { struct stat st; return (stat(s, &st) == 0); } /* ---------------- modification time ---------------- * 1 with *t set on success, 0 when the file cannot be statted. */ long m2osMtimeD(long *desc, long long *t) { char buf[4096]; struct stat st; desc2c(desc, buf, sizeof buf); if (stat(buf, &st) != 0) return 0; *t = (long long)st.st_mtime; return 1; } long m2osMtimeS(char *s, long long *t) { struct stat st; if (stat(s, &st) != 0) return 0; *t = (long long)st.st_mtime; return 1; } /* ---------------- process execution ---------------- * Runs cmd via /bin/sh, returns the raw system() code. */ long m2osSystemD(long *desc) { char buf[4096]; desc2c(desc, buf, sizeof buf); return (long)system(buf); } long m2osSystemS(char *s) { return (long)system(s); } /* ---------------- process exit (ABI-neutral) ---------------- */ void m2os_flush(void) { fflush(NULL); } void m2osBye(long code) { fflush(NULL); exit((int)code); } /* ---------------- command-line arguments (V3 side) ---------------- * Skips argv[0] (the program name), unlike the raw SysShim.arg. */ static char *m2os_args[1024]; static long m2os_nargs = -1; static void m2os_argv_init(void) { static char cmd[65536]; FILE *f; long i, n; if (m2os_nargs >= 0) return; m2os_nargs = 0; f = fopen("/proc/self/cmdline", "rb"); if (f == NULL) return; n = (long)fread(cmd, 1, sizeof cmd - 1, f); fclose(f); if (n <= 0) return; cmd[n] = 0; i = 0; while (i < n && m2os_nargs < 1024) { while (i < n && cmd[i] == 0) i++; if (i >= n) break; m2os_args[m2os_nargs++] = &cmd[i]; while (i < n && cmd[i] != 0) i++; } } long m2osArgcD(void) { m2os_argv_init(); if (m2os_nargs <= 0) return 0; return m2os_nargs - 1; } long m2osArgD(long i, long *desc, unsigned long cap) { const char *a; unsigned long n, k; char *out; m2os_argv_init(); if (cap == 0) return 0; if (i < 0 || i + 1 >= m2os_nargs) return 0; a = m2os_args[i + 1]; n = (unsigned long)strlen(a); if (n >= cap) n = cap - 1; out = (char *)(desc + 1); for (k = 0; k < n; k++) out[k] = a[k]; out[n] = 0; return 1; } /* ---------------- sequential text input ---------------- */ long m2osOpenreadD(long *desc) { char buf[4096]; desc2c(desc, buf, sizeof buf); return (long)fopen(buf, "r"); } long m2osOpenreadS(char *s) { return (long)fopen(s, "r"); } /* Shared readline core: 1 with a line stored, 0 at end of file. * The trailing newline (and a preceding carriage return) is * stripped. A final line without a newline still returns 1. */ static long readline_core(FILE *fp, char *out, unsigned long cap) { static char tmp[M2OS_TMP]; unsigned long n, i; if (cap == 0) return 0; if (fgets(tmp, sizeof tmp, fp) == NULL) return 0; n = (unsigned long)strlen(tmp); while (n > 0 && (tmp[n - 1] == 10 || tmp[n - 1] == 13)) n--; if (n >= cap) n = cap - 1; for (i = 0; i < n; i++) out[i] = tmp[i]; out[n] = 0; return 1; } long m2osReadlineD(long h, long *desc, unsigned long cap) { char *out; if (h == 0 || cap == 0) return 0; out = (char *)(desc + 1); return readline_core((FILE *)h, out, cap); } long m2osReadlineS(long h, char *buf, unsigned long cap) { if (h == 0) return 0; return readline_core((FILE *)h, buf, cap); } long m2osClose(long h) { if (h != 0) fclose((FILE *)h); return 0; }