diff --git a/docs/system.md b/docs/system.md index 53b63c1a..8f77b99e 100644 --- a/docs/system.md +++ b/docs/system.md @@ -24,7 +24,7 @@ See [the BQN specification](https://mlochbaum.github.io/BQN/spec/system.html) fo | `•Repr` | | | `•Fmt` | | | `•term` | Fields: `Flush`, `RawMode`, `CharB`, `CharN`; has extensions | -| `•SH` | Left argument can be `{stdin⇐"input"}` | +| `•SH` | Left argument can be a namespace; `stdin⇐"input"` in it passes stdin, `raw⇐1` takes stdin & returns stdout/stderr as raw bytes represented as characters. These extensions may be changed/removed if different interfaces are standardized. | | `•Type` | | | `•Glyph` | | | `•Decompose` | | @@ -106,6 +106,6 @@ Namespace of various internal functions. May change at any time. # FFI -Currently, there is no support for structs, nested pointers, or constant-length arrays, aka the only supported types are scalars (e.g. `i8`, `u64`, `*`), pointers to those (e.g. `*i8`, `&u64`), and conversions of either of those (e.g. `u64:i32`, `**:c8`). +Currently, there is no support for structs, nested pointers, or constant-length arrays, aka the only supported types are scalars (e.g. `i8`, `u64`), pointers to those (e.g. `*i8`, `&u64`), and conversions of either those or opaque pointers (e.g. `u64:i32`, `*i64:i32`, `**:c8`). -Additionally, the `a` type maps to `BQNV` from [bqnffi.h](../include/bqnffi.h) (example usage in [FFI tests](../test/ffi/)). \ No newline at end of file +The `a` type maps to `BQNV` from [bqnffi.h](../include/bqnffi.h) (example usage in [FFI tests](../test/ffi/)). \ No newline at end of file diff --git a/src/builtins/sysfn.c b/src/builtins/sysfn.c index fabe65eb..bf0a2c92 100644 --- a/src/builtins/sysfn.c +++ b/src/builtins/sysfn.c @@ -858,12 +858,15 @@ B sh_c2(B t, B w, B x) { // parse options B inObj = bi_N; + bool raw = false; if (!q_N(w)) { if (!isNsp(w)) thrM("•SH: 𝕨 must be a namespace"); inObj = ns_getC(w, "stdin"); if (!q_N(inObj) && !isArr(inObj)) thrM("•SH: Invalid stdin value"); + B rawObj = ns_getC(w, "raw"); + if (!q_N(rawObj)) raw = o2b(rawObj); } - u64 iLen = q_N(inObj)? 0 : utf8lenB(inObj); + u64 iLen = q_N(inObj)? 0 : (raw? IA(inObj) : utf8lenB(inObj)); // allocate args if (isAtm(x) || RNK(x)>1) thrM("•SH: 𝕩 must be a vector of strings"); @@ -912,8 +915,20 @@ B sh_c2(B t, B w, B x) { // allocate stdin u64 iOff = 0; - TALLOC(char, iBuf, iLen); - if (iLen>0) toUTF8(inObj, iBuf); + char* iBuf; + CharBuf iBufRaw; + if (iLen>0) { + if (raw) { + iBufRaw = get_chars(inObj); + iBuf = iBufRaw.data; + } else { + TALLOC(char, iBufT, iLen); + toUTF8(inObj, iBufT); + iBuf = iBufT; + } + } else iBuf = NULL; + #define FREE_INPUT do { if (iLen>0) { if (raw) free_chars(iBufRaw); else TFREE(iBuf); } } while(0) + bool iDone = false; // allocate output buffer B s_out = emptyCVec(); @@ -944,7 +959,7 @@ B sh_c2(B t, B w, B x) { shDbg("written %zd/"N64u"\n", ww, iLen-iOff); if (ww >= 0) { iOff+= ww; - if (iOff==iLen) { iDone=true; shClose(p_in[1]); TFREE(iBuf); shDbg("writing done\n"); } + if (iOff==iLen) { iDone=true; shClose(p_in[1]); FREE_INPUT; shDbg("writing done\n"); } any = true; } } @@ -960,7 +975,7 @@ B sh_c2(B t, B w, B x) { assert(reusable(oBufObj)); mm_free(v(oBufObj)); // free our ends of pipes - if (!iDone) { shClose(p_in[1]); TFREE(iBuf); shDbg("only got to write "N64u"/"N64u"\n", iOff, iLen); } + if (!iDone) { shClose(p_in[1]); FREE_INPUT; shDbg("only got to write "N64u"/"N64u"\n", iOff, iLen); } shClose(p_out[0]); shClose(p_err[0]); @@ -970,8 +985,15 @@ B sh_c2(B t, B w, B x) { dec(w); dec(x); B s_outRaw = toC8Any(s_out); B s_errRaw = toC8Any(s_err); - B s_outObj = utf8Decode((char*)c8any_ptr(s_outRaw), IA(s_outRaw)); dec(s_outRaw); - B s_errObj = utf8Decode((char*)c8any_ptr(s_errRaw), IA(s_errRaw)); dec(s_errRaw); + B s_outObj; + B s_errObj; + if (raw) { + s_outObj = s_outRaw; + s_errObj = s_errRaw; + } else { + s_outObj = utf8Decode((char*)c8any_ptr(s_outRaw), IA(s_outRaw)); dec(s_outRaw); + s_errObj = utf8Decode((char*)c8any_ptr(s_errRaw), IA(s_errRaw)); dec(s_errRaw); + } return m_hVec3(m_i32(WEXITSTATUS(status)), s_outObj, s_errObj); } #else diff --git a/src/ns.h b/src/ns.h index 42684c86..fa15aaf7 100644 --- a/src/ns.h +++ b/src/ns.h @@ -18,7 +18,7 @@ B m_ns(Scope* sc, NSDesc* desc); // consumes both B ns_getU(B ns, i32 gid); // doesn't consume, doesn't increment result B ns_qgetU(B ns, i32 gid); // ns_getU but return bi_N on fail B ns_getNU(B ns, B name, bool thrEmpty); // doesn't consume anything, doesn't increment result; returns bi_N if doesn't exist and !thrEmpty -B ns_getC(B ns, char* name); // get namespace field by C string; returns bi_N if doesn't exist +B ns_getC(B ns, char* name); // get namespace field by C string; returns bi_N if doesn't exist, otherwise doesn't increment result like ns_getU void ns_set(B ns, B name, B val); // consumes val i32 pos2gid(Body* body, i32 pos); // converts a variable position to a gid; errors on special name variables diff --git a/src/utils/file.c b/src/utils/file.c index 17fdabea..41778090 100644 --- a/src/utils/file.c +++ b/src/utils/file.c @@ -165,6 +165,29 @@ B path_abs(B path) { #endif } +CharBuf get_chars(B x) { + char* buf; + bool alloc; + u64 len = IA(x); + u8 el = TI(x,elType); + if (el==el_i8 || el==el_c8) { + buf = tyany_ptr(x); + alloc = false; + } else { + TALLOC(char, val, len); + buf = val; + alloc = true; + SGetU(x) + for (u64 i = 0; i < len; i++) { + B c = GetU(x,i); + buf[i] = isNum(c)? o2iu(c) : o2c(c); + } + } + return (CharBuf){.data=buf, .alloc=alloc}; +} +void free_chars(CharBuf b) { + if (b.alloc) TFREE(b.data); +} void path_wChars(B path, B x) { // consumes path FILE* f = file_open(path, "write to", "w"); @@ -181,29 +204,14 @@ void path_wChars(B path, B x) { // consumes path void file_wBytes(FILE* f, B name, B x) { u64 len = IA(x); - bool newBuf = false; - char* buf; + CharBuf buf = get_chars(x); - u8 el = TI(x,elType); - if (el==el_i8 || el==el_c8) { - buf = tyany_ptr(x); - } else { - TALLOC(char, val, len); - buf = val; - newBuf = true; - SGetU(x) - for (u64 i = 0; i < len; i++) { - B c = GetU(x,i); - buf[i] = isNum(c)? o2iu(c) : o2c(c); - } - } - - if (fwrite(buf, 1, len, f) != len) { + if (fwrite(buf.data, 1, len, f) != len) { if (q_N(name)) thrM("Error writing to file"); else thrF("Error writing to file \"%R\"", name); } - if (newBuf) TFREE(buf); + free_chars(buf); } void path_wBytes(B path, B x) { // consumes path FILE* f = file_open(path, "write to", "w"); diff --git a/src/utils/file.h b/src/utils/file.h index 36cbe718..ec9d7cd5 100644 --- a/src/utils/file.h +++ b/src/utils/file.h @@ -13,6 +13,10 @@ B path_lines(B path); // consumes I8Arr* stream_bytes(FILE* f); +typedef struct { char* data; bool alloc; } CharBuf; +CharBuf get_chars(B x); // convert x to character data; expects x isn't freed before free_chars call. May error. +void free_chars(CharBuf b); // free the result of the above + B mmap_file(B path); // consumes bool dir_create(B path); // doesn't consume bool path_rename(B old_path, B new_path); // consumes only old_path