1 /*
2 # This file is part of the Astrometry.net suite.
3 # Licensed under a 3-clause BSD style license - see LICENSE
4 */
5
6 #include <stdio.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <stdint.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <sys/time.h>
14 #include <sys/resource.h>
15 #include <sys/select.h>
16 #include <fcntl.h>
17 #include <arpa/inet.h>
18 #include <netinet/in.h>
19 #include <stdlib.h>
20 #include <signal.h>
21 #include <assert.h>
22 #include <unistd.h>
23 #include <stdarg.h>
24 #include <libgen.h>
25 #include <dirent.h>
26 #include <time.h>
27
28 #include "os-features.h"
29 #include "ioutils.h"
30 #include "errors.h"
31 #include "log.h"
32
33 uint32_t ENDIAN_DETECTOR = 0x01020304;
34
35 #include "qsort_reentrant.c"
36
copy_file(const char * infn,const char * outfn)37 int copy_file(const char* infn, const char* outfn) {
38 FILE* fin = fopen(infn, "rb");
39 FILE* fout = fopen(outfn, "wb");
40 struct stat st;
41 off_t len;
42 if (!fin) {
43 SYSERROR("Failed to open xyls file \"%s\" for copying", infn);
44 return -1;
45 }
46 if (stat(infn, &st)) {
47 SYSERROR("Failed to stat file \"%s\"", infn);
48 return -1;
49 }
50 len = st.st_size;
51 if (!fout) {
52 SYSERROR("Failed to open output xyls file \"%s\" for copying", outfn);
53 return -1;
54 }
55 if (pipe_file_offset(fin, 0, len, fout)) {
56 ERROR("Failed to copy xyls file \"%s\" to \"%s\"", infn, outfn);
57 return -1;
58 }
59 if (fclose(fin)) {
60 SYSERROR("Failed to close input file \"%s\"", infn);
61 return -1;
62 }
63 if (fclose(fout)) {
64 SYSERROR("Failed to close output file \"%s\"", outfn);
65 return -1;
66 }
67 return 0;
68 }
69
split_long_string(const char * str,int firstlinew,int linew,sl * lst)70 sl* split_long_string(const char* str, int firstlinew, int linew, sl* lst) {
71 const char* s;
72 char* added;
73 int lw = firstlinew;
74 if (!lst)
75 lst = sl_new(16);
76 assert(linew > 1);
77 assert(str);
78 s = str;
79 while (1) {
80 int brk = -1;
81 int i, N;
82 N = strlen(s);
83 if (!N)
84 break;
85 if (N <= lw) {
86 sl_append(lst, s);
87 break;
88 }
89 // scan for last space (' ') before "lw".
90 for (i=0; i<MIN(lw+1, N); i++) {
91 if (s[i] == ' ')
92 brk = i;
93 }
94 if (brk <= 1) {
95 // no space -- hard-break at "lw"; add hyphen.
96 added = sl_appendf(lst, "%.*s-", lw-1, s);
97 s += strlen(added)-1;
98 } else {
99 // trim trailing spaces...
100 while (brk >= 1 && s[brk-1] == ' ')
101 brk--;
102 added = sl_appendf(lst, "%.*s", brk, s);
103 s += strlen(added);
104 // trim spaces.
105 while (s && s[0]==' ') s++;
106 }
107 lw = linew;
108 }
109 return lst;
110 }
111
split_string_once(const char * str,const char * splitstr,char ** first,char ** second)112 int split_string_once(const char* str, const char* splitstr,
113 char** first, char** second) {
114 char* start = strstr(str, splitstr);
115 int n;
116 if (!start) {
117 if (first) *first = NULL;
118 if (second) *second = NULL;
119 return 0;
120 }
121 if (first) {
122 n = start - str;
123 *first = malloc(1 + n);
124 memcpy(*first, str, n);
125 (*first)[n] = '\0';
126 }
127 if (second) {
128 char* sec = start + strlen(splitstr);
129 n = strlen(sec);
130 *second = malloc(1 + n);
131 memcpy(*second, sec, n);
132 (*second)[n] = '\0';
133 }
134 return 1;
135 }
136
write_file(const char * fn,const char * data,int len)137 int write_file(const char* fn, const char* data, int len) {
138 FILE* fid = fopen(fn, "wb");
139 if (!fid) {
140 SYSERROR("Failed to open file \"%s\"", fn);
141 return -1;
142 }
143 if (fwrite(data, 1, len, fid) != len) {
144 SYSERROR("Failed to write %i bytes to file \"%s\"", len, fn);
145 return -1;
146 }
147 if (fclose(fid)) {
148 SYSERROR("Failed to close file \"%s\"", fn);
149 return -1;
150 }
151 return 0;
152 }
153
pad_fid(FILE * fid,size_t len,char pad)154 int pad_fid(FILE* fid, size_t len, char pad) {
155 off_t offset;
156 size_t npad;
157 size_t i;
158 char buf[1024];
159 offset = ftello(fid);
160 if (len <= offset)
161 return 0;
162 npad = len - offset;
163 // pad in blocks.
164 memset(buf, pad, sizeof(buf));
165 for (i=0; i<npad; i+=sizeof(buf)) {
166 size_t n = MIN(sizeof(buf), npad-i);
167 if (fwrite(buf, 1, n, fid) != n) {
168 SYSERROR("Failed to pad file");
169 return -1;
170 }
171 }
172 return 0;
173 }
174
pad_file(char * filename,size_t len,char pad)175 int pad_file(char* filename, size_t len, char pad) {
176 int rtn;
177 FILE* fid = fopen(filename, "ab");
178 if (!fid) {
179 SYSERROR("Failed to open file \"%s\" for padding", filename);
180 return -1;
181 }
182 rtn = pad_fid(fid, len, pad);
183 if (!rtn && fclose(fid)) {
184 SYSERROR("Failed to close file \"%s\" after padding it", filename);
185 return -1;
186 }
187 return rtn;
188 }
189
190 Malloc
dirname_safe(const char * path)191 char* dirname_safe(const char* path) {
192 char* copy = strdup(path);
193 char* res = strdup(dirname(copy));
194 free(copy);
195 return res;
196 }
197
198 Malloc
basename_safe(const char * path)199 char* basename_safe(const char* path) {
200 char* copy = strdup(path);
201 char* res = strdup(basename(copy));
202 free(copy);
203 return res;
204 }
205
find_file_in_dirs(const char ** dirs,int ndirs,const char * filename,anbool allow_absolute)206 char* find_file_in_dirs(const char** dirs, int ndirs, const char* filename, anbool allow_absolute) {
207 int i;
208 if (!filename) return NULL;
209 if (allow_absolute && filename[0] == '/') {
210 if (file_readable(filename))
211 return strdup(filename);
212 }
213 for (i=0; i<ndirs; i++) {
214 char* fn;
215 asprintf_safe(&fn, "%s/%s", dirs[i], filename);
216 if (file_readable(fn))
217 return fn;
218 free(fn);
219 }
220 return NULL;
221 }
222
get_cpu_usage()223 float get_cpu_usage() {
224 struct rusage r;
225 float sofar;
226 if (getrusage(RUSAGE_SELF, &r)) {
227 SYSERROR("Failed to get resource usage");
228 return -1.0;
229 }
230 sofar = (float)(r.ru_utime.tv_sec + r.ru_stime.tv_sec) +
231 (1e-6 * (r.ru_utime.tv_usec + r.ru_stime.tv_usec));
232 return sofar;
233 }
234
streq(const char * s1,const char * s2)235 anbool streq(const char* s1, const char* s2) {
236 if (s1 == NULL || s2 == NULL)
237 return (s1 == s2);
238 return (strcmp(s1, s2) == 0) ? TRUE : FALSE;
239 }
240
strcaseeq(const char * s1,const char * s2)241 anbool strcaseeq(const char* s1, const char* s2) {
242 if (s1 == NULL || s2 == NULL)
243 return (s1 == s2);
244 return !strcasecmp(s1, s2);
245 }
246
pipe_file_offset(FILE * fin,off_t offset,off_t length,FILE * fout)247 int pipe_file_offset(FILE* fin, off_t offset, off_t length, FILE* fout) {
248 char buf[1024];
249 off_t i;
250 if (fseeko(fin, offset, SEEK_SET)) {
251 SYSERROR("Failed to seek to offset %zu", (size_t)offset);
252 return -1;
253 }
254 for (i=0; i<length; i+=sizeof(buf)) {
255 int n = sizeof(buf);
256 if (i + n > length) {
257 n = length - i;
258 }
259 if (fread(buf, 1, n, fin) != n) {
260 SYSERROR("Failed to read %i bytes", n);
261 return -1;
262 }
263 if (fwrite(buf, 1, n, fout) != n) {
264 SYSERROR("Failed to write %i bytes", n);
265 return -1;
266 }
267 }
268 return 0;
269 }
270
asprintf_safe(char ** strp,const char * format,...)271 void asprintf_safe(char** strp, const char* format, ...) {
272 va_list lst;
273 int rtn;
274 va_start(lst, format);
275 rtn = vasprintf(strp, format, lst);
276 if (rtn == -1) {
277 fprintf(stderr, "Error, vasprintf() failed: %s\n", strerror(errno));
278 fprintf(stderr, " (format: \"%s\")\n", format);
279 assert(0);
280 *strp = NULL;
281 }
282 va_end(lst);
283 }
284
dir_get_contents(const char * path,sl * list,anbool filesonly,anbool recurse)285 sl* dir_get_contents(const char* path, sl* list, anbool filesonly, anbool recurse) {
286 DIR* dir = opendir(path);
287 if (!dir) {
288 fprintf(stderr, "Failed to open directory \"%s\": %s\n", path, strerror(errno));
289 return NULL;
290 }
291 if (!list)
292 list = sl_new(256);
293 while (1) {
294 struct dirent* de;
295 struct stat st;
296 char* name;
297 char* fullpath;
298 anbool freeit = FALSE;
299 errno = 0;
300 de = readdir(dir);
301 if (!de) {
302 if (errno)
303 fprintf(stderr, "Failed to read entry from directory \"%s\": %s\n", path, strerror(errno));
304 break;
305 }
306 name = de->d_name;
307 if (!strcmp(name, ".") || !strcmp(name, ".."))
308 continue;
309 asprintf_safe(&fullpath, "%s/%s", path, name);
310 if (stat(fullpath, &st)) {
311 fprintf(stderr, "Failed to stat file %s: %s\n", fullpath, strerror(errno));
312 // this can happen when files are deleted, eg
313 continue;
314 //closedir(dir);
315 //sl_free2(list);
316 //return NULL;
317 }
318
319 if (filesonly) {
320 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
321 sl_append_nocopy(list, fullpath);
322 else
323 freeit = TRUE;
324 } else {
325 sl_append_nocopy(list, fullpath);
326 }
327 if (recurse && S_ISDIR(st.st_mode)) {
328 dir_get_contents(path, list, filesonly, recurse);
329 }
330 if (freeit)
331 free(fullpath);
332 }
333 closedir(dir);
334 return list;
335 }
336
readfd(int fd,char * buf,int NB,char ** pcursor,sl * lines,anbool * pdone)337 static int readfd(int fd, char* buf, int NB, char** pcursor,
338 sl* lines, anbool* pdone) {
339 int nr;
340 int i, nleft;
341 char* cursor = *pcursor;
342 nr = read(fd, cursor, buf + NB - cursor);
343 //printf("nr = %i\n", nr);
344 if (nr == -1) {
345 SYSERROR("Failed to read output fd");
346 return -1;
347 }
348 if (nr == 0) {
349 if (cursor != buf) {
350 //printf("flushing the last line\n");
351 sl_appendf(lines, "%.*s", (int)(cursor - buf), buf);
352 }
353 *pdone = TRUE;
354 return 0;
355 }
356
357 // join the newly-read bytes with the carried-over ones.
358 nleft = nr + (cursor - buf);
359 cursor = buf;
360 //printf("nleft = %i\n", nleft);
361
362 for (i=0; i<nleft; i++) {
363 if (cursor[i] == '\n' || cursor[i] == '\0') {
364 cursor[i] = '\0';
365 sl_append(lines, cursor);
366 //printf(" found line of length %i: \"%s\"\n", i, cursor);
367 cursor += (i+1);
368 nleft -= (i+1);
369 //printf(" now nleft = %i\n", nleft);
370 i = -1;
371 }
372 }
373
374 if (nleft == NB) {
375 //printf(" buffer full with no newline\n");
376 sl_appendf(lines, "%.*s", NB, buf);
377 cursor = buf;
378 } else if (nleft) {
379 if (buf == cursor) {
380 cursor += nleft;
381 } else {
382 //printf(" moving %i to the start of the buffer\n", nleft);
383 memmove(buf, cursor, nleft);
384 cursor = buf + nleft;
385 }
386 } else {
387 cursor = buf;
388 }
389 *pcursor = cursor;
390 return 0;
391 }
392
run_command_get_outputs(const char * cmd,sl ** outlines,sl ** errlines)393 int run_command_get_outputs(const char* cmd, sl** outlines, sl** errlines) {
394 int outpipe[2];
395 int errpipe[2];
396 pid_t pid;
397
398 if (outlines) {
399 if (pipe(outpipe) == -1) {
400 SYSERROR("Failed to create stdout pipe");
401 return -1;
402 }
403 }
404 if (errlines) {
405 if (pipe(errpipe) == -1) {
406 SYSERROR("Failed to create stderr pipe");
407 return -1;
408 }
409 }
410
411 fflush(stdout);
412 pid = fork();
413 if (pid == -1) {
414 SYSERROR("Failed to fork");
415 return -1;
416 } else if (pid == 0) {
417 // Child process.
418 if (outlines) {
419 close(outpipe[0]);
420 // bind stdout to the pipe.
421 if (dup2(outpipe[1], STDOUT_FILENO) == -1) {
422 SYSERROR("Failed to dup2 stdout");
423 _exit( -1);
424 }
425 }
426 if (errlines) {
427 close(errpipe[0]);
428 // bind stderr to the pipe.
429 if (dup2(errpipe[1], STDERR_FILENO) == -1) {
430 SYSERROR("Failed to dup2 stderr");
431 _exit( -1);
432 }
433 }
434 // Use a "system"-like command to allow fancier commands.
435 if (execlp("/bin/sh", "/bin/sh", "-c", cmd, (char*)NULL)) {
436 SYSERROR("Failed to execlp");
437 _exit( -1);
438 }
439 // execlp doesn't return.
440 } else {
441 char outbuf[1024];
442 char errbuf[1024];
443 int status;
444 anbool outdone=TRUE, errdone=TRUE;
445 int outfd = -1;
446 int errfd = -1;
447 char* outcursor = outbuf;
448 char* errcursor = errbuf;
449 int rtn = 0;
450
451 // Parent process.
452 if (outlines) {
453 close(outpipe[1]);
454 outdone = FALSE;
455 *outlines = sl_new(256);
456 outfd = outpipe[0];
457 assert(outfd<FD_SETSIZE);
458 }
459 if (errlines) {
460 close(errpipe[1]);
461 errdone = FALSE;
462 *errlines = sl_new(256);
463 errfd = errpipe[0];
464 assert(errfd<FD_SETSIZE);
465 }
466
467 // Read from child process's streams...
468 while (!outdone || !errdone) {
469 fd_set readset;
470 #if !(defined(__CYGWIN__))
471 fd_set errset;
472 #endif
473 int ready;
474 FD_ZERO(&readset);
475 #if !(defined(__CYGWIN__))
476 FD_ZERO(&errset);
477 #endif
478 //printf("outdone = %i, errdone = %i\n", outdone, errdone);
479 if (!outdone) {
480 FD_SET(outfd, &readset);
481 #if !(defined(__CYGWIN__))
482 FD_SET(outfd, &errset);
483 #endif
484 }
485 if (!errdone) {
486 FD_SET(errfd, &readset);
487 #if !(defined(__CYGWIN__))
488 FD_SET(errfd, &errset);
489 #endif
490 }
491 ready = select(MAX(outfd, errfd) + 1, &readset, NULL,
492 #if !(defined(__CYGWIN__))
493 &errset,
494 #else
495 NULL,
496 #endif
497 NULL);
498 if (ready == -1) {
499 SYSERROR("select() failed");
500 rtn = -1;
501 goto parentreturn;
502 }
503 if (!outdone) {
504 if (FD_ISSET(outfd, &readset)) {
505 // printf("reading 'out' stream\n");
506 if (readfd(outfd, outbuf, sizeof(outbuf), &outcursor,
507 *outlines, &outdone)) {
508 ERROR("Failed to read from child's output stream");
509 rtn = -1;
510 goto parentreturn;
511 }
512 }
513 // https://groups.google.com/d/msg/astrometry/H0bQBjaoZeo/19pe8DXGoigJ
514 // and https://groups.google.com/forum/#!topic/astrometry/quGEbY1CgR8
515 #if !(defined(__CYGWIN__) || defined(__sun))
516 if (FD_ISSET(outfd, &errset)) {
517 SYSERROR("error reading from child output stream");
518 rtn = -1;
519 goto parentreturn;
520 }
521 #endif
522 }
523 if (!errdone) {
524 if (FD_ISSET(errfd, &readset)) {
525 // printf("reading 'err' stream\n");
526 if (readfd(errfd, errbuf, sizeof(errbuf), &errcursor,
527 *errlines, &errdone)) {
528 ERROR("Failed to read from child's error stream");
529 rtn = -1;
530 goto parentreturn;
531 }
532
533 }
534 #if !(defined(__CYGWIN__))
535 if (FD_ISSET(errfd, &errset)) {
536 SYSERROR("error reading from child error stream");
537 rtn = -1;
538 goto parentreturn;
539 }
540 #endif
541 }
542 }
543
544 //printf("Waiting for command to finish (PID %i).\n", (int)pid);
545 do {
546 //logverb("Waiting for command to finish...\n");
547 int opts = 0; //WNOHANG;
548 pid_t wpid = waitpid(pid, &status, opts);
549 if (wpid == -1) {
550 SYSERROR("Failed to waitpid() for command to finish");
551 rtn = -1;
552 goto parentreturn;
553 }
554 //logverb("waitpid() returned\n");
555 //if (pid == 0)
556 // process has not finished.
557 if (WIFSIGNALED(status)) {
558 ERROR("Command was killed by signal %i", WTERMSIG(status));
559 rtn = -1;
560 goto parentreturn;
561 } else {
562 int exitval = WEXITSTATUS(status);
563 if (exitval == 127) {
564 ERROR("Command not found: %s", cmd);
565 rtn = exitval;
566 goto parentreturn;
567 } else if (exitval) {
568 ERROR("Command failed: return value %i", exitval);
569 rtn = exitval;
570 goto parentreturn;
571 }
572 }
573 } while (!WIFEXITED(status) && !WIFSIGNALED(status));
574
575 parentreturn:
576 if (outlines)
577 close(outpipe[0]);
578 if (errlines)
579 close(errpipe[0]);
580 return rtn;
581 }
582
583 return 0;
584 }
585
mkdir_p(const char * dirpath)586 int mkdir_p(const char* dirpath) {
587 sl* tomake = sl_new(4);
588 char* path = strdup(dirpath);
589 while (!file_exists(path)) {
590 char* dir;
591 sl_push(tomake, path);
592 dir = strdup(dirname(path));
593 free(path);
594 path = dir;
595 }
596 free(path);
597 while (sl_size(tomake)) {
598 char* path = sl_pop(tomake);
599 if (mkdir(path, 0777)) {
600 SYSERROR("Failed to mkdir(%s)", path);
601 sl_free2(tomake);
602 free(path);
603 return -1;
604 }
605 free(path);
606 }
607 sl_free2(tomake);
608 return 0;
609 }
610
shell_escape(const char * str)611 char* shell_escape(const char* str) {
612 char* escape = "|&;()<> \t\n\\'\"";
613 int nescape = 0;
614 int len = strlen(str);
615 int i;
616 char* result;
617 int j;
618
619 for (i=0; i<len; i++) {
620 char* cp = strchr(escape, str[i]);
621 if (!cp) continue;
622 nescape++;
623 }
624 result = malloc(len + nescape + 1);
625 for (i=0, j=0; i<len; i++, j++) {
626 char* cp = strchr(escape, str[i]);
627 if (!cp) {
628 result[j] = str[i];
629 } else {
630 result[j] = '\\';
631 j++;
632 result[j] = str[i];
633 }
634 }
635 assert(j == (len + nescape));
636 result[j] = '\0';
637 return result;
638 }
639
get_temp_dir()640 static char* get_temp_dir() {
641 char* dir = getenv("TMP");
642 if (!dir) {
643 dir = "/tmp";
644 }
645 return dir;
646 }
647
create_temp_file(const char * fn,const char * dir)648 char* create_temp_file(const char* fn, const char* dir) {
649 char* tempfile;
650 int fid;
651 if (!dir) {
652 dir = get_temp_dir();
653 }
654
655 asprintf_safe(&tempfile, "%s/tmp.%s.XXXXXX", dir, fn);
656 fid = mkstemp(tempfile);
657 if (fid == -1) {
658 fprintf(stderr, "Failed to create temp file: %s\n", strerror(errno));
659 exit(-1);
660 }
661 close(fid);
662 //printf("Created temp file %s\n", tempfile);
663 return tempfile;
664 }
665
create_temp_dir(const char * name,const char * dir)666 char* create_temp_dir(const char* name, const char* dir) {
667 char* tempdir;
668 if (!dir) {
669 dir = get_temp_dir();
670 }
671 asprintf_safe(&tempdir, "%s/tmp.%s.XXXXXX", dir, name);
672 // no mkdtemp() in some versions of Solaris;
673 // https://groups.google.com/forum/#!topic/astrometry/quGEbY1CgR8
674 #if defined(__sun)
675 mktemp(tempdir);
676 if (!mkdir(tempdir, 0700)) {
677 SYSERROR("Failed to create temp dir");
678 return NULL;
679 }
680 #else
681 if (!mkdtemp(tempdir)) {
682 SYSERROR("Failed to create temp dir");
683 return NULL;
684 }
685 #endif
686 return tempdir;
687 }
688
file_get_lines(const char * fn,anbool include_newlines)689 sl* file_get_lines(const char* fn, anbool include_newlines) {
690 FILE* fid;
691 sl* list;
692 fid = fopen(fn, "r");
693 if (!fid) {
694 SYSERROR("Failed to open file %s", fn);
695 return NULL;
696 }
697 list = fid_get_lines(fid, include_newlines);
698 fclose(fid);
699 return list;
700 }
701
fid_add_lines(FILE * fid,anbool include_newlines,sl * list)702 sl* fid_add_lines(FILE* fid, anbool include_newlines, sl* list) {
703 if (!list)
704 list = sl_new(256);
705 while (1) {
706 char* line = read_string_terminated(fid, "\n\r\0", 3, include_newlines);
707 if (!line) {
708 // error.
709 SYSERROR("Failed to read a line");
710 sl_free2(list);
711 return NULL;
712 }
713 if (feof(fid) && line[0] == '\0') {
714 free(line);
715 break;
716 }
717 sl_append_nocopy(list, line);
718 if (feof(fid))
719 break;
720 }
721 return list;
722 }
723
fid_get_lines(FILE * fid,anbool include_newlines)724 sl* fid_get_lines(FILE* fid, anbool include_newlines) {
725 return fid_add_lines(fid, include_newlines, NULL);
726 }
727
file_get_contents_offset(const char * fn,int offset,int size)728 char* file_get_contents_offset(const char* fn, int offset, int size) {
729 char* buf = NULL;
730 FILE* fid = NULL;
731 fid = fopen(fn, "rb");
732 if (!fid) {
733 SYSERROR("failed to open file \"%s\"", fn);
734 goto bailout;
735 }
736 buf = malloc(size);
737 if (!buf) {
738 SYSERROR("failed to malloc %i bytes", size);
739 goto bailout;
740 }
741 if (offset) {
742 if (fseeko(fid, offset, SEEK_SET)) {
743 SYSERROR("failed to fseeko to %i in file \"%s\"", offset, fn);
744 goto bailout;
745 }
746 }
747 if (fread(buf, 1, size, fid) != size) {
748 SYSERROR("failed to read %i bytes from \"%s\"", size, fn);
749 goto bailout;
750 }
751 fclose(fid);
752 return buf;
753 bailout:
754 if (fid)
755 fclose(fid);
756 if (buf)
757 free(buf);
758 return NULL;
759 }
760
file_get_contents(const char * fn,size_t * len,anbool addzero)761 void* file_get_contents(const char* fn, size_t* len, anbool addzero) {
762 struct stat st;
763 char* buf;
764 FILE* fid;
765 off_t size;
766 if (stat(fn, &st)) {
767 fprintf(stderr, "file_get_contents: failed to stat file \"%s\"", fn);
768 return NULL;
769 }
770 size = st.st_size;
771 fid = fopen(fn, "rb");
772 if (!fid) {
773 fprintf(stderr, "file_get_contents: failed to open file \"%s\": %s\n", fn, strerror(errno));
774 return NULL;
775 }
776 buf = malloc(size + (addzero ? 1 : 0));
777 if (!buf) {
778 fprintf(stderr, "file_get_contents: couldn't malloc %lu bytes.\n", (long)size);
779 return NULL;
780 }
781 if (fread(buf, 1, size, fid) != size) {
782 fprintf(stderr, "file_get_contents: failed to read %lu bytes: %s\n", (long)size, strerror(errno));
783 free(buf);
784 return NULL;
785 }
786 fclose(fid);
787 if (addzero)
788 buf[size] = '\0';
789 if (len)
790 *len = size;
791 return buf;
792 }
get_mmap_size(size_t start,size_t size,off_t * mapstart,size_t * mapsize,int * pgap)793 void get_mmap_size(size_t start, size_t size, off_t* mapstart, size_t* mapsize, int* pgap) {
794 int ps = getpagesize();
795 int gap = start % ps;
796 // start must be a multiple of pagesize.
797 *mapstart = start - gap;
798 *mapsize = size + gap;
799 *pgap = gap;
800 }
801
file_get_last_modified_time(const char * fn)802 time_t file_get_last_modified_time(const char* fn) {
803 struct stat st;
804 if (stat(fn, &st)) {
805 SYSERROR("Failed to stat() file \"%s\"", fn);
806 return 0;
807 }
808 return st.st_mtime;
809 }
810
file_get_last_modified_string(const char * fn,const char * timeformat,anbool utc,char * output,size_t outsize)811 int file_get_last_modified_string(const char* fn, const char* timeformat,
812 anbool utc, char* output, size_t outsize) {
813 struct tm tym;
814 time_t t;
815
816 t = file_get_last_modified_time(fn);
817 if (t == 0) {
818 return -1;
819 }
820 if (utc) {
821 if (!gmtime_r(&t, &tym)) {
822 SYSERROR("gmtime_r() failed");
823 return -1;
824 }
825 } else {
826 if (!localtime_r(&t, &tym)) {
827 SYSERROR("localtime_r() failed");
828 return -1;
829 }
830 }
831 strftime(output, outsize, timeformat, &tym);
832 return 0;
833 }
834
file_exists(const char * fn)835 anbool file_exists(const char* fn) {
836 return fn && (access(fn, F_OK) == 0);
837 }
838
file_readable(const char * fn)839 anbool file_readable(const char* fn) {
840 return fn && (access(fn, R_OK) == 0);
841 }
842
file_executable(const char * fn)843 anbool file_executable(const char* fn) {
844 return fn && (access(fn, X_OK) == 0);
845 }
846
path_is_dir(const char * path)847 anbool path_is_dir(const char* path) {
848 struct stat st;
849 if (stat(path, &st)) {
850 SYSERROR("Couldn't stat path %s", path);
851 return FALSE;
852 }
853 //return st.st_mode & S_IFDIR;
854 return S_ISDIR(st.st_mode);
855 }
856
starts_with(const char * str,const char * prefix)857 int starts_with(const char* str, const char* prefix) {
858 int len = strlen(prefix);
859 if (strncmp(str, prefix, len))
860 return 0;
861 return 1;
862 }
863
ends_with(const char * str,const char * suffix)864 int ends_with(const char* str, const char* suffix) {
865 int len = strlen(suffix);
866 int len2 = strlen(str);
867 if (len > len2)
868 return 0;
869 if (strncmp(str + len2 - len, suffix, len))
870 return 0;
871 return 1;
872 }
873
strdup_safe(const char * str)874 char* strdup_safe(const char* str) {
875 char* rtn;
876 if (!str) return NULL;
877 rtn = strdup(str);
878 if (!rtn) {
879 fprintf(stderr, "Failed to strdup: %s\n", strerror(errno));
880 assert(0);
881 }
882 return rtn;
883 }
884
885 static int oldsigbus_valid = 0;
886 static struct sigaction oldsigbus;
sigbus_handler(int sig)887 static void sigbus_handler(int sig) {
888 fprintf(stderr, "\n\n"
889 "SIGBUS (Bus error) signal received.\n"
890 "One reason this can happen is that an I/O error is encountered\n"
891 "on a file that we are reading with \"mmap\".\n\n"
892 "Bailing out now.\n\n");
893 fflush(stderr);
894 exit(-1);
895 }
896
add_sigbus_mmap_warning()897 void add_sigbus_mmap_warning() {
898 struct sigaction sigbus;
899 memset(&sigbus, 0, sizeof(struct sigaction));
900 sigbus.sa_handler = sigbus_handler;
901 if (sigaction(SIGBUS, &sigbus, &oldsigbus)) {
902 fprintf(stderr, "Failed to change SIGBUS handler: %s\n", strerror(errno));
903 return;
904 }
905 oldsigbus_valid = 1;
906 }
907
reset_sigbus_mmap_warning()908 void reset_sigbus_mmap_warning() {
909 if (oldsigbus_valid) {
910 if (sigaction(SIGBUS, &oldsigbus, NULL)) {
911 fprintf(stderr, "Failed to restore SIGBUS handler: %s\n", strerror(errno));
912 return;
913 }
914 }
915 }
916
is_word(const char * cmdline,const char * keyword,char ** cptr)917 int is_word(const char* cmdline, const char* keyword, char** cptr) {
918 int len = strlen(keyword);
919 if (strncmp(cmdline, keyword, len))
920 return 0;
921 *cptr = (char*)(cmdline + len);
922 return 1;
923 }
924
read_complain(FILE * fin,const char * attempted)925 void read_complain(FILE* fin, const char* attempted) {
926 if (feof(fin)) {
927 SYSERROR("Couldn't read %s: end-of-file", attempted);
928 } else if (ferror(fin)) {
929 SYSERROR("Couldn't read %s", attempted);
930 } else {
931 SYSERROR("Couldn't read %s", attempted);
932 }
933 }
934
read_u8(FILE * fin,unsigned char * val)935 int read_u8(FILE* fin, unsigned char* val) {
936 if (fread(val, 1, 1, fin) == 1) {
937 return 0;
938 } else {
939 read_complain(fin, "u8");
940 return 1;
941 }
942 }
943
read_u16(FILE * fin,unsigned int * val)944 int read_u16(FILE* fin, unsigned int* val) {
945 uint16_t v;
946 if (fread(&v, 2, 1, fin) == 1) {
947 *val = v;
948 return 0;
949 } else {
950 read_complain(fin, "u8");
951 return 1;
952 }
953 }
954
read_u32_portable(FILE * fin,unsigned int * val)955 int read_u32_portable(FILE* fin, unsigned int* val) {
956 uint32_t u;
957 if (fread(&u, 4, 1, fin) == 1) {
958 *val = ntohl(u);
959 return 0;
960 } else {
961 read_complain(fin, "u32");
962 return 1;
963 }
964 }
965
read_double(FILE * fin,double * val)966 int read_double(FILE* fin, double* val) {
967 if (fread(val, sizeof(double), 1, fin) == 1) {
968 return 0;
969 } else {
970 read_complain(fin, "double");
971 return 1;
972 }
973 }
974
read_u32(FILE * fin,unsigned int * val)975 int read_u32(FILE* fin, unsigned int* val) {
976 uint32_t u;
977 if (fread(&u, 4, 1, fin) == 1) {
978 *val = (unsigned int)u;
979 return 0;
980 } else {
981 read_complain(fin, "u32 native");
982 return 1;
983 }
984 }
985
read_u32s_portable(FILE * fin,unsigned int * val,int n)986 int read_u32s_portable(FILE* fin, unsigned int* val, int n) {
987 int i;
988 uint32_t* u = malloc(sizeof(uint32_t) * n);
989 if (!u) {
990 fprintf(stderr, "Couldn't real uint32s: couldn't allocate temp array.\n");
991 return 1;
992 }
993 if (fread(u, sizeof(uint32_t), n, fin) == n) {
994 for (i=0; i<n; i++) {
995 val[i] = ntohl(u[i]);
996 }
997 free(u);
998 return 0;
999 } else {
1000 read_complain(fin, "uint32s");
1001 free(u);
1002 return 1;
1003 }
1004 }
1005
read_fixed_length_string(FILE * fin,char * s,int length)1006 int read_fixed_length_string(FILE* fin, char* s, int length) {
1007 if (fread(s, 1, length, fin) != length) {
1008 read_complain(fin, "fixed-length string");
1009 return 1;
1010 }
1011 return 0;
1012 }
1013
read_string(FILE * fin)1014 char* read_string(FILE* fin) {
1015 return read_string_terminated(fin, "\0", 1, FALSE);
1016 }
1017
growable_buffer_add(char * buf,int index,char c,int * size,int * sizestep,int * maxstep)1018 static char* growable_buffer_add(char* buf, int index, char c, int* size, int* sizestep, int* maxstep) {
1019 if (index == *size) {
1020 // expand
1021 *size += *sizestep;
1022 buf = realloc(buf, *size);
1023 if (!buf) {
1024 fprintf(stderr, "Couldn't allocate buffer: %i.\n", *size);
1025 return NULL;
1026 }
1027 if (*sizestep < *maxstep)
1028 *sizestep *= 2;
1029 }
1030 buf[index] = c;
1031 return buf;
1032 }
1033
read_string_terminated(FILE * fin,const char * terminators,int nterminators,anbool include_terminator)1034 char* read_string_terminated(FILE* fin, const char* terminators, int nterminators,
1035 anbool include_terminator) {
1036 int step = 1024;
1037 int maxstep = 1024*1024;
1038 int i = 0;
1039 int size = 0;
1040 char* rtn = NULL;
1041 for (;;) {
1042 int c = fgetc(fin);
1043 if (c == EOF)
1044 break;
1045 rtn = growable_buffer_add(rtn, i, c, &size, &step, &maxstep);
1046 if (!rtn)
1047 return NULL;
1048 i++;
1049 if (memchr(terminators, c, nterminators)) {
1050 if (!include_terminator)
1051 i--;
1052 break;
1053 }
1054 }
1055 if (ferror(fin)) {
1056 read_complain(fin, "string");
1057 free(rtn);
1058 return NULL;
1059 }
1060 // add \0 if it isn't already there;
1061 // return "\0" if nothing was read.
1062 if (i==0 || (rtn[i-1] != '\0')) {
1063 rtn = growable_buffer_add(rtn, i, '\0', &size, &step, &maxstep);
1064 if (!rtn)
1065 return NULL;
1066 i++;
1067 }
1068 if (i < size) {
1069 rtn = realloc(rtn, i);
1070 // shouldn't happen - we're shrinking.
1071 if (!rtn) {
1072 fprintf(stderr, "Couldn't realloc buffer: %i\n", i);
1073 }
1074 }
1075 return rtn;
1076 }
1077
write_string(FILE * fout,char * s)1078 int write_string(FILE* fout, char* s) {
1079 int len = strlen(s) + 1;
1080 if (fwrite(s, 1, len, fout) != len) {
1081 fprintf(stderr, "Couldn't write string: %s\n", strerror(errno));
1082 return 1;
1083 }
1084 return 0;
1085 }
1086
write_fixed_length_string(FILE * fout,char * s,int length)1087 int write_fixed_length_string(FILE* fout, char* s, int length) {
1088 char* str;
1089 int res;
1090 str = calloc(length, 1);
1091 if (!str) {
1092 fprintf(stderr, "Couldn't allocate a temp buffer of size %i.\n", length);
1093 return 1;
1094 }
1095 sprintf(str, "%.*s", length, s);
1096 res = fwrite(str, 1, length, fout);
1097 free(str);
1098 if (res != length) {
1099 fprintf(stderr, "Couldn't write fixed-length string: %s\n", strerror(errno));
1100 return 1;
1101 }
1102 return 0;
1103 }
1104
write_double(FILE * fout,double val)1105 int write_double(FILE* fout, double val) {
1106 if (fwrite(&val, sizeof(double), 1, fout) == 1) {
1107 return 0;
1108 } else {
1109 fprintf(stderr, "Couldn't write double: %s\n", strerror(errno));
1110 return 1;
1111 }
1112 }
1113
write_float(FILE * fout,float val)1114 int write_float(FILE* fout, float val) {
1115 if (fwrite(&val, sizeof(float), 1, fout) == 1) {
1116 return 0;
1117 } else {
1118 fprintf(stderr, "Couldn't write float: %s\n", strerror(errno));
1119 return 1;
1120 }
1121 }
1122
write_u8(FILE * fout,unsigned char val)1123 int write_u8(FILE* fout, unsigned char val) {
1124 if (fwrite(&val, 1, 1, fout) == 1) {
1125 return 0;
1126 } else {
1127 fprintf(stderr, "Couldn't write u8: %s\n", strerror(errno));
1128 return 1;
1129 }
1130 }
1131
write_u32_portable(FILE * fout,unsigned int val)1132 int write_u32_portable(FILE* fout, unsigned int val) {
1133 uint32_t v = htonl((uint32_t)val);
1134 if (fwrite(&v, 4, 1, fout) == 1) {
1135 return 0;
1136 } else {
1137 fprintf(stderr, "Couldn't write u32: %s\n", strerror(errno));
1138 return 1;
1139 }
1140 }
1141
write_u32s_portable(FILE * fout,unsigned int * val,int n)1142 int write_u32s_portable(FILE* fout, unsigned int* val, int n) {
1143 int i;
1144 uint32_t* v = malloc(sizeof(uint32_t) * n);
1145 if (!v) {
1146 fprintf(stderr, "Couldn't write u32s: couldn't allocate temp array.\n");
1147 return 1;
1148 }
1149 for (i=0; i<n; i++) {
1150 v[i] = htonl((uint32_t)val[i]);
1151 }
1152 if (fwrite(v, sizeof(uint32_t), n, fout) == n) {
1153 free(v);
1154 return 0;
1155 } else {
1156 fprintf(stderr, "Couldn't write u32s: %s\n", strerror(errno));
1157 free(v);
1158 return 1;
1159 }
1160 }
1161
write_u32(FILE * fout,unsigned int val)1162 int write_u32(FILE* fout, unsigned int val) {
1163 uint32_t v = (uint32_t)val;
1164 if (fwrite(&v, 4, 1, fout) == 1) {
1165 return 0;
1166 } else {
1167 fprintf(stderr, "Couldn't write u32: %s\n", strerror(errno));
1168 return 1;
1169 }
1170 }
1171
write_u16(FILE * fout,unsigned int val)1172 int write_u16(FILE* fout, unsigned int val) {
1173 uint16_t v = (uint16_t)val;
1174 if (fwrite(&v, 2, 1, fout) == 1) {
1175 return 0;
1176 } else {
1177 fprintf(stderr, "Couldn't write u32: %s\n", strerror(errno));
1178 return 1;
1179 }
1180 }
1181
write_uints(FILE * fout,unsigned int * val,int n)1182 int write_uints(FILE* fout, unsigned int* val, int n) {
1183 if (fwrite(val, sizeof(unsigned int), n, fout) == n) {
1184 return 0;
1185 } else {
1186 fprintf(stderr, "Couldn't write uints: %s\n", strerror(errno));
1187 return 1;
1188 }
1189 }
1190
buffered_read_new(int elementsize,int Nbuffer,int Ntotal,int (* refill_buffer)(void * userdata,void * buffer,unsigned int offs,unsigned int nelems),void * userdata)1191 bread_t* buffered_read_new(int elementsize, int Nbuffer, int Ntotal,
1192 int (*refill_buffer)(void* userdata, void* buffer, unsigned int offs, unsigned int nelems),
1193 void* userdata) {
1194 bread_t* br;
1195 br = calloc(1, sizeof(bread_t));
1196 br->blocksize = Nbuffer;
1197 br->elementsize = elementsize;
1198 br->ntotal = Ntotal;
1199 br->refill_buffer = refill_buffer;
1200 br->userdata = userdata;
1201 return br;
1202 }
1203
buffered_read(bread_t * br)1204 void* buffered_read(bread_t* br) {
1205 void* rtn;
1206 if (!br->buffer) {
1207 br->buffer = malloc(br->blocksize * br->elementsize);
1208 br->nbuff = br->off = br->buffind = 0;
1209 }
1210 if (br->buffind == br->nbuff) {
1211 // read a new block!
1212 int n = br->blocksize;
1213 // the new block to read starts after the current block...
1214 br->off += br->nbuff;
1215 if (n + br->off > br->ntotal)
1216 n = br->ntotal - br->off;
1217 if (!n)
1218 return NULL;
1219 memset(br->buffer, 0, br->blocksize * br->elementsize);
1220 if (br->refill_buffer(br->userdata, br->buffer, br->off, n)) {
1221 fprintf(stderr, "buffered_read: Error filling buffer.\n");
1222 return NULL;
1223 }
1224 br->nbuff = n;
1225 br->buffind = 0;
1226 }
1227 rtn = (char*)br->buffer + (br->buffind * br->elementsize);
1228 br->buffind++;
1229 return rtn;
1230 }
1231
buffered_read_resize(bread_t * br,int newsize)1232 void buffered_read_resize(bread_t* br, int newsize) {
1233 br->blocksize = newsize;
1234 if (br->buffer)
1235 br->buffer = realloc(br->buffer, br->blocksize * br->elementsize);
1236 }
1237
buffered_read_reset(bread_t * br)1238 void buffered_read_reset(bread_t* br) {
1239 br->nbuff = br->off = br->buffind = 0;
1240 }
1241
buffered_read_pushback(bread_t * br)1242 void buffered_read_pushback(bread_t* br) {
1243 if (!br->buffind) {
1244 fprintf(stderr, "buffered_read_pushback: Can't push back any further!\n");
1245 return;
1246 }
1247 br->buffind--;
1248 }
1249
buffered_read_free(bread_t * br)1250 void buffered_read_free(bread_t* br) {
1251 free(br->buffer);
1252 }
1253