]> git.tdb.fi Git - ext/subsurface.git/blobdiff - parse-xml.c
Start "output unit management" support
[ext/subsurface.git] / parse-xml.c
index 90fdb1ad1ea751fe29cf0045e1acc8f4f4e41b7f..92da468f44a2f946bdada7e310a924f6141e931b 100644 (file)
@@ -62,21 +62,16 @@ static int match(const char *pattern, int plen,
        return 1;
 }
 
+
+struct units input_units;
+
 /*
- * We keep our internal data in well-specified units, but
- * the input may come in some random format. This keeps track
- * of the incoming units.
+ * We're going to default to SI units for input. Yes,
+ * technically the SI unit for pressure is Pascal, but
+ * we default to bar (10^5 pascal), which people
+ * actually use. Similarly, C instead of Kelvin.
  */
-static struct units {
-       enum { METERS, FEET } length;
-       enum { LITER, CUFT } volume;
-       enum { BAR, PSI } pressure;
-       enum { CELSIUS, FAHRENHEIT } temperature;
-       enum { KG, LBS } weight;
-} units;
-
-/* We're going to default to SI units for input */
-static const struct units SI_units = {
+const struct units SI_units = {
        .length = METERS,
        .volume = LITER,
        .pressure = BAR,
@@ -84,6 +79,14 @@ static const struct units SI_units = {
        .weight = KG
 };
 
+const struct units IMPERIAL_units = {
+       .length = FEET,
+       .volume = CUFT,
+       .pressure = PSI,
+       .temperature = FAHRENHEIT,
+       .weight = LBS
+};
+
 /*
  * Dive info as it is being built up..
  */
@@ -99,6 +102,7 @@ static enum import_source {
        SUUNTO,
        UEMIS,
        DIVINGLOG,
+       UDDF,
 } import_source;
 
 static time_t utc_mktime(struct tm *tm)
@@ -237,7 +241,10 @@ static void pressure(char *buffer, void *_press)
                /* Just ignore zero values */
                if (!val.fp)
                        break;
-               switch (units.pressure) {
+               switch (input_units.pressure) {
+               case PASCAL:
+                       mbar = val.fp / 100;
+                       break;
                case BAR:
                        /* Assume mbar, but if it's really small, it's bar */
                        mbar = val.fp;
@@ -266,7 +273,7 @@ static void depth(char *buffer, void *_depth)
 
        switch (integer_or_float(buffer, &val)) {
        case FLOAT:
-               switch (units.length) {
+               switch (input_units.length) {
                case METERS:
                        depth->mm = val.fp * 1000 + 0.5;
                        break;
@@ -292,7 +299,10 @@ static void temperature(char *buffer, void *_temperature)
                if (!val.fp)
                        break;
                /* Celsius */
-               switch (units.temperature) {
+               switch (input_units.temperature) {
+               case KELVIN:
+                       temperature->mkelvin = val.fp * 1000;
+                       break;
                case CELSIUS:
                        temperature->mkelvin = (val.fp + 273.15) * 1000 + 0.5;
                        break;
@@ -503,6 +513,14 @@ static int divinglog_fill_sample(struct sample *sample, const char *name, int le
                0;
 }
 
+static int uddf_fill_sample(struct sample *sample, const char *name, int len, char *buf)
+{
+       return  MATCH(".divetime", sampletime, &sample->time) ||
+               MATCH(".depth", depth, &sample->depth) ||
+               MATCH(".temperature", temperature, &sample->temperature) ||
+               0;
+}
+
 /* 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)
 {
@@ -535,6 +553,11 @@ static void try_to_fill_sample(struct sample *sample, const char *name, char *bu
                        return;
                break;
 
+       case UDDF:
+               if (uddf_fill_sample(sample, name, len, buf))
+                       return;
+               break;
+
        default:
                break;
        }
@@ -607,29 +630,29 @@ static int buffer_value(char *buffer)
 
 static void uemis_length_unit(char *buffer, void *_unused)
 {
-       units.length = buffer_value(buffer) ? FEET : METERS;
+       input_units.length = buffer_value(buffer) ? FEET : METERS;
 }
 
 static void uemis_volume_unit(char *buffer, void *_unused)
 {
-       units.volume = buffer_value(buffer) ? CUFT : LITER;
+       input_units.volume = buffer_value(buffer) ? CUFT : LITER;
 }
 
 static void uemis_pressure_unit(char *buffer, void *_unused)
 {
 #if 0
-       units.pressure = buffer_value(buffer) ? PSI : BAR;
+       input_units.pressure = buffer_value(buffer) ? PSI : BAR;
 #endif
 }
 
 static void uemis_temperature_unit(char *buffer, void *_unused)
 {
-       units.temperature = buffer_value(buffer) ? FAHRENHEIT : CELSIUS;
+       input_units.temperature = buffer_value(buffer) ? FAHRENHEIT : CELSIUS;
 }
 
 static void uemis_weight_unit(char *buffer, void *_unused)
 {
-       units.weight = buffer_value(buffer) ? LBS : KG;
+       input_units.weight = buffer_value(buffer) ? LBS : KG;
 }
 
 static void uemis_time_unit(char *buffer, void *_unused)
@@ -648,7 +671,7 @@ static void uemis_date_time(char *buffer, void *_when)
 
         switch (integer_or_float(buffer, &val)) {
         case FLOAT:
-               *when = (val.fp - 40587.5) * 86400;
+               *when = (val.fp - 40587) * 86400;
                break;
        default:
                fprintf(stderr, "Strange julian date: %s", buffer);
@@ -665,10 +688,14 @@ static void uemis_date_time(char *buffer, void *_when)
  */
 static void uemis_time_zone(char *buffer, void *_when)
 {
+#if 0 /* seems like this is only used to display it correctly
+       * the stored time appears to be UTC */
+
        time_t *when = _when;
        signed char tz = atoi(buffer);
 
        *when += tz * 3600;
+#endif
 }
 
 /* 0 - air ; 1 - nitrox1 ; 2 - nitrox2 ; 3 = nitrox3 */
@@ -737,13 +764,13 @@ static void uemis_percent(char *buffer, void *_cylinder)
 
 static int uemis_dive_match(struct dive *dive, const char *name, int len, char *buf)
 {
-       return  MATCH(".units.length", uemis_length_unit, &units) ||
-               MATCH(".units.volume", uemis_volume_unit, &units) ||
-               MATCH(".units.pressure", uemis_pressure_unit, &units) ||
-               MATCH(".units.temperature", uemis_temperature_unit, &units) ||
-               MATCH(".units.weight", uemis_weight_unit, &units) ||
-               MATCH(".units.time", uemis_time_unit, &units) ||
-               MATCH(".units.date", uemis_date_unit, &units) ||
+       return  MATCH(".units.length", uemis_length_unit, &input_units) ||
+               MATCH(".units.volume", uemis_volume_unit, &input_units) ||
+               MATCH(".units.pressure", uemis_pressure_unit, &input_units) ||
+               MATCH(".units.temperature", uemis_temperature_unit, &input_units) ||
+               MATCH(".units.weight", uemis_weight_unit, &input_units) ||
+               MATCH(".units.time", uemis_time_unit, &input_units) ||
+               MATCH(".units.date", uemis_date_unit, &input_units) ||
                MATCH(".date_time", uemis_date_time, &dive->when) ||
                MATCH(".time_zone", uemis_time_zone, &dive->when) ||
                MATCH(".ambient.temperature", decicelsius, &dive->airtemp) ||
@@ -765,6 +792,58 @@ static int uemis_dive_match(struct dive *dive, const char *name, int len, char *
                0;
 }
 
+/*
+ * Uddf specifies ISO 8601 time format.
+ *
+ * There are many variations on that. This handles the useful cases.
+ */
+static void uddf_datetime(char *buffer, void *_when)
+{
+       char c;
+       int y,m,d,hh,mm,ss;
+       time_t *when = _when;
+       struct tm tm = { 0 };
+       int i;
+
+       i = sscanf(buffer, "%d-%d-%d%c%d:%d:%d", &y, &m, &d, &c, &hh, &mm, &ss);
+       if (i == 7)
+               goto success;
+       ss = 0;
+       if (i == 6)
+               goto success;
+
+       i = sscanf(buffer, "%04d%02d%02d%c%02d%02d%02d", &y, &m, &d, &c, &hh, &mm, &ss);
+       if (i == 7)
+               goto success;
+       ss = 0;
+       if (i == 6)
+               goto success;
+bad_date:
+       printf("Bad date time %s\n", buffer);
+       free(buffer);
+       return;
+
+success:
+       if (c != 'T' && c != ' ')
+               goto bad_date;
+       tm.tm_year = y;
+       tm.tm_mon = m - 1;
+       tm.tm_mday = d;
+       tm.tm_hour = hh;
+       tm.tm_min = mm;
+       tm.tm_sec = ss;
+       *when = utc_mktime(&tm);
+       free(buffer);
+}
+
+static int uddf_dive_match(struct dive *dive, const char *name, int len, char *buf)
+{
+       return  MATCH(".datetime", uddf_datetime, &dive->when) ||
+               MATCH(".diveduration", duration, &dive->duration) ||
+               MATCH(".greatestdepth", depth, &dive->maxdepth) ||
+               0;
+}
+
 /* 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)
 {
@@ -788,6 +867,11 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf)
                        return;
                break;
 
+       case UDDF:
+               if (uddf_dive_match(dive, name, len, buf))
+                       return;
+               break;
+
        default:
                break;
        }
@@ -1143,13 +1227,13 @@ static void visit(xmlNode *n)
 static void suunto_importer(void)
 {
        import_source = SUUNTO;
-       units = SI_units;
+       input_units = SI_units;
 }
 
 static void uemis_importer(void)
 {
        import_source = UEMIS;
-       units = SI_units;
+       input_units = SI_units;
 }
 
 static void DivingLog_importer(void)
@@ -1163,8 +1247,16 @@ static void DivingLog_importer(void)
         * when they are in Fahrenheit. Depths are in
         * meters, but pressure is in PSI.
         */
-       units = SI_units;
-       units.pressure = PSI;
+       input_units = SI_units;
+       input_units.pressure = PSI;
+}
+
+static void uddf_importer(void)
+{
+       import_source = UDDF;
+       input_units = SI_units;
+       input_units.pressure = PASCAL;
+       input_units.temperature = KELVIN;
 }
 
 /*
@@ -1178,6 +1270,7 @@ static struct nesting {
        { "dive", dive_start, dive_end },
        { "Dive", dive_start, dive_end },
        { "sample", sample_start, sample_end },
+       { "waypoint", sample_start, sample_end },
        { "SAMPLE", sample_start, sample_end },
        { "reading", sample_start, sample_end },
        { "event", event_start, event_end },
@@ -1189,6 +1282,7 @@ static struct nesting {
        { "SUUNTO", suunto_importer },
        { "Divinglog", DivingLog_importer },
        { "pre_dive", uemis_importer },
+       { "uddf", uddf_importer },
 
        { NULL, }
 };
@@ -1224,7 +1318,7 @@ static void reset_all(void)
         * data within one file, we might have to reset it per
         * dive for that format.
         */
-       units = SI_units;
+       input_units = SI_units;
        import_source = UNKNOWN;
 }