/* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #include "sqstdstream.h" #include "sqstdblobimpl.h" static const SQChar SQSTD_STREAM_TYPE_TAG[] = _SC("std_stream"); #define SETUP_STREAM(v) \ SQStream *self = NULL; \ if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_STREAM_TYPE_TAG))) \ return sq_throwerror(v,_SC("invalid type tag")); \ if(!self || !self->IsValid()) \ return sq_throwerror(v,_SC("the stream is invalid")); SQInteger _stream_read_line(HSQUIRRELVM v) { SETUP_STREAM(v); const SQChar nl = _SC('\n'); const SQChar rc = _SC('\r'); SQInteger size, read_size; bool new_line_found = false; //optional expected line size if(sq_gettop(v) > 1) sq_getinteger(v,2,&size); else size = 2048; SQBlob line_buf(0, size); while(!self->EOS()){ char *buf = (SQChar*)line_buf.GetBuf(); read_size = self->Gets(buf + line_buf.Len(), size); if(!read_size) //end of file { break; } if(buf[read_size-1] == nl) //complete line found { //remove '\r' and/or '\n' if( (read_size > 1) && (buf[read_size-2] == rc)) { read_size -= 2; } else { read_size -= 1; } line_buf.SetLen(read_size); new_line_found = true; break; } line_buf.SetLen(read_size); line_buf.GrowBufOf(size); } if(line_buf.Len() > 0) { sq_pushstring(v, (const SQChar*)line_buf.GetBuf(), line_buf.Len()); } else if(new_line_found) //empty line { sq_pushstring(v, "", 0); } else sq_pushnull(v); //end of file return 1; } SQInteger _stream_read(HSQUIRRELVM v) { SETUP_STREAM(v); SQChar *data; SQInteger size,res; sq_getinteger(v,2,&size); /* DAD come back here if(self->GetHandle() != stdin && size > self->Len()) { size = self->Len(); } */ data = sq_getscratchpad(v,size); res = self->Read(data,size); if(res <= 0) return sq_throwerror(v,_SC("no data left to read")); sq_pushstring(v,data,res); return 1; } SQInteger _stream_gets(HSQUIRRELVM v) { SETUP_STREAM(v); SQChar *data; SQInteger size, read_size; sq_getinteger(v,2,&size); /* DAD come back here if(self->GetHandle() != stdin && size > self->Len()) { size = self->Len(); } */ data = sq_getscratchpad(v,size); read_size = self->Gets(data,size); if(!read_size) return sq_throwerror(v,_SC("no data left to read")); sq_pushstring(v,data,read_size); return 1; } SQInteger _stream_readblob(HSQUIRRELVM v) { SETUP_STREAM(v); SQUserPointer blobp; SQInteger size,res; sq_getinteger(v,2,&size); if(size > self->Len()) { size = self->Len(); } blobp = sqstd_createblob(v,size); res = self->Read(blobp,size); if(res <= 0) return sq_throwerror(v,_SC("no data left to read")); return 1; } #define SAFE_READN(ptr,len) { \ if(self->Read(ptr,len) != len) return sq_throwerror(v,_SC("io error")); \ } SQInteger _stream_readn(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger format; sq_getinteger(v, 2, &format); switch(format) { case 'l': { SQInteger i; SAFE_READN(&i, sizeof(i)); sq_pushinteger(v, i); } break; case 'i': { SQInt32 i; SAFE_READN(&i, sizeof(i)); sq_pushinteger(v, i); } break; case 's': { short s; SAFE_READN(&s, sizeof(short)); sq_pushinteger(v, s); } break; case 'w': { unsigned short w; SAFE_READN(&w, sizeof(unsigned short)); sq_pushinteger(v, w); } break; case 'c': { char c; SAFE_READN(&c, sizeof(char)); sq_pushinteger(v, c); } break; case 'b': { unsigned char c; SAFE_READN(&c, sizeof(unsigned char)); sq_pushinteger(v, c); } break; case 'f': { float f; SAFE_READN(&f, sizeof(float)); sq_pushfloat(v, f); } break; case 'd': { double d; SAFE_READN(&d, sizeof(double)); sq_pushfloat(v, (SQFloat)d); } break; default: return sq_throwerror(v, _SC("invalid format")); } return 1; } SQInteger _stream_write_str(HSQUIRRELVM v) { SQ_FUNC_VARS(v); SETUP_STREAM(v); SQ_GET_STRING(v, 2, str); SQ_OPT_INTEGER(v, 3, start, 0); if(start < 0 || start > str_size) return sq_throwerror(v, _SC("start position out of range (%d)"), start); SQ_OPT_INTEGER(v, 4, len, str_size - start); if(len < 0 || len > (str_size-start)) return sq_throwerror(v, _SC("len value out of range (%d)"), len); if(self->Write(((SQChar*)str)+start, len) != len) return sq_throwerror(v,_SC("io error")); sq_pushinteger(v,len); return 1; } SQInteger _stream_write(HSQUIRRELVM v) { const SQChar *str; SQInteger total_size, size; SETUP_STREAM(v); total_size = 0; for(SQInteger i=2, len=sq_gettop(v); i <= len; ++i){ if(SQ_FAILED(sq_tostring(v,i))) return sq_throwerror(v,_SC("invalid parameter")); sq_getstring(v,-1,&str); size = sq_getsize(v,-1); if(self->Write((SQChar*)str,size) != size) return sq_throwerror(v,_SC("io error")); sq_poptop(v); //remove converted string total_size += size; } sq_pushinteger(v,total_size); return 1; } SQInteger _stream_write_non_null(HSQUIRRELVM v) { if(sq_gettype(v, 2) != OT_NULL) { return _stream_write(v); } return 0; } SQInteger _stream_write_fmt(HSQUIRRELVM v) { const SQChar *str; SQInteger size; SETUP_STREAM(v); if(SQ_FAILED(sq_tostring(v,2))) return sq_throwerror(v,_SC("invalid parameter")); sq_getstring(v,-1,&str); size = sq_getsize(v,-1); if(self->Write((SQChar*)str,size) != size) return sq_throwerror(v,_SC("io error")); sq_poptop(v); //remove converted string sq_pushinteger(v,size); return 1; } SQInteger _stream_writeblob(HSQUIRRELVM v) { SQUserPointer data; SQInteger size; SETUP_STREAM(v); if(SQ_FAILED(sqstd_getblob(v,2,&data))) return sq_throwerror(v,_SC("invalid parameter")); size = sqstd_getblobsize(v,2); if(self->Write(data,size) != size) return sq_throwerror(v,_SC("io error")); sq_pushinteger(v,size); return 1; } SQInteger _stream_writen(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger format, ti; SQFloat tf; sq_getinteger(v, 3, &format); switch(format) { case 'l': { SQInteger i; sq_getinteger(v, 2, &ti); i = ti; self->Write(&i, sizeof(SQInteger)); } break; case 'i': { SQInt32 i; sq_getinteger(v, 2, &ti); i = (SQInt32)ti; self->Write(&i, sizeof(SQInt32)); } break; case 's': { short s; sq_getinteger(v, 2, &ti); s = (short)ti; self->Write(&s, sizeof(short)); } break; case 'w': { unsigned short w; sq_getinteger(v, 2, &ti); w = (unsigned short)ti; self->Write(&w, sizeof(unsigned short)); } break; case 'c': { char c; sq_getinteger(v, 2, &ti); c = (char)ti; self->Write(&c, sizeof(char)); } break; case 'b': { unsigned char b; sq_getinteger(v, 2, &ti); b = (unsigned char)ti; self->Write(&b, sizeof(unsigned char)); } break; case 'f': { float f; sq_getfloat(v, 2, &tf); f = (float)tf; self->Write(&f, sizeof(float)); } break; case 'd': { double d; sq_getfloat(v, 2, &tf); d = tf; self->Write(&d, sizeof(double)); } break; default: return sq_throwerror(v, _SC("invalid format")); } return 0; } SQInteger _stream_seek(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger offset, origin = SQ_SEEK_SET; sq_getinteger(v, 2, &offset); if(sq_gettop(v) > 2) { SQInteger t; sq_getinteger(v, 3, &t); switch(t) { case 'b': origin = SQ_SEEK_SET; break; case 'c': origin = SQ_SEEK_CUR; break; case 'e': origin = SQ_SEEK_END; break; default: return sq_throwerror(v,_SC("invalid origin")); } } sq_pushinteger(v, self->Seek(offset, origin)); return 1; } SQInteger _stream_tell(HSQUIRRELVM v) { SETUP_STREAM(v); sq_pushinteger(v, self->Tell()); return 1; } SQInteger _stream_len(HSQUIRRELVM v) { SETUP_STREAM(v); sq_pushinteger(v, self->Len()); return 1; } SQInteger _stream_flush(HSQUIRRELVM v) { SETUP_STREAM(v); if(!self->Flush()) sq_pushinteger(v, 1); else sq_pushnull(v); return 1; } SQInteger _stream_eos(HSQUIRRELVM v) { SETUP_STREAM(v); if(self->EOS()) sq_pushinteger(v, 1); else sq_pushnull(v); return 1; } SQInteger _stream__cloned(HSQUIRRELVM v) { return sq_throwerror(v,_SC("this object cannot be cloned")); } static SQRegFunction _stream_methods[] = { _DECL_STREAM_FUNC(read_line,-1,_SC("xi")), _DECL_STREAM_FUNC(read,2,_SC("xn")), _DECL_STREAM_FUNC(gets,2,_SC("xn")), _DECL_STREAM_FUNC(readblob,2,_SC("xn")), _DECL_STREAM_FUNC(readn,2,_SC("xn")), _DECL_STREAM_FUNC(write_str,-2,_SC("xsii")), _DECL_STREAM_FUNC(write,-2,_SC("x.")), _DECL_STREAM_FUNC(writeblob,-2,_SC("xx")), _DECL_STREAM_FUNC(writen,3,_SC("xnn")), _DECL_STREAM_FUNC(write_non_null,2,_SC("x.")), _DECL_STREAM_FUNC(seek,-2,_SC("xnn")), _DECL_STREAM_FUNC(tell,1,_SC("x")), _DECL_STREAM_FUNC(len,1,_SC("x")), _DECL_STREAM_FUNC(eos,1,_SC("x")), _DECL_STREAM_FUNC(flush,1,_SC("x")), _DECL_STREAM_FUNC(_cloned,0,NULL), {NULL,(SQFUNCTION)0,0,NULL} }; void init_streamclass(HSQUIRRELVM v) { sq_pushregistrytable(v); sq_pushstring(v,SQSTD_STREAM_TYPE_TAG,-1); if(SQ_FAILED(sq_get(v,-2))) { sq_pushstring(v,SQSTD_STREAM_TYPE_TAG,-1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(SQUserPointer)SQSTD_STREAM_TYPE_TAG); SQInteger i = 0; while(_stream_methods[i].name != 0) { SQRegFunction &f = _stream_methods[i]; sq_pushstring(v,f.name,-1); sq_newclosure(v,f.f,0); sq_setparamscheck(v,f.nparamscheck,f.typemask); sq_newslot(v,-3,SQFalse); i++; } sq_newslot(v,-3,SQFalse); sq_pushroottable(v); sq_pushstring(v,_SC("stream"),-1); sq_pushstring(v,_SC("std_stream"),-1); sq_get(v,-4); sq_newslot(v,-3,SQFalse); sq_pop(v,1); } else { sq_pop(v,1); //result } sq_pop(v,1); } SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals) { if(sq_gettype(v,-1) != OT_TABLE) return sq_throwerror(v,_SC("table expected")); SQInteger top = sq_gettop(v); //create delegate init_streamclass(v); sq_pushregistrytable(v); sq_pushstring(v,reg_name,-1); sq_pushstring(v,_SC("std_stream"),-1); if(SQ_SUCCEEDED(sq_get(v,-3))) { sq_newclass(v,SQTrue); sq_settypetag(v,-1,typetag); SQInteger i = 0; while(methods[i].name != 0) { const SQRegFunction &f = methods[i]; sq_pushstring(v,f.name,-1); sq_newclosure(v,f.f,0); sq_setparamscheck(v,f.nparamscheck,f.typemask); sq_setnativeclosurename(v,-1,f.name); sq_newslot(v,-3,SQFalse); i++; } sq_newslot(v,-3,SQFalse); sq_pop(v,1); i = 0; while(globals[i].name!=0) { const SQRegFunction &f = globals[i]; sq_pushstring(v,f.name,-1); sq_newclosure(v,f.f,0); sq_setparamscheck(v,f.nparamscheck,f.typemask); sq_setnativeclosurename(v,-1,f.name); sq_newslot(v,-3,SQFalse); i++; } //register the class in the target table sq_pushstring(v,name,-1); sq_pushregistrytable(v); sq_pushstring(v,reg_name,-1); sq_get(v,-2); sq_remove(v,-2); sq_newslot(v,-3,SQFalse); sq_settop(v,top); return SQ_OK; } sq_settop(v,top); return SQ_ERROR; }