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