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