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