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