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 #ifndef _UNICODE
12 return ( SQFILE )fopen( filename, mode );
13 #else
14 return ( SQFILE )_wfopen( filename, mode );
15 #endif
16 }
17
sqstd_fread(void * buffer,SQInteger size,SQInteger count,SQFILE file)18 SQInteger sqstd_fread( void* buffer, SQInteger size, SQInteger count, SQFILE file ) {
19 return ( SQInteger )fread( buffer, size, count, ( FILE * )file );
20 }
21
sqstd_fwrite(const SQUserPointer buffer,SQInteger size,SQInteger count,SQFILE file)22 SQInteger sqstd_fwrite( const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file ) {
23 return ( SQInteger )fwrite( buffer, size, count, ( FILE * )file );
24 }
25
sqstd_fseek(SQFILE file,SQInteger offset,SQInteger origin)26 SQInteger sqstd_fseek( SQFILE file, SQInteger offset, SQInteger origin ) {
27 SQInteger realorigin;
28 switch ( origin ) {
29 case SQ_SEEK_CUR: realorigin = SEEK_CUR; break;
30 case SQ_SEEK_END: realorigin = SEEK_END; break;
31 case SQ_SEEK_SET: realorigin = SEEK_SET; break;
32 default: return -1; //failed
33 }
34 return fseek( ( FILE * )file, offset, realorigin );
35 }
36
sqstd_ftell(SQFILE file)37 SQInteger sqstd_ftell( SQFILE file ) {
38 return ftell( ( FILE * )file );
39 }
40
sqstd_fflush(SQFILE file)41 SQInteger sqstd_fflush( SQFILE file ) {
42 return fflush( ( FILE * )file );
43 }
44
sqstd_fclose(SQFILE file)45 SQInteger sqstd_fclose( SQFILE file ) {
46 return fclose( ( FILE * )file );
47 }
48
sqstd_feof(SQFILE file)49 SQInteger sqstd_feof( SQFILE file ) {
50 return feof( ( FILE * )file );
51 }
52
53 //File
54 struct SQFile : public SQStream {
SQFileSQFile55 SQFile() {
56 _handle = NULL; _owns = false;
57 }
SQFileSQFile58 SQFile( SQFILE file, bool owns ) {
59 _handle = file; _owns = owns;
60 }
~SQFileSQFile61 ~SQFile() {
62 Close();
63 }
OpenSQFile64 bool Open( const SQChar *filename , const SQChar *mode ) {
65 Close();
66 if ( _handle = sqstd_fopen( filename, mode ) ) {
67 _owns = true;
68 return true;
69 }
70 return false;
71 }
CloseSQFile72 void Close() {
73 if ( _handle && _owns ) {
74 sqstd_fclose( _handle );
75 _handle = NULL;
76 _owns = false;
77 }
78 }
ReadSQFile79 SQInteger Read( void *buffer, SQInteger size ) {
80 return sqstd_fread( buffer, 1, size, _handle );
81 }
WriteSQFile82 SQInteger Write( void *buffer, SQInteger size ) {
83 return sqstd_fwrite( buffer, 1, size, _handle );
84 }
FlushSQFile85 SQInteger Flush() {
86 return sqstd_fflush( _handle );
87 }
TellSQFile88 SQInteger Tell() {
89 return sqstd_ftell( _handle );
90 }
LenSQFile91 SQInteger Len() {
92 SQInteger prevpos = Tell();
93 Seek( 0, SQ_SEEK_END );
94 SQInteger size = Tell();
95 Seek( prevpos, SQ_SEEK_SET );
96 return size;
97 }
SeekSQFile98 SQInteger Seek( SQInteger offset, SQInteger origin ) {
99 return sqstd_fseek( _handle, offset, origin );
100 }
IsValidSQFile101 bool IsValid() {
102 return _handle ? true : false;
103 }
EOSSQFile104 bool EOS() {
105 return Tell() == Len() ? true : false;
106 }
GetHandleSQFile107 SQFILE GetHandle() {
108 return _handle;
109 }
110 private:
111 SQFILE _handle;
112 bool _owns;
113 };
114
_file__typeof(HSQUIRRELVM v)115 static SQInteger _file__typeof( HSQUIRRELVM v ) {
116 sq_pushstring( v, _SC( "file" ), -1 );
117 return 1;
118 }
119
_file_releasehook(SQUserPointer p,SQInteger size)120 static SQInteger _file_releasehook( SQUserPointer p, SQInteger size ) {
121 SQFile *self = ( SQFile* )p;
122 delete self;
123 return 1;
124 }
125
_file_constructor(HSQUIRRELVM v)126 static SQInteger _file_constructor( HSQUIRRELVM v ) {
127 const SQChar *filename, *mode;
128 bool owns = true;
129 SQFile *f;
130 SQFILE newf;
131 if ( sq_gettype( v, 2 ) == OT_STRING && sq_gettype( v, 3 ) == OT_STRING ) {
132 sq_getstring( v, 2, &filename );
133 sq_getstring( v, 3, &mode );
134 newf = sqstd_fopen( filename, mode );
135 if ( !newf ) return sq_throwerror( v, _SC( "cannot open file" ) );
136 } else if ( sq_gettype( v, 2 ) == OT_USERPOINTER ) {
137 owns = !( sq_gettype( v, 3 ) == OT_NULL );
138 sq_getuserpointer( v, 2, &newf );
139 } else {
140 return sq_throwerror( v, _SC( "wrong parameter" ) );
141 }
142 f = new SQFile( newf, owns );
143 if ( SQ_FAILED( sq_setinstanceup( v, 1, f ) ) ) {
144 delete f;
145 return sq_throwerror( v, _SC( "cannot create blob with negative size" ) );
146 }
147 sq_setreleasehook( v, 1, _file_releasehook );
148 return 0;
149 }
150
151 //bindings
152 #define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}
153 static SQRegFunction _file_methods[] = {
154 _DECL_FILE_FUNC( constructor, 3, _SC( "x" ) ),
155 _DECL_FILE_FUNC( _typeof, 1, _SC( "x" ) ),
156 {0, 0, 0, 0},
157 };
158
159
160
sqstd_createfile(HSQUIRRELVM v,SQFILE file,SQBool own)161 SQRESULT sqstd_createfile( HSQUIRRELVM v, SQFILE file, SQBool own ) {
162 SQInteger top = sq_gettop( v );
163 sq_pushregistrytable( v );
164 sq_pushstring( v, _SC( "std_file" ), -1 );
165 if ( SQ_SUCCEEDED( sq_get( v, -2 ) ) ) {
166 sq_remove( v, -2 ); //removes the registry
167 sq_pushroottable( v ); // push the this
168 sq_pushuserpointer( v, file ); //file
169 if ( own ) {
170 sq_pushinteger( v, 1 ); //true
171 } else {
172 sq_pushnull( v ); //false
173 }
174 if ( SQ_SUCCEEDED( sq_call( v, 3, SQTrue ) ) ) {
175 sq_remove( v, -2 );
176 return SQ_OK;
177 }
178 }
179 sq_settop( v, top );
180 return SQ_OK;
181 }
182
sqstd_getfile(HSQUIRRELVM v,SQInteger idx,SQFILE * file)183 SQRESULT sqstd_getfile( HSQUIRRELVM v, SQInteger idx, SQFILE *file ) {
184 SQFile *fileobj = NULL;
185 if ( SQ_SUCCEEDED( sq_getinstanceup( v, idx, ( SQUserPointer* )&fileobj, ( SQUserPointer )SQSTD_FILE_TYPE_TAG ) ) ) {
186 *file = fileobj->GetHandle();
187 return SQ_OK;
188 }
189 return sq_throwerror( v, _SC( "not a file" ) );
190 }
191
192
193
_io_file_lexfeed_ASCII(SQUserPointer file)194 static SQInteger _io_file_lexfeed_ASCII( SQUserPointer file ) {
195 SQInteger ret;
196 char c;
197 if ( ( ret = sqstd_fread( &c, sizeof( c ), 1, ( FILE * )file ) > 0 ) )
198 return c;
199 return 0;
200 }
201
_io_file_lexfeed_UTF8(SQUserPointer file)202 static SQInteger _io_file_lexfeed_UTF8( SQUserPointer file ) {
203 #define READ() \
204 if(sqstd_fread(&inchar,sizeof(inchar),1,(FILE *)file) != 1) \
205 return 0;
206
207 static const SQInteger utf8_lengths[16] = {
208 1, 1, 1, 1, 1, 1, 1, 1, /* 0000 to 0111 : 1 byte (plain ASCII) */
209 0, 0, 0, 0, /* 1000 to 1011 : not valid */
210 2, 2, /* 1100, 1101 : 2 bytes */
211 3, /* 1110 : 3 bytes */
212 4 /* 1111 :4 bytes */
213 };
214 static unsigned char byte_masks[5] = {0, 0, 0x1f, 0x0f, 0x07};
215 unsigned char inchar;
216 SQInteger c = 0;
217 READ();
218 c = inchar;
219 //
220 if ( c >= 0x80 ) {
221 SQInteger tmp;
222 SQInteger codelen = utf8_lengths[c>>4];
223 if ( codelen == 0 )
224 return 0;
225 //"invalid UTF-8 stream";
226 tmp = c & byte_masks[codelen];
227 for ( SQInteger n = 0; n < codelen - 1; n++ ) {
228 tmp <<= 6;
229 READ();
230 tmp |= inchar & 0x3F;
231 }
232 c = tmp;
233 }
234 return c;
235 }
236
_io_file_lexfeed_UCS2_LE(SQUserPointer file)237 static SQInteger _io_file_lexfeed_UCS2_LE( SQUserPointer file ) {
238 SQInteger ret;
239 wchar_t c;
240 if ( ( ret = sqstd_fread( &c, sizeof( c ), 1, ( FILE * )file ) > 0 ) )
241 return ( SQChar )c;
242 return 0;
243 }
244
_io_file_lexfeed_UCS2_BE(SQUserPointer file)245 static SQInteger _io_file_lexfeed_UCS2_BE( SQUserPointer file ) {
246 SQInteger ret;
247 unsigned short c;
248 if ( ( ret = sqstd_fread( &c, sizeof( c ), 1, ( FILE * )file ) > 0 ) ) {
249 c = ( ( c >> 8 ) & 0x00FF ) | ( ( c << 8 ) & 0xFF00 );
250 return ( SQChar )c;
251 }
252 return 0;
253 }
254
file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)255 SQInteger file_read( SQUserPointer file, SQUserPointer buf, SQInteger size ) {
256 SQInteger ret;
257 if ( ( ret = sqstd_fread( buf, 1, size, ( SQFILE )file ) ) != 0 )return ret;
258 return -1;
259 }
260
file_write(SQUserPointer file,SQUserPointer p,SQInteger size)261 SQInteger file_write( SQUserPointer file, SQUserPointer p, SQInteger size ) {
262 return sqstd_fwrite( p, 1, size, ( SQFILE )file );
263 }
264
sqstd_loadfile(HSQUIRRELVM v,const SQChar * filename,SQBool printerror)265 SQRESULT sqstd_loadfile( HSQUIRRELVM v, const SQChar *filename, SQBool printerror ) {
266 SQFILE file = sqstd_fopen( filename, _SC( "rb" ) );
267 SQInteger ret;
268 unsigned short us;
269 unsigned char uc;
270 SQLEXREADFUNC func = _io_file_lexfeed_ASCII;
271 if ( file ) {
272 ret = sqstd_fread( &us, 1, 2, file );
273 if ( ret != 2 ) {
274 //probably an empty file
275 us = 0;
276 }
277 if ( us == SQ_BYTECODE_STREAM_TAG ) { //BYTECODE
278 sqstd_fseek( file, 0, SQ_SEEK_SET );
279 if ( SQ_SUCCEEDED( sq_readclosure( v, file_read, file ) ) ) {
280 sqstd_fclose( file );
281 return SQ_OK;
282 }
283 } else { //SCRIPT
284 switch ( us ) {
285 //gotta swap the next 2 lines on BIG endian machines
286 case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
287 case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
288 case 0xBBEF:
289 if ( sqstd_fread( &uc, 1, sizeof( uc ), file ) == 0 ) {
290 sqstd_fclose( file );
291 return sq_throwerror( v, _SC( "io error" ) );
292 }
293 if ( uc != 0xBF ) {
294 sqstd_fclose( file );
295 return sq_throwerror( v, _SC( "Unrecognozed ecoding" ) );
296 }
297 func = _io_file_lexfeed_UTF8;
298 break;//UTF-8 ;
299 default: sqstd_fseek( file, 0, SQ_SEEK_SET ); break; // ascii
300 }
301
302 if ( SQ_SUCCEEDED( sq_compile( v, func, file, filename, printerror ) ) ) {
303 sqstd_fclose( file );
304 return SQ_OK;
305 }
306 }
307 sqstd_fclose( file );
308 return SQ_ERROR;
309 }
310 return sq_throwerror( v, _SC( "cannot open the file" ) );
311 }
312
sqstd_dofile(HSQUIRRELVM v,const SQChar * filename,SQBool retval,SQBool printerror)313 SQRESULT sqstd_dofile( HSQUIRRELVM v, const SQChar *filename, SQBool retval, SQBool printerror ) {
314 if ( SQ_SUCCEEDED( sqstd_loadfile( v, filename, printerror ) ) ) {
315 sq_push( v, -2 );
316 SQInteger ntop = sq_gettop( v );
317 if ( SQ_SUCCEEDED( sq_call( v, 1, retval ) ) ) {
318 sq_remove( v, retval ? -2 : -1 ); //removes the closure
319 return 1;
320 }
321 sq_pop( v, 1 ); //removes the closure
322 }
323 return SQ_ERROR;
324 }
325
sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar * filename)326 SQRESULT sqstd_writeclosuretofile( HSQUIRRELVM v, const SQChar *filename ) {
327 SQFILE file = sqstd_fopen( filename, _SC( "wb+" ) );
328 if ( !file ) return sq_throwerror( v, _SC( "cannot open the file" ) );
329 if ( SQ_SUCCEEDED( sq_writeclosure( v, file_write, file ) ) ) {
330 sqstd_fclose( file );
331 return SQ_OK;
332 }
333 sqstd_fclose( file );
334 return SQ_ERROR; //forward the error
335 }
336
_g_io_loadfile(HSQUIRRELVM v)337 SQInteger _g_io_loadfile( HSQUIRRELVM v ) {
338 const SQChar *filename;
339 SQBool printerror = SQFalse;
340 sq_getstring( v, 2, &filename );
341 if ( sq_gettop( v ) >= 3 ) {
342 sq_getbool( v, 3, &printerror );
343 }
344 if ( SQ_SUCCEEDED( sqstd_loadfile( v, filename, printerror ) ) )
345 return 1;
346 return SQ_ERROR; //propagates the error
347 }
348
_g_io_dofile(HSQUIRRELVM v)349 SQInteger _g_io_dofile( HSQUIRRELVM v ) {
350 const SQChar *filename;
351 SQBool printerror = SQFalse;
352 sq_getstring( v, 2, &filename );
353 if ( sq_gettop( v ) >= 3 ) {
354 sq_getbool( v, 3, &printerror );
355 }
356 sq_push( v, 1 ); //repush the this
357 if ( SQ_SUCCEEDED( sqstd_dofile( v, filename, SQTrue, printerror ) ) )
358 return 1;
359 return SQ_ERROR; //propagates the error
360 }
361
362 #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
363 static SQRegFunction iolib_funcs[] = {
364 _DECL_GLOBALIO_FUNC( loadfile, -2, _SC( ".sb" ) ),
365 _DECL_GLOBALIO_FUNC( dofile, -2, _SC( ".sb" ) ),
366 {0, 0}
367 };
368
sqstd_register_iolib(HSQUIRRELVM v)369 SQRESULT sqstd_register_iolib( HSQUIRRELVM v ) {
370 SQInteger top = sq_gettop( v );
371 //create delegate
372 declare_stream( v, _SC( "file" ), ( SQUserPointer )SQSTD_FILE_TYPE_TAG, _SC( "std_file" ), _file_methods, iolib_funcs );
373 sq_pushstring( v, _SC( "stdout" ), -1 );
374 sqstd_createfile( v, stdout, SQFalse );
375 sq_createslot( v, -3 );
376 sq_pushstring( v, _SC( "stdin" ), -1 );
377 sqstd_createfile( v, stdin, SQFalse );
378 sq_createslot( v, -3 );
379 sq_pushstring( v, _SC( "stderr" ), -1 );
380 sqstd_createfile( v, stderr, SQFalse );
381 sq_createslot( v, -3 );
382 sq_settop( v, top );
383 return SQ_OK;
384 }
385