]> git.tdb.fi Git - ext/subsurface.git/blob - parse-xml.c
Drop surface events when reading from an XML file too
[ext/subsurface.git] / parse-xml.c
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <errno.h>
6 #include <time.h>
7 #include <libxml/parser.h>
8 #include <libxml/tree.h>
9
10 #include "dive.h"
11
12 int verbose;
13
14 struct dive_table dive_table;
15
16 /*
17  * Add a dive into the dive_table array
18  */
19 void record_dive(struct dive *dive)
20 {
21         int nr = dive_table.nr, allocated = dive_table.allocated;
22         struct dive **dives = dive_table.dives;
23
24         if (nr >= allocated) {
25                 allocated = (nr + 32) * 3 / 2;
26                 dives = realloc(dives, allocated * sizeof(struct dive *));
27                 if (!dives)
28                         exit(1);
29                 dive_table.dives = dives;
30                 dive_table.allocated = allocated;
31         }
32         dives[nr] = fixup_dive(dive);
33         dive_table.nr = nr+1;
34 }
35
36 static void start_match(const char *type, const char *name, char *buffer)
37 {
38         if (verbose > 2)
39                 printf("Matching %s '%s' (%s)\n",
40                         type, name, buffer);
41 }
42
43 static void nonmatch(const char *type, const char *name, char *buffer)
44 {
45         if (verbose > 1)
46                 printf("Unable to match %s '%s' (%s)\n",
47                         type, name, buffer);
48         free(buffer);
49 }
50
51 typedef void (*matchfn_t)(char *buffer, void *);
52
53 static int match(const char *pattern, int plen,
54                  const char *name, int nlen,
55                  matchfn_t fn, char *buf, void *data)
56 {
57         if (plen > nlen)
58                 return 0;
59         if (memcmp(pattern, name + nlen - plen, plen))
60                 return 0;
61         fn(buf, data);
62         return 1;
63 }
64
65
66 struct units input_units;
67
68 /*
69  * We're going to default to SI units for input. Yes,
70  * technically the SI unit for pressure is Pascal, but
71  * we default to bar (10^5 pascal), which people
72  * actually use. Similarly, C instead of Kelvin.
73  */
74 const struct units SI_units = {
75         .length = METERS,
76         .volume = LITER,
77         .pressure = BAR,
78         .temperature = CELSIUS,
79         .weight = KG
80 };
81
82 const struct units IMPERIAL_units = {
83         .length = FEET,
84         .volume = CUFT,
85         .pressure = PSI,
86         .temperature = FAHRENHEIT,
87         .weight = LBS
88 };
89
90 /*
91  * Dive info as it is being built up..
92  */
93 static struct dive *dive;
94 static struct sample *sample;
95 static struct {
96         int active;
97         duration_t time;
98         int type, flags, value;
99         const char *name;
100 } event;
101 static struct tm tm;
102 static int cylinder_index;
103
104 static enum import_source {
105         UNKNOWN,
106         LIBDIVECOMPUTER,
107         SUUNTO,
108         UEMIS,
109         DIVINGLOG,
110         UDDF,
111 } import_source;
112
113 time_t utc_mktime(struct tm *tm)
114 {
115         static const int mdays[] = {
116             0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
117         };
118         int year = tm->tm_year;
119         int month = tm->tm_mon;
120         int day = tm->tm_mday;
121
122         /* First normalize relative to 1900 */
123         if (year < 70)
124                 year += 100;
125         else if (year > 1900)
126                 year -= 1900;
127
128         /* Normalized to Jan 1, 1970: unix time */
129         year -= 70;
130
131         if (year < 0 || year > 129) /* algo only works for 1970-2099 */
132                 return -1;
133         if (month < 0 || month > 11) /* array bounds */
134                 return -1;
135         if (month < 2 || (year + 2) % 4)
136                 day--;
137         if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
138                 return -1;
139         return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
140                 tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
141 }
142
143 static void divedate(char *buffer, void *_when)
144 {
145         int d,m,y;
146         time_t *when = _when;
147         int success = 0;
148
149         success = tm.tm_sec | tm.tm_min | tm.tm_hour;
150         if (sscanf(buffer, "%d.%d.%d", &d, &m, &y) == 3) {
151                 tm.tm_year = y;
152                 tm.tm_mon = m-1;
153                 tm.tm_mday = d;
154         } else if (sscanf(buffer, "%d-%d-%d", &y, &m, &d) == 3) {
155                 tm.tm_year = y;
156                 tm.tm_mon = m-1;
157                 tm.tm_mday = d;
158         } else {
159                 fprintf(stderr, "Unable to parse date '%s'\n", buffer);
160                 success = 0;
161         }
162
163         if (success)
164                 *when = utc_mktime(&tm);
165
166         free(buffer);
167 }
168
169 static void divetime(char *buffer, void *_when)
170 {
171         int h,m,s = 0;
172         time_t *when = _when;
173
174         if (sscanf(buffer, "%d:%d:%d", &h, &m, &s) >= 2) {
175                 tm.tm_hour = h;
176                 tm.tm_min = m;
177                 tm.tm_sec = s;
178                 if (tm.tm_year)
179                         *when = utc_mktime(&tm);
180         }
181         free(buffer);
182 }
183
184 /* Libdivecomputer: "2011-03-20 10:22:38" */
185 static void divedatetime(char *buffer, void *_when)
186 {
187         int y,m,d;
188         int hr,min,sec;
189         time_t *when = _when;
190
191         if (sscanf(buffer, "%d-%d-%d %d:%d:%d",
192                 &y, &m, &d, &hr, &min, &sec) == 6) {
193                 tm.tm_year = y;
194                 tm.tm_mon = m-1;
195                 tm.tm_mday = d;
196                 tm.tm_hour = hr;
197                 tm.tm_min = min;
198                 tm.tm_sec = sec;
199                 *when = utc_mktime(&tm);
200         }
201         free(buffer);
202 }
203
204 union int_or_float {
205         double fp;
206 };
207
208 enum number_type {
209         NEITHER,
210         FLOAT
211 };
212
213 static enum number_type integer_or_float(char *buffer, union int_or_float *res)
214 {
215         char *end;
216         long val;
217         double fp;
218
219         /* Integer or floating point? */
220         val = strtol(buffer, &end, 10);
221         if (val < 0 || end == buffer)
222                 return NEITHER;
223
224         /* Looks like it might be floating point? */
225         if (*end == '.') {
226                 errno = 0;
227                 fp = strtod(buffer, &end);
228                 if (!errno) {
229                         res->fp = fp;
230                         return FLOAT;
231                 }
232         }
233
234         res->fp = val;
235         return FLOAT;
236 }
237
238 static void pressure(char *buffer, void *_press)
239 {
240         double mbar;
241         pressure_t *pressure = _press;
242         union int_or_float val;
243
244         switch (integer_or_float(buffer, &val)) {
245         case FLOAT:
246                 /* Just ignore zero values */
247                 if (!val.fp)
248                         break;
249                 switch (input_units.pressure) {
250                 case PASCAL:
251                         mbar = val.fp / 100;
252                         break;
253                 case BAR:
254                         /* Assume mbar, but if it's really small, it's bar */
255                         mbar = val.fp;
256                         if (mbar < 5000)
257                                 mbar = mbar * 1000;
258                         break;
259                 case PSI:
260                         mbar = val.fp * 68.95;
261                         break;
262                 }
263                 if (mbar > 5 && mbar < 500000) {
264                         pressure->mbar = mbar + 0.5;
265                         break;
266                 }
267         /* fallthrough */
268         default:
269                 printf("Strange pressure reading %s\n", buffer);
270         }
271         free(buffer);
272 }
273
274 static void depth(char *buffer, void *_depth)
275 {
276         depth_t *depth = _depth;
277         union int_or_float val;
278
279         switch (integer_or_float(buffer, &val)) {
280         case FLOAT:
281                 switch (input_units.length) {
282                 case METERS:
283                         depth->mm = val.fp * 1000 + 0.5;
284                         break;
285                 case FEET:
286                         depth->mm = val.fp * 304.8 + 0.5;
287                         break;
288                 }
289                 break;
290         default:
291                 printf("Strange depth reading %s\n", buffer);
292         }
293         free(buffer);
294 }
295
296 static void temperature(char *buffer, void *_temperature)
297 {
298         temperature_t *temperature = _temperature;
299         union int_or_float val;
300
301         switch (integer_or_float(buffer, &val)) {
302         case FLOAT:
303                 /* Ignore zero. It means "none" */
304                 if (!val.fp)
305                         break;
306                 /* Celsius */
307                 switch (input_units.temperature) {
308                 case KELVIN:
309                         temperature->mkelvin = val.fp * 1000;
310                         break;
311                 case CELSIUS:
312                         temperature->mkelvin = (val.fp + 273.15) * 1000 + 0.5;
313                         break;
314                 case FAHRENHEIT:
315                         temperature->mkelvin = (val.fp + 459.67) * 5000/9;
316                         break;
317                 }
318                 break;
319         default:
320                 printf("Strange temperature reading %s\n", buffer);
321         }
322         free(buffer);
323 }
324
325 static void sampletime(char *buffer, void *_time)
326 {
327         int i;
328         int min, sec;
329         duration_t *time = _time;
330
331         i = sscanf(buffer, "%d:%d", &min, &sec);
332         switch (i) {
333         case 1:
334                 sec = min;
335                 min = 0;
336         /* fallthrough */
337         case 2:
338                 time->seconds = sec + min*60;
339                 break;
340         default:
341                 printf("Strange sample time reading %s\n", buffer);
342         }
343         free(buffer);
344 }
345
346 static void duration(char *buffer, void *_time)
347 {
348         sampletime(buffer, _time);
349 }
350
351 static void percent(char *buffer, void *_fraction)
352 {
353         fraction_t *fraction = _fraction;
354         union int_or_float val;
355
356         switch (integer_or_float(buffer, &val)) {
357         case FLOAT:
358                 if (val.fp <= 100.0)
359                         fraction->permille = val.fp * 10 + 0.5;
360                 break;
361
362         default:
363                 printf("Strange percentage reading %s\n", buffer);
364                 break;
365         }
366         free(buffer);
367 }
368
369 static void gasmix(char *buffer, void *_fraction)
370 {
371         /* libdivecomputer does negative percentages. */
372         if (*buffer == '-')
373                 return;
374         if (cylinder_index < MAX_CYLINDERS)
375                 percent(buffer, _fraction);
376 }
377
378 static void gasmix_nitrogen(char *buffer, void *_gasmix)
379 {
380         /* Ignore n2 percentages. There's no value in them. */
381 }
382
383 static void cylindersize(char *buffer, void *_volume)
384 {
385         volume_t *volume = _volume;
386         union int_or_float val;
387
388         switch (integer_or_float(buffer, &val)) {
389         case FLOAT:
390                 volume->mliter = val.fp * 1000 + 0.5;
391                 break;
392
393         default:
394                 printf("Strange volume reading %s\n", buffer);
395                 break;
396         }
397         free(buffer);
398 }
399
400 static void utf8_string(char *buffer, void *_res)
401 {
402         *(char **)_res = buffer;
403 }
404
405 /*
406  * Uemis water_pressure. In centibar. And when converting to
407  * depth, I'm just going to always use saltwater, because I
408  * think "true depth" is just stupid. From a diving standpoint,
409  * "true depth" is pretty much completely pointless, unless
410  * you're doing some kind of underwater surveying work.
411  *
412  * So I give water depths in "pressure depth", always assuming
413  * salt water. So one atmosphere per 10m.
414  */
415 static void water_pressure(char *buffer, void *_depth)
416 {
417         depth_t *depth = _depth;
418         union int_or_float val;
419         double atm, cm;
420
421         switch (integer_or_float(buffer, &val)) {
422         case FLOAT:
423                 if (!val.fp)
424                         break;
425                 /* cbar to atm */
426                 atm = (val.fp / 100) / 1.01325;
427                 /*
428                  * atm to cm. Why not mm? The precision just isn't
429                  * there.
430                  */
431                 cm = 100 * atm + 0.5;
432                 if (cm > 0) {
433                         depth->mm = 10 * (long)cm;
434                         break;
435                 }
436         default:
437                 fprintf(stderr, "Strange water pressure '%s'\n", buffer);
438         }
439         free(buffer);
440 }
441
442 #define MATCH(pattern, fn, dest) \
443         match(pattern, strlen(pattern), name, len, fn, buf, dest)
444
445 static void get_index(char *buffer, void *_i)
446 {
447         int *i = _i;
448         *i = atoi(buffer);
449         free(buffer);
450 }
451
452 static void centibar(char *buffer, void *_pressure)
453 {
454         pressure_t *pressure = _pressure;
455         union int_or_float val;
456
457         switch (integer_or_float(buffer, &val)) {
458         case FLOAT:
459                 pressure->mbar = val.fp * 10 + 0.5;
460                 break;
461         default:
462                 fprintf(stderr, "Strange centibar pressure '%s'\n", buffer);
463         }
464         free(buffer);
465 }
466
467 static void decicelsius(char *buffer, void *_temp)
468 {
469         temperature_t *temp = _temp;
470         union int_or_float val;
471
472         switch (integer_or_float(buffer, &val)) {
473         case FLOAT:
474                 temp->mkelvin = (val.fp/10 + 273.15) * 1000 + 0.5;
475                 break;
476         default:
477                 fprintf(stderr, "Strange julian date: %s", buffer);
478         }
479         free(buffer);
480 }
481
482 static int uemis_fill_sample(struct sample *sample, const char *name, int len, char *buf)
483 {
484         return  MATCH(".reading.dive_time", sampletime, &sample->time) ||
485                 MATCH(".reading.water_pressure", water_pressure, &sample->depth) ||
486                 MATCH(".reading.active_tank", get_index, &sample->cylinderindex) ||
487                 MATCH(".reading.tank_pressure", centibar, &sample->cylinderpressure) ||
488                 MATCH(".reading.dive_temperature", decicelsius, &sample->temperature) ||
489                 0;
490 }
491
492 /*
493  * Divinglog is crazy. The temperatures are in celsius. EXCEPT
494  * for the sample temperatures, that are in Fahrenheit.
495  * WTF?
496  *
497  * Oh, and I think Diving Log *internally* probably kept them
498  * in celsius, because I'm seeing entries like
499  *
500  *      <Temp>32.0</Temp>
501  *
502  * in there. Which is freezing, aka 0 degC. I bet the "0" is
503  * what Diving Log uses for "no temperature".
504  *
505  * So throw away crap like that.
506  */
507 static void fahrenheit(char *buffer, void *_temperature)
508 {
509         temperature_t *temperature = _temperature;
510         union int_or_float val;
511
512         switch (integer_or_float(buffer, &val)) {
513         case FLOAT:
514                 /* Floating point equality is evil, but works for small integers */
515                 if (val.fp == 32.0)
516                         break;
517                 temperature->mkelvin = (val.fp + 459.67) * 5000/9;
518                 break;
519         default:
520                 fprintf(stderr, "Crazy Diving Log temperature reading %s\n", buffer);
521         }
522         free(buffer);
523 }
524
525 /*
526  * Did I mention how bat-shit crazy divinglog is? The sample
527  * pressures are in PSI. But the tank working pressure is in
528  * bar. WTF^2?
529  *
530  * Crazy stuff like this is why subsurface has everything in
531  * these inconvenient typed structures, and you have to say
532  * "pressure->mbar" to get the actual value. Exactly so that
533  * you can never have unit confusion.
534  */
535 static void psi(char *buffer, void *_pressure)
536 {
537         pressure_t *pressure = _pressure;
538         union int_or_float val;
539
540         switch (integer_or_float(buffer, &val)) {
541         case FLOAT:
542                 pressure->mbar = val.fp * 68.95 + 0.5;
543                 break;
544         default:
545                 fprintf(stderr, "Crazy Diving Log PSI reading %s\n", buffer);
546         }
547         free(buffer);
548 }
549
550 static int divinglog_fill_sample(struct sample *sample, const char *name, int len, char *buf)
551 {
552         return  MATCH(".p.time", sampletime, &sample->time) ||
553                 MATCH(".p.depth", depth, &sample->depth) ||
554                 MATCH(".p.temp", fahrenheit, &sample->temperature) ||
555                 MATCH(".p.press1", psi, &sample->cylinderpressure) ||
556                 0;
557 }
558
559 static int uddf_fill_sample(struct sample *sample, const char *name, int len, char *buf)
560 {
561         return  MATCH(".divetime", sampletime, &sample->time) ||
562                 MATCH(".depth", depth, &sample->depth) ||
563                 MATCH(".temperature", temperature, &sample->temperature) ||
564                 0;
565 }
566
567 static void eventtime(char *buffer, void *_duration)
568 {
569         duration_t *duration = _duration;
570         sampletime(buffer, duration);
571         if (sample)
572                 duration->seconds += sample->time.seconds;
573 }
574
575 static void try_to_fill_event(const char *name, char *buf)
576 {
577         int len = strlen(name);
578
579         start_match("event", name, buf);
580         if (MATCH(".event", utf8_string, &event.name))
581                 return;
582         if (MATCH(".name", utf8_string, &event.name))
583                 return;
584         if (MATCH(".time", eventtime, &event.time))
585                 return;
586         if (MATCH(".type", get_index, &event.type))
587                 return;
588         if (MATCH(".flags", get_index, &event.flags))
589                 return;
590         nonmatch("event", name, buf);
591 }
592
593 /* We're in samples - try to convert the random xml value to something useful */
594 static void try_to_fill_sample(struct sample *sample, const char *name, char *buf)
595 {
596         int len = strlen(name);
597
598         start_match("sample", name, buf);
599         if (MATCH(".sample.pressure", pressure, &sample->cylinderpressure))
600                 return;
601         if (MATCH(".sample.cylpress", pressure, &sample->cylinderpressure))
602                 return;
603         if (MATCH(".sample.depth", depth, &sample->depth))
604                 return;
605         if (MATCH(".sample.temp", temperature, &sample->temperature))
606                 return;
607         if (MATCH(".sample.temperature", temperature, &sample->temperature))
608                 return;
609         if (MATCH(".sample.sampletime", sampletime, &sample->time))
610                 return;
611         if (MATCH(".sample.time", sampletime, &sample->time))
612                 return;
613
614         switch (import_source) {
615         case UEMIS:
616                 if (uemis_fill_sample(sample, name, len, buf))
617                         return;
618                 break;
619
620         case DIVINGLOG:
621                 if (divinglog_fill_sample(sample, name, len, buf))
622                         return;
623                 break;
624
625         case UDDF:
626                 if (uddf_fill_sample(sample, name, len, buf))
627                         return;
628                 break;
629
630         default:
631                 break;
632         }
633
634         nonmatch("sample", name, buf);
635 }
636
637 /*
638  * Crazy suunto xml. Look at how those o2/he things match up.
639  */
640 static int suunto_dive_match(struct dive *dive, const char *name, int len, char *buf)
641 {
642         return  MATCH(".o2pct", percent, &dive->cylinder[0].gasmix.o2) ||
643                 MATCH(".hepct_0", percent, &dive->cylinder[0].gasmix.he) ||
644                 MATCH(".o2pct_2", percent, &dive->cylinder[1].gasmix.o2) ||
645                 MATCH(".hepct_1", percent, &dive->cylinder[1].gasmix.he) ||
646                 MATCH(".o2pct_3", percent, &dive->cylinder[2].gasmix.o2) ||
647                 MATCH(".hepct_2", percent, &dive->cylinder[2].gasmix.he) ||
648                 MATCH(".o2pct_4", percent, &dive->cylinder[3].gasmix.o2) ||
649                 MATCH(".hepct_3", percent, &dive->cylinder[3].gasmix.he) ||
650                 MATCH(".cylindersize", cylindersize, &dive->cylinder[0].type.size) ||
651                 MATCH(".cylinderworkpressure", pressure, &dive->cylinder[0].type.workingpressure) ||
652                 0;
653 }
654
655 static const char *country, *city;
656
657 static void divinglog_place(char *place, void *_location)
658 {
659         char **location = _location;
660         char buffer[256], *p;
661         int len;
662
663         len = snprintf(buffer, sizeof(buffer),
664                 "%s%s%s%s%s",
665                 place,
666                 city ? ", " : "",
667                 city ? city : "",
668                 country ? ", " : "",
669                 country ? country : "");
670
671         p = malloc(len+1);
672         memcpy(p, buffer, len+1);
673         *location = p;
674
675         city = NULL;
676         country = NULL;
677 }
678
679 static int divinglog_dive_match(struct dive *dive, const char *name, int len, char *buf)
680 {
681         return  MATCH(".divedate", divedate, &dive->when) ||
682                 MATCH(".entrytime", divetime, &dive->when) ||
683                 MATCH(".depth", depth, &dive->maxdepth) ||
684                 MATCH(".tanksize", cylindersize, &dive->cylinder[0].type.size) ||
685                 MATCH(".presw", pressure, &dive->cylinder[0].type.workingpressure) ||
686                 MATCH(".comments", utf8_string, &dive->notes) ||
687                 MATCH(".buddy.names", utf8_string, &dive->buddy) ||
688                 MATCH(".country.name", utf8_string, &country) ||
689                 MATCH(".city.name", utf8_string, &city) ||
690                 MATCH(".place.name", divinglog_place, &dive->location) ||
691                 0;
692 }
693
694 static int buffer_value(char *buffer)
695 {
696         int val = atoi(buffer);
697         free(buffer);
698         return val;
699 }
700
701 static void uemis_length_unit(char *buffer, void *_unused)
702 {
703         input_units.length = buffer_value(buffer) ? FEET : METERS;
704 }
705
706 static void uemis_volume_unit(char *buffer, void *_unused)
707 {
708         input_units.volume = buffer_value(buffer) ? CUFT : LITER;
709 }
710
711 static void uemis_pressure_unit(char *buffer, void *_unused)
712 {
713 #if 0
714         input_units.pressure = buffer_value(buffer) ? PSI : BAR;
715 #endif
716 }
717
718 static void uemis_temperature_unit(char *buffer, void *_unused)
719 {
720         input_units.temperature = buffer_value(buffer) ? FAHRENHEIT : CELSIUS;
721 }
722
723 static void uemis_weight_unit(char *buffer, void *_unused)
724 {
725         input_units.weight = buffer_value(buffer) ? LBS : KG;
726 }
727
728 static void uemis_time_unit(char *buffer, void *_unused)
729 {
730 }
731
732 static void uemis_date_unit(char *buffer, void *_unused)
733 {
734 }
735
736 /* Modified julian day, yay! */
737 static void uemis_date_time(char *buffer, void *_when)
738 {
739         time_t *when = _when;
740         union int_or_float val;
741
742         switch (integer_or_float(buffer, &val)) {
743         case FLOAT:
744                 *when = (val.fp - 40587) * 86400;
745                 break;
746         default:
747                 fprintf(stderr, "Strange julian date: %s", buffer);
748         }
749         free(buffer);
750 }
751
752 /*
753  * Uemis doesn't know time zones. You need to do them as
754  * minutes, not hours.
755  *
756  * But that's ok, we don't track timezones yet either. We
757  * just turn everything into "localtime expressed as UTC".
758  */
759 static void uemis_time_zone(char *buffer, void *_when)
760 {
761 #if 0 /* seems like this is only used to display it correctly
762        * the stored time appears to be UTC */
763
764         time_t *when = _when;
765         signed char tz = atoi(buffer);
766
767         *when += tz * 3600;
768 #endif
769 }
770
771 /* 0 - air ; 1 - nitrox1 ; 2 - nitrox2 ; 3 = nitrox3 */
772 static int uemis_gas_template;
773
774 /*
775  * Christ. Uemis tank data is a total mess.
776  *
777  * We're passed a "virtual cylinder" (0 - 6) for the different
778  * Uemis tank cases ("air", "nitrox_1", "nitrox_2.{bottom,deco}"
779  * and "nitrox_3.{bottom,deco,travel}". We need to turn that
780  * into the actual cylinder data depending on the gas template,
781  * and ignore the ones that are irrelevant for that template.
782  *
783  * So for "template 2" (nitrox_2), we ignore virtual tanks 0-1
784  * (which are "air" and "nitrox_1" respectively), and tanks 4-6
785  * (which are the three "nitrox_3" tanks), and we turn virtual
786  * tanks 2/3 into actual tanks 0/1.
787  *
788  * Confused yet?
789  */
790 static int uemis_cylinder_index(void *_cylinder)
791 {
792         cylinder_t *cylinder = _cylinder;
793         unsigned int index = cylinder - dive->cylinder;
794
795         if (index > 6) {
796                 fprintf(stderr, "Uemis cylinder pointer calculations broken\n");
797                 return -1;
798         }
799         switch(uemis_gas_template) {
800         case 1: /* Dive uses tank 1 */
801                 index -= 1;
802         /* Fallthrough */
803         case 0: /* Dive uses tank 0 */
804                 if (index)
805                         index = -1;
806                 break;
807         case 2: /* Dive uses tanks 2-3 */
808                 index -= 2;
809                 if (index > 1)
810                         index = -1;
811                 break;
812         case 3: /* Dive uses tanks 4-6 */
813                 index -= 4;
814                 if (index > 2)
815                         index = -1;
816                 break;
817         }
818         return index;
819 }
820
821 static void uemis_cylindersize(char *buffer, void *_cylinder)
822 {
823         int index = uemis_cylinder_index(_cylinder);
824         if (index >= 0)
825                 cylindersize(buffer, &dive->cylinder[index].type.size);
826 }
827
828 static void uemis_percent(char *buffer, void *_cylinder)
829 {
830         int index = uemis_cylinder_index(_cylinder);
831         if (index >= 0)
832                 percent(buffer, &dive->cylinder[index].gasmix.o2);
833 }
834
835 static int uemis_dive_match(struct dive *dive, const char *name, int len, char *buf)
836 {
837         return  MATCH(".units.length", uemis_length_unit, &input_units) ||
838                 MATCH(".units.volume", uemis_volume_unit, &input_units) ||
839                 MATCH(".units.pressure", uemis_pressure_unit, &input_units) ||
840                 MATCH(".units.temperature", uemis_temperature_unit, &input_units) ||
841                 MATCH(".units.weight", uemis_weight_unit, &input_units) ||
842                 MATCH(".units.time", uemis_time_unit, &input_units) ||
843                 MATCH(".units.date", uemis_date_unit, &input_units) ||
844                 MATCH(".date_time", uemis_date_time, &dive->when) ||
845                 MATCH(".time_zone", uemis_time_zone, &dive->when) ||
846                 MATCH(".ambient.temperature", decicelsius, &dive->airtemp) ||
847                 MATCH(".gas.template", get_index, &uemis_gas_template) ||
848                 MATCH(".air.bottom_tank.size", uemis_cylindersize, dive->cylinder + 0) ||
849                 MATCH(".air.bottom_tank.oxygen", uemis_percent, dive->cylinder + 0) ||
850                 MATCH(".nitrox_1.bottom_tank.size", uemis_cylindersize, dive->cylinder + 1) ||
851                 MATCH(".nitrox_1.bottom_tank.oxygen", uemis_percent, dive->cylinder + 1) ||
852                 MATCH(".nitrox_2.bottom_tank.size", uemis_cylindersize, dive->cylinder + 2) ||
853                 MATCH(".nitrox_2.bottom_tank.oxygen", uemis_percent, dive->cylinder + 2) ||
854                 MATCH(".nitrox_2.deco_tank.size", uemis_cylindersize, dive->cylinder + 3) ||
855                 MATCH(".nitrox_2.deco_tank.oxygen", uemis_percent, dive->cylinder + 3) ||
856                 MATCH(".nitrox_3.bottom_tank.size", uemis_cylindersize, dive->cylinder + 4) ||
857                 MATCH(".nitrox_3.bottom_tank.oxygen", uemis_percent, dive->cylinder + 4) ||
858                 MATCH(".nitrox_3.deco_tank.size", uemis_cylindersize, dive->cylinder + 5) ||
859                 MATCH(".nitrox_3.deco_tank.oxygen", uemis_percent, dive->cylinder + 5) ||
860                 MATCH(".nitrox_3.travel_tank.size", uemis_cylindersize, dive->cylinder + 6) ||
861                 MATCH(".nitrox_3.travel_tank.oxygen", uemis_percent, dive->cylinder + 6) ||
862                 0;
863 }
864
865 /*
866  * Uddf specifies ISO 8601 time format.
867  *
868  * There are many variations on that. This handles the useful cases.
869  */
870 static void uddf_datetime(char *buffer, void *_when)
871 {
872         char c;
873         int y,m,d,hh,mm,ss;
874         time_t *when = _when;
875         struct tm tm = { 0 };
876         int i;
877
878         i = sscanf(buffer, "%d-%d-%d%c%d:%d:%d", &y, &m, &d, &c, &hh, &mm, &ss);
879         if (i == 7)
880                 goto success;
881         ss = 0;
882         if (i == 6)
883                 goto success;
884
885         i = sscanf(buffer, "%04d%02d%02d%c%02d%02d%02d", &y, &m, &d, &c, &hh, &mm, &ss);
886         if (i == 7)
887                 goto success;
888         ss = 0;
889         if (i == 6)
890                 goto success;
891 bad_date:
892         printf("Bad date time %s\n", buffer);
893         free(buffer);
894         return;
895
896 success:
897         if (c != 'T' && c != ' ')
898                 goto bad_date;
899         tm.tm_year = y;
900         tm.tm_mon = m - 1;
901         tm.tm_mday = d;
902         tm.tm_hour = hh;
903         tm.tm_min = mm;
904         tm.tm_sec = ss;
905         *when = utc_mktime(&tm);
906         free(buffer);
907 }
908
909 static int uddf_dive_match(struct dive *dive, const char *name, int len, char *buf)
910 {
911         return  MATCH(".datetime", uddf_datetime, &dive->when) ||
912                 MATCH(".diveduration", duration, &dive->duration) ||
913                 MATCH(".greatestdepth", depth, &dive->maxdepth) ||
914                 0;
915 }
916
917 static void gps_location(char *buffer, void *_dive)
918 {
919         int i;
920         struct dive *dive = _dive;
921         double latitude, longitude;
922
923         i = sscanf(buffer, "%lf %lf", &latitude, &longitude);
924         if (i == 2) {
925                 dive->latitude = latitude;
926                 dive->longitude = longitude;
927         }
928         free(buffer);
929 }
930
931 /* We're in the top-level dive xml. Try to convert whatever value to a dive value */
932 static void try_to_fill_dive(struct dive *dive, const char *name, char *buf)
933 {
934         int len = strlen(name);
935
936         start_match("dive", name, buf);
937
938         switch (import_source) {
939         case SUUNTO:
940                 if (suunto_dive_match(dive, name, len, buf))
941                         return;
942                 break;
943
944         case UEMIS:
945                 if (uemis_dive_match(dive, name, len, buf))
946                         return;
947                 break;
948
949         case DIVINGLOG:
950                 if (divinglog_dive_match(dive, name, len, buf))
951                         return;
952                 break;
953
954         case UDDF:
955                 if (uddf_dive_match(dive, name, len, buf))
956                         return;
957                 break;
958
959         default:
960                 break;
961         }
962
963         if (MATCH(".number", get_index, &dive->number))
964                 return;
965         if (MATCH(".date", divedate, &dive->when))
966                 return;
967         if (MATCH(".time", divetime, &dive->when))
968                 return;
969         if (MATCH(".datetime", divedatetime, &dive->when))
970                 return;
971         if (MATCH(".maxdepth", depth, &dive->maxdepth))
972                 return;
973         if (MATCH(".meandepth", depth, &dive->meandepth))
974                 return;
975         if (MATCH(".depth.max", depth, &dive->maxdepth))
976                 return;
977         if (MATCH(".depth.mean", depth, &dive->meandepth))
978                 return;
979         if (MATCH(".duration", duration, &dive->duration))
980                 return;
981         if (MATCH(".divetime", duration, &dive->duration))
982                 return;
983         if (MATCH(".divetimesec", duration, &dive->duration))
984                 return;
985         if (MATCH(".surfacetime", duration, &dive->surfacetime))
986                 return;
987         if (MATCH(".airtemp", temperature, &dive->airtemp))
988                 return;
989         if (MATCH(".watertemp", temperature, &dive->watertemp))
990                 return;
991         if (MATCH(".temperature.air", temperature, &dive->airtemp))
992                 return;
993         if (MATCH(".temperature.water", temperature, &dive->watertemp))
994                 return;
995         if (MATCH(".cylinderstartpressure", pressure, &dive->cylinder[0].start))
996                 return;
997         if (MATCH(".cylinderendpressure", pressure, &dive->cylinder[0].end))
998                 return;
999         if (MATCH(".gps", gps_location, dive))
1000                 return;
1001         if (MATCH(".location", utf8_string, &dive->location))
1002                 return;
1003         if (MATCH(".notes", utf8_string, &dive->notes))
1004                 return;
1005         if (MATCH(".divemaster", utf8_string, &dive->divemaster))
1006                 return;
1007         if (MATCH(".buddy", utf8_string, &dive->buddy))
1008                 return;
1009
1010         if (MATCH(".cylinder.size", cylindersize, &dive->cylinder[cylinder_index].type.size))
1011                 return;
1012         if (MATCH(".cylinder.workpressure", pressure, &dive->cylinder[cylinder_index].type.workingpressure))
1013                 return;
1014         if (MATCH(".cylinder.description", utf8_string, &dive->cylinder[cylinder_index].type.description))
1015                 return;
1016         if (MATCH(".cylinder.start", pressure, &dive->cylinder[cylinder_index].start))
1017                 return;
1018         if (MATCH(".cylinder.end", pressure, &dive->cylinder[cylinder_index].end))
1019                 return;
1020
1021         if (MATCH(".o2", gasmix, &dive->cylinder[cylinder_index].gasmix.o2))
1022                 return;
1023         if (MATCH(".n2", gasmix_nitrogen, &dive->cylinder[cylinder_index].gasmix))
1024                 return;
1025         if (MATCH(".he", gasmix, &dive->cylinder[cylinder_index].gasmix.he))
1026                 return;
1027
1028         nonmatch("dive", name, buf);
1029 }
1030
1031 /*
1032  * File boundaries are dive boundaries. But sometimes there are
1033  * multiple dives per file, so there can be other events too that
1034  * trigger a "new dive" marker and you may get some nesting due
1035  * to that. Just ignore nesting levels.
1036  */
1037 static void dive_start(void)
1038 {
1039         if (dive)
1040                 return;
1041         dive = alloc_dive();
1042         memset(&tm, 0, sizeof(tm));
1043 }
1044
1045 static void sanitize_gasmix(struct gasmix *mix)
1046 {
1047         unsigned int o2, he;
1048
1049         o2 = mix->o2.permille;
1050         he = mix->he.permille;
1051
1052         /* Regular air: leave empty */
1053         if (!he) {
1054                 if (!o2)
1055                         return;
1056                 /* 20.9% or 21% O2 is just air */
1057                 if (o2 >= 209 && o2 <= 210) {
1058                         mix->o2.permille = 0;
1059                         return;
1060                 }
1061         }
1062
1063         /* Sane mix? */
1064         if (o2 <= 1000 && he <= 1000 && o2+he <= 1000)
1065                 return;
1066         fprintf(stderr, "Odd gasmix: %d O2 %d He\n", o2, he);
1067         memset(mix, 0, sizeof(*mix));
1068 }
1069
1070 /*
1071  * See if the size/workingpressure looks like some standard cylinder
1072  * size, eg "AL80".
1073  */
1074 static void match_standard_cylinder(cylinder_type_t *type)
1075 {
1076         double cuft;
1077         int psi, len;
1078         const char *fmt;
1079         char buffer[20], *p;
1080
1081         /* Do we already have a cylinder description? */
1082         if (type->description)
1083                 return;
1084
1085         cuft = type->size.mliter / 28317.0;
1086         cuft *= type->workingpressure.mbar / 1013.25;
1087         psi = type->workingpressure.mbar / 68.95;
1088
1089         switch (psi) {
1090         case 2300 ... 2500:     /* 2400 psi: LP tank */
1091                 fmt = "LP%d";
1092                 break;
1093         case 2600 ... 2700:     /* 2640 psi: LP+10% */
1094                 fmt = "LP%d";
1095                 break;
1096         case 2900 ... 3100:     /* 3000 psi: ALx tank */
1097                 fmt = "AL%d";
1098                 break;
1099         case 3400 ... 3500:     /* 3442 psi: HP tank */
1100                 fmt = "HP%d";
1101                 break;
1102         case 3700 ... 3850:     /* HP+10% */
1103                 fmt = "HP%d+";
1104                 break;
1105         default:
1106                 return;
1107         }
1108         len = snprintf(buffer, sizeof(buffer), fmt, (int) (cuft+0.5));
1109         p = malloc(len+1);
1110         if (!p)
1111                 return;
1112         memcpy(p, buffer, len+1);
1113         type->description = p;
1114 }
1115
1116
1117 /*
1118  * There are two ways to give cylinder size information:
1119  *  - total amount of gas in cuft (depends on working pressure and physical size)
1120  *  - physical size
1121  *
1122  * where "physical size" is the one that actually matters and is sane.
1123  *
1124  * We internally use physical size only. But we save the workingpressure
1125  * so that we can do the conversion if required.
1126  */
1127 static void sanitize_cylinder_type(cylinder_type_t *type)
1128 {
1129         double volume_of_air, atm, volume;
1130
1131         /* If we have no working pressure, it had *better* be just a physical size! */
1132         if (!type->workingpressure.mbar)
1133                 return;
1134
1135         /* No size either? Nothing to go on */
1136         if (!type->size.mliter)
1137                 return;
1138
1139         if (input_units.volume == CUFT || import_source == SUUNTO) {
1140                 volume_of_air = type->size.mliter * 28.317;     /* milli-cu ft to milliliter */
1141                 atm = type->workingpressure.mbar / 1013.25;     /* working pressure in atm */
1142                 volume = volume_of_air / atm;                   /* milliliters at 1 atm: "true size" */
1143                 type->size.mliter = volume + 0.5;
1144         }
1145
1146         /* Ok, we have both size and pressure: try to match a description */
1147         match_standard_cylinder(type);
1148 }
1149
1150 static void sanitize_cylinder_info(struct dive *dive)
1151 {
1152         int i;
1153
1154         for (i = 0; i < MAX_CYLINDERS; i++) {
1155                 sanitize_gasmix(&dive->cylinder[i].gasmix);
1156                 sanitize_cylinder_type(&dive->cylinder[i].type);
1157         }
1158 }
1159
1160 static void dive_end(void)
1161 {
1162         if (!dive)
1163                 return;
1164         sanitize_cylinder_info(dive);
1165         record_dive(dive);
1166         dive = NULL;
1167         cylinder_index = 0;
1168 }
1169
1170 static void event_start(void)
1171 {
1172         memset(&event, 0, sizeof(event));
1173         event.active = 1;
1174 }
1175
1176 static void event_end(void)
1177 {
1178         if (event.name && strcmp(event.name, "surface") != 0)
1179                 add_event(dive, event.time.seconds, event.type, event.flags, event.value, event.name);
1180         event.active = 0;
1181 }
1182
1183 static void cylinder_start(void)
1184 {
1185 }
1186
1187 static void cylinder_end(void)
1188 {
1189         cylinder_index++;
1190 }
1191
1192 static void sample_start(void)
1193 {
1194         sample = prepare_sample(&dive);
1195 }
1196
1197 static void sample_end(void)
1198 {
1199         if (!dive)
1200                 return;
1201
1202         finish_sample(dive, sample);
1203         sample = NULL;
1204 }
1205
1206 static void entry(const char *name, int size, const char *raw)
1207 {
1208         char *buf = malloc(size+1);
1209
1210         if (!buf)
1211                 return;
1212         memcpy(buf, raw, size);
1213         buf[size] = 0;
1214         if (event.active) {
1215                 try_to_fill_event(name, buf);
1216                 return;
1217         }
1218         if (sample) {
1219                 try_to_fill_sample(sample, name, buf);
1220                 return;
1221         }
1222         if (dive) {
1223                 try_to_fill_dive(dive, name, buf);
1224                 return;
1225         }
1226 }
1227
1228 static const char *nodename(xmlNode *node, char *buf, int len)
1229 {
1230         if (!node || !node->name)
1231                 return "root";
1232
1233         buf += len;
1234         *--buf = 0;
1235         len--;
1236
1237         for(;;) {
1238                 const char *name = node->name;
1239                 int i = strlen(name);
1240                 while (--i >= 0) {
1241                         unsigned char c = name[i];
1242                         *--buf = tolower(c);
1243                         if (!--len)
1244                                 return buf;
1245                 }
1246                 node = node->parent;
1247                 if (!node || !node->name)
1248                         return buf;
1249                 *--buf = '.';
1250                 if (!--len)
1251                         return buf;
1252         }
1253 }
1254
1255 #define MAXNAME 64
1256
1257 static void visit_one_node(xmlNode *node)
1258 {
1259         int len;
1260         const unsigned char *content;
1261         char buffer[MAXNAME];
1262         const char *name;
1263
1264         content = node->content;
1265         if (!content)
1266                 return;
1267
1268         /* Trim whitespace at beginning */
1269         while (isspace(*content))
1270                 content++;
1271
1272         /* Trim whitespace at end */
1273         len = strlen(content);
1274         while (len && isspace(content[len-1]))
1275                 len--;
1276
1277         if (!len)
1278                 return;
1279
1280         /* Don't print out the node name if it is "text" */
1281         if (!strcmp(node->name, "text"))
1282                 node = node->parent;
1283
1284         name = nodename(node, buffer, sizeof(buffer));
1285
1286         entry(name, len, content);
1287 }
1288
1289 static void traverse(xmlNode *root);
1290
1291 static void traverse_properties(xmlNode *node)
1292 {
1293         xmlAttr *p;
1294
1295         for (p = node->properties; p; p = p->next)
1296                 traverse(p->children);
1297 }
1298
1299 static void visit(xmlNode *n)
1300 {
1301         visit_one_node(n);
1302         traverse_properties(n);
1303         traverse(n->children);
1304 }
1305
1306 static void suunto_importer(void)
1307 {
1308         import_source = SUUNTO;
1309         input_units = SI_units;
1310 }
1311
1312 static void uemis_importer(void)
1313 {
1314         import_source = UEMIS;
1315         input_units = SI_units;
1316 }
1317
1318 static void DivingLog_importer(void)
1319 {
1320         import_source = DIVINGLOG;
1321
1322         /*
1323          * Diving Log units are really strange.
1324          *
1325          * Temperatures are in C, except in samples,
1326          * when they are in Fahrenheit. Depths are in
1327          * meters, an dpressure is in PSI in the samples,
1328          * but in bar when it comes to working pressure.
1329          *
1330          * Crazy f*%^ morons.
1331          */
1332         input_units = SI_units;
1333 }
1334
1335 static void uddf_importer(void)
1336 {
1337         import_source = UDDF;
1338         input_units = SI_units;
1339         input_units.pressure = PASCAL;
1340         input_units.temperature = KELVIN;
1341 }
1342
1343 /*
1344  * I'm sure this could be done as some fancy DTD rules.
1345  * It's just not worth the headache.
1346  */
1347 static struct nesting {
1348         const char *name;
1349         void (*start)(void), (*end)(void);
1350 } nesting[] = {
1351         { "dive", dive_start, dive_end },
1352         { "Dive", dive_start, dive_end },
1353         { "sample", sample_start, sample_end },
1354         { "waypoint", sample_start, sample_end },
1355         { "SAMPLE", sample_start, sample_end },
1356         { "reading", sample_start, sample_end },
1357         { "event", event_start, event_end },
1358         { "gasmix", cylinder_start, cylinder_end },
1359         { "cylinder", cylinder_start, cylinder_end },
1360         { "P", sample_start, sample_end },
1361
1362         /* Import type recognition */
1363         { "SUUNTO", suunto_importer },
1364         { "Divinglog", DivingLog_importer },
1365         { "pre_dive", uemis_importer },
1366         { "uddf", uddf_importer },
1367
1368         { NULL, }
1369 };
1370
1371 static void traverse(xmlNode *root)
1372 {
1373         xmlNode *n;
1374
1375         for (n = root; n; n = n->next) {
1376                 struct nesting *rule = nesting;
1377
1378                 do {
1379                         if (!strcmp(rule->name, n->name))
1380                                 break;
1381                         rule++;
1382                 } while (rule->name);
1383
1384                 if (rule->start)
1385                         rule->start();
1386                 visit(n);
1387                 if (rule->end)
1388                         rule->end();
1389         }
1390 }
1391
1392 /* Per-file reset */
1393 static void reset_all(void)
1394 {
1395         /*
1396          * We reset the units for each file. You'd think it was
1397          * a per-dive property, but I'm not going to trust people
1398          * to do per-dive setup. If the xml does have per-dive
1399          * data within one file, we might have to reset it per
1400          * dive for that format.
1401          */
1402         input_units = SI_units;
1403         import_source = UNKNOWN;
1404 }
1405
1406 void parse_xml_file(const char *filename, GError **error)
1407 {
1408         xmlDoc *doc;
1409
1410         doc = xmlReadFile(filename, NULL, 0);
1411         if (!doc) {
1412                 fprintf(stderr, "Failed to parse '%s'.\n", filename);
1413                 if (error != NULL)
1414                 {
1415                         *error = g_error_new(g_quark_from_string("subsurface"),
1416                                              DIVE_ERROR_PARSE,
1417                                              "Failed to parse '%s'",
1418                                              filename);
1419                 }
1420                 return;
1421         }
1422         /* we assume that the last (or only) filename passed as argument is a 
1423          * great filename to use as default when saving the dives */ 
1424         set_filename(filename);
1425         reset_all();
1426         dive_start();
1427         traverse(xmlDocGetRootElement(doc));
1428         dive_end();
1429         xmlFreeDoc(doc);
1430         xmlCleanupParser();
1431 }
1432
1433 void parse_xml_init(void)
1434 {
1435         LIBXML_TEST_VERSION
1436 }