diff --git a/src/builtins/sysfn.c b/src/builtins/sysfn.c index d5e2af1e..fe7a5896 100644 --- a/src/builtins/sysfn.c +++ b/src/builtins/sysfn.c @@ -1204,12 +1204,12 @@ B tFlush_c1(B t, B x) { } B tOutRaw_c1(B t, B x) { if (isAtm(x) || RNK(x)!=1) thrM("•term.OutRaw: 𝕩 must be a list"); - file_wBytes(stdout, bi_N, x); + file_wBytes(stdout, "stdout", x); return x; } B tErrRaw_c1(B t, B x) { if (isAtm(x) || RNK(x)!=1) thrM("•term.ErrRaw: 𝕩 must be a list"); - file_wBytes(stderr, bi_N, x); + file_wBytes(stderr, "stderr", x); return x; } diff --git a/src/utils/file.c b/src/utils/file.c index edb6af93..a90f257e 100644 --- a/src/utils/file.c +++ b/src/utils/file.c @@ -217,37 +217,44 @@ void free_chars(CharBuf b) { if (b.alloc) TFREE(b.data); } -void path_wChars(B path, B x) { // consumes path +static u64 file_openWriteClose(B path, char* data, u64 len) { FILE* f = file_open(path, "write to", "wb"); - + u64 written = fwrite(data, 1, len, f); + fclose(f); + return written; +} +static void file_checkWritten(B path, u64 written, u64 len) { + if (written != len) thrF("Error writing to file \"%R\"", path); +} + +void path_wChars(B path, B x) { // consumes path u64 len = utf8lenB(x); TALLOC(char, val, len); toUTF8(x, val); - if (fwrite(val, 1, len, f) != len) { fclose(f); thrF("Error writing to file \"%R\"", path); } + u64 written = file_openWriteClose(path, val, len); TFREE(val); + file_checkWritten(path, written, len); + dec(path); - fclose(f); -} -void file_wBytes(FILE* f, B name, B x) { - u64 len = IA(x); - - CharBuf buf = get_chars(x); - - 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); - } - - free_chars(buf); } void path_wBytes(B path, B x) { // consumes path - FILE* f = file_open(path, "write to", "wb"); - file_wBytes(f, path, x); - fclose(f); + u64 len = IA(x); + CharBuf buf = get_chars(x); + + u64 written = file_openWriteClose(path, buf.data, len); + free_chars(buf); + file_checkWritten(path, written, len); + dec(path); } +void file_wBytes(FILE* f, char* name, B x) { + u64 len = IA(x); + CharBuf buf = get_chars(x); + if (fwrite(buf.data, 1, len, f) != len) thrF("Error writing data to %S", name); + free_chars(buf); +} B path_list(B path) { DIR* d = dir_open(path); struct dirent *c; diff --git a/src/utils/file.h b/src/utils/file.h index 30fef0cb..f515d1b0 100644 --- a/src/utils/file.h +++ b/src/utils/file.h @@ -24,7 +24,7 @@ bool path_remove(B path); // consumes void path_wChars(B path, B x); // consumes path void path_wBytes(B path, B x); // consumes path -void file_wBytes(FILE* file, B name, B x); // doesn't consume +void file_wBytes(FILE* file, char* name, B x); // doesn't consume B path_list(B path); // consumes char path_type(B path); // consumes; errors only if path isn't a string diff --git a/test/cases/system.bqn b/test/cases/system.bqn index ed0b0da3..c482e8bb 100644 --- a/test/cases/system.bqn +++ b/test/cases/system.bqn @@ -1,4 +1,4 @@ -# not tested here: •Out •Show •Exit •term •GetLine •Cmp •CurrentError •FFI •internal.HeapDump •file.MapBytes +# not tested here: •Out •Show •Exit •GetLine •Cmp •CurrentError •FFI •internal.HeapDump •file.MapBytes %DEF var V←•internal.Variation ⋄ LV←•internal.ListVariations ⋄ CLR←•internal.ClearRefs %DEF tvar %USE var ⋄ _tvar ← {F _𝕣 x: (CLR@) ⊢ {F 𝕩 V x}¨ LV 𝕩; w F _𝕣 x: (CLR@) ⊢ (LV 𝕨) {(𝕨 V w) F 𝕩 V x}⌜ LV 𝕩} @@ -62,7 +62,7 @@ f ← •ReBQN{repl⇐"none"} ⋄ !6≡•Type F "⇐" # files; tests are ordered! {•file.Exists 𝕩? ⊑•SH⟨"rmdir", •file.At 𝕩⟩; 0} "testdirNested" %% 0 -•file.Remove⍟•file.Exists¨ "testfile.bqn"‿"testfile2.bqn"‿"testfile3B.bqn" ⋄ 1 %% 1 +•file.Remove⍟•file.Exists¨ "testfile.bqn"‿"testfile2.bqn"‿"testfile3B.bqn"‿"badwrite" ⋄ 1 %% 1 •file.At "/a/b" %% "/a/b" ! (•file.At "a/b") ≡ •file.path •file.At "a/b" "a/b" •file.At "c/d" %% "a/b/c/d" @@ -85,6 +85,9 @@ f ← •ReBQN{repl⇐"none"} ⋄ !6≡•Type F "⇐" !"•file.Bytes: 𝕩 must be a list" % "testfile.bqn" •file.Bytes <'a' !"Path wasn't a list of characters" % 'a' •FChars "hi" !"Path wasn't a list of characters" % (4‿2⥊"testfile.bqn") •file.Chars "hi" +!"Expected character" % "badwrite"•FChars ""‿"" +!"Expected character" % "badwrite"•FLines ⟨⟨"foo"⟩⟩ +!"•file.Chars: 𝕩 must be a list of characters" % "badwrite"•FChars @ ! {𝕩≡•file.Name 𝕩 •FChars "abc"∾(@+10)∾"def𝕩"}"testfile.bqn" ! {𝕩≡•file.Name 𝕩 •FBytes •ToUTF8 "abc"∾(@+10)∾"def𝕩"}"testfile2.bqn" @@ -111,11 +114,15 @@ f ← •ReBQN{repl⇐"none"} ⋄ !6≡•Type F "⇐" •file.Remove "testfile.bqn" %% 1 •file.Remove "testfile2.bqn" %% 1 •file.Remove "testfile3B.bqn" %% 1 -"testfile.bqn"‿"testfile2.bqn"‿"testfile3B.bqn" ∊ •file.List "." %% 0‿0‿0 +"testfile.bqn"‿"testfile2.bqn"‿"testfile3B.bqn"‿"badwrite" ∊ •file.List "." %% 0‿0‿0‿0 !"Failed to resolve ""/dev/file that doesn't exist"": No such file or directory" % •file.RealPath "/dev/file that doesn't exist" !"Failed to resolve ""/dev/file that doesn't exist"": No such file or directory" % •Import "/dev/file that doesn't exist" +# some of •term +!"Expected character" % •term.OutRaw 2⥊{⇐} +!"•term.ErrRaw: 𝕩 must be a list" % •term.ErrRaw 2‿2⥊0 + # •ParseFloat & •Repr