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 ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000001))
9//basic API
10SQFILE 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
19SQInteger 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
25SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file)
26{
27 return (SQInteger)fwrite(buffer,size,count,(FILE *)file);
28}
29
30SQInteger 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
42SQInteger sqstd_ftell(SQFILE file)
43{
44 return ftell((FILE *)file);
45}
46
47SQInteger sqstd_fflush(SQFILE file)
48{
49 return fflush((FILE *)file);
50}
51
52SQInteger sqstd_fclose(SQFILE file)
53{
54 return fclose((FILE *)file);
55}
56
57SQInteger sqstd_feof(SQFILE file)
58{
59 return feof((FILE *)file);
60}
61
62//File
63struct SQFile : public SQStream {
64 SQFile() { _handle = NULL; _owns = false;}
65 SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;}
66 virtual ~SQFile() { Close(); }
67 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 }
75 void Close() {
76 if(_handle && _owns) {
77 sqstd_fclose(_handle);
78 _handle = NULL;
79 _owns = false;
80 }
81 }
82 SQInteger Read(void *buffer,SQInteger size) {
83 return sqstd_fread(buffer,1,size,_handle);
84 }
85 SQInteger Write(void *buffer,SQInteger size) {
86 return sqstd_fwrite(buffer,1,size,_handle);
87 }
88 SQInteger Flush() {
89 return sqstd_fflush(_handle);
90 }
91 SQInteger Tell() {
92 return sqstd_ftell(_handle);
93 }
94 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 }
101 SQInteger Seek(SQInteger offset, SQInteger origin) {
102 return sqstd_fseek(_handle,offset,origin);
103 }
104 bool IsValid() { return _handle?true:false; }
105 bool EOS() { return Tell()==Len()?true:false;}
106 SQFILE GetHandle() {return _handle;}
107private:
108 SQFILE _handle;
109 bool _owns;
110};
111
112static SQInteger _file__typeof(HSQUIRRELVM v)
113{
114 sq_pushstring(v,_SC("file"),-1);
115 return 1;
116}
117
118static 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
126static 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
154static 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}
167static 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
176SQRESULT 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
200SQRESULT 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
213struct IOBuffer {
214 unsigned char buffer[IO_BUFFER_SIZE];
215 SQInteger size;
216 SQInteger ptr;
217 SQFILE file;
218};
219
220SQInteger _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
240SQInteger _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
261static SQInteger _io_file_lexfeed_PLAIN(SQUserPointer iobuf)
262{
263 IOBuffer *iobuffer = (IOBuffer *)iobuf;
264 return _read_byte(iobuffer);
265
266}
267
268#ifdef SQUNICODE
269static 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
308static 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
317static 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
328SQInteger 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
335SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
336{
337 return sqstd_fwrite(p,1,size,(SQFILE)file);
338}
339
340SQRESULT 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("Unrecognized encoding"));
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
400SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
401{
402 //at least one entry must exist in order for us to push it as the environment
403 if(sq_gettop(v) == 0)
404 return sq_throwerror(v,_SC("environment table expected"));
405
406 if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
407 sq_push(v,-2);
408 if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
409 sq_remove(v,retval?-2:-1); //removes the closure
410 return 1;
411 }
412 sq_pop(v,1); //removes the closure
413 }
414 return SQ_ERROR;
415}
416
417SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
418{
419 SQFILE file = sqstd_fopen(filename,_SC("wb+"));
420 if(!file) return sq_throwerror(v,_SC("cannot open the file"));
421 if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
422 sqstd_fclose(file);
423 return SQ_OK;
424 }
425 sqstd_fclose(file);
426 return SQ_ERROR; //forward the error
427}
428
429SQInteger _g_io_loadfile(HSQUIRRELVM v)
430{
431 const SQChar *filename;
432 SQBool printerror = SQFalse;
433 sq_getstring(v,2,&filename);
434 if(sq_gettop(v) >= 3) {
435 sq_getbool(v,3,&printerror);
436 }
437 if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
438 return 1;
439 return SQ_ERROR; //propagates the error
440}
441
442SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
443{
444 const SQChar *filename;
445 sq_getstring(v,2,&filename);
446 if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
447 return 1;
448 return SQ_ERROR; //propagates the error
449}
450
451SQInteger _g_io_dofile(HSQUIRRELVM v)
452{
453 const SQChar *filename;
454 SQBool printerror = SQFalse;
455 sq_getstring(v,2,&filename);
456 if(sq_gettop(v) >= 3) {
457 sq_getbool(v,3,&printerror);
458 }
459 sq_push(v,1); //repush the this
460 if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
461 return 1;
462 return SQ_ERROR; //propagates the error
463}
464
465#define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
466static const SQRegFunction iolib_funcs[]={
467 _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
468 _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
469 _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
470 {NULL,(SQFUNCTION)0,0,NULL}
471};
472
473SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
474{
475 SQInteger top = sq_gettop(v);
476 //create delegate
477 declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
478 sq_pushstring(v,_SC("stdout"),-1);
479 sqstd_createfile(v,stdout,SQFalse);
480 sq_newslot(v,-3,SQFalse);
481 sq_pushstring(v,_SC("stdin"),-1);
482 sqstd_createfile(v,stdin,SQFalse);
483 sq_newslot(v,-3,SQFalse);
484 sq_pushstring(v,_SC("stderr"),-1);
485 sqstd_createfile(v,stderr,SQFalse);
486 sq_newslot(v,-3,SQFalse);
487 sq_settop(v,top);
488 return SQ_OK;
489}
490