Add library/modules/std/cli.sx: a pure-sx command-line argument accessor backed by the macOS C runtime (_NSGetArgv/_NSGetArgc), no compiler change. os_argc() -> s64 os_args(buf: []string) -> []string Zero heap, zero per-arg allocation: os_args fills a caller-provided buffer (stack array) with string VIEWS over the process's own argv block, which lives for the whole process. The returned slice header is a by-value stack return; nothing touches context.allocator. Documents the `sx run` reality: under `sx run <prog.sx> ...` the process argv is the interpreter's argv (sx, run, prog.sx, ...), not a program's logical args. This accessor reports the real process argv truthfully; mapping to logical args is a later consumer concern (distribution P3.1). Non-macOS platforms bail loudly (message + _exit) rather than returning a silent empty. examples/0716-modules-cli-argv.sx asserts only deterministic structural invariants (argc >= 1, argv[0] non-empty, os_argc() == filled length).
95 lines
4.2 KiB
Plaintext
95 lines
4.2 KiB
Plaintext
// =====================================================================
|
|
// 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 <prog.sx> ...` 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);
|
|
}
|
|
}
|
|
}
|