code wiki / (root) / nx_shell_lib.nx

nx_shell_lib.nx source

↩ module page · 166 lines · 9011 B

1// nx_shell_lib.nx -- GENERAL SOVEREIGN SHELL CAPABILITY (the shared library for the Nishi shell toolchain). 2// Extracted (rule 15) so the gate (nx_shell) and the drivable tool (nx_shell_run) share ONE capability, not copies. 3// Pure library: no main. Imports only nx_syscalls (the kernel last-mile). NEVER-BRICK: spawns userspace, writes 0 4// persistent hardware state -> brick-safe by construction (cardinal 26). 5// UI g_puts/g_pn 6// STR sh_slen/sh_streq 7// R1 sh_tokenize (command line -> argv) 8// R3 sh_builtin (echo/cd/true/false in-process) 9// R4 sh_grep (substring) / sh_grep_stream (pipeline filter: fd0->fd1) / sh_run_capture (pipe-capture) 10// R2 sh_run (fork+execve+wait) / sh_exec_one (builtin-or-exec one command) 11// R5 sh_split_ops / sh_exec_line (; && ||) 12// R6/full sh_split_pipe / sh_stage_exec / sh_run_pipeline (N-stage |) / sh_exec_full (pipelines + control) 13import "nx_syscalls.nx" 14import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 15import "nx_regex_lib.nx" 16const K_MAGIC_65536: i64 = 65536 17 18func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 19// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 20// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 21// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 22// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 23func g_pn(v: i64) -> i64 { nxi_out(v); return 0 } 24func sh_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 25func sh_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]==(0 as u8) { return 1 } return 0 } 26 27func sh_tokenize(line: *u8, copy: *u8, argv: *i64, maxtok: i64) -> i64 { 28 var i: i64=0; var argc: i64=0; var intok: i64=0 29 while line[i]!=(0 as u8) { 30 let c: i64 = line[i] as i64 31 if c==32 { copy[i]=0 as u8; intok=0 } 32 else { copy[i]=c as u8; if intok==0 { if argc<maxtok { argv[argc]=(copy+i as i64) as i64; argc=argc+1 } intok=1 } } 33 i=i+1 34 } 35 copy[i]=0 as u8; argv[argc]=0; return argc 36} 37 38func sh_builtin(argv: *i64, argc: i64) -> i64 { 39 let cmd: *u8 = argv[0] as *u8 40 if sh_streq(cmd, "true" as *u8)==1 { return 0 } 41 if sh_streq(cmd, "false" as *u8)==1 { return 1 } 42 if sh_streq(cmd, "cd" as *u8)==1 { if argc>1 { sys_chdir(argv[1] as *u8) } return 0 } 43 if sh_streq(cmd, "echo" as *u8)==1 { var i: i64=1; while i<argc { g_puts(argv[i] as *u8); if i<argc-1 { g_puts(" " as *u8) } i=i+1 } g_puts("\n" as *u8); return 0 } 44 return 0-1 45} 46 47// grep is now REGEX-powered (nx_regex_lib): `needle` is a regex pattern (literals still match literally => backward-compatible) 48func sh_grep(buf: *u8, n: i64, needle: *u8) -> i64 { if sh_slen(needle)==0 { return 0 } return re_search(needle, buf, n) } 49func sh_grep_stream(pattern: *u8) -> i64 { 50 let buf: *u8 = sys_mmap(K_MAGIC_65536); var n: i64=0; var go: i64=1 51 while go==1 { let k: i64=sys_read(0, buf+n as i64, K_MAGIC_65536-n); if k<=0 { go=0 } else { n=n+k; if n>=K_MAGIC_65536 { go=0 } } } 52 var start: i64=0; var i: i64=0 53 while i<n { 54 if buf[i]==(10 as u8) { if sh_grep(buf+start as i64, i-start, pattern)==1 { sys_write(1, buf+start as i64, i-start+1) } start=i+1 } 55 i=i+1 56 } 57 if start<n { if sh_grep(buf+start as i64, n-start, pattern)==1 { sys_write(1, buf+start as i64, n-start) } } 58 return 0 59} 60 61func sh_run(path: *u8, argv: *i64) -> i64 { 62 let pid: i64 = sys_fork() 63 if pid==0 { sys_execve(path, argv, 0 as *i64); sys_exit(127); return 0 } 64 let st: *i64 = sys_mmap(8); sys_wait4(pid, st, 0); return wait_exit_code(st[0]) 65} 66func sh_run_capture(path: *u8, argv: *i64, out: *u8, cap: i64) -> i64 { 67 let pb: *i64 = sys_mmap(16); sys_pipe2(pb, 0) 68 let rfd: i64 = pb[0] & 0xFFFFFFFF; let wfd: i64 = (pb[0] >> 32) & 0xFFFFFFFF 69 let pid: i64 = sys_fork() 70 if pid==0 { sys_dup3(wfd, 1, 0); sys_close(rfd); sys_close(wfd); sys_execve(path, argv, 0 as *i64); sys_exit(127); return 0 } 71 sys_close(wfd) 72 var total: i64=0; var go: i64=1 73 while go==1 { let k: i64=sys_read(rfd, out+total as i64, cap-total); if k<=0 { go=0 } else { total=total+k; if total>=cap { go=0 } } } 74 sys_close(rfd); let st: *i64=sys_mmap(8); sys_wait4(pid, st, 0); return total 75} 76 77func sh_exec_one(cmdline: *u8) -> i64 { 78 let cp: *u8 = sys_mmap(256); let av: *i64 = sys_mmap(32*8) as *i64 79 let ac: i64 = sh_tokenize(cmdline, cp, av, 32) 80 if ac==0 { return 0 } 81 let b: i64 = sh_builtin(av, ac) 82 if b != (0-1) { return b } 83 return sh_run(av[0] as *u8, av) 84} 85 86func sh_split_ops(line: *u8, copy: *u8, segp: *i64, sego: *i64, maxseg: i64) -> i64 { 87 var i: i64=0; var segc: i64=1 88 segp[0]=copy as i64; sego[0]=0 89 while line[i]!=(0 as u8) { 90 let c: i64 = line[i] as i64 91 if c==38 { if (line[i+1] as i64)==38 { copy[i]=0 as u8; copy[i+1]=0 as u8; if segc<maxseg { segp[segc]=(copy+(i+2) as i64) as i64; sego[segc]=1; segc=segc+1 } i=i+2 } else { copy[i]=c as u8; i=i+1 } } 92 else { if c==124 { if (line[i+1] as i64)==124 { copy[i]=0 as u8; copy[i+1]=0 as u8; if segc<maxseg { segp[segc]=(copy+(i+2) as i64) as i64; sego[segc]=2; segc=segc+1 } i=i+2 } else { copy[i]=c as u8; i=i+1 } } 93 else { if c==59 { copy[i]=0 as u8; if segc<maxseg { segp[segc]=(copy+(i+1) as i64) as i64; sego[segc]=0; segc=segc+1 } i=i+1 } 94 else { copy[i]=c as u8; i=i+1 } } } 95 } 96 copy[i]=0 as u8; return segc 97} 98func sh_exec_line(line: *u8) -> i64 { 99 let copy: *u8 = sys_mmap(512); let segp: *i64 = sys_mmap(16*8) as *i64; let sego: *i64 = sys_mmap(16*8) as *i64 100 let nseg: i64 = sh_split_ops(line, copy, segp, sego, 16) 101 var prev: i64=0; var s: i64=0 102 while s<nseg { 103 let op: i64=sego[s]; var run: i64=1 104 if op==1 { if prev!=0 { run=0 } } 105 if op==2 { if prev==0 { run=0 } } 106 if run==1 { prev=sh_exec_one(segp[s] as *u8) } 107 s=s+1 108 } 109 return prev 110} 111 112// split a pipeline segment on single '|' into stage command-lines (NUL-terminated in `copy`). returns nstages. 113func sh_split_pipe(line: *u8, copy: *u8, stages: *i64, maxst: i64) -> i64 { 114 var i: i64=0; var nst: i64=1 115 stages[0]=copy as i64 116 while line[i]!=(0 as u8) { let c: i64=line[i] as i64; if c==124 { copy[i]=0 as u8; if nst<maxst { stages[nst]=(copy+(i+1) as i64) as i64; nst=nst+1 } } else { copy[i]=c as u8 } i=i+1 } 117 copy[i]=0 as u8; return nst 118} 119// run ONE pipeline stage in the current (child) process after stdin/stdout are wired; never returns (exits). 120func sh_stage_exec(stage_line: *u8) -> i64 { 121 let cp: *u8 = sys_mmap(256); let av: *i64 = sys_mmap(32*8) as *i64 122 let ac: i64 = sh_tokenize(stage_line, cp, av, 32) 123 if ac==0 { sys_exit(0); return 0 } 124 if sh_streq(av[0] as *u8, "grep" as *u8)==1 { sh_grep_stream(av[1] as *u8); sys_exit(0); return 0 } 125 let b: i64 = sh_builtin(av, ac); if b != (0-1) { sys_exit(b); return 0 } 126 sys_execve(av[0] as *u8, av, 0 as *i64); sys_exit(127); return 0 127} 128// run an N-stage pipeline (stages joined by '|'); stage1.stdout->stage2.stdin->... ; returns last stage exit code. 129func sh_run_pipeline(segment: *u8) -> i64 { 130 let copy: *u8 = sys_mmap(512); let stages: *i64 = sys_mmap(8*8) as *i64 131 let nst: i64 = sh_split_pipe(segment, copy, stages, 8) 132 if nst==1 { return sh_exec_one(stages[0] as *u8) } 133 let prd: *i64 = sys_mmap(8*8) as *i64; let pwr: *i64 = sys_mmap(8*8) as *i64 134 var p: i64=0 135 while p<nst-1 { let pb: *i64=sys_mmap(16); sys_pipe2(pb,0); prd[p]=pb[0]&0xFFFFFFFF; pwr[p]=(pb[0]>>32)&0xFFFFFFFF; p=p+1 } 136 let pids: *i64 = sys_mmap(8*8) as *i64 137 var i: i64=0 138 while i<nst { 139 let pid: i64=sys_fork() 140 if pid==0 { 141 if i>0 { sys_dup3(prd[i-1],0,0) } 142 if i<nst-1 { sys_dup3(pwr[i],1,0) } 143 var c: i64=0; while c<nst-1 { sys_close(prd[c]); sys_close(pwr[c]); c=c+1 } 144 sh_stage_exec(stages[i] as *u8); sys_exit(127); return 0 145 } 146 pids[i]=pid; i=i+1 147 } 148 var c2: i64=0; while c2<nst-1 { sys_close(prd[c2]); sys_close(pwr[c2]); c2=c2+1 } 149 var last: i64=0; var w: i64=0 150 while w<nst { let st: *i64=sys_mmap(8); sys_wait4(pids[w], st, 0); if w==nst-1 { last=wait_exit_code(st[0]) } w=w+1 } 151 return last 152} 153// the COMPLETE shell-line executor: control flow (; && ||) where each segment is itself a pipeline (|). 154func sh_exec_full(line: *u8) -> i64 { 155 let copy: *u8 = sys_mmap(512); let segp: *i64 = sys_mmap(16*8) as *i64; let sego: *i64 = sys_mmap(16*8) as *i64 156 let nseg: i64 = sh_split_ops(line, copy, segp, sego, 16) 157 var prev: i64=0; var s: i64=0 158 while s<nseg { 159 let op: i64=sego[s]; var run: i64=1 160 if op==1 { if prev!=0 { run=0 } } 161 if op==2 { if prev==0 { run=0 } } 162 if run==1 { prev=sh_run_pipeline(segp[s] as *u8) } 163 s=s+1 164 } 165 return prev 166}