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