]> sigrok.org Git - libsigrok.git/blame - src/input/csv.c
input/csv: rework user accessible options for consistency
[libsigrok.git] / src / input / csv.c
CommitLineData
4a35548b
MS
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2013 Marc Schink <sigrok-dev@marcschink.de>
e53f32d2 5 * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
4a35548b
MS
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
e05f1827
GS
21#include "config.h"
22
23#include <glib.h>
4a35548b
MS
24#include <stdlib.h>
25#include <string.h>
e05f1827 26
c1aae900 27#include <libsigrok/libsigrok.h>
4a35548b 28#include "libsigrok-internal.h"
f6dcb320 29#include "scpi.h" /* String un-quote for channel name from header line. */
4a35548b 30
3544f848 31#define LOG_PREFIX "input/csv"
4a35548b 32
9a4fd01a 33#define CHUNK_SIZE (4 * 1024 * 1024)
cd59e6ec 34
4a35548b
MS
35/*
36 * The CSV input module has the following options:
37 *
72903e9d
GS
38 * column_formats: Specifies the data formats and channel counts for the
39 * input file's text columns. Accepts a comma separated list of tuples
40 * with: an optional column repeat count ('*' as a wildcard meaning
41 * "all remaining columns", only applicable to the last field), a format
42 * specifying character ('x' hexadecimal, 'o' octal, 'b' binary, 'l'
43 * single-bit logic), and an optional bit count (translating to: logic
44 * channels communicated in that column). This "column_formats" option
45 * is most versatile, other forms of specifying the column layout only
46 * exist for backwards compatibility.
4a35548b 47 *
72903e9d
GS
48 * single_column: Specifies the column number which contains the logic data
49 * for single-column mode. All logic data is taken from several bits
50 * which all are kept within that one column. Only exists for backwards
51 * compatibility, see "column_formats" for more flexibility.
4a35548b 52 *
72903e9d
GS
53 * first_column: Specifies the number of the first column with logic data
54 * in simple multi-column mode. Only exists for backwards compatibility,
55 * see "column_formats" for more flexibility.
4a35548b 56 *
72903e9d
GS
57 * logic_channels: Specifies the number of logic channels. Is required in
58 * simple single-column mode. Is optional in simple multi-column mode
59 * (and defaults to all remaining columns). Only exists for backwards
60 * compatibility, see "column_formats" for more flexibility.
4a35548b 61 *
72903e9d
GS
62 * single_format: Specifies the format of the input text in simple single-
63 * column mode. Available formats are: 'bin' (default), 'hex' and 'oct'.
64 * Simple multi-column mode always uses single-bit data per column.
65 * Only exists for backwards compatibility, see "column_formats" for
66 * more flexibility.
4a35548b 67 *
72903e9d
GS
68 * start_line: Specifies at which line to start processing the input file.
69 * Allows to skip leading lines which neither are header nor data lines.
70 * By default all of the input file gets processed.
4a35548b 71 *
72903e9d
GS
72 * header: Boolean option, controls whether the first processed line is used
73 * to determine channel names. Off by default. Generic channel names are
74 * used in the absence of header line content.
4a35548b 75 *
72903e9d
GS
76 * samplerate: Specifies the samplerate of the input data. Defaults to 0.
77 * User specs take precedence over data which optionally gets derived
78 * from input data.
4a35548b 79 *
72903e9d
GS
80 * column_separator: Specifies the sequence which separates the text file
81 * columns. Cannot be empty. Defaults to comma.
82 *
83 * comment_leader: Specifies the sequence which starts comments that run
84 * up to the end of the current text line. Can be empty to disable
85 * comment support. Defaults to semicolon.
86 *
87 * Typical examples of using these options:
88 * - ... -I csv:column_formats=*l ...
89 * All columns are single-bit logic data. Identical to the previous
90 * multi-column mode (the default when no options were given at all).
91 * - ... -I csv:column_formats=3-,*l ...
92 * Ignore the first three columns, get single-bit logic data from all
93 * remaining lines (multi-column mode with first-column above 1).
94 * - ... -I csv:column_formats=3-,4l,x8 ...
95 * Ignore the first three columns, get single-bit logic data from the
96 * next four columns, then eight-bit data in hex format from the next
97 * column. More columns may follow in the input text but won't get
98 * processed. (Mix of previous multi-column as well as single-column
99 * modes.)
100 * - ... -I csv:column_formats=4x8,b16,5l ...
101 * Get eight-bit data in hex format from the first four columns, then
102 * sixteen-bit data in binary format, then five times single-bit data.
103 * - ... -I csv:single_column=2:single_format=bin:logic_channels=8 ...
104 * Get eight logic bits in binary format from column 2. (Simple
105 * single-column mode, corresponds to the "-,b8" format.)
106 * - ... -I csv:first_column=6:logic_channels=4 ...
107 * Get four single-bit logic channels from columns 6 to 9 respectively.
108 * (Simple multi-column mode, corresponds to the "5-,4b" format.)
109 * - ... -I csv:start_line=20:header=yes:...
110 * Skip the first 19 text lines. Use line 20 to derive channel names.
111 * Data starts at line 21.
4a35548b
MS
112 */
113
ccff468b
GS
114/*
115 * TODO
116 *
117 * - Determine how the text line handling can get improved, regarding
118 * all of robustness and flexibility and correctness.
119 * - The current implementation splits on "any run of CR and LF". Which
120 * translates to: Line numbers are wrong in the presence of empty
de788af4 121 * lines in the input stream. See below for an (expensive) fix.
ccff468b
GS
122 * - Dropping support for CR style end-of-line markers could improve
123 * the situation a lot. Code could search for and split on LF, and
124 * trim optional trailing CR. This would result in proper support
125 * for CRLF (Windows) as well as LF (Unix), and allow for correct
126 * line number counts.
127 * - When support for CR-only line termination cannot get dropped,
128 * then the current implementation is inappropriate. Currently the
129 * input stream is scanned for the first occurance of either of the
130 * supported termination styles (which is good). For the remaining
131 * session a consistent encoding of the text lines is assumed (which
de788af4 132 * is acceptable).
ccff468b
GS
133 * - When line numbers need to be correct and reliable, _and_ the full
134 * set of previously supported line termination sequences are required,
135 * and potentially more are to get added for improved compatibility
136 * with more platforms or generators, then the current approach of
137 * splitting on runs of termination characters needs to get replaced,
138 * by the more expensive approach to scan for and count the initially
139 * determined termination sequence.
140 *
141 * - Add support for analog input data? (optional)
142 * - Needs a syntax first for user specs which channels (columns) are
143 * logic and which are analog. May need heuristics(?) to guess from
144 * input data in the absence of user provided specs.
145 */
146
4a35548b 147/* Single column formats. */
ad6a2bee 148enum single_col_format {
e53f32d2
GS
149 FORMAT_NONE, /* Ignore this column. */
150 FORMAT_BIN, /* Bin digits for a set of bits (or just one bit). */
151 FORMAT_HEX, /* Hex digits for a set of bits. */
152 FORMAT_OCT, /* Oct digits for a set of bits. */
153};
154
155static const char *col_format_text[] = {
156 [FORMAT_NONE] = "unknown",
157 [FORMAT_BIN] = "binary",
158 [FORMAT_HEX] = "hexadecimal",
159 [FORMAT_OCT] = "octal",
160};
161
1a920e33
GS
162static const char col_format_char[] = {
163 [FORMAT_NONE] = '?',
164 [FORMAT_BIN] = 'b',
165 [FORMAT_HEX] = 'x',
166 [FORMAT_OCT] = 'o',
167};
168
e53f32d2
GS
169struct column_details {
170 size_t col_nr;
171 enum single_col_format text_format;
172 size_t channel_offset;
173 size_t channel_count;
4a35548b
MS
174};
175
176struct context {
41d214f6
BV
177 gboolean started;
178
4a35548b
MS
179 /* Current selected samplerate. */
180 uint64_t samplerate;
246aca5f 181 gboolean samplerate_sent;
4a35548b 182
836fac9c
GS
183 /* Number of logic channels. */
184 size_t logic_channels;
4a35548b 185
836fac9c 186 /* Column delimiter (actually separator), comment leader, EOL sequence. */
4a35548b 187 GString *delimiter;
4a35548b 188 GString *comment;
41d214f6
BV
189 char *termination;
190
1a920e33
GS
191 /* Format specs for input columns, and processing state. */
192 size_t column_seen_count;
193 const char *column_formats;
e53f32d2
GS
194 size_t column_want_count;
195 struct column_details *column_details;
196
4a35548b 197 /* Line number to start processing. */
6433156c 198 size_t start_line;
4a35548b
MS
199
200 /*
201 * Determines if the first line should be treated as header and used for
ba7dd8bb 202 * channel names in multi column mode.
4a35548b 203 */
de8fe3b5
GS
204 gboolean use_header;
205 gboolean header_seen;
4a35548b 206
cd59e6ec
GS
207 size_t sample_unit_size; /**!< Byte count for a single sample. */
208 uint8_t *sample_buffer; /**!< Buffer for a single sample. */
4a35548b 209
cd59e6ec
GS
210 uint8_t *datafeed_buffer; /**!< Queue for datafeed submission. */
211 size_t datafeed_buf_size;
212 size_t datafeed_buf_fill;
4a35548b 213
4a35548b 214 /* Current line number. */
6433156c 215 size_t line_number;
affaf540
GS
216
217 /* List of previously created sigrok channels. */
218 GSList *prev_sr_channels;
4a35548b
MS
219};
220
626c388a
GS
221/*
222 * Primitive operations to handle sample sets:
223 * - Keep a buffer for datafeed submission, capable of holding many
224 * samples (reduces call overhead, improves throughput).
225 * - Have a "current sample set" pointer reference one position in that
226 * large samples buffer.
227 * - Clear the current sample set before text line inspection, then set
228 * the bits which are found active in the current line of text input.
229 * Phrase the API such that call sites can be kept simple. Advance to
230 * the next sample set between lines, flush the larger buffer as needed
231 * (when it is full, or upon EOF).
232 */
233
234static void clear_logic_samples(struct context *inc)
235{
236 inc->sample_buffer = &inc->datafeed_buffer[inc->datafeed_buf_fill];
237 memset(inc->sample_buffer, 0, inc->sample_unit_size);
238}
239
240static void set_logic_level(struct context *inc, size_t ch_idx, int on)
241{
242 size_t byte_idx, bit_idx;
243 uint8_t bit_mask;
244
836fac9c 245 if (ch_idx >= inc->logic_channels)
626c388a
GS
246 return;
247 if (!on)
248 return;
249
250 byte_idx = ch_idx / 8;
251 bit_idx = ch_idx % 8;
252 bit_mask = 1 << bit_idx;
253 inc->sample_buffer[byte_idx] |= bit_mask;
254}
255
256static int flush_logic_samples(const struct sr_input *in)
257{
258 struct context *inc;
259 struct sr_datafeed_packet packet;
246aca5f
GS
260 struct sr_datafeed_meta meta;
261 struct sr_config *src;
262 uint64_t samplerate;
626c388a
GS
263 struct sr_datafeed_logic logic;
264 int rc;
265
266 inc = in->priv;
267 if (!inc->datafeed_buf_fill)
268 return SR_OK;
269
246aca5f
GS
270 if (inc->samplerate && !inc->samplerate_sent) {
271 packet.type = SR_DF_META;
272 packet.payload = &meta;
273 samplerate = inc->samplerate;
274 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate));
275 meta.config = g_slist_append(NULL, src);
276 sr_session_send(in->sdi, &packet);
277 g_slist_free(meta.config);
278 sr_config_free(src);
279 inc->samplerate_sent = TRUE;
280 }
281
626c388a
GS
282 memset(&packet, 0, sizeof(packet));
283 memset(&logic, 0, sizeof(logic));
284 packet.type = SR_DF_LOGIC;
285 packet.payload = &logic;
286 logic.unitsize = inc->sample_unit_size;
287 logic.length = inc->datafeed_buf_fill;
288 logic.data = inc->datafeed_buffer;
289
290 rc = sr_session_send(in->sdi, &packet);
291 if (rc != SR_OK)
292 return rc;
293
294 inc->datafeed_buf_fill = 0;
295 return SR_OK;
296}
297
298static int queue_logic_samples(const struct sr_input *in)
299{
300 struct context *inc;
301 int rc;
302
303 inc = in->priv;
836fac9c
GS
304 if (!inc->logic_channels)
305 return SR_OK;
626c388a
GS
306
307 inc->datafeed_buf_fill += inc->sample_unit_size;
308 if (inc->datafeed_buf_fill == inc->datafeed_buf_size) {
309 rc = flush_logic_samples(in);
310 if (rc != SR_OK)
311 return rc;
312 }
313 return SR_OK;
314}
315
2142a79b
GS
316/* Helpers for "column processing". */
317
318static int split_column_format(const char *spec,
319 size_t *column_count, enum single_col_format *format, size_t *bit_count)
320{
321 size_t count;
322 char *endp, format_char;
323 enum single_col_format format_code;
324
325 if (!spec || !*spec)
326 return SR_ERR_ARG;
327
1a920e33 328 /* Get the (optional, decimal, default 1) column count. Accept '*'. */
2142a79b 329 endp = NULL;
1a920e33
GS
330 if (*spec == '*') {
331 count = 0;
332 endp = (char *)&spec[1];
333 } else {
334 count = strtoul(spec, &endp, 10);
335 }
2142a79b
GS
336 if (!endp)
337 return SR_ERR_ARG;
338 if (endp == spec)
339 count = 1;
340 if (column_count)
341 *column_count = count;
342 spec = endp;
343
344 /* Get the (mandatory, single letter) type spec (-/xob/l). */
345 format_char = *spec++;
346 switch (format_char) {
347 case '-': /* Might conflict with number-parsing. */
348 case '/':
349 format_char = '-';
350 format_code = FORMAT_NONE;
351 break;
352 case 'x':
353 format_code = FORMAT_HEX;
354 break;
355 case 'o':
356 format_code = FORMAT_OCT;
357 break;
358 case 'b':
359 case 'l':
360 format_code = FORMAT_BIN;
361 break;
362 default: /* includes NUL */
363 return SR_ERR_ARG;
364 }
365 if (format)
366 *format = format_code;
367
368 /* Get the (optional, decimal, default 1) bit count. */
369 endp = NULL;
370 count = strtoul(spec, &endp, 10);
371 if (!endp)
372 return SR_ERR_ARG;
373 if (endp == spec)
374 count = 1;
375 if (format_char == '-')
376 count = 0;
377 if (format_char == 'l')
378 count = 1;
379 if (bit_count)
380 *bit_count = count;
381 spec = endp;
382
383 /* Input spec must have been exhausted. */
384 if (*spec)
385 return SR_ERR_ARG;
386
387 return SR_OK;
388}
389
390static int make_column_details_from_format(struct context *inc,
391 const char *column_format)
392{
393 char **formats, *format;
394 size_t format_count, column_count, bit_count;
1a920e33 395 size_t auto_column_count;
2142a79b
GS
396 size_t format_idx, c, b, column_idx, channel_idx;
397 enum single_col_format f;
398 struct column_details *detail;
399 int ret;
400
2142a79b
GS
401 /* Split the input spec, count involved columns and bits. */
402 formats = g_strsplit(column_format, ",", 0);
403 if (!formats) {
404 sr_err("Cannot parse columns format %s (comma split).", column_format);
405 return SR_ERR_ARG;
406 }
407 format_count = g_strv_length(formats);
408 if (!format_count) {
409 sr_err("Cannot parse columns format %s (field count).", column_format);
410 g_strfreev(formats);
411 return SR_ERR_ARG;
412 }
413 column_count = bit_count = 0;
1a920e33 414 auto_column_count = 0;
2142a79b
GS
415 for (format_idx = 0; format_idx < format_count; format_idx++) {
416 format = formats[format_idx];
417 ret = split_column_format(format, &c, &f, &b);
418 sr_dbg("fmt %s -> %zu cols, %s fmt, %zu bits, rc %d", format, c, col_format_text[f], b, ret);
419 if (ret != SR_OK) {
420 sr_err("Cannot parse columns format %s (field split, %s).", column_format, format);
421 g_strfreev(formats);
422 return SR_ERR_ARG;
423 }
1a920e33
GS
424 if (f && !c) {
425 /* User requested "auto-count", must be last format. */
426 if (formats[format_idx + 1]) {
427 sr_err("Auto column count must be last format field.");
428 g_strfreev(formats);
429 return SR_ERR_ARG;
430 }
431 auto_column_count = inc->column_seen_count - column_count;
432 c = auto_column_count;
433 }
2142a79b
GS
434 column_count += c;
435 bit_count += c * b;
436 }
437 sr_dbg("Column format %s -> %zu columns, %zu logic channels.",
438 column_format, column_count, bit_count);
439
440 /* Allocate and fill in "column processing" details. */
441 inc->column_want_count = column_count;
442 inc->column_details = g_malloc0_n(column_count, sizeof(inc->column_details[0]));
443 column_idx = channel_idx = 0;
444 for (format_idx = 0; format_idx < format_count; format_idx++) {
445 format = formats[format_idx];
446 (void)split_column_format(format, &c, &f, &b);
1a920e33
GS
447 if (f && !c)
448 c = auto_column_count;
2142a79b
GS
449 while (c-- > 0) {
450 detail = &inc->column_details[column_idx++];
451 detail->col_nr = column_idx;
452 detail->text_format = f;
453 if (detail->text_format) {
454 detail->channel_offset = channel_idx;
455 detail->channel_count = b;
456 channel_idx += b;
457 }
458 sr_dbg("detail -> col %zu, fmt %s, ch off/cnt %zu/%zu",
459 detail->col_nr, col_format_text[detail->text_format],
460 detail->channel_offset, detail->channel_count);
461 }
462 }
463 inc->logic_channels = channel_idx;
464 g_strfreev(formats);
465
466 return SR_OK;
467}
468
e53f32d2
GS
469static const struct column_details *lookup_column_details(struct context *inc, size_t nr)
470{
471 if (!inc || !inc->column_details)
472 return NULL;
473 if (!nr || nr > inc->column_want_count)
474 return NULL;
475 return &inc->column_details[nr - 1];
476}
477
19267272
GS
478/*
479 * Primitive operations for text input: Strip comments off text lines.
480 * Split text lines into columns. Process input text for individual
481 * columns.
482 */
483
41d214f6 484static void strip_comment(char *buf, const GString *prefix)
4a35548b
MS
485{
486 char *ptr;
487
488 if (!prefix->len)
489 return;
490
b2c4dde2 491 if ((ptr = strstr(buf, prefix->str))) {
41d214f6 492 *ptr = '\0';
b2c4dde2
GS
493 g_strstrip(buf);
494 }
4a35548b
MS
495}
496
19267272 497/**
e53f32d2 498 * @brief Splits a text line into a set of columns.
19267272 499 *
e53f32d2 500 * @param[in] buf The input text line to split.
19267272
GS
501 * @param[in] inc The input module's context.
502 *
e53f32d2 503 * @returns An array of strings, representing the columns' text.
19267272 504 *
e53f32d2 505 * This routine splits a text line on previously determined separators.
19267272 506 */
e53f32d2 507static char **split_line(char *buf, struct context *inc)
4a35548b 508{
e53f32d2 509 return g_strsplit(buf, inc->delimiter->str, 0);
4a35548b
MS
510}
511
19267272 512/**
e53f32d2 513 * @brief Parse a multi-bit field into several logic channels.
19267272 514 *
e53f32d2 515 * @param[in] column The input text, a run of bin/hex/oct digits.
19267272 516 * @param[in] inc The input module's context.
836fac9c 517 * @param[in] details The column processing details.
19267272
GS
518 *
519 * @retval SR_OK Success.
520 * @retval SR_ERR Invalid input data (empty, or format error).
521 *
522 * This routine modifies the logic levels in the current sample set,
e53f32d2 523 * based on the text input and a user provided format spec.
19267272 524 */
836fac9c
GS
525static int parse_logic(const char *column, struct context *inc,
526 const struct column_details *details)
4a35548b 527{
e53f32d2
GS
528 size_t length, ch_rem, ch_idx, ch_inc;
529 const char *rdptr;
4a35548b 530 char c;
e53f32d2
GS
531 gboolean valid;
532 const char *type_text;
533 uint8_t bits;
534
e53f32d2
GS
535 /*
536 * Prepare to read the digits from the text end towards the start.
537 * A digit corresponds to a variable number of channels (depending
538 * on the value's radix). Prepare the mapping of text digits to
539 * (a number of) logic channels.
540 */
541 length = strlen(column);
4a35548b 542 if (!length) {
836fac9c 543 sr_err("Column %zu in line %zu is empty.", details->col_nr,
41d214f6 544 inc->line_number);
4a35548b
MS
545 return SR_ERR;
546 }
e53f32d2 547 rdptr = &column[length];
836fac9c
GS
548 ch_idx = details->channel_offset;
549 ch_rem = details->channel_count;
4a35548b 550
e53f32d2
GS
551 /*
552 * Get another digit and derive up to four logic channels' state from
553 * it. Make sure to not process more bits than the column has channels
554 * associated with it.
555 */
556 while (rdptr > column && ch_rem) {
557 /* Check for valid digits according to the input radix. */
558 c = *(--rdptr);
836fac9c 559 switch (details->text_format) {
e53f32d2
GS
560 case FORMAT_BIN:
561 valid = g_ascii_isxdigit(c) && c < '2';
562 ch_inc = 1;
563 break;
564 case FORMAT_OCT:
565 valid = g_ascii_isxdigit(c) && c < '8';
566 ch_inc = 3;
567 break;
568 case FORMAT_HEX:
569 valid = g_ascii_isxdigit(c);
570 ch_inc = 4;
571 break;
572 default:
573 valid = FALSE;
574 break;
4a35548b 575 }
e53f32d2 576 if (!valid) {
836fac9c 577 type_text = col_format_text[details->text_format];
e53f32d2 578 sr_err("Invalid text '%s' in %s type column %zu in line %zu.",
836fac9c 579 column, type_text, details->col_nr, inc->line_number);
4a35548b 580 return SR_ERR;
e53f32d2
GS
581 }
582 /* Use the digit's bits for logic channels' data. */
583 bits = g_ascii_xdigit_value(c);
836fac9c 584 switch (details->text_format) {
e53f32d2
GS
585 case FORMAT_HEX:
586 if (ch_rem >= 4) {
587 ch_rem--;
588 set_logic_level(inc, ch_idx + 3, bits & (1 << 3));
589 }
590 /* FALLTHROUGH */
591 case FORMAT_OCT:
592 if (ch_rem >= 3) {
593 ch_rem--;
594 set_logic_level(inc, ch_idx + 2, bits & (1 << 2));
595 }
596 if (ch_rem >= 2) {
597 ch_rem--;
598 set_logic_level(inc, ch_idx + 1, bits & (1 << 1));
599 }
600 /* FALLTHROUGH */
601 case FORMAT_BIN:
602 ch_rem--;
603 set_logic_level(inc, ch_idx + 0, bits & (1 << 0));
604 break;
836fac9c
GS
605 case FORMAT_NONE:
606 /* ShouldNotHappen(TM), but silences compiler warning. */
607 return SR_ERR;
4a35548b 608 }
e53f32d2 609 ch_idx += ch_inc;
4a35548b 610 }
e53f32d2
GS
611 /*
612 * TODO Determine whether the availability of extra input data
613 * for unhandled logic channels is worth warning here. In this
614 * implementation users are in control, and can have the more
615 * significant bits ignored (which can be considered a feature
616 * and not really a limitation).
617 */
4a35548b
MS
618
619 return SR_OK;
620}
621
836fac9c
GS
622/**
623 * @brief Parse routine which ignores the input text.
624 *
625 * This routine exists to unify dispatch code paths, mapping input file
626 * columns' data types to their respective parse routines.
627 */
628static int parse_ignore(const char *column, struct context *inc,
629 const struct column_details *details)
630{
631 (void)column;
632 (void)inc;
633 (void)details;
634 return SR_OK;
635}
636
637typedef int (*col_parse_cb)(const char *column, struct context *inc,
638 const struct column_details *details);
639
640static const col_parse_cb col_parse_funcs[] = {
641 [FORMAT_NONE] = parse_ignore,
642 [FORMAT_BIN] = parse_logic,
643 [FORMAT_OCT] = parse_logic,
644 [FORMAT_HEX] = parse_logic,
645};
646
41d214f6 647static int init(struct sr_input *in, GHashTable *options)
4a35548b 648{
41d214f6 649 struct context *inc;
1a920e33 650 size_t single_column, first_column, logic_channels;
41d214f6 651 const char *s;
836fac9c 652 enum single_col_format format;
1a920e33 653 char format_char;
4a35548b 654
836fac9c
GS
655 in->sdi = g_malloc0(sizeof(*in->sdi));
656 in->priv = inc = g_malloc0(sizeof(*inc));
4a35548b 657
72903e9d 658 single_column = g_variant_get_uint32(g_hash_table_lookup(options, "single_column"));
4a35548b 659
72903e9d 660 logic_channels = g_variant_get_uint32(g_hash_table_lookup(options, "logic_channels"));
4a35548b 661
41d214f6 662 inc->delimiter = g_string_new(g_variant_get_string(
72903e9d 663 g_hash_table_lookup(options, "column_separator"), NULL));
836fac9c 664 if (!inc->delimiter->len) {
72903e9d 665 sr_err("Column separator cannot be empty.");
41d214f6 666 return SR_ERR_ARG;
4a35548b
MS
667 }
668
72903e9d 669 s = g_variant_get_string(g_hash_table_lookup(options, "single_format"), NULL);
836fac9c
GS
670 if (g_ascii_strncasecmp(s, "bin", 3) == 0) {
671 format = FORMAT_BIN;
672 } else if (g_ascii_strncasecmp(s, "hex", 3) == 0) {
673 format = FORMAT_HEX;
674 } else if (g_ascii_strncasecmp(s, "oct", 3) == 0) {
675 format = FORMAT_OCT;
41d214f6 676 } else {
72903e9d 677 sr_err("Invalid single-column format: '%s'", s);
41d214f6 678 return SR_ERR_ARG;
4a35548b
MS
679 }
680
41d214f6 681 inc->comment = g_string_new(g_variant_get_string(
72903e9d 682 g_hash_table_lookup(options, "comment_leader"), NULL));
41d214f6 683 if (g_string_equal(inc->comment, inc->delimiter)) {
e53f32d2
GS
684 /*
685 * Using the same sequence as comment leader and column
72903e9d
GS
686 * separator won't work. The user probably specified ';'
687 * as the column separator but did not adjust the comment
e53f32d2
GS
688 * leader. Try DWIM, drop comment strippin support here.
689 */
72903e9d 690 sr_warn("Comment leader and column separator conflict, disabling comment support.");
41d214f6 691 g_string_truncate(inc->comment, 0);
4a35548b
MS
692 }
693
6e8d95a5 694 inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
4a35548b 695
72903e9d 696 first_column = g_variant_get_uint32(g_hash_table_lookup(options, "first_column"));
4a35548b 697
de8fe3b5 698 inc->use_header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
4a35548b 699
72903e9d 700 inc->start_line = g_variant_get_uint32(g_hash_table_lookup(options, "start_line"));
41d214f6 701 if (inc->start_line < 1) {
6433156c 702 sr_err("Invalid start line %zu.", inc->start_line);
41d214f6 703 return SR_ERR_ARG;
4a35548b
MS
704 }
705
e53f32d2 706 /*
1a920e33
GS
707 * Scan flexible, to get prefered format specs which describe
708 * the input file's data formats. As well as some simple specs
709 * for backwards compatibility and user convenience.
710 *
711 * This logic ends up with a copy of the format string, either
712 * user provided or internally derived. Actual creation of the
713 * column processing details gets deferred until the first line
714 * of input data was seen. To support automatic determination of
715 * e.g. channel counts from column counts.
e53f32d2 716 */
72903e9d 717 s = g_variant_get_string(g_hash_table_lookup(options, "column_formats"), NULL);
2142a79b 718 if (s && *s) {
1a920e33 719 inc->column_formats = g_strdup(s);
72903e9d 720 sr_dbg("User specified column_formats: %s.", s);
1a920e33
GS
721 } else if (single_column && logic_channels) {
722 format_char = col_format_char[format];
723 if (single_column == 1) {
724 inc->column_formats = g_strdup_printf("%c%zu",
725 format_char, logic_channels);
e53f32d2 726 } else {
1a920e33
GS
727 inc->column_formats = g_strdup_printf("%zu-,%c%zu",
728 single_column - 1,
729 format_char, logic_channels);
e53f32d2 730 }
72903e9d 731 sr_dbg("Backwards compat single_column, col %zu, fmt %s, bits %zu -> %s.",
1a920e33
GS
732 single_column, col_format_text[format], logic_channels,
733 inc->column_formats);
734 } else if (!single_column) {
735 if (first_column > 1) {
736 inc->column_formats = g_strdup_printf("%zu-,%zul",
737 first_column - 1, logic_channels);
738 } else {
739 inc->column_formats = g_strdup_printf("%zul",
740 logic_channels);
741 }
742 sr_dbg("Backwards compat multi-column, col %zu, chans %zu -> %s.",
743 first_column, logic_channels,
744 inc->column_formats);
e53f32d2 745 } else {
72903e9d 746 sr_warn("Unknown or unsupported columns layout spec, assuming simple multi-column mode.");
1a920e33 747 inc->column_formats = g_strdup("*l");
4a35548b
MS
748 }
749
41d214f6
BV
750 return SR_OK;
751}
4a35548b 752
affaf540
GS
753/*
754 * Check the channel list for consistency across file re-import. See
755 * the VCD input module for more details and motivation.
756 */
757
758static void keep_header_for_reread(const struct sr_input *in)
759{
760 struct context *inc;
761
762 inc = in->priv;
763 g_slist_free_full(inc->prev_sr_channels, sr_channel_free_cb);
764 inc->prev_sr_channels = in->sdi->channels;
765 in->sdi->channels = NULL;
766}
767
768static int check_header_in_reread(const struct sr_input *in)
769{
770 struct context *inc;
771
772 if (!in)
773 return FALSE;
774 inc = in->priv;
775 if (!inc)
776 return FALSE;
777 if (!inc->prev_sr_channels)
778 return TRUE;
779
780 if (sr_channel_lists_differ(inc->prev_sr_channels, in->sdi->channels)) {
781 sr_err("Channel list change not supported for file re-read.");
782 return FALSE;
783 }
784 g_slist_free_full(in->sdi->channels, sr_channel_free_cb);
785 in->sdi->channels = inc->prev_sr_channels;
786 inc->prev_sr_channels = NULL;
787
788 return TRUE;
789}
790
492dfa90
GS
791static const char *delim_set = "\r\n";
792
329733d9 793static const char *get_line_termination(GString *buf)
41d214f6 794{
329733d9 795 const char *term;
4a35548b 796
41d214f6
BV
797 term = NULL;
798 if (g_strstr_len(buf->str, buf->len, "\r\n"))
799 term = "\r\n";
800 else if (memchr(buf->str, '\n', buf->len))
801 term = "\n";
802 else if (memchr(buf->str, '\r', buf->len))
803 term = "\r";
4a35548b 804
41d214f6
BV
805 return term;
806}
4a35548b 807
41d214f6
BV
808static int initial_parse(const struct sr_input *in, GString *buf)
809{
810 struct context *inc;
41d214f6 811 GString *channel_name;
e53f32d2
GS
812 size_t num_columns, ch_idx, ch_name_idx, col_idx, col_nr;
813 size_t line_number, line_idx;
41d214f6 814 int ret;
df0db9fd 815 char **lines, *line, **columns, *column;
f6dcb320
GS
816 const char *col_caption;
817 gboolean got_caption;
e53f32d2 818 const struct column_details *detail;
41d214f6
BV
819
820 ret = SR_OK;
821 inc = in->priv;
822 columns = NULL;
823
824 line_number = 0;
ef0b9935
GS
825 if (inc->termination)
826 lines = g_strsplit(buf->str, inc->termination, 0);
827 else
828 lines = g_strsplit_set(buf->str, delim_set, 0);
e53f32d2 829 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6
BV
830 line_number++;
831 if (inc->start_line > line_number) {
e53f32d2 832 sr_spew("Line %zu skipped (before start).", line_number);
4a35548b
MS
833 continue;
834 }
df0db9fd 835 if (line[0] == '\0') {
41d214f6
BV
836 sr_spew("Blank line %zu skipped.", line_number);
837 continue;
838 }
df0db9fd
GS
839 strip_comment(line, inc->comment);
840 if (line[0] == '\0') {
41d214f6 841 sr_spew("Comment-only line %zu skipped.", line_number);
4a35548b
MS
842 continue;
843 }
844
41d214f6
BV
845 /* Reached first proper line. */
846 break;
847 }
e53f32d2 848 if (!line) {
41d214f6 849 /* Not enough data for a proper line yet. */
60107497 850 ret = SR_ERR_NA;
41d214f6 851 goto out;
4a35548b
MS
852 }
853
e53f32d2
GS
854 /* See how many columns the current line has. */
855 columns = split_line(line, inc);
df0db9fd 856 if (!columns) {
41d214f6
BV
857 sr_err("Error while parsing line %zu.", line_number);
858 ret = SR_ERR;
859 goto out;
4a35548b 860 }
4a35548b 861 num_columns = g_strv_length(columns);
4a35548b 862 if (!num_columns) {
e53f32d2 863 sr_err("Error while parsing line %zu.", line_number);
41d214f6
BV
864 ret = SR_ERR;
865 goto out;
4a35548b 866 }
e53f32d2
GS
867 sr_dbg("DIAG Got %zu columns in text line: %s.", num_columns, line);
868
1a920e33
GS
869 /*
870 * Track the observed number of columns in the input file. Do
871 * process the previously gathered columns format spec now that
872 * automatic channel count can be dealt with.
873 */
874 inc->column_seen_count = num_columns;
875 ret = make_column_details_from_format(inc, inc->column_formats);
876 if (ret != SR_OK) {
877 sr_err("Cannot parse columns format using line %zu.", line_number);
878 goto out;
4a35548b
MS
879 }
880
e53f32d2
GS
881 /*
882 * Assume all lines have equal length (column count). Bail out
883 * early on suspicious or insufficient input data (check input
884 * which became available here against previous user specs or
885 * auto-determined properties, regardless of layout variant).
886 */
887 if (num_columns < inc->column_want_count) {
888 sr_err("Insufficient input text width for desired data amount, got %zu but want %zu columns.",
889 num_columns, inc->column_want_count);
890 ret = SR_ERR;
891 goto out;
892 }
893
894 /*
895 * Determine channel names. Optionally use text from a header
896 * line (when requested by the user, and only works in multi
897 * column mode). In the absence of header text, or in single
898 * column mode, channels are assigned rather generic names.
f6dcb320
GS
899 *
900 * Manipulation of the column's caption is acceptable here, the
901 * header line will never get processed another time.
e53f32d2 902 */
41d214f6 903 channel_name = g_string_sized_new(64);
e53f32d2 904 for (col_idx = 0; col_idx < inc->column_want_count; col_idx++) {
f6dcb320 905
e53f32d2
GS
906 col_nr = col_idx + 1;
907 detail = lookup_column_details(inc, col_nr);
908 if (detail->text_format == FORMAT_NONE)
909 continue;
910 column = columns[col_idx];
f6dcb320
GS
911 col_caption = sr_scpi_unquote_string(column);
912 got_caption = inc->use_header && *col_caption;
e53f32d2 913 sr_dbg("DIAG col %zu, ch count %zu, text %s.",
f6dcb320 914 col_nr, detail->channel_count, col_caption);
e53f32d2
GS
915 for (ch_idx = 0; ch_idx < detail->channel_count; ch_idx++) {
916 ch_name_idx = detail->channel_offset + ch_idx;
f6dcb320
GS
917 if (got_caption && detail->channel_count == 1)
918 g_string_assign(channel_name, col_caption);
919 else if (got_caption)
920 g_string_printf(channel_name, "%s[%zu]",
921 col_caption, ch_idx);
e53f32d2
GS
922 else
923 g_string_printf(channel_name, "%zu", ch_name_idx);
924 sr_dbg("DIAG ch idx %zu, name %s.", ch_name_idx, channel_name->str);
925 sr_channel_new(in->sdi, ch_name_idx, SR_CHANNEL_LOGIC, TRUE,
926 channel_name->str);
927 }
4a35548b 928 }
41d214f6 929 g_string_free(channel_name, TRUE);
affaf540
GS
930 if (!check_header_in_reread(in)) {
931 ret = SR_ERR_DATA;
932 goto out;
933 }
4a35548b
MS
934
935 /*
cd59e6ec
GS
936 * Calculate the minimum buffer size to store the set of samples
937 * of all channels (unit size). Determine a larger buffer size
938 * for datafeed submission that is a multiple of the unit size.
626c388a
GS
939 * Allocate the larger buffer, the "sample buffer" will point
940 * to a location within that large buffer later.
4a35548b 941 */
836fac9c 942 inc->sample_unit_size = (inc->logic_channels + 7) / 8;
8bc2fa6d 943 inc->datafeed_buf_size = CHUNK_SIZE;
cd59e6ec
GS
944 inc->datafeed_buf_size *= inc->sample_unit_size;
945 inc->datafeed_buffer = g_malloc(inc->datafeed_buf_size);
946 inc->datafeed_buf_fill = 0;
4a35548b 947
41d214f6
BV
948out:
949 if (columns)
950 g_strfreev(columns);
951 g_strfreev(lines);
4a35548b 952
41d214f6 953 return ret;
4a35548b
MS
954}
955
4439363a
GS
956/*
957 * Gets called from initial_receive(), which runs until the end-of-line
958 * encoding of the input stream could get determined. Assumes that this
959 * routine receives enough buffered initial input data to either see the
960 * BOM when there is one, or that no BOM will follow when a text line
961 * termination sequence was seen. Silently drops the UTF-8 BOM sequence
962 * from the input buffer if one was seen. Does not care to protect
963 * against multiple execution or dropping the BOM multiple times --
964 * there should be at most one in the input stream.
965 */
966static void initial_bom_check(const struct sr_input *in)
967{
968 static const char *utf8_bom = "\xef\xbb\xbf";
969
970 if (in->buf->len < strlen(utf8_bom))
971 return;
972 if (strncmp(in->buf->str, utf8_bom, strlen(utf8_bom)) != 0)
973 return;
974 g_string_erase(in->buf, 0, strlen(utf8_bom));
975}
976
41d214f6 977static int initial_receive(const struct sr_input *in)
4a35548b 978{
41d214f6
BV
979 struct context *inc;
980 GString *new_buf;
981 int len, ret;
329733d9
UH
982 char *p;
983 const char *termination;
4a35548b 984
4439363a
GS
985 initial_bom_check(in);
986
41d214f6 987 inc = in->priv;
4a35548b 988
df0db9fd
GS
989 termination = get_line_termination(in->buf);
990 if (!termination)
41d214f6 991 /* Don't have a full line yet. */
d0181813 992 return SR_ERR_NA;
4a35548b 993
df0db9fd
GS
994 p = g_strrstr_len(in->buf->str, in->buf->len, termination);
995 if (!p)
41d214f6 996 /* Don't have a full line yet. */
d0181813 997 return SR_ERR_NA;
41d214f6
BV
998 len = p - in->buf->str - 1;
999 new_buf = g_string_new_len(in->buf->str, len);
1000 g_string_append_c(new_buf, '\0');
4a35548b 1001
41d214f6
BV
1002 inc->termination = g_strdup(termination);
1003
1004 if (in->buf->str[0] != '\0')
1005 ret = initial_parse(in, new_buf);
1006 else
1007 ret = SR_OK;
1008
1009 g_string_free(new_buf, TRUE);
1010
1011 return ret;
1012}
1013
7f4c3a62 1014static int process_buffer(struct sr_input *in, gboolean is_eof)
41d214f6 1015{
41d214f6
BV
1016 struct context *inc;
1017 gsize num_columns;
e53f32d2 1018 size_t line_idx, col_idx, col_nr;
836fac9c
GS
1019 const struct column_details *details;
1020 col_parse_cb parse_func;
ad6a2bee 1021 int ret;
e53f32d2 1022 char *p, **lines, *line, **columns, *column;
41d214f6 1023
41d214f6 1024 inc = in->priv;
d0181813 1025 if (!inc->started) {
bee2b016 1026 std_session_send_df_header(in->sdi);
d0181813 1027 inc->started = TRUE;
4a35548b
MS
1028 }
1029
4555d3bd
GS
1030 /*
1031 * Consider empty input non-fatal. Keep accumulating input until
1032 * at least one full text line has become available. Grab the
1033 * maximum amount of accumulated data that consists of full text
1034 * lines, and process what has been received so far, leaving not
1035 * yet complete lines for the next invocation.
7f4c3a62
GS
1036 *
1037 * Enforce that all previously buffered data gets processed in
1038 * the "EOF" condition. Do not insist in the presence of the
1039 * termination sequence for the last line (may often be missing
1040 * on Windows). A present termination sequence will just result
1041 * in the "execution of an empty line", and does not harm.
4555d3bd
GS
1042 */
1043 if (!in->buf->len)
1044 return SR_OK;
7f4c3a62
GS
1045 if (is_eof) {
1046 p = in->buf->str + in->buf->len;
1047 } else {
1048 p = g_strrstr_len(in->buf->str, in->buf->len, inc->termination);
1049 if (!p)
1050 return SR_ERR;
1051 *p = '\0';
1052 p += strlen(inc->termination);
1053 }
41d214f6 1054 g_strstrip(in->buf->str);
4a35548b 1055
18078d05 1056 ret = SR_OK;
ef0b9935 1057 lines = g_strsplit(in->buf->str, inc->termination, 0);
e53f32d2 1058 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6 1059 inc->line_number++;
ef0b9935
GS
1060 if (inc->line_number < inc->start_line) {
1061 sr_spew("Line %zu skipped (before start).", inc->line_number);
1062 continue;
1063 }
df0db9fd 1064 if (line[0] == '\0') {
41d214f6 1065 sr_spew("Blank line %zu skipped.", inc->line_number);
4a35548b
MS
1066 continue;
1067 }
1068
1069 /* Remove trailing comment. */
df0db9fd
GS
1070 strip_comment(line, inc->comment);
1071 if (line[0] == '\0') {
41d214f6 1072 sr_spew("Comment-only line %zu skipped.", inc->line_number);
4a35548b
MS
1073 continue;
1074 }
1075
160691b9 1076 /* Skip the header line, its content was used as the channel names. */
de8fe3b5 1077 if (inc->use_header && !inc->header_seen) {
160691b9 1078 sr_spew("Header line %zu skipped.", inc->line_number);
de8fe3b5 1079 inc->header_seen = TRUE;
160691b9
JS
1080 continue;
1081 }
1082
e53f32d2
GS
1083 /* Split the line into columns, check for minimum length. */
1084 columns = split_line(line, inc);
df0db9fd 1085 if (!columns) {
41d214f6 1086 sr_err("Error while parsing line %zu.", inc->line_number);
2355d229 1087 g_strfreev(lines);
4a35548b
MS
1088 return SR_ERR;
1089 }
4a35548b 1090 num_columns = g_strv_length(columns);
e53f32d2
GS
1091 if (num_columns < inc->column_want_count) {
1092 sr_err("Insufficient column count %zu in line %zu.",
1093 num_columns, inc->line_number);
4a35548b 1094 g_strfreev(columns);
2355d229 1095 g_strfreev(lines);
4a35548b
MS
1096 return SR_ERR;
1097 }
1098
836fac9c 1099 /* Have the columns of the current text line processed. */
626c388a 1100 clear_logic_samples(inc);
e53f32d2
GS
1101 for (col_idx = 0; col_idx < inc->column_want_count; col_idx++) {
1102 column = columns[col_idx];
1103 col_nr = col_idx + 1;
836fac9c
GS
1104 details = lookup_column_details(inc, col_nr);
1105 if (!details || !details->text_format)
1106 continue;
1107 parse_func = col_parse_funcs[details->text_format];
1108 if (!parse_func)
1109 continue;
1110 ret = parse_func(column, inc, details);
e53f32d2
GS
1111 if (ret != SR_OK) {
1112 g_strfreev(columns);
1113 g_strfreev(lines);
1114 return SR_ERR;
1115 }
4a35548b
MS
1116 }
1117
626c388a
GS
1118 /* Send sample data to the session bus (buffered). */
1119 ret = queue_logic_samples(in);
41d214f6 1120 if (ret != SR_OK) {
4a35548b 1121 sr_err("Sending samples failed.");
cd59e6ec 1122 g_strfreev(columns);
2355d229 1123 g_strfreev(lines);
4a35548b
MS
1124 return SR_ERR;
1125 }
cd59e6ec 1126
41d214f6
BV
1127 g_strfreev(columns);
1128 }
1129 g_strfreev(lines);
241c386a 1130 g_string_erase(in->buf, 0, p - in->buf->str);
41d214f6 1131
7066fd46 1132 return ret;
41d214f6
BV
1133}
1134
7066fd46 1135static int receive(struct sr_input *in, GString *buf)
41d214f6
BV
1136{
1137 struct context *inc;
7066fd46
BV
1138 int ret;
1139
1140 g_string_append_len(in->buf, buf->str, buf->len);
41d214f6
BV
1141
1142 inc = in->priv;
1a920e33 1143 if (!inc->column_seen_count) {
df0db9fd
GS
1144 ret = initial_receive(in);
1145 if (ret == SR_ERR_NA)
7066fd46
BV
1146 /* Not enough data yet. */
1147 return SR_OK;
1148 else if (ret != SR_OK)
1149 return SR_ERR;
1150
1151 /* sdi is ready, notify frontend. */
1152 in->sdi_ready = TRUE;
41d214f6 1153 return SR_OK;
7066fd46
BV
1154 }
1155
7f4c3a62 1156 ret = process_buffer(in, FALSE);
7066fd46
BV
1157
1158 return ret;
1159}
1160
1161static int end(struct sr_input *in)
1162{
1163 struct context *inc;
7066fd46 1164 int ret;
41d214f6 1165
7066fd46 1166 if (in->sdi_ready)
7f4c3a62 1167 ret = process_buffer(in, TRUE);
7066fd46
BV
1168 else
1169 ret = SR_OK;
cd59e6ec
GS
1170 if (ret != SR_OK)
1171 return ret;
1172
626c388a 1173 ret = flush_logic_samples(in);
cd59e6ec
GS
1174 if (ret != SR_OK)
1175 return ret;
7066fd46
BV
1176
1177 inc = in->priv;
3be42bc2 1178 if (inc->started)
bee2b016 1179 std_session_send_df_end(in->sdi);
4a35548b 1180
7066fd46
BV
1181 return ret;
1182}
1183
d5cc282f 1184static void cleanup(struct sr_input *in)
7066fd46
BV
1185{
1186 struct context *inc;
1187
affaf540
GS
1188 keep_header_for_reread(in);
1189
7066fd46
BV
1190 inc = in->priv;
1191
b1f83103 1192 g_free(inc->termination);
539188e5 1193 inc->termination = NULL;
cd59e6ec 1194 g_free(inc->datafeed_buffer);
539188e5 1195 inc->datafeed_buffer = NULL;
4a35548b
MS
1196}
1197
ad93bfb0
SA
1198static int reset(struct sr_input *in)
1199{
1200 struct context *inc = in->priv;
1201
1202 cleanup(in);
1203 inc->started = FALSE;
1204 g_string_truncate(in->buf, 0);
1205
1206 return SR_OK;
1207}
1208
c6aa9870 1209enum option_index {
2142a79b 1210 OPT_COL_FMTS,
c6aa9870 1211 OPT_SINGLE_COL,
72903e9d 1212 OPT_FIRST_COL,
c6aa9870 1213 OPT_NUM_LOGIC,
c6aa9870 1214 OPT_FORMAT,
c6aa9870 1215 OPT_START,
72903e9d
GS
1216 OPT_HEADER,
1217 OPT_RATE,
1218 OPT_DELIM,
1219 OPT_COMMENT,
c6aa9870
GS
1220 OPT_MAX,
1221};
1222
41d214f6 1223static struct sr_option options[] = {
72903e9d
GS
1224 [OPT_COL_FMTS] = {
1225 "column_formats", "Column format specs",
1226 "Specifies text columns data types: comma separated list of [<cols>]<fmt>[<bits>], with -/x/o/b/l format specifiers.",
1227 NULL, NULL,
1228 },
1229 [OPT_SINGLE_COL] = {
1230 "single_column", "Single column",
1231 "Enable single-column mode, exclusively use text from the specified column (number starting at 1).",
1232 NULL, NULL,
1233 },
1234 [OPT_FIRST_COL] = {
1235 "first_column", "First column",
1236 "Number of the first column with logic data in simple multi-column mode (number starting at 1, default 1).",
1237 NULL, NULL,
1238 },
1239 [OPT_NUM_LOGIC] = {
1240 "logic_channels", "Number of logic channels",
1241 "Logic channel count, required in simple single-column mode, defaults to \"all remaining columns\" in simple multi-column mode. Obsoleted by 'column_formats'.",
1242 NULL, NULL,
1243 },
1244 [OPT_FORMAT] = {
1245 "single_format", "Data format for simple single-column mode.",
1246 "The number format of single-column mode input data: bin, hex, oct.",
1247 NULL, NULL,
1248 },
1249 [OPT_START] = {
1250 "start_line", "Start line",
1251 "The line number at which to start processing input text (default: 1).",
1252 NULL, NULL,
1253 },
1254 [OPT_HEADER] = {
1255 "header", "Get channel names from first line.",
1256 "Use the first processed line's column captions (when available) as channel names.",
1257 NULL, NULL,
1258 },
1259 [OPT_RATE] = {
1260 "samplerate", "Samplerate (Hz)",
1261 "The input data's sample rate in Hz.",
1262 NULL, NULL,
1263 },
1264 [OPT_DELIM] = {
1265 "column_separator", "Column separator",
1266 "The sequence which separates text columns. Non-empty text, comma by default.",
1267 NULL, NULL,
1268 },
1269 [OPT_COMMENT] = {
1270 "comment_leader", "Comment leader character",
1271 "The text which starts comments at the end of text lines.",
1272 NULL, NULL,
1273 },
c6aa9870 1274 [OPT_MAX] = ALL_ZERO,
41d214f6
BV
1275};
1276
2c240774 1277static const struct sr_option *get_options(void)
41d214f6 1278{
31c41782
UH
1279 GSList *l;
1280
41d214f6 1281 if (!options[0].def) {
1a920e33 1282 options[OPT_COL_FMTS].def = g_variant_ref_sink(g_variant_new_string(""));
e53f32d2 1283 options[OPT_SINGLE_COL].def = g_variant_ref_sink(g_variant_new_uint32(0));
72903e9d 1284 options[OPT_FIRST_COL].def = g_variant_ref_sink(g_variant_new_uint32(1));
e53f32d2 1285 options[OPT_NUM_LOGIC].def = g_variant_ref_sink(g_variant_new_uint32(0));
c6aa9870 1286 options[OPT_FORMAT].def = g_variant_ref_sink(g_variant_new_string("bin"));
31c41782
UH
1287 l = NULL;
1288 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("bin")));
1289 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("hex")));
1290 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct")));
c6aa9870 1291 options[OPT_FORMAT].values = l;
e53f32d2 1292 options[OPT_START].def = g_variant_ref_sink(g_variant_new_uint32(1));
72903e9d
GS
1293 options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
1294 options[OPT_RATE].def = g_variant_ref_sink(g_variant_new_uint64(0));
1295 options[OPT_DELIM].def = g_variant_ref_sink(g_variant_new_string(","));
1296 options[OPT_COMMENT].def = g_variant_ref_sink(g_variant_new_string(";"));
41d214f6
BV
1297 }
1298
1299 return options;
1300}
1301
d4c93774 1302SR_PRIV struct sr_input_module input_csv = {
4a35548b 1303 .id = "csv",
41d214f6
BV
1304 .name = "CSV",
1305 .desc = "Comma-separated values",
c7bc82ff 1306 .exts = (const char*[]){"csv", NULL},
41d214f6 1307 .options = get_options,
4a35548b 1308 .init = init,
41d214f6 1309 .receive = receive,
7066fd46 1310 .end = end,
41d214f6 1311 .cleanup = cleanup,
ad93bfb0 1312 .reset = reset,
4a35548b 1313};