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