sqstdio.cpp 13 KB

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