fs.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /* ----- fs.c ----- */
  2. typedef struct realname_entry {
  3. char* realname;
  4. time_t mtime;
  5. } realname_entry;
  6. struct {
  7. hashtable names;
  8. int len;
  9. int alloc;
  10. struct realname_entry* entries;
  11. } realname_cache;
  12. hashtable mkdir_cache;
  13. #define FSU_EXCLUDE_HIDDEN (1<<0)
  14. #define FSU_NO_FOLLOW_SYMLINKS (1<<1)
  15. #define FSU_INCLUDE_DIRS (1<<2)
  16. #define FSU_EXCLUDE_FILES (1<<3)
  17. // return 0 to continue, nonzero to stop all directory scanning
  18. typedef int (*readDirCallbackFn)(char* /*fullPath*/, char* /*fileName*/, unsigned char /*type*/, void* /*data*/);
  19. // returns negative on error, nonzero if scanning was halted by the callback
  20. int recurse_dirs(
  21. char* path,
  22. readDirCallbackFn fn,
  23. void* data,
  24. int depth,
  25. unsigned int flags
  26. );
  27. void realname_cache_init();
  28. time_t realname_cache_add(char* fake_name, char* real_name);
  29. realname_entry* realname_cache_search_real(char* real_name);
  30. realname_entry* realname_cache_search(char* fake_name);
  31. char* realname_cache_find(char* fake_name);
  32. char* resolve_path(char* in, time_t* mtime_out);
  33. char* read_whole_file(char* path, size_t* srcLen);
  34. char* dir_name(char* path) {
  35. char* n = strdup(path);
  36. char* o = dirname(n);
  37. return strcache(o);
  38. }
  39. char* base_name(char* path) {
  40. char* n = strdup(path);
  41. char* o = basename(n);
  42. return strcache(o);
  43. }
  44. // does not handle escaped slashes
  45. int mkdirp(char* path, mode_t mode) {
  46. char* clean_path = strdup(path);
  47. // inch along the path creating each directory in line
  48. for(char* p = clean_path; *p; p++) {
  49. if(*p == '/') {
  50. *p = 0;
  51. if(mkdir(clean_path, mode)) {
  52. if(errno != EEXIST) goto FAIL;
  53. }
  54. *p = '/';
  55. }
  56. }
  57. // mop up the last dir
  58. if(mkdir(clean_path, mode)) {
  59. if(errno != EEXIST) goto FAIL;
  60. }
  61. free(clean_path);
  62. return 0;
  63. FAIL:
  64. free(clean_path);
  65. return -1;
  66. }
  67. void mkdirp_cached(char* path, mode_t mode) {
  68. void* there = hash_find(&mkdir_cache, path);
  69. if(!there) {
  70. hash_insert(&mkdir_cache, path, NULL);
  71. mkdirp(path, mode);
  72. }
  73. }
  74. // works like realpath(), except also handles ~/
  75. char* resolve_path(char* in, time_t* mtime_out) {
  76. int tmp_was_malloced = 0;
  77. char* out, *tmp;
  78. if(!in) return NULL;
  79. realname_entry* e = realname_cache_search(in);
  80. if(e) {
  81. if(mtime_out) *mtime_out = e->mtime;
  82. return strcache(e->realname);
  83. }
  84. // skip leading whitespace
  85. while(isspace(*in)) in++;
  86. // handle home dir shorthand
  87. if(in[0] == '~') {
  88. char* home = getenv("HOME");
  89. tmp_was_malloced = 1;
  90. tmp = malloc(sizeof(*tmp) * (strlen(home) + strlen(in) + 2));
  91. strcpy(tmp, home);
  92. strcat(tmp, "/"); // just in case
  93. strcat(tmp, in + 1);
  94. }
  95. else tmp = in;
  96. out = realpath(tmp, NULL);
  97. if(tmp_was_malloced) free(tmp);
  98. time_t t = 0;
  99. if(out) {
  100. // put it in the cache
  101. t = realname_cache_add(in, out);
  102. }
  103. else {
  104. // temporary
  105. struct stat st;
  106. if(!lstat(in, &st))
  107. t = st.st_mtim.tv_sec;
  108. }
  109. if(mtime_out) *mtime_out = t;
  110. return out ? out : in;
  111. }
  112. void realname_cache_init() {
  113. realname_cache.len = 0;
  114. realname_cache.alloc = 1024;
  115. realname_cache.entries = malloc(realname_cache.alloc * sizeof(*realname_cache.entries));
  116. hash_init(&realname_cache.names, 1024);
  117. }
  118. time_t realname_cache_add(char* fake_name, char* real_name) {
  119. realname_entry* e = hash_find(&realname_cache.names, fake_name);
  120. if(e) return e->mtime;
  121. e = hash_find(&realname_cache.names, real_name);
  122. if(!e) {
  123. struct stat st;
  124. lstat(real_name, &st);
  125. e = &realname_cache.entries[realname_cache.len];
  126. e->realname = strcache(real_name);
  127. e->mtime = st.st_mtim.tv_sec;
  128. realname_cache.len++;
  129. hash_insert(&realname_cache.names, real_name, e);
  130. }
  131. hash_insert(&realname_cache.names, fake_name, e);
  132. return e->mtime;
  133. }
  134. realname_entry* realname_cache_search_real(char* real_name) {
  135. for(int i = 0; i < realname_cache.len; i++) {
  136. if(0 == strcmp(real_name, realname_cache.entries[i].realname)) {
  137. return &realname_cache.entries[i];
  138. }
  139. }
  140. return NULL;
  141. }
  142. realname_entry* realname_cache_search(char* fake_name) {
  143. return hash_find(&realname_cache.names, fake_name);
  144. }
  145. char* realname_cache_find(char* fake_name) {
  146. realname_entry* r = realname_cache_search(fake_name);
  147. return r ? r->realname : NULL;
  148. }
  149. // returns negative on error, nonzero if scanning was halted by the callback
  150. int recurse_dirs(
  151. char* path,
  152. readDirCallbackFn fn,
  153. void* data,
  154. int depth,
  155. unsigned int flags
  156. ) {
  157. DIR* derp;
  158. struct dirent* result;
  159. int stop = 0;
  160. if(fn == NULL) {
  161. fprintf(stderr, "Error: readAllDir called with null function pointer.\n");
  162. return -1;
  163. }
  164. derp = opendir(path);
  165. if(derp == NULL) {
  166. fprintf(stderr, "Error opening directory '%s': %s\n", path, strerror(errno));
  167. return -1;
  168. }
  169. while((result = readdir(derp)) && !stop) {
  170. char* n = result->d_name;
  171. unsigned char type = DT_UNKNOWN;
  172. char* fullPath;
  173. // skip self and parent dir entries
  174. if(n[0] == '.') {
  175. if(n[1] == '.' && n[2] == 0) continue;
  176. if(n[1] == 0) continue;
  177. if(flags & FSU_EXCLUDE_HIDDEN) continue;
  178. }
  179. #ifdef _DIRENT_HAVE_D_TYPE
  180. type = result->d_type; // the way life should be
  181. #else
  182. // do some slow extra bullshit to get the type
  183. fullPath = path_join(path, n);
  184. struct stat upgrade_your_fs;
  185. lstat(fullPath, &upgrade_your_fs);
  186. if(S_ISREG(upgrade_your_fs.st_mode)) type = DT_REG;
  187. else if(S_ISDIR(upgrade_your_fs.st_mode)) type = DT_DIR;
  188. else if(S_ISLNK(upgrade_your_fs.st_mode)) type = DT_LNK;
  189. #endif
  190. if(flags & FSU_NO_FOLLOW_SYMLINKS && type == DT_LNK) {
  191. continue;
  192. }
  193. #ifdef _DIRENT_HAVE_D_TYPE
  194. fullPath = path_join(path, n);
  195. #endif
  196. if(type == DT_DIR) {
  197. if(flags & FSU_INCLUDE_DIRS) {
  198. stop = fn(fullPath, n, type, data);
  199. }
  200. if(depth != 0) {
  201. stop |= recurse_dirs(fullPath, fn, data, depth - 1, flags);
  202. }
  203. }
  204. else if(type == DT_REG) {
  205. if(!(flags & FSU_EXCLUDE_FILES)) {
  206. stop = fn(fullPath, n, type, data);
  207. }
  208. }
  209. free(fullPath);
  210. }
  211. closedir(derp);
  212. return stop;
  213. }
  214. char* read_whole_file(char* path, size_t* srcLen) {
  215. size_t fsize, total_read = 0, bytes_read;
  216. char* contents;
  217. FILE* f;
  218. f = fopen(path, "rb");
  219. if(!f) {
  220. fprintf(stderr, "Could not open file \"%s\"\n", path);
  221. return NULL;
  222. }
  223. fseek(f, 0, SEEK_END);
  224. fsize = ftell(f);
  225. rewind(f);
  226. contents = malloc(fsize + 1);
  227. while(total_read < fsize) {
  228. bytes_read = fread(contents + total_read, sizeof(char), fsize - total_read, f);
  229. total_read += bytes_read;
  230. }
  231. contents[fsize] = 0;
  232. fclose(f);
  233. if(srcLen) *srcLen = fsize;
  234. return contents;
  235. }
  236. /* -END- fs.c ----- */