X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=parse-xml.c;h=6c93ad1a534e1f6fc7e5bf4dae944eeb7032fd75;hb=adc92d6de2ee1c71e88b66fb9c41e52de8cf4ea1;hp=58d446db099c46f408a3c27d35bdfec99e249d40;hpb=4350a75b9455387c0da36b9b52349a858d8fca9c;p=ext%2Fsubsurface.git diff --git a/parse-xml.c b/parse-xml.c index 58d446d..6c93ad1 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -33,26 +33,30 @@ static void record_dive(struct dive *dive) dive_table.nr = nr+1; } -static void nonmatch(const char *type, const char *fullname, const char *name, char *buffer) +static void start_match(const char *type, const char *name, char *buffer) { - if (verbose > 1) - printf("Unable to match %s '(%.*s)%s' (%s)\n", type, - (int) (name - fullname), fullname, name, - buffer); - free(buffer); + if (verbose > 2) + printf("Matching %s '%s' (%s)\n", + type, name, buffer); } -static const char *last_part(const char *name) +static void nonmatch(const char *type, const char *name, char *buffer) { - const char *p = strrchr(name, '.'); - return p ? p+1 : name; + if (verbose > 1) + printf("Unable to match %s '%s' (%s)\n", + type, name, buffer); + free(buffer); } typedef void (*matchfn_t)(char *buffer, void *); -static int match(const char *pattern, const char *name, matchfn_t fn, char *buf, void *data) +static int match(const char *pattern, int plen, + const char *name, int nlen, + matchfn_t fn, char *buf, void *data) { - if (strcasecmp(pattern, name)) + if (plen > nlen) + return 0; + if (memcmp(pattern, name + nlen - plen, plen)) return 0; fn(buf, data); return 1; @@ -65,6 +69,8 @@ static int alloc_samples; static struct dive *dive; static struct sample *sample; static struct tm tm; +static int suunto; +static int event_index, gasmix_index; static time_t utc_mktime(struct tm *tm) { @@ -100,14 +106,25 @@ static void divedate(char *buffer, void *_when) { int d,m,y; time_t *when = _when; + int success = 0; + success = tm.tm_sec | tm.tm_min | tm.tm_hour; if (sscanf(buffer, "%d.%d.%d", &d, &m, &y) == 3) { tm.tm_year = y; tm.tm_mon = m-1; tm.tm_mday = d; - if (tm.tm_sec | tm.tm_min | tm.tm_hour) - *when = utc_mktime(&tm); + } else if (sscanf(buffer, "%d-%d-%d", &y, &m, &d) == 3) { + tm.tm_year = y; + tm.tm_mon = m-1; + tm.tm_mday = d; + } else { + fprintf(stderr, "Unable to parse date '%s'\n", buffer); + success = 0; } + + if (success) + *when = utc_mktime(&tm); + free(buffer); } @@ -191,7 +208,7 @@ static void pressure(char *buffer, void *_press) case FLOAT: /* Maybe it's in Bar? */ if (val.fp < 500.0) { - pressure->mbar = val.fp * 1000; + pressure->mbar = val.fp * 1000 + 0.5; break; } printf("Unknown fractional pressure reading %s\n", buffer); @@ -227,7 +244,7 @@ static void depth(char *buffer, void *_depth) val.fp = val.i; /* fallthrough */ case FLOAT: - depth->mm = val.fp * 1000; + depth->mm = val.fp * 1000 + 0.5; break; default: printf("Strange depth reading %s\n", buffer); @@ -251,7 +268,7 @@ static void temperature(char *buffer, void *_temperature) break; /* Celsius */ if (val.fp < 50.0) { - temperature->mkelvin = (val.fp + 273.16) * 1000; + temperature->mkelvin = (val.fp + 273.15) * 1000 + 0.5; break; } /* Fahrenheit */ @@ -296,71 +313,129 @@ static void duration(char *buffer, void *_time) sampletime(buffer, _time); } -static void ignore(char *buffer, void *_time) +static void percent(char *buffer, void *_fraction) { + fraction_t *fraction = _fraction; + union int_or_float val; + + switch (integer_or_float(buffer, &val)) { + /* C or F? Who knows? Let's default to Celsius */ + case INTEGER: + val.fp = val.i; + /* Fallthrough */ + case FLOAT: + if (val.fp <= 100.0) + fraction->permille = val.fp * 10 + 0.5; + break; + + default: + printf("Strange percentage reading %s\n", buffer); + break; + } + free(buffer); } +static void gasmix(char *buffer, void *_fraction) +{ + /* libdivecomputer does negative percentages. */ + if (*buffer == '-') + return; + if (gasmix_index < MAX_MIXES) + percent(buffer, _fraction); +} + +static void gasmix_nitrogen(char *buffer, void *_gasmix) +{ + /* Ignore n2 percentages. There's no value in them. */ +} + +#define MATCH(pattern, fn, dest) \ + match(pattern, strlen(pattern), name, len, fn, buf, dest) + /* We're in samples - try to convert the random xml value to something useful */ static void try_to_fill_sample(struct sample *sample, const char *name, char *buf) { - const char *last = last_part(name); + int len = strlen(name); - if (match("pressure", last, pressure, buf, &sample->tankpressure)) + start_match("sample", name, buf); + if (MATCH(".sample.pressure", pressure, &sample->tankpressure)) return; - if (match("cylpress", last, pressure, buf, &sample->tankpressure)) + if (MATCH(".sample.cylpress", pressure, &sample->tankpressure)) return; - if (match("depth", last, depth, buf, &sample->depth)) + if (MATCH(".sample.depth", depth, &sample->depth)) return; - if (match("temperature", last, temperature, buf, &sample->temperature)) + if (MATCH(".sample.temp", temperature, &sample->temperature)) return; - if (match("sampletime", last, sampletime, buf, &sample->time)) + if (MATCH(".sample.temperature", temperature, &sample->temperature)) return; - if (match("time", last, sampletime, buf, &sample->time)) + if (MATCH(".sample.sampletime", sampletime, &sample->time)) return; + if (MATCH(".sample.time", sampletime, &sample->time)) + return; + + nonmatch("sample", name, buf); +} - nonmatch("sample", name, last, buf); +/* + * Crazy suunto xml. Look at how those o2/he things match up. + */ +static int suunto_dive_match(struct dive *dive, const char *name, int len, char *buf) +{ + return MATCH(".o2pct", percent, &dive->gasmix[0].o2) || + MATCH(".hepct_0", percent, &dive->gasmix[0].he) || + MATCH(".o2pct_2", percent, &dive->gasmix[1].o2) || + MATCH(".hepct_1", percent, &dive->gasmix[1].he) || + MATCH(".o2pct_3", percent, &dive->gasmix[2].o2) || + MATCH(".hepct_2", percent, &dive->gasmix[2].he) || + MATCH(".o2pct_4", percent, &dive->gasmix[3].o2) || + MATCH(".hepct_3", percent, &dive->gasmix[3].he); } /* We're in the top-level dive xml. Try to convert whatever value to a dive value */ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf) { - const char *last = last_part(name); + int len = strlen(name); - if (match("date", last, divedate, buf, &dive->when)) + start_match("dive", name, buf); + if (MATCH(".date", divedate, &dive->when)) return; - if (match("time", last, divetime, buf, &dive->when)) + if (MATCH(".time", divetime, &dive->when)) return; - if (match("datetime", last, divedatetime, buf, &dive->when)) + if (MATCH(".datetime", divedatetime, &dive->when)) return; - if (match("maxdepth", last, depth, buf, &dive->maxdepth)) + if (MATCH(".maxdepth", depth, &dive->maxdepth)) return; - if (match("meandepth", last, depth, buf, &dive->meandepth)) + if (MATCH(".meandepth", depth, &dive->meandepth)) return; - if (match("divetime", last, duration, buf, &dive->duration)) + if (MATCH(".duration", duration, &dive->duration)) return; - if (match("divetimesec", last, duration, buf, &dive->duration)) + if (MATCH(".divetime", duration, &dive->duration)) return; - if (match("surfacetime", last, duration, buf, &dive->surfacetime)) + if (MATCH(".divetimesec", duration, &dive->duration)) return; - if (match("airtemp", last, temperature, buf, &dive->airtemp)) + if (MATCH(".surfacetime", duration, &dive->surfacetime)) return; - if (match("watertemp", last, temperature, buf, &dive->watertemp)) + if (MATCH(".airtemp", temperature, &dive->airtemp)) return; - if (match("cylinderstartpressure", last, pressure, buf, &dive->beginning_pressure)) + if (MATCH(".watertemp", temperature, &dive->watertemp)) return; - if (match("cylinderendpressure", last, pressure, buf, &dive->end_pressure)) + if (MATCH(".cylinderstartpressure", pressure, &dive->beginning_pressure)) return; - if (match("divenumber", last, ignore, buf, NULL)) + if (MATCH(".cylinderendpressure", pressure, &dive->end_pressure)) return; - if (match("diveseries", last, ignore, buf, NULL)) + + if (MATCH(".o2", gasmix, &dive->gasmix[gasmix_index].o2)) return; - if (match("number", last, ignore, buf, NULL)) + if (MATCH(".n2", gasmix_nitrogen, &dive->gasmix[gasmix_index])) return; - if (match("size", last, ignore, buf, NULL)) + if (MATCH(".he", gasmix, &dive->gasmix[gasmix_index].he)) return; - if (match("fingerprint", last, ignore, buf, NULL)) + + /* Suunto XML files are some crazy sh*t. */ + if (suunto && suunto_dive_match(dive, name, len, buf)) return; - nonmatch("dive", name, last, buf); + + nonmatch("dive", name, buf); } static unsigned int dive_size(int samples) @@ -412,14 +487,74 @@ static char *generate_name(struct dive *dive) return p; } +static void sanitize_gasmix(struct dive *dive) +{ + int i; + + for (i = 0; i < MAX_MIXES; i++) { + gasmix_t *mix = dive->gasmix+i; + unsigned int o2, he; + + o2 = mix->o2.permille; + he = mix->he.permille; + + /* Regular air: leave empty */ + if (!he) { + if (!o2) + continue; + /* 20.9% or 21% O2 is just air */ + if (o2 >= 209 && o2 <= 210) { + mix->o2.permille = 0; + continue; + } + } + + /* Sane mix? */ + if (o2 <= 1000 && he <= 1000 && o2+he <= 1000) + continue; + fprintf(stderr, "Odd gasmix: %d O2 %d He\n", o2, he); + memset(mix, 0, sizeof(*mix)); + } +} + static void dive_end(void) { if (!dive) return; if (!dive->name) dive->name = generate_name(dive); + sanitize_gasmix(dive); record_dive(dive); dive = NULL; + gasmix_index = 0; +} + +static void suunto_start(void) +{ + suunto++; +} + +static void suunto_end(void) +{ + suunto--; +} + +static void event_start(void) +{ +} + +static void event_end(void) +{ + event_index++; +} + +static void gasmix_start(void) +{ +} + +static void gasmix_end(void) +{ + gasmix_index++; } static void sample_start(void) @@ -440,6 +575,7 @@ static void sample_start(void) } sample = dive->sample + nr; memset(sample, 0, sizeof(*sample)); + event_index = 0; } static void sample_end(void) @@ -538,35 +674,58 @@ static void visit_one_node(xmlNode *node) entry(name, len, content); } -static void traverse(xmlNode *node) +static void traverse(xmlNode *root); + +static void traverse_properties(xmlNode *node) +{ + xmlAttr *p; + + for (p = node->properties; p; p = p->next) + traverse(p->children); +} + +static void visit(xmlNode *n) +{ + visit_one_node(n); + traverse_properties(n); + traverse(n->children); +} + +/* + * I'm sure this could be done as some fancy DTD rules. + * It's just not worth the headache. + */ +static struct nesting { + const char *name; + void (*start)(void), (*end)(void); +} nesting[] = { + { "dive", dive_start, dive_end }, + { "SUUNTO", suunto_start, suunto_end }, + { "sample", sample_start, sample_end }, + { "SAMPLE", sample_start, sample_end }, + { "event", event_start, event_end }, + { "gasmix", gasmix_start, gasmix_end }, + { NULL, } +}; + +static void traverse(xmlNode *root) { xmlNode *n; - for (n = node; n; n = n->next) { - /* XML from libdivecomputer: 'dive' per new dive */ - if (!strcmp(n->name, "dive")) { - dive_start(); - traverse(n->children); - dive_end(); - continue; - } + for (n = root; n; n = n->next) { + struct nesting *rule = nesting; - /* - * At least both libdivecomputer and Suunto - * agree on "sample". - * - * Well - almost. Ignore case. - */ - if (!strcasecmp(n->name, "sample")) { - sample_start(); - traverse(n->children); - sample_end(); - continue; - } + do { + if (!strcmp(rule->name, n->name)) + break; + rule++; + } while (rule->name); - /* Anything else - just visit it and recurse */ - visit_one_node(n); - traverse(n->children); + if (rule->start) + rule->start(); + visit(n); + if (rule->end) + rule->end(); } }