Back to home page

Enduro/X

 
 

    


0001 /* see copyright notice in pscript.h */
0002 #include <new>
0003 #include <stdio.h>
0004 #include <pscript.h>
0005 #include <psstdio.h>
0006 #include "psstdstream.h"
0007 #include <ndrstandard.h>
0008 #include <errno.h>
0009 
0010 #define PSSTD_FILE_TYPE_TAG (PSSTD_STREAM_TYPE_TAG | 0x00000001)
0011 //basic API
0012 PSFILE psstd_fopen(const PSChar *filename ,const PSChar *mode)
0013 {
0014 #ifndef PSUNICODE
0015     return (PSFILE)fopen(filename,mode);
0016 #else
0017     return (PSFILE)_wfopen(filename,mode);
0018 #endif
0019 }
0020 
0021 PSInteger psstd_fread(void* buffer, PSInteger size, PSInteger count, PSFILE file)
0022 {
0023     PSInteger ret = (PSInteger)fread(buffer,size,count,(FILE *)file);
0024     return ret;
0025 }
0026 
0027 PSUserPointer psstd_fgets(PSUserPointer buffer, PSInteger size, PSFILE file)
0028 {
0029     char *ret = fgets((char *)buffer,size,(FILE *)file);
0030     
0031     //Strip off \r\n
0032     if (NULL!=ret)
0033     {
0034         int len = strlen(ret);
0035         
0036         if (len>0 && ret[len-1]=='\n')
0037         {
0038             ret[len-1]=EXEOS;
0039             
0040             if (len>1 && ret[len-2]=='\r')
0041             {
0042                 ret[len-2]=EXEOS;
0043             }
0044         }
0045     }
0046     
0047     return (PSUserPointer)ret;
0048     
0049 }
0050 
0051 PSInteger psstd_fwrite(const PSUserPointer buffer, PSInteger size, PSInteger count, PSFILE file)
0052 {
0053     return (PSInteger)fwrite(buffer,size,count,(FILE *)file);
0054 }
0055 
0056 PSInteger psstd_fseek(PSFILE file, PSInteger offset, PSInteger origin)
0057 {
0058     PSInteger realorigin;
0059     switch(origin) {
0060         case PS_SEEK_CUR: realorigin = SEEK_CUR; break;
0061         case PS_SEEK_END: realorigin = SEEK_END; break;
0062         case PS_SEEK_SET: realorigin = SEEK_SET; break;
0063         default: return -1; //failed
0064     }
0065     return fseek((FILE *)file,(long)offset,(int)realorigin);
0066 }
0067 
0068 PSInteger psstd_ftell(PSFILE file)
0069 {
0070     return ftell((FILE *)file);
0071 }
0072 
0073 PSInteger psstd_fflush(PSFILE file)
0074 {
0075     return fflush((FILE *)file);
0076 }
0077 
0078 PSInteger psstd_fclose(PSFILE file)
0079 {
0080     return fclose((FILE *)file);
0081 }
0082 
0083 PSInteger psstd_feof(PSFILE file)
0084 {
0085     return feof((FILE *)file);
0086 }
0087 
0088 //File
0089 struct PSFile : public PSStream {
0090     PSFile() { _handle = NULL; _owns = false;}
0091     PSFile(PSFILE file, bool owns) { _handle = file; _owns = owns;}
0092     virtual ~PSFile() { Close(); }
0093     bool Open(const PSChar *filename ,const PSChar *mode) {
0094         Close();
0095         if( (_handle = psstd_fopen(filename,mode)) ) {
0096             _owns = true;
0097             return true;
0098         }
0099         return false;
0100     }
0101     void Close() {
0102         if(_handle && _owns) {
0103             psstd_fclose(_handle);
0104             _handle = NULL;
0105             _owns = false;
0106         }
0107     }
0108     PSInteger Read(void *buffer,PSInteger size) {
0109         return psstd_fread(buffer,1,size,_handle);
0110     }
0111     PSUserPointer ReadLine(PSUserPointer buffer,PSInteger size) {
0112         return psstd_fgets(buffer,size,_handle);
0113     }
0114     PSInteger Write(void *buffer,PSInteger size) {
0115         return psstd_fwrite(buffer,1,size,_handle);
0116     }
0117     PSInteger Flush() {
0118         return psstd_fflush(_handle);
0119     }
0120     PSInteger Tell() {
0121         return psstd_ftell(_handle);
0122     }
0123     PSInteger Len() {
0124         PSInteger prevpos=Tell();
0125         Seek(0,PS_SEEK_END);
0126         PSInteger size=Tell();
0127         Seek(prevpos,PS_SEEK_SET);
0128         return size;
0129     }
0130     PSInteger Seek(PSInteger offset, PSInteger origin)  {
0131         return psstd_fseek(_handle,offset,origin);
0132     }
0133     bool IsValid() { return _handle?true:false; }
0134     bool EOS() { return Tell()==Len()?true:false;}
0135     PSFILE GetHandle() {return _handle;}
0136 private:
0137     PSFILE _handle;
0138     bool _owns;
0139 };
0140 
0141 static PSInteger _file__typeof(HPSCRIPTVM v)
0142 {
0143     ps_pushstring(v,_SC("file"),-1);
0144     return 1;
0145 }
0146 
0147 static PSInteger _file_releasehook(PSUserPointer p, PSInteger PS_UNUSED_ARG(size))
0148 {
0149     PSFile *self = (PSFile*)p;
0150     self->~PSFile();
0151     ps_free(self,sizeof(PSFile));
0152     return 1;
0153 }
0154 
0155 static PSInteger _file_constructor(HPSCRIPTVM v)
0156 {
0157     const PSChar *filename,*mode;
0158     bool owns = true;
0159     char dbg_name[PATH_MAX+1];
0160     PSFile *f;
0161     PSFILE newf;
0162     if(ps_gettype(v,2) == OT_STRING && ps_gettype(v,3) == OT_STRING) {
0163         ps_getstring(v, 2, &filename);
0164         ps_getstring(v, 3, &mode);
0165         newf = psstd_fopen(filename, mode);
0166         if(!newf) {
0167         int err = errno;
0168         snprintf(dbg_name, sizeof(dbg_name), _SC("cannot open file [%s]: %s"),
0169                 filename, strerror(errno));
0170         return ps_throwerror(v, dbg_name);
0171     }
0172     } else if(ps_gettype(v,2) == OT_USERPOINTER) {
0173         owns = !(ps_gettype(v,3) == OT_NULL);
0174         ps_getuserpointer(v,2,&newf);
0175     } else {
0176         return ps_throwerror(v,_SC("wrong parameter"));
0177     }
0178 
0179     f = new (ps_malloc(sizeof(PSFile)))PSFile(newf,owns);
0180     if(PS_FAILED(ps_setinstanceup(v,1,f))) {
0181         f->~PSFile();
0182         ps_free(f,sizeof(PSFile));
0183         return ps_throwerror(v, _SC("cannot create blob with negative size"));
0184     }
0185     ps_setreleasehook(v,1,_file_releasehook);
0186     return 0;
0187 }
0188 
0189 static PSInteger _file_close(HPSCRIPTVM v)
0190 {
0191     PSFile *self = NULL;
0192     if(PS_SUCCEEDED(ps_getinstanceup(v,1,(PSUserPointer*)&self,(PSUserPointer)PSSTD_FILE_TYPE_TAG))
0193         && self != NULL)
0194     {
0195         self->Close();
0196     }
0197     return 0;
0198 }
0199 
0200 //bindings
0201 #define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}
0202 static const PSRegFunction _file_methods[] = {
0203     _DECL_FILE_FUNC(constructor,3,_SC("x")),
0204     _DECL_FILE_FUNC(_typeof,1,_SC("x")),
0205     _DECL_FILE_FUNC(close,1,_SC("x")),
0206     {NULL,(PSFUNCTION)0,0,NULL}
0207 };
0208 
0209 
0210 
0211 PSRESULT psstd_createfile(HPSCRIPTVM v, PSFILE file,PSBool own)
0212 {
0213     PSInteger top = ps_gettop(v);
0214     ps_pushregistrytable(v);
0215     ps_pushstring(v,_SC("std_file"),-1);
0216     if(PS_SUCCEEDED(ps_get(v,-2))) {
0217         ps_remove(v,-2); //removes the registry
0218         ps_pushroottable(v); // push the this
0219         ps_pushuserpointer(v,file); //file
0220         if(own){
0221             ps_pushinteger(v,1); //true
0222         }
0223         else{
0224             ps_pushnull(v); //false
0225         }
0226         if(PS_SUCCEEDED( ps_call(v,3,PSTrue,PSFalse) )) {
0227             ps_remove(v,-2);
0228             return PS_OK;
0229         }
0230     }
0231     ps_settop(v,top);
0232     return PS_ERROR;
0233 }
0234 
0235 PSRESULT psstd_getfile(HPSCRIPTVM v, PSInteger idx, PSFILE *file)
0236 {
0237     PSFile *fileobj = NULL;
0238     if(PS_SUCCEEDED(ps_getinstanceup(v,idx,(PSUserPointer*)&fileobj,(PSUserPointer)PSSTD_FILE_TYPE_TAG))) {
0239         *file = fileobj->GetHandle();
0240         return PS_OK;
0241     }
0242     return ps_throwerror(v,_SC("not a file"));
0243 }
0244 
0245 
0246 
0247 #define IO_BUFFER_SIZE 2048
0248 struct IOBuffer {
0249     unsigned char buffer[IO_BUFFER_SIZE];
0250     PSInteger size;
0251     PSInteger ptr;
0252     PSFILE file;
0253 };
0254 
0255 PSInteger _read_byte(IOBuffer *iobuffer)
0256 {
0257     if(iobuffer->ptr < iobuffer->size) {
0258 
0259         PSInteger ret = iobuffer->buffer[iobuffer->ptr];
0260         iobuffer->ptr++;
0261         return ret;
0262     }
0263     else {
0264         if( (iobuffer->size = psstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 )
0265         {
0266             PSInteger ret = iobuffer->buffer[0];
0267             iobuffer->ptr = 1;
0268             return ret;
0269         }
0270     }
0271 
0272     return 0;
0273 }
0274 
0275 PSInteger _read_two_bytes(IOBuffer *iobuffer)
0276 {
0277     if(iobuffer->ptr < iobuffer->size) {
0278         if(iobuffer->size < 2) return 0;
0279         PSInteger ret = *((const wchar_t*)&iobuffer->buffer[iobuffer->ptr]);
0280         iobuffer->ptr += 2;
0281         return ret;
0282     }
0283     else {
0284         if( (iobuffer->size = psstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 )
0285         {
0286             if(iobuffer->size < 2) return 0;
0287             PSInteger ret = *((const wchar_t*)&iobuffer->buffer[0]);
0288             iobuffer->ptr = 2;
0289             return ret;
0290         }
0291     }
0292 
0293     return 0;
0294 }
0295 
0296 static PSInteger _io_file_lexfeed_PLAIN(PSUserPointer iobuf)
0297 {
0298     IOBuffer *iobuffer = (IOBuffer *)iobuf;
0299     return _read_byte(iobuffer);
0300 
0301 }
0302 
0303 #ifdef PSUNICODE
0304 static PSInteger _io_file_lexfeed_UTF8(PSUserPointer iobuf)
0305 {
0306     IOBuffer *iobuffer = (IOBuffer *)iobuf;
0307 #define READ(iobuf) \
0308     if((inchar = (unsigned char)_read_byte(iobuf)) == 0) \
0309         return 0;
0310 
0311     static const PSInteger utf8_lengths[16] =
0312     {
0313         1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */
0314         0,0,0,0,                /* 1000 to 1011 : not valid */
0315         2,2,                    /* 1100, 1101 : 2 bytes */
0316         3,                      /* 1110 : 3 bytes */
0317         4                       /* 1111 :4 bytes */
0318     };
0319     static const unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
0320     unsigned char inchar;
0321     PSInteger c = 0;
0322     READ(iobuffer);
0323     c = inchar;
0324     //
0325     if(c >= 0x80) {
0326         PSInteger tmp;
0327         PSInteger codelen = utf8_lengths[c>>4];
0328         if(codelen == 0)
0329             return 0;
0330             //"invalid UTF-8 stream";
0331         tmp = c&byte_masks[codelen];
0332         for(PSInteger n = 0; n < codelen-1; n++) {
0333             tmp<<=6;
0334             READ(iobuffer);
0335             tmp |= inchar & 0x3F;
0336         }
0337         c = tmp;
0338     }
0339     return c;
0340 }
0341 #endif
0342 
0343 static PSInteger _io_file_lexfeed_UCS2_LE(PSUserPointer iobuf)
0344 {
0345     PSInteger ret;
0346     IOBuffer *iobuffer = (IOBuffer *)iobuf;
0347     if( (ret = _read_two_bytes(iobuffer)) > 0 )
0348         return ret;
0349     return 0;
0350 }
0351 
0352 static PSInteger _io_file_lexfeed_UCS2_BE(PSUserPointer iobuf)
0353 {
0354     PSInteger c;
0355     IOBuffer *iobuffer = (IOBuffer *)iobuf;
0356     if( (c = _read_two_bytes(iobuffer)) > 0 ) {
0357         c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
0358         return c;
0359     }
0360     return 0;
0361 }
0362 
0363 PSInteger file_read(PSUserPointer file,PSUserPointer buf,PSInteger size)
0364 {
0365     PSInteger ret;
0366     if( ( ret = psstd_fread(buf,1,size,(PSFILE)file ))!=0 )return ret;
0367     return -1;
0368 }
0369 
0370 PSInteger file_write(PSUserPointer file,PSUserPointer p,PSInteger size)
0371 {
0372     return psstd_fwrite(p,1,size,(PSFILE)file);
0373 }
0374 
0375 PSRESULT psstd_loadfile(HPSCRIPTVM v,const PSChar *filename,PSBool printerror)
0376 {
0377     PSFILE file = psstd_fopen(filename,_SC("rb"));
0378 
0379     PSInteger ret;
0380     unsigned short us;
0381     unsigned char uc;
0382     PSLEXREADFUNC func = _io_file_lexfeed_PLAIN;
0383     if(file){
0384         ret = psstd_fread(&us,1,2,file);
0385         if(ret != 2) {
0386             //probably an empty file
0387             us = 0;
0388         }
0389         if(us == PS_BYTECODE_STREAM_TAG) { //BYTECODE
0390             psstd_fseek(file,0,PS_SEEK_SET);
0391             if(PS_SUCCEEDED(ps_readclosure(v,file_read,file))) {
0392                 psstd_fclose(file);
0393                 return PS_OK;
0394             }
0395         }
0396         else { //SCRIPT
0397 
0398             switch(us)
0399             {
0400                 //gotta swap the next 2 lines on BIG endian machines
0401                 case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
0402                 case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
0403                 case 0xBBEF:
0404                     if(psstd_fread(&uc,1,sizeof(uc),file) == 0) {
0405                         psstd_fclose(file);
0406                         return ps_throwerror(v,_SC("io error"));
0407                     }
0408                     if(uc != 0xBF) {
0409                         psstd_fclose(file);
0410                         return ps_throwerror(v,_SC("Unrecognozed ecoding"));
0411                     }
0412 #ifdef PSUNICODE
0413                     func = _io_file_lexfeed_UTF8;
0414 #else
0415                     func = _io_file_lexfeed_PLAIN;
0416 #endif
0417                     break;//UTF-8 ;
0418                 default: psstd_fseek(file,0,PS_SEEK_SET); break; // ascii
0419             }
0420             IOBuffer buffer;
0421             buffer.ptr = 0;
0422             buffer.size = 0;
0423             buffer.file = file;
0424             if(PS_SUCCEEDED(ps_compile(v,func,&buffer,filename,printerror))){
0425                 psstd_fclose(file);
0426                 return PS_OK;
0427             }
0428         }
0429         psstd_fclose(file);
0430         return PS_ERROR;
0431     }
0432     return ps_throwerror(v,_SC("cannot open the file"));
0433 }
0434 
0435 /**
0436  * Memory reader
0437  * @param memptr field offset must be reset to 0 when starting to read
0438  * @param buf output buffer
0439  * @param size output buffer size
0440  * @return bytes read, or -1 (in case of error or EOF)
0441  */
0442 static PSInteger mem_read(PSUserPointer memreader,PSUserPointer buf,PSInteger size)
0443 {
0444     PSInteger ret;
0445     PSMemReader *reader = (PSMemReader *)memreader;
0446     int out_block = reader->size - reader->offset;
0447     
0448     if (out_block > size)
0449     {
0450         out_block = size;
0451     }
0452     else if (out_block==0)
0453     {
0454         /* nothing to load / EOF */
0455         ret = -1;
0456         goto out;
0457     }
0458     /* load bytes */
0459     memcpy(buf, reader->memptr+reader->offset, out_block);
0460     reader->offset+=out_block;
0461     ret = out_block;
0462     
0463 out:
0464     return ret;
0465 }
0466 
0467 /**
0468  * Load bytecode from memory block
0469  * @param v VirtualMachine
0470  * @param reader ptr to mem reader structure
0471  * @return PS_OK, PS_ERROR 
0472  */
0473 PSRESULT psstd_loadmem(HPSCRIPTVM v, PSMemReader *reader)
0474 {
0475     if(PS_SUCCEEDED(ps_readclosure(v,mem_read,reader))) {
0476         return PS_OK;
0477     }
0478     return PS_ERROR;
0479 }
0480 
0481 PSRESULT psstd_dofile(HPSCRIPTVM v,const PSChar *filename,PSBool retval,PSBool printerror)
0482 {
0483     if(PS_SUCCEEDED(psstd_loadfile(v,filename,printerror))) {
0484         ps_push(v,-2);
0485         if(PS_SUCCEEDED(ps_call(v,1,retval,PSTrue))) {
0486             ps_remove(v,retval?-2:-1); //removes the closure
0487             return 1;
0488         }
0489         ps_pop(v,1); //removes the closure
0490     }
0491     return PS_ERROR;
0492 }
0493 
0494 PSRESULT psstd_writeclosuretofile(HPSCRIPTVM v,const PSChar *filename)
0495 {
0496     PSFILE file = psstd_fopen(filename,_SC("wb+"));
0497     if(!file) return ps_throwerror(v,_SC("cannot open the file"));
0498     if(PS_SUCCEEDED(ps_writeclosure(v,file_write,file))) {
0499         psstd_fclose(file);
0500         return PS_OK;
0501     }
0502     psstd_fclose(file);
0503     return PS_ERROR; //forward the error
0504 }
0505 
0506 PSInteger _g_io_loadfile(HPSCRIPTVM v)
0507 {
0508     const PSChar *filename;
0509     PSBool printerror = PSFalse;
0510     ps_getstring(v,2,&filename);
0511     if(ps_gettop(v) >= 3) {
0512         ps_getbool(v,3,&printerror);
0513     }
0514     if(PS_SUCCEEDED(psstd_loadfile(v,filename,printerror)))
0515         return 1;
0516     return PS_ERROR; //propagates the error
0517 }
0518 
0519 PSInteger _g_io_writeclosuretofile(HPSCRIPTVM v)
0520 {
0521     const PSChar *filename;
0522     ps_getstring(v,2,&filename);
0523     if(PS_SUCCEEDED(psstd_writeclosuretofile(v,filename)))
0524         return 1;
0525     return PS_ERROR; //propagates the error
0526 }
0527 
0528 PSInteger _g_io_dofile(HPSCRIPTVM v)
0529 {
0530     const PSChar *filename;
0531     PSBool printerror = PSFalse;
0532     ps_getstring(v,2,&filename);
0533     if(ps_gettop(v) >= 3) {
0534         ps_getbool(v,3,&printerror);
0535     }
0536     ps_push(v,1); //repush the this
0537     if(PS_SUCCEEDED(psstd_dofile(v,filename,PSTrue,printerror)))
0538         return 1;
0539     return PS_ERROR; //propagates the error
0540 }
0541 
0542 #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
0543 static const PSRegFunction iolib_funcs[]={
0544     _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
0545     _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
0546     _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
0547     {NULL,(PSFUNCTION)0,0,NULL}
0548 };
0549 
0550 PSRESULT psstd_register_iolib(HPSCRIPTVM v)
0551 {
0552     PSInteger top = ps_gettop(v);
0553     //create delegate
0554     declare_stream(v,_SC("file"),(PSUserPointer)PSSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
0555     ps_pushstring(v,_SC("stdout"),-1);
0556     psstd_createfile(v,stdout,PSFalse);
0557     ps_newslot(v,-3,PSFalse);
0558     ps_pushstring(v,_SC("stdin"),-1);
0559     psstd_createfile(v,stdin,PSFalse);
0560     ps_newslot(v,-3,PSFalse);
0561     ps_pushstring(v,_SC("stderr"),-1);
0562     psstd_createfile(v,stderr,PSFalse);
0563     ps_newslot(v,-3,PSFalse);
0564     ps_settop(v,top);
0565     return PS_OK;
0566 }