From 46203f940ea253e00052c8d52f5de71098eae1ec Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Fri, 23 Nov 2018 20:29:02 -0800 Subject: [PATCH] D868UV: parse CSV files downloaded from amateurradio.digital. --- d868uv.c | 62 ++++++++++--------------------- util.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 19 ++++++++++ 3 files changed, 150 insertions(+), 42 deletions(-) diff --git a/d868uv.c b/d868uv.c index 0040c40..9c4e89c 100644 --- a/d868uv.c +++ b/d868uv.c @@ -2687,7 +2687,7 @@ static int d868uv_verify_config(radio_device_t *radio) // // Read and dump the callsign database. // -static void read_csv(radio_device_t *radio) +static void dump_csv(radio_device_t *radio) { callsign_sizes_t sz = {0}; @@ -2809,52 +2809,30 @@ static void d868uv_write_csv(radio_device_t *radio, FILE *csv) // Need to rearrange the fields like: // Radio ID, Name, City, Callsign, State, Country, Remarks // - char line[256]; unsigned nbytes = 0; + char *radioid, *callsign, *name, *city, *state, *country, *remarks; - while (fgets(line, sizeof(line), csv)) { - trim_spaces(line, 255); - if (line[0] < '0' || line[0] > '9') { - // Eastern egg: when file contains a line "dump", - // read the callsign database from the radio - // and save to a file. - if (strcmp(line, "dump") == 0) { - free(data); - read_csv(radio); - return; - } - // Skip header. - continue; - } + if (csv_init(csv) < 0) { + free(data); + return; + } + while (csv_read(csv, &radioid, &callsign, &name, &city, &state, &country, &remarks)) { + //printf("%s,%s,%s,%s,%s,%s,%s\n", radioid, callsign, name, city, state, country, remarks); - // Replace non-ASCII characters with '?'. - char *p; - for (p=line; *p; p++) { - if (*p < ' ' || *p > '~') - *p = '?'; - } - - char *callsign = strchr(line, ','); if (! callsign) continue; *callsign++ = 0; - char *name = strchr(callsign, ','); if (! name) continue; *name++ = 0; - char *city = strchr(name, ','); if (! city) continue; *city++ = 0; - char *state = strchr(city, ','); if (! state) continue; *state++ = 0; - char *country = strchr(state, ','); if (! country) continue; *country++ = 0; - char *remarks = strchr(country, ','); if (! remarks) continue; *remarks++ = 0; - if ((p = strchr(remarks, ',')) != 0) - *p = 0; - callsign = trim_spaces(callsign, 16); - name = trim_spaces(name, 16); - city = trim_spaces(city, 15); - state = trim_spaces(state, 16); - country = trim_spaces(country, 16); - remarks = trim_spaces(remarks, 16); - //printf("%s,%s,%s,%s,%s,%s,%s\n", line, callsign, name, city, state, country, remarks); - - unsigned id = strtoul(line, 0, 10); + unsigned id = strtoul(radioid, 0, 10); if (id < 1 || id > 0xffffff) { fprintf(stderr, "Bad id: %d\n", id); fprintf(stderr, "Line: '%s,%s,%s,%s,%s,%s,%s'\n", - line, callsign, name, city, state, country, remarks); + radioid, callsign, name, city, state, country, remarks); + return; + } + + // Eastern egg: when file contains id 1 with callsign 'dump', + // read the callsign database from the radio + // and save to a file. + if (id == 1 && strcmp(callsign, "dump") == 0) { + free(data); + dump_csv(radio); return; } @@ -2872,7 +2850,7 @@ static void d868uv_write_csv(radio_device_t *radio, FILE *csv) m->offset = nbytes; // Fill data. - p = &data[nbytes]; + char *p = &data[nbytes]; // Radio ID. *p++ = 0; diff --git a/util.c b/util.c index 43f80fc..6f670d3 100644 --- a/util.c +++ b/util.c @@ -142,6 +142,22 @@ char *trim_spaces(char *line, int limit) return line; } +// +// Strip optional quotes around the string. +// +char *trim_quotes(char *line) +{ + if (*line == '"') { + int last = strlen(line) - 1; + + if (line[last] == '"') { + line[last] = 0; + return line+1; + } + } + return line; +} + // // Delay in milliseconds. // @@ -722,3 +738,98 @@ void print_tone(FILE *out, unsigned data) break; } } + +// +// Initialize CSV parser. +// Check header for correctness. +// Return negative on error. +// +static int csv_skip_field1; + +int csv_init(FILE *csv) +{ + char line[256]; + + if (!fgets(line, sizeof(line), csv)) + return -1; + + char *field1 = line; + char *field2 = strchr(field1, ','); if (! field2) return -1; *field2++ = 0; + char *field3 = strchr(field2, ','); if (! field3) return -1; *field3++ = 0; + char *field4 = strchr(field3, ','); if (! field4) return -1; *field4++ = 0; + + field1 = trim_quotes(field1); + field2 = trim_quotes(field2); + field3 = trim_quotes(field3); +//printf("Line: %s,%s,%s\n", field1, field2, field3); + + if (strcmp(field1, "Radio ID") == 0 && + strcmp(field2, "Callsign") == 0) { + // Correct format: + // Radio ID,Callsign,Name,City,State,Country,Remarks + csv_skip_field1 = 0; + return 0; + } + if (strcmp(field2, "Radio ID") == 0 && + strcmp(field3, "Callsign") == 0) { + // Correct format: + // "No.","Radio ID","Callsign","Name","City","State","Country","Remarks" + csv_skip_field1 = 1; + return 0; + } + + //TODO + return -1; +} + +// +// Parse one line of CSV file. +// Return 1 on success, 0 on EOF. +// +int csv_read(FILE *csv, char **radioid, char **callsign, char **name, + char **city, char **state, char **country, char **remarks) +{ + static char line[256]; + +again: + if (!fgets(line, sizeof(line), csv)) + return 0; +//printf("Line: '%s'\n", line); + + // Replace non-ASCII characters with '?'. + char *p; + for (p=line; *p; p++) { + if ((uint8_t)*p > '~') + *p = '?'; + } + + if (csv_skip_field1) { + *radioid = strchr(line, ','); + if (! *radioid) + return 0; + *(*radioid)++ = 0; + } else + *radioid = line; + + *callsign = strchr(*radioid, ','); if (! *callsign) return 0; *(*callsign)++ = 0; + *name = strchr(*callsign, ','); if (! *name) return 0; *(*name)++ = 0; + *city = strchr(*name, ','); if (! *city) return 0; *(*city)++ = 0; + *state = strchr(*city, ','); if (! *state) return 0; *(*state)++ = 0; + *country = strchr(*state, ','); if (! *country) return 0; *(*country)++ = 0; + *remarks = strchr(*country, ','); if (! *remarks) return 0; *(*remarks)++ = 0; + if ((p = strchr(*remarks, ',')) != 0) + *p = 0; + + *radioid = trim_spaces(trim_quotes(*radioid), 16); + *callsign = trim_spaces(trim_quotes(*callsign), 16); + *name = trim_spaces(trim_quotes(*name), 16); + *city = trim_spaces(trim_quotes(*city), 15); + *state = trim_spaces(trim_quotes(*state), 16); + *country = trim_spaces(trim_quotes(*country), 16); + *remarks = trim_spaces(trim_quotes(*remarks), 16); +//printf("%s,%s,%s,%s,%s,%s,%s\n", *radioid, *callsign, *name, *city, *state, *country, *remarks); + + if (**radioid < '1' || **radioid > '9') + goto again; + return 1; +} diff --git a/util.h b/util.h index b305204..1052bd1 100644 --- a/util.h +++ b/util.h @@ -49,6 +49,25 @@ void print_hex_addr_data(unsigned addr, const unsigned char *data, int len); // char *trim_spaces(char *line, int limit); +// +// Strip optional quotes around the string. +// +char *trim_quotes(char *line); + +// +// Initialize CSV parser. +// Check header for correctness. +// Return -1 on error. +// +int csv_init(FILE *csv); + +// +// Parse one line of CSV file. +// Return 1 on success, 0 on EOF. +// +int csv_read(FILE *csv, char **radioid, char **callsign, char **name, + char **city, char **state, char **country, char **remarks); + // // DFU functions. //