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