diff options
| author | Cliff Click <cliffc@acm.org> | 2026-11-08 14:10:02 -0800 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-11-08 14:10:02 -0800 |
| commit | 06fb5aceeb65a75b1cd643debd667c52fdcafcb9 (patch) | |
| tree | c99813eaf27d98f840245302c4d6e923c964307e /chapter30/src/main/smp/sys/io.smp | |
| parent | 6560a92433a38825fd1d44d9c66ffba9f26990ff (diff) | |
| download | Simple-ch30.tar.gz Simple-ch30.zip | |
Better error messagesch30
Better error messages.
Buffs to the std lib; getch() to fetch a single character; streaming I/O handles retries, and large sizes, with better error reporting.
Formatted strings.
Default int moved to i32.
TicTacToe, the game.
Diffstat (limited to 'chapter30/src/main/smp/sys/io.smp')
| -rw-r--r-- | chapter30/src/main/smp/sys/io.smp | 75 |
1 files changed, 53 insertions, 22 deletions
diff --git a/chapter30/src/main/smp/sys/io.smp b/chapter30/src/main/smp/sys/io.smp index d6263ffe..14993429 100644 --- a/chapter30/src/main/smp/sys/io.smp +++ b/chapter30/src/main/smp/sys/io.smp @@ -4,43 +4,74 @@ // Write byte array to stdout val p = { u8[~] str -> write(1,str); }; val p_noInline = { u8[~] str -> write(1,str); }; +// Write to stderr +val err = { u8[~] str -> write(libc.STDERR,str); }; -// Return fd contents as a u8[]. + +// Read until EOF, including after short reads. Return an empty array for an +// empty stream, or null on error with errno preserved (partial data discarded). val read = { i32 fd -> - var buf = new u8[8*1024]; - i64 ptr = buf; - int len = 0; + val chunk = new u8[8*1024]; + i64 ptr = chunk; + val buf = new aryu8(); while( true ) { - i32 rez = libc.read(fd,ptr,buf#); - if( rez > buf# ) libc._exit(libc.errno()); - len += rez; - if( rez < buf# ) - break; - libc._exit(-2); // Need to implement for more than 8K input + i64 rez = libc.read(fd,ptr,chunk#); + if( rez < 0 ) { + if( libc.errno() == libc.EINTR ) continue; + return null; + } + if( rez == 0 ) break; + if( buf.len+rez > 4294967295 ) { + libc.set_errno(libc.EOVERFLOW); + return null; + } + u32 count = rez; + buf._grow(count); + ary.copy(buf.buf,buf.len,chunk,0,count); + buf.len += count; } - var out = new u8[len]; - ary.copy(out,0,buf,0,len); + val out = new u8[buf.len]; + return ary.copy(out,0,buf.buf,0,buf.len); }; -// Write all of str to fd. +// Write all bytes, returning the count or -1 with errno set. An error can +// follow a successfully written prefix; it does not undo that prefix. val write = { i32 fd, u8[~] str -> - i64 ptr = str; // Convert array pointer to a i64 for the native call - libc.write(fd,ptr,str#); + writeOff(fd,str,0,str#); }; -// Write all of str to fd. +// Write a slice, capping len at the remaining bytes. An offset past the end +// is invalid, including for an empty write. A valid empty slice returns zero. +// Invalid arguments return -1; val writeOff = { i32 fd, u8[~] str, u32 off, u32 len -> - i64 ptr = str; // Convert array pointer to a i64 for the native call - ptr += off; // Skip offset - // Cap at available length - if( len > str# - off ) len = str# - off; - // Do the write - libc.write(fd,ptr,len); + if( off > str# ) return libc.set_errno(libc.EINVAL); + if( len > str# - off ) len = str# - off; + i64 ptr = str; + ptr += off; + i64 done = 0; + while( done < len ) { + i64 rez = libc.write(fd,ptr+done,len-done); + if( rez < 0 ) { + if( libc.errno() == libc.EINTR ) continue; + return -1; + } + if( rez == 0 ) { + libc.set_errno(libc.EIO); + return -1; + } + done += rez; + } + return done; }; // Return stdin as a u8[]. val stdin = { -> read(0); }; +// One byte, without Enter or echo. Restores the previous terminal mode before +// returning. Files/pipes also work. Returns 0..255, or -1 with libc.errno() set +// to libc.EOF or an OS error. Ctrl-C is byte 3; escape/UTF-8 sequences are multiple bytes. +val getch = { -> libc.simple_getch(); }; + // Create file for writing. Return fd, or -1 and errno has the error code val create = { u8[~] str -> i64 ptr = sys.aryu8.str0(str); |
