#include "std/base.toc", base; #include "std/mem.toc", mem; str_to_cstr ::= fn(s: []char) []char { ret := mem.news(char, s.len+1); mem.mem_copy(&ret[0], &s[0], s.len); return ret; } // @TODO(eventually): enum FileError ::= int; FILE_ERR_OK ::= 0; FILE_ERR_MISC ::= 1; // @TODO: more of these /* the raw file interface: raw_stdout - standard output raw_stderr - standard error raw_file_write - write to a raw file raw_file_open_write - open a raw file for writing raw_file_close - close a raw file */ #if base.PLATFORM_IS_UNIX { // @TODO: use syscall instead (it'll allow other functions called write) write ::= #foreign("write", base.libc) fn(#C int, #C &"const void", #C size_t) #C long; open ::= #foreign("open", base.libc) fn (#C &"const char", #C int, #C unsigned) #C int; close ::= #foreign("close", base.libc) fn (#C int) #C int; DEFAULT_MODE ::= 0o644; O_RDONLY ::= 0x00; O_WRONLY ::= 0x01; O_CREAT ::= 0x40; RawFile ::= int; raw_file_write ::= fn(file: RawFile, buf: &void, size: int) err := FILE_ERR_OK { while size > 0 { bytes_written := write(file as #C int, buf, size as #C size_t); if bytes_written < 0 { err = FILE_ERR_MISC; // @TODO return; } size -= bytes_written; buf += bytes_written; } } raw_stdout ::= fn() RawFile { return 1; } raw_stderr ::= fn() RawFile { return 2; } raw_file_open_write ::= fn(name: []char) f: RawFile, err := FILE_ERR_OK { cstr := str_to_cstr(name); defer mem.dels(cstr); // @TODO: switch to bitwise or when that exists f = open(&cstr[0], (O_WRONLY + O_CREAT) as #C int, DEFAULT_MODE as #C unsigned) as RawFile; if f == -1 { err = FILE_ERR_MISC; // @TODO } } raw_file_close ::= fn(f: RawFile) err := FILE_ERR_OK { if close(f as #C int) == -1 { err = FILE_ERR_MISC; // @TODO } } } else { // @TODO: on windows, use WriteFile fwrite ::= #foreign("fwrite", base.libc) fn(#C &"const void", #C size_t, #C size_t, &void) #C size_t; RawFile ::= &void; raw_file_write ::= fn(file: RawFile, buf: &void, size: int) bool { bytes_written := fwrite(buf, 1, size, file) as int; return bytes_written == size; } raw_stdout ::= fn() RawFile { return #builtin("stdout"); } raw_stderr ::= fn() RawFile { return #builtin("stderr"); } raw_file_open_write ::= fn(name: []char) f: RawFile, err := FILE_ERR_OK { cstr := base.str_to_cstr(name); defer mem.dels(cstr); mode := "w\0"; f = fopen(&cstr[0], &mode[0]); if f == null { err = FILE_ERR_MISC; // @TODO } } raw_file_close ::= fn(f: RawFile) err := FILE_ERR_OK { if fclose(f) != 0 { err = FILE_ERR_MISC; // @TODO } } } File ::= struct { BUFSZ ::= 4096; raw : RawFile; buffer_used : int; // ranges from 0 to FILE_BUFSZ-1 nobuffer : bool; // if true, flush after every write buffer : [BUFSZ]char; } raw_file_to_file ::= fn(raw : RawFile, f: &File) { f.raw = raw; } std_out, std_err : File; fopen_write ::= fn(name: []char) f: &File, error: FileError { raw : RawFile; raw, error = raw_file_open_write(name); if !error { f = mem.new(File); raw_file_to_file(raw, f); } } flush ::= fn(use f: &File) err: FileError { err = raw_file_write(raw, &buffer[0], buffer_used); buffer_used = 0; } fclose ::= fn(f: &File) err: FileError { flush(f); err = raw_file_close(f.raw); mem.del(f); } fwrites ::= fn(use f: &File, s : []char) FileError { if f.nobuffer { return raw_file_write(raw, &s[0], s.len); } if buffer_used + s.len > BUFSZ { flush(f); if s.len > BUFSZ { return raw_file_write(raw, &s[0], s.len); } else { mem.mem_copy(&buffer[0], &s[0], s.len); buffer_used = s.len; return FILE_ERR_OK; } } else { mem.mem_copy(&buffer[buffer_used], &s[0], s.len); buffer_used += s.len; return FILE_ERR_OK; } } fputs ::= fn(f: &File, s: []char) err : FileError { err = fwrites(f, s); if !err { err = fwrites(f, "\n"); } } puts ::= fn(s: []char) FileError { return fputs(&std_out, s); } io_init ::= fn() { raw_file_to_file(raw_stdout(), &std_out); std_out.nobuffer = true; raw_file_to_file(raw_stderr(), &std_err); std_err.nobuffer = true; } #init io_init();