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