ref: 12c9d2fc728b51aa1eb9a70d0d331eb9464912d9
dir: /src/iostream.c/
#include "sl.h"
#include "cvalues.h"
#include "types.h"
#include "print.h"
#include "read.h"
#include "iostream.h"
static sl_v sl_linesym, sl_blocksym, sl_memorysym, sl_nonesym;
static sl_type *sl_iostreamtype;
static void
print_iostream(sl_v v, sl_ios *f)
{
sl_ios *s = value2c(sl_ios*, v);
sl_print_str(f, "#<io stream");
if(*s->loc.filename){
sl_print_chr(f, ' ');
sl_print_str(f, s->loc.filename);
}
sl_print_chr(f, '>');
}
static void
free_iostream(sl_v self)
{
sl_ios *s = value2c(sl_ios*, self);
ios_close(s);
ios_free(s);
}
static void
relocate_iostream(sl_v oldv, sl_v newv)
{
sl_ios *olds = value2c(sl_ios*, oldv);
sl_ios *news = value2c(sl_ios*, newv);
if(news->buf == &olds->local[0])
news->buf = &news->local[0];
}
static sl_cvtable iostream_vtable = {
print_iostream,
relocate_iostream,
free_iostream,
nil
};
int
isiostream(sl_v v)
{
return iscvalue(v) && cv_class(ptr(v)) == sl_iostreamtype;
}
sl_purefn
BUILTIN("iostream?", iostreamp)
{
argcount(nargs, 1);
return isiostream(args[0]) ? sl_t : sl_nil;
}
sl_purefn
BUILTIN("eof-object?", eof_objectp)
{
argcount(nargs, 1);
return args[0] == sl_eof ? sl_t : sl_nil;
}
sl_purefn
sl_ios *
toiostream(sl_v v)
{
if(sl_unlikely(!isiostream(v)))
type_error("iostream", v);
return value2c(sl_ios*, v);
}
BUILTIN("file", file)
{
if(nargs < 1)
argcount(nargs, 1);
bool r = false, w = false, c = false, t = false, a = false;
for(int i = 1; i < nargs; i++){
if(args[i] == sl_rdsym)
r = 1;
else if(args[i] == sl_wrsym)
w = 1;
else if(args[i] == sl_apsym)
a = w = 1;
else if(args[i] == sl_crsym)
c = w = 1;
else if(args[i] == sl_truncsym)
t = w = 1;
}
if(!r && !w && !c && !t && !a)
r = true; // default to reading
sl_v f = cvalue(sl_iostreamtype, sizeof(sl_ios));
char *fname = tostring(args[0]);
sl_ios *s = value2c(sl_ios*, f);
if(ios_file(s, fname, r, w, c, t) == nil)
lerrorf(sl_errio, "could not open \"%s\"", fname);
if(a)
ios_seek_end(s);
return f;
}
BUILTIN("io-buffer-mode", io_buffer_mode)
{
if(nargs < 1 || nargs > 2)
argcount(nargs, 1);
sl_ios *s = toiostream(args[0]);
if(nargs == 1){
switch(s->bm){
case bm_none: return sl_nonesym;
case bm_line: return sl_linesym;
case bm_block: return sl_blocksym;
case bm_mem: return sl_memorysym;
}
assert("impossible" == nil);
}
sl_v a = args[1];
int bm = -1;
if(a == sl_nonesym)
bm = bm_none;
else if(a == sl_linesym)
bm = bm_line;
else if(a == sl_blocksym)
bm = bm_block;
else if(a == sl_memorysym)
bm = bm_mem;
if(bm < 0 || ios_bufmode(s, bm) != 0)
lerrorf(sl_errarg, "invalid buffer mode");
return sl_void;
}
BUILTIN("buffer", buffer)
{
argcount(nargs, 0);
USED(args);
sl_v f = cvalue(sl_iostreamtype, sizeof(sl_ios));
sl_ios *s = value2c(sl_ios*, f);
if(ios_mem(s, 0) == nil)
lerrorf(sl_errmem, "could not allocate stream");
return f;
}
BUILTIN("read", read)
{
if(nargs > 1)
argcount(nargs, 1);
sl_v a = nargs == 0 ? symbol_value(sl_instrsym) : args[0];
sl_gc_handle(&a);
sl_v v = sl_read_sexpr(a);
sl_free_gc_handles(1);
return ios_eof(toiostream(a)) ? sl_eof : v;
}
BUILTIN("io-getc", io_getc)
{
argcount(nargs, 1);
sl_ios *s = toiostream(args[0]);
Rune r;
int res;
if((res = ios_getutf8(s, &r)) == IOS_EOF)
//lerrorf(sl_errio, "end of file reached");
return sl_eof;
if(res == 0)
lerrorf(sl_errio, "invalid UTF-8 sequence");
return mk_rune(r);
}
BUILTIN("io-wait", io_wait)
{
if(nargs > 2)
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
int r = ios_wait(s, nargs > 1 ? todouble(args[1]) : -1);
if(r >= 0)
return r ? sl_t : sl_nil;
if(r == IOS_EOF)
return sl_eof;
lerrorf(sl_errio, "i/o error");
}
BUILTIN("io-putc", io_putc)
{
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
sl_cprim *cp = ptr(args[1]);
if(!iscprim(args[1]) || cp_class(cp) != sl_runetype)
type_error("rune", args[1]);
Rune r = *(Rune*)cp_data(cp);
return fixnum(ios_pututf8(s, r));
}
BUILTIN("io-skip", io_skip)
{
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
soffset off = tooffset(args[1]);
soffset res = ios_skip(s, off);
if(res < 0)
return sl_nil;
return sizeof(res) == sizeof(s64int) ? mk_s64(res) : mk_s32(res);
}
BUILTIN("io-flush", io_flush)
{
argcount(nargs, 1);
return ios_flush(toiostream(args[0])) == 0 ? sl_t : sl_nil;
}
BUILTIN("io-close", io_close)
{
argcount(nargs, 1);
ios_close(toiostream(args[0]));
return sl_void;
}
BUILTIN("io-truncate", io_truncate)
{
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
if(ios_trunc(s, tooffset(args[1])) < 0)
lerrorf(sl_errio, "truncation failed");
return sl_void;
}
BUILTIN("io-discardbuffer", io_discardbuffer)
{
argcount(nargs, 1);
ios_purge(toiostream(args[0]));
return sl_void;
}
sl_purefn
BUILTIN("io-eof?", io_eofp)
{
argcount(nargs, 1);
return ios_eof(toiostream(args[0])) ? sl_t : sl_nil;
}
BUILTIN("io-seek", io_seek)
{
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
usize pos = tosize(args[1]);
soffset res = ios_seek(s, (soffset)pos);
if(res < 0)
return sl_nil;
return sl_t;
}
BUILTIN("io-pos", io_pos)
{
argcount(nargs, 1);
sl_ios *s = toiostream(args[0]);
soffset res = ios_pos(s);
if(res < 0)
return sl_nil;
return size_wrap((usize)res);
}
BUILTIN("write", write)
{
if(nargs < 1 || nargs > 2)
argcount(nargs, 1);
sl_ios *s;
s = nargs == 2 ? toiostream(args[1]) : toiostream(symbol_value(sl_outstrsym));
sl_print(s, args[0]);
return args[0];
}
BUILTIN("io-read", io_read)
{
if(nargs != 3)
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
usize n;
sl_type *ft;
if(nargs == 3){
// form (io.read s type count)
ft = get_array_type(args[1]);
n = tosize(args[2]) * ft->elsz;
}else{
ft = get_type(args[1]);
if(ft->eltype != nil && !iscons(cdr_(cdr_(args[1]))))
lerrorf(sl_errarg, "incomplete type");
n = ft->size;
}
sl_v cv = cvalue(ft, n);
u8int *data = cptr(cv);
usize got = ios_read(s, data, n);
if(got < n)
//lerrorf(sl_errio, "end of input reached");
return sl_eof;
return cv;
}
// args must contain data[, offset[, count]]
static void
get_start_count_args(sl_v *args, u32int nargs, usize sz, usize *offs, usize *nb)
{
if(nargs > 1){
*offs = tosize(args[1]);
*nb = nargs > 2 ? tosize(args[2]) : sz - *offs;
if(*offs >= sz || *offs + *nb > sz)
bounds_error(args[0], args[1]);
}
}
BUILTIN("io-write", io_write)
{
if(nargs < 2 || nargs > 4)
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
sl_v v = args[1];
sl_cprim *cp = ptr(v);
if(iscprim(args[1]) && cp_class(cp) == sl_runetype){
if(nargs > 2)
lerrorf(sl_errarg, "offset argument not supported for characters");
Rune r = *(Rune*)cp_data(ptr(args[1]));
return fixnum(ios_pututf8(s, r));
}
u8int *data;
usize sz, offs = 0;
to_sized_ptr(v, &data, &sz);
usize nb = sz;
if(nargs > 2){
get_start_count_args(args+1, nargs-1, sz, &offs, &nb);
data += offs;
}
return size_wrap(ios_write(s, data, nb));
}
static u8int
get_delim_arg(sl_v arg)
{
usize uldelim = tosize(arg);
if(uldelim > 0x7f){
// runes > 0x7f, or anything else > 0xff, are out of range
if((iscprim(arg) && cp_class(ptr(arg)) == sl_runetype) || uldelim > 0xff)
lerrorf(sl_errarg, "delimiter out of range");
}
return (u8int)uldelim;
}
BUILTIN("io-readuntil", io_readuntil)
{
argcount(nargs, 2);
sl_v str = cvalue_string(80);
csl_v *cv = ptr(str);
u8int *data = cv_data(cv);
sl_ios dest;
ios_mem(&dest, 0);
ios_setbuf(&dest, data, 80, 0);
u8int delim = get_delim_arg(args[1]);
sl_ios *src = toiostream(args[0]);
usize n = ios_copyuntil(&dest, src, delim);
cv->len = n;
if(dest.buf != data){
// outgrew initial space
usize sz;
cv->data = ios_takebuf(&dest, &sz);
cv_autorelease(cv);
}else{
((u8int*)cv->data)[n] = 0;
}
if(n == 0 && ios_eof(src))
return sl_eof;
return str;
}
BUILTIN("io-copyuntil", io_copyuntil)
{
argcount(nargs, 3);
sl_ios *dest = toiostream(args[0]);
sl_ios *src = toiostream(args[1]);
u8int delim = get_delim_arg(args[2]);
return size_wrap(ios_copyuntil(dest, src, delim));
}
BUILTIN("io-copy", io_copy)
{
if(nargs < 2 || nargs > 3)
argcount(nargs, 2);
sl_ios *dest = toiostream(args[0]);
sl_ios *src = toiostream(args[1]);
if(nargs == 3)
return size_wrap(ios_copy(dest, src, tosize(args[2])));
return size_wrap(ios_copyall(dest, src));
}
BUILTIN("io-filename", io_filename)
{
argcount(nargs, 1);
return string_from_cstr(toiostream(args[0])->loc.filename);
}
BUILTIN("io-set-filename!", io_set_filename)
{
argcount(nargs, 2);
sl_ios *s = toiostream(args[0]);
char *f = tostring(args[1]);
MEM_FREE(s->loc.filename);
s->loc.filename = MEM_STRDUP(f);
return args[1];
}
BUILTIN("io-line", io_line)
{
argcount(nargs, 1);
return size_wrap(toiostream(args[0])->loc.lineno);
}
BUILTIN("io-set-line!", io_set_line)
{
argcount(nargs, 2);
toiostream(args[0])->loc.lineno = tosize(args[1]);
return args[1];
}
BUILTIN("io-column", io_column)
{
argcount(nargs, 1);
return size_wrap(toiostream(args[0])->loc.colno);
}
BUILTIN("io-set-column!", io_set_column)
{
argcount(nargs, 2);
toiostream(args[0])->loc.colno = tosize(args[1]);
return args[1];
}
sl_v
stream_to_string(sl_v *ps)
{
sl_v str;
usize n;
sl_ios *st = value2c(sl_ios*, *ps);
if(st->buf == &st->local[0]){
n = st->size;
str = cvalue_string(n);
memcpy(cvalue_data(str), st->buf, n);
ios_trunc(st, 0);
}else{
u8int *b = ios_takebuf(st, &n); n--;
if(n == 0)
return sl_emptystr;
b[n] = '\0';
str = cvalue_from_ref(sl_stringtype, b, n);
cv_autorelease(ptr(str));
}
return str;
}
BUILTIN("iostream->string", io_tostring)
{
argcount(nargs, 1);
sl_ios *src = toiostream(args[0]);
if(src->bm != bm_mem)
lerrorf(sl_errarg, "requires memory stream");
bool eof = ios_eof(src);
sl_v v = stream_to_string(&args[0]);
if(eof && v == sl_emptystr)
v = sl_eof;
return v;
}
void
iostream_init(void)
{
sl_iostreamsym = csymbol("iostream");
sl_rdsym = csymbol(":read");
sl_wrsym = csymbol(":write");
sl_apsym = csymbol(":append");
sl_crsym = csymbol(":create");
sl_truncsym = csymbol(":truncate");
sl_nonesym = csymbol(":none");
sl_linesym = csymbol(":line");
sl_blocksym = csymbol(":block");
sl_memorysym = csymbol(":memory");
sl_instrsym = csymbol("*input-stream*");
sl_outstrsym = csymbol("*output-stream*");
sl_iostreamtype = define_opaque_type(sl_iostreamsym, sizeof(sl_ios), &iostream_vtable, nil);
set(csymbol("*stdout*"), cvalue_from_ref(sl_iostreamtype, ios_stdout, sizeof(sl_ios)));
set(csymbol("*stderr*"), cvalue_from_ref(sl_iostreamtype, ios_stderr, sizeof(sl_ios)));
set(csymbol("*stdin*"), cvalue_from_ref(sl_iostreamtype, ios_stdin, sizeof(sl_ios)));
}