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 	return (SQInteger)fread(buffer,size,count,(FILE *)file);
22 }
23 
sqstd_fwrite(const SQUserPointer buffer,SQInteger size,SQInteger count,SQFILE file)24 SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file)
25 {
26 	return (SQInteger)fwrite(buffer,size,count,(FILE *)file);
27 }
28 
sqstd_fseek(SQFILE file,SQInteger offset,SQInteger origin)29 SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin)
30 {
31 	SQInteger realorigin;
32 	switch(origin) {
33 		case SQ_SEEK_CUR: realorigin = SEEK_CUR; break;
34 		case SQ_SEEK_END: realorigin = SEEK_END; break;
35 		case SQ_SEEK_SET: realorigin = SEEK_SET; break;
36 		default: return -1; //failed
37 	}
38 	return fseek((FILE *)file,(long)offset,(int)realorigin);
39 }
40 
sqstd_ftell(SQFILE file)41 SQInteger sqstd_ftell(SQFILE file)
42 {
43 	return ftell((FILE *)file);
44 }
45 
sqstd_fflush(SQFILE file)46 SQInteger sqstd_fflush(SQFILE file)
47 {
48 	return fflush((FILE *)file);
49 }
50 
sqstd_fclose(SQFILE file)51 SQInteger sqstd_fclose(SQFILE file)
52 {
53 	return fclose((FILE *)file);
54 }
55 
sqstd_feof(SQFILE file)56 SQInteger sqstd_feof(SQFILE file)
57 {
58 	return feof((FILE *)file);
59 }
60 
61 //File
62 struct SQFile : public SQStream {
SQFileSQFile63 	SQFile() { _handle = NULL; _owns = false;}
SQFileSQFile64 	SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;}
~SQFileSQFile65 	virtual ~SQFile() { Close(); }
OpenSQFile66 	bool Open(const SQChar *filename ,const SQChar *mode) {
67 		Close();
68 		if( (_handle = sqstd_fopen(filename,mode)) ) {
69 			_owns = true;
70 			return true;
71 		}
72 		return false;
73 	}
CloseSQFile74 	void Close() {
75 		if(_handle && _owns) {
76 			sqstd_fclose(_handle);
77 			_handle = NULL;
78 			_owns = false;
79 		}
80 	}
ReadSQFile81 	SQInteger Read(void *buffer,SQInteger size) {
82 		return sqstd_fread(buffer,1,size,_handle);
83 	}
WriteSQFile84 	SQInteger Write(void *buffer,SQInteger size) {
85 		return sqstd_fwrite(buffer,1,size,_handle);
86 	}
FlushSQFile87 	SQInteger Flush() {
88 		return sqstd_fflush(_handle);
89 	}
TellSQFile90 	SQInteger Tell() {
91 		return sqstd_ftell(_handle);
92 	}
LenSQFile93 	SQInteger Len() {
94 		SQInteger prevpos=Tell();
95 		Seek(0,SQ_SEEK_END);
96 		SQInteger size=Tell();
97 		Seek(prevpos,SQ_SEEK_SET);
98 		return size;
99 	}
SeekSQFile100 	SQInteger Seek(SQInteger offset, SQInteger origin)	{
101 		return sqstd_fseek(_handle,offset,origin);
102 	}
IsValidSQFile103 	bool IsValid() { return _handle?true:false; }
EOSSQFile104 	bool EOS() { return Tell()==Len()?true:false;}
GetHandleSQFile105 	SQFILE GetHandle() {return _handle;}
106 private:
107 	SQFILE _handle;
108 	bool _owns;
109 };
110 
_file__typeof(HSQUIRRELVM v)111 static SQInteger _file__typeof(HSQUIRRELVM v)
112 {
113 	sq_pushstring(v,_SC("file"),-1);
114 	return 1;
115 }
116 
117 // C::B patch: Make the compiler happy by commenting unused variables
_file_releasehook(SQUserPointer p,SQInteger)118 static SQInteger _file_releasehook(SQUserPointer p, SQInteger /*size*/)
119 {
120 	SQFile *self = (SQFile*)p;
121 	delete self;
122 	return 1;
123 }
124 
_file_constructor(HSQUIRRELVM v)125 static SQInteger _file_constructor(HSQUIRRELVM v)
126 {
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 {
163 	SQInteger top = sq_gettop(v);
164 	sq_pushregistrytable(v);
165 	sq_pushstring(v,_SC("std_file"),-1);
166 	if(SQ_SUCCEEDED(sq_get(v,-2))) {
167 		sq_remove(v,-2); //removes the registry
168 		sq_pushroottable(v); // push the this
169 		sq_pushuserpointer(v,file); //file
170 		if(own){
171 			sq_pushinteger(v,1); //true
172 		}
173 		else{
174 			sq_pushnull(v); //false
175 		}
176 		if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) {
177 			sq_remove(v,-2);
178 			return SQ_OK;
179 		}
180 	}
181 	sq_settop(v,top);
182 	return SQ_OK;
183 }
184 
sqstd_getfile(HSQUIRRELVM v,SQInteger idx,SQFILE * file)185 SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file)
186 {
187 	SQFile *fileobj = NULL;
188 	if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) {
189 		*file = fileobj->GetHandle();
190 		return SQ_OK;
191 	}
192 	return sq_throwerror(v,_SC("not a file"));
193 }
194 
195 
196 
_io_file_lexfeed_ASCII(SQUserPointer file)197 static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)
198 {
199 	SQInteger ret;
200 	char c;
201 	if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
202 		return c;
203 	return 0;
204 }
205 
_io_file_lexfeed_UTF8(SQUserPointer file)206 static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)
207 {
208 #define READ() \
209 	if(sqstd_fread(&inchar,sizeof(inchar),1,(FILE *)file) != 1) \
210 		return 0;
211 
212 	static const SQInteger utf8_lengths[16] =
213 	{
214 		1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */
215 		0,0,0,0,                /* 1000 to 1011 : not valid */
216 		2,2,                    /* 1100, 1101 : 2 bytes */
217 		3,                      /* 1110 : 3 bytes */
218 		4                       /* 1111 :4 bytes */
219 	};
220 	static unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
221 	unsigned char inchar;
222 	SQInteger c = 0;
223 	READ();
224 	c = inchar;
225 	//
226 	if(c >= 0x80) {
227 		SQInteger tmp;
228 		SQInteger codelen = utf8_lengths[c>>4];
229 		if(codelen == 0)
230 			return 0;
231 			//"invalid UTF-8 stream";
232 		tmp = c&byte_masks[codelen];
233 		for(SQInteger n = 0; n < codelen-1; n++) {
234 			tmp<<=6;
235 			READ();
236 			tmp |= inchar & 0x3F;
237 		}
238 		c = tmp;
239 	}
240 	return c;
241 }
242 
_io_file_lexfeed_UCS2_LE(SQUserPointer file)243 static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)
244 {
245 	SQInteger ret;
246 	wchar_t c;
247 	if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
248 		return (SQChar)c;
249 	return 0;
250 }
251 
_io_file_lexfeed_UCS2_BE(SQUserPointer file)252 static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)
253 {
254 	SQInteger ret;
255 	unsigned short c;
256 	if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) ) {
257 		c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
258 		return (SQChar)c;
259 	}
260 	return 0;
261 }
262 
file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)263 SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)
264 {
265 	SQInteger ret;
266 	if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;
267 	return -1;
268 }
269 
file_write(SQUserPointer file,SQUserPointer p,SQInteger size)270 SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
271 {
272 	return sqstd_fwrite(p,1,size,(SQFILE)file);
273 }
274 
sqstd_loadfile(HSQUIRRELVM v,const SQChar * filename,SQBool printerror)275 SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)
276 {
277 	SQFILE file = sqstd_fopen(filename,_SC("rb"));
278 	SQInteger ret;
279 	unsigned short us;
280 	unsigned char uc;
281 	SQLEXREADFUNC func = _io_file_lexfeed_ASCII;
282 	if(file){
283 		ret = sqstd_fread(&us,1,2,file);
284 		if(ret != 2) {
285 			//probably an empty file
286 			us = 0;
287 		}
288 		if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE
289 			sqstd_fseek(file,0,SQ_SEEK_SET);
290 			if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {
291 				sqstd_fclose(file);
292 				return SQ_OK;
293 			}
294 		}
295 		else { //SCRIPT
296 			switch(us)
297 			{
298 				//gotta swap the next 2 lines on BIG endian machines
299 				case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
300 				case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
301 				case 0xBBEF:
302 					if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) {
303 						sqstd_fclose(file);
304 						return sq_throwerror(v,_SC("io error"));
305 					}
306 					if(uc != 0xBF) {
307 						sqstd_fclose(file);
308 						return sq_throwerror(v,_SC("Unrecognozed ecoding"));
309 					}
310 					func = _io_file_lexfeed_UTF8;
311 					break;//UTF-8 ;
312 				default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii
313 			}
314 
315 			if(SQ_SUCCEEDED(sq_compile(v,func,file,filename,printerror))){
316 				sqstd_fclose(file);
317 				return SQ_OK;
318 			}
319 		}
320 		sqstd_fclose(file);
321 		return SQ_ERROR;
322 	}
323 	return sq_throwerror(v,_SC("cannot open the file"));
324 }
325 
sqstd_dofile(HSQUIRRELVM v,const SQChar * filename,SQBool retval,SQBool printerror)326 SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
327 {
328 	if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
329 		sq_push(v,-2);
330 		if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
331 			sq_remove(v,retval?-2:-1); //removes the closure
332 			return 1;
333 		}
334 		sq_pop(v,1); //removes the closure
335 	}
336 	return SQ_ERROR;
337 }
338 
sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar * filename)339 SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
340 {
341 	SQFILE file = sqstd_fopen(filename,_SC("wb+"));
342 	if(!file) return sq_throwerror(v,_SC("cannot open the file"));
343 	if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
344 		sqstd_fclose(file);
345 		return SQ_OK;
346 	}
347 	sqstd_fclose(file);
348 	return SQ_ERROR; //forward the error
349 }
350 
_g_io_loadfile(HSQUIRRELVM v)351 SQInteger _g_io_loadfile(HSQUIRRELVM v)
352 {
353 	const SQChar *filename;
354 	SQBool printerror = SQFalse;
355 	sq_getstring(v,2,&filename);
356 	if(sq_gettop(v) >= 3) {
357 		sq_getbool(v,3,&printerror);
358 	}
359 	if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
360 		return 1;
361 	return SQ_ERROR; //propagates the error
362 }
363 
_g_io_writeclosuretofile(HSQUIRRELVM v)364 SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
365 {
366 	const SQChar *filename;
367 	sq_getstring(v,2,&filename);
368 	if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
369 		return 1;
370 	return SQ_ERROR; //propagates the error
371 }
372 
_g_io_dofile(HSQUIRRELVM v)373 SQInteger _g_io_dofile(HSQUIRRELVM v)
374 {
375 	const SQChar *filename;
376 	SQBool printerror = SQFalse;
377 	sq_getstring(v,2,&filename);
378 	if(sq_gettop(v) >= 3) {
379 		sq_getbool(v,3,&printerror);
380 	}
381 	sq_push(v,1); //repush the this
382 	if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
383 		return 1;
384 	return SQ_ERROR; //propagates the error
385 }
386 
387 #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
388 static SQRegFunction iolib_funcs[]={
389 	_DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
390 	_DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
391 	_DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
392 	{0,0}
393 };
394 
sqstd_register_iolib(HSQUIRRELVM v)395 SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
396 {
397 	SQInteger top = sq_gettop(v);
398 	//create delegate
399 	declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
400 	sq_pushstring(v,_SC("stdout"),-1);
401 	sqstd_createfile(v,stdout,SQFalse);
402 	sq_createslot(v,-3);
403 	sq_pushstring(v,_SC("stdin"),-1);
404 	sqstd_createfile(v,stdin,SQFalse);
405 	sq_createslot(v,-3);
406 	sq_pushstring(v,_SC("stderr"),-1);
407 	sqstd_createfile(v,stderr,SQFalse);
408 	sq_createslot(v,-3);
409 	sq_settop(v,top);
410 	return SQ_OK;
411 }
412