build.inc.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  1. // gcc -lutil build.c -o hacer && ./hacer
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <ctype.h>
  6. #include <errno.h>
  7. #include <stdarg.h>
  8. #include <signal.h>
  9. #include <unistd.h>
  10. #include <sys/prctl.h>
  11. #include <pty.h>
  12. #include <fcntl.h>
  13. #include <fnmatch.h>
  14. #include <dirent.h>
  15. #include <libgen.h>
  16. #include <sys/stat.h>
  17. #include <sys/types.h>
  18. // link with -lutil
  19. char** g_gcc_opts_list;
  20. char* g_gcc_opts_flat;
  21. struct child_process_info {
  22. int pid;
  23. int child_stdin;
  24. int child_stdout;
  25. int child_stderr;
  26. FILE* f_stdin;
  27. FILE* f_stdout;
  28. FILE* f_stderr;
  29. };
  30. typedef struct rglob_entry {
  31. char type;
  32. char* full_path;
  33. char* file_name;
  34. // char* dir_name;
  35. } rglob_entry;
  36. typedef struct rglob {
  37. char* pattern;
  38. int len;
  39. int alloc;
  40. rglob_entry* entries;
  41. } rglob;
  42. typedef struct strlist {
  43. int len;
  44. int alloc;
  45. char** entries;
  46. } strlist;
  47. #define PP_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, N, ...) N
  48. #define PP_RSEQ_N() 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
  49. #define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
  50. #define PP_NARG(...) PP_NARG_(__VA_ARGS__, PP_RSEQ_N())
  51. #define check_alloc(x) \
  52. if((x)->len >= (x)->alloc) { \
  53. (x)->alloc *= 2; \
  54. (x)->entries = realloc((x)->entries, (x)->alloc * sizeof(*(x)->entries)); \
  55. }
  56. void strlist_init(strlist* sl) {
  57. sl->len = 0;
  58. sl->alloc = 32;
  59. sl->entries = malloc(sl->alloc * sizeof(*sl->entries));
  60. }
  61. strlist* strlist_new() {
  62. strlist* sl = malloc(sizeof(*sl));
  63. strlist_init(sl);
  64. return sl;
  65. }
  66. void strlist_push(strlist* sl, char* e) {
  67. check_alloc(sl);
  68. sl->entries[sl->len++] = e;
  69. }
  70. typedef unsigned long hash_t;
  71. hash_t strhash(char* str) {
  72. unsigned long h = 0;
  73. int c;
  74. while(c = *str++) {
  75. h = c + (h << 6) + (h << 16) - h;
  76. }
  77. return h;
  78. }
  79. hash_t strnhash(char* str, size_t n) {
  80. unsigned long h = 0;
  81. int c;
  82. while((c = *str++) && n--) {
  83. h = c + (h << 6) + (h << 16) - h;
  84. }
  85. return h;
  86. }
  87. typedef struct {
  88. hash_t hash;
  89. char* str;
  90. unsigned int len;
  91. int refs;
  92. } string_cache_bucket;
  93. typedef struct {
  94. int fill;
  95. int alloc;
  96. string_cache_bucket* buckets;
  97. } string_cache_t;
  98. string_cache_t string_cache;
  99. void string_cache_init(int alloc) {
  100. string_cache.fill = 0;
  101. string_cache.alloc = alloc ? alloc : 1024;
  102. string_cache.buckets = calloc(1, string_cache.alloc * sizeof(*string_cache.buckets));
  103. }
  104. int string_cache_find_bucket(string_cache_t* ht, hash_t hash, char* key) {
  105. int b = hash % ht->alloc;
  106. for(int i = 0; i < ht->alloc; i++) {
  107. // empty bucket
  108. if(ht->buckets[b].str == NULL) return b;
  109. // full bucket
  110. if(ht->buckets[b].hash == hash) {
  111. if(0 == strcmp(key, ht->buckets[b].str)) {
  112. return b;
  113. }
  114. }
  115. // probe forward on collisions
  116. b = (b + 1) % ht->alloc;
  117. }
  118. // should never reach here
  119. printf("oops. -1 bucket \n");
  120. return -1;
  121. }
  122. void string_cache_expand(string_cache_t* ht) {
  123. int old_alloc = ht->alloc;
  124. ht->alloc *= 2;
  125. string_cache_bucket* old = ht->buckets;
  126. ht->buckets = calloc(1, ht->alloc * sizeof(*ht->buckets));
  127. for(int i = 0, f = 0; i < old_alloc && f < ht->fill; i++) {
  128. if(!old[i].str) continue;
  129. int b = string_cache_find_bucket(ht, old[i].hash, old[i].str);
  130. ht->buckets[b] = old[i];
  131. f++;
  132. }
  133. free(old);
  134. }
  135. char* strcache(char* in) {
  136. hash_t hash = strhash(in);
  137. int b = string_cache_find_bucket(&string_cache, hash, in);
  138. if(!string_cache.buckets[b].str) {
  139. if(string_cache.fill > string_cache.alloc * .80) {
  140. string_cache_expand(&string_cache);
  141. // the bucket location has changed
  142. b = string_cache_find_bucket(&string_cache, hash, in);
  143. }
  144. string_cache.fill++;
  145. string_cache.buckets[b].str = strdup(in);
  146. string_cache.buckets[b].hash = hash;
  147. string_cache.buckets[b].refs = 1;
  148. string_cache.buckets[b].len = strlen(in);
  149. }
  150. else {
  151. string_cache.buckets[b].refs++;
  152. }
  153. return string_cache.buckets[b].str;
  154. }
  155. char* strncache(char* in, size_t n) {
  156. hash_t hash = strnhash(in, n);
  157. int b = string_cache_find_bucket(&string_cache, hash, in);
  158. if(!string_cache.buckets[b].str) {
  159. if(string_cache.fill > string_cache.alloc * .80) {
  160. string_cache_expand(&string_cache);
  161. // the bucket location has changed
  162. b = string_cache_find_bucket(&string_cache, hash, in);
  163. }
  164. string_cache.fill++;
  165. string_cache.buckets[b].str = strndup(in, n);
  166. string_cache.buckets[b].hash = hash;
  167. string_cache.buckets[b].refs = 1;
  168. string_cache.buckets[b].len = n;
  169. }
  170. else {
  171. string_cache.buckets[b].refs++;
  172. }
  173. return string_cache.buckets[b].str;
  174. }
  175. void struncache(char* in) {
  176. hash_t hash = strhash(in);
  177. int b = string_cache_find_bucket(&string_cache, hash, in);
  178. if(!string_cache.buckets[b].str) {
  179. // normal string, free it
  180. free(in);
  181. return;
  182. }
  183. string_cache.buckets[b].refs--;
  184. if(string_cache.buckets[b].refs == 0) {
  185. // just do nothing for now. deletion is a pain.
  186. }
  187. }
  188. typedef struct {
  189. hash_t hash;
  190. char* key;
  191. void* value;
  192. } hashbucket;
  193. typedef struct hashtable {
  194. int fill;
  195. int alloc;
  196. hashbucket* buckets;
  197. } hashtable;
  198. void hash_init(hashtable* ht, int alloc) {
  199. ht->fill = 0;
  200. ht->alloc = alloc ? alloc : 128;
  201. ht->buckets = calloc(1, ht->alloc * sizeof(*ht->buckets));
  202. }
  203. hashtable* hash_new(int alloc) {
  204. hashtable* ht = malloc(sizeof(*ht));
  205. hash_init(ht, alloc);
  206. return ht;
  207. }
  208. int hash_find_bucket(hashtable* ht, hash_t hash, char* key) {
  209. int b = hash % ht->alloc;
  210. for(int i = 0; i < ht->alloc; i++) {
  211. // empty bucket
  212. if(ht->buckets[b].key == NULL) return b;
  213. // full bucket
  214. if(ht->buckets[b].hash == hash) {
  215. if(0 == strcmp(key, ht->buckets[b].key)) {
  216. return b;
  217. }
  218. }
  219. // probe forward on collisions
  220. b = (b + 1) % ht->alloc;
  221. }
  222. // should never reach here
  223. printf("oops. -1 bucket \n");
  224. return -1;
  225. }
  226. void hash_expand(hashtable* ht) {
  227. int old_alloc = ht->alloc;
  228. ht->alloc *= 2;
  229. hashbucket* old = ht->buckets;
  230. ht->buckets = calloc(1, ht->alloc * sizeof(*ht->buckets));
  231. for(int i = 0, f = 0; i < old_alloc && f < ht->fill; i++) {
  232. if(!old[i].key) continue;
  233. int b = hash_find_bucket(ht, old[i].hash, old[i].key);
  234. ht->buckets[b] = old[i];
  235. f++;
  236. }
  237. free(old);
  238. }
  239. void hash_insert(hashtable* ht, char* key, void* value) {
  240. hash_t hash = strhash(key);
  241. int b = hash_find_bucket(ht, hash, key);
  242. if(!ht->buckets[b].key) {
  243. if(ht->fill > ht->alloc * .80) {
  244. hash_expand(ht);
  245. // the bucket location has changed
  246. b = hash_find_bucket(ht, hash, key);
  247. }
  248. ht->fill++;
  249. }
  250. ht->buckets[b].key = strcache(key);
  251. ht->buckets[b].hash = hash;
  252. ht->buckets[b].value = value;
  253. }
  254. void* hash_find(hashtable* ht, char* key) {
  255. hash_t hash = strhash(key);
  256. int b = hash_find_bucket(ht, hash, key);
  257. return ht->buckets[b].value;
  258. }
  259. struct child_process_info* exec_process_pipe(char* exec_path, char* args[]);
  260. int mkdirp(char* path, mode_t mode);
  261. char* resolve_path(char* in, time_t* mtime_out);
  262. #define path_join(...) path_join_(PP_NARG(__VA_ARGS__), __VA_ARGS__)
  263. char* path_join_(size_t nargs, ...);
  264. #define concat_lists(...) concat_lists_(PP_NARG(__VA_ARGS__), __VA_ARGS__)
  265. char** concat_lists_(int nargs, ...);
  266. #define strjoin(j, ...) strjoin_(j, PP_NARG(__VA_ARGS__), __VA_ARGS__)
  267. char* strjoin_(char* joiner, size_t nargs, ...);
  268. #define strcatdup(...) strcatdup_(PP_NARG(__VA_ARGS__), __VA_ARGS__)
  269. char* strcatdup_(size_t nargs, ...);
  270. void recursive_glob(char* base_path, char* pattern, int flags, rglob* results);
  271. char* dir_name(char* path);
  272. char* base_name(char* path);
  273. size_t list_len(char** list);
  274. char* join_str_list(char* list[], char* joiner);
  275. char* sprintfdup(char* fmt, ...);
  276. char* read_whole_file(char* path, size_t* srcLen);
  277. static inline char* strskip(char* s, char* skip) {
  278. return s + strspn(s, skip);
  279. }
  280. #define FSU_EXCLUDE_HIDDEN (1<<0)
  281. #define FSU_NO_FOLLOW_SYMLINKS (1<<1)
  282. #define FSU_INCLUDE_DIRS (1<<2)
  283. #define FSU_EXCLUDE_FILES (1<<3)
  284. // return 0 to continue, nonzero to stop all directory scanning
  285. typedef int (*readDirCallbackFn)(char* /*fullPath*/, char* /*fileName*/, unsigned char /*type*/, void* /*data*/);
  286. // returns negative on error, nonzero if scanning was halted by the callback
  287. int recurse_dirs(
  288. char* path,
  289. readDirCallbackFn fn,
  290. void* data,
  291. int depth,
  292. unsigned int flags
  293. );
  294. typedef struct realname_entry {
  295. char* realname;
  296. time_t mtime;
  297. } realname_entry;
  298. struct {
  299. /*
  300. struct {
  301. int len;
  302. int alloc;
  303. struct {
  304. char* fake_name;
  305. realname_entry* entry;
  306. }* entries;
  307. } lookup; */
  308. hashtable names;
  309. int len;
  310. int alloc;
  311. struct realname_entry* entries;
  312. } realname_cache;
  313. void realname_cache_init();
  314. time_t realname_cache_add(char* fake_name, char* real_name);
  315. realname_entry* realname_cache_search_real(char* real_name);
  316. realname_entry* realname_cache_search(char* fake_name);
  317. char* realname_cache_find(char* fake_name);
  318. hashtable mkdir_cache;
  319. strlist compile_cache;
  320. void mkdirp_cached(char* path, mode_t mode) {
  321. void* there = hash_find(&mkdir_cache, path);
  322. if(!there) {
  323. hash_insert(&mkdir_cache, path, NULL);
  324. mkdirp(path, mode);
  325. }
  326. }
  327. int compile_cache_execute() {
  328. int ret = 0;
  329. // printf("compile cache length %d", compile_cache.len);
  330. for(int i = 0; i < compile_cache.len; i++) {
  331. // printf("%s\n", compile_cache.entries[i]);
  332. ret |= system(compile_cache.entries[i]);
  333. free(compile_cache.entries[i]);
  334. }
  335. compile_cache.len = 0;
  336. return ret;
  337. }
  338. size_t span_path(char* s) {
  339. size_t n = 0;
  340. for(; *s; s++, n++) {
  341. if(isspace(*s)) break;
  342. if(*s == '\\') {
  343. s++;
  344. n++;
  345. }
  346. }
  347. return n;
  348. }
  349. strlist* parse_gcc_dep_file(char* dep_file_path, time_t* newest_mtime) {
  350. size_t dep_src_len = 0;
  351. strlist* dep_list;
  352. time_t newest = 0;
  353. char* dep_src = read_whole_file(dep_file_path, &dep_src_len);
  354. if(!dep_src) return NULL;
  355. dep_list = strlist_new();
  356. // skip the first filename junk
  357. char* s = strchr(dep_src, ':');
  358. s++;
  359. int ret = 0;
  360. // gather dep strings, ignoring line continuations
  361. while(*s) {
  362. do {
  363. s = strskip(s, " \t\r\n");
  364. if(*s == '\\') {
  365. if(s[1] == '\r') s++;
  366. if(s[1] == '\n') s++;
  367. }
  368. } while(isspace(*s));
  369. int dlen = span_path(s);
  370. if(dlen == 0) break;
  371. time_t dep_mtime;
  372. char* dep_fake = strncache(s, dlen);
  373. char* dep_real = resolve_path(dep_fake, &dep_mtime);
  374. if(dep_mtime > newest) newest = dep_mtime;
  375. strlist_push(dep_list, dep_real);
  376. struncache(dep_fake);
  377. struncache(dep_real);
  378. s += dlen;
  379. }
  380. free(dep_src);
  381. if(newest_mtime) *newest_mtime = newest;
  382. return dep_list;
  383. }
  384. int gen_deps(char* src_path, char* dep_path, time_t src_mtime, time_t obj_mtime) {
  385. time_t dep_mtime = 0;
  386. time_t newest_mtime = 0;
  387. char* real_dep_path = resolve_path(dep_path, &dep_mtime);
  388. if(dep_mtime < src_mtime) {
  389. //gcc -MM -MG -MT $1 -MF "build/$1.d" $1 $CFLAGS $LDADD
  390. printf(" generating deps\n");
  391. char* cmd = sprintfdup("gcc -MM -MG -MT '' -MF %s %s %s", dep_path, src_path, g_gcc_opts_flat);
  392. system(cmd);
  393. free(cmd);
  394. }
  395. strlist* deps = parse_gcc_dep_file(real_dep_path, &newest_mtime);
  396. // free or process deps
  397. return newest_mtime > obj_mtime;
  398. FAIL:
  399. return 0;
  400. }
  401. size_t list_len(char** list) {
  402. size_t total = 0;
  403. for(; *list; list++) total++;
  404. return total;
  405. }
  406. char** concat_lists_(int nargs, ...) {
  407. size_t total = 0;
  408. char** out, **end;
  409. if(nargs == 0) return NULL;
  410. // calculate total list length
  411. va_list va;
  412. va_start(va, nargs);
  413. for(size_t i = 0; i < nargs; i++) {
  414. char** s = va_arg(va, char**);
  415. if(s) total += list_len(s);
  416. }
  417. va_end(va);
  418. out = malloc((total + 1) * sizeof(char**));
  419. end = out;
  420. va_start(va, nargs);
  421. // concat lists
  422. for(size_t i = 0; i < nargs; i++) {
  423. char** s = va_arg(va, char**);
  424. size_t l = list_len(s);
  425. if(s) {
  426. memcpy(end, s, l * sizeof(*s));
  427. end += l;
  428. }
  429. }
  430. va_end(va);
  431. *end = 0;
  432. return out;
  433. }
  434. char* join_str_list(char* list[], char* joiner) {
  435. size_t list_len = 0;
  436. size_t total = 0;
  437. size_t jlen = strlen(joiner);
  438. // calculate total length
  439. for(int i = 0; list[i]; i++) {
  440. list_len++;
  441. total += strlen(list[i]);
  442. }
  443. if(total == 0) return strdup("");
  444. total += (list_len - 1) * jlen;
  445. char* out = malloc((total + 1) * sizeof(*out));
  446. char* end = out;
  447. for(int i = 0; list[i]; i++) {
  448. char* s = list[i];
  449. size_t l = strlen(s);
  450. if(i > 0) {
  451. memcpy(end, joiner, jlen);
  452. end += jlen;
  453. }
  454. if(s) {
  455. memcpy(end, s, l);
  456. end += l;
  457. }
  458. total += strlen(list[i]);
  459. }
  460. *end = 0;
  461. return out;
  462. }
  463. // concatenate all argument strings together in a new buffer
  464. char* strcatdup_(size_t nargs, ...) {
  465. size_t total = 0;
  466. char* out, *end;
  467. if(nargs == 0) return NULL;
  468. // calculate total buffer len
  469. va_list va;
  470. va_start(va, nargs);
  471. for(size_t i = 0; i < nargs; i++) {
  472. char* s = va_arg(va, char*);
  473. if(s) total += strlen(s);
  474. }
  475. va_end(va);
  476. out = malloc((total + 1) * sizeof(char*));
  477. end = out;
  478. va_start(va, nargs);
  479. for(size_t i = 0; i < nargs; i++) {
  480. char* s = va_arg(va, char*);
  481. if(s) {
  482. strcpy(end, s); // not exactly the ost efficient, but maybe faster than
  483. end += strlen(s); // a C version. TODO: test the speed
  484. };
  485. }
  486. va_end(va);
  487. *end = 0;
  488. return out;
  489. }
  490. // concatenate all argument strings together in a new buffer,
  491. // with the given joining string between them
  492. char* strjoin_(char* joiner, size_t nargs, ...) {
  493. size_t total = 0;
  494. char* out, *end;
  495. size_t j_len;
  496. if(nargs == 0) return NULL;
  497. // calculate total buffer len
  498. va_list va;
  499. va_start(va, nargs);
  500. for(size_t i = 0; i < nargs; i++) {
  501. char* s = va_arg(va, char*);
  502. if(s) total += strlen(s);
  503. }
  504. va_end(va);
  505. j_len = strlen(joiner);
  506. total += j_len * (nargs - 1);
  507. out = malloc((total + 1) * sizeof(char*));
  508. end = out;
  509. va_start(va, nargs);
  510. for(size_t i = 0; i < nargs; i++) {
  511. char* s = va_arg(va, char*);
  512. if(s) {
  513. if(i > 0) {
  514. strcpy(end, joiner);
  515. end += j_len;
  516. }
  517. strcpy(end, s); // not exactly the ost efficient, but maybe faster than
  518. end += strlen(s); // a C version. TODO: test the speed
  519. };
  520. }
  521. va_end(va);
  522. *end = 0;
  523. return out;
  524. }
  525. // allocates a new buffer and calls sprintf with it
  526. // why isn't this a standard function?
  527. char* sprintfdup(char* fmt, ...) {
  528. va_list va;
  529. va_start(va, fmt);
  530. size_t n = vsnprintf(NULL, 0, fmt, va);
  531. char* buf = malloc(n + 1);
  532. va_end(va);
  533. va_start(va, fmt);
  534. vsnprintf(buf, n + 1, fmt, va);
  535. va_end(va);
  536. return buf;
  537. }
  538. char* dir_name(char* path) {
  539. char* n = strdup(path);
  540. char* o = dirname(n);
  541. return strcache(o);
  542. }
  543. char* base_name(char* path) {
  544. char* n = strdup(path);
  545. char* o = basename(n);
  546. return strcache(o);
  547. }
  548. int rglob_fn(char* full_path, char* file_name, unsigned char type, void* _results) {
  549. rglob* res = (rglob*)_results;
  550. if(0 == fnmatch(res->pattern, file_name, 0)) {
  551. check_alloc(res);
  552. res->entries[res->len].type = type;
  553. res->entries[res->len].full_path = strcache(full_path);
  554. res->entries[res->len].file_name = strcache(file_name);
  555. res->len++;
  556. }
  557. return 0;
  558. }
  559. void recursive_glob(char* base_path, char* pattern, int flags, rglob* results) {
  560. // to pass into recurse_dirs()
  561. results->pattern = pattern;
  562. results->len = 0;
  563. results->alloc = 32;
  564. results->entries = malloc(sizeof(*results->entries) * results->alloc);
  565. recurse_dirs(base_path, rglob_fn, results, -1, flags);
  566. }
  567. // does not handle escaped slashes
  568. int mkdirp(char* path, mode_t mode) {
  569. char* clean_path = strdup(path);
  570. // inch along the path creating each directory in line
  571. for(char* p = clean_path; *p; p++) {
  572. if(*p == '/') {
  573. *p = 0;
  574. if(mkdir(clean_path, mode)) {
  575. if(errno != EEXIST) goto FAIL;
  576. }
  577. *p = '/';
  578. }
  579. }
  580. // mop up the last dir
  581. if(mkdir(clean_path, mode)) {
  582. if(errno != EEXIST) goto FAIL;
  583. }
  584. free(clean_path);
  585. return 0;
  586. FAIL:
  587. free(clean_path);
  588. return -1;
  589. }
  590. void realname_cache_init() {
  591. realname_cache.len = 0;
  592. realname_cache.alloc = 1024;
  593. realname_cache.entries = malloc(realname_cache.alloc * sizeof(*realname_cache.entries));
  594. hash_init(&realname_cache.names, 1024);
  595. }
  596. time_t realname_cache_add(char* fake_name, char* real_name) {
  597. realname_entry* e = hash_find(&realname_cache.names, fake_name);
  598. if(e) return e->mtime;
  599. e = hash_find(&realname_cache.names, real_name);
  600. if(!e) {
  601. struct stat st;
  602. lstat(real_name, &st);
  603. e = &realname_cache.entries[realname_cache.len];
  604. e->realname = strcache(real_name);
  605. e->mtime = st.st_mtim.tv_sec;
  606. realname_cache.len++;
  607. hash_insert(&realname_cache.names, real_name, e);
  608. }
  609. hash_insert(&realname_cache.names, fake_name, e);
  610. return e->mtime;
  611. }
  612. realname_entry* realname_cache_search_real(char* real_name) {
  613. for(int i = 0; i < realname_cache.len; i++) {
  614. if(0 == strcmp(real_name, realname_cache.entries[i].realname)) {
  615. return &realname_cache.entries[i];
  616. }
  617. }
  618. return NULL;
  619. }
  620. realname_entry* realname_cache_search(char* fake_name) {
  621. return hash_find(&realname_cache.names, fake_name);
  622. }
  623. char* realname_cache_find(char* fake_name) {
  624. realname_entry* r = realname_cache_search(fake_name);
  625. return r ? r->realname : NULL;
  626. }
  627. // works like realpath(), except also handles ~/
  628. char* resolve_path(char* in, time_t* mtime_out) {
  629. int tmp_was_malloced = 0;
  630. char* out, *tmp;
  631. if(!in) return NULL;
  632. realname_entry* e = realname_cache_search(in);
  633. if(e) {
  634. if(mtime_out) *mtime_out = e->mtime;
  635. return strcache(e->realname);
  636. }
  637. // skip leading whitespace
  638. while(isspace(*in)) in++;
  639. // handle home dir shorthand
  640. if(in[0] == '~') {
  641. char* home = getenv("HOME");
  642. tmp_was_malloced = 1;
  643. tmp = malloc(sizeof(*tmp) * (strlen(home) + strlen(in) + 2));
  644. strcpy(tmp, home);
  645. strcat(tmp, "/"); // just in case
  646. strcat(tmp, in + 1);
  647. }
  648. else tmp = in;
  649. out = realpath(tmp, NULL);
  650. if(tmp_was_malloced) free(tmp);
  651. time_t t = 0;
  652. if(out) {
  653. // put it in the cache
  654. t = realname_cache_add(in, out);
  655. }
  656. else {
  657. // temporary
  658. struct stat st;
  659. if(!lstat(in, &st))
  660. t = st.st_mtim.tv_sec;
  661. }
  662. if(mtime_out) *mtime_out = t;
  663. return out ? out : in;
  664. }
  665. char* path_join_(size_t nargs, ...) {
  666. size_t total = 0;
  667. char* out, *end;
  668. size_t j_len;
  669. char* joiner = "/";
  670. int escape;
  671. if(nargs == 0) return NULL;
  672. // calculate total buffer length
  673. va_list va;
  674. va_start(va, nargs);
  675. for(size_t i = 0; i < nargs; i++) {
  676. char* s = va_arg(va, char*);
  677. if(s) total += strlen(s);
  678. }
  679. va_end(va);
  680. j_len = strlen(joiner);
  681. total += j_len * (nargs - 1);
  682. out = malloc((total + 1) * sizeof(char*));
  683. end = out;
  684. va_start(va, nargs);
  685. for(size_t i = 0; i < nargs; i++) {
  686. char* s = va_arg(va, char*);
  687. size_t l = strlen(s);
  688. if(s) {
  689. if(l > 1) {
  690. escape = s[l-2] == '\\' ? 1 : 0;
  691. }
  692. if(i > 0 && (s[0] == joiner[0])) {
  693. s++;
  694. l--;
  695. }
  696. if(i > 0 && i != nargs-1 && !escape && (s[l-1] == joiner[0])) {
  697. l--;
  698. }
  699. if(i > 0) {
  700. strcpy(end, joiner);
  701. end += j_len;
  702. }
  703. // should be strncpy, but GCC is so fucking stupid that it
  704. // has a warning about using strncpy to do exactly what
  705. // strncpy does if you read the fucking man page.
  706. // fortunately, we are already terminating our strings
  707. // manually so memcpy is a drop-in replacement here.
  708. memcpy(end, s, l);
  709. end += l;
  710. }
  711. }
  712. va_end(va);
  713. *end = 0;
  714. return out;
  715. }
  716. // returns negative on error, nonzero if scanning was halted by the callback
  717. int recurse_dirs(
  718. char* path,
  719. readDirCallbackFn fn,
  720. void* data,
  721. int depth,
  722. unsigned int flags
  723. ) {
  724. DIR* derp;
  725. struct dirent* result;
  726. int stop = 0;
  727. if(fn == NULL) {
  728. fprintf(stderr, "Error: readAllDir called with null function pointer.\n");
  729. return -1;
  730. }
  731. derp = opendir(path);
  732. if(derp == NULL) {
  733. fprintf(stderr, "Error opening directory '%s': %s\n", path, strerror(errno));
  734. return -1;
  735. }
  736. while((result = readdir(derp)) && !stop) {
  737. char* n = result->d_name;
  738. unsigned char type = DT_UNKNOWN;
  739. char* fullPath;
  740. // skip self and parent dir entries
  741. if(n[0] == '.') {
  742. if(n[1] == '.' && n[2] == 0) continue;
  743. if(n[1] == 0) continue;
  744. if(flags & FSU_EXCLUDE_HIDDEN) continue;
  745. }
  746. #ifdef _DIRENT_HAVE_D_TYPE
  747. type = result->d_type; // the way life should be
  748. #else
  749. // do some slow extra bullshit to get the type
  750. fullPath = path_join(path, n);
  751. struct stat upgrade_your_fs;
  752. lstat(fullPath, &upgrade_your_fs);
  753. if(S_ISREG(upgrade_your_fs.st_mode)) type = DT_REG;
  754. else if(S_ISDIR(upgrade_your_fs.st_mode)) type = DT_DIR;
  755. else if(S_ISLNK(upgrade_your_fs.st_mode)) type = DT_LNK;
  756. #endif
  757. if(flags & FSU_NO_FOLLOW_SYMLINKS && type == DT_LNK) {
  758. continue;
  759. }
  760. #ifdef _DIRENT_HAVE_D_TYPE
  761. fullPath = path_join(path, n);
  762. #endif
  763. if(type == DT_DIR) {
  764. if(flags & FSU_INCLUDE_DIRS) {
  765. stop = fn(fullPath, n, type, data);
  766. }
  767. if(depth != 0) {
  768. stop |= recurse_dirs(fullPath, fn, data, depth - 1, flags);
  769. }
  770. }
  771. else if(type == DT_REG) {
  772. if(!(flags & FSU_EXCLUDE_FILES)) {
  773. stop = fn(fullPath, n, type, data);
  774. }
  775. }
  776. free(fullPath);
  777. }
  778. closedir(derp);
  779. return stop;
  780. }
  781. // effectively a better, asynchronous version of system()
  782. // redirects and captures the child process i/o
  783. struct child_process_info* exec_process_pipe(char* exec_path, char* args[]) {
  784. int master, slave; //pty
  785. int in[2]; // io pipes
  786. int out[2];
  787. int err[2];
  788. const int RE = 0;
  789. const int WR = 1;
  790. // 0 = read, 1 = write
  791. if(pipe(in) < 0) {
  792. return NULL;
  793. }
  794. if(pipe(out) < 0) {
  795. close(in[0]);
  796. close(in[1]);
  797. return NULL;
  798. }
  799. if(pipe(err) < 0) {
  800. close(in[0]);
  801. close(in[1]);
  802. close(out[0]);
  803. close(out[1]);
  804. return NULL;
  805. }
  806. errno = 0;
  807. if(openpty(&master, &slave, NULL, NULL, NULL) < 0) {
  808. close(in[0]);
  809. close(in[1]);
  810. close(out[0]);
  811. close(out[1]);
  812. fprintf(stderr, "Error opening new pty for '%s' [errno=%d]\n", exec_path, errno);
  813. return NULL;
  814. }
  815. errno = 0;
  816. int child_pid = fork();
  817. if(child_pid == -1) {
  818. fprintf(stderr, "failed to fork trying to execute '%s'\n", exec_path);
  819. perror(strerror(errno));
  820. return NULL;
  821. }
  822. else if(child_pid == 0) { // child process
  823. // redirect standard fd's to the pipe fd's
  824. if(dup2(in[RE], fileno(stdin)) == -1) {
  825. printf("failed 1\n");
  826. exit(errno);
  827. }
  828. if(dup2(out[WR], fileno(stdout)) == -1) {
  829. printf("failed 2\n");
  830. exit(errno);
  831. }
  832. if(dup2(err[WR], fileno(stderr)) == -1) {
  833. printf("failed 3\n");
  834. exit(errno);
  835. }
  836. // close original fd's used by the parent
  837. close(in[0]);
  838. close(in[1]);
  839. close(out[0]);
  840. close(out[1]);
  841. close(err[0]);
  842. close(err[1]);
  843. close(master);
  844. close(slave);
  845. // die when the parent does (linux only)
  846. prctl(PR_SET_PDEATHSIG, SIGHUP);
  847. // swap for the desired program
  848. execvp(exec_path, args); // never returns if successful
  849. fprintf(stderr, "failed to execute '%s'\n", exec_path);
  850. exit(1); // kill the forked process
  851. }
  852. else { // parent process
  853. // close the child-end of the pipes
  854. struct child_process_info* cpi;
  855. cpi = calloc(1, sizeof(*cpi));
  856. cpi->child_stdin = in[WR];
  857. cpi->child_stdout = out[RE];
  858. cpi->child_stderr = err[RE];
  859. cpi->f_stdin = fdopen(cpi->child_stdin, "wb");
  860. cpi->f_stdout = fdopen(cpi->child_stdout, "rb");
  861. cpi->f_stderr = fdopen(cpi->child_stderr, "rb");
  862. // set to non-blocking
  863. fcntl(cpi->child_stdout, F_SETFL, fcntl(cpi->child_stdout, F_GETFL) | O_NONBLOCK);
  864. fcntl(cpi->child_stderr, F_SETFL, fcntl(cpi->child_stderr, F_GETFL) | O_NONBLOCK);
  865. close(in[0]);
  866. close(out[1]);
  867. close(err[1]);
  868. close(slave);
  869. cpi->pid = child_pid;
  870. // int status;
  871. // returns 0 if nothing happened, -1 on error
  872. // pid = waitpid(childPID, &status, WNOHANG);
  873. return cpi;
  874. }
  875. return NULL; // shouldn't reach here
  876. }
  877. char* read_whole_file(char* path, size_t* srcLen) {
  878. size_t fsize, total_read = 0, bytes_read;
  879. char* contents;
  880. FILE* f;
  881. f = fopen(path, "rb");
  882. if(!f) {
  883. fprintf(stderr, "Could not open file \"%s\"\n", path);
  884. return NULL;
  885. }
  886. fseek(f, 0, SEEK_END);
  887. fsize = ftell(f);
  888. rewind(f);
  889. contents = malloc(fsize + 1);
  890. while(total_read < fsize) {
  891. bytes_read = fread(contents + total_read, sizeof(char), fsize - total_read, f);
  892. total_read += bytes_read;
  893. }
  894. contents[fsize] = 0;
  895. fclose(f);
  896. if(srcLen) *srcLen = fsize;
  897. return contents;
  898. }