7spw.ha (2382B)
- use encoding::utf8;
- use io;
- use types::c;
- @symbol("close") fn close(int) int;
- @symbol("dup") fn dup(int) int;
- @symbol("dup2") fn dup2(int, int) int;
- @symbol("lseek") fn lseek(int, io::off, int) io::off;
- @symbol("mkstemp") fn mkstemp(*c::char) int;
- @symbol("perror") fn perror(nullable *const c::char) void;
- @symbol("read") fn read(int, *opaque, size) c::ssize;
- @symbol("unlink") fn unlink(*const c::char) int;
- @symbol("write") fn write(int, *const opaque, size) c::ssize;
- type error = !str;
- def STDOUT_FILENO: int = 1;
- def STDERR_FILENO: int = 2;
- let stderr = -1;
- let tmpfd = -1;
- let tmpname: [_]u8 = ['/', 't', 'm', 'p', '/', 'X', 'X', 'X', 'X', 'X', 'X', '\0'];
- @init fn tmp() void = {
- stderr = dup(STDERR_FILENO);
- assert(stderr != -1);
- tmpfd = mkstemp(tmpname[..]: *[*]u8: *c::char);
- assert(tmpfd != -1);
- // libc wrapper functions will set stderr back to actual stderr before
- // performing any operation on it
- // (in this demo i didn't bother with this, but like hypothetically)
- const n = dup2(tmpfd, STDERR_FILENO);
- assert(n != -1);
- };
- @fini fn tmp() void = {
- if (tmpfd != -1) {
- close(tmpfd);
- unlink(tmpname[..]: *[*]u8: *const c::char);
- };
- };
- export fn main() void = {
- // set errno to EBADF to demonstrate
- const n = close(-1);
- assert(n == -1);
- const err = errno();
- write(STDOUT_FILENO, *(&err: **const opaque), len(err));
- write(STDOUT_FILENO, &10u8, 1);
- };
- fn errno() error = {
- perror(null);
- if (write(STDERR_FILENO, &0u8, 1) == -1) {
- return "Unknown error";
- };
- // we don't know the value of SEEK_SET, so iterate from 0 to 2 and check
- // which one has expected seek behavior
- // technically the value may not be from 0 to 2, but if that's the case
- // then eh just abort, i think this is a reasonable assumption to make
- let i = 0;
- for (i < 2; i += 1) {
- if (lseek(STDERR_FILENO, 0, i) == 0) {
- break;
- };
- };
- assert(i < 2);
- defer {
- // seek to beginning of file when function returns, so next time
- // errno() is called the position is already set (necessary
- // since lseek may overwrite errno)
- lseek(STDERR_FILENO, 0, i);
- };
- static let buf: [1024]u8 = [0...];
- const n = read(STDERR_FILENO, &buf, len(buf));
- if (n == -1) {
- return "Unknown error";
- };
- const n = c::strlen(buf[..]: *[*]u8: *const c::char);
- if (n == 0 || !utf8::valid(buf[..n - 1])) {
- return "Unknown error";
- };
- return *(&buf[..n - 1]: *str);
- };