7 #include <libxml/parser.h>
8 #include <libxml/tree.h>
14 struct dive_table dive_table;
17 * Add a dive into the dive_table array
19 static void record_dive(struct dive *dive)
21 int nr = dive_table.nr, allocated = dive_table.allocated;
22 struct dive **dives = dive_table.dives;
24 if (nr >= allocated) {
25 allocated = (nr + 32) * 3 / 2;
26 dives = realloc(dives, allocated * sizeof(struct dive *));
29 dive_table.dives = dives;
30 dive_table.allocated = allocated;
36 static void nonmatch(const char *type, const char *fullname, const char *name, char *buffer)
39 printf("Unable to match %s '(%.*s)%s' (%s)\n", type,
40 (int) (name - fullname), fullname, name,
45 static const char *last_part(const char *name)
47 const char *p = strrchr(name, '.');
48 return p ? p+1 : name;
51 typedef void (*matchfn_t)(char *buffer, void *);
53 static int match(const char *pattern, const char *name, matchfn_t fn, char *buf, void *data)
55 if (strcasecmp(pattern, name))
62 * Dive info as it is being built up..
64 static int alloc_samples;
65 static struct dive *dive;
66 static struct sample *sample;
69 static time_t utc_mktime(struct tm *tm)
71 static const int mdays[] = {
72 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
74 int year = tm->tm_year;
75 int month = tm->tm_mon;
76 int day = tm->tm_mday;
78 /* First normalize relative to 1900 */
84 /* Normalized to Jan 1, 1970: unix time */
87 if (year < 0 || year > 129) /* algo only works for 1970-2099 */
89 if (month < 0 || month > 11) /* array bounds */
91 if (month < 2 || (year + 2) % 4)
93 if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
95 return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
96 tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
99 static void divedate(char *buffer, void *_when)
102 time_t *when = _when;
104 if (sscanf(buffer, "%d.%d.%d", &d, &m, &y) == 3) {
108 if (tm.tm_sec | tm.tm_min | tm.tm_hour)
109 *when = utc_mktime(&tm);
114 static void divetime(char *buffer, void *_when)
117 time_t *when = _when;
119 if (sscanf(buffer, "%d:%d:%d", &h, &m, &s) >= 2) {
124 *when = utc_mktime(&tm);
129 /* Libdivecomputer: "2011-03-20 10:22:38" */
130 static void divedatetime(char *buffer, void *_when)
134 time_t *when = _when;
136 if (sscanf(buffer, "%d-%d-%d %d:%d:%d",
137 &y, &m, &d, &hr, &min, &sec) == 6) {
144 *when = utc_mktime(&tm);
160 static enum number_type integer_or_float(char *buffer, union int_or_float *res)
166 /* Integer or floating point? */
167 val = strtol(buffer, &end, 10);
168 if (val < 0 || end == buffer)
171 /* Looks like it might be floating point? */
174 fp = strtod(buffer, &end);
185 static void pressure(char *buffer, void *_press)
187 pressure_t *pressure = _press;
188 union int_or_float val;
190 switch (integer_or_float(buffer, &val)) {
192 /* Maybe it's in Bar? */
193 if (val.fp < 500.0) {
194 pressure->mbar = val.fp * 1000;
197 printf("Unknown fractional pressure reading %s\n", buffer);
202 * Random integer? Maybe in PSI? Or millibar already?
204 * We assume that 5 bar is a ridiculous tank pressure,
205 * so if it's smaller than 5000, it's in PSI..
208 pressure->mbar = val.i * 68.95;
211 pressure->mbar = val.i;
214 printf("Strange pressure reading %s\n", buffer);
219 static void depth(char *buffer, void *_depth)
221 depth_t *depth = _depth;
222 union int_or_float val;
224 switch (integer_or_float(buffer, &val)) {
225 /* All values are probably in meters */
230 depth->mm = val.fp * 1000;
233 printf("Strange depth reading %s\n", buffer);
238 static void temperature(char *buffer, void *_temperature)
240 temperature_t *temperature = _temperature;
241 union int_or_float val;
243 switch (integer_or_float(buffer, &val)) {
244 /* C or F? Who knows? Let's default to Celsius */
249 /* Ignore zero. It means "none" */
254 temperature->mkelvin = (val.fp + 273.16) * 1000;
258 if (val.fp < 212.0) {
259 temperature->mkelvin = (val.fp + 459.67) * 5000/9;
262 /* Kelvin or already millikelvin */
265 temperature->mkelvin = val.fp;
268 printf("Strange temperature reading %s\n", buffer);
273 static void sampletime(char *buffer, void *_time)
277 duration_t *time = _time;
279 i = sscanf(buffer, "%d:%d", &min, &sec);
286 time->seconds = sec + min*60;
289 printf("Strange sample time reading %s\n", buffer);
294 static void duration(char *buffer, void *_time)
296 sampletime(buffer, _time);
299 static void ignore(char *buffer, void *_time)
303 /* We're in samples - try to convert the random xml value to something useful */
304 static void try_to_fill_sample(struct sample *sample, const char *name, char *buf)
306 const char *last = last_part(name);
308 if (match("pressure", last, pressure, buf, &sample->tankpressure))
310 if (match("cylpress", last, pressure, buf, &sample->tankpressure))
312 if (match("depth", last, depth, buf, &sample->depth))
314 if (match("temperature", last, temperature, buf, &sample->temperature))
316 if (match("sampletime", last, sampletime, buf, &sample->time))
318 if (match("time", last, sampletime, buf, &sample->time))
321 nonmatch("sample", name, last, buf);
324 /* We're in the top-level dive xml. Try to convert whatever value to a dive value */
325 static void try_to_fill_dive(struct dive *dive, const char *name, char *buf)
327 const char *last = last_part(name);
329 if (match("date", last, divedate, buf, &dive->when))
331 if (match("time", last, divetime, buf, &dive->when))
333 if (match("datetime", last, divedatetime, buf, &dive->when))
335 if (match("maxdepth", last, depth, buf, &dive->maxdepth))
337 if (match("meandepth", last, depth, buf, &dive->meandepth))
339 if (match("divetime", last, duration, buf, &dive->duration))
341 if (match("divetimesec", last, duration, buf, &dive->duration))
343 if (match("surfacetime", last, duration, buf, &dive->surfacetime))
345 if (match("airtemp", last, temperature, buf, &dive->airtemp))
347 if (match("watertemp", last, temperature, buf, &dive->watertemp))
349 if (match("cylinderstartpressure", last, pressure, buf, &dive->beginning_pressure))
351 if (match("cylinderendpressure", last, pressure, buf, &dive->end_pressure))
353 if (match("divenumber", last, ignore, buf, NULL))
355 if (match("diveseries", last, ignore, buf, NULL))
357 if (match("number", last, ignore, buf, NULL))
359 if (match("size", last, ignore, buf, NULL))
361 if (match("fingerprint", last, ignore, buf, NULL))
363 nonmatch("dive", name, last, buf);
366 static unsigned int dive_size(int samples)
368 return sizeof(struct dive) + samples*sizeof(struct sample);
372 * File boundaries are dive boundaries. But sometimes there are
373 * multiple dives per file, so there can be other events too that
374 * trigger a "new dive" marker and you may get some nesting due
375 * to that. Just ignore nesting levels.
377 static void dive_start(void)
385 size = dive_size(alloc_samples);
389 memset(dive, 0, size);
390 memset(&tm, 0, sizeof(tm));
393 static char *generate_name(struct dive *dive)
397 char buffer[256], *p;
399 tm = gmtime(&dive->when);
401 len = snprintf(buffer, sizeof(buffer),
405 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
406 tm->tm_hour, tm->tm_min, tm->tm_sec,
407 to_feet(dive->maxdepth), dive->duration.seconds / 60);
411 memcpy(p, buffer, len+1);
415 static void dive_end(void)
420 dive->name = generate_name(dive);
425 static void sample_start(void)
432 if (nr >= alloc_samples) {
435 alloc_samples = (alloc_samples * 3)/2 + 10;
436 size = dive_size(alloc_samples);
437 dive = realloc(dive, size);
441 sample = dive->sample + nr;
442 memset(sample, 0, sizeof(*sample));
445 static void sample_end(void)
450 if (sample->time.seconds > dive->duration.seconds) {
451 if (sample->depth.mm)
452 dive->duration = sample->time;
455 if (sample->depth.mm > dive->maxdepth.mm)
456 dive->maxdepth.mm = sample->depth.mm;
462 static void entry(const char *name, int size, const char *raw)
464 char *buf = malloc(size+1);
468 memcpy(buf, raw, size);
471 try_to_fill_sample(sample, name, buf);
475 try_to_fill_dive(dive, name, buf);
480 static const char *nodename(xmlNode *node, char *buf, int len)
482 if (!node || !node->name)
490 const char *name = node->name;
491 int i = strlen(name);
493 unsigned char c = name[i];
499 if (!node || !node->name)
509 static void visit_one_node(xmlNode *node)
512 const unsigned char *content;
513 char buffer[MAXNAME];
516 content = node->content;
520 /* Trim whitespace at beginning */
521 while (isspace(*content))
524 /* Trim whitespace at end */
525 len = strlen(content);
526 while (len && isspace(content[len-1]))
532 /* Don't print out the node name if it is "text" */
533 if (!strcmp(node->name, "text"))
536 name = nodename(node, buffer, sizeof(buffer));
538 entry(name, len, content);
541 static void traverse(xmlNode *node)
545 for (n = node; n; n = n->next) {
546 /* XML from libdivecomputer: 'dive' per new dive */
547 if (!strcmp(n->name, "dive")) {
549 traverse(n->children);
555 * At least both libdivecomputer and Suunto
558 * Well - almost. Ignore case.
560 if (!strcasecmp(n->name, "sample")) {
562 traverse(n->children);
567 /* Anything else - just visit it and recurse */
569 traverse(n->children);
573 void parse_xml_file(const char *filename)
577 doc = xmlReadFile(filename, NULL, 0);
579 fprintf(stderr, "Failed to parse '%s'.\n", filename);
584 traverse(xmlDocGetRootElement(doc));
590 void parse_xml_init(void)