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