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