diff options
author | Miika Turkia <miika.turkia@gmail.com> | 2018-01-06 22:24:38 +0200 |
---|---|---|
committer | mturkia <miika.turkia@gmail.com> | 2018-01-08 06:06:12 +0200 |
commit | b808723f9cf6d65da0d662dd523d8088892dfdfc (patch) | |
tree | d200a975565640affb34095724ca58cb8c2fe496 /core/import-csv.c | |
parent | fbbca93d53d25560711c556b6dfda3d84f28b3bd (diff) | |
download | subsurface-b808723f9cf6d65da0d662dd523d8088892dfdfc.tar.gz |
Refactor CSV import
Move CSV import related functions into import-csv.c.
Signed-off-by: Miika Turkia <miika.turkia@gmail.com>
Diffstat (limited to 'core/import-csv.c')
-rw-r--r-- | core/import-csv.c | 854 |
1 files changed, 854 insertions, 0 deletions
diff --git a/core/import-csv.c b/core/import-csv.c new file mode 100644 index 000000000..c5b4ff8f3 --- /dev/null +++ b/core/import-csv.c @@ -0,0 +1,854 @@ +#include <unistd.h> +#include <errno.h> +#include <libdivecomputer/parser.h> + +#include "dive.h" +#include "divelist.h" +#include "file.h" +#include "parse.h" +#include "divelist.h" +#include "gettext.h" +#include "import-csv.h" +#include "qthelperfromc.h" + +#define MATCH(buffer, pattern) \ + memcmp(buffer, pattern, strlen(pattern)) + +void add_sample_data(struct sample *sample, enum csv_format type, double val) +{ + switch (type) { + case CSV_DEPTH: + sample->depth.mm = feet_to_mm(val); + break; + case CSV_TEMP: + sample->temperature.mkelvin = F_to_mkelvin(val); + break; + case CSV_PRESSURE: + sample->pressure[0].mbar = psi_to_mbar(val * 4); + break; + case POSEIDON_DEPTH: + sample->depth.mm = lrint(val * 0.5 * 1000); + break; + case POSEIDON_TEMP: + sample->temperature.mkelvin = C_to_mkelvin(val * 0.2); + break; + case POSEIDON_SETPOINT: + sample->setpoint.mbar = lrint(val * 10); + break; + case POSEIDON_SENSOR1: + sample->o2sensor[0].mbar = lrint(val * 10); + break; + case POSEIDON_SENSOR2: + sample->o2sensor[1].mbar = lrint(val * 10); + break; + case POSEIDON_NDL: + sample->ndl.seconds = lrint(val * 60); + break; + case POSEIDON_CEILING: + sample->stopdepth.mm = lrint(val * 1000); + break; + } +} + +int parse_dan_format(const char *filename, char **params, int pnr) +{ + int ret = 0, i; + size_t end_ptr = 0; + struct memblock mem, mem_csv; + char tmpbuf[MAXCOLDIGITS]; + + char *ptr = NULL; + char *NL = NULL; + char *iter = NULL; + + if (readfile(filename, &mem) < 0) + return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); + + /* Determine NL (new line) character and the start of CSV data */ + if ((ptr = strstr(mem.buffer, "\r\n")) != NULL) { + NL = "\r\n"; + } else if ((ptr = strstr(mem.buffer, "\n")) != NULL) { + NL = "\n"; + } else { + fprintf(stderr, "DEBUG: failed to detect NL\n"); + return -1; + } + + while ((end_ptr < mem.size) && (ptr = strstr(mem.buffer + end_ptr, "ZDH"))) { + + /* + * Process the dives, but let the last round be parsed + * from C++ code + */ + + if (end_ptr) + process_dives(true, false); + + mem_csv.buffer = malloc(mem.size + 1); + mem_csv.size = mem.size; + + iter = ptr + 4; + iter = strchr(iter, '|'); + if (iter) { + memcpy(tmpbuf, ptr + 4, iter - ptr - 4); + tmpbuf[iter - ptr - 4] = 0; + params[pnr] = "diveNro"; + params[pnr + 1] = strdup(tmpbuf); + } + + //fprintf(stderr, "DEBUG: BEGIN end_ptr %d round %d <%s>\n", end_ptr, j++, ptr); + iter = ptr + 1; + for (i = 0; i <= 4 && iter; ++i) { + iter = strchr(iter, '|'); + if (iter) + ++iter; + } + + if (!iter) { + fprintf(stderr, "DEBUG: Data corrupt"); + return -1; + } + + /* Setting date */ + memcpy(tmpbuf, iter, 8); + tmpbuf[8] = 0; + params[pnr + 2] = "date"; + params[pnr + 3] = strdup(tmpbuf); + + /* Setting time, gotta prepend it with 1 to + * avoid octal parsing (this is stripped out in + * XSLT */ + tmpbuf[0] = '1'; + memcpy(tmpbuf + 1, iter + 8, 6); + tmpbuf[7] = 0; + params[pnr + 4] = "time"; + params[pnr + 5] = strdup(tmpbuf); + params[pnr + 6] = NULL; + + ptr = strstr(ptr, "ZDP{"); + if (ptr && ptr[4] == '}') { + end_ptr += ptr - (char *)mem_csv.buffer; + return report_error(translate("gettextFromC", "No dive profile found from '%s'"), filename); + } + if (ptr) + ptr = strstr(ptr, NL); + if (ptr) { + ptr += strlen(NL); + } else { + fprintf(stderr, "DEBUG: Data corrupt"); + return -1; + } + end_ptr = ptr - (char *)mem.buffer; + + /* Copy the current dive data to start of mem_csv buffer */ + memcpy(mem_csv.buffer, ptr, mem.size - (ptr - (char *)mem.buffer)); + ptr = strstr(mem_csv.buffer, "ZDP}"); + if (ptr) { + *ptr = 0; + } else { + fprintf(stderr, "DEBUG: failed to find end ZDP\n"); + return -1; + } + mem_csv.size = ptr - (char*)mem_csv.buffer; + + if (try_to_xslt_open_csv(filename, &mem_csv, "csv")) + return -1; + + ret |= parse_xml_buffer(filename, mem_csv.buffer, mem_csv.size, &dive_table, (const char **)params); + end_ptr += ptr - (char *)mem_csv.buffer; + free(mem_csv.buffer); + } + + free(mem.buffer); + for (i = 0; params[i]; i += 2) + free(params[i + 1]); + + return ret; +} + +int parse_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate) +{ + int ret, i; + struct memblock mem; + time_t now; + struct tm *timep = NULL; + char tmpbuf[MAXCOLDIGITS]; + + /* Increase the limits for recursion and variables on XSLT + * parsing */ + xsltMaxDepth = 30000; +#if LIBXSLT_VERSION > 10126 + xsltMaxVars = 150000; +#endif + + if (filename == NULL) + return report_error("No CSV filename"); + + mem.size = 0; + if (!strcmp("DL7", csvtemplate)) { + return parse_dan_format(filename, params, pnr); + } else if (strcmp(params[0], "date")) { + time(&now); + timep = localtime(&now); + + strftime(tmpbuf, MAXCOLDIGITS, "%Y%m%d", timep); + params[pnr++] = "date"; + params[pnr++] = strdup(tmpbuf); + + /* As the parameter is numeric, we need to ensure that the leading zero + * is not discarded during the transform, thus prepend time with 1 */ + + strftime(tmpbuf, MAXCOLDIGITS, "1%H%M", timep); + params[pnr++] = "time"; + params[pnr++] = strdup(tmpbuf); + params[pnr++] = NULL; + } + + if (try_to_xslt_open_csv(filename, &mem, csvtemplate)) + return -1; + + /* + * Lets print command line for manual testing with xsltproc if + * verbosity level is high enough. The printed line needs the + * input file added as last parameter. + */ + +#ifndef SUBSURFACE_MOBILE + if (verbose >= 2) { + fprintf(stderr, "(echo '<csv>'; cat %s;echo '</csv>') | xsltproc ", filename); + for (i=0; params[i]; i+=2) + fprintf(stderr, "--stringparam %s %s ", params[i], params[i+1]); + fprintf(stderr, "%s/xslt/csv2xml.xslt -\n", SUBSURFACE_SOURCE); + } +#endif + ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params); + + free(mem.buffer); + for (i = 0; params[i]; i += 2) + free(params[i + 1]); + + return ret; +} + + +int try_to_xslt_open_csv(const char *filename, struct memblock *mem, const char *tag) +{ + char *buf; + + if (mem->size == 0 && readfile(filename, mem) < 0) + return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); + + /* Surround the CSV file content with XML tags to enable XSLT + * parsing + * + * Tag markers take: strlen("<></>") = 5 + */ + buf = realloc(mem->buffer, mem->size + 7 + strlen(tag) * 2); + if (buf != NULL) { + char *starttag = NULL; + char *endtag = NULL; + + starttag = malloc(3 + strlen(tag)); + endtag = malloc(5 + strlen(tag)); + + if (starttag == NULL || endtag == NULL) { + /* this is fairly silly - so the malloc fails, but we strdup the error? + * let's complete the silliness by freeing the two pointers in case one malloc succeeded + * and the other one failed - this will make static analysis tools happy */ + free(starttag); + free(endtag); + free(buf); + return report_error("Memory allocation failed in %s", __func__); + } + + sprintf(starttag, "<%s>", tag); + sprintf(endtag, "\n</%s>", tag); + + memmove(buf + 2 + strlen(tag), buf, mem->size); + memcpy(buf, starttag, 2 + strlen(tag)); + memcpy(buf + mem->size + 2 + strlen(tag), endtag, 5 + strlen(tag)); + mem->size += (6 + 2 * strlen(tag)); + mem->buffer = buf; + + free(starttag); + free(endtag); + } else { + free(mem->buffer); + return report_error("realloc failed in %s", __func__); + } + + return 0; +} + +int try_to_open_csv(struct memblock *mem, enum csv_format type) +{ + char *p = mem->buffer; + char *header[8]; + int i, time; + timestamp_t date; + struct dive *dive; + struct divecomputer *dc; + + for (i = 0; i < 8; i++) { + header[i] = p; + p = strchr(p, ','); + if (!p) + return 0; + p++; + } + + date = parse_date(header[2]); + if (!date) + return 0; + + dive = alloc_dive(); + dive->when = date; + dive->number = atoi(header[1]); + dc = &dive->dc; + + time = 0; + for (;;) { + char *end; + double val; + struct sample *sample; + + errno = 0; + val = strtod(p, &end); // FIXME == localization issue + if (end == p) + break; + if (errno) + break; + + sample = prepare_sample(dc); + sample->time.seconds = time; + add_sample_data(sample, type, val); + finish_sample(dc); + + time++; + dc->duration.seconds = time; + if (*end != ',') + break; + p = end + 1; + } + record_dive(dive); + return 1; +} + +char *parse_mkvi_value(const char *haystack, const char *needle) +{ + char *lineptr, *valueptr, *endptr, *ret = NULL; + + if ((lineptr = strstr(haystack, needle)) != NULL) { + if ((valueptr = strstr(lineptr, ": ")) != NULL) { + valueptr += 2; + } + if ((endptr = strstr(lineptr, "\n")) != NULL) { + char terminator = '\n'; + if (*(endptr - 1) == '\r') { + --endptr; + terminator = '\r'; + } + *endptr = 0; + ret = copy_string(valueptr); + *endptr = terminator; + + } + } + return ret; +} + +char *next_mkvi_key(const char *haystack) +{ + char *valueptr, *endptr, *ret = NULL; + + if ((valueptr = strstr(haystack, "\n")) != NULL) { + valueptr += 1; + if ((endptr = strstr(valueptr, ": ")) != NULL) { + *endptr = 0; + ret = strdup(valueptr); + *endptr = ':'; + } + } + return ret; +} + +int parse_txt_file(const char *filename, const char *csv) +{ + struct memblock memtxt, memcsv; + + if (readfile(filename, &memtxt) < 0) { + return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); + } + + /* + * MkVI stores some information in .txt file but the whole profile and events are stored in .csv file. First + * make sure the input .txt looks like proper MkVI file, then start parsing the .csv. + */ + if (MATCH(memtxt.buffer, "MkVI_Config") == 0) { + int d, m, y, he; + int hh = 0, mm = 0, ss = 0; + int prev_depth = 0, cur_sampletime = 0, prev_setpoint = -1, prev_ndl = -1; + bool has_depth = false, has_setpoint = false, has_ndl = false; + char *lineptr, *key, *value; + int cur_cylinder_index = 0; + unsigned int prev_time = 0; + + struct dive *dive; + struct divecomputer *dc; + struct tm cur_tm; + + value = parse_mkvi_value(memtxt.buffer, "Dive started at"); + if (sscanf(value, "%d-%d-%d %d:%d:%d", &y, &m, &d, &hh, &mm, &ss) != 6) { + free(value); + return -1; + } + free(value); + cur_tm.tm_year = y; + cur_tm.tm_mon = m - 1; + cur_tm.tm_mday = d; + cur_tm.tm_hour = hh; + cur_tm.tm_min = mm; + cur_tm.tm_sec = ss; + + dive = alloc_dive(); + dive->when = utc_mktime(&cur_tm);; + dive->dc.model = strdup("Poseidon MkVI Discovery"); + value = parse_mkvi_value(memtxt.buffer, "Rig Serial number"); + dive->dc.deviceid = atoi(value); + free(value); + dive->dc.divemode = CCR; + dive->dc.no_o2sensors = 2; + + dive->cylinder[cur_cylinder_index].cylinder_use = OXYGEN; + dive->cylinder[cur_cylinder_index].type.size.mliter = 3000; + dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000; + dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6"); + dive->cylinder[cur_cylinder_index].gasmix.o2.permille = 1000; + cur_cylinder_index++; + + dive->cylinder[cur_cylinder_index].cylinder_use = DILUENT; + dive->cylinder[cur_cylinder_index].type.size.mliter = 3000; + dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000; + dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6"); + value = parse_mkvi_value(memtxt.buffer, "Helium percentage"); + he = atoi(value); + free(value); + value = parse_mkvi_value(memtxt.buffer, "Nitrogen percentage"); + dive->cylinder[cur_cylinder_index].gasmix.o2.permille = (100 - atoi(value) - he) * 10; + free(value); + dive->cylinder[cur_cylinder_index].gasmix.he.permille = he * 10; + cur_cylinder_index++; + + lineptr = strstr(memtxt.buffer, "Dive started at"); + while (lineptr && *lineptr && (lineptr = strchr(lineptr, '\n')) && ++lineptr) { + key = next_mkvi_key(lineptr); + if (!key) + break; + value = parse_mkvi_value(lineptr, key); + if (!value) { + free(key); + break; + } + add_extra_data(&dive->dc, key, value); + free(key); + free(value); + } + dc = &dive->dc; + + /* + * Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has + * the following format: + * + * timestamp, type, value + * + * And following fields are of interest to us: + * + * 6 sensor1 + * 7 sensor2 + * 8 depth + * 13 o2 tank pressure + * 14 diluent tank pressure + * 20 o2 setpoint + * 39 water temp + */ + + if (readfile(csv, &memcsv) < 0) { + free(dive); + return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv); + } + lineptr = memcsv.buffer; + for (;;) { + struct sample *sample; + int type; + int value; + int sampletime; + int gaschange = 0; + + /* Collect all the information for one sample */ + sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value); + + has_depth = false; + has_setpoint = false; + has_ndl = false; + sample = prepare_sample(dc); + + /* + * There was a bug in MKVI download tool that resulted in erroneous sample + * times. This fix should work similarly as the vendor's own. + */ + + sample->time.seconds = cur_sampletime < 0xFFFF * 3 / 4 ? cur_sampletime : prev_time; + prev_time = sample->time.seconds; + + do { + int i = sscanf(lineptr, "%d,%d,%d", &sampletime, &type, &value); + switch (i) { + case 3: + switch (type) { + case 0: + //Mouth piece position event: 0=OC, 1=CC, 2=UN, 3=NC + switch (value) { + case 0: + add_event(dc, cur_sampletime, 0, 0, 0, + QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position OC")); + break; + case 1: + add_event(dc, cur_sampletime, 0, 0, 0, + QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position CC")); + break; + case 2: + add_event(dc, cur_sampletime, 0, 0, 0, + QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position unknown")); + break; + case 3: + add_event(dc, cur_sampletime, 0, 0, 0, + QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position not connected")); + break; + } + break; + case 3: + //Power Off event + add_event(dc, cur_sampletime, 0, 0, 0, + QT_TRANSLATE_NOOP("gettextFromC", "Power off")); + break; + case 4: + //Battery State of Charge in % +#ifdef SAMPLE_EVENT_BATTERY + add_event(dc, cur_sampletime, SAMPLE_EVENT_BATTERY, 0, + value, QT_TRANSLATE_NOOP("gettextFromC", "battery")); +#endif + break; + case 6: + //PO2 Cell 1 Average + add_sample_data(sample, POSEIDON_SENSOR1, value); + break; + case 7: + //PO2 Cell 2 Average + add_sample_data(sample, POSEIDON_SENSOR2, value); + break; + case 8: + //Depth * 2 + has_depth = true; + prev_depth = value; + add_sample_data(sample, POSEIDON_DEPTH, value); + break; + //9 Max Depth * 2 + //10 Ascent/Descent Rate * 2 + case 11: + //Ascent Rate Alert >10 m/s + add_event(dc, cur_sampletime, SAMPLE_EVENT_ASCENT, 0, 0, + QT_TRANSLATE_NOOP("gettextFromC", "ascent")); + break; + case 13: + //O2 Tank Pressure + add_sample_pressure(sample, 0, lrint(value * 1000)); + break; + case 14: + //Diluent Tank Pressure + add_sample_pressure(sample, 1, lrint(value * 1000)); + break; + //16 Remaining dive time #1? + //17 related to O2 injection + case 20: + //PO2 Setpoint + has_setpoint = true; + prev_setpoint = value; + add_sample_data(sample, POSEIDON_SETPOINT, value); + break; + case 22: + //End of O2 calibration Event: 0 = OK, 2 = Failed, rest of dive setpoint 1.0 + if (value == 2) + add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0, + QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration failed")); + add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0, + QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration")); + break; + case 25: + //25 Max Ascent depth + add_sample_data(sample, POSEIDON_CEILING, value); + break; + case 31: + //Start of O2 calibration Event + add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_BEGIN, 0, + QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration")); + break; + case 37: + //Remaining dive time #2? + has_ndl = true; + prev_ndl = value; + add_sample_data(sample, POSEIDON_NDL, value); + break; + case 39: + // Water Temperature in Celcius + add_sample_data(sample, POSEIDON_TEMP, value); + break; + case 85: + //He diluent part in % + gaschange += value << 16; + break; + case 86: + //O2 diluent part in % + gaschange += value; + break; + //239 Unknown, maybe PO2 at sensor validation? + //240 Unknown, maybe PO2 at sensor validation? + //247 Unknown, maybe PO2 Cell 1 during pressure test + //248 Unknown, maybe PO2 Cell 2 during pressure test + //250 PO2 Cell 1 + //251 PO2 Cell 2 + default: + break; + } /* sample types */ + break; + case EOF: + break; + default: + printf("Unable to parse input: %s\n", lineptr); + break; + } + + lineptr = strchr(lineptr, '\n'); + if (!lineptr || !*lineptr) + break; + lineptr++; + + /* Grabbing next sample time */ + sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value); + } while (sampletime == cur_sampletime); + + if (gaschange) + add_event(dc, cur_sampletime, SAMPLE_EVENT_GASCHANGE2, 0, gaschange, + QT_TRANSLATE_NOOP("gettextFromC", "gaschange")); + if (!has_depth) + add_sample_data(sample, POSEIDON_DEPTH, prev_depth); + if (!has_setpoint && prev_setpoint >= 0) + add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint); + if (!has_ndl && prev_ndl >= 0) + add_sample_data(sample, POSEIDON_NDL, prev_ndl); + finish_sample(dc); + + if (!lineptr || !*lineptr) + break; + } + record_dive(dive); + return 1; + } else { + return 0; + } + + return 0; +} + +#define DATESTR 9 +#define TIMESTR 6 + +#define SBPARAMS 40 +int parse_seabear_log(const char *filename) +{ + char *params[SBPARAMS]; + int pnr = 0; + + pnr = parse_seabear_header(filename, params, pnr); + + if (parse_seabear_csv_file(filename, params, pnr, "csv") < 0) { + return -1; + } + + return 0; +} + + +int parse_seabear_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate) +{ + int ret, i; + struct memblock mem; + time_t now; + struct tm *timep = NULL; + char *ptr, *ptr_old = NULL; + char *NL = NULL; + char tmpbuf[MAXCOLDIGITS]; + + /* Increase the limits for recursion and variables on XSLT + * parsing */ + xsltMaxDepth = 30000; +#if LIBXSLT_VERSION > 10126 + xsltMaxVars = 150000; +#endif + + time(&now); + timep = localtime(&now); + + strftime(tmpbuf, MAXCOLDIGITS, "%Y%m%d", timep); + params[pnr++] = "date"; + params[pnr++] = strdup(tmpbuf); + + /* As the parameter is numeric, we need to ensure that the leading zero + * is not discarded during the transform, thus prepend time with 1 */ + strftime(tmpbuf, MAXCOLDIGITS, "1%H%M", timep); + params[pnr++] = "time"; + params[pnr++] = strdup(tmpbuf); + + + if (filename == NULL) + return report_error("No CSV filename"); + + if (readfile(filename, &mem) < 0) + return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); + + /* Determine NL (new line) character and the start of CSV data */ + ptr = mem.buffer; + while ((ptr = strstr(ptr, "\r\n\r\n")) != NULL) { + ptr_old = ptr; + ptr += 1; + NL = "\r\n"; + } + + if (!ptr_old) { + ptr = mem.buffer; + while ((ptr = strstr(ptr, "\n\n")) != NULL) { + ptr_old = ptr; + ptr += 1; + NL = "\n"; + } + ptr_old += 2; + } else + ptr_old += 4; + + /* + * If file does not contain empty lines, it is not a valid + * Seabear CSV file. + */ + if (NL == NULL) + return -1; + + /* + * On my current sample of Seabear DC log file, the date is + * without any identifier. Thus we must search for the previous + * line and step through from there. That is the line after + * Serial number. + */ + ptr = strstr(mem.buffer, "Serial number:"); + if (ptr) + ptr = strstr(ptr, NL); + + /* + * Write date and time values to params array, if available in + * the CSV header + */ + + if (ptr) { + ptr += strlen(NL) + 2; + /* + * pnr is the index of NULL on the params as filled by + * the init function. The two last entries should be + * date and time. Here we overwrite them with the data + * from the CSV header. + */ + + memcpy(params[pnr - 3], ptr, 4); + memcpy(params[pnr - 3] + 4, ptr + 5, 2); + memcpy(params[pnr - 3] + 6, ptr + 8, 2); + params[pnr - 3][8] = 0; + + memcpy(params[pnr - 1] + 1, ptr + 11, 2); + memcpy(params[pnr - 1] + 3, ptr + 14, 2); + params[pnr - 1][5] = 0; + } + + params[pnr++] = NULL; + + /* Move the CSV data to the start of mem buffer */ + memmove(mem.buffer, ptr_old, mem.size - (ptr_old - (char*)mem.buffer)); + mem.size = (int)mem.size - (ptr_old - (char*)mem.buffer); + + if (try_to_xslt_open_csv(filename, &mem, csvtemplate)) + return -1; + + /* + * Lets print command line for manual testing with xsltproc if + * verbosity level is high enough. The printed line needs the + * input file added as last parameter. + */ + + if (verbose >= 2) { + fprintf(stderr, "xsltproc "); + for (i=0; params[i]; i+=2) + fprintf(stderr, "--stringparam %s %s ", params[i], params[i+1]); + fprintf(stderr, "xslt/csv2xml.xslt\n"); + } + + ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params); + free(mem.buffer); + for (i = 0; params[i]; i += 2) + free(params[i + 1]); + + return ret; +} + +int parse_manual_file(const char *filename, char **params, int pnr) +{ + struct memblock mem; + time_t now; + struct tm *timep; + char curdate[9]; + char curtime[6]; + int ret, i; + + + time(&now); + timep = localtime(&now); + strftime(curdate, DATESTR, "%Y%m%d", timep); + + /* As the parameter is numeric, we need to ensure that the leading zero + * is not discarded during the transform, thus prepend time with 1 */ + strftime(curtime, TIMESTR, "1%H%M", timep); + + + params[pnr++] = strdup("date"); + params[pnr++] = strdup(curdate); + params[pnr++] = strdup("time"); + params[pnr++] = strdup(curtime); + params[pnr++] = NULL; + + if (filename == NULL) + return report_error("No manual CSV filename"); + + mem.size = 0; + if (try_to_xslt_open_csv(filename, &mem, "manualCSV")) + return -1; + +#ifndef SUBSURFACE_MOBILE + if (verbose >= 2) { + fprintf(stderr, "(echo '<manualCSV>'; cat %s;echo '</manualCSV>') | xsltproc ", filename); + for (i=0; params[i]; i+=2) + fprintf(stderr, "--stringparam %s %s ", params[i], params[i+1]); + fprintf(stderr, "%s/xslt/manualcsv2xml.xslt -\n", SUBSURFACE_SOURCE); + } +#endif + ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params); + + free(mem.buffer); + for (i = 0; i < pnr - 2; ++i) + free(params[i]); + return ret; +} |