// ===================================================================== // cli.sx — process command-line argument accessor (macOS), pure sx. // // `os_args(buf)` returns the real OS-level process argv as a `[]string`, // each element a zero-copy VIEW over the C runtime's argv memory. The // caller provides the `buf: []string` backing (typically a stack array); // every element points straight into the process's own argv block, which // lives for the whole process lifetime, so the views never dangle. // // Zero heap, zero per-arg allocation: nothing here touches // `context.allocator`. The returned slice header is a by-value stack // return whose `.ptr` is the caller's `buf` and whose elements are views // into C argv — ladder rungs "by-value", "view", and "caller buffer". // // `sx run ...` reality — READ THIS BEFORE CONSUMING: // Under `sx run`, the process argv is the sx INTERPRETER's argv, e.g. // ["sx", "run", "prog.sx", ...] — NOT a program's "own" logical args. // (The interpreter also consumes trailing tokens as additional source // files, so they don't reach the program as plain args anyway.) This // accessor reports the real process argv truthfully and does NOT strip // the interpreter prefix. Mapping process argv -> a program's logical // args (dropping the `sx run prog.sx` prefix, or via an sx-run // convention) is a CONSUMER concern handled later (distribution P3.1), // NOT here. // // Platform: macOS only for now, via the C runtime's `_NSGetArgv()` // (char***) and `_NSGetArgc()` (int*). On any other OS the accessors bail // loudly (message + non-zero exit) rather than returning a silent empty. // ===================================================================== #import "modules/std.sx"; #import "modules/compiler.sx"; libc :: #library "c"; // macOS C-runtime argv/argc accessors (crt_externs.h): // extern char ***_NSGetArgv(void); extern int *_NSGetArgc(void); // Each returns a pointer to the runtime's slot; dereference once for the // `char**` / `int` the process was launched with. Declared as `*s64` / // `*s32` since on 64-bit a `char***` is just a pointer to a pointer-sized // slot. ns_get_argv :: () -> *s64 #foreign libc "_NSGetArgv"; ns_get_argc :: () -> *s32 #foreign libc "_NSGetArgc"; // Bound to POSIX `_exit(2)`. Used only on the unsupported-platform path to // terminate loudly instead of handing back a misleading empty slice. cli_bail_exit :: (code: s32) -> noreturn #foreign libc "_exit"; // Number of process arguments (argc). >= 1 for any normally-launched // process, since argv[0] is the executable path. os_argc :: () -> s64 { inline if OS == { case .macos: { return cast(s64) ns_get_argc().*; } else: { out("std.cli: unsupported platform — only macOS is implemented (needs _NSGetArgv/_NSGetArgc).\n"); cli_bail_exit(70); } } } // Fill `buf` with VIEWS over the process argv and return the filled prefix // `buf[0 .. min(argc, buf.len)]`. Zero heap, zero copy: each element's // bytes live in the C runtime's argv block, valid for the whole process. // // The caller owns `buf` (typically a stack `[N]string`); the returned // slice points into it and is valid for as long as `buf` is in scope. If // the process has more than `buf.len` arguments only the first `buf.len` // are returned — call `os_argc()` first and size `buf` accordingly when an // exact count matters. os_args :: (buf: []string) -> []string { inline if OS == { case .macos: { argc := cast(s64) ns_get_argc().*; argv : [*]s64 = xx ns_get_argv().*; n := if argc > buf.len then buf.len else argc; i := 0; while i < n { cstr : [*]u8 = xx argv[i]; len := 0; while cstr[len] != 0 { len += 1; } buf[i] = string.{ ptr = cstr, len = len }; i += 1; } result : []string = ---; result.ptr = buf.ptr; result.len = n; return result; } else: { out("std.cli: unsupported platform — only macOS is implemented (needs _NSGetArgv/_NSGetArgc).\n"); cli_bail_exit(70); } } }