source: trunk/src/astrometry/util/ioutils.c @ 10420

Revision 10420, 25.1 KB checked in by gmaps, 19 months ago (diff)

tidy gmaps python/tilerender interface

Line 
1/*
2  This file is part of the Astrometry.net suite.
3  Copyright 2006, 2007 Dustin Lang, Keir Mierle and Sam Roweis.
4
5  The Astrometry.net suite is free software; you can redistribute
6  it and/or modify it under the terms of the GNU General Public License
7  as published by the Free Software Foundation, version 2.
8
9  The Astrometry.net suite is distributed in the hope that it will be
10  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  General Public License for more details.
13
14  You should have received a copy of the GNU General Public License
15  along with the Astrometry.net suite ; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17*/
18
19#include <stdio.h>
20#include <errno.h>
21#include <string.h>
22#include <stdint.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26#include <sys/time.h>
27#include <sys/resource.h>
28#include <arpa/inet.h>
29#include <netinet/in.h>
30#include <stdlib.h>
31#include <signal.h>
32#include <assert.h>
33#include <unistd.h>
34#include <stdarg.h>
35#include <libgen.h>
36#include <dirent.h>
37#include <time.h>
38
39#include "ioutils.h"
40#include "gnu-specific.h"
41#include "errors.h"
42#include "log.h"
43
44uint32_t ENDIAN_DETECTOR = 0x01020304;
45
46Malloc
47char* basename_safe(const char* path) {
48        char* copy = strdup(path);
49        char* res = strdup(basename(copy));
50        free(copy);
51        return res;
52}
53
54char* find_file_in_dirs(const char** dirs, int ndirs, const char* filename, bool allow_absolute) {
55    int i;
56    if (!filename) return NULL;
57    if (allow_absolute && filename[0] == '/') {
58        if (file_readable(filename))
59            return strdup(filename);
60    }
61    for (i=0; i<ndirs; i++) {
62        char* fn;
63        asprintf_safe(&fn, "%s/%s", dirs[i], filename);
64        if (file_readable(fn))
65            return fn;
66        free(fn);
67    }
68    return NULL;
69}
70
71float get_cpu_usage() {
72        struct rusage r;
73        float sofar;
74        if (getrusage(RUSAGE_SELF, &r)) {
75                SYSERROR("Failed to get resource usage");
76                return -1.0;
77        }
78        sofar = (float)(r.ru_utime.tv_sec + r.ru_stime.tv_sec) +
79        (1e-6 * (r.ru_utime.tv_usec + r.ru_stime.tv_usec));
80        return sofar;
81}
82
83char* an_canonicalize_file_name(const char* fn) {
84    sl* dirs;
85    int i;
86    char* result;
87    // Ugh, special cases.
88    if (streq(fn, ".") || streq(fn, "/"))
89        return strdup(fn);
90
91    dirs = sl_split(NULL, fn, "/");
92    for (i=0; i<sl_size(dirs); i++) {
93        if (streq(sl_get(dirs, i), "")) {
94            // don't remove '/' from beginning of path!
95            if (i) {
96                sl_remove(dirs, i);
97                i--;
98            }
99        } else if (streq(sl_get(dirs, i), ".")) {
100            sl_remove(dirs, i);
101            i--;
102        } else if (streq(sl_get(dirs, i), "..")) {
103            // don't remove ".." at start of path.
104            if (!i)
105                continue;
106            // don't remove chains of '../../../' at the start.
107            if (streq(sl_get(dirs, i-1), ".."))
108                continue;
109            // but do collapse '/../' to '/' at the start.
110            if (streq(sl_get(dirs, i-1), "")) {
111                sl_remove(dirs, i);
112                i--;
113            } else {
114                sl_remove(dirs, i-1);
115                sl_remove(dirs, i-1);
116                i -= 2;
117            }
118        }
119    }
120    result = sl_join(dirs, "/");
121    sl_free2(dirs);
122    return result;
123}
124
125bool streq(const char* s1, const char* s2) {
126    if (s1 == NULL || s2 == NULL)
127        return (s1 == s2);
128    return !strcmp(s1, s2);
129}
130
131int pipe_file_offset(FILE* fin, int offset, int length, FILE* fout) {
132    char buf[1024];
133    int i;
134    if (fseeko(fin, offset, SEEK_SET)) {
135        SYSERROR("Failed to seek to offset %i", offset);
136        return -1;
137    }
138    for (i=0; i<length; i+=sizeof(buf)) {
139        int n = sizeof(buf);
140        if (i + n > length) {
141            n = length - i;
142        }
143        if (fread(buf, 1, n, fin) != n) {
144            SYSERROR("Failed to read %i bytes", n);
145            return -1;
146        }
147        if (fwrite(buf, 1, n, fout) != n) {
148            SYSERROR("Failed to write %i bytes", n);
149            return -1;
150        }
151    }
152    return 0;
153}
154
155void asprintf_safe(char** strp, const char* format, ...) {
156        va_list lst;
157        int rtn;
158        va_start(lst, format);
159    rtn = vasprintf(strp, format, lst);
160    if (rtn == -1) {
161        fprintf(stderr, "Error, vasprintf() failed: %s\n", strerror(errno));
162        fprintf(stderr, "  (format: \"%s\")\n", format);
163        assert(0);
164        *strp = NULL;
165    }
166        va_end(lst);
167}
168
169sl* dir_get_contents(const char* path, sl* list, bool filesonly, bool recurse) {
170    DIR* dir = opendir(path);
171    if (!dir) {
172        fprintf(stderr, "Failed to open directory \"%s\": %s\n", path, strerror(errno));
173        return NULL;
174    }
175    if (!list)
176        list = sl_new(256);
177    while (1) {
178        struct dirent* de;
179        struct stat st;
180        char* name;
181        char* fullpath;
182        bool freeit = FALSE;
183        errno = 0;
184        de = readdir(dir);
185        if (!de) {
186            if (errno)
187                fprintf(stderr, "Failed to read entry from directory \"%s\": %s\n", path, strerror(errno));
188            break;
189        }
190        name = de->d_name;
191        if (!strcmp(name, ".") || !strcmp(name, ".."))
192            continue;
193        asprintf_safe(&fullpath, "%s/%s", path, name);
194        if (stat(fullpath, &st)) {
195            fprintf(stderr, "Failed to stat file %s: %s\n", fullpath, strerror(errno));
196            closedir(dir);
197            sl_free2(list);
198            return NULL;
199        }
200
201        if (filesonly) {
202            if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
203                sl_append_nocopy(list, fullpath);
204            else
205                freeit = TRUE;
206        } else {
207            sl_append_nocopy(list, fullpath);
208        }
209        if (recurse && S_ISDIR(st.st_mode)) {
210            dir_get_contents(path, list, filesonly, recurse);
211        }
212        if (freeit)
213            free(fullpath);
214    }
215    closedir(dir);
216    return list;
217}
218
219char* resolve_path(const char* filename, const char* basedir) {
220    // we don't use canonicalize_file_name() because it requires the paths
221    // to actually exist, while this function should work for output files
222    // that don't already exist.
223    char* path;
224    char* rtn;
225    // absolute path?
226    if (filename[0] == '/')
227        //return strdup(filename);
228        return an_canonicalize_file_name(filename);
229    asprintf_safe(&path, "%s/%s", basedir, filename);
230    //return path;
231    rtn = an_canonicalize_file_name(path);
232    free(path);
233    return rtn;
234}
235
236char* find_executable(const char* progname, const char* sibling) {
237    char* sib;
238    char* sibdir;
239    char* path;
240    char* pathenv;
241
242    // If it's an absolute path, just return it.
243    if (progname[0] == '/')
244        return strdup(progname);
245
246    // If it's a relative path, resolve it.
247    if (strchr(progname, '/')) {
248        path = canonicalize_file_name(progname);
249        if (path && file_executable(path))
250            return path;
251        free(path);
252    }
253
254    // If "sibling" contains a "/", then check relative to it.
255    if (sibling && strchr(sibling, '/')) {
256        // dirname() overwrites its arguments, so make a copy...
257        sib = strdup(sibling);
258        sibdir = strdup(dirname(sib));
259        free(sib);
260
261        asprintf_safe(&path, "%s/%s", sibdir, progname);
262        free(sibdir);
263
264        if (file_executable(path))
265            return path;
266
267        free(path);
268    }
269
270    // Search PATH.
271    pathenv = getenv("PATH");
272    while (1) {
273        char* colon;
274        int len;
275        if (!strlen(pathenv))
276            break;
277        colon = strchr(pathenv, ':');
278        if (colon)
279            len = colon - pathenv;
280        else
281            len = strlen(pathenv);
282        if (pathenv[len - 1] == '/')
283            len--;
284        asprintf_safe(&path, "%.*s/%s", len, pathenv, progname);
285        if (file_executable(path))
286            return path;
287        free(path);
288        if (colon)
289            pathenv = colon + 1;
290        else
291            break;
292    }
293
294    // Not found.
295    return NULL;
296}
297
298int run_command_get_outputs(const char* cmd, sl** outlines, sl** errlines) {
299        int outpipe[2];
300        int errpipe[2];
301        pid_t pid;
302
303        if (outlines) {
304                if (pipe(outpipe) == -1) {
305            SYSERROR("Failed to create stdout pipe");
306                        return -1;
307                }
308        }
309        if (errlines) {
310                if (pipe(errpipe) == -1) {
311            SYSERROR("Failed to create stderr pipe");
312                        return -1;
313                }
314        }
315
316        pid = fork();
317        if (pid == -1) {
318        SYSERROR("Failed to fork");
319                return -1;
320        } else if (pid == 0) {
321                // Child process.
322                if (outlines) {
323                        close(outpipe[0]);
324                        // bind stdout to the pipe.
325                        if (dup2(outpipe[1], STDOUT_FILENO) == -1) {
326                SYSERROR("Failed to dup2 stdout");
327                                _exit( -1);
328                        }
329                }
330                if (errlines) {
331                        close(errpipe[0]);
332                        // bind stderr to the pipe.
333                        if (dup2(errpipe[1], STDERR_FILENO) == -1) {
334                SYSERROR("Failed to dup2 stderr");
335                                _exit( -1);
336                        }
337                }
338                // Use a "system"-like command to allow fancier commands.
339                if (execlp("/bin/sh", "/bin/sh", "-c", cmd, (char*)NULL)) {
340            SYSERROR("Failed to execlp");
341                        _exit( -1);
342                }
343                // execlp doesn't return.
344        } else {
345                FILE *fout = NULL, *ferr = NULL;
346                int status;
347                // Parent process.
348                if (outlines) {
349                        close(outpipe[1]);
350                        fout = fdopen(outpipe[0], "r");
351                        if (!fout) {
352                SYSERROR("Failed to open stdout pipe");
353                                return -1;
354                        }
355                }
356                if (errlines) {
357                        close(errpipe[1]);
358                        ferr = fdopen(errpipe[0], "r");
359                        if (!ferr) {
360                SYSERROR("Failed to open stderr pipe");
361                                return -1;
362                        }
363                }
364                // Wait for command to finish.
365                // FIXME - do we need to read from the pipes to prevent the command
366                // from blocking?
367                //printf("Waiting for command to finish (PID %i).\n", (int)pid);
368                do {
369                        if (waitpid(pid, &status, 0) == -1) {
370                SYSERROR("Failed to waitpid() for command to finish");
371                                return -1;
372                        }
373                        if (WIFSIGNALED(status)) {
374                ERROR("Command was killed by signal %i", WTERMSIG(status));
375                                return -1;
376                        } else {
377                                int exitval = WEXITSTATUS(status);
378                                if (exitval == 127) {
379                    ERROR("Command not found: %s", cmd);
380                                        return exitval;
381                                } else if (exitval) {
382                    ERROR("Command failed: return value %i", exitval);
383                                        return exitval;
384                                }
385                        }
386                } while (!WIFEXITED(status) && !WIFSIGNALED(status));
387
388                if (outlines) {
389                        *outlines = fid_get_lines(fout, FALSE);
390                        fclose(fout);
391                }
392                if (errlines) {
393                        *errlines = fid_get_lines(ferr, FALSE);
394                        fclose(ferr);
395                }
396        }
397        return 0;
398}
399
400int mkdir_p(const char* dirpath) {
401    sl* tomake = sl_new(4);
402    char* path = strdup(dirpath);
403    while (!file_exists(path)) {
404        char* dir;
405        sl_push(tomake, path);
406        dir = strdup(dirname(path));
407        free(path);
408        path = dir;
409    }
410    free(path);
411    while (sl_size(tomake)) {
412        char* path = sl_pop(tomake);
413        if (mkdir(path, 0777)) {
414            SYSERROR("Failed to mkdir(%s)", path);
415            sl_free2(tomake);
416            free(path);
417            return -1;
418        }
419        free(path);
420    }
421    sl_free2(tomake);
422    return 0;
423}
424
425char* shell_escape(const char* str) {
426    char* escape = "|&;()<> \t\n\\'\"";
427    int nescape = 0;
428    int len = strlen(str);
429    int i;
430    char* result;
431    int j;
432
433    for (i=0; i<len; i++) {
434        char* cp = strchr(escape, str[i]);
435        if (!cp) continue;
436        nescape++;
437    }
438    result = malloc(len + nescape + 1);
439    for (i=0, j=0; i<len; i++, j++) {
440        char* cp = strchr(escape, str[i]);
441        if (!cp) {
442            result[j] = str[i];
443        } else {
444            result[j] = '\\';
445            j++;
446            result[j] = str[i];
447        }
448    }
449    assert(j == (len + nescape));
450    result[j] = '\0';
451    return result;
452}
453
454char* create_temp_file(const char* fn, const char* dir) {
455    char* tempfile;
456    int fid;
457    asprintf_safe(&tempfile, "%s/tmp.%s.XXXXXX", dir, fn);
458    fid = mkstemp(tempfile);
459    if (fid == -1) {
460        fprintf(stderr, "Failed to create temp file: %s\n", strerror(errno));
461        exit(-1);
462    }
463    close(fid);
464    //printf("Created temp file %s\n", tempfile);
465    return tempfile;
466}
467
468char* create_temp_dir(const char* name, const char* dir) {
469    char* tempdir;
470    asprintf_safe(&tempdir, "%s/tmp.%s.XXXXXX", dir, name);
471    if (!mkdtemp(tempdir)) {
472        SYSERROR("Failed to create temp dir");
473        return NULL;
474    }
475    return tempdir;
476}
477
478sl* file_get_lines(const char* fn, bool include_newlines) {
479    FILE* fid;
480        sl* list;
481    fid = fopen(fn, "r");
482    if (!fid) {
483        SYSERROR("Failed to open file %s", fn);
484        return NULL;
485    }
486        list = fid_get_lines(fid, include_newlines);
487        fclose(fid);
488        return list;
489}
490
491sl* fid_get_lines(FILE* fid, bool include_newlines) {
492        sl* list;
493        list = sl_new(256);
494        while (1) {
495                char* line = read_string_terminated(fid, "\n\r\0", 3, include_newlines);
496                if (!line) {
497                        // error.
498            SYSERROR("Failed to read a line");
499                        sl_free2(list);
500                        return NULL;
501                }
502        if (feof(fid) && line[0] == '\0') {
503            free(line);
504            break;
505        }
506        sl_append_nocopy(list, line);
507                if (feof(fid))
508                        break;
509        }
510        return list;
511}
512
513char* file_get_contents_offset(const char* fn, int offset, int size) {
514    char* buf;
515    FILE* fid;
516    fid = fopen(fn, "rb");
517    if (!fid) {
518        fprintf(stderr, "file_get_contents_offset: failed to open file \"%s\": %s\n", fn, strerror(errno));
519        return NULL;
520    }
521    buf = malloc(size);
522    if (!buf) {
523        fprintf(stderr, "file_get_contents_offset: couldn't malloc %lu bytes.\n", (long)size);
524        return NULL;
525    }
526        if (offset) {
527                if (fseeko(fid, offset, SEEK_SET)) {
528                        fprintf(stderr, "file_get_contents_offset: failed to fseeko: %s.\n", strerror(errno));
529                        return NULL;
530                }
531        }
532        if (fread(buf, 1, size, fid) != size) {
533        fprintf(stderr, "file_get_contents_offset: failed to read %lu bytes: %s\n", (long)size, strerror(errno));
534        free(buf);
535        return NULL;
536    }
537        fclose(fid);
538    return buf;
539}
540
541void* file_get_contents(const char* fn, size_t* len, bool addzero) {
542    struct stat st;
543    char* buf;
544    FILE* fid;
545    off_t size;
546    if (stat(fn, &st)) {
547        fprintf(stderr, "file_get_contents: failed to stat file \"%s\"", fn);
548        return NULL;
549    }
550    size = st.st_size;
551    fid = fopen(fn, "rb");
552    if (!fid) {
553        fprintf(stderr, "file_get_contents: failed to open file \"%s\": %s\n", fn, strerror(errno));
554        return NULL;
555    }
556    buf = malloc(size + (addzero ? 1 : 0));
557    if (!buf) {
558        fprintf(stderr, "file_get_contents: couldn't malloc %lu bytes.\n", (long)size);
559        return NULL;
560    }
561    if (fread(buf, 1, size, fid) != size) {
562        fprintf(stderr, "file_get_contents: failed to read %lu bytes: %s\n", (long)size, strerror(errno));
563        free(buf);
564        return NULL;
565    }
566        fclose(fid);
567    if (addzero)
568        buf[size] = '\0';
569    if (len)
570        *len = size;
571    return buf;
572}
573
574void get_mmap_size(int start, int size, off_t* mapstart, size_t* mapsize, int* pgap) {
575        int ps = getpagesize();
576        int gap = start % ps;
577        // start must be a multiple of pagesize.
578        *mapstart = start - gap;
579        *mapsize  = size  + gap;
580        *pgap = gap;
581}
582
583int file_get_last_modified_string(const char* fn, const char* timeformat,
584                                  bool utc, char* output, size_t outsize) {
585    struct stat st;
586    time_t t;
587    struct tm tym;
588    if (stat(fn, &st)) {
589        SYSERROR("Failed to stat() file \"%s\"", fn);
590        return -1;
591    }
592    t = st.st_mtime;
593    if (utc) {
594        if (!gmtime_r(&t, &tym)) {
595            SYSERROR("gmtime_r() failed");
596            return -1;
597        }
598    } else {
599        if (!localtime_r(&t, &tym)) {
600            SYSERROR("localtime_r() failed");
601            return -1;
602        }
603    }
604    strftime(output, outsize, timeformat, &tym);
605    return 0;
606}
607
608bool file_exists(const char* fn) {
609    return (access(fn, F_OK) == 0);
610}
611
612bool file_readable(const char* fn) {
613    return (access(fn, R_OK) == 0);
614}
615
616bool file_executable(const char* fn) {
617    return (access(fn, X_OK) == 0);
618}
619
620bool path_is_dir(const char* path) {
621    struct stat st;
622    if (stat(path, &st)) {
623        SYSERROR("Couldn't stat path %s", path);
624        return FALSE;
625    }
626    //return st.st_mode & S_IFDIR;
627    return S_ISDIR(st.st_mode);
628}
629
630int starts_with(const char* str, const char* prefix) {
631        int len = strlen(prefix);
632        if (strncmp(str, prefix, len))
633                return 0;
634        return 1;
635}
636
637int ends_with(const char* str, const char* suffix) {
638        int len = strlen(suffix);
639    int len2 = strlen(str);
640    if (len > len2)
641        return 0;
642        if (strncmp(str + len2 - len, suffix, len))
643                return 0;
644        return 1;
645}
646
647char* strdup_safe(const char* str) {
648        char* rtn;
649        if (!str) return NULL;
650        rtn = strdup(str);
651        if (!rtn) {
652                fprintf(stderr, "Failed to strdup: %s\n", strerror(errno));
653                assert(0);
654        }
655        return rtn;
656}
657
658static int oldsigbus_valid = 0;
659static struct sigaction oldsigbus;
660static void sigbus_handler(int sig) {
661        fprintf(stderr, "\n\n"
662                        "SIGBUS (Bus error) signal received.\n"
663                        "One reason this can happen is that an I/O error is encountered\n"
664                        "on a file that we are reading with \"mmap\".\n\n"
665                        "Bailing out now.\n\n");
666        fflush(stderr);
667        exit(-1);
668}
669
670void add_sigbus_mmap_warning() {
671        struct sigaction sigbus;
672        memset(&sigbus, 0, sizeof(struct sigaction));
673        sigbus.sa_handler = sigbus_handler;
674        if (sigaction(SIGBUS, &sigbus, &oldsigbus)) {
675                fprintf(stderr, "Failed to change SIGBUS handler: %s\n", strerror(errno));
676                return;
677        }
678        oldsigbus_valid = 1;
679}
680
681void reset_sigbus_mmap_warning() {
682        if (oldsigbus_valid) {
683                if (sigaction(SIGBUS, &oldsigbus, NULL)) {
684                        fprintf(stderr, "Failed to restore SIGBUS handler: %s\n", strerror(errno));
685                        return;
686                }
687        }
688}
689
690int is_word(const char* cmdline, const char* keyword, char** cptr) {
691        int len = strlen(keyword);
692        if (strncmp(cmdline, keyword, len))
693                return 0;
694        *cptr = (char*)(cmdline + len);
695        return 1;
696}
697
698void read_complain(FILE* fin, const char* attempted) {
699        if (feof(fin)) {
700                fprintf(stderr, "Couldn't read %s: end-of-file.\n", attempted);
701        } else if (ferror(fin)) {
702                fprintf(stderr, "Couldn't read %s: error: %s\n", attempted, strerror(errno));
703        } else {
704                fprintf(stderr, "Couldn't read %s: %s\n", attempted, strerror(errno));
705        }
706}
707
708int read_u8(FILE* fin, unsigned char* val) {
709    if (fread(val, 1, 1, fin) == 1) {
710                return 0;
711    } else {
712                read_complain(fin, "u8");
713                return 1;
714    }
715}
716
717int read_u16(FILE* fin, unsigned int* val) {
718        uint16_t v;
719    if (fread(&v, 2, 1, fin) == 1) {
720                *val = v;
721                return 0;
722    } else {
723                read_complain(fin, "u8");
724                return 1;
725    }
726}
727
728int read_u32_portable(FILE* fin, unsigned int* val) {
729    uint32_t u;
730    if (fread(&u, 4, 1, fin) == 1) {
731                *val = ntohl(u);
732                return 0;
733    } else {
734                read_complain(fin, "u32");
735                return 1;
736    }
737}
738
739int read_double(FILE* fin, double* val) {
740    if (fread(val, sizeof(double), 1, fin) == 1) {
741                return 0;
742    } else {
743                read_complain(fin, "double");
744                return 1;
745    }
746}
747
748int read_u32(FILE* fin, unsigned int* val) {
749    uint32_t u;
750    if (fread(&u, 4, 1, fin) == 1) {
751                *val = (unsigned int)u;
752                return 0;
753    } else {
754                read_complain(fin, "u32 native");
755                return 1;
756    }
757}
758
759int read_u32s_portable(FILE* fin, unsigned int* val, int n) {
760    int i;
761    uint32_t* u = malloc(sizeof(uint32_t) * n);
762    if (!u) {
763                fprintf(stderr, "Couldn't real uint32s: couldn't allocate temp array.\n");
764                return 1;
765    }
766    if (fread(u, sizeof(uint32_t), n, fin) == n) {
767                for (i=0; i<n; i++) {
768                        val[i] = ntohl(u[i]);
769                }
770                free(u);
771                return 0;
772    } else {
773                read_complain(fin, "uint32s");
774                free(u);
775                return 1;
776    }
777}
778
779int read_fixed_length_string(FILE* fin, char* s, int length) {
780        if (fread(s, 1, length, fin) != length) {
781                read_complain(fin, "fixed-length string");
782                return 1;
783        }
784        return 0;
785}
786
787char* read_string(FILE* fin) {
788        return read_string_terminated(fin, "\0", 1, FALSE);
789}
790
791static char* growable_buffer_add(char* buf, int index, char c, int* size, int* sizestep, int* maxstep) {
792        if (index == *size) {
793                // expand
794                *size += *sizestep;
795                buf = realloc(buf, *size);
796                if (!buf) {
797                        fprintf(stderr, "Couldn't allocate buffer: %i.\n", *size);
798                        return NULL;
799                }
800                if (*sizestep < *maxstep)
801                        *sizestep *= 2;
802        }
803        buf[index] = c;
804        return buf;
805}
806
807char* read_string_terminated(FILE* fin, const char* terminators, int nterminators,
808                                                         bool include_terminator) {
809        int step = 1024;
810        int maxstep = 1024*1024;
811        int i = 0;
812        int size = 0;
813        char* rtn = NULL;
814        for (;;) {
815                int c = fgetc(fin);
816                if (c == EOF)
817                        break;
818                rtn = growable_buffer_add(rtn, i, c, &size, &step, &maxstep);
819                if (!rtn)
820                        return NULL;
821                i++;
822                if (memchr(terminators, c, nterminators)) {
823                        if (!include_terminator)
824                                i--;
825                        break;
826                }
827        }
828        if (ferror(fin)) {
829                read_complain(fin, "string");
830                free(rtn);
831                return NULL;
832        }
833        // add \0 if it isn't already there;
834        // return "\0" if nothing was read.
835        if (i==0 || (rtn[i-1] != '\0')) {
836                rtn = growable_buffer_add(rtn, i, '\0', &size, &step, &maxstep);
837                if (!rtn)
838                        return NULL;
839                i++;
840        }
841        if (i < size) {
842                rtn = realloc(rtn, i);
843                // shouldn't happen - we're shrinking.
844                if (!rtn) {
845                        fprintf(stderr, "Couldn't realloc buffer: %i\n", i);
846                }
847        }
848        return rtn;
849}
850
851int write_string(FILE* fout, char* s) {
852        int len = strlen(s) + 1;
853        if (fwrite(s, 1, len, fout) != len) {
854                fprintf(stderr, "Couldn't write string: %s\n", strerror(errno));
855                return 1;
856        }
857        return 0;
858}
859
860int write_fixed_length_string(FILE* fout, char* s, int length) {
861        char* str;
862        int res;
863        str = calloc(length, 1);
864        if (!str) {
865                fprintf(stderr, "Couldn't allocate a temp buffer of size %i.\n", length);
866                return 1;
867        }
868        sprintf(str, "%.*s", length, s);
869        res = fwrite(str, 1, length, fout);
870        free(str);
871        if (res != length) {
872                fprintf(stderr, "Couldn't write fixed-length string: %s\n", strerror(errno));
873                return 1;
874        }
875        return 0;
876}
877
878int write_double(FILE* fout, double val) {
879    if (fwrite(&val, sizeof(double), 1, fout) == 1) {
880                return 0;
881    } else {
882                fprintf(stderr, "Couldn't write double: %s\n", strerror(errno));
883                return 1;
884    }
885}
886
887int write_float(FILE* fout, float val) {
888    if (fwrite(&val, sizeof(float), 1, fout) == 1) {
889                return 0;
890    } else {
891                fprintf(stderr, "Couldn't write float: %s\n", strerror(errno));
892                return 1;
893    }
894}
895
896int write_u8(FILE* fout, unsigned char val) {
897    if (fwrite(&val, 1, 1, fout) == 1) {
898                return 0;
899    } else {
900                fprintf(stderr, "Couldn't write u8: %s\n", strerror(errno));
901                return 1;
902    }
903}
904
905int write_u32_portable(FILE* fout, unsigned int val) {
906    uint32_t v = htonl((uint32_t)val);
907    if (fwrite(&v, 4, 1, fout) == 1) {
908                return 0;
909    } else {
910                fprintf(stderr, "Couldn't write u32: %s\n", strerror(errno));
911                return 1;
912    }
913}
914
915int write_u32s_portable(FILE* fout, unsigned int* val, int n) {
916    int i;
917    uint32_t* v = malloc(sizeof(uint32_t) * n);
918    if (!v) {
919                fprintf(stderr, "Couldn't write u32s: couldn't allocate temp array.\n");
920                return 1;
921    }
922    for (i=0; i<n; i++) {
923                v[i] = htonl((uint32_t)val[i]);
924    }
925    if (fwrite(v, sizeof(uint32_t), n, fout) == n) {
926                free(v);
927                return 0;
928    } else {
929                fprintf(stderr, "Couldn't write u32s: %s\n", strerror(errno));
930                free(v);
931                return 1;
932    }
933}
934
935int write_u32(FILE* fout, unsigned int val) {
936    uint32_t v = (uint32_t)val;
937    if (fwrite(&v, 4, 1, fout) == 1) {
938                return 0;
939    } else {
940                fprintf(stderr, "Couldn't write u32: %s\n", strerror(errno));
941                return 1;
942    }
943}
944
945int write_u16(FILE* fout, unsigned int val) {
946    uint16_t v = (uint16_t)val;
947    if (fwrite(&v, 2, 1, fout) == 1) {
948                return 0;
949    } else {
950                fprintf(stderr, "Couldn't write u32: %s\n", strerror(errno));
951                return 1;
952    }
953}
954
955int write_uints(FILE* fout, unsigned int* val, int n) {
956    if (fwrite(val, sizeof(unsigned int), n, fout) == n) {
957                return 0;
958    } else {
959                fprintf(stderr, "Couldn't write uints: %s\n", strerror(errno));
960                return 1;
961    }
962}
963
964bread_t* buffered_read_new(int elementsize, int Nbuffer, int Ntotal,
965                           int (*refill_buffer)(void* userdata, void* buffer, unsigned int offs, unsigned int nelems),
966                           void* userdata) {
967    bread_t* br;
968    br = calloc(1, sizeof(bread_t));
969    br->blocksize = Nbuffer;
970    br->elementsize = elementsize;
971    br->ntotal = Ntotal;
972    br->refill_buffer = refill_buffer;
973    br->userdata = userdata;
974    return br;
975}
976
977void* buffered_read(bread_t* br) {
978        void* rtn;
979        if (!br->buffer) {
980                br->buffer = malloc(br->blocksize * br->elementsize);
981                br->nbuff = br->off = br->buffind = 0;
982        }
983        if (br->buffind == br->nbuff) {
984                // read a new block!
985                int n = br->blocksize;
986                // the new block to read starts after the current block...
987                br->off += br->nbuff;
988                if (n + br->off > br->ntotal)
989                        n = br->ntotal - br->off;
990                if (!n)
991                        return NULL;
992                memset(br->buffer, 0, br->blocksize * br->elementsize);
993                if (br->refill_buffer(br->userdata, br->buffer, br->off, n)) {
994                        fprintf(stderr, "buffered_read: Error filling buffer.\n");
995                        return NULL;
996                }
997                br->nbuff = n;
998                br->buffind = 0;
999        }
1000        rtn = (char*)br->buffer + (br->buffind * br->elementsize);
1001        br->buffind++;
1002        return rtn;
1003}
1004
1005void buffered_read_resize(bread_t* br, int newsize) {
1006    br->blocksize = newsize;
1007    if (br->buffer)
1008        br->buffer = realloc(br->buffer, br->blocksize * br->elementsize);
1009}
1010
1011void buffered_read_reset(bread_t* br) {
1012        br->nbuff = br->off = br->buffind = 0;
1013}
1014
1015void buffered_read_pushback(bread_t* br) {
1016        if (!br->buffind) {
1017                fprintf(stderr, "buffered_read_pushback: Can't push back any further!\n");
1018                return;
1019        }
1020        br->buffind--;
1021}
1022
1023void buffered_read_free(bread_t* br) {
1024        free(br->buffer);
1025}
Note: See TracBrowser for help on using the repository browser.