#include #include "../core.h" #include "file.h" #include "mut.h" #include "talloc.h" #include "cstr.h" FILE* file_open(B path, char* desc, char* mode) { // doesn't consume char* p = toCStr(path); FILE* f = fopen(p, mode); freeCStr(p); if (f==NULL) thrF("Couldn't %S file \"%R\"", desc, path); return f; } static DIR* dir_open(B path) { // doesn't consume u64 plen = utf8lenB(path); TALLOC(char, p, plen+1); toUTF8(path, p); p[plen] = 0; DIR* f = opendir(p); TFREE(p); if (f==NULL) thrF("Couldn't open directory \"%R\"", path); return f; } I8Arr* stream_bytes(FILE* f) { B r = emptyIVec(); u64 SZ = 8192; TALLOC(i8, t, SZ); while(true) { u64 read = fread(t, 1, SZ, f); if (read==0) break; i8* ap; B a = m_i8arrv(&ap, read); memcpy(ap, t, read); r = vec_join(r, a); } TFREE(t); return toI8Arr(r); } I8Arr* path_bytes(B path) { // consumes FILE* f = file_open(path, "read", "r"); int seekRes = fseek(f, 0, SEEK_END); I8Arr* src; if (seekRes==-1) { src = stream_bytes(f); } else { i64 len = ftell(f); fseek(f, 0, SEEK_SET); src = m_arr(fsizeof(I8Arr,a,u8,len), t_i8arr, len); arr_shVec((Arr*)src); if (fread((char*)src->a, 1, len, f)!=len) thrF("Error reading file \"%R\"", path); } dec(path); fclose(f); return src; } B path_chars(B path) { // consumes return fromUTF8a(path_bytes(path)); } B path_lines(B path) { // consumes; TODO rewrite this, it's horrible I8Arr* tf = path_bytes(path); usz ia = tf->ia; u8* p = (u8*)tf->a; usz lineCount = 0; for (usz i = 0; i < ia; i++) { if (p[i]=='\n') lineCount++; else if (p[i]=='\r') { lineCount++; if(i+1ia; SGetU(x) for (usz i = 0; i < xia; i++) if (!isC32(GetU(x, i))) thrM("Paths must be character vectors"); } B path_rel(B base, B rel) { // consumes rel; assumes base is a char vector or bi_N assert(isArr(base) || q_N(base)); if (!isArr(rel) || rnk(rel)!=1) thrM("Paths must be character vectors"); SGetU(rel) usz ria = a(rel)->ia; if (rnk(rel)!=1) thrM("Paths must be character vectors"); guaranteeStr(rel); if (ria>0 && o2cu(GetU(rel, 0))=='/') return rel; if (q_N(base)) thrM("Using relative path with no absolute base path known"); if (ria==0) { dec(rel); return inc(base); } SGetU(base) usz bia = a(base)->ia; bool has = bia && o2cu(GetU(base, bia-1))=='/'; u32* rp; B r = m_c32arrv(&rp, bia+ria+(has?0:1)); usz ri = 0; for (usz i = 0; i < bia-(has?1:0); i++) rp[ri++] = o2cu(GetU(base, i)); rp[ri++] = '/'; for (usz i = 0; i < ria; i++) rp[ri++] = o2cu(GetU(rel, i)); dec(rel); return r; } B path_dir(B path) { assert(isArr(path)); SGetU(path) usz pia = a(path)->ia; if (pia==0) thrM("Empty file path"); guaranteeStr(path); for (i64 i = (i64)pia-1; i >= 0; i--) { if (o2cu(GetU(path, i))=='/') { Arr* r = TI(path,slice)(path, 0, i+1); arr_shVec(r); return taga(r); } } dec(path); u32* rp; B r = m_c32arrv(&rp, 2); rp[0] = '.'; rp[1] = '/'; return r; } B path_name(B path) { assert(isArr(path)); SGetU(path) usz pia = a(path)->ia; if (pia==0) thrM("Empty file path"); guaranteeStr(path); for (i64 i = (i64)pia-1; i >= 0; i--) { if (o2cu(GetU(path, i))=='/') { if (i == pia-1) thrF("File path ended with a slash: '%R'", path); Arr* r = TI(path,slice)(path, i+1, pia - (i+1)); arr_shVec(r); return taga(r); } } return path; } B path_abs(B path) { #if WASM return path; // lazy #else if (q_N(path)) return path; u64 plen = utf8lenB(path); TALLOC(char, p, plen+1); toUTF8(path, p); p[plen] = 0; char* res = realpath(p, NULL); if (res==NULL) thrF("Failed to convert %R to absolute path", path); B r = fromUTF8l(res); free(res); dec(path); TFREE(p); return r; #endif } void path_wChars(B path, B x) { // consumes path FILE* f = file_open(path, "write to", "w"); u64 len = utf8lenB(x); TALLOC(char, val, len); toUTF8(x, val); if (fwrite(val, 1, len, f) != len) thrF("Error writing to file \"%R\"", path); TFREE(val); dec(path); fclose(f); } void file_wBytes(FILE* f, B name, B x) { u64 len = a(x)->ia; TALLOC(char, val, len); SGetU(x) for (u64 i = 0; i < len; i++) { B c = GetU(x,i); val[i] = isNum(c)? o2iu(c) : o2c(c); } if (fwrite(val, 1, len, f) != len) { if (q_N(name)) thrM("Error writing to file"); else thrF("Error writing to file \"%R\"", name); } TFREE(val); } void path_wBytes(B path, B x) { // consumes path FILE* f = file_open(path, "write to", "w"); file_wBytes(f, path, x); fclose(f); } B path_list(B path) { DIR* d = dir_open(path); struct dirent *c; B res = emptySVec(); while ((c = readdir(d)) != NULL) { char* name = c->d_name; if (name[0]=='.'? !(name[1]==0 || (name[1]=='.'&&name[2]==0)) : true) res = vec_add(res, m_str8l(name)); } closedir(d); dec(path); return res; } #if __has_include() && __has_include() && __has_include() && __has_include() && !WASM #include #include #include #include typedef struct MmapHolder { struct Arr; int fd; u64 size; u8* a; } MmapHolder; void mmapH_visit(Value* v) { } DEF_FREE(mmapH) { MmapHolder* p = (MmapHolder*)x; if (munmap(p->a, p->size)) thrF("Failed to unmap: %S", strerror(errno)); if (close(p->fd)) thrF("Failed to close file: %S", strerror(errno)); } B info_c1(B,B); static Arr* mmapH_slice(B x, usz s, usz ia) { TySlice* r = m_arr(sizeof(TySlice), t_i8slice, ia); r->a = c(MmapHolder,x)->a + s; r->p = a(x); return (Arr*)r; } B mmap_file(B path) { char* p = toCStr(path); dec(path); int fd = open(p, 0); freeCStr(p); u64 len = lseek(fd, 0, SEEK_END); if (fd==-1) thrF("Failed to open file: %S", strerror(errno)); u8* data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); // TODO count in heap usage if (data==MAP_FAILED) { close(fd); thrM("failed to mmap file"); } MmapHolder* holder = m_arr(sizeof(MmapHolder), t_mmapH, len); holder->fd = fd; holder->a = data; holder->size = len; arr_shVec((Arr*)holder); Arr* r = mmapH_slice(taga(holder), 0, len); arr_shVec(r); return taga(r); } B mmapH_get(Arr* a, usz pos) { thrM("Reading mmapH directly"); } void mmap_init() { TIi(t_mmapH,get) = mmapH_get; TIi(t_mmapH,getU) = mmapH_get; TIi(t_mmapH,slice) = mmapH_slice; TIi(t_mmapH,freeO) = mmapH_freeO; TIi(t_mmapH,freeF) = mmapH_freeF; TIi(t_mmapH,visit) = noop_visit; TIi(t_mmapH,print) = farr_print; TIi(t_mmapH,isArr) = true; TIi(t_mmapH,arrD1) = true; TIi(t_mmapH,elType) = el_i8; // use default canStore } #else B mmap_file(B path) { thrM("CBQN was compiled without mmap"); } void mmap_init() { } #endif #include #include char path_type(B path) { char* p = toCStr(path); struct stat path_stat; int r = stat(p, &path_stat); freeCStr(p); dec(path); if (r==-1) return 0; i64 mode = path_stat.st_mode; if (S_ISREG (mode)) return 'f'; if (S_ISDIR (mode)) return 'd'; if (S_ISLNK (mode)) return 'l'; if (S_ISFIFO(mode)) return 'p'; if (S_ISSOCK(mode)) return 's'; if (S_ISBLK (mode)) return 'b'; if (S_ISCHR (mode)) return 'c'; thrM("Unexpected file type"); } void mmX_dumpHeap(FILE* f); void cbqn_heapDump() { char* name = "CBQNHeapDump"; FILE* f = fopen(name, "w"); if (f==NULL) { fprintf(stderr, "Failed to dump heap - could not open file for writing\n"); return; } // fwrite(&size, 8, 1, f); u8 t8 = 0; fwrite(&t8, 1, 1, f); // version // sizeof(ur), sizeof(usz) u8 urW[4]; for (i32 i = 0; i < 4; i++) urW[i] = (sizeof(ur )>>(8*i)) & 0xff; fwrite( &urW, 1, 4, f); u8 uszW[4]; for (i32 i = 0; i < 4; i++) uszW[i] = (sizeof(usz)>>(8*i)) & 0xff; fwrite(&uszW, 1, 4, f); // t_names #define F(X) { t8=t_##X; fwrite(&t8, 1, 1, f); char* s = #X; fwrite(s, 1, strlen(s)+1, f); } FOR_TYPE(F) #undef F t8 = 255; fwrite(&t8, 1, 1, f); // end of t_names t8 = 12; fwrite(&t8, 1, 1, f); // number of tag names u8 t16a[2]; #define F(X) { \ t16a[0] = X##_TAG&0xff; t16a[1] = (X##_TAG>>8)&0xff; fwrite(&t16a, 1, 2, f); \ char* s = #X"_TAG"; fwrite(s, 1, strlen(s)+1, f); \ } F(C32)F(TAG)F(VAR)F(EXT)F(RAW)F(MD1)F(MD2)F(FUN)F(NSP)F(OBJ)F(ARR)F(VAL) #undef F mm_dumpHeap(f); mmX_dumpHeap(f); fprintf(stderr, "Heap dumped to \"%s\"\n", name); fclose(f); }