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