io.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #include "pocketpy/io.h"
  2. namespace pkpy{
  3. #if PK_ENABLE_OS
  4. static FILE* io_fopen(const char* name, const char* mode){
  5. #if _MSC_VER
  6. FILE* fp;
  7. errno_t err = fopen_s(&fp, name, mode);
  8. if(err != 0) return nullptr;
  9. return fp;
  10. #else
  11. return fopen(name, mode);
  12. #endif
  13. }
  14. static size_t io_fread(void* buffer, size_t size, size_t count, FILE* fp){
  15. #if _MSC_VER
  16. return fread_s(buffer, std::numeric_limits<size_t>::max(), size, count, fp);
  17. #else
  18. return fread(buffer, size, count, fp);
  19. #endif
  20. }
  21. #endif
  22. unsigned char* _default_import_handler(const char* name_p, int name_size, int* out_size){
  23. #if PK_ENABLE_OS
  24. std::string name(name_p, name_size);
  25. bool exists = std::filesystem::exists(std::filesystem::path(name));
  26. if(!exists) return nullptr;
  27. FILE* fp = io_fopen(name.c_str(), "rb");
  28. if(!fp) return nullptr;
  29. fseek(fp, 0, SEEK_END);
  30. int buffer_size = ftell(fp);
  31. unsigned char* buffer = new unsigned char[buffer_size];
  32. fseek(fp, 0, SEEK_SET);
  33. size_t sz = io_fread(buffer, 1, buffer_size, fp);
  34. PK_UNUSED(sz);
  35. fclose(fp);
  36. *out_size = buffer_size;
  37. return buffer;
  38. #else
  39. return nullptr;
  40. #endif
  41. };
  42. #if PK_ENABLE_OS
  43. void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
  44. vm->bind_constructor<3>(type, [](VM* vm, ArgsView args){
  45. Type cls = PK_OBJ_GET(Type, args[0]);
  46. return vm->heap.gcnew<FileIO>(cls, vm,
  47. py_cast<Str&>(vm, args[1]).str(),
  48. py_cast<Str&>(vm, args[2]).str());
  49. });
  50. vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){
  51. FileIO& io = CAST(FileIO&, args[0]);
  52. fseek(io.fp, 0, SEEK_END);
  53. int buffer_size = ftell(io.fp);
  54. unsigned char* buffer = new unsigned char[buffer_size];
  55. fseek(io.fp, 0, SEEK_SET);
  56. size_t actual_size = io_fread(buffer, 1, buffer_size, io.fp);
  57. PK_ASSERT(actual_size <= buffer_size);
  58. // in text mode, CR may be dropped, which may cause `actual_size < buffer_size`
  59. Bytes b(buffer, actual_size);
  60. if(io.is_text()) return VAR(b.str());
  61. return VAR(std::move(b));
  62. });
  63. vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){
  64. FileIO& io = CAST(FileIO&, args[0]);
  65. if(io.is_text()){
  66. Str& s = CAST(Str&, args[1]);
  67. fwrite(s.data, 1, s.length(), io.fp);
  68. }else{
  69. Bytes& buffer = CAST(Bytes&, args[1]);
  70. fwrite(buffer.data(), 1, buffer.size(), io.fp);
  71. }
  72. return vm->None;
  73. });
  74. vm->bind_method<0>(type, "close", [](VM* vm, ArgsView args){
  75. FileIO& io = CAST(FileIO&, args[0]);
  76. io.close();
  77. return vm->None;
  78. });
  79. vm->bind_method<0>(type, "__exit__", [](VM* vm, ArgsView args){
  80. FileIO& io = CAST(FileIO&, args[0]);
  81. io.close();
  82. return vm->None;
  83. });
  84. vm->bind_method<0>(type, "__enter__", PK_LAMBDA(args[0]));
  85. }
  86. FileIO::FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) {
  87. fp = io_fopen(file.c_str(), mode.c_str());
  88. if(!fp) vm->IOError(strerror(errno));
  89. }
  90. void FileIO::close(){
  91. if(fp == nullptr) return;
  92. fclose(fp);
  93. fp = nullptr;
  94. }
  95. #endif
  96. void add_module_io(VM* vm){
  97. #if PK_ENABLE_OS
  98. PyObject* mod = vm->new_module("io");
  99. FileIO::register_class(vm, mod);
  100. vm->bind(vm->builtins, "open(path, mode='r')", [](VM* vm, ArgsView args){
  101. PK_LOCAL_STATIC StrName m_io("io");
  102. PK_LOCAL_STATIC StrName m_FileIO("FileIO");
  103. return vm->call(vm->_modules[m_io]->attr(m_FileIO), args[0], args[1]);
  104. });
  105. #endif
  106. }
  107. void add_module_os(VM* vm){
  108. #if PK_ENABLE_OS
  109. PyObject* mod = vm->new_module("os");
  110. PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object);
  111. mod->attr().set("path", path_obj);
  112. // Working directory is shared by all VMs!!
  113. vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){
  114. return VAR(std::filesystem::current_path().string());
  115. });
  116. vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){
  117. std::filesystem::path path(CAST(Str&, args[0]).sv());
  118. std::filesystem::current_path(path);
  119. return vm->None;
  120. });
  121. vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){
  122. std::filesystem::path path(CAST(Str&, args[0]).sv());
  123. std::filesystem::directory_iterator di;
  124. try{
  125. di = std::filesystem::directory_iterator(path);
  126. }catch(std::filesystem::filesystem_error& e){
  127. std::string msg = e.what();
  128. auto pos = msg.find_last_of(":");
  129. if(pos != std::string::npos) msg = msg.substr(pos + 1);
  130. vm->IOError(Str(msg).lstrip());
  131. }
  132. List ret;
  133. for(auto& p: di) ret.push_back(VAR(p.path().filename().string()));
  134. return VAR(ret);
  135. });
  136. vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){
  137. std::filesystem::path path(CAST(Str&, args[0]).sv());
  138. bool ok = std::filesystem::remove(path);
  139. if(!ok) vm->IOError("operation failed");
  140. return vm->None;
  141. });
  142. vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){
  143. std::filesystem::path path(CAST(Str&, args[0]).sv());
  144. bool ok = std::filesystem::create_directory(path);
  145. if(!ok) vm->IOError("operation failed");
  146. return vm->None;
  147. });
  148. vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){
  149. std::filesystem::path path(CAST(Str&, args[0]).sv());
  150. bool ok = std::filesystem::remove(path);
  151. if(!ok) vm->IOError("operation failed");
  152. return vm->None;
  153. });
  154. vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){
  155. std::filesystem::path path;
  156. for(int i=0; i<args.size(); i++){
  157. path /= CAST(Str&, args[i]).sv();
  158. }
  159. return VAR(path.string());
  160. });
  161. vm->bind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){
  162. std::filesystem::path path(CAST(Str&, args[0]).sv());
  163. bool exists = std::filesystem::exists(path);
  164. return VAR(exists);
  165. });
  166. vm->bind_func<1>(path_obj, "basename", [](VM* vm, ArgsView args){
  167. std::filesystem::path path(CAST(Str&, args[0]).sv());
  168. return VAR(path.filename().string());
  169. });
  170. #endif
  171. }
  172. } // namespace pkpy