sqstdio.cpp 12 KB

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