Update SQUIRREL to 2.2.5
[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         delete self;\r
121         return 1;\r
122 }\r
123 \r
124 static SQInteger _file_constructor(HSQUIRRELVM v)\r
125 {\r
126         const SQChar *filename,*mode;\r
127         bool owns = true;\r
128         SQFile *f;\r
129         SQFILE newf;\r
130         if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) {\r
131                 sq_getstring(v, 2, &filename);\r
132                 sq_getstring(v, 3, &mode);\r
133                 newf = sqstd_fopen(filename, mode);\r
134                 if(!newf) return sq_throwerror(v, _SC("cannot open file"));\r
135         } else if(sq_gettype(v,2) == OT_USERPOINTER) {\r
136                 owns = !(sq_gettype(v,3) == OT_NULL);\r
137                 sq_getuserpointer(v,2,&newf);\r
138         } else {\r
139                 return sq_throwerror(v,_SC("wrong parameter"));\r
140         }\r
141         f = new SQFile(newf,owns);\r
142         if(SQ_FAILED(sq_setinstanceup(v,1,f))) {\r
143                 delete f;\r
144                 return sq_throwerror(v, _SC("cannot create blob with negative size"));\r
145         }\r
146         sq_setreleasehook(v,1,_file_releasehook);\r
147         return 0;\r
148 }\r
149 \r
150 //bindings\r
151 #define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}\r
152 static SQRegFunction _file_methods[] = {\r
153         _DECL_FILE_FUNC(constructor,3,_SC("x")),\r
154         _DECL_FILE_FUNC(_typeof,1,_SC("x")),\r
155         {0,0,0,0},\r
156 };\r
157 \r
158 \r
159 \r
160 SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own)\r
161 {\r
162         SQInteger top = sq_gettop(v);\r
163         sq_pushregistrytable(v);\r
164         sq_pushstring(v,_SC("std_file"),-1);\r
165         if(SQ_SUCCEEDED(sq_get(v,-2))) {\r
166                 sq_remove(v,-2); //removes the registry\r
167                 sq_pushroottable(v); // push the this\r
168                 sq_pushuserpointer(v,file); //file\r
169                 if(own){\r
170                         sq_pushinteger(v,1); //true\r
171                 }\r
172                 else{\r
173                         sq_pushnull(v); //false\r
174                 }\r
175                 if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) {\r
176                         sq_remove(v,-2);\r
177                         return SQ_OK;\r
178                 }\r
179         }\r
180         sq_settop(v,top);\r
181         return SQ_OK;\r
182 }\r
183 \r
184 SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file)\r
185 {\r
186         SQFile *fileobj = NULL;\r
187         if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) {\r
188                 *file = fileobj->GetHandle();\r
189                 return SQ_OK;\r
190         }\r
191         return sq_throwerror(v,_SC("not a file"));\r
192 }\r
193 \r
194 \r
195 \r
196 static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)\r
197 {\r
198         SQInteger ret;\r
199         char c;\r
200         if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )\r
201                 return c;\r
202         return 0;\r
203 }\r
204 \r
205 static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)\r
206 {\r
207 #define READ() \\r
208         if(sqstd_fread(&inchar,sizeof(inchar),1,(FILE *)file) != 1) \\r
209                 return 0;\r
210 \r
211         static const SQInteger utf8_lengths[16] =\r
212         {\r
213                 1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */\r
214                 0,0,0,0,                /* 1000 to 1011 : not valid */\r
215                 2,2,                    /* 1100, 1101 : 2 bytes */\r
216                 3,                      /* 1110 : 3 bytes */\r
217                 4                       /* 1111 :4 bytes */\r
218         };\r
219         static unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};\r
220         unsigned char inchar;\r
221         SQInteger c = 0;\r
222         READ();\r
223         c = inchar;\r
224         //\r
225         if(c >= 0x80) {\r
226                 SQInteger tmp;\r
227                 SQInteger codelen = utf8_lengths[c>>4];\r
228                 if(codelen == 0) \r
229                         return 0;\r
230                         //"invalid UTF-8 stream";\r
231                 tmp = c&byte_masks[codelen];\r
232                 for(SQInteger n = 0; n < codelen-1; n++) {\r
233                         tmp<<=6;\r
234                         READ();\r
235                         tmp |= inchar & 0x3F;\r
236                 }\r
237                 c = tmp;\r
238         }\r
239         return c;\r
240 }\r
241 \r
242 static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)\r
243 {\r
244         SQInteger ret;\r
245         wchar_t c;\r
246         if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )\r
247                 return (SQChar)c;\r
248         return 0;\r
249 }\r
250 \r
251 static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)\r
252 {\r
253         SQInteger ret;\r
254         unsigned short c;\r
255         if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) ) {\r
256                 c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);\r
257                 return (SQChar)c;\r
258         }\r
259         return 0;\r
260 }\r
261 \r
262 SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)\r
263 {\r
264         SQInteger ret;\r
265         if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;\r
266         return -1;\r
267 }\r
268 \r
269 SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)\r
270 {\r
271         return sqstd_fwrite(p,1,size,(SQFILE)file);\r
272 }\r
273 \r
274 SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)\r
275 {\r
276         SQFILE file = sqstd_fopen(filename,_SC("rb"));\r
277         SQInteger ret;\r
278         unsigned short us;\r
279         unsigned char uc;\r
280         SQLEXREADFUNC func = _io_file_lexfeed_ASCII;\r
281         if(file){\r
282                 ret = sqstd_fread(&us,1,2,file);\r
283                 if(ret != 2) {\r
284                         //probably an empty file\r
285                         us = 0;\r
286                 }\r
287                 if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE\r
288                         sqstd_fseek(file,0,SQ_SEEK_SET);\r
289                         if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {\r
290                                 sqstd_fclose(file);\r
291                                 return SQ_OK;\r
292                         }\r
293                 }\r
294                 else { //SCRIPT\r
295                         switch(us)\r
296                         {\r
297                                 //gotta swap the next 2 lines on BIG endian machines\r
298                                 case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;\r
299                                 case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;\r
300                                 case 0xBBEF: \r
301                                         if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) { \r
302                                                 sqstd_fclose(file); \r
303                                                 return sq_throwerror(v,_SC("io error")); \r
304                                         }\r
305                                         if(uc != 0xBF) { \r
306                                                 sqstd_fclose(file); \r
307                                                 return sq_throwerror(v,_SC("Unrecognozed ecoding")); \r
308                                         }\r
309                                         func = _io_file_lexfeed_UTF8;\r
310                                         break;//UTF-8 ;\r
311                                 default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii\r
312                         }\r
313 \r
314                         if(SQ_SUCCEEDED(sq_compile(v,func,file,filename,printerror))){\r
315                                 sqstd_fclose(file);\r
316                                 return SQ_OK;\r
317                         }\r
318                 }\r
319                 sqstd_fclose(file);\r
320                 return SQ_ERROR;\r
321         }\r
322         return sq_throwerror(v,_SC("cannot open the file"));\r
323 }\r
324 \r
325 SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)\r
326 {\r
327         if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {\r
328                 sq_push(v,-2);\r
329                 if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {\r
330                         sq_remove(v,retval?-2:-1); //removes the closure\r
331                         return 1;\r
332                 }\r
333                 sq_pop(v,1); //removes the closure\r
334         }\r
335         return SQ_ERROR;\r
336 }\r
337 \r
338 SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)\r
339 {\r
340         SQFILE file = sqstd_fopen(filename,_SC("wb+"));\r
341         if(!file) return sq_throwerror(v,_SC("cannot open the file"));\r
342         if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {\r
343                 sqstd_fclose(file);\r
344                 return SQ_OK;\r
345         }\r
346         sqstd_fclose(file);\r
347         return SQ_ERROR; //forward the error\r
348 }\r
349 \r
350 SQInteger _g_io_loadfile(HSQUIRRELVM v)\r
351 {\r
352         const SQChar *filename;\r
353         SQBool printerror = SQFalse;\r
354         sq_getstring(v,2,&filename);\r
355         if(sq_gettop(v) >= 3) {\r
356                 sq_getbool(v,3,&printerror);\r
357         }\r
358         if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))\r
359                 return 1;\r
360         return SQ_ERROR; //propagates the error\r
361 }\r
362 \r
363 SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)\r
364 {\r
365         const SQChar *filename;\r
366         sq_getstring(v,2,&filename);\r
367         if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))\r
368                 return 1;\r
369         return SQ_ERROR; //propagates the error\r
370 }\r
371 \r
372 SQInteger _g_io_dofile(HSQUIRRELVM v)\r
373 {\r
374         const SQChar *filename;\r
375         SQBool printerror = SQFalse;\r
376         sq_getstring(v,2,&filename);\r
377         if(sq_gettop(v) >= 3) {\r
378                 sq_getbool(v,3,&printerror);\r
379         }\r
380         sq_push(v,1); //repush the this\r
381         if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))\r
382                 return 1;\r
383         return SQ_ERROR; //propagates the error\r
384 }\r
385 \r
386 #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}\r
387 static SQRegFunction iolib_funcs[]={\r
388         _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),\r
389         _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),\r
390         _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),\r
391         {0,0}\r
392 };\r
393 \r
394 SQRESULT sqstd_register_iolib(HSQUIRRELVM v)\r
395 {\r
396         SQInteger top = sq_gettop(v);\r
397         //create delegate\r
398         declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);\r
399         sq_pushstring(v,_SC("stdout"),-1);\r
400         sqstd_createfile(v,stdout,SQFalse);\r
401         sq_createslot(v,-3);\r
402         sq_pushstring(v,_SC("stdin"),-1);\r
403         sqstd_createfile(v,stdin,SQFalse);\r
404         sq_createslot(v,-3);\r
405         sq_pushstring(v,_SC("stderr"),-1);\r
406         sqstd_createfile(v,stderr,SQFalse);\r
407         sq_createslot(v,-3);\r
408         sq_settop(v,top);\r
409         return SQ_OK;\r
410 }\r