]> git.tdb.fi Git - ext/subsurface.git/blob - parse.c
Fix stupid mis-initialization of current sample
[ext/subsurface.git] / parse.c
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <string.h>
4 #include <libxml/parser.h>
5 #include <libxml/tree.h>
6
7 /*
8  * Some silly typedefs to make our units very explicit.
9  *
10  * Also, the units are chosen so that values can be expressible as
11  * integers, so that we never have FP rounding issues. And they
12  * are small enough that converting to/from imperial units doesn't
13  * really matter.
14  *
15  * We also strive to make '0' a meaningless number saying "not
16  * initialized", since many values are things that may not have
17  * been reported (eg tank pressure or temperature from dive
18  * computers that don't support them). But sometimes -1 is an even
19  * more explicit way of saying "not there".
20  *
21  * Thus "millibar" for pressure, for example, or "millikelvin" for
22  * temperatures. Doing temperatures in celsius or fahrenheit would
23  * make for loss of precision when converting from one to the other,
24  * and using millikelvin is SI-like but also means that a temperature
25  * of '0' is clearly just a missing temperature or tank pressure.
26  *
27  * Also strive to use units that can not possibly be mistaken for a
28  * valid value in a "normal" system without conversion. If the max
29  * depth of a dive is '20000', you probably didn't convert from mm on
30  * output, or if the max depth gets reported as "0.2ft" it was either
31  * a really boring dive, or there was some missing input conversion,
32  * and a 60-ft dive got recorded as 60mm.
33  *
34  * Doing these as "structs containing value" means that we always
35  * have to explicitly write out those units in order to get at the
36  * actual value. So there is hopefully little fear of using a value
37  * in millikelvin as Fahrenheit by mistake.
38  *
39  * We don't actually use these all yet, so maybe they'll change, but
40  * I made a number of types as guidelines.
41  */
42 typedef struct {
43         int seconds;
44 } duration_t;
45
46 typedef struct {
47         int mm;
48 } depth_t;
49
50 typedef struct {
51         int mbar;
52 } pressure_t;
53
54 typedef struct {
55         int mkelvin;
56 } temperature_t;
57
58 typedef struct {
59         int mliter;
60 } volume_t;
61
62 typedef struct {
63         int permille;
64 } fraction_t;
65
66 typedef struct {
67         int grams;
68 } weight_t;
69
70 typedef struct {
71         fraction_t o2;
72         fraction_t n2;
73         fraction_t he2;
74 } gasmix_t;
75
76 typedef struct {
77         volume_t size;
78         pressure_t pressure;
79 } tank_type_t;
80
81 struct sample {
82         duration_t time;
83         depth_t depth;
84         temperature_t temperature;
85         pressure_t tankpressure;
86         int tankindex;
87 };
88
89 struct dive {
90         time_t when;
91         depth_t maxdepth, meandepth;
92         duration_t duration, surfacetime;
93         depth_t visibility;
94         temperature_t airtemp, watertemp;
95         pressure_t beginning_pressure, end_pressure;
96         int samples;
97         struct sample sample[];
98 };
99
100 static void record_dive(struct dive *dive)
101 {
102         static int nr;
103
104         printf("Recording dive %d with %d samples\n", ++nr, dive->samples);
105 }
106
107 static void nonmatch(const char *type, const char *fullname, const char *name, int size, const char *buffer)
108 {
109         printf("Unable to match %s '(%.*s)%s' (%.*s)\n", type,
110                 (int) (name - fullname), fullname, name,
111                 size, buffer);
112 }
113
114 static const char *last_part(const char *name)
115 {
116         const char *p = strrchr(name, '.');
117         return p ? p+1 : name;
118 }
119
120 /* We're in samples - try to convert the random xml value to something useful */
121 static void try_to_fill_sample(struct sample *sample, const char *name, int size, const char *buffer)
122 {
123         const char *last = last_part(name);
124         nonmatch("sample", name, last, size, buffer);
125 }
126
127 /* We're in the top-level dive xml. Try to convert whatever value to a dive value */
128 static void try_to_fill_dive(struct dive *dive, const char *name, int size, const char *buffer)
129 {
130         const char *last = last_part(name);
131         nonmatch("dive", name, last, size, buffer);
132 }
133
134 /*
135  * Dive info as it is being built up..
136  */
137 static int alloc_samples;
138 static struct dive *dive;
139 static struct sample *sample;
140
141 static unsigned int dive_size(int samples)
142 {
143         return sizeof(struct dive) + samples*sizeof(struct sample);
144 }
145
146 /*
147  * File boundaries are dive boundaries. But sometimes there are
148  * multiple dives per file, so there can be other events too that
149  * trigger a "new dive" marker and you may get some nesting due
150  * to that. Just ignore nesting levels.
151  */
152 static void dive_start(void)
153 {
154         unsigned int size;
155
156         alloc_samples = 5;
157         size = dive_size(alloc_samples);
158         dive = malloc(size);
159         if (!dive)
160                 exit(1);
161         memset(dive, 0, size);
162 }
163
164 static void dive_end(void)
165 {
166         if (!dive)
167                 return;
168         record_dive(dive);
169         dive = NULL;
170 }
171
172 static void sample_start(void)
173 {
174         int nr;
175
176         if (!dive)
177                 return;
178         nr = dive->samples;
179         if (nr >= alloc_samples) {
180                 unsigned int size;
181
182                 alloc_samples = (alloc_samples * 3)/2 + 10;
183                 size = dive_size(alloc_samples);
184                 dive = realloc(dive, size);
185                 if (!dive)
186                         return;
187         }
188         sample = dive->sample + nr;
189 }
190
191 static void sample_end(void)
192 {
193         sample = NULL;
194         if (!dive)
195                 return;
196         dive->samples++;
197 }
198
199 static void entry(const char *name, int size, const char *buffer)
200 {
201         if (sample) {
202                 try_to_fill_sample(sample, name, size, buffer);
203                 return;
204         }
205         if (dive) {
206                 try_to_fill_dive(dive, name, size, buffer);
207                 return;
208         }
209 }
210
211 static const char *nodename(xmlNode *node, char *buf, int len)
212 {
213         /* Don't print out the node name if it is "text" */
214         if (!strcmp(node->name, "text")) {
215                 node = node->parent;
216                 if (!node || !node->name)
217                         return "root";
218         }
219
220         buf += len;
221         *--buf = 0;
222         len--;
223
224         for(;;) {
225                 const char *name = node->name;
226                 int i = strlen(name);
227                 while (--i >= 0) {
228                         unsigned char c = name[i];
229                         *--buf = tolower(c);
230                         if (!--len)
231                                 return buf;
232                 }
233                 node = node->parent;
234                 if (!node || !node->name)
235                         return buf;
236                 *--buf = '.';
237                 if (!--len)
238                         return buf;
239         }
240 }
241
242 #define MAXNAME 64
243
244 static void visit_one_node(xmlNode *node)
245 {
246         int len;
247         const unsigned char *content;
248         char buffer[MAXNAME];
249         const char *name;
250
251         content = node->content;
252         if (!content)
253                 return;
254
255         /* Trim whitespace at beginning */
256         while (isspace(*content))
257                 content++;
258
259         /* Trim whitespace at end */
260         len = strlen(content);
261         while (len && isspace(content[len-1]))
262                 len--;
263
264         if (!len)
265                 return;
266
267         name = nodename(node, buffer, sizeof(buffer));
268
269         entry(name, len, content);
270 }
271
272 static void traverse(xmlNode *node)
273 {
274         xmlNode *n;
275
276         for (n = node; n; n = n->next) {
277                 /* XML from libdivecomputer: 'dive' per new dive */
278                 if (!strcmp(n->name, "dive")) {
279                         dive_start();
280                         traverse(n->children);
281                         dive_end();
282                         continue;
283                 }
284
285                 /*
286                  * At least both libdivecomputer and Suunto
287                  * agree on "sample".
288                  *
289                  * Well - almost. Ignore case.
290                  */
291                 if (!strcasecmp(n->name, "sample")) {
292                         sample_start();
293                         traverse(n->children);
294                         sample_end();
295                         continue;
296                 }
297
298                 /* Anything else - just visit it and recurse */
299                 visit_one_node(n);
300                 traverse(n->children);
301         }
302 }
303
304 static void parse(const char *filename)
305 {
306         xmlDoc *doc;
307
308         doc = xmlReadFile(filename, NULL, 0);
309         if (!doc) {
310                 fprintf(stderr, "Failed to parse '%s'.\n", filename);
311                 return;
312         }
313
314         dive_start();
315         traverse(xmlDocGetRootElement(doc));
316         dive_end();
317         xmlFreeDoc(doc);
318         xmlCleanupParser();
319 }
320
321 int main(int argc, char **argv)
322 {
323         int i;
324
325         LIBXML_TEST_VERSION
326
327         for (i = 1; i < argc; i++)
328                 parse(argv[i]);
329         return 0;
330 }