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 |
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 | |
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 | |
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 | |
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 | |
42 | SQInteger sqstd_ftell(SQFILE file) |
43 | { |
44 | return ftell((FILE *)file); |
45 | } |
46 | |
47 | SQInteger sqstd_fflush(SQFILE file) |
48 | { |
49 | return fflush((FILE *)file); |
50 | } |
51 | |
52 | SQInteger sqstd_fclose(SQFILE file) |
53 | { |
54 | return fclose((FILE *)file); |
55 | } |
56 | |
57 | SQInteger sqstd_feof(SQFILE file) |
58 | { |
59 | return feof((FILE *)file); |
60 | } |
61 | |
62 | //File |
63 | struct 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;} |
107 | private: |
108 | SQFILE _handle; |
109 | bool _owns; |
110 | }; |
111 | |
112 | static SQInteger _file__typeof(HSQUIRRELVM v) |
113 | { |
114 | sq_pushstring(v,_SC("file" ),-1); |
115 | return 1; |
116 | } |
117 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 |
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 | |
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 | |
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 | |
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 | |
335 | SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size) |
336 | { |
337 | return sqstd_fwrite(p,1,size,(SQFILE)file); |
338 | } |
339 | |
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("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 | |
400 | SQRESULT 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 | |
417 | SQRESULT 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 | |
429 | SQInteger _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 | |
442 | SQInteger _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 | |
451 | SQInteger _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} |
466 | static 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 | |
473 | SQRESULT 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 | |