Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/app/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,14 @@ int main(int argc, char** argv) {
}

/* Expose the full command line to Rayfall via (.sys.args). */
ray_runtime_set_sys_args(ray_build_sys_args(argc, argv));
ray_t* sys_args = ray_build_sys_args(argc, argv);
if (!sys_args || RAY_IS_ERR(sys_args)) {
fprintf(stderr, "error: invalid command-line arguments\n");
if (sys_args && RAY_IS_ERR(sys_args)) ray_error_free(sys_args);
ray_runtime_destroy(rt);
return 2;
}
ray_runtime_set_sys_args(sys_args);

/* Initialise the worker pool before anything else that might use it
* (file load, REPL eval, builtins). If -c wasn't given, leave the
Expand Down
19 changes: 16 additions & 3 deletions src/lang/syscmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
void* ray_runtime_get_poll(void);

#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -79,9 +81,20 @@ static int64_t arg_as_i64(ray_t* arg, int* err) {
int sign = 1;
if (i < len && (p[i] == '+' || p[i] == '-')) { if (p[i] == '-') sign = -1; i++; }
if (i >= len || p[i] < '0' || p[i] > '9') { *err = 1; return 0; }
int64_t v = 0;
while (i < len && p[i] >= '0' && p[i] <= '9') { v = v * 10 + (p[i] - '0'); i++; }
return sign * v;
uint64_t v = 0;
uint64_t limit = sign < 0 ? (uint64_t)INT64_MAX + 1u : (uint64_t)INT64_MAX;
while (i < len && p[i] >= '0' && p[i] <= '9') {
uint64_t digit = (uint64_t)(p[i] - '0');
if (v > (limit - digit) / 10u) { *err = 1; return 0; }
v = v * 10u + digit;
i++;
}
if (i != len) { *err = 1; return 0; }
if (sign < 0) {
if (v == (uint64_t)INT64_MAX + 1u) return INT64_MIN;
return -(int64_t)v;
}
return (int64_t)v;
}
*err = 1;
return 0;
Expand Down
53 changes: 47 additions & 6 deletions src/ops/system.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ void* ray_runtime_get_poll(void);
void ray_runtime_set_sys_args(void* dict);
void* ray_runtime_get_sys_args(void);
#include <time.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#if !defined(RAY_OS_WINDOWS)
#include <unistd.h>
#include <sys/wait.h> /* WIFEXITED/WEXITSTATUS — .sys.exec exit codes */
Expand All @@ -67,6 +69,25 @@ void* ray_runtime_get_sys_args(void);
#define RAY_PCLOSE(f) _pclose(f)
#endif

static int parse_sys_int_arg(const char* s, bool nonnegative, bool core_count,
int64_t* out) {
if (!s || !*s) return 0;
const unsigned char* p = (const unsigned char*)s;
if (!nonnegative && (*p == '+' || *p == '-')) p++;
if (!isdigit(*p)) return 0;
for (const unsigned char* q = p; *q; q++)
if (!isdigit(*q)) return 0;

char* end = NULL;
errno = 0;
long long v = strtoll(s, &end, 10);
if (errno == ERANGE || end == s || *end != '\0' ||
(nonnegative && v < 0) || (core_count && v > INT_MAX))
return 0;
*out = (int64_t)v;
return 1;
}

/* ══════════════════════════════════════════
* Serialization / storage
* ══════════════════════════════════════════ */
Expand Down Expand Up @@ -1553,13 +1574,33 @@ ray_t* ray_build_sys_args(int argc, char** argv) {
interactive = true;
else if ((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) && i + 1 < argc)
port = (int64_t)atoll(argv[++i]);
else if ((strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--cores") == 0) && i + 1 < argc) {
long long v = atoll(argv[++i]); if (v < 0) v = 0; cores = (int64_t)v;
else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--cores") == 0) {
if (i + 1 >= argc)
return ray_error("rank", ".sys.args: %s requires an argument", argv[i]);
int64_t v;
if (!parse_sys_int_arg(argv[++i], true, true, &v))
return ray_error("domain", ".sys.args: invalid %s value \"%s\"",
argv[i - 1], argv[i]);
cores = v;
}
else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--timeit") == 0) {
if (i + 1 >= argc)
return ray_error("rank", ".sys.args: %s requires an argument", argv[i]);
int64_t v;
if (!parse_sys_int_arg(argv[++i], false, false, &v))
return ray_error("domain", ".sys.args: invalid %s value \"%s\"",
argv[i - 1], argv[i]);
timeit = (v != 0);
}
else if (strcmp(argv[i], "-Q") == 0 || strcmp(argv[i], "--querylog") == 0) {
if (i + 1 >= argc)
return ray_error("rank", ".sys.args: %s requires an argument", argv[i]);
int64_t v;
if (!parse_sys_int_arg(argv[++i], false, false, &v))
return ray_error("domain", ".sys.args: invalid %s value \"%s\"",
argv[i - 1], argv[i]);
querylog = (v != 0);
}
else if ((strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--timeit") == 0) && i + 1 < argc)
timeit = (atoll(argv[++i]) != 0);
else if ((strcmp(argv[i], "-Q") == 0 || strcmp(argv[i], "--querylog") == 0) && i + 1 < argc)
querylog = (atoll(argv[++i]) != 0);
else if ((strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--file") == 0) && i + 1 < argc)
file = argv[++i];
else if (strcmp(argv[i], "-u") == 0 && i + 1 < argc) i++; /* skip secret */
Expand Down
10 changes: 5 additions & 5 deletions test/rfl/system/syscmd_coverage.rfl
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@
(.sys.cmd "timeit 100") -- 1
(.sys.cmd "timeit 0") -- 0

;; Empty-after-sign / non-digit start → type error (line 80).
;; Empty-after-sign / non-digit start / trailing garbage → type error.
(.sys.cmd "timeit +") !- type
(.sys.cmd "timeit -") !- type
(.sys.cmd "timeit abc") !- type
(.sys.cmd "timeit 1.5") -- 1
;; ^ "1.5" parses up to the dot → 1, profile turns on; the trailing
;; ".5" is not an error (the parser stops at first non-digit and
;; returns what it has). Reset state for any later toggle tests.
(.sys.cmd "timeit 1.5") !- type
(.sys.timeit "1abc") !- type
(.sys.timeit "9223372036854775808") !- type
;; Reset state for any later toggle tests.
(.sys.timeit 0) -- 0

;; ────────────── arg_as_i64: type-fallthrough (lines 85-86) ──────────────
Expand Down
17 changes: 16 additions & 1 deletion test/test_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,21 @@ static test_result_t test_build_sys_args_edges(void) {
PASS();
}

static test_result_t test_build_sys_args_rejects_malformed_numbers(void) {
char* cases[][3] = {
{ "rayforce", "-c", "2abc" },
{ "rayforce", "-c", "999999999999999999999999" },
{ "rayforce", "-t", "1abc" },
{ "rayforce", "-Q", "0abc" },
};
for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++) {
ray_t* d = ray_build_sys_args(3, cases[i]);
TEST_ASSERT_TRUE(RAY_IS_ERR(d));
ray_error_free(d);
}
PASS();
}

/* .sys.args builtin: only `source` when unset; reflects stored dict when set */
static test_result_t test_sys_args_builtin(void) {
/* unset → a dict holding just `source` (empty here: ray_eval_str is
Expand Down Expand Up @@ -1288,6 +1303,7 @@ const test_entry_t runtime_entries[] = {
{ "runtime/build_sys_args_defaults", test_build_sys_args_defaults, sys_setup, sys_teardown },
{ "runtime/build_sys_args_flags_user", test_build_sys_args_flags_and_user, sys_setup, sys_teardown },
{ "runtime/build_sys_args_edges", test_build_sys_args_edges, sys_setup, sys_teardown },
{ "runtime/build_sys_args_rejects_malformed_numbers", test_build_sys_args_rejects_malformed_numbers, sys_setup, sys_teardown },
{ "runtime/sys_args_builtin", test_sys_args_builtin, sys_setup, sys_teardown },
{ "runtime/syscov_rc", test_syscov_rc, sys_setup, sys_teardown },
{ "runtime/syscov_time_now", test_syscov_time_now, sys_setup, sys_teardown },
Expand Down Expand Up @@ -1315,4 +1331,3 @@ const test_entry_t runtime_entries[] = {

{ NULL, NULL, NULL, NULL },
};

Loading