7 #include <libxml/parser.h>
8 #include <libxml/tree.h>
14 static struct dive **dive_table;
15 static int nr_dives, nr_allocated;
17 static void record_dive(struct dive *dive)
19 if (nr_dives >= nr_allocated) {
20 nr_allocated = (nr_dives + 32) * 3 / 2;
21 dive_table = realloc(dive_table, nr_allocated * sizeof(struct dive *));
25 dive_table[nr_dives++] = dive;
28 static void show_dive(int nr, struct dive *dive)
33 tm = gmtime(&dive->when);
35 printf("At %02d:%02d:%02d %04d-%02d-%02d (%d ft max, %d minutes)\n",
36 tm->tm_hour, tm->tm_min, tm->tm_sec,
37 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
38 to_feet(dive->maxdepth), dive->duration.seconds / 60);
43 for (i = 0; i < dive->samples; i++) {
44 struct sample *s = dive->sample + i;
46 printf("%4d:%02d: %3d ft, %2d C, %4d PSI\n",
51 to_PSI(s->tankpressure));
55 static int sortfn(const void *_a, const void *_b)
57 const struct dive *a = *(void **)_a;
58 const struct dive *b = *(void **)_b;
60 if (a->when < b->when)
62 if (a->when > b->when)
67 static void report_dives(void)
70 qsort(dive_table, nr_dives, sizeof(struct dive *), sortfn);
72 for (i = 0; i < nr_dives; i++)
73 show_dive(i+1, dive_table[i]);
76 static void nonmatch(const char *type, const char *fullname, const char *name, char *buffer)
79 printf("Unable to match %s '(%.*s)%s' (%s)\n", type,
80 (int) (name - fullname), fullname, name,
85 static const char *last_part(const char *name)
87 const char *p = strrchr(name, '.');
88 return p ? p+1 : name;
91 typedef void (*matchfn_t)(char *buffer, void *);
93 static int match(const char *pattern, const char *name, matchfn_t fn, char *buf, void *data)
95 if (strcasecmp(pattern, name))
102 * Dive info as it is being built up..
104 static int alloc_samples;
105 static struct dive *dive;
106 static struct sample *sample;
109 static time_t utc_mktime(struct tm *tm)
111 static const int mdays[] = {
112 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
114 int year = tm->tm_year;
115 int month = tm->tm_mon;
116 int day = tm->tm_mday;
118 /* First normalize relative to 1900 */
121 else if (year > 1900)
124 /* Normalized to Jan 1, 1970: unix time */
127 if (year < 0 || year > 129) /* algo only works for 1970-2099 */
129 if (month < 0 || month > 11) /* array bounds */
131 if (month < 2 || (year + 2) % 4)
133 if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
135 return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
136 tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
139 static void divedate(char *buffer, void *_when)
142 time_t *when = _when;
144 if (sscanf(buffer, "%d.%d.%d", &d, &m, &y) == 3) {
148 if (tm.tm_sec | tm.tm_min | tm.tm_hour)
149 *when = utc_mktime(&tm);
154 static void divetime(char *buffer, void *_when)
157 time_t *when = _when;
159 if (sscanf(buffer, "%d:%d:%d", &h, &m, &s) >= 2) {
164 *when = utc_mktime(&tm);
169 /* Libdivecomputer: "2011-03-20 10:22:38" */
170 static void divedatetime(char *buffer, void *_when)
174 time_t *when = _when;
176 if (sscanf(buffer, "%d-%d-%d %d:%d:%d",
177 &y, &m, &d, &hr, &min, &sec) == 6) {
184 *when = utc_mktime(&tm);
200 static enum number_type integer_or_float(char *buffer, union int_or_float *res)
206 /* Integer or floating point? */
207 val = strtol(buffer, &end, 10);
208 if (val < 0 || end == buffer)
211 /* Looks like it might be floating point? */
214 fp = strtod(buffer, &end);
225 static void pressure(char *buffer, void *_press)
227 pressure_t *pressure = _press;
228 union int_or_float val;
230 switch (integer_or_float(buffer, &val)) {
232 /* Maybe it's in Bar? */
233 if (val.fp < 500.0) {
234 pressure->mbar = val.fp * 1000;
237 printf("Unknown fractional pressure reading %s\n", buffer);
242 * Random integer? Maybe in PSI? Or millibar already?
244 * We assume that 5 bar is a ridiculous tank pressure,
245 * so if it's smaller than 5000, it's in PSI..
248 pressure->mbar = val.i * 68.95;
251 pressure->mbar = val.i;
254 printf("Strange pressure reading %s\n", buffer);
259 static void depth(char *buffer, void *_depth)
261 depth_t *depth = _depth;
262 union int_or_float val;
264 switch (integer_or_float(buffer, &val)) {
265 /* Integer values are probably in feet */
267 depth->mm = 304.8 * val.i;
269 /* Float? Probably meters.. */
271 depth->mm = val.fp * 1000;
274 printf("Strange depth reading %s\n", buffer);
279 static void temperature(char *buffer, void *_temperature)
281 temperature_t *temperature = _temperature;
282 union int_or_float val;
284 switch (integer_or_float(buffer, &val)) {
285 /* C or F? Who knows? Let's default to Celsius */
290 /* Ignore zero. It means "none" */
295 temperature->mkelvin = (val.fp + 273.16) * 1000;
299 if (val.fp < 212.0) {
300 temperature->mkelvin = (val.fp + 459.67) * 5000/9;
303 /* Kelvin or already millikelvin */
306 temperature->mkelvin = val.fp;
309 printf("Strange temperature reading %s\n", buffer);
314 static void sampletime(char *buffer, void *_time)
318 duration_t *time = _time;
320 i = sscanf(buffer, "%d:%d", &min, &sec);
327 time->seconds = sec + min*60;
330 printf("Strange sample time reading %s\n", buffer);
335 static void duration(char *buffer, void *_time)
337 sampletime(buffer, _time);
340 static void ignore(char *buffer, void *_time)
344 /* We're in samples - try to convert the random xml value to something useful */
345 static void try_to_fill_sample(struct sample *sample, const char *name, char *buf)
347 const char *last = last_part(name);
349 if (match("pressure", last, pressure, buf, &sample->tankpressure))
351 if (match("cylpress", last, pressure, buf, &sample->tankpressure))
353 if (match("depth", last, depth, buf, &sample->depth))
355 if (match("temperature", last, temperature, buf, &sample->temperature))
357 if (match("sampletime", last, sampletime, buf, &sample->time))
359 if (match("time", last, sampletime, buf, &sample->time))
362 nonmatch("sample", name, last, buf);
365 /* We're in the top-level dive xml. Try to convert whatever value to a dive value */
366 static void try_to_fill_dive(struct dive *dive, const char *name, char *buf)
368 const char *last = last_part(name);
370 if (match("date", last, divedate, buf, &dive->when))
372 if (match("time", last, divetime, buf, &dive->when))
374 if (match("datetime", last, divedatetime, buf, &dive->when))
376 if (match("maxdepth", last, depth, buf, &dive->maxdepth))
378 if (match("meandepth", last, depth, buf, &dive->meandepth))
380 if (match("divetime", last, duration, buf, &dive->duration))
382 if (match("divetimesec", last, duration, buf, &dive->duration))
384 if (match("surfacetime", last, duration, buf, &dive->surfacetime))
386 if (match("airtemp", last, temperature, buf, &dive->airtemp))
388 if (match("watertemp", last, temperature, buf, &dive->watertemp))
390 if (match("cylinderstartpressure", last, pressure, buf, &dive->beginning_pressure))
392 if (match("cylinderendpressure", last, pressure, buf, &dive->end_pressure))
394 if (match("divenumber", last, ignore, buf, NULL))
396 if (match("diveseries", last, ignore, buf, NULL))
398 if (match("number", last, ignore, buf, NULL))
400 if (match("size", last, ignore, buf, NULL))
402 if (match("fingerprint", last, ignore, buf, NULL))
404 nonmatch("dive", name, last, buf);
407 static unsigned int dive_size(int samples)
409 return sizeof(struct dive) + samples*sizeof(struct sample);
413 * File boundaries are dive boundaries. But sometimes there are
414 * multiple dives per file, so there can be other events too that
415 * trigger a "new dive" marker and you may get some nesting due
416 * to that. Just ignore nesting levels.
418 static void dive_start(void)
426 size = dive_size(alloc_samples);
430 memset(dive, 0, size);
431 memset(&tm, 0, sizeof(tm));
434 static void dive_end(void)
442 static void sample_start(void)
449 if (nr >= alloc_samples) {
452 alloc_samples = (alloc_samples * 3)/2 + 10;
453 size = dive_size(alloc_samples);
454 dive = realloc(dive, size);
458 sample = dive->sample + nr;
459 memset(sample, 0, sizeof(*sample));
462 static void sample_end(void)
470 static void entry(const char *name, int size, const char *raw)
472 char *buf = malloc(size+1);
476 memcpy(buf, raw, size);
479 try_to_fill_sample(sample, name, buf);
483 try_to_fill_dive(dive, name, buf);
488 static const char *nodename(xmlNode *node, char *buf, int len)
490 if (!node || !node->name)
498 const char *name = node->name;
499 int i = strlen(name);
501 unsigned char c = name[i];
507 if (!node || !node->name)
517 static void visit_one_node(xmlNode *node)
520 const unsigned char *content;
521 char buffer[MAXNAME];
524 content = node->content;
528 /* Trim whitespace at beginning */
529 while (isspace(*content))
532 /* Trim whitespace at end */
533 len = strlen(content);
534 while (len && isspace(content[len-1]))
540 /* Don't print out the node name if it is "text" */
541 if (!strcmp(node->name, "text"))
544 name = nodename(node, buffer, sizeof(buffer));
546 entry(name, len, content);
549 static void traverse(xmlNode *node)
553 for (n = node; n; n = n->next) {
554 /* XML from libdivecomputer: 'dive' per new dive */
555 if (!strcmp(n->name, "dive")) {
557 traverse(n->children);
563 * At least both libdivecomputer and Suunto
566 * Well - almost. Ignore case.
568 if (!strcasecmp(n->name, "sample")) {
570 traverse(n->children);
575 /* Anything else - just visit it and recurse */
577 traverse(n->children);
581 static void parse_xml_file(const char *filename)
585 doc = xmlReadFile(filename, NULL, 0);
587 fprintf(stderr, "Failed to parse '%s'.\n", filename);
592 traverse(xmlDocGetRootElement(doc));
598 static void parse_argument(const char *arg)
600 const char *p = arg+1;
608 fprintf(stderr, "Bad argument '%s'\n", arg);
614 int main(int argc, char **argv)
620 for (i = 1; i < argc; i++) {
621 const char *a = argv[i];