# sqstdio.cpp

    00001 /* see copyright notice in squirrel.h */
    00002 #include <stdio.h>
    00003 #include <squirrel.h>
    00004 #include <new>
    00005 #include <sqstdio.h>
    00006 #include "sqstdstream.h"
    00007
    00008 #define SQSTD_FILE_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000001)
    00009 //basic API
    00010 SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode)
    00011 {
    00012 #ifndef SQUNICODE
    00013   return (SQFILE)fopen(filename,mode);
    00014 #else
    00015   return (SQFILE)_wfopen(filename,mode);
    00016 #endif
    00017 }
    00018
    00019 SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file)
    00020 {
    00021   return (SQInteger)fread(buffer,size,count,(FILE *)file);
    00022 }
    00023
    00024 SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file)
    00025 {
    00026   return (SQInteger)fwrite(buffer,size,count,(FILE *)file);
    00027 }
    00028
    00029 SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin)
    00030 {
    00031   SQInteger realorigin;
    00032   switch(origin) {
    00033     case SQ_SEEK_CUR: realorigin = SEEK_CUR; break;
    00034     case SQ_SEEK_END: realorigin = SEEK_END; break;
    00035     case SQ_SEEK_SET: realorigin = SEEK_SET; break;
    00036     default: return -1; //failed
    00037   }
    00038   return fseek((FILE *)file,(long)offset,(int)realorigin);
    00039 }
    00040
    00041 SQInteger sqstd_ftell(SQFILE file)
    00042 {
    00043   return ftell((FILE *)file);
    00044 }
    00045
    00046 SQInteger sqstd_fflush(SQFILE file)
    00047 {
    00048   return fflush((FILE *)file);
    00049 }
    00050
    00051 SQInteger sqstd_fclose(SQFILE file)
    00052 {
    00053   return fclose((FILE *)file);
    00054 }
    00055
    00056 SQInteger sqstd_feof(SQFILE file)
    00057 {
    00058   return feof((FILE *)file);
    00059 }
    00060
    00061 //File
    00062 struct SQFile : public SQStream {
    00063   SQFile() { _handle = NULL; _owns = false;}
    00064   SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;}
    00065   virtual ~SQFile() { Close(); }
    00066   bool Open(const SQChar *filename ,const SQChar *mode) {
    00067     Close();
    00068     if( (_handle = sqstd_fopen(filename,mode)) ) {
    00069       _owns = true;
    00070       return true;
    00071     }
    00072     return false;
    00073   }
    00074   void Close() {
    00075     if(_handle && _owns) {
    00076       sqstd_fclose(_handle);
    00077       _handle = NULL;
    00078       _owns = false;
    00079     }
    00080   }
    00081   SQInteger Read(void *buffer,SQInteger size) {
    00082     return sqstd_fread(buffer,1,size,_handle);
    00083   }
    00084   SQInteger Write(void *buffer,SQInteger size) {
    00085     return sqstd_fwrite(buffer,1,size,_handle);
    00086   }
    00087   SQInteger Flush() {
    00088     return sqstd_fflush(_handle);
    00089   }
    00090   SQInteger Tell() {
    00091     return sqstd_ftell(_handle);
    00092   }
    00093   SQInteger Len() {
    00094     SQInteger prevpos=Tell();
    00095     Seek(0,SQ_SEEK_END);
    00096     SQInteger size=Tell();
    00097     Seek(prevpos,SQ_SEEK_SET);
    00098     return size;
    00099   }
    00100   SQInteger Seek(SQInteger offset, SQInteger origin)  {
    00101     return sqstd_fseek(_handle,offset,origin);
    00102   }
    00103   bool IsValid() { return _handle?true:false; }
    00104   bool EOS() { return Tell()==Len()?true:false;}
    00105   SQFILE GetHandle() {return _handle;}
    00106 private:
    00107   SQFILE _handle;
    00108   bool _owns;
    00109 };
    00110
    00111 static SQInteger _file__typeof(HSQUIRRELVM v)
    00112 {
    00113   sq_pushstring(v,_SC("file"),-1);
    00114   return 1;
    00115 }
    00116
    00117 static SQInteger _file_releasehook(SQUserPointer p, SQInteger size)
    00118 {
    00119   SQFile *self = (SQFile*)p;
    00120   delete self;
    00121   return 1;
    00122 }
    00123
    00124 static SQInteger _file_constructor(HSQUIRRELVM v)
    00125 {
    00126   const SQChar *filename,*mode;
    00127   bool owns = true;
    00128   SQFile *f;
    00129   SQFILE newf;
    00130   if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) {
    00131     sq_getstring(v, 2, &filename);
    00132     sq_getstring(v, 3, &mode);
    00133     newf = sqstd_fopen(filename, mode);
    00134     if(!newf) return sq_throwerror(v, _SC("cannot open file"));
    00135   } else if(sq_gettype(v,2) == OT_USERPOINTER) {
    00136     owns = !(sq_gettype(v,3) == OT_NULL);
    00137     sq_getuserpointer(v,2,&newf);
    00138   } else {
    00139     return sq_throwerror(v,_SC("wrong parameter"));
    00140   }
    00141   f = new SQFile(newf,owns);
    00142   if(SQ_FAILED(sq_setinstanceup(v,1,f))) {
    00143     delete f;
    00144     return sq_throwerror(v, _SC("cannot create blob with negative size"));
    00145   }
    00146   sq_setreleasehook(v,1,_file_releasehook);
    00147   return 0;
    00148 }
    00149
    00150 //bindings
    00151 #define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}
    00152 static SQRegFunction _file_methods[] = {
    00153   _DECL_FILE_FUNC(constructor,3,_SC("x")),
    00154   _DECL_FILE_FUNC(_typeof,1,_SC("x")),
    00155   {0,0,0,0},
    00156 };
    00157
    00158
    00159
    00160 SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own)
    00161 {
    00162   SQInteger top = sq_gettop(v);
    00163   sq_pushregistrytable(v);
    00164   sq_pushstring(v,_SC("std_file"),-1);
    00165   if(SQ_SUCCEEDED(sq_get(v,-2))) {
    00166     sq_remove(v,-2); //removes the registry
    00167     sq_pushroottable(v); // push the this
    00168     sq_pushuserpointer(v,file); //file
    00169     if(own){
    00170       sq_pushinteger(v,1); //true
    00171     }
    00172     else{
    00173       sq_pushnull(v); //false
    00174     }
    00175     if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) {
    00176       sq_remove(v,-2);
    00177       return SQ_OK;
    00178     }
    00179   }
    00180   sq_settop(v,top);
    00181   return SQ_OK;
    00182 }
    00183
    00184 SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file)
    00185 {
    00186   SQFile *fileobj = NULL;
    00187   if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) {
    00188     *file = fileobj->GetHandle();
    00189     return SQ_OK;
    00190   }
    00191   return sq_throwerror(v,_SC("not a file"));
    00192 }
    00193
    00194
    00195
    00196 static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)
    00197 {
    00198   SQInteger ret;
    00199   char c;
    00200   if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
    00201     return c;
    00202   return 0;
    00203 }
    00204
    00205 static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)
    00206 {
    00207 #define READ() \
    00208   if(sqstd_fread(&inchar,sizeof(inchar),1,(FILE *)file) != 1) \
    00209     return 0;
    00210
    00211   static const SQInteger utf8_lengths[16] =
    00212   {
    00213     1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */
    00214     0,0,0,0,                /* 1000 to 1011 : not valid */
    00215     2,2,                    /* 1100, 1101 : 2 bytes */
    00216     3,                      /* 1110 : 3 bytes */
    00217     4                       /* 1111 :4 bytes */
    00218   };
    00219   static unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
    00220   unsigned char inchar;
    00221   SQInteger c = 0;
    00222   READ();
    00223   c = inchar;
    00224   //
    00225   if(c >= 0x80) {
    00226     SQInteger tmp;
    00227     SQInteger codelen = utf8_lengths[c>>4];
    00228     if(codelen == 0)
    00229       return 0;
    00230       //"invalid UTF-8 stream";
    00231     tmp = c&byte_masks[codelen];
    00232     for(SQInteger n = 0; n < codelen-1; n++) {
    00233       tmp<<=6;
    00234       READ();
    00235       tmp |= inchar & 0x3F;
    00236     }
    00237     c = tmp;
    00238   }
    00239   return c;
    00240 }
    00241
    00242 static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)
    00243 {
    00244   SQInteger ret;
    00245   wchar_t c;
    00246   if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
    00247     return (SQChar)c;
    00248   return 0;
    00249 }
    00250
    00251 static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)
    00252 {
    00253   SQInteger ret;
    00254   unsigned short c;
    00255   if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) ) {
    00256     c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
    00257     return (SQChar)c;
    00258   }
    00259   return 0;
    00260 }
    00261
    00262 SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)
    00263 {
    00264   SQInteger ret;
    00265   if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;
    00266   return -1;
    00267 }
    00268
    00269 SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
    00270 {
    00271   return sqstd_fwrite(p,1,size,(SQFILE)file);
    00272 }
    00273
    00274 SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)
    00275 {
    00276   SQFILE file = sqstd_fopen(filename,_SC("rb"));
    00277   SQInteger ret;
    00278   unsigned short us;
    00279   unsigned char uc;
    00280   SQLEXREADFUNC func = _io_file_lexfeed_ASCII;
    00281   if(file){
    00282     ret = sqstd_fread(&us,1,2,file);
    00283     if(ret != 2) {
    00284       //probably an empty file
    00285       us = 0;
    00286     }
    00287     if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE
    00288       sqstd_fseek(file,0,SQ_SEEK_SET);
    00289       if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {
    00290         sqstd_fclose(file);
    00291         return SQ_OK;
    00292       }
    00293     }
    00294     else { //SCRIPT
    00295       switch(us)
    00296       {
    00297         //gotta swap the next 2 lines on BIG endian machines
    00298         case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
    00299         case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
    00300         case 0xBBEF:
    00301           if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) {
    00302             sqstd_fclose(file);
    00303             return sq_throwerror(v,_SC("io error"));
    00304           }
    00305           if(uc != 0xBF) {
    00306             sqstd_fclose(file);
    00307             return sq_throwerror(v,_SC("Unrecognozed ecoding"));
    00308           }
    00309           func = _io_file_lexfeed_UTF8;
    00310           break;//UTF-8 ;
    00311         default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii
    00312       }
    00313
    00314       if(SQ_SUCCEEDED(sq_compile(v,func,file,filename,printerror))){
    00315         sqstd_fclose(file);
    00316         return SQ_OK;
    00317       }
    00318     }
    00319     sqstd_fclose(file);
    00320     return SQ_ERROR;
    00321   }
    00322   return sq_throwerror(v,_SC("cannot open the file"));
    00323 }
    00324
    00325 SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
    00326 {
    00327   if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
    00328     sq_push(v,-2);
    00329     if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
    00330       sq_remove(v,retval?-2:-1); //removes the closure
    00331       return 1;
    00332     }
    00333     sq_pop(v,1); //removes the closure
    00334   }
    00335   return SQ_ERROR;
    00336 }
    00337
    00338 SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
    00339 {
    00340   SQFILE file = sqstd_fopen(filename,_SC("wb+"));
    00341   if(!file) return sq_throwerror(v,_SC("cannot open the file"));
    00342   if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
    00343     sqstd_fclose(file);
    00344     return SQ_OK;
    00345   }
    00346   sqstd_fclose(file);
    00347   return SQ_ERROR; //forward the error
    00348 }
    00349
    00350 SQInteger _g_io_loadfile(HSQUIRRELVM v)
    00351 {
    00352   const SQChar *filename;
    00353   SQBool printerror = SQFalse;
    00354   sq_getstring(v,2,&filename);
    00355   if(sq_gettop(v) >= 3) {
    00356     sq_getbool(v,3,&printerror);
    00357   }
    00358   if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
    00359     return 1;
    00360   return SQ_ERROR; //propagates the error
    00361 }
    00362
    00363 SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
    00364 {
    00365   const SQChar *filename;
    00366   sq_getstring(v,2,&filename);
    00367   if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
    00368     return 1;
    00369   return SQ_ERROR; //propagates the error
    00370 }
    00371
    00372 SQInteger _g_io_dofile(HSQUIRRELVM v)
    00373 {
    00374   const SQChar *filename;
    00375   SQBool printerror = SQFalse;
    00376   sq_getstring(v,2,&filename);
    00377   if(sq_gettop(v) >= 3) {
    00378     sq_getbool(v,3,&printerror);
    00379   }
    00380   sq_push(v,1); //repush the this
    00381   if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
    00382     return 1;
    00383   return SQ_ERROR; //propagates the error
    00384 }
    00385
    00386 #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
    00387 static SQRegFunction iolib_funcs[]={
    00388   _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
    00389   _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
    00390   _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
    00391   {0,0,0,0}
    00392 };
    00393
    00394 SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
    00395 {
    00396   SQInteger top = sq_gettop(v);
    00397   //create delegate
    00398   declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
    00399   sq_pushstring(v,_SC("stdout"),-1);
    00400   sqstd_createfile(v,stdout,SQFalse);
    00401   sq_createslot(v,-3);
    00402   sq_pushstring(v,_SC("stdin"),-1);
    00403   sqstd_createfile(v,stdin,SQFalse);
    00404   sq_createslot(v,-3);
    00405   sq_pushstring(v,_SC("stderr"),-1);
    00406   sqstd_createfile(v,stderr,SQFalse);
    00407   sq_createslot(v,-3);
    00408   sq_settop(v,top);
    00409   return SQ_OK;
    00410 }