X-Git-Url: http://git.tdb.fi/?p=ext%2Fsubsurface.git;a=blobdiff_plain;f=file.c;h=da498995cc0c1522c802a86f4d34ae4522023702;hp=5db36a849d10acc39332f3b2465a1380dcec676b;hb=b693bb3fb962c54767f8430058575441df243b13;hpb=4d10bc017ad683ffa3e7e399b9bcb9dcf524d964 diff --git a/file.c b/file.c index 5db36a8..da49899 100644 --- a/file.c +++ b/file.c @@ -2,24 +2,27 @@ #include #include #include +#include #include #include "dive.h" +#include "file.h" -struct memblock { - void *buffer; - size_t size; -}; +/* Crazy windows sh*t */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif static int readfile(const char *filename, struct memblock *mem) { - int ret, fd = open(filename, O_RDONLY); + int ret, fd; struct stat st; + char *buf; mem->buffer = NULL; mem->size = 0; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) return fd; ret = fstat(fd, &st); @@ -31,15 +34,17 @@ static int readfile(const char *filename, struct memblock *mem) ret = 0; if (!st.st_size) goto out; - mem->buffer = malloc(st.st_size); + buf = malloc(st.st_size+1); ret = -1; errno = ENOMEM; - if (!mem->buffer) + if (!buf) goto out; + mem->buffer = buf; mem->size = st.st_size; - ret = read(fd, mem->buffer, mem->size); + ret = read(fd, buf, mem->size); if (ret < 0) goto free; + buf[ret] = 0; if (ret == mem->size) goto out; errno = EIO; @@ -53,6 +58,196 @@ out: return ret; } +#ifdef LIBZIP +#include + +static void suunto_read(struct zip_file *file, GError **error) +{ + int size = 1024, n, read = 0; + char *mem = malloc(size); + + while ((n = zip_fread(file, mem+read, size-read)) > 0) { + read += n; + size = read * 3 / 2; + mem = realloc(mem, size); + } + parse_xml_buffer("SDE file", mem, read, error); + free(mem); +} +#endif + +static int try_to_open_suunto(const char *filename, struct memblock *mem, GError **error) +{ + int success = 0; +#ifdef LIBZIP + /* Grr. libzip needs to re-open the file, it can't take a buffer */ + struct zip *zip = zip_open(filename, ZIP_CHECKCONS, NULL); + + if (zip) { + int index; + for (index = 0; ;index++) { + struct zip_file *file = zip_fopen_index(zip, index, 0); + if (!file) + break; + suunto_read(file, error); + zip_fclose(file); + success++; + } + zip_close(zip); + } +#endif + return success; +} + +static time_t parse_date(const char *date) +{ + int hour, min, sec; + struct tm tm; + char *p; + + memset(&tm, 0, sizeof(tm)); + tm.tm_mday = strtol(date, &p, 10); + if (tm.tm_mday < 1 || tm.tm_mday > 31) + return 0; + for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) { + if (!memcmp(p, monthname(tm.tm_mon), 3)) + break; + } + if (tm.tm_mon > 11) + return 0; + date = p+3; + tm.tm_year = strtol(date, &p, 10); + if (date == p) + return 0; + if (tm.tm_year < 70) + tm.tm_year += 2000; + if (tm.tm_year < 100) + tm.tm_year += 1900; + if (sscanf(p, "%d:%d:%d", &hour, &min, &sec) != 3) + return 0; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + return utc_mktime(&tm); +} + +enum csv_format { + CSV_DEPTH, CSV_TEMP, CSV_PRESSURE +}; + +static void add_sample_data(struct sample *sample, enum csv_format type, double val) +{ + switch (type) { + case CSV_DEPTH: + sample->depth.mm = feet_to_mm(val); + break; + case CSV_TEMP: + sample->temperature.mkelvin = F_to_mkelvin(val); + break; + case CSV_PRESSURE: + sample->cylinderpressure.mbar = psi_to_mbar(val*4); + break; + } +} + +/* + * Cochran comma-separated values: depth in feet, temperature in F, pressure in psi. + * + * They start with eight comma-separated fields like: + * + * filename: {C:\Analyst4\can\T036785.can},{C:\Analyst4\can\K031892.can} + * divenr: %d + * datetime: {03Sep11 16:37:22},{15Dec11 18:27:02} + * ??: 1 + * serialnr??: {CCI134},{CCI207} + * computer??: {GeminiII},{CommanderIII} + * computer??: {GeminiII},{CommanderIII} + * ??: 1 + * + * Followed by the data values (all comma-separated, all one long line). + */ +static int try_to_open_csv(const char *filename, struct memblock *mem, enum csv_format type) +{ + char *p = mem->buffer; + char *header[8]; + int i, time; + time_t date; + struct dive *dive; + + for (i = 0; i < 8; i++) { + header[i] = p; + p = strchr(p, ','); + if (!p) + return 0; + p++; + } + + date = parse_date(header[2]); + if (!date) + return 0; + + dive = alloc_dive(); + dive->when = date; + dive->number = atoi(header[1]); + + time = 0; + for (;;) { + char *end; + double val; + struct sample *sample; + + errno = 0; + val = strtod(p,&end); + if (end == p) + break; + if (errno) + break; + + sample = prepare_sample(&dive); + sample->time.seconds = time; + add_sample_data(sample, type, val); + finish_sample(dive); + + time++; + dive->duration.seconds = time; + if (*end != ',') + break; + p = end+1; + } + record_dive(dive); + return 1; +} + +static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem, GError **error) +{ + /* Suunto Dive Manager files: SDE */ + if (!strcasecmp(fmt, "SDE")) + return try_to_open_suunto(filename, mem, error); + + /* Truly nasty intentionally obfuscated Cochran Anal software */ + if (!strcasecmp(fmt, "CAN")) + return try_to_open_cochran(filename, mem, error); + + /* Cochran export comma-separated-value files */ + if (!strcasecmp(fmt, "DPT")) + return try_to_open_csv(filename, mem, CSV_DEPTH); + if (!strcasecmp(fmt, "TMP")) + return try_to_open_csv(filename, mem, CSV_TEMP); + if (!strcasecmp(fmt, "HP1")) + return try_to_open_csv(filename, mem, CSV_PRESSURE); + + return 0; +} + +static void parse_file_buffer(const char *filename, struct memblock *mem, GError **error) +{ + char *fmt = strrchr(filename, '.'); + if (fmt && open_by_filename(filename, fmt+1, mem, error)) + return; + + parse_xml_buffer(filename, mem->buffer, mem->size, error); +} + void parse_file(const char *filename, GError **error) { struct memblock mem; @@ -68,6 +263,6 @@ void parse_file(const char *filename, GError **error) return; } - parse_xml_buffer(filename, mem.buffer, mem.size, error); + parse_file_buffer(filename, &mem, error); free(mem.buffer); }