boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs

commit 592d347926f1abf565a5cd11a360a18a3a645432
parent 2dfba4fcce1a94138f06d44d816896ca76f89065
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 23 Apr 2026 20:09:14 -0700

shell.scm

Diffstat:
Alisp/shell.scm | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 201 insertions(+), 0 deletions(-)

diff --git a/lisp/shell.scm b/lisp/shell.scm @@ -0,0 +1,201 @@ +;; Shell-style Scheme library: processes and file I/O. +;; +;; Primitives required from the runtime: +;; (sys-exit code) +;; (sys-clone) → (#t . pid) parent, (#t . 0) child, +;; (#f . errno) on failure +;; (sys-execve path argv) → only returns (#f . errno) on failure; +;; parent env is inherited by the child +;; (sys-wait pid) → (ok . raw-wstatus) +;; (sys-openat dirfd path flags mode) → (ok . fd) +;; (sys-close fd) → (ok . _) +;; (sys-read fd buf count) → (ok . nread), fills buf[0..nread] +;; (sys-write fd buf count) → (ok . nwritten), writes buf[0..count] +;; (sys-argv) → list of bytevectors (argv[0..]); +;; cannot fail, not wrapped in (ok . val) +;; +;; All wrappers that touch syscalls return a pair (ok . val): +;; ok = #t → val is the result +;; ok = #f → val is an errno + +;; ============================================================ +;; processes +;; ============================================================ + +(define (spawn prog . args) + (let ((r (sys-clone))) + (cond + ((not (car r)) r) + ((zero? (cdr r)) + (sys-execve prog (cons prog args)) + (sys-exit 127)) + (else r)))) + +(define (wait pid) + (let ((r (sys-wait pid))) + (if (car r) + (cons #t (decode-wait-status (cdr r))) + r))) + +(define (run prog . args) + (let ((r (apply spawn prog args))) + (if (car r) (wait (cdr r)) r))) + +(define (exit . rest) + (sys-exit (if (null? rest) 0 (car rest)))) + +(define (argv) (sys-argv)) + +;; POSIX wstatus → shell-style code (128+sig for signal termination). +(define (decode-wait-status s) + (let ((termsig (bit-and s #x7f))) + (if (zero? termsig) + (bit-and (arithmetic-shift s -8) #xff) + (+ 128 termsig)))) + +;; ============================================================ +;; file I/O +;; ============================================================ + +(define BUFSIZE 4096) +(define AT_FDCWD -100) +(define O_RDONLY 0) +(define O_WRONLY 1) +(define O_CREAT #x40) ; 0o100 +(define O_TRUNC #x200) ; 0o1000 +(define O_APPEND #x400) ; 0o2000 +(define MODE_644 #x1a4) ; 0o644 +(define NL-BYTE 10) +(define NL-BV (make-bytevector 1 10)) + +(define-record-type port + (%port fd buf pos end) + port? + (fd port-fd) + (buf port-buf) + (pos port-pos port-pos-set!) + (end port-end port-end-set!)) + +(define stdin (%port 0 (make-bytevector BUFSIZE) 0 0)) +(define stdout (%port 1 #f 0 0)) +(define stderr (%port 2 #f 0 0)) + +(define (open-input path) + (let ((r (sys-openat AT_FDCWD path O_RDONLY 0))) + (if (car r) + (cons #t (%port (cdr r) (make-bytevector BUFSIZE) 0 0)) + r))) + +(define (open-output path) + (let ((r (sys-openat AT_FDCWD path + (bit-or O_WRONLY O_CREAT O_TRUNC) MODE_644))) + (if (car r) (cons #t (%port (cdr r) #f 0 0)) r))) + +(define (open-append path) + (let ((r (sys-openat AT_FDCWD path + (bit-or O_WRONLY O_CREAT O_APPEND) MODE_644))) + (if (car r) (cons #t (%port (cdr r) #f 0 0)) r))) + +(define (close p) (sys-close (port-fd p))) + +;; ----- reads ----- + +(define (refill! p) + (let ((r (sys-read (port-fd p) (port-buf p) BUFSIZE))) + (cond + ((not (car r)) r) + (else (port-pos-set! p 0) + (port-end-set! p (cdr r)) + r)))) + +(define (read-bytes p n) + (let ((out (make-bytevector n))) + (let loop ((i 0)) + (cond + ((= i n) (cons #t out)) + ((< (port-pos p) (port-end p)) + (let* ((avail (- (port-end p) (port-pos p))) + (take (if (< avail (- n i)) avail (- n i)))) + (bytevector-copy! out i (port-buf p) (port-pos p) take) + (port-pos-set! p (+ (port-pos p) take)) + (loop (+ i take)))) + (else + (let ((r (refill! p))) + (cond + ((not (car r)) r) + ((zero? (cdr r)) + (cons #t (if (zero? i) (eof-object) (bytevector-copy out 0 i)))) + (else (loop i))))))))) + +(define (read-line p) + (let loop ((acc '())) + (cond + ((< (port-pos p) (port-end p)) + (let* ((buf (port-buf p)) + (start (port-pos p)) + (end (port-end p))) + (let scan ((i start)) + (cond + ((= i end) + (port-pos-set! p i) + (loop (cons (bytevector-copy buf start i) acc))) + ((= (bytevector-u8-ref buf i) NL-BYTE) + (port-pos-set! p (+ i 1)) + (cons #t (bv-concat-reverse + (cons (bytevector-copy buf start i) acc)))) + (else (scan (+ i 1))))))) + (else + (let ((r (refill! p))) + (cond + ((not (car r)) r) + ((zero? (cdr r)) + (cons #t (if (null? acc) (eof-object) (bv-concat-reverse acc)))) + (else (loop acc)))))))) + +(define (read-all p) + (let loop ((acc '())) + (cond + ((< (port-pos p) (port-end p)) + (let ((chunk (bytevector-copy (port-buf p) + (port-pos p) (port-end p)))) + (port-pos-set! p (port-end p)) + (loop (cons chunk acc)))) + (else + (let ((r (refill! p))) + (cond + ((not (car r)) r) + ((zero? (cdr r)) (cons #t (bv-concat-reverse acc))) + (else (loop acc)))))))) + +(define (bv-concat-reverse chunks) + (let* ((xs (reverse chunks)) + (total (let sum ((ys xs) (n 0)) + (if (null? ys) n + (sum (cdr ys) (+ n (bytevector-length (car ys))))))) + (out (make-bytevector total))) + (let loop ((ys xs) (i 0)) + (if (null? ys) + out + (let ((len (bytevector-length (car ys)))) + (bytevector-copy! out i (car ys) 0 len) + (loop (cdr ys) (+ i len))))))) + +;; ----- writes (unbuffered; handle partial writes) ----- + +(define (write-bytes p bv) + (let loop ((bv bv) (total 0)) + (let ((len (bytevector-length bv))) + (if (zero? len) + (cons #t total) + (let ((r (sys-write (port-fd p) bv len))) + (cond + ((not (car r)) r) + ((= (cdr r) len) (cons #t (+ total len))) + (else (loop (bytevector-copy bv (cdr r) len) + (+ total (cdr r)))))))))) + +(define write-string write-bytes) + +(define (write-line p s) + (let ((r (write-bytes p s))) + (if (car r) (write-bytes p NL-BV) r)))