]> git.tdb.fi Git - ext/subsurface.git/blob - file.c
Fix profile and average depth for freedives
[ext/subsurface.git] / file.c
1 #include <unistd.h>
2 #include <fcntl.h>
3 #include <sys/stat.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7
8 #include "dive.h"
9 #include "file.h"
10
11 /* Crazy windows sh*t */
12 #ifndef O_BINARY
13 #define O_BINARY 0
14 #endif
15
16 static int readfile(const char *filename, struct memblock *mem)
17 {
18         int ret, fd;
19         struct stat st;
20         char *buf;
21
22         mem->buffer = NULL;
23         mem->size = 0;
24
25         fd = open(filename, O_RDONLY | O_BINARY);
26         if (fd < 0)
27                 return fd;
28         ret = fstat(fd, &st);
29         if (ret < 0)
30                 goto out;
31         ret = -EINVAL;
32         if (!S_ISREG(st.st_mode))
33                 goto out;
34         ret = 0;
35         if (!st.st_size)
36                 goto out;
37         buf = malloc(st.st_size+1);
38         ret = -1;
39         errno = ENOMEM;
40         if (!buf)
41                 goto out;
42         mem->buffer = buf;
43         mem->size = st.st_size;
44         ret = read(fd, buf, mem->size);
45         if (ret < 0)
46                 goto free;
47         buf[ret] = 0;
48         if (ret == mem->size)
49                 goto out;
50         errno = EIO;
51         ret = -1;
52 free:
53         free(mem->buffer);
54         mem->buffer = NULL;
55         mem->size = 0;
56 out:
57         close(fd);
58         return ret;
59 }
60
61 #ifdef LIBZIP
62 #include <zip.h>
63
64 static void suunto_read(struct zip_file *file, GError **error)
65 {
66         int size = 1024, n, read = 0;
67         char *mem = malloc(size);
68
69         while ((n = zip_fread(file, mem+read, size-read)) > 0) {
70                 read += n;
71                 size = read * 3 / 2;
72                 mem = realloc(mem, size);
73         }
74         parse_xml_buffer("SDE file", mem, read, error);
75         free(mem);
76 }
77 #endif
78
79 static int try_to_open_suunto(const char *filename, struct memblock *mem, GError **error)
80 {
81         int success = 0;
82 #ifdef LIBZIP
83         /* Grr. libzip needs to re-open the file, it can't take a buffer */
84         struct zip *zip = zip_open(filename, ZIP_CHECKCONS, NULL);
85
86         if (zip) {
87                 int index;
88                 for (index = 0; ;index++) {
89                         struct zip_file *file = zip_fopen_index(zip, index, 0);
90                         if (!file)
91                                 break;
92                         suunto_read(file, error);
93                         zip_fclose(file);
94                         success++;
95                 }
96                 zip_close(zip);
97         }
98 #endif
99         return success;
100 }
101
102 static time_t parse_date(const char *date)
103 {
104         int hour, min, sec;
105         struct tm tm;
106         char *p;
107
108         memset(&tm, 0, sizeof(tm));
109         tm.tm_mday = strtol(date, &p, 10);
110         if (tm.tm_mday < 1 || tm.tm_mday > 31)
111                 return 0;
112         for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
113                 if (!memcmp(p, monthname(tm.tm_mon), 3))
114                         break;
115         }
116         if (tm.tm_mon > 11)
117                 return 0;
118         date = p+3;
119         tm.tm_year = strtol(date, &p, 10);
120         if (date == p)
121                 return 0;
122         if (tm.tm_year < 70)
123                 tm.tm_year += 2000;
124         if (tm.tm_year < 100)
125                 tm.tm_year += 1900;
126         if (sscanf(p, "%d:%d:%d", &hour, &min, &sec) != 3)
127                 return 0;
128         tm.tm_hour = hour;
129         tm.tm_min = min;
130         tm.tm_sec = sec;
131         return utc_mktime(&tm);
132 }
133
134 enum csv_format {
135         CSV_DEPTH, CSV_TEMP, CSV_PRESSURE
136 };
137
138 static void add_sample_data(struct sample *sample, enum csv_format type, double val)
139 {
140         switch (type) {
141         case CSV_DEPTH:
142                 sample->depth.mm = feet_to_mm(val);
143                 break;
144         case CSV_TEMP:
145                 sample->temperature.mkelvin = F_to_mkelvin(val);
146                 break;
147         case CSV_PRESSURE:
148                 sample->cylinderpressure.mbar = psi_to_mbar(val*4);
149                 break;
150         }
151 }
152
153 /*
154  * Cochran comma-separated values: depth in feet, temperature in F, pressure in psi.
155  *
156  * They start with eight comma-separated fields like:
157  *
158  *   filename: {C:\Analyst4\can\T036785.can},{C:\Analyst4\can\K031892.can}
159  *   divenr: %d
160  *   datetime: {03Sep11 16:37:22},{15Dec11 18:27:02}
161  *   ??: 1
162  *   serialnr??: {CCI134},{CCI207}
163  *   computer??: {GeminiII},{CommanderIII}
164  *   computer??: {GeminiII},{CommanderIII}
165  *   ??: 1
166  *
167  * Followed by the data values (all comma-separated, all one long line).
168  */
169 static int try_to_open_csv(const char *filename, struct memblock *mem, enum csv_format type)
170 {
171         char *p = mem->buffer;
172         char *header[8];
173         int i, time;
174         time_t date;
175         struct dive *dive;
176
177         for (i = 0; i < 8; i++) {
178                 header[i] = p;
179                 p = strchr(p, ',');
180                 if (!p)
181                         return 0;
182                 p++;
183         }
184
185         date = parse_date(header[2]);
186         if (!date)
187                 return 0;
188
189         dive = alloc_dive();
190         dive->when = date;
191         dive->number = atoi(header[1]);
192
193         time = 0;
194         for (;;) {
195                 char *end;
196                 double val;
197                 struct sample *sample;
198
199                 errno = 0;
200                 val = strtod(p,&end);
201                 if (end == p)
202                         break;
203                 if (errno)
204                         break;
205
206                 sample = prepare_sample(&dive);
207                 sample->time.seconds = time;
208                 add_sample_data(sample, type, val);
209                 finish_sample(dive);
210
211                 time++;
212                 dive->duration.seconds = time;
213                 if (*end != ',')
214                         break;
215                 p = end+1;
216         }
217         record_dive(dive);
218         return 1;
219 }
220
221 static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem, GError **error)
222 {
223         /* Suunto Dive Manager files: SDE */
224         if (!strcasecmp(fmt, "SDE"))
225                 return try_to_open_suunto(filename, mem, error);
226
227         /* Truly nasty intentionally obfuscated Cochran Anal software */
228         if (!strcasecmp(fmt, "CAN"))
229                 return try_to_open_cochran(filename, mem, error);
230
231         /* Cochran export comma-separated-value files */
232         if (!strcasecmp(fmt, "DPT"))
233                 return try_to_open_csv(filename, mem, CSV_DEPTH);
234         if (!strcasecmp(fmt, "TMP"))
235                 return try_to_open_csv(filename, mem, CSV_TEMP);
236         if (!strcasecmp(fmt, "HP1"))
237                 return try_to_open_csv(filename, mem, CSV_PRESSURE);
238
239         return 0;
240 }
241
242 static void parse_file_buffer(const char *filename, struct memblock *mem, GError **error)
243 {
244         char *fmt = strrchr(filename, '.');
245         if (fmt && open_by_filename(filename, fmt+1, mem, error))
246                 return;
247
248         parse_xml_buffer(filename, mem->buffer, mem->size, error);
249 }
250
251 void parse_file(const char *filename, GError **error)
252 {
253         struct memblock mem;
254
255         if (readfile(filename, &mem) < 0) {
256                 fprintf(stderr, "Failed to read '%s'.\n", filename);
257                 if (error) {
258                         *error = g_error_new(g_quark_from_string("subsurface"),
259                                              DIVE_ERROR_PARSE,
260                                              "Failed to read '%s'",
261                                              filename);
262                 }
263                 return;
264         }
265
266         parse_file_buffer(filename, &mem, error);
267         free(mem.buffer);
268 }