]> sigrok.org Git - libsigrok.git/blobdiff - src/input/csv.c
input/csv: stricter input data test for multi column mode
[libsigrok.git] / src / input / csv.c
index 5957ed491533fcba4eac5363ad5e402850352bed..895c1c6d8c175031b3f97282e42adfe2df1696d4 100644 (file)
@@ -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;