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

Revision 12099, 25.7 KB checked in by dstn, 15 months ago (diff)

add pad-file

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