1 /* see copyright notice in squirrel.h */
2 #include <new>
3 #include <stdio.h>
4 #include <squirrel.h>
5 #include <sqstdio.h>
6 #include "sqstdstream.h"
7 
8 #define SQSTD_FILE_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000001)
9 //basic API
sqstd_fopen(const SQChar * filename,const SQChar * mode)10 SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode)
11 {
12 #ifndef SQUNICODE
13     return (SQFILE)fopen(filename,mode);
14 #else
15     return (SQFILE)_wfopen(filename,mode);
16 #endif
17 }
18 
sqstd_fread(void * buffer,SQInteger size,SQInteger count,SQFILE file)19 SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file)
20 {
21     SQInteger ret = (SQInteger)fread(buffer,size,count,(FILE *)file);
22     return ret;
23 }
24 
sqstd_fwrite(const SQUserPointer buffer,SQInteger size,SQInteger count,SQFILE file)25 SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file)
26 {
27     return (SQInteger)fwrite(buffer,size,count,(FILE *)file);
28 }
29 
sqstd_fseek(SQFILE file,SQInteger offset,SQInteger origin)30 SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin)
31 {
32     SQInteger realorigin;
33     switch(origin) {
34         case SQ_SEEK_CUR: realorigin = SEEK_CUR; break;
35         case SQ_SEEK_END: realorigin = SEEK_END; break;
36         case SQ_SEEK_SET: realorigin = SEEK_SET; break;
37         default: return -1; //failed
38     }
39     return fseek((FILE *)file,(long)offset,(int)realorigin);
40 }
41 
sqstd_ftell(SQFILE file)42 SQInteger sqstd_ftell(SQFILE file)
43 {
44     return ftell((FILE *)file);
45 }
46 
sqstd_fflush(SQFILE file)47 SQInteger sqstd_fflush(SQFILE file)
48 {
49     return fflush((FILE *)file);
50 }
51 
sqstd_fclose(SQFILE file)52 SQInteger sqstd_fclose(SQFILE file)
53 {
54     return fclose((FILE *)file);
55 }
56 
sqstd_feof(SQFILE file)57 SQInteger sqstd_feof(SQFILE file)
58 {
59     return feof((FILE *)file);
60 }
61 
62 //File
63 struct SQFile : public SQStream {
SQFileSQFile64     SQFile() { _handle = NULL; _owns = false;}
SQFileSQFile65     SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;}
~SQFileSQFile66     virtual ~SQFile() { Close(); }
OpenSQFile67     bool Open(const SQChar *filename ,const SQChar *mode) {
68         Close();
69         if( (_handle = sqstd_fopen(filename,mode)) ) {
70             _owns = true;
71             return true;
72         }
73         return false;
74     }
CloseSQFile75     void Close() {
76         if(_handle && _owns) {
77             sqstd_fclose(_handle);
78             _handle = NULL;
79             _owns = false;
80         }
81     }
ReadSQFile82     SQInteger Read(void *buffer,SQInteger size) {
83         return sqstd_fread(buffer,1,size,_handle);
84     }
WriteSQFile85     SQInteger Write(void *buffer,SQInteger size) {
86         return sqstd_fwrite(buffer,1,size,_handle);
87     }
FlushSQFile88     SQInteger Flush() {
89         return sqstd_fflush(_handle);
90     }
TellSQFile91     SQInteger Tell() {
92         return sqstd_ftell(_handle);
93     }
LenSQFile94     SQInteger Len() {
95         SQInteger prevpos=Tell();
96         Seek(0,SQ_SEEK_END);
97         SQInteger size=Tell();
98         Seek(prevpos,SQ_SEEK_SET);
99         return size;
100     }
SeekSQFile101     SQInteger Seek(SQInteger offset, SQInteger origin)  {
102         return sqstd_fseek(_handle,offset,origin);
103     }
IsValidSQFile104     bool IsValid() { return _handle?true:false; }
EOSSQFile105     bool EOS() { return Tell()==Len()?true:false;}
GetHandleSQFile106     SQFILE GetHandle() {return _handle;}
107 private:
108     SQFILE _handle;
109     bool _owns;
110 };
111 
_file__typeof(HSQUIRRELVM v)112 static SQInteger _file__typeof(HSQUIRRELVM v)
113 {
114     sq_pushstring(v,_SC("file"),-1);
115     return 1;
116 }
117 
_file_releasehook(SQUserPointer p,SQInteger SQ_UNUSED_ARG (size))118 static SQInteger _file_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size))
119 {
120     SQFile *self = (SQFile*)p;
121     self->~SQFile();
122     sq_free(self,sizeof(SQFile));
123     return 1;
124 }
125 
_file_constructor(HSQUIRRELVM v)126 static SQInteger _file_constructor(HSQUIRRELVM v)
127 {
128     const SQChar *filename,*mode;
129     bool owns = true;
130     SQFile *f;
131     SQFILE newf;
132     if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) {
133         sq_getstring(v, 2, &filename);
134         sq_getstring(v, 3, &mode);
135         newf = sqstd_fopen(filename, mode);
136         if(!newf) return sq_throwerror(v, _SC("cannot open file"));
137     } else if(sq_gettype(v,2) == OT_USERPOINTER) {
138         owns = !(sq_gettype(v,3) == OT_NULL);
139         sq_getuserpointer(v,2,&newf);
140     } else {
141         return sq_throwerror(v,_SC("wrong parameter"));
142     }
143 
144     f = new (sq_malloc(sizeof(SQFile)))SQFile(newf,owns);
145     if(SQ_FAILED(sq_setinstanceup(v,1,f))) {
146         f->~SQFile();
147         sq_free(f,sizeof(SQFile));
148         return sq_throwerror(v, _SC("cannot create blob with negative size"));
149     }
150     sq_setreleasehook(v,1,_file_releasehook);
151     return 0;
152 }
153 
_file_close(HSQUIRRELVM v)154 static SQInteger _file_close(HSQUIRRELVM v)
155 {
156     SQFile *self = NULL;
157     if(SQ_SUCCEEDED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_FILE_TYPE_TAG))
158         && self != NULL)
159     {
160         self->Close();
161     }
162     return 0;
163 }
164 
165 //bindings
166 #define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}
167 static const SQRegFunction _file_methods[] = {
168     _DECL_FILE_FUNC(constructor,3,_SC("x")),
169     _DECL_FILE_FUNC(_typeof,1,_SC("x")),
170     _DECL_FILE_FUNC(close,1,_SC("x")),
171     {NULL,(SQFUNCTION)0,0,NULL}
172 };
173 
174 
175 
sqstd_createfile(HSQUIRRELVM v,SQFILE file,SQBool own)176 SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own)
177 {
178     SQInteger top = sq_gettop(v);
179     sq_pushregistrytable(v);
180     sq_pushstring(v,_SC("std_file"),-1);
181     if(SQ_SUCCEEDED(sq_get(v,-2))) {
182         sq_remove(v,-2); //removes the registry
183         sq_pushroottable(v); // push the this
184         sq_pushuserpointer(v,file); //file
185         if(own){
186             sq_pushinteger(v,1); //true
187         }
188         else{
189             sq_pushnull(v); //false
190         }
191         if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) {
192             sq_remove(v,-2);
193             return SQ_OK;
194         }
195     }
196     sq_settop(v,top);
197     return SQ_ERROR;
198 }
199 
sqstd_getfile(HSQUIRRELVM v,SQInteger idx,SQFILE * file)200 SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file)
201 {
202     SQFile *fileobj = NULL;
203     if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) {
204         *file = fileobj->GetHandle();
205         return SQ_OK;
206     }
207     return sq_throwerror(v,_SC("not a file"));
208 }
209 
210 
211 
212 #define IO_BUFFER_SIZE 2048
213 struct IOBuffer {
214     unsigned char buffer[IO_BUFFER_SIZE];
215     SQInteger size;
216     SQInteger ptr;
217     SQFILE file;
218 };
219 
_read_byte(IOBuffer * iobuffer)220 SQInteger _read_byte(IOBuffer *iobuffer)
221 {
222     if(iobuffer->ptr < iobuffer->size) {
223 
224         SQInteger ret = iobuffer->buffer[iobuffer->ptr];
225         iobuffer->ptr++;
226         return ret;
227     }
228     else {
229         if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 )
230         {
231             SQInteger ret = iobuffer->buffer[0];
232             iobuffer->ptr = 1;
233             return ret;
234         }
235     }
236 
237     return 0;
238 }
239 
_read_two_bytes(IOBuffer * iobuffer)240 SQInteger _read_two_bytes(IOBuffer *iobuffer)
241 {
242     if(iobuffer->ptr < iobuffer->size) {
243         if(iobuffer->size < 2) return 0;
244         SQInteger ret = *((const wchar_t*)&iobuffer->buffer[iobuffer->ptr]);
245         iobuffer->ptr += 2;
246         return ret;
247     }
248     else {
249         if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 )
250         {
251             if(iobuffer->size < 2) return 0;
252             SQInteger ret = *((const wchar_t*)&iobuffer->buffer[0]);
253             iobuffer->ptr = 2;
254             return ret;
255         }
256     }
257 
258     return 0;
259 }
260 
_io_file_lexfeed_PLAIN(SQUserPointer iobuf)261 static SQInteger _io_file_lexfeed_PLAIN(SQUserPointer iobuf)
262 {
263     IOBuffer *iobuffer = (IOBuffer *)iobuf;
264     return _read_byte(iobuffer);
265 
266 }
267 
268 #ifdef SQUNICODE
_io_file_lexfeed_UTF8(SQUserPointer iobuf)269 static SQInteger _io_file_lexfeed_UTF8(SQUserPointer iobuf)
270 {
271     IOBuffer *iobuffer = (IOBuffer *)iobuf;
272 #define READ(iobuf) \
273     if((inchar = (unsigned char)_read_byte(iobuf)) == 0) \
274         return 0;
275 
276     static const SQInteger utf8_lengths[16] =
277     {
278         1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */
279         0,0,0,0,                /* 1000 to 1011 : not valid */
280         2,2,                    /* 1100, 1101 : 2 bytes */
281         3,                      /* 1110 : 3 bytes */
282         4                       /* 1111 :4 bytes */
283     };
284     static const unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
285     unsigned char inchar;
286     SQInteger c = 0;
287     READ(iobuffer);
288     c = inchar;
289     //
290     if(c >= 0x80) {
291         SQInteger tmp;
292         SQInteger codelen = utf8_lengths[c>>4];
293         if(codelen == 0)
294             return 0;
295             //"invalid UTF-8 stream";
296         tmp = c&byte_masks[codelen];
297         for(SQInteger n = 0; n < codelen-1; n++) {
298             tmp<<=6;
299             READ(iobuffer);
300             tmp |= inchar & 0x3F;
301         }
302         c = tmp;
303     }
304     return c;
305 }
306 #endif
307 
_io_file_lexfeed_UCS2_LE(SQUserPointer iobuf)308 static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer iobuf)
309 {
310     SQInteger ret;
311     IOBuffer *iobuffer = (IOBuffer *)iobuf;
312     if( (ret = _read_two_bytes(iobuffer)) > 0 )
313         return ret;
314     return 0;
315 }
316 
_io_file_lexfeed_UCS2_BE(SQUserPointer iobuf)317 static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer iobuf)
318 {
319     SQInteger c;
320     IOBuffer *iobuffer = (IOBuffer *)iobuf;
321     if( (c = _read_two_bytes(iobuffer)) > 0 ) {
322         c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
323         return c;
324     }
325     return 0;
326 }
327 
file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)328 SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)
329 {
330     SQInteger ret;
331     if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;
332     return -1;
333 }
334 
file_write(SQUserPointer file,SQUserPointer p,SQInteger size)335 SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
336 {
337     return sqstd_fwrite(p,1,size,(SQFILE)file);
338 }
339 
sqstd_loadfile(HSQUIRRELVM v,const SQChar * filename,SQBool printerror)340 SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)
341 {
342     SQFILE file = sqstd_fopen(filename,_SC("rb"));
343 
344     SQInteger ret;
345     unsigned short us;
346     unsigned char uc;
347     SQLEXREADFUNC func = _io_file_lexfeed_PLAIN;
348     if(file){
349         ret = sqstd_fread(&us,1,2,file);
350         if(ret != 2) {
351             //probably an empty file
352             us = 0;
353         }
354         if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE
355             sqstd_fseek(file,0,SQ_SEEK_SET);
356             if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {
357                 sqstd_fclose(file);
358                 return SQ_OK;
359             }
360         }
361         else { //SCRIPT
362 
363             switch(us)
364             {
365                 //gotta swap the next 2 lines on BIG endian machines
366                 case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
367                 case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
368                 case 0xBBEF:
369                     if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) {
370                         sqstd_fclose(file);
371                         return sq_throwerror(v,_SC("io error"));
372                     }
373                     if(uc != 0xBF) {
374                         sqstd_fclose(file);
375                         return sq_throwerror(v,_SC("Unrecognozed ecoding"));
376                     }
377 #ifdef SQUNICODE
378                     func = _io_file_lexfeed_UTF8;
379 #else
380                     func = _io_file_lexfeed_PLAIN;
381 #endif
382                     break;//UTF-8 ;
383                 default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii
384             }
385             IOBuffer buffer;
386             buffer.ptr = 0;
387             buffer.size = 0;
388             buffer.file = file;
389             if(SQ_SUCCEEDED(sq_compile(v,func,&buffer,filename,printerror))){
390                 sqstd_fclose(file);
391                 return SQ_OK;
392             }
393         }
394         sqstd_fclose(file);
395         return SQ_ERROR;
396     }
397     return sq_throwerror(v,_SC("cannot open the file"));
398 }
399 
sqstd_dofile(HSQUIRRELVM v,const SQChar * filename,SQBool retval,SQBool printerror)400 SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
401 {
402     if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
403         sq_push(v,-2);
404         if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
405             sq_remove(v,retval?-2:-1); //removes the closure
406             return 1;
407         }
408         sq_pop(v,1); //removes the closure
409     }
410     return SQ_ERROR;
411 }
412 
sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar * filename)413 SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
414 {
415     SQFILE file = sqstd_fopen(filename,_SC("wb+"));
416     if(!file) return sq_throwerror(v,_SC("cannot open the file"));
417     if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
418         sqstd_fclose(file);
419         return SQ_OK;
420     }
421     sqstd_fclose(file);
422     return SQ_ERROR; //forward the error
423 }
424 
_g_io_loadfile(HSQUIRRELVM v)425 SQInteger _g_io_loadfile(HSQUIRRELVM v)
426 {
427     const SQChar *filename;
428     SQBool printerror = SQFalse;
429     sq_getstring(v,2,&filename);
430     if(sq_gettop(v) >= 3) {
431         sq_getbool(v,3,&printerror);
432     }
433     if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
434         return 1;
435     return SQ_ERROR; //propagates the error
436 }
437 
_g_io_writeclosuretofile(HSQUIRRELVM v)438 SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
439 {
440     const SQChar *filename;
441     sq_getstring(v,2,&filename);
442     if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
443         return 1;
444     return SQ_ERROR; //propagates the error
445 }
446 
_g_io_dofile(HSQUIRRELVM v)447 SQInteger _g_io_dofile(HSQUIRRELVM v)
448 {
449     const SQChar *filename;
450     SQBool printerror = SQFalse;
451     sq_getstring(v,2,&filename);
452     if(sq_gettop(v) >= 3) {
453         sq_getbool(v,3,&printerror);
454     }
455     sq_push(v,1); //repush the this
456     if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
457         return 1;
458     return SQ_ERROR; //propagates the error
459 }
460 
461 #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
462 static const SQRegFunction iolib_funcs[]={
463     _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
464     _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
465     _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
466     {NULL,(SQFUNCTION)0,0,NULL}
467 };
468 
sqstd_register_iolib(HSQUIRRELVM v)469 SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
470 {
471     SQInteger top = sq_gettop(v);
472     //create delegate
473     declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
474     sq_pushstring(v,_SC("stdout"),-1);
475     sqstd_createfile(v,stdout,SQFalse);
476     sq_newslot(v,-3,SQFalse);
477     sq_pushstring(v,_SC("stdin"),-1);
478     sqstd_createfile(v,stdin,SQFalse);
479     sq_newslot(v,-3,SQFalse);
480     sq_pushstring(v,_SC("stderr"),-1);
481     sqstd_createfile(v,stderr,SQFalse);
482     sq_newslot(v,-3,SQFalse);
483     sq_settop(v,top);
484     return SQ_OK;
485 }
486