X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Finput%2Fcsv.c;h=895c1c6d8c175031b3f97282e42adfe2df1696d4;hb=dbc38383b27b4c754317db4d1c786ba8700c2981;hp=5957ed491533fcba4eac5363ad5e402850352bed;hpb=ad6a2beec33447fbf56e91588a7e22e4381f95a7;p=libsigrok.git diff --git a/src/input/csv.c b/src/input/csv.c index 5957ed49..895c1c6d 100644 --- a/src/input/csv.c +++ b/src/input/csv.c @@ -174,6 +174,12 @@ struct context { GSList *prev_sr_channels; }; +/* + * Primitive operations for text input: Strip comments off text lines. + * Split text lines into columns. Process input text for individual + * columns. + */ + static void strip_comment(char *buf, const GString *prefix) { char *ptr; @@ -181,10 +187,26 @@ static void strip_comment(char *buf, const GString *prefix) if (!prefix->len) return; - if ((ptr = strstr(buf, prefix->str))) + if ((ptr = strstr(buf, prefix->str))) { *ptr = '\0'; + g_strstrip(buf); + } } +/* TODO Move parse_line() here. */ + +/** + * @brief Parse a text field into multiple bits, binary presentation. + * + * @param[in] str The input text, a run of 0/1 digits. + * @param[in] inc The input module's context. + * + * @retval SR_OK Success. + * @retval SR_ERR Invalid input data (empty, or format error). + * + * This routine modifies the logic levels in the current sample set, + * based on the text input which consists of binary digits. + */ static int parse_binstr(const char *str, struct context *inc) { gsize i, j, length; @@ -215,6 +237,18 @@ static int parse_binstr(const char *str, struct context *inc) return SR_OK; } +/** + * @brief Parse a text field into multiple bits, hexadecimal presentation. + * + * @param[in] str The input text, a run of hex digits. + * @param[in] inc The input module's context. + * + * @retval SR_OK Success. + * @retval SR_ERR Invalid input data (empty, or format error). + * + * This routine modifies the logic levels in the current sample set, + * based on the text input which consists of hexadecimal digits. + */ static int parse_hexstr(const char *str, struct context *inc) { gsize i, j, k, length; @@ -259,6 +293,18 @@ static int parse_hexstr(const char *str, struct context *inc) return SR_OK; } +/** + * @brief Parse a text field into multiple bits, octal presentation. + * + * @param[in] str The input text, a run of oct digits. + * @param[in] inc The input module's context. + * + * @retval SR_OK Success. + * @retval SR_ERR Invalid input data (empty, or format error). + * + * This routine modifies the logic levels in the current sample set, + * based on the text input which consists of octal digits. + */ static int parse_octstr(const char *str, struct context *inc) { gsize i, j, k, length; @@ -303,6 +349,20 @@ static int parse_octstr(const char *str, struct context *inc) return SR_OK; } +static int parse_single_column(const char *column, struct context *inc) +{ + switch (inc->format) { + case FORMAT_BIN: + return parse_binstr(column, inc); + case FORMAT_HEX: + return parse_hexstr(column, inc); + case FORMAT_OCT: + return parse_octstr(column, inc); + } + + return SR_ERR; +} + /** * @brief Splits a text line into a set of columns. * @@ -311,6 +371,12 @@ static int parse_octstr(const char *str, struct context *inc) * @param[in] max_cols The maximum column count, negative to get all of them. * * @returns An array of strings, representing the columns' text. + * + * This routine splits a text line on previously determined separators. + * A previously determined set of columns gets isolated (starting at a + * first position and spanning a given number of columns). A negative + * value for the maximum number of columns results in no restriction on + * the result set's length (the first columns still get trimmed off). */ static char **parse_line(char *buf, struct context *inc, ssize_t max_cols) { @@ -318,48 +384,58 @@ static char **parse_line(char *buf, struct context *inc, ssize_t max_cols) GSList *list, *l; char **columns; char *column; - gsize n, k; + gsize seen, taken; - n = 0; - k = 0; + seen = 0; + taken = 0; list = NULL; remainder = buf; str = strstr(remainder, inc->delimiter->str); - while (str && max_cols) { - if (n >= inc->first_column) { + if (seen >= inc->first_column) { column = g_strndup(remainder, str - remainder); list = g_slist_prepend(list, g_strstrip(column)); max_cols--; - k++; + taken++; } remainder = str + inc->delimiter->len; str = strstr(remainder, inc->delimiter->str); - n++; + seen++; } - if (buf[0] && max_cols && n >= inc->first_column) { + if (buf[0] && max_cols && seen >= inc->first_column) { column = g_strdup(remainder); list = g_slist_prepend(list, g_strstrip(column)); - k++; + taken++; } - if (!(columns = g_try_new(char *, k + 1))) + if (!(columns = g_try_new(char *, taken + 1))) return NULL; - - columns[k--] = NULL; - + columns[taken--] = NULL; for (l = list; l; l = l->next) - columns[k--] = l->data; + columns[taken--] = l->data; g_slist_free(list); return columns; } +/** + * @brief Picks logic levels from multiple binary colomns, one channel per column. + * + * @param[in] columns The text fields which are kept in the columns. + * @param[in] inc The input module's context. + * + * @retval SR_OK Success. + * @retval SR_ERR Insufficient input, or syntax errors. + * + * This routine exclusively handles binary input where one logic channel + * occupies one column each. All channels are expected to reside in one + * consequtive run of columns. + */ static int parse_multi_columns(char **columns, struct context *inc) { gsize i; @@ -370,13 +446,13 @@ static int parse_multi_columns(char **columns, struct context *inc) for (i = 0; i < inc->num_channels; i++) { column = columns[i]; - if (column[0] == '1') { + if (strcmp(column, "1") == 0) { inc->sample_buffer[i / 8] |= (1 << (i % 8)); } else if (!strlen(column)) { sr_err("Column %zu in line %zu is empty.", inc->first_channel + i, inc->line_number); return SR_ERR; - } else if (column[0] != '0') { + } else if (strcmp(column, "0") != 0) { sr_err("Invalid value '%s' in column %zu in line %zu.", column, inc->first_channel + i, inc->line_number); @@ -387,27 +463,6 @@ static int parse_multi_columns(char **columns, struct context *inc) return SR_OK; } -static int parse_single_column(const char *column, struct context *inc) -{ - int res; - - res = SR_ERR; - - switch (inc->format) { - case FORMAT_BIN: - res = parse_binstr(column, inc); - break; - case FORMAT_HEX: - res = parse_hexstr(column, inc); - break; - case FORMAT_OCT: - res = parse_octstr(column, inc); - break; - } - - return res; -} - static int flush_samples(const struct sr_input *in) { struct context *inc; @@ -965,17 +1020,30 @@ static int reset(struct sr_input *in) return SR_OK; } +enum option_index { + OPT_SINGLE_COL, + OPT_NUM_LOGIC, + OPT_DELIM, + OPT_FORMAT, + OPT_COMMENT, + OPT_RATE, + OPT_FIRST_LOGIC, + OPT_HEADER, + OPT_START, + OPT_MAX, +}; + static struct sr_option options[] = { - { "single-column", "Single column", "Enable single-column mode, using the specified column (>= 1); 0: multi-col. mode", NULL, NULL }, - { "numchannels", "Number of logic channels", "The number of (logic) channels (single-col. mode: number of bits beginning at 'first channel', LSB-first)", NULL, NULL }, - { "delimiter", "Column delimiter", "The column delimiter (>= 1 characters)", NULL, NULL }, - { "format", "Data format (single-col. mode)", "The numeric format of the data (single-col. mode): bin, hex, oct", NULL, NULL }, - { "comment", "Comment character(s)", "The comment prefix character(s)", NULL, NULL }, - { "samplerate", "Samplerate (Hz)", "The sample rate (used during capture) in Hz", NULL, NULL }, - { "first-channel", "First channel", "The column number of the first channel (multi-col. mode); bit position for the first channel (single-col. mode)", NULL, NULL }, - { "header", "Interpret first line as header (multi-col. mode)", "Treat the first line as header with channel names (multi-col. mode)", NULL, NULL }, - { "startline", "Start line", "The line number at which to start processing samples (>= 1)", NULL, NULL }, - ALL_ZERO + [OPT_SINGLE_COL] = { "single-column", "Single column", "Enable single-column mode, using the specified column (>= 1); 0: multi-col. mode", NULL, NULL }, + [OPT_NUM_LOGIC] = { "numchannels", "Number of logic channels", "The number of (logic) channels (single-col. mode: number of bits beginning at 'first channel', LSB-first)", NULL, NULL }, + [OPT_DELIM] = { "delimiter", "Column delimiter", "The column delimiter (>= 1 characters)", NULL, NULL }, + [OPT_FORMAT] = { "format", "Data format (single-col. mode)", "The numeric format of the data (single-col. mode): bin, hex, oct", NULL, NULL }, + [OPT_COMMENT] = { "comment", "Comment character(s)", "The comment prefix character(s)", NULL, NULL }, + [OPT_RATE] = { "samplerate", "Samplerate (Hz)", "The sample rate (used during capture) in Hz", NULL, NULL }, + [OPT_FIRST_LOGIC] = { "first-channel", "First channel", "The column number of the first channel (multi-col. mode); bit position for the first channel (single-col. mode)", NULL, NULL }, + [OPT_HEADER] = { "header", "Interpret first line as header (multi-col. mode)", "Treat the first line as header with channel names (multi-col. mode)", NULL, NULL }, + [OPT_START] = { "startline", "Start line", "The line number at which to start processing samples (>= 1)", NULL, NULL }, + [OPT_MAX] = ALL_ZERO, }; static const struct sr_option *get_options(void) @@ -983,20 +1051,20 @@ static const struct sr_option *get_options(void) GSList *l; if (!options[0].def) { - options[0].def = g_variant_ref_sink(g_variant_new_int32(0)); - options[1].def = g_variant_ref_sink(g_variant_new_int32(0)); - options[2].def = g_variant_ref_sink(g_variant_new_string(",")); - options[3].def = g_variant_ref_sink(g_variant_new_string("bin")); + options[OPT_SINGLE_COL].def = g_variant_ref_sink(g_variant_new_int32(0)); + options[OPT_NUM_LOGIC].def = g_variant_ref_sink(g_variant_new_int32(0)); + options[OPT_DELIM].def = g_variant_ref_sink(g_variant_new_string(",")); + options[OPT_FORMAT].def = g_variant_ref_sink(g_variant_new_string("bin")); l = NULL; l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("bin"))); l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("hex"))); l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct"))); - options[3].values = l; - options[4].def = g_variant_ref_sink(g_variant_new_string(";")); - options[5].def = g_variant_ref_sink(g_variant_new_uint64(0)); - options[6].def = g_variant_ref_sink(g_variant_new_int32(0)); - options[7].def = g_variant_ref_sink(g_variant_new_boolean(FALSE)); - options[8].def = g_variant_ref_sink(g_variant_new_int32(1)); + options[OPT_FORMAT].values = l; + options[OPT_COMMENT].def = g_variant_ref_sink(g_variant_new_string(";")); + options[OPT_RATE].def = g_variant_ref_sink(g_variant_new_uint64(0)); + options[OPT_FIRST_LOGIC].def = g_variant_ref_sink(g_variant_new_int32(0)); + options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE)); + options[OPT_START].def = g_variant_ref_sink(g_variant_new_int32(1)); } return options;