io.cpp 6.3 KB

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