X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Finput%2Fcsv.c;h=32a309eb25c77364412b38cff5f32186a3c469cc;hb=5ada72fc0a423fe1b525f14132bfc3f8ae03c95d;hp=e3e609917ab5c99cb1e9a77aa5bc648771fa2f1c;hpb=1a920e33fefffe474a8949fccb9f604dfef8401b;p=libsigrok.git diff --git a/src/input/csv.c b/src/input/csv.c index e3e60991..32a309eb 100644 --- a/src/input/csv.c +++ b/src/input/csv.c @@ -35,75 +35,97 @@ /* * The CSV input module has the following options: * - * single-column: Specifies the column number which stores the sample data for - * single column mode and enables single column mode. Multi - * column mode is used if this parameter is omitted. + * column_formats: Specifies the data formats and channel counts for the + * input file's text columns. Accepts a comma separated list of tuples + * with: an optional column repeat count ('*' as a wildcard meaning + * "all remaining columns", only applicable to the last field), a format + * specifying character ('x' hexadecimal, 'o' octal, 'b' binary, 'l' + * single-bit logic), and an optional bit count (translating to: logic + * channels communicated in that column). This "column_formats" option + * is most versatile, other forms of specifying the column layout only + * exist for backwards compatibility. * - * numchannels: Specifies the number of channels to use. In multi column mode - * the number of channels are the number of columns and in single - * column mode the number of bits (LSB first) beginning at - * 'first-channel'. + * single_column: Specifies the column number which contains the logic data + * for single-column mode. All logic data is taken from several bits + * which all are kept within that one column. Only exists for backwards + * compatibility, see "column_formats" for more flexibility. * - * delimiter: Specifies the delimiter for columns. Must be at least one - * character. Comma is used as default delimiter. + * first_column: Specifies the number of the first column with logic data + * in simple multi-column mode. Only exists for backwards compatibility, + * see "column_formats" for more flexibility. * - * format: Specifies the format of the sample data in single column mode. - * Available formats are: 'bin', 'hex' and 'oct'. The binary - * format is used by default. This option has no effect in multi - * column mode. + * logic_channels: Specifies the number of logic channels. Is required in + * simple single-column mode. Is optional in simple multi-column mode + * (and defaults to all remaining columns). Only exists for backwards + * compatibility, see "column_formats" for more flexibility. * - * comment: Specifies the prefix character(s) for comments. No prefix - * characters are used by default which disables removing of - * comments. + * single_format: Specifies the format of the input text in simple single- + * column mode. Available formats are: 'bin' (default), 'hex' and 'oct'. + * Simple multi-column mode always uses single-bit data per column. + * Only exists for backwards compatibility, see "column_formats" for + * more flexibility. * - * samplerate: Samplerate which the sample data was captured with. Default - * value is 0. + * start_line: Specifies at which line to start processing the input file. + * Allows to skip leading lines which neither are header nor data lines. + * By default all of the input file gets processed. * - * first-channel: Column number of the first channel in multi column mode and - * position of the bit for the first channel in single column mode. - * Default value is 0. + * header: Boolean option, controls whether the first processed line is used + * to determine channel names. Off by default. Generic channel names are + * used in the absence of header line content. * - * header: Determines if the first line should be treated as header - * and used for channel names in multi column mode. Empty header - * names will be replaced by the channel number. If enabled in - * single column mode the first line will be skipped. Usage of - * header is disabled by default. + * samplerate: Specifies the samplerate of the input data. Defaults to 0. + * User specs take precedence over data which optionally gets derived + * from input data. * - * startline: Line number to start processing sample data. Must be greater - * than 0. The default line number to start processing is 1. + * column_separator: Specifies the sequence which separates the text file + * columns. Cannot be empty. Defaults to comma. + * + * comment_leader: Specifies the sequence which starts comments that run + * up to the end of the current text line. Can be empty to disable + * comment support. Defaults to semicolon. + * + * Typical examples of using these options: + * - ... -I csv:column_formats=*l ... + * All columns are single-bit logic data. Identical to the previous + * multi-column mode (the default when no options were given at all). + * - ... -I csv:column_formats=3-,*l ... + * Ignore the first three columns, get single-bit logic data from all + * remaining lines (multi-column mode with first-column above 1). + * - ... -I csv:column_formats=3-,4l,x8 ... + * Ignore the first three columns, get single-bit logic data from the + * next four columns, then eight-bit data in hex format from the next + * column. More columns may follow in the input text but won't get + * processed. (Mix of previous multi-column as well as single-column + * modes.) + * - ... -I csv:column_formats=4x8,b16,5l ... + * Get eight-bit data in hex format from the first four columns, then + * sixteen-bit data in binary format, then five times single-bit data. + * - ... -I csv:single_column=2:single_format=bin:logic_channels=8 ... + * Get eight logic bits in binary format from column 2. (Simple + * single-column mode, corresponds to the "-,b8" format.) + * - ... -I csv:first_column=6:logic_channels=4 ... + * Get four single-bit logic channels from columns 6 to 9 respectively. + * (Simple multi-column mode, corresponds to the "5-,4b" format.) + * - ... -I csv:start_line=20:header=yes:... + * Skip the first 19 text lines. Use line 20 to derive channel names. + * Data starts at line 21. */ /* * TODO * - * - Determine how the text line handling can get improved, regarding - * all of robustness and flexibility and correctness. - * - The current implementation splits on "any run of CR and LF". Which - * translates to: Line numbers are wrong in the presence of empty - * lines in the input stream. See below for an (expensive) fix. - * - Dropping support for CR style end-of-line markers could improve - * the situation a lot. Code could search for and split on LF, and - * trim optional trailing CR. This would result in proper support - * for CRLF (Windows) as well as LF (Unix), and allow for correct - * line number counts. - * - When support for CR-only line termination cannot get dropped, - * then the current implementation is inappropriate. Currently the - * input stream is scanned for the first occurance of either of the - * supported termination styles (which is good). For the remaining - * session a consistent encoding of the text lines is assumed (which - * is acceptable). - * - When line numbers need to be correct and reliable, _and_ the full - * set of previously supported line termination sequences are required, - * and potentially more are to get added for improved compatibility - * with more platforms or generators, then the current approach of - * splitting on runs of termination characters needs to get replaced, - * by the more expensive approach to scan for and count the initially - * determined termination sequence. - * * - Add support for analog input data? (optional) - * - Needs a syntax first for user specs which channels (columns) are - * logic and which are analog. May need heuristics(?) to guess from - * input data in the absence of user provided specs. + * - Extend the set of supported column types. Just grab a double + * value from floating point format input text. + * - Optionally get precision ('digits') from the column's format spec? + * From the position which is "bit count" for logic channels? + * - Optionally get sample rate from timestamp column. Just best-effort + * approach, not necessarily reliable. Users can always specify rates. + * - Add a test suite for input modules in general, and CSV in specific? + * Becomes more important with the multitude of options and their + * interaction. Could cover edge cases (BOM presence, line termination + * absence, etc) and auto-stuff as well (channel names, channel counts, + * samplerates, etc). */ /* Single column formats. */ @@ -221,7 +243,6 @@ static int flush_logic_samples(const struct sr_input *in) struct sr_datafeed_packet packet; struct sr_datafeed_meta meta; struct sr_config *src; - uint64_t samplerate; struct sr_datafeed_logic logic; int rc; @@ -232,8 +253,7 @@ static int flush_logic_samples(const struct sr_input *in) if (inc->samplerate && !inc->samplerate_sent) { packet.type = SR_DF_META; packet.payload = &meta; - samplerate = inc->samplerate; - src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate)); + src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate)); meta.config = g_slist_append(NULL, src); sr_session_send(in->sdi, &packet); g_slist_free(meta.config); @@ -290,6 +310,7 @@ static int split_column_format(const char *spec, /* Get the (optional, decimal, default 1) column count. Accept '*'. */ endp = NULL; if (*spec == '*') { + /* Workaround, strtoul("*") won't always yield expected endp. */ count = 0; endp = (char *)&spec[1]; } else { @@ -306,7 +327,7 @@ static int split_column_format(const char *spec, /* Get the (mandatory, single letter) type spec (-/xob/l). */ format_char = *spec++; switch (format_char) { - case '-': /* Might conflict with number-parsing. */ + case '-': case '/': format_char = '-'; format_code = FORMAT_NONE; @@ -349,17 +370,25 @@ static int split_column_format(const char *spec, return SR_OK; } -static int make_column_details_from_format(struct context *inc, - const char *column_format) +static int make_column_details_from_format(const struct sr_input *in, + const char *column_format, char **column_texts) { + struct context *inc; char **formats, *format; size_t format_count, column_count, bit_count; size_t auto_column_count; size_t format_idx, c, b, column_idx, channel_idx; enum single_col_format f; struct column_details *detail; + GString *channel_name; + size_t create_idx; + char *column; + const char *caption; int ret; + inc = in->priv; + inc->column_seen_count = g_strv_length(column_texts); + /* Split the input spec, count involved columns and bits. */ formats = g_strsplit(column_format, ",", 0); if (!formats) { @@ -399,16 +428,25 @@ static int make_column_details_from_format(struct context *inc, sr_dbg("Column format %s -> %zu columns, %zu logic channels.", column_format, column_count, bit_count); - /* Allocate and fill in "column processing" details. */ + /* Allocate and fill in "column processing" details. Create channels. */ inc->column_want_count = column_count; + if (inc->column_seen_count < inc->column_want_count) { + sr_err("Insufficient input text width for desired data amount, got %zu but want %zu columns.", + inc->column_seen_count, inc->column_want_count); + g_strfreev(formats); + return SR_ERR_ARG; + } inc->column_details = g_malloc0_n(column_count, sizeof(inc->column_details[0])); column_idx = channel_idx = 0; + channel_name = g_string_sized_new(64); for (format_idx = 0; format_idx < format_count; format_idx++) { + /* Process a format field, which can span multiple columns. */ format = formats[format_idx]; (void)split_column_format(format, &c, &f, &b); if (f && !c) c = auto_column_count; while (c-- > 0) { + /* Fill in a column's processing details. */ detail = &inc->column_details[column_idx++]; detail->col_nr = column_idx; detail->text_format = f; @@ -420,9 +458,42 @@ static int make_column_details_from_format(struct context *inc, sr_dbg("detail -> col %zu, fmt %s, ch off/cnt %zu/%zu", detail->col_nr, col_format_text[detail->text_format], detail->channel_offset, detail->channel_count); + if (!detail->text_format) + continue; + /* + * Create channels with appropriate names. Optionally + * use text from a header line (when requested by the + * user). In the absence of header text, channels are + * assigned rather generic names. + * + * Manipulation of the column's caption (when a header + * line is seen) is acceptable, because this header + * line won't get processed another time. + */ + column = column_texts[detail->col_nr - 1]; + if (inc->use_header && column && *column) + caption = sr_scpi_unquote_string(column); + else + caption = NULL; + if (!caption || !*caption) + caption = NULL; + for (create_idx = 0; create_idx < detail->channel_count; create_idx++) { + if (caption && detail->channel_count == 1) { + g_string_assign(channel_name, caption); + } else if (caption) { + g_string_printf(channel_name, "%s[%zu]", + caption, create_idx); + } else { + g_string_printf(channel_name, "%zu", + detail->channel_offset + create_idx); + } + sr_channel_new(in->sdi, detail->channel_offset + create_idx, + SR_CHANNEL_LOGIC, TRUE, channel_name->str); + } } } inc->logic_channels = channel_idx; + g_string_free(channel_name, TRUE); g_strfreev(formats); return SR_OK; @@ -617,18 +688,15 @@ static int init(struct sr_input *in, GHashTable *options) in->sdi = g_malloc0(sizeof(*in->sdi)); in->priv = inc = g_malloc0(sizeof(*inc)); - single_column = g_variant_get_uint32(g_hash_table_lookup(options, "single-column")); - - logic_channels = g_variant_get_uint32(g_hash_table_lookup(options, "numchannels")); - + single_column = g_variant_get_uint32(g_hash_table_lookup(options, "single_column")); + logic_channels = g_variant_get_uint32(g_hash_table_lookup(options, "logic_channels")); inc->delimiter = g_string_new(g_variant_get_string( - g_hash_table_lookup(options, "delimiter"), NULL)); + g_hash_table_lookup(options, "column_separator"), NULL)); if (!inc->delimiter->len) { - sr_err("Column delimiter cannot be empty."); + sr_err("Column separator cannot be empty."); return SR_ERR_ARG; } - - s = g_variant_get_string(g_hash_table_lookup(options, "format"), NULL); + s = g_variant_get_string(g_hash_table_lookup(options, "single_format"), NULL); if (g_ascii_strncasecmp(s, "bin", 3) == 0) { format = FORMAT_BIN; } else if (g_ascii_strncasecmp(s, "hex", 3) == 0) { @@ -636,30 +704,25 @@ static int init(struct sr_input *in, GHashTable *options) } else if (g_ascii_strncasecmp(s, "oct", 3) == 0) { format = FORMAT_OCT; } else { - sr_err("Invalid format: '%s'", s); + sr_err("Invalid single-column format: '%s'", s); return SR_ERR_ARG; } - inc->comment = g_string_new(g_variant_get_string( - g_hash_table_lookup(options, "comment"), NULL)); + g_hash_table_lookup(options, "comment_leader"), NULL)); if (g_string_equal(inc->comment, inc->delimiter)) { /* * Using the same sequence as comment leader and column - * delimiter won't work. The user probably specified ';' - * as the column delimiter but did not adjust the comment + * separator won't work. The user probably specified ';' + * as the column separator but did not adjust the comment * leader. Try DWIM, drop comment strippin support here. */ - sr_warn("Comment leader and column delimiter conflict, disabling comment support."); + sr_warn("Comment leader and column separator conflict, disabling comment support."); g_string_truncate(inc->comment, 0); } - inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate")); - - first_column = g_variant_get_uint32(g_hash_table_lookup(options, "first-column")); - + first_column = g_variant_get_uint32(g_hash_table_lookup(options, "first_column")); inc->use_header = g_variant_get_boolean(g_hash_table_lookup(options, "header")); - - inc->start_line = g_variant_get_uint32(g_hash_table_lookup(options, "startline")); + inc->start_line = g_variant_get_uint32(g_hash_table_lookup(options, "start_line")); if (inc->start_line < 1) { sr_err("Invalid start line %zu.", inc->start_line); return SR_ERR_ARG; @@ -676,10 +739,10 @@ static int init(struct sr_input *in, GHashTable *options) * of input data was seen. To support automatic determination of * e.g. channel counts from column counts. */ - s = g_variant_get_string(g_hash_table_lookup(options, "column-formats"), NULL); + s = g_variant_get_string(g_hash_table_lookup(options, "column_formats"), NULL); if (s && *s) { inc->column_formats = g_strdup(s); - sr_dbg("User specified column-formats: %s.", s); + sr_dbg("User specified column_formats: %s.", s); } else if (single_column && logic_channels) { format_char = col_format_char[format]; if (single_column == 1) { @@ -690,7 +753,7 @@ static int init(struct sr_input *in, GHashTable *options) single_column - 1, format_char, logic_channels); } - sr_dbg("Backwards compat single-column, col %zu, fmt %s, bits %zu -> %s.", + sr_dbg("Backwards compat single_column, col %zu, fmt %s, bits %zu -> %s.", single_column, col_format_text[format], logic_channels, inc->column_formats); } else if (!single_column) { @@ -705,7 +768,7 @@ static int init(struct sr_input *in, GHashTable *options) first_column, logic_channels, inc->column_formats); } else { - sr_warn("Unknown or unsupported format spec, assuming trivial multi-column."); + sr_warn("Unknown or unsupported columns layout spec, assuming simple multi-column mode."); inc->column_formats = g_strdup("*l"); } @@ -770,19 +833,16 @@ static const char *get_line_termination(GString *buf) static int initial_parse(const struct sr_input *in, GString *buf) { struct context *inc; - GString *channel_name; - size_t num_columns, ch_idx, ch_name_idx, col_idx, col_nr; + size_t num_columns; size_t line_number, line_idx; int ret; - char **lines, *line, **columns, *column; - const char *col_caption; - gboolean got_caption; - const struct column_details *detail; + char **lines, *line, **columns; ret = SR_OK; inc = in->priv; columns = NULL; + /* Search for the first line to process (header or data). */ line_number = 0; if (inc->termination) lines = g_strsplit(buf->str, inc->termination, 0); @@ -813,7 +873,7 @@ static int initial_parse(const struct sr_input *in, GString *buf) goto out; } - /* See how many columns the current line has. */ + /* Get the number of columns in the line. */ columns = split_line(line, inc); if (!columns) { sr_err("Error while parsing line %zu.", line_number); @@ -829,72 +889,27 @@ static int initial_parse(const struct sr_input *in, GString *buf) sr_dbg("DIAG Got %zu columns in text line: %s.", num_columns, line); /* - * Track the observed number of columns in the input file. Do - * process the previously gathered columns format spec now that - * automatic channel count can be dealt with. + * Interpret the user provided column format specs. This might + * involve inspection of the now received input text, to support + * e.g. automatic detection of channel counts in the absence of + * user provided specs. Optionally a header line is used to get + * channels' names. + * + * Check the then created channels for consistency across .reset + * and .receive sequences (file re-load). */ - inc->column_seen_count = num_columns; - ret = make_column_details_from_format(inc, inc->column_formats); + ret = make_column_details_from_format(in, inc->column_formats, columns); if (ret != SR_OK) { sr_err("Cannot parse columns format using line %zu.", line_number); goto out; } - - /* - * Assume all lines have equal length (column count). Bail out - * early on suspicious or insufficient input data (check input - * which became available here against previous user specs or - * auto-determined properties, regardless of layout variant). - */ - if (num_columns < inc->column_want_count) { - sr_err("Insufficient input text width for desired data amount, got %zu but want %zu columns.", - num_columns, inc->column_want_count); - ret = SR_ERR; - goto out; - } - - /* - * Determine channel names. Optionally use text from a header - * line (when requested by the user, and only works in multi - * column mode). In the absence of header text, or in single - * column mode, channels are assigned rather generic names. - * - * Manipulation of the column's caption is acceptable here, the - * header line will never get processed another time. - */ - channel_name = g_string_sized_new(64); - for (col_idx = 0; col_idx < inc->column_want_count; col_idx++) { - - col_nr = col_idx + 1; - detail = lookup_column_details(inc, col_nr); - if (detail->text_format == FORMAT_NONE) - continue; - column = columns[col_idx]; - col_caption = sr_scpi_unquote_string(column); - got_caption = inc->use_header && *col_caption; - sr_dbg("DIAG col %zu, ch count %zu, text %s.", - col_nr, detail->channel_count, col_caption); - for (ch_idx = 0; ch_idx < detail->channel_count; ch_idx++) { - ch_name_idx = detail->channel_offset + ch_idx; - if (got_caption && detail->channel_count == 1) - g_string_assign(channel_name, col_caption); - else if (got_caption) - g_string_printf(channel_name, "%s[%zu]", - col_caption, ch_idx); - else - g_string_printf(channel_name, "%zu", ch_name_idx); - sr_dbg("DIAG ch idx %zu, name %s.", ch_name_idx, channel_name->str); - sr_channel_new(in->sdi, ch_name_idx, SR_CHANNEL_LOGIC, TRUE, - channel_name->str); - } - } - g_string_free(channel_name, TRUE); if (!check_header_in_reread(in)) { ret = SR_ERR_DATA; goto out; } /* + * Allocate buffer memory for datafeed submission of sample data. * Calculate the minimum buffer size to store the set of samples * of all channels (unit size). Determine a larger buffer size * for datafeed submission that is a multiple of the unit size. @@ -1171,28 +1186,68 @@ static int reset(struct sr_input *in) enum option_index { OPT_COL_FMTS, OPT_SINGLE_COL, + OPT_FIRST_COL, OPT_NUM_LOGIC, - OPT_DELIM, OPT_FORMAT, - OPT_COMMENT, - OPT_RATE, - OPT_FIRST_COL, - OPT_HEADER, OPT_START, + OPT_HEADER, + OPT_RATE, + OPT_DELIM, + OPT_COMMENT, OPT_MAX, }; static struct sr_option options[] = { - [OPT_COL_FMTS] = { "column-formats", "Column format specs", "Specifies text columns data types: comma separated list of [][]", NULL, NULL }, - [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_COL] = { "first-column", "First column", "The column number of the first channel (multi-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_COL_FMTS] = { + "column_formats", "Column format specs", + "Specifies text columns data types: comma separated list of [][], with -/x/o/b/l format specifiers.", + NULL, NULL, + }, + [OPT_SINGLE_COL] = { + "single_column", "Single column", + "Enable single-column mode, exclusively use text from the specified column (number starting at 1).", + NULL, NULL, + }, + [OPT_FIRST_COL] = { + "first_column", "First column", + "Number of the first column with logic data in simple multi-column mode (number starting at 1, default 1).", + NULL, NULL, + }, + [OPT_NUM_LOGIC] = { + "logic_channels", "Number of logic channels", + "Logic channel count, required in simple single-column mode, defaults to \"all remaining columns\" in simple multi-column mode. Obsoleted by 'column_formats'.", + NULL, NULL, + }, + [OPT_FORMAT] = { + "single_format", "Data format for simple single-column mode.", + "The number format of single-column mode input data: bin, hex, oct.", + NULL, NULL, + }, + [OPT_START] = { + "start_line", "Start line", + "The line number at which to start processing input text (default: 1).", + NULL, NULL, + }, + [OPT_HEADER] = { + "header", "Get channel names from first line.", + "Use the first processed line's column captions (when available) as channel names.", + NULL, NULL, + }, + [OPT_RATE] = { + "samplerate", "Samplerate (Hz)", + "The input data's sample rate in Hz.", + NULL, NULL, + }, + [OPT_DELIM] = { + "column_separator", "Column separator", + "The sequence which separates text columns. Non-empty text, comma by default.", + NULL, NULL, + }, + [OPT_COMMENT] = { + "comment_leader", "Comment leader character", + "The text which starts comments at the end of text lines.", + NULL, NULL, + }, [OPT_MAX] = ALL_ZERO, }; @@ -1203,19 +1258,19 @@ static const struct sr_option *get_options(void) if (!options[0].def) { options[OPT_COL_FMTS].def = g_variant_ref_sink(g_variant_new_string("")); options[OPT_SINGLE_COL].def = g_variant_ref_sink(g_variant_new_uint32(0)); + options[OPT_FIRST_COL].def = g_variant_ref_sink(g_variant_new_uint32(1)); options[OPT_NUM_LOGIC].def = g_variant_ref_sink(g_variant_new_uint32(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[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_COL].def = g_variant_ref_sink(g_variant_new_uint32(1)); - options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE)); options[OPT_START].def = g_variant_ref_sink(g_variant_new_uint32(1)); + options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE)); + options[OPT_RATE].def = g_variant_ref_sink(g_variant_new_uint64(0)); + options[OPT_DELIM].def = g_variant_ref_sink(g_variant_new_string(",")); + options[OPT_COMMENT].def = g_variant_ref_sink(g_variant_new_string(";")); } return options;