sqstdio.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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 ((SQUnsignedInteger)(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. SQInteger ret = (SQInteger)fread(buffer,size,count,(FILE *)file);
  20. return ret;
  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(void *buffer,SQInteger size) {
  77. return sqstd_fwrite(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 SQ_UNUSED_ARG(size))
  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, SQTrue))
  144. && self != NULL)
  145. {
  146. self->Close();
  147. }
  148. return 0;
  149. }
  150. //bindings
  151. #define _DECL_FILE_FUNC(name,nparamsmin,nparamsmax,typecheck) {_SC(#name),_file_##name,nparamsmin,nparamsmax,typecheck}
  152. static const SQRegFunction _file_methods[] = {
  153. _DECL_FILE_FUNC(constructor,3,3,_SC("x")),
  154. _DECL_FILE_FUNC(_typeof,1,1,_SC("x")),
  155. _DECL_FILE_FUNC(close,1,1,_SC("x")),
  156. {NULL,(SQFUNCTION)0,0,0,NULL}
  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,SQFalse))) {
  185. *file = fileobj->GetHandle();
  186. return SQ_OK;
  187. }
  188. return sq_throwerror(v,_SC("not a file"));
  189. }
  190. #define IO_BUFFER_SIZE 2048
  191. struct IOBuffer {
  192. unsigned char buffer[IO_BUFFER_SIZE];
  193. SQInteger size;
  194. SQInteger ptr;
  195. SQFILE file;
  196. };
  197. SQInteger _read_byte(IOBuffer *iobuffer)
  198. {
  199. if(iobuffer->ptr < iobuffer->size) {
  200. SQInteger ret = iobuffer->buffer[iobuffer->ptr];
  201. iobuffer->ptr++;
  202. return ret;
  203. }
  204. else {
  205. if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 )
  206. {
  207. SQInteger ret = iobuffer->buffer[0];
  208. iobuffer->ptr = 1;
  209. return ret;
  210. }
  211. }
  212. return 0;
  213. }
  214. SQInteger _read_two_bytes(IOBuffer *iobuffer)
  215. {
  216. if(iobuffer->ptr < iobuffer->size) {
  217. if(iobuffer->size < 2) return 0;
  218. SQInteger ret = *((const wchar_t*)&iobuffer->buffer[iobuffer->ptr]);
  219. iobuffer->ptr += 2;
  220. return ret;
  221. }
  222. else {
  223. if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 )
  224. {
  225. if(iobuffer->size < 2) return 0;
  226. SQInteger ret = *((const wchar_t*)&iobuffer->buffer[0]);
  227. iobuffer->ptr = 2;
  228. return ret;
  229. }
  230. }
  231. return 0;
  232. }
  233. static SQInteger _io_file_lexfeed_PLAIN(SQUserPointer iobuf)
  234. {
  235. IOBuffer *iobuffer = (IOBuffer *)iobuf;
  236. return _read_byte(iobuffer);
  237. }
  238. #ifdef SQUNICODE
  239. static SQInteger _io_file_lexfeed_UTF8(SQUserPointer iobuf)
  240. {
  241. IOBuffer *iobuffer = (IOBuffer *)iobuf;
  242. #define READ(iobuf) \
  243. if((inchar = (unsigned char)_read_byte(iobuf)) == 0) \
  244. return 0;
  245. static const SQInteger utf8_lengths[16] =
  246. {
  247. 1,1,1,1,1,1,1,1, /* 0000 to 0111 : 1 byte (plain ASCII) */
  248. 0,0,0,0, /* 1000 to 1011 : not valid */
  249. 2,2, /* 1100, 1101 : 2 bytes */
  250. 3, /* 1110 : 3 bytes */
  251. 4 /* 1111 :4 bytes */
  252. };
  253. static const unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
  254. unsigned char inchar;
  255. SQInteger c = 0;
  256. READ(iobuffer);
  257. c = inchar;
  258. //
  259. if(c >= 0x80) {
  260. SQInteger tmp;
  261. SQInteger codelen = utf8_lengths[c>>4];
  262. if(codelen == 0)
  263. return 0;
  264. //"invalid UTF-8 stream";
  265. tmp = c&byte_masks[codelen];
  266. for(SQInteger n = 0; n < codelen-1; n++) {
  267. tmp<<=6;
  268. READ(iobuffer);
  269. tmp |= inchar & 0x3F;
  270. }
  271. c = tmp;
  272. }
  273. return c;
  274. }
  275. #endif
  276. static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer iobuf)
  277. {
  278. SQInteger ret;
  279. IOBuffer *iobuffer = (IOBuffer *)iobuf;
  280. if( (ret = _read_two_bytes(iobuffer)) > 0 )
  281. return ret;
  282. return 0;
  283. }
  284. static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer iobuf)
  285. {
  286. SQInteger c;
  287. IOBuffer *iobuffer = (IOBuffer *)iobuf;
  288. if( (c = _read_two_bytes(iobuffer)) > 0 ) {
  289. c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
  290. return c;
  291. }
  292. return 0;
  293. }
  294. SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)
  295. {
  296. SQInteger ret;
  297. if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;
  298. return -1;
  299. }
  300. SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
  301. {
  302. return sqstd_fwrite(p,1,size,(SQFILE)file);
  303. }
  304. SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)
  305. {
  306. SQFILE file = sqstd_fopen(filename,_SC("rb"));
  307. SQInteger ret;
  308. unsigned short us;
  309. unsigned char uc;
  310. SQLEXREADFUNC func = _io_file_lexfeed_PLAIN;
  311. if(file){
  312. ret = sqstd_fread(&us,1,2,file);
  313. if(ret != 2) {
  314. //probably an empty file
  315. us = 0;
  316. }
  317. if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE
  318. sqstd_fseek(file,0,SQ_SEEK_SET);
  319. if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {
  320. sqstd_fclose(file);
  321. return SQ_OK;
  322. }
  323. }
  324. else { //SCRIPT
  325. switch(us)
  326. {
  327. //gotta swap the next 2 lines on BIG endian machines
  328. case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
  329. case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
  330. case 0xBBEF:
  331. if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) {
  332. sqstd_fclose(file);
  333. return sq_throwerror(v,_SC("io error"));
  334. }
  335. if(uc != 0xBF) {
  336. sqstd_fclose(file);
  337. return sq_throwerror(v,_SC("Unrecognized encoding"));
  338. }
  339. #ifdef SQUNICODE
  340. func = _io_file_lexfeed_UTF8;
  341. #else
  342. func = _io_file_lexfeed_PLAIN;
  343. #endif
  344. break;//UTF-8 ;
  345. default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii
  346. }
  347. IOBuffer buffer;
  348. buffer.ptr = 0;
  349. buffer.size = 0;
  350. buffer.file = file;
  351. if(SQ_SUCCEEDED(sq_compile(v,func,&buffer,filename,printerror))){
  352. sqstd_fclose(file);
  353. return SQ_OK;
  354. }
  355. }
  356. sqstd_fclose(file);
  357. return SQ_ERROR;
  358. }
  359. return sq_throwerror(v,_SC("cannot open the file"));
  360. }
  361. SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
  362. {
  363. //at least one entry must exist in order for us to push it as the environment
  364. if(sq_gettop(v) == 0)
  365. return sq_throwerror(v,_SC("environment table expected"));
  366. if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
  367. sq_push(v,-2);
  368. if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
  369. sq_remove(v,retval?-2:-1); //removes the closure
  370. return 1;
  371. }
  372. sq_pop(v,1); //removes the closure
  373. }
  374. return SQ_ERROR;
  375. }
  376. SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
  377. {
  378. SQFILE file = sqstd_fopen(filename,_SC("wb+"));
  379. if(!file) return sq_throwerror(v,_SC("cannot open the file"));
  380. if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
  381. sqstd_fclose(file);
  382. return SQ_OK;
  383. }
  384. sqstd_fclose(file);
  385. return SQ_ERROR; //forward the error
  386. }
  387. SQInteger _g_io_loadfile(HSQUIRRELVM v)
  388. {
  389. const SQChar *filename;
  390. SQBool printerror = SQFalse;
  391. sq_getstring(v,2,&filename);
  392. if(sq_gettop(v) >= 3) {
  393. sq_getbool(v,3,&printerror);
  394. }
  395. if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
  396. return 1;
  397. return SQ_ERROR; //propagates the error
  398. }
  399. SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
  400. {
  401. const SQChar *filename;
  402. sq_getstring(v,2,&filename);
  403. if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
  404. return 1;
  405. return SQ_ERROR; //propagates the error
  406. }
  407. SQInteger _g_io_dofile(HSQUIRRELVM v)
  408. {
  409. const SQChar *filename;
  410. SQBool printerror = SQFalse;
  411. sq_getstring(v,2,&filename);
  412. if(sq_gettop(v) >= 3) {
  413. sq_getbool(v,3,&printerror);
  414. }
  415. sq_push(v,1); //repush the this
  416. if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
  417. return 1;
  418. return SQ_ERROR; //propagates the error
  419. }
  420. #define _DECL_GLOBALIO_FUNC(name,nparamsmin,nparamsmax,typecheck) {_SC(#name),_g_io_##name,nparamsmin,nparamsmax,typecheck}
  421. static const SQRegFunction iolib_funcs[]={
  422. _DECL_GLOBALIO_FUNC(loadfile,2,0,_SC(".sb")),
  423. _DECL_GLOBALIO_FUNC(dofile,2,0,_SC(".sb")),
  424. _DECL_GLOBALIO_FUNC(writeclosuretofile,3,3,_SC(".sc")),
  425. {NULL,(SQFUNCTION)0,0,0,NULL}
  426. };
  427. SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
  428. {
  429. SQInteger top = sq_gettop(v);
  430. //create delegate
  431. declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
  432. sq_pushstring(v,_SC("stdout"),-1);
  433. sqstd_createfile(v,stdout,SQFalse);
  434. sq_newslot(v,-3,SQFalse);
  435. sq_pushstring(v,_SC("stdin"),-1);
  436. sqstd_createfile(v,stdin,SQFalse);
  437. sq_newslot(v,-3,SQFalse);
  438. sq_pushstring(v,_SC("stderr"),-1);
  439. sqstd_createfile(v,stderr,SQFalse);
  440. sq_newslot(v,-3,SQFalse);
  441. sq_settop(v,top);
  442. return SQ_OK;
  443. }