]> sigrok.org Git - libsigrok.git/blame - src/input/csv.c
input/csv: update developer comments and TODO list
[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
cd7c5f96 23#include <ctype.h>
e05f1827 24#include <glib.h>
4a35548b
MS
25#include <stdlib.h>
26#include <string.h>
cd7c5f96 27#include <strings.h>
e05f1827 28
c1aae900 29#include <libsigrok/libsigrok.h>
4a35548b 30#include "libsigrok-internal.h"
f6dcb320 31#include "scpi.h" /* String un-quote for channel name from header line. */
4a35548b 32
3544f848 33#define LOG_PREFIX "input/csv"
4a35548b 34
9a4fd01a 35#define CHUNK_SIZE (4 * 1024 * 1024)
cd59e6ec 36
4a35548b
MS
37/*
38 * The CSV input module has the following options:
39 *
72903e9d
GS
40 * column_formats: Specifies the data formats and channel counts for the
41 * input file's text columns. Accepts a comma separated list of tuples
42 * with: an optional column repeat count ('*' as a wildcard meaning
43 * "all remaining columns", only applicable to the last field), a format
44 * specifying character ('x' hexadecimal, 'o' octal, 'b' binary, 'l'
45 * single-bit logic), and an optional bit count (translating to: logic
08eb955a
GS
46 * channels communicated in that column). The 'a' format marks analog
47 * data, an optionally following number is the digits count (resolution).
7e4e65bf
GS
48 * The 't' format marks timestamp values, which could help in automatic
49 * determination of the input stream's samplerate. This "column_formats"
50 * option is most versatile, other forms of specifying the column layout
51 * only exist for backwards compatibility, and are rather limited. They
52 * exclusively support logic input data in strictly adjacent columns,
53 * with further constraints on column layout for multi-bit data.
4a35548b 54 *
72903e9d
GS
55 * single_column: Specifies the column number which contains the logic data
56 * for single-column mode. All logic data is taken from several bits
57 * which all are kept within that one column. Only exists for backwards
58 * compatibility, see "column_formats" for more flexibility.
4a35548b 59 *
72903e9d
GS
60 * first_column: Specifies the number of the first column with logic data
61 * in simple multi-column mode. Only exists for backwards compatibility,
62 * see "column_formats" for more flexibility.
4a35548b 63 *
72903e9d
GS
64 * logic_channels: Specifies the number of logic channels. Is required in
65 * simple single-column mode. Is optional in simple multi-column mode
66 * (and defaults to all remaining columns). Only exists for backwards
67 * compatibility, see "column_formats" for more flexibility.
4a35548b 68 *
72903e9d
GS
69 * single_format: Specifies the format of the input text in simple single-
70 * column mode. Available formats are: 'bin' (default), 'hex' and 'oct'.
71 * Simple multi-column mode always uses single-bit data per column.
72 * Only exists for backwards compatibility, see "column_formats" for
73 * more flexibility.
4a35548b 74 *
72903e9d
GS
75 * start_line: Specifies at which line to start processing the input file.
76 * Allows to skip leading lines which neither are header nor data lines.
77 * By default all of the input file gets processed.
4a35548b 78 *
72903e9d
GS
79 * header: Boolean option, controls whether the first processed line is used
80 * to determine channel names. Off by default. Generic channel names are
81 * used in the absence of header line content.
4a35548b 82 *
72903e9d
GS
83 * samplerate: Specifies the samplerate of the input data. Defaults to 0.
84 * User specs take precedence over data which optionally gets derived
85 * from input data.
4a35548b 86 *
72903e9d
GS
87 * column_separator: Specifies the sequence which separates the text file
88 * columns. Cannot be empty. Defaults to comma.
89 *
90 * comment_leader: Specifies the sequence which starts comments that run
91 * up to the end of the current text line. Can be empty to disable
92 * comment support. Defaults to semicolon.
93 *
94 * Typical examples of using these options:
95 * - ... -I csv:column_formats=*l ...
96 * All columns are single-bit logic data. Identical to the previous
97 * multi-column mode (the default when no options were given at all).
98 * - ... -I csv:column_formats=3-,*l ...
99 * Ignore the first three columns, get single-bit logic data from all
100 * remaining lines (multi-column mode with first-column above 1).
101 * - ... -I csv:column_formats=3-,4l,x8 ...
102 * Ignore the first three columns, get single-bit logic data from the
103 * next four columns, then eight-bit data in hex format from the next
104 * column. More columns may follow in the input text but won't get
105 * processed. (Mix of previous multi-column as well as single-column
106 * modes.)
107 * - ... -I csv:column_formats=4x8,b16,5l ...
108 * Get eight-bit data in hex format from the first four columns, then
109 * sixteen-bit data in binary format, then five times single-bit data.
110 * - ... -I csv:single_column=2:single_format=bin:logic_channels=8 ...
111 * Get eight logic bits in binary format from column 2. (Simple
112 * single-column mode, corresponds to the "-,b8" format.)
113 * - ... -I csv:first_column=6:logic_channels=4 ...
114 * Get four single-bit logic channels from columns 6 to 9 respectively.
115 * (Simple multi-column mode, corresponds to the "5-,4b" format.)
116 * - ... -I csv:start_line=20:header=yes:...
117 * Skip the first 19 text lines. Use line 20 to derive channel names.
118 * Data starts at line 21.
08eb955a
GS
119 * - ... -I csv:column_formats=*a6 ...
120 * Each column contains an analog value with six significant digits
121 * after the decimal period.
7e4e65bf
GS
122 * - ... -I csv:column_formats=t,2a ...
123 * The first column contains timestamps, the next two columns contain
124 * analog values. The capture's samplerate could get determined from
125 * the timestamp values if not provided by the user by means of the
126 * 'samplerate' option. This assumes a mere number in units of seconds,
127 * and equidistant rows, there is no fancy support for textual unit
128 * suffixes nor gaps in the stream of samples nor other non-linearity,
129 * just '-' ignore the column if the format is not supported).
cb3b8051
GS
130 *
131 * IMPORTANT! Make sure the .format_match() logic matches the default
132 * values for the input module's options. Ideally the format match test
133 * shall pass for all input data that is supported by default.
4a35548b
MS
134 */
135
ccff468b
GS
136/*
137 * TODO
138 *
cb3b8051
GS
139 * - Unbreak analog data when submitted in the 'double' data type. This
140 * was observed with sigrok-cli screen output. Is analog.encoding->unitsize
141 * not handled appropriately? Is it a sigrok-cli or libsigrok issue?
5a971176
GS
142 * - Add a test suite for input modules in general, and CSV in specific?
143 * Becomes more important with the multitude of options and their
144 * interaction. Could cover edge cases (BOM presence, line termination
145 * absence, etc) and auto-stuff as well (channel names, channel counts,
146 * samplerates, etc).
ccff468b
GS
147 */
148
43bdef26
GS
149typedef float csv_analog_t; /* 'double' currently is flawed. */
150
4a35548b 151/* Single column formats. */
ad6a2bee 152enum single_col_format {
e53f32d2
GS
153 FORMAT_NONE, /* Ignore this column. */
154 FORMAT_BIN, /* Bin digits for a set of bits (or just one bit). */
155 FORMAT_HEX, /* Hex digits for a set of bits. */
156 FORMAT_OCT, /* Oct digits for a set of bits. */
43bdef26 157 FORMAT_ANALOG, /* Floating point number for an analog channel. */
7e4e65bf 158 FORMAT_TIME, /* Timestamps. */
e53f32d2
GS
159};
160
161static const char *col_format_text[] = {
162 [FORMAT_NONE] = "unknown",
163 [FORMAT_BIN] = "binary",
164 [FORMAT_HEX] = "hexadecimal",
165 [FORMAT_OCT] = "octal",
43bdef26 166 [FORMAT_ANALOG] = "analog",
7e4e65bf 167 [FORMAT_TIME] = "timestamp",
e53f32d2
GS
168};
169
1a920e33
GS
170static const char col_format_char[] = {
171 [FORMAT_NONE] = '?',
172 [FORMAT_BIN] = 'b',
173 [FORMAT_HEX] = 'x',
174 [FORMAT_OCT] = 'o',
43bdef26 175 [FORMAT_ANALOG] = 'a',
7e4e65bf 176 [FORMAT_TIME] = 't',
1a920e33
GS
177};
178
fc3b42e9
GS
179static gboolean format_is_ignore(enum single_col_format fmt)
180{
181 return fmt == FORMAT_NONE;
182}
183
184static gboolean format_is_logic(enum single_col_format fmt)
185{
186 return fmt >= FORMAT_BIN && fmt <= FORMAT_OCT;
187}
188
189static gboolean format_is_analog(enum single_col_format fmt)
190{
191 return fmt == FORMAT_ANALOG;
192}
193
7e4e65bf
GS
194static gboolean format_is_timestamp(enum single_col_format fmt)
195{
196 return fmt == FORMAT_TIME;
197}
198
e53f32d2
GS
199struct column_details {
200 size_t col_nr;
201 enum single_col_format text_format;
202 size_t channel_offset;
203 size_t channel_count;
a267bf45 204 int analog_digits;
05719d75 205 GString **channel_names;
4a35548b
MS
206};
207
208struct context {
41d214f6
BV
209 gboolean started;
210
7e4e65bf 211 /* Current samplerate, optionally determined from input data. */
4a35548b 212 uint64_t samplerate;
7e4e65bf 213 double prev_timestamp;
246aca5f 214 gboolean samplerate_sent;
4a35548b 215
a267bf45 216 /* Number of channels. */
836fac9c 217 size_t logic_channels;
43bdef26 218 size_t analog_channels;
4a35548b 219
836fac9c 220 /* Column delimiter (actually separator), comment leader, EOL sequence. */
4a35548b 221 GString *delimiter;
4a35548b 222 GString *comment;
41d214f6
BV
223 char *termination;
224
1a920e33
GS
225 /* Format specs for input columns, and processing state. */
226 size_t column_seen_count;
227 const char *column_formats;
e53f32d2
GS
228 size_t column_want_count;
229 struct column_details *column_details;
230
4a35548b 231 /* Line number to start processing. */
6433156c 232 size_t start_line;
4a35548b
MS
233
234 /*
235 * Determines if the first line should be treated as header and used for
ba7dd8bb 236 * channel names in multi column mode.
4a35548b 237 */
de8fe3b5
GS
238 gboolean use_header;
239 gboolean header_seen;
4a35548b 240
cd59e6ec
GS
241 size_t sample_unit_size; /**!< Byte count for a single sample. */
242 uint8_t *sample_buffer; /**!< Buffer for a single sample. */
43bdef26 243 csv_analog_t *analog_sample_buffer; /**!< Buffer for one set of analog values. */
4a35548b 244
cd59e6ec
GS
245 uint8_t *datafeed_buffer; /**!< Queue for datafeed submission. */
246 size_t datafeed_buf_size;
247 size_t datafeed_buf_fill;
43bdef26
GS
248 /* "Striped" layout, M samples for N channels each. */
249 csv_analog_t *analog_datafeed_buffer; /**!< Queue for analog datafeed. */
250 size_t analog_datafeed_buf_size;
251 size_t analog_datafeed_buf_fill;
a267bf45 252 int *analog_datafeed_digits;
05719d75 253 GSList **analog_datafeed_channels;
4a35548b 254
4a35548b 255 /* Current line number. */
6433156c 256 size_t line_number;
affaf540
GS
257
258 /* List of previously created sigrok channels. */
259 GSList *prev_sr_channels;
4a35548b
MS
260};
261
626c388a
GS
262/*
263 * Primitive operations to handle sample sets:
264 * - Keep a buffer for datafeed submission, capable of holding many
265 * samples (reduces call overhead, improves throughput).
266 * - Have a "current sample set" pointer reference one position in that
267 * large samples buffer.
268 * - Clear the current sample set before text line inspection, then set
269 * the bits which are found active in the current line of text input.
270 * Phrase the API such that call sites can be kept simple. Advance to
271 * the next sample set between lines, flush the larger buffer as needed
272 * (when it is full, or upon EOF).
273 */
274
43bdef26
GS
275static int flush_samplerate(const struct sr_input *in)
276{
277 struct context *inc;
278 struct sr_datafeed_packet packet;
279 struct sr_datafeed_meta meta;
280 struct sr_config *src;
281
282 inc = in->priv;
283 if (inc->samplerate && !inc->samplerate_sent) {
284 packet.type = SR_DF_META;
285 packet.payload = &meta;
286 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
287 meta.config = g_slist_append(NULL, src);
288 sr_session_send(in->sdi, &packet);
289 g_slist_free(meta.config);
290 sr_config_free(src);
291 inc->samplerate_sent = TRUE;
292 }
293
294 return SR_OK;
295}
296
626c388a
GS
297static void clear_logic_samples(struct context *inc)
298{
43bdef26
GS
299 if (!inc->logic_channels)
300 return;
626c388a
GS
301 inc->sample_buffer = &inc->datafeed_buffer[inc->datafeed_buf_fill];
302 memset(inc->sample_buffer, 0, inc->sample_unit_size);
303}
304
305static void set_logic_level(struct context *inc, size_t ch_idx, int on)
306{
307 size_t byte_idx, bit_idx;
308 uint8_t bit_mask;
309
836fac9c 310 if (ch_idx >= inc->logic_channels)
626c388a
GS
311 return;
312 if (!on)
313 return;
314
315 byte_idx = ch_idx / 8;
316 bit_idx = ch_idx % 8;
317 bit_mask = 1 << bit_idx;
318 inc->sample_buffer[byte_idx] |= bit_mask;
319}
320
321static int flush_logic_samples(const struct sr_input *in)
322{
323 struct context *inc;
324 struct sr_datafeed_packet packet;
325 struct sr_datafeed_logic logic;
326 int rc;
327
328 inc = in->priv;
329 if (!inc->datafeed_buf_fill)
330 return SR_OK;
331
43bdef26
GS
332 rc = flush_samplerate(in);
333 if (rc != SR_OK)
334 return rc;
246aca5f 335
626c388a
GS
336 memset(&packet, 0, sizeof(packet));
337 memset(&logic, 0, sizeof(logic));
338 packet.type = SR_DF_LOGIC;
339 packet.payload = &logic;
340 logic.unitsize = inc->sample_unit_size;
341 logic.length = inc->datafeed_buf_fill;
342 logic.data = inc->datafeed_buffer;
343
344 rc = sr_session_send(in->sdi, &packet);
345 if (rc != SR_OK)
346 return rc;
347
348 inc->datafeed_buf_fill = 0;
349 return SR_OK;
350}
351
352static int queue_logic_samples(const struct sr_input *in)
353{
354 struct context *inc;
355 int rc;
356
357 inc = in->priv;
836fac9c
GS
358 if (!inc->logic_channels)
359 return SR_OK;
626c388a
GS
360
361 inc->datafeed_buf_fill += inc->sample_unit_size;
362 if (inc->datafeed_buf_fill == inc->datafeed_buf_size) {
363 rc = flush_logic_samples(in);
364 if (rc != SR_OK)
365 return rc;
366 }
367 return SR_OK;
368}
369
43bdef26
GS
370static void set_analog_value(struct context *inc, size_t ch_idx, csv_analog_t value);
371
372static void clear_analog_samples(struct context *inc)
373{
374 size_t idx;
375
376 if (!inc->analog_channels)
377 return;
378 inc->analog_sample_buffer = &inc->analog_datafeed_buffer[inc->analog_datafeed_buf_fill];
379 for (idx = 0; idx < inc->analog_channels; idx++)
380 set_analog_value(inc, idx, 0.0);
381}
382
383static void set_analog_value(struct context *inc, size_t ch_idx, csv_analog_t value)
384{
385 if (ch_idx >= inc->analog_channels)
386 return;
387 if (!value)
388 return;
389 inc->analog_sample_buffer[ch_idx * inc->analog_datafeed_buf_size] = value;
390}
391
392static int flush_analog_samples(const struct sr_input *in)
393{
43bdef26
GS
394 struct context *inc;
395 struct sr_datafeed_packet packet;
396 struct sr_datafeed_analog analog;
397 struct sr_analog_encoding encoding;
398 struct sr_analog_meaning meaning;
399 struct sr_analog_spec spec;
400 csv_analog_t *samples;
401 size_t ch_idx;
a267bf45 402 int digits;
43bdef26
GS
403 int rc;
404
405 inc = in->priv;
406 if (!inc->analog_datafeed_buf_fill)
407 return SR_OK;
408
409 rc = flush_samplerate(in);
410 if (rc != SR_OK)
411 return rc;
412
413 samples = inc->analog_datafeed_buffer;
414 for (ch_idx = 0; ch_idx < inc->analog_channels; ch_idx++) {
a267bf45 415 digits = inc->analog_datafeed_digits[ch_idx];
43bdef26
GS
416 sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
417 memset(&packet, 0, sizeof(packet));
418 packet.type = SR_DF_ANALOG;
419 packet.payload = &analog;
420 analog.num_samples = inc->analog_datafeed_buf_fill;
421 analog.data = samples;
422 analog.meaning->channels = inc->analog_datafeed_channels[ch_idx];
423 analog.meaning->mq = 0;
424 analog.meaning->mqflags = 0;
425 analog.meaning->unit = 0;
426 analog.encoding->unitsize = sizeof(samples[0]);
427 analog.encoding->is_signed = TRUE;
428 analog.encoding->is_float = TRUE;
429#ifdef WORDS_BIGENDIAN
430 analog.encoding->is_bigendian = TRUE;
431#else
432 analog.encoding->is_bigendian = FALSE;
433#endif
434 analog.encoding->digits = spec.spec_digits;
435 rc = sr_session_send(in->sdi, &packet);
436 if (rc != SR_OK)
437 return rc;
438 samples += inc->analog_datafeed_buf_size;
439 }
440
441 inc->analog_datafeed_buf_fill = 0;
442 return SR_OK;
443}
444
445static int queue_analog_samples(const struct sr_input *in)
446{
447 struct context *inc;
448 int rc;
449
450 inc = in->priv;
451 if (!inc->analog_channels)
452 return SR_OK;
453
454 inc->analog_datafeed_buf_fill++;
455 if (inc->analog_datafeed_buf_fill == inc->analog_datafeed_buf_size) {
456 rc = flush_analog_samples(in);
457 if (rc != SR_OK)
458 return rc;
459 }
460 return SR_OK;
461}
462
2142a79b
GS
463/* Helpers for "column processing". */
464
465static int split_column_format(const char *spec,
466 size_t *column_count, enum single_col_format *format, size_t *bit_count)
467{
468 size_t count;
469 char *endp, format_char;
470 enum single_col_format format_code;
471
472 if (!spec || !*spec)
473 return SR_ERR_ARG;
474
1a920e33 475 /* Get the (optional, decimal, default 1) column count. Accept '*'. */
2142a79b 476 endp = NULL;
1a920e33 477 if (*spec == '*') {
5ada72fc 478 /* Workaround, strtoul("*") won't always yield expected endp. */
1a920e33
GS
479 count = 0;
480 endp = (char *)&spec[1];
481 } else {
482 count = strtoul(spec, &endp, 10);
483 }
2142a79b
GS
484 if (!endp)
485 return SR_ERR_ARG;
486 if (endp == spec)
487 count = 1;
488 if (column_count)
489 *column_count = count;
490 spec = endp;
491
492 /* Get the (mandatory, single letter) type spec (-/xob/l). */
493 format_char = *spec++;
494 switch (format_char) {
5ada72fc 495 case '-':
2142a79b
GS
496 case '/':
497 format_char = '-';
498 format_code = FORMAT_NONE;
499 break;
500 case 'x':
501 format_code = FORMAT_HEX;
502 break;
503 case 'o':
504 format_code = FORMAT_OCT;
505 break;
506 case 'b':
507 case 'l':
508 format_code = FORMAT_BIN;
509 break;
43bdef26
GS
510 case 'a':
511 format_code = FORMAT_ANALOG;
512 break;
7e4e65bf
GS
513 case 't':
514 format_code = FORMAT_TIME;
515 break;
2142a79b
GS
516 default: /* includes NUL */
517 return SR_ERR_ARG;
518 }
519 if (format)
520 *format = format_code;
521
522 /* Get the (optional, decimal, default 1) bit count. */
523 endp = NULL;
524 count = strtoul(spec, &endp, 10);
525 if (!endp)
526 return SR_ERR_ARG;
527 if (endp == spec)
fc3b42e9
GS
528 count = format_is_analog(format_code) ? 3 : 1;
529 if (format_is_ignore(format_code))
2142a79b
GS
530 count = 0;
531 if (format_char == 'l')
532 count = 1;
533 if (bit_count)
534 *bit_count = count;
535 spec = endp;
536
537 /* Input spec must have been exhausted. */
538 if (*spec)
539 return SR_ERR_ARG;
540
541 return SR_OK;
542}
543
9e7af34e
GS
544static int make_column_details_from_format(const struct sr_input *in,
545 const char *column_format, char **column_texts)
2142a79b 546{
9e7af34e 547 struct context *inc;
2142a79b 548 char **formats, *format;
43bdef26 549 size_t format_count, column_count, logic_count, analog_count;
1a920e33 550 size_t auto_column_count;
43bdef26 551 size_t format_idx, c, b, column_idx, channel_idx, analog_idx;
2142a79b
GS
552 enum single_col_format f;
553 struct column_details *detail;
9e7af34e
GS
554 GString *channel_name;
555 size_t create_idx;
556 char *column;
557 const char *caption;
43bdef26 558 int channel_type, channel_sdi_nr;
05719d75 559 void *channel;
2142a79b
GS
560 int ret;
561
9e7af34e
GS
562 inc = in->priv;
563 inc->column_seen_count = g_strv_length(column_texts);
564
05719d75 565 /* Split the input spec, count involved columns and channels. */
2142a79b
GS
566 formats = g_strsplit(column_format, ",", 0);
567 if (!formats) {
568 sr_err("Cannot parse columns format %s (comma split).", column_format);
569 return SR_ERR_ARG;
570 }
571 format_count = g_strv_length(formats);
572 if (!format_count) {
573 sr_err("Cannot parse columns format %s (field count).", column_format);
574 g_strfreev(formats);
575 return SR_ERR_ARG;
576 }
43bdef26 577 column_count = logic_count = analog_count = 0;
1a920e33 578 auto_column_count = 0;
2142a79b
GS
579 for (format_idx = 0; format_idx < format_count; format_idx++) {
580 format = formats[format_idx];
581 ret = split_column_format(format, &c, &f, &b);
582 sr_dbg("fmt %s -> %zu cols, %s fmt, %zu bits, rc %d", format, c, col_format_text[f], b, ret);
583 if (ret != SR_OK) {
584 sr_err("Cannot parse columns format %s (field split, %s).", column_format, format);
585 g_strfreev(formats);
586 return SR_ERR_ARG;
587 }
1a920e33
GS
588 if (f && !c) {
589 /* User requested "auto-count", must be last format. */
590 if (formats[format_idx + 1]) {
591 sr_err("Auto column count must be last format field.");
592 g_strfreev(formats);
593 return SR_ERR_ARG;
594 }
595 auto_column_count = inc->column_seen_count - column_count;
596 c = auto_column_count;
597 }
2142a79b 598 column_count += c;
fc3b42e9 599 if (format_is_analog(f))
43bdef26 600 analog_count += c;
fc3b42e9 601 else if (format_is_logic(f))
43bdef26 602 logic_count += c * b;
2142a79b 603 }
43bdef26
GS
604 sr_dbg("Column format %s -> %zu columns, %zu logic, %zu analog channels.",
605 column_format, column_count, logic_count, analog_count);
2142a79b 606
05719d75 607 /* Allocate and fill in "column processing" details. */
2142a79b 608 inc->column_want_count = column_count;
9e7af34e
GS
609 if (inc->column_seen_count < inc->column_want_count) {
610 sr_err("Insufficient input text width for desired data amount, got %zu but want %zu columns.",
611 inc->column_seen_count, inc->column_want_count);
612 g_strfreev(formats);
613 return SR_ERR_ARG;
614 }
05719d75
GS
615 inc->logic_channels = logic_count;
616 inc->analog_channels = analog_count;
617 inc->analog_datafeed_digits = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_digits[0]));
618 inc->analog_datafeed_channels = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_channels[0]));
2142a79b 619 inc->column_details = g_malloc0_n(column_count, sizeof(inc->column_details[0]));
43bdef26 620 column_idx = channel_idx = analog_idx = 0;
9e7af34e 621 channel_name = g_string_sized_new(64);
2142a79b 622 for (format_idx = 0; format_idx < format_count; format_idx++) {
9e7af34e 623 /* Process a format field, which can span multiple columns. */
2142a79b
GS
624 format = formats[format_idx];
625 (void)split_column_format(format, &c, &f, &b);
1a920e33
GS
626 if (f && !c)
627 c = auto_column_count;
2142a79b 628 while (c-- > 0) {
9e7af34e 629 /* Fill in a column's processing details. */
2142a79b
GS
630 detail = &inc->column_details[column_idx++];
631 detail->col_nr = column_idx;
632 detail->text_format = f;
fc3b42e9 633 if (format_is_analog(detail->text_format)) {
43bdef26
GS
634 detail->channel_offset = analog_idx;
635 detail->channel_count = 1;
a267bf45 636 detail->analog_digits = b;
43bdef26 637 analog_idx += detail->channel_count;
fc3b42e9 638 } else if (format_is_logic(detail->text_format)) {
2142a79b
GS
639 detail->channel_offset = channel_idx;
640 detail->channel_count = b;
43bdef26 641 channel_idx += detail->channel_count;
fc3b42e9
GS
642 } else if (format_is_ignore(detail->text_format)) {
643 /* EMPTY */
9e7af34e 644 continue;
fc3b42e9
GS
645 } else {
646 /*
647 * Neither logic nor analog data, nor ignore.
648 * Format was noted. No channel creation involved.
649 */
650 continue;
651 }
9e7af34e 652 /*
08eb955a 653 * Pick most appropriate channel names. Optionally
9e7af34e
GS
654 * use text from a header line (when requested by the
655 * user). In the absence of header text, channels are
656 * assigned rather generic names.
657 *
658 * Manipulation of the column's caption (when a header
659 * line is seen) is acceptable, because this header
660 * line won't get processed another time.
661 */
662 column = column_texts[detail->col_nr - 1];
663 if (inc->use_header && column && *column)
664 caption = sr_scpi_unquote_string(column);
665 else
666 caption = NULL;
667 if (!caption || !*caption)
668 caption = NULL;
3f1f63f0 669 /*
05719d75
GS
670 * Collect channel creation details here, but defer
671 * actual creation of the channels such that all
672 * logic channels can get created first and analog
673 * channels only get created afterwards.
3f1f63f0 674 */
05719d75 675 detail->channel_names = g_malloc0(detail->channel_count * sizeof(detail->channel_names[0]));
9e7af34e
GS
676 for (create_idx = 0; create_idx < detail->channel_count; create_idx++) {
677 if (caption && detail->channel_count == 1) {
678 g_string_assign(channel_name, caption);
679 } else if (caption) {
680 g_string_printf(channel_name, "%s[%zu]",
681 caption, create_idx);
682 } else {
683 g_string_printf(channel_name, "%zu",
684 detail->channel_offset + create_idx);
685 }
05719d75 686 detail->channel_names[create_idx] = g_string_new_len(channel_name->str, channel_name->len);
9e7af34e 687 }
2142a79b
GS
688 }
689 }
9e7af34e 690 g_string_free(channel_name, TRUE);
2142a79b
GS
691 g_strfreev(formats);
692
05719d75
GS
693 /* Create channels in strict logic to analog order. */
694 channel_type = SR_CHANNEL_LOGIC;
695 for (column_idx = 0; column_idx < inc->column_want_count; column_idx++) {
696 detail = &inc->column_details[column_idx];
697 if (!format_is_logic(detail->text_format))
698 continue;
699 for (create_idx = 0; create_idx < detail->channel_count; create_idx++) {
700 caption = detail->channel_names[create_idx]->str;
701 channel_sdi_nr = g_slist_length(in->sdi->channels);
702 sr_channel_new(in->sdi, channel_sdi_nr, channel_type, TRUE, caption);
703 }
704 }
705 channel_type = SR_CHANNEL_ANALOG;
706 for (column_idx = 0; column_idx < inc->column_want_count; column_idx++) {
707 detail = &inc->column_details[column_idx];
708 if (!format_is_analog(detail->text_format))
709 continue;
710 caption = detail->channel_names[0]->str;
711 channel_sdi_nr = g_slist_length(in->sdi->channels);
712 channel = sr_channel_new(in->sdi, channel_sdi_nr, channel_type, TRUE, caption);
713 channel_idx = channel_sdi_nr - inc->logic_channels;
714 inc->analog_datafeed_digits[channel_idx] = detail->analog_digits;
715 inc->analog_datafeed_channels[channel_idx] = g_slist_append(NULL, channel);
716 }
717
2142a79b
GS
718 return SR_OK;
719}
720
e53f32d2
GS
721static const struct column_details *lookup_column_details(struct context *inc, size_t nr)
722{
723 if (!inc || !inc->column_details)
724 return NULL;
725 if (!nr || nr > inc->column_want_count)
726 return NULL;
727 return &inc->column_details[nr - 1];
728}
729
19267272
GS
730/*
731 * Primitive operations for text input: Strip comments off text lines.
732 * Split text lines into columns. Process input text for individual
733 * columns.
734 */
735
41d214f6 736static void strip_comment(char *buf, const GString *prefix)
4a35548b
MS
737{
738 char *ptr;
739
740 if (!prefix->len)
741 return;
742
b2c4dde2 743 if ((ptr = strstr(buf, prefix->str))) {
41d214f6 744 *ptr = '\0';
b2c4dde2
GS
745 g_strstrip(buf);
746 }
4a35548b
MS
747}
748
19267272 749/**
e53f32d2 750 * @brief Splits a text line into a set of columns.
19267272 751 *
e53f32d2 752 * @param[in] buf The input text line to split.
19267272
GS
753 * @param[in] inc The input module's context.
754 *
e53f32d2 755 * @returns An array of strings, representing the columns' text.
19267272 756 *
e53f32d2 757 * This routine splits a text line on previously determined separators.
19267272 758 */
e53f32d2 759static char **split_line(char *buf, struct context *inc)
4a35548b 760{
e53f32d2 761 return g_strsplit(buf, inc->delimiter->str, 0);
4a35548b
MS
762}
763
19267272 764/**
e53f32d2 765 * @brief Parse a multi-bit field into several logic channels.
19267272 766 *
e53f32d2 767 * @param[in] column The input text, a run of bin/hex/oct digits.
19267272 768 * @param[in] inc The input module's context.
836fac9c 769 * @param[in] details The column processing details.
19267272
GS
770 *
771 * @retval SR_OK Success.
772 * @retval SR_ERR Invalid input data (empty, or format error).
773 *
774 * This routine modifies the logic levels in the current sample set,
e53f32d2 775 * based on the text input and a user provided format spec.
19267272 776 */
836fac9c
GS
777static int parse_logic(const char *column, struct context *inc,
778 const struct column_details *details)
4a35548b 779{
e53f32d2
GS
780 size_t length, ch_rem, ch_idx, ch_inc;
781 const char *rdptr;
4a35548b 782 char c;
e53f32d2
GS
783 gboolean valid;
784 const char *type_text;
785 uint8_t bits;
786
e53f32d2
GS
787 /*
788 * Prepare to read the digits from the text end towards the start.
789 * A digit corresponds to a variable number of channels (depending
790 * on the value's radix). Prepare the mapping of text digits to
791 * (a number of) logic channels.
792 */
793 length = strlen(column);
4a35548b 794 if (!length) {
836fac9c 795 sr_err("Column %zu in line %zu is empty.", details->col_nr,
41d214f6 796 inc->line_number);
4a35548b
MS
797 return SR_ERR;
798 }
e53f32d2 799 rdptr = &column[length];
836fac9c
GS
800 ch_idx = details->channel_offset;
801 ch_rem = details->channel_count;
4a35548b 802
e53f32d2
GS
803 /*
804 * Get another digit and derive up to four logic channels' state from
805 * it. Make sure to not process more bits than the column has channels
806 * associated with it.
807 */
808 while (rdptr > column && ch_rem) {
809 /* Check for valid digits according to the input radix. */
810 c = *(--rdptr);
836fac9c 811 switch (details->text_format) {
e53f32d2
GS
812 case FORMAT_BIN:
813 valid = g_ascii_isxdigit(c) && c < '2';
814 ch_inc = 1;
815 break;
816 case FORMAT_OCT:
817 valid = g_ascii_isxdigit(c) && c < '8';
818 ch_inc = 3;
819 break;
820 case FORMAT_HEX:
821 valid = g_ascii_isxdigit(c);
822 ch_inc = 4;
823 break;
824 default:
825 valid = FALSE;
826 break;
4a35548b 827 }
e53f32d2 828 if (!valid) {
836fac9c 829 type_text = col_format_text[details->text_format];
e53f32d2 830 sr_err("Invalid text '%s' in %s type column %zu in line %zu.",
836fac9c 831 column, type_text, details->col_nr, inc->line_number);
4a35548b 832 return SR_ERR;
e53f32d2
GS
833 }
834 /* Use the digit's bits for logic channels' data. */
835 bits = g_ascii_xdigit_value(c);
836fac9c 836 switch (details->text_format) {
e53f32d2
GS
837 case FORMAT_HEX:
838 if (ch_rem >= 4) {
839 ch_rem--;
840 set_logic_level(inc, ch_idx + 3, bits & (1 << 3));
841 }
842 /* FALLTHROUGH */
843 case FORMAT_OCT:
844 if (ch_rem >= 3) {
845 ch_rem--;
846 set_logic_level(inc, ch_idx + 2, bits & (1 << 2));
847 }
848 if (ch_rem >= 2) {
849 ch_rem--;
850 set_logic_level(inc, ch_idx + 1, bits & (1 << 1));
851 }
852 /* FALLTHROUGH */
853 case FORMAT_BIN:
854 ch_rem--;
855 set_logic_level(inc, ch_idx + 0, bits & (1 << 0));
856 break;
fc3b42e9 857 default:
836fac9c
GS
858 /* ShouldNotHappen(TM), but silences compiler warning. */
859 return SR_ERR;
4a35548b 860 }
e53f32d2 861 ch_idx += ch_inc;
4a35548b 862 }
e53f32d2
GS
863 /*
864 * TODO Determine whether the availability of extra input data
865 * for unhandled logic channels is worth warning here. In this
866 * implementation users are in control, and can have the more
867 * significant bits ignored (which can be considered a feature
868 * and not really a limitation).
869 */
4a35548b
MS
870
871 return SR_OK;
872}
873
43bdef26
GS
874/**
875 * @brief Parse a floating point text into an analog value.
876 *
877 * @param[in] column The input text, a floating point number.
878 * @param[in] inc The input module's context.
879 * @param[in] details The column processing details.
880 *
881 * @retval SR_OK Success.
882 * @retval SR_ERR Invalid input data (empty, or format error).
883 *
884 * This routine modifies the analog values in the current sample set,
885 * based on the text input and a user provided format spec.
886 */
887static int parse_analog(const char *column, struct context *inc,
888 const struct column_details *details)
889{
890 size_t length;
891 double dvalue; float fvalue;
892 csv_analog_t value;
893 int ret;
894
fc3b42e9 895 if (!format_is_analog(details->text_format))
43bdef26
GS
896 return SR_ERR_BUG;
897
898 length = strlen(column);
899 if (!length) {
900 sr_err("Column %zu in line %zu is empty.", details->col_nr,
901 inc->line_number);
902 return SR_ERR;
903 }
904 if (sizeof(value) == sizeof(double)) {
905 ret = sr_atod_ascii(column, &dvalue);
906 value = dvalue;
907 } else if (sizeof(value) == sizeof(float)) {
908 ret = sr_atof_ascii(column, &fvalue);
909 value = fvalue;
910 } else {
911 ret = SR_ERR_BUG;
912 }
913 if (ret != SR_OK) {
914 sr_err("Cannot parse analog text %s in column %zu in line %zu.",
915 column, details->col_nr, inc->line_number);
916 return SR_ERR_DATA;
917 }
918 set_analog_value(inc, details->channel_offset, value);
919
920 return SR_OK;
921}
922
7e4e65bf
GS
923/**
924 * @brief Parse a timestamp text, auto-determine samplerate.
925 *
926 * @param[in] column The input text, a floating point number.
927 * @param[in] inc The input module's context.
928 * @param[in] details The column processing details.
929 *
930 * @retval SR_OK Success.
931 * @retval SR_ERR Invalid input data (empty, or format error).
932 *
933 * This routine attempts to automatically determine the input data's
934 * samplerate from text rows' timestamp values. Only simple formats are
935 * supported, user provided values always take precedence.
936 */
937static int parse_timestamp(const char *column, struct context *inc,
938 const struct column_details *details)
939{
940 double ts, rate;
941 int ret;
942
943 if (!format_is_timestamp(details->text_format))
944 return SR_ERR_BUG;
945
946 /*
947 * Implementor's notes on timestamp interpretation. Use a simple
948 * approach for improved maintainability which covers most cases
949 * of input data. There is not much gain in adding complexity,
950 * users can easily provide the rate when auto-detection fails.
951 * - Bail out if samplerate is known already.
952 * - Try to interpret the timestamp (simple float conversion).
953 * If conversion fails then clear all previous knowledge and
954 * bail out (non-fatal, perhaps warn). Silently ignore values
955 * of zero since those could be silent fails -- assume that
956 * genuine data contains at least two adjacent rows with useful
957 * timestamps for the feature to work reliably. Annoying users
958 * with "failed to detect" messages is acceptable here, since
959 * users expecting the feature to work should provide useful
960 * data, and there are easy ways to disable the detection or
961 * ignore the column.
962 * - If there is no previous timestamp, keep the current value
963 * for later reference and bail out.
964 * - If a previous timestamp was seen, determine the difference
965 * between them, and derive the samplerate. Update internal
966 * state (the value automatically gets sent to the datafeed),
967 * and clear previous knowledge. Subsequent calls will ignore
968 * following input data (see above, rate is known).
969 *
970 * TODO Potential future improvements:
971 * - Prefer rationals over floats for improved precision and
972 * reduced rounding errors which result in odd rates.
973 * - Support other formats ("2 ms" or similar)?
974 */
975 if (inc->samplerate)
976 return SR_OK;
977 ret = sr_atod_ascii(column, &ts);
978 if (ret != SR_OK)
979 ts = 0.0;
980 if (!ts) {
981 sr_warn("Cannot convert timestamp text %s in line %zu (or zero value).",
982 column, inc->line_number);
983 inc->prev_timestamp = 0.0;
984 return SR_OK;
985 }
986 if (!inc->prev_timestamp) {
987 sr_dbg("First timestamp value %g in line %zu.",
988 ts, inc->line_number);
989 inc->prev_timestamp = ts;
990 return SR_OK;
991 }
992 sr_dbg("Second timestamp value %g in line %zu.", ts, inc->line_number);
993 ts -= inc->prev_timestamp;
994 sr_dbg("Timestamp difference %g in line %zu.",
995 ts, inc->line_number);
996 if (!ts) {
997 sr_warn("Zero timestamp difference in line %zu.",
998 inc->line_number);
999 inc->prev_timestamp = ts;
1000 return SR_OK;
1001 }
1002 rate = 1.0 / ts;
1003 rate += 0.5;
1004 rate = (uint64_t)rate;
1005 sr_dbg("Rate from timestamp %g in line %zu.", rate, inc->line_number);
1006 inc->samplerate = rate;
1007 inc->prev_timestamp = 0.0;
1008
1009 return SR_OK;
1010}
1011
836fac9c
GS
1012/**
1013 * @brief Parse routine which ignores the input text.
1014 *
1015 * This routine exists to unify dispatch code paths, mapping input file
1016 * columns' data types to their respective parse routines.
1017 */
1018static int parse_ignore(const char *column, struct context *inc,
1019 const struct column_details *details)
1020{
1021 (void)column;
1022 (void)inc;
1023 (void)details;
1024 return SR_OK;
1025}
1026
1027typedef int (*col_parse_cb)(const char *column, struct context *inc,
1028 const struct column_details *details);
1029
1030static const col_parse_cb col_parse_funcs[] = {
1031 [FORMAT_NONE] = parse_ignore,
1032 [FORMAT_BIN] = parse_logic,
1033 [FORMAT_OCT] = parse_logic,
1034 [FORMAT_HEX] = parse_logic,
43bdef26 1035 [FORMAT_ANALOG] = parse_analog,
7e4e65bf 1036 [FORMAT_TIME] = parse_timestamp,
836fac9c
GS
1037};
1038
cd7c5f96
GS
1039/*
1040 * BEWARE! Implementor's notes. Sync with feature set and default option
1041 * values required during maintenance of the input module implementation.
1042 *
1043 * When applications invoke .format_match() routines, trying automatic
1044 * determination of an input file's format handler, then no options are
1045 * in effect. Because specifying options requires selection of an input
1046 * module to pass the options to, which obsoletes the format-match check.
1047 *
1048 * Which means that we only need to deal with the default format here,
1049 * which happens to be the simple multi-column format without header
1050 * lines or leading garbage. Which means that the check can be rather
1051 * strict, resulting in high levels of confidence upon match, never
1052 * "accidently" winning for unreadable or unsupported-by-default formats.
1053 *
1054 * This .format_match() logic only needs to become more involved when
1055 * default option values change, or when automatic detection of column
1056 * data types improves. Then the supported-by-default types of input
1057 * data must be considered acceptable here in the format-match check
1058 * as well.
1059 *
1060 * Notice that the format check cannot re-use regular processing logic
1061 * when their implementation assumes proper input data and wll generate
1062 * diagnostics for unexpected input data. Failure to match the format is
1063 * non-fatal here, mismatch must remain silent. It's up to applications
1064 * how large a chunk of data gets passed here (start of the file's
1065 * content). But inspection of the first few hundred bytes will usually
1066 * be GoodEnough(TM) for the format-match purpose. Notice that filenames
1067 * need not necessarily be available to the format-match routine.
1068 *
1069 * This implementation errs on the safe side. Users can always select
1070 * the CSV input module when automatic format detection fails.
1071 */
1072static int format_match(GHashTable *metadata, unsigned int *confidence)
1073{
1074 const int match_confidence = 100;
1075 const char *default_extension = ".csv";
1076 const char *line_termination = "\n";
1077 const char *comment_leader = ";";
1078 const char *column_separator = ",";
1079 const char *binary_charset = "01";
1080
1081 const char *fn;
1082 GString *buf;
1083 size_t fn_len;
1084 GString *tmpbuf;
1085 gboolean status;
1086 size_t line_idx, col_idx;
1087 char *rdptr, **lines, *line;
1088 char **cols, *col;
1089
1090 /* Get the application provided input data properties. */
1091 fn = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_FILENAME));
1092 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
1093
1094 /* Filenames are a strong hint. Use then when available. */
1095 if (fn && *fn && (fn_len = strlen(fn)) >= strlen(default_extension)) {
1096 if (strcasecmp(&fn[fn_len - strlen(default_extension)], default_extension) == 0) {
1097 *confidence = 10;
1098 return SR_OK;
1099 }
1100 }
1101
1102 /*
1103 * Check file content for compatibility with the input module's
1104 * default format. Which translates to:
1105 * - Must be at least one text line worth of input data. Ignore
1106 * incomplete lines at the end of the available buffer.
1107 * - Must be LF terminated text lines, optional CR-LF sequence.
1108 * (Drop CR-only for simplicity since that's rare and users
1109 * can override the automatic detection.)
1110 * - Strip comments and skip empty lines.
1111 * - Data lines must be binary input (potentially multiple bits
1112 * per column which then get ignored). Presence of comma is
1113 * optional but then must be followed by another data column.
1114 * - No other content is acceptable, there neither are ignored
1115 * columns nor analog data nor timestamps in the default layout.
1116 * (See the above "sync format match with default options"
1117 * comment though during maintenance!)
1118 * Run the check on a copy to not affect the caller's buffer.
1119 */
1120 if (!buf || !buf->len || !buf->str || !*buf->str)
1121 return SR_ERR;
1122 rdptr = g_strstr_len(buf->str, buf->len, line_termination);
1123 if (!rdptr)
1124 return SR_ERR;
1125 tmpbuf = g_string_new_len(buf->str, rdptr + 1 - buf->str);
1126 tmpbuf->str[tmpbuf->len - 1] = '\0';
1127 status = TRUE;
1128 *confidence = match_confidence;
1129 lines = g_strsplit(tmpbuf->str, line_termination, 0);
1130 for (line_idx = 0; status && (line = lines[line_idx]); line_idx++) {
1131 rdptr = strstr(line, comment_leader);
1132 if (rdptr)
1133 *rdptr = '\0';
1134 line = g_strstrip(line);
1135 if (!line || !*line)
1136 continue;
1137 cols = g_strsplit(line, column_separator, 0);
1138 if (!cols) {
1139 status = FALSE;
1140 break;
1141 }
1142 for (col_idx = 0; status && (col = cols[col_idx]); col_idx++) {
1143 if (strspn(col, binary_charset) != strlen(col)) {
1144 status = FALSE;
1145 break;
1146 }
1147 }
1148 g_strfreev(cols);
1149 }
1150 g_strfreev(lines);
1151 g_string_free(tmpbuf, TRUE);
1152
1153 if (!status)
1154 return SR_ERR;
1155 return SR_OK;
1156}
1157
41d214f6 1158static int init(struct sr_input *in, GHashTable *options)
4a35548b 1159{
41d214f6 1160 struct context *inc;
1a920e33 1161 size_t single_column, first_column, logic_channels;
41d214f6 1162 const char *s;
836fac9c 1163 enum single_col_format format;
1a920e33 1164 char format_char;
4a35548b 1165
836fac9c
GS
1166 in->sdi = g_malloc0(sizeof(*in->sdi));
1167 in->priv = inc = g_malloc0(sizeof(*inc));
4a35548b 1168
72903e9d 1169 single_column = g_variant_get_uint32(g_hash_table_lookup(options, "single_column"));
72903e9d 1170 logic_channels = g_variant_get_uint32(g_hash_table_lookup(options, "logic_channels"));
41d214f6 1171 inc->delimiter = g_string_new(g_variant_get_string(
72903e9d 1172 g_hash_table_lookup(options, "column_separator"), NULL));
836fac9c 1173 if (!inc->delimiter->len) {
72903e9d 1174 sr_err("Column separator cannot be empty.");
41d214f6 1175 return SR_ERR_ARG;
4a35548b 1176 }
72903e9d 1177 s = g_variant_get_string(g_hash_table_lookup(options, "single_format"), NULL);
836fac9c
GS
1178 if (g_ascii_strncasecmp(s, "bin", 3) == 0) {
1179 format = FORMAT_BIN;
1180 } else if (g_ascii_strncasecmp(s, "hex", 3) == 0) {
1181 format = FORMAT_HEX;
1182 } else if (g_ascii_strncasecmp(s, "oct", 3) == 0) {
1183 format = FORMAT_OCT;
41d214f6 1184 } else {
72903e9d 1185 sr_err("Invalid single-column format: '%s'", s);
41d214f6 1186 return SR_ERR_ARG;
4a35548b 1187 }
41d214f6 1188 inc->comment = g_string_new(g_variant_get_string(
72903e9d 1189 g_hash_table_lookup(options, "comment_leader"), NULL));
41d214f6 1190 if (g_string_equal(inc->comment, inc->delimiter)) {
e53f32d2
GS
1191 /*
1192 * Using the same sequence as comment leader and column
72903e9d
GS
1193 * separator won't work. The user probably specified ';'
1194 * as the column separator but did not adjust the comment
e53f32d2
GS
1195 * leader. Try DWIM, drop comment strippin support here.
1196 */
72903e9d 1197 sr_warn("Comment leader and column separator conflict, disabling comment support.");
41d214f6 1198 g_string_truncate(inc->comment, 0);
4a35548b 1199 }
6e8d95a5 1200 inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
72903e9d 1201 first_column = g_variant_get_uint32(g_hash_table_lookup(options, "first_column"));
de8fe3b5 1202 inc->use_header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
72903e9d 1203 inc->start_line = g_variant_get_uint32(g_hash_table_lookup(options, "start_line"));
41d214f6 1204 if (inc->start_line < 1) {
6433156c 1205 sr_err("Invalid start line %zu.", inc->start_line);
41d214f6 1206 return SR_ERR_ARG;
4a35548b
MS
1207 }
1208
e53f32d2 1209 /*
1a920e33
GS
1210 * Scan flexible, to get prefered format specs which describe
1211 * the input file's data formats. As well as some simple specs
1212 * for backwards compatibility and user convenience.
1213 *
1214 * This logic ends up with a copy of the format string, either
1215 * user provided or internally derived. Actual creation of the
1216 * column processing details gets deferred until the first line
1217 * of input data was seen. To support automatic determination of
1218 * e.g. channel counts from column counts.
e53f32d2 1219 */
72903e9d 1220 s = g_variant_get_string(g_hash_table_lookup(options, "column_formats"), NULL);
2142a79b 1221 if (s && *s) {
1a920e33 1222 inc->column_formats = g_strdup(s);
72903e9d 1223 sr_dbg("User specified column_formats: %s.", s);
1a920e33
GS
1224 } else if (single_column && logic_channels) {
1225 format_char = col_format_char[format];
1226 if (single_column == 1) {
1227 inc->column_formats = g_strdup_printf("%c%zu",
1228 format_char, logic_channels);
e53f32d2 1229 } else {
1a920e33
GS
1230 inc->column_formats = g_strdup_printf("%zu-,%c%zu",
1231 single_column - 1,
1232 format_char, logic_channels);
e53f32d2 1233 }
72903e9d 1234 sr_dbg("Backwards compat single_column, col %zu, fmt %s, bits %zu -> %s.",
1a920e33
GS
1235 single_column, col_format_text[format], logic_channels,
1236 inc->column_formats);
1237 } else if (!single_column) {
1238 if (first_column > 1) {
1239 inc->column_formats = g_strdup_printf("%zu-,%zul",
1240 first_column - 1, logic_channels);
1241 } else {
1242 inc->column_formats = g_strdup_printf("%zul",
1243 logic_channels);
1244 }
1245 sr_dbg("Backwards compat multi-column, col %zu, chans %zu -> %s.",
1246 first_column, logic_channels,
1247 inc->column_formats);
e53f32d2 1248 } else {
72903e9d 1249 sr_warn("Unknown or unsupported columns layout spec, assuming simple multi-column mode.");
1a920e33 1250 inc->column_formats = g_strdup("*l");
4a35548b
MS
1251 }
1252
41d214f6
BV
1253 return SR_OK;
1254}
4a35548b 1255
affaf540
GS
1256/*
1257 * Check the channel list for consistency across file re-import. See
1258 * the VCD input module for more details and motivation.
1259 */
1260
1261static void keep_header_for_reread(const struct sr_input *in)
1262{
1263 struct context *inc;
1264
1265 inc = in->priv;
1266 g_slist_free_full(inc->prev_sr_channels, sr_channel_free_cb);
1267 inc->prev_sr_channels = in->sdi->channels;
1268 in->sdi->channels = NULL;
1269}
1270
1271static int check_header_in_reread(const struct sr_input *in)
1272{
1273 struct context *inc;
1274
1275 if (!in)
1276 return FALSE;
1277 inc = in->priv;
1278 if (!inc)
1279 return FALSE;
1280 if (!inc->prev_sr_channels)
1281 return TRUE;
1282
1283 if (sr_channel_lists_differ(inc->prev_sr_channels, in->sdi->channels)) {
1284 sr_err("Channel list change not supported for file re-read.");
1285 return FALSE;
1286 }
1287 g_slist_free_full(in->sdi->channels, sr_channel_free_cb);
1288 in->sdi->channels = inc->prev_sr_channels;
1289 inc->prev_sr_channels = NULL;
1290
1291 return TRUE;
1292}
1293
492dfa90
GS
1294static const char *delim_set = "\r\n";
1295
329733d9 1296static const char *get_line_termination(GString *buf)
41d214f6 1297{
329733d9 1298 const char *term;
4a35548b 1299
41d214f6
BV
1300 term = NULL;
1301 if (g_strstr_len(buf->str, buf->len, "\r\n"))
1302 term = "\r\n";
1303 else if (memchr(buf->str, '\n', buf->len))
1304 term = "\n";
1305 else if (memchr(buf->str, '\r', buf->len))
1306 term = "\r";
4a35548b 1307
41d214f6
BV
1308 return term;
1309}
4a35548b 1310
41d214f6
BV
1311static int initial_parse(const struct sr_input *in, GString *buf)
1312{
1313 struct context *inc;
9e7af34e 1314 size_t num_columns;
3f1f63f0 1315 size_t line_number, line_idx;
41d214f6 1316 int ret;
9e7af34e 1317 char **lines, *line, **columns;
41d214f6
BV
1318
1319 ret = SR_OK;
1320 inc = in->priv;
1321 columns = NULL;
1322
9e7af34e 1323 /* Search for the first line to process (header or data). */
41d214f6 1324 line_number = 0;
ef0b9935
GS
1325 if (inc->termination)
1326 lines = g_strsplit(buf->str, inc->termination, 0);
1327 else
1328 lines = g_strsplit_set(buf->str, delim_set, 0);
e53f32d2 1329 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6
BV
1330 line_number++;
1331 if (inc->start_line > line_number) {
e53f32d2 1332 sr_spew("Line %zu skipped (before start).", line_number);
4a35548b
MS
1333 continue;
1334 }
df0db9fd 1335 if (line[0] == '\0') {
41d214f6
BV
1336 sr_spew("Blank line %zu skipped.", line_number);
1337 continue;
1338 }
df0db9fd
GS
1339 strip_comment(line, inc->comment);
1340 if (line[0] == '\0') {
41d214f6 1341 sr_spew("Comment-only line %zu skipped.", line_number);
4a35548b
MS
1342 continue;
1343 }
1344
41d214f6
BV
1345 /* Reached first proper line. */
1346 break;
1347 }
e53f32d2 1348 if (!line) {
41d214f6 1349 /* Not enough data for a proper line yet. */
60107497 1350 ret = SR_ERR_NA;
41d214f6 1351 goto out;
4a35548b
MS
1352 }
1353
9e7af34e 1354 /* Get the number of columns in the line. */
e53f32d2 1355 columns = split_line(line, inc);
df0db9fd 1356 if (!columns) {
41d214f6
BV
1357 sr_err("Error while parsing line %zu.", line_number);
1358 ret = SR_ERR;
1359 goto out;
4a35548b 1360 }
4a35548b 1361 num_columns = g_strv_length(columns);
4a35548b 1362 if (!num_columns) {
e53f32d2 1363 sr_err("Error while parsing line %zu.", line_number);
41d214f6
BV
1364 ret = SR_ERR;
1365 goto out;
4a35548b 1366 }
e53f32d2
GS
1367 sr_dbg("DIAG Got %zu columns in text line: %s.", num_columns, line);
1368
1a920e33 1369 /*
9e7af34e
GS
1370 * Interpret the user provided column format specs. This might
1371 * involve inspection of the now received input text, to support
1372 * e.g. automatic detection of channel counts in the absence of
1373 * user provided specs. Optionally a header line is used to get
1374 * channels' names.
1375 *
1376 * Check the then created channels for consistency across .reset
1377 * and .receive sequences (file re-load).
1a920e33 1378 */
9e7af34e 1379 ret = make_column_details_from_format(in, inc->column_formats, columns);
1a920e33
GS
1380 if (ret != SR_OK) {
1381 sr_err("Cannot parse columns format using line %zu.", line_number);
1382 goto out;
4a35548b 1383 }
affaf540
GS
1384 if (!check_header_in_reread(in)) {
1385 ret = SR_ERR_DATA;
1386 goto out;
1387 }
4a35548b
MS
1388
1389 /*
9e7af34e 1390 * Allocate buffer memory for datafeed submission of sample data.
cd59e6ec
GS
1391 * Calculate the minimum buffer size to store the set of samples
1392 * of all channels (unit size). Determine a larger buffer size
1393 * for datafeed submission that is a multiple of the unit size.
626c388a
GS
1394 * Allocate the larger buffer, the "sample buffer" will point
1395 * to a location within that large buffer later.
fc3b42e9
GS
1396 *
1397 * TODO Move channel creation here, and just store required
1398 * parameters in the format parser above? Could simplify the
1399 * arrangement that logic and analog channels get created in
1400 * strict sequence in their respective groups.
4a35548b 1401 */
43bdef26
GS
1402 if (inc->logic_channels) {
1403 inc->sample_unit_size = (inc->logic_channels + 7) / 8;
1404 inc->datafeed_buf_size = CHUNK_SIZE;
1405 inc->datafeed_buf_size *= inc->sample_unit_size;
1406 inc->datafeed_buffer = g_malloc(inc->datafeed_buf_size);
1407 if (!inc->datafeed_buffer) {
1408 sr_err("Cannot allocate datafeed send buffer (logic).");
1409 ret = SR_ERR_MALLOC;
1410 goto out;
1411 }
1412 inc->datafeed_buf_fill = 0;
1413 }
1414
1415 if (inc->analog_channels) {
1416 size_t sample_size, sample_count;
1417 sample_size = sizeof(inc->analog_datafeed_buffer[0]);
1418 inc->analog_datafeed_buf_size = CHUNK_SIZE;
1419 inc->analog_datafeed_buf_size /= sample_size;
1420 inc->analog_datafeed_buf_size /= inc->analog_channels;
1421 sample_count = inc->analog_channels * inc->analog_datafeed_buf_size;
1422 inc->analog_datafeed_buffer = g_malloc0(sample_count * sample_size);
1423 if (!inc->analog_datafeed_buffer) {
1424 sr_err("Cannot allocate datafeed send buffer (analog).");
1425 ret = SR_ERR_MALLOC;
1426 goto out;
1427 }
1428 inc->analog_datafeed_buf_fill = 0;
43bdef26 1429 }
4a35548b 1430
41d214f6
BV
1431out:
1432 if (columns)
1433 g_strfreev(columns);
1434 g_strfreev(lines);
4a35548b 1435
41d214f6 1436 return ret;
4a35548b
MS
1437}
1438
4439363a
GS
1439/*
1440 * Gets called from initial_receive(), which runs until the end-of-line
1441 * encoding of the input stream could get determined. Assumes that this
1442 * routine receives enough buffered initial input data to either see the
1443 * BOM when there is one, or that no BOM will follow when a text line
1444 * termination sequence was seen. Silently drops the UTF-8 BOM sequence
1445 * from the input buffer if one was seen. Does not care to protect
1446 * against multiple execution or dropping the BOM multiple times --
1447 * there should be at most one in the input stream.
1448 */
1449static void initial_bom_check(const struct sr_input *in)
1450{
1451 static const char *utf8_bom = "\xef\xbb\xbf";
1452
1453 if (in->buf->len < strlen(utf8_bom))
1454 return;
1455 if (strncmp(in->buf->str, utf8_bom, strlen(utf8_bom)) != 0)
1456 return;
1457 g_string_erase(in->buf, 0, strlen(utf8_bom));
1458}
1459
41d214f6 1460static int initial_receive(const struct sr_input *in)
4a35548b 1461{
41d214f6
BV
1462 struct context *inc;
1463 GString *new_buf;
1464 int len, ret;
329733d9
UH
1465 char *p;
1466 const char *termination;
4a35548b 1467
4439363a
GS
1468 initial_bom_check(in);
1469
41d214f6 1470 inc = in->priv;
4a35548b 1471
df0db9fd
GS
1472 termination = get_line_termination(in->buf);
1473 if (!termination)
41d214f6 1474 /* Don't have a full line yet. */
d0181813 1475 return SR_ERR_NA;
4a35548b 1476
df0db9fd
GS
1477 p = g_strrstr_len(in->buf->str, in->buf->len, termination);
1478 if (!p)
41d214f6 1479 /* Don't have a full line yet. */
d0181813 1480 return SR_ERR_NA;
41d214f6
BV
1481 len = p - in->buf->str - 1;
1482 new_buf = g_string_new_len(in->buf->str, len);
1483 g_string_append_c(new_buf, '\0');
4a35548b 1484
41d214f6
BV
1485 inc->termination = g_strdup(termination);
1486
1487 if (in->buf->str[0] != '\0')
1488 ret = initial_parse(in, new_buf);
1489 else
1490 ret = SR_OK;
1491
1492 g_string_free(new_buf, TRUE);
1493
1494 return ret;
1495}
1496
7f4c3a62 1497static int process_buffer(struct sr_input *in, gboolean is_eof)
41d214f6 1498{
41d214f6
BV
1499 struct context *inc;
1500 gsize num_columns;
e53f32d2 1501 size_t line_idx, col_idx, col_nr;
836fac9c
GS
1502 const struct column_details *details;
1503 col_parse_cb parse_func;
ad6a2bee 1504 int ret;
e53f32d2 1505 char *p, **lines, *line, **columns, *column;
41d214f6 1506
41d214f6 1507 inc = in->priv;
d0181813 1508 if (!inc->started) {
bee2b016 1509 std_session_send_df_header(in->sdi);
d0181813 1510 inc->started = TRUE;
4a35548b
MS
1511 }
1512
4555d3bd
GS
1513 /*
1514 * Consider empty input non-fatal. Keep accumulating input until
1515 * at least one full text line has become available. Grab the
1516 * maximum amount of accumulated data that consists of full text
1517 * lines, and process what has been received so far, leaving not
1518 * yet complete lines for the next invocation.
7f4c3a62
GS
1519 *
1520 * Enforce that all previously buffered data gets processed in
1521 * the "EOF" condition. Do not insist in the presence of the
1522 * termination sequence for the last line (may often be missing
1523 * on Windows). A present termination sequence will just result
1524 * in the "execution of an empty line", and does not harm.
4555d3bd
GS
1525 */
1526 if (!in->buf->len)
1527 return SR_OK;
7f4c3a62
GS
1528 if (is_eof) {
1529 p = in->buf->str + in->buf->len;
1530 } else {
1531 p = g_strrstr_len(in->buf->str, in->buf->len, inc->termination);
1532 if (!p)
1533 return SR_ERR;
1534 *p = '\0';
1535 p += strlen(inc->termination);
1536 }
41d214f6 1537 g_strstrip(in->buf->str);
4a35548b 1538
18078d05 1539 ret = SR_OK;
ef0b9935 1540 lines = g_strsplit(in->buf->str, inc->termination, 0);
e53f32d2 1541 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6 1542 inc->line_number++;
ef0b9935
GS
1543 if (inc->line_number < inc->start_line) {
1544 sr_spew("Line %zu skipped (before start).", inc->line_number);
1545 continue;
1546 }
df0db9fd 1547 if (line[0] == '\0') {
41d214f6 1548 sr_spew("Blank line %zu skipped.", inc->line_number);
4a35548b
MS
1549 continue;
1550 }
1551
1552 /* Remove trailing comment. */
df0db9fd
GS
1553 strip_comment(line, inc->comment);
1554 if (line[0] == '\0') {
41d214f6 1555 sr_spew("Comment-only line %zu skipped.", inc->line_number);
4a35548b
MS
1556 continue;
1557 }
1558
160691b9 1559 /* Skip the header line, its content was used as the channel names. */
de8fe3b5 1560 if (inc->use_header && !inc->header_seen) {
160691b9 1561 sr_spew("Header line %zu skipped.", inc->line_number);
de8fe3b5 1562 inc->header_seen = TRUE;
160691b9
JS
1563 continue;
1564 }
1565
e53f32d2
GS
1566 /* Split the line into columns, check for minimum length. */
1567 columns = split_line(line, inc);
df0db9fd 1568 if (!columns) {
41d214f6 1569 sr_err("Error while parsing line %zu.", inc->line_number);
2355d229 1570 g_strfreev(lines);
4a35548b
MS
1571 return SR_ERR;
1572 }
4a35548b 1573 num_columns = g_strv_length(columns);
e53f32d2
GS
1574 if (num_columns < inc->column_want_count) {
1575 sr_err("Insufficient column count %zu in line %zu.",
1576 num_columns, inc->line_number);
4a35548b 1577 g_strfreev(columns);
2355d229 1578 g_strfreev(lines);
4a35548b
MS
1579 return SR_ERR;
1580 }
1581
836fac9c 1582 /* Have the columns of the current text line processed. */
626c388a 1583 clear_logic_samples(inc);
43bdef26 1584 clear_analog_samples(inc);
e53f32d2
GS
1585 for (col_idx = 0; col_idx < inc->column_want_count; col_idx++) {
1586 column = columns[col_idx];
1587 col_nr = col_idx + 1;
836fac9c
GS
1588 details = lookup_column_details(inc, col_nr);
1589 if (!details || !details->text_format)
1590 continue;
1591 parse_func = col_parse_funcs[details->text_format];
1592 if (!parse_func)
1593 continue;
1594 ret = parse_func(column, inc, details);
e53f32d2
GS
1595 if (ret != SR_OK) {
1596 g_strfreev(columns);
1597 g_strfreev(lines);
1598 return SR_ERR;
1599 }
4a35548b
MS
1600 }
1601
626c388a
GS
1602 /* Send sample data to the session bus (buffered). */
1603 ret = queue_logic_samples(in);
43bdef26 1604 ret += queue_analog_samples(in);
41d214f6 1605 if (ret != SR_OK) {
4a35548b 1606 sr_err("Sending samples failed.");
cd59e6ec 1607 g_strfreev(columns);
2355d229 1608 g_strfreev(lines);
4a35548b
MS
1609 return SR_ERR;
1610 }
cd59e6ec 1611
41d214f6
BV
1612 g_strfreev(columns);
1613 }
1614 g_strfreev(lines);
241c386a 1615 g_string_erase(in->buf, 0, p - in->buf->str);
41d214f6 1616
7066fd46 1617 return ret;
41d214f6
BV
1618}
1619
7066fd46 1620static int receive(struct sr_input *in, GString *buf)
41d214f6
BV
1621{
1622 struct context *inc;
7066fd46
BV
1623 int ret;
1624
1625 g_string_append_len(in->buf, buf->str, buf->len);
41d214f6
BV
1626
1627 inc = in->priv;
1a920e33 1628 if (!inc->column_seen_count) {
df0db9fd
GS
1629 ret = initial_receive(in);
1630 if (ret == SR_ERR_NA)
7066fd46
BV
1631 /* Not enough data yet. */
1632 return SR_OK;
1633 else if (ret != SR_OK)
1634 return SR_ERR;
1635
1636 /* sdi is ready, notify frontend. */
1637 in->sdi_ready = TRUE;
41d214f6 1638 return SR_OK;
7066fd46
BV
1639 }
1640
7f4c3a62 1641 ret = process_buffer(in, FALSE);
7066fd46
BV
1642
1643 return ret;
1644}
1645
1646static int end(struct sr_input *in)
1647{
1648 struct context *inc;
7066fd46 1649 int ret;
41d214f6 1650
7066fd46 1651 if (in->sdi_ready)
7f4c3a62 1652 ret = process_buffer(in, TRUE);
7066fd46
BV
1653 else
1654 ret = SR_OK;
cd59e6ec
GS
1655 if (ret != SR_OK)
1656 return ret;
1657
626c388a 1658 ret = flush_logic_samples(in);
43bdef26 1659 ret += flush_analog_samples(in);
cd59e6ec
GS
1660 if (ret != SR_OK)
1661 return ret;
7066fd46
BV
1662
1663 inc = in->priv;
3be42bc2 1664 if (inc->started)
bee2b016 1665 std_session_send_df_end(in->sdi);
4a35548b 1666
7066fd46
BV
1667 return ret;
1668}
1669
d5cc282f 1670static void cleanup(struct sr_input *in)
7066fd46
BV
1671{
1672 struct context *inc;
1673
affaf540
GS
1674 keep_header_for_reread(in);
1675
7066fd46
BV
1676 inc = in->priv;
1677
b1f83103 1678 g_free(inc->termination);
539188e5 1679 inc->termination = NULL;
cd59e6ec 1680 g_free(inc->datafeed_buffer);
539188e5 1681 inc->datafeed_buffer = NULL;
43bdef26
GS
1682 g_free(inc->analog_datafeed_buffer);
1683 inc->analog_datafeed_buffer = NULL;
4a35548b
MS
1684}
1685
ad93bfb0
SA
1686static int reset(struct sr_input *in)
1687{
1688 struct context *inc = in->priv;
1689
1690 cleanup(in);
1691 inc->started = FALSE;
1692 g_string_truncate(in->buf, 0);
1693
1694 return SR_OK;
1695}
1696
c6aa9870 1697enum option_index {
2142a79b 1698 OPT_COL_FMTS,
c6aa9870 1699 OPT_SINGLE_COL,
72903e9d 1700 OPT_FIRST_COL,
c6aa9870 1701 OPT_NUM_LOGIC,
43e1e23a
GS
1702 OPT_SINGLE_FMT,
1703 OPT_START_LINE,
72903e9d 1704 OPT_HEADER,
43e1e23a
GS
1705 OPT_SAMPLERATE,
1706 OPT_COL_SEP,
72903e9d 1707 OPT_COMMENT,
c6aa9870
GS
1708 OPT_MAX,
1709};
1710
41d214f6 1711static struct sr_option options[] = {
72903e9d
GS
1712 [OPT_COL_FMTS] = {
1713 "column_formats", "Column format specs",
43e1e23a 1714 "Text columns data types. A comma separated list of [<cols>]<fmt>[<bits>] items. * for all remaining columns. - ignores columns, x/o/b/l logic data, a (and digits) analog data, t timestamps.",
72903e9d
GS
1715 NULL, NULL,
1716 },
1717 [OPT_SINGLE_COL] = {
1718 "single_column", "Single column",
43e1e23a 1719 "Simple single-column mode, exclusively use text from the specified column (number starting at 1). Obsoleted by 'column_formats=4-,x16'.",
72903e9d
GS
1720 NULL, NULL,
1721 },
1722 [OPT_FIRST_COL] = {
1723 "first_column", "First column",
43e1e23a 1724 "First column with logic data in simple multi-column mode (number starting at 1, default 1). Obsoleted by 'column_formats=4-,*l'.",
72903e9d
GS
1725 NULL, NULL,
1726 },
1727 [OPT_NUM_LOGIC] = {
1728 "logic_channels", "Number of logic channels",
43e1e23a 1729 "Logic channel count, required in simple single-column mode, defaults to \"all remaining columns\" in simple multi-column mode. Obsoleted by 'column_formats=8l'.",
72903e9d
GS
1730 NULL, NULL,
1731 },
43e1e23a 1732 [OPT_SINGLE_FMT] = {
72903e9d 1733 "single_format", "Data format for simple single-column mode.",
43e1e23a 1734 "The input text number format of simple single-column mode: bin, hex, oct. Obsoleted by 'column_formats=x8'.",
72903e9d
GS
1735 NULL, NULL,
1736 },
43e1e23a 1737 [OPT_START_LINE] = {
72903e9d
GS
1738 "start_line", "Start line",
1739 "The line number at which to start processing input text (default: 1).",
1740 NULL, NULL,
1741 },
1742 [OPT_HEADER] = {
1743 "header", "Get channel names from first line.",
08eb955a 1744 "Use the first processed line's column captions (when available) as channel names. Off by default",
72903e9d
GS
1745 NULL, NULL,
1746 },
43e1e23a 1747 [OPT_SAMPLERATE] = {
72903e9d 1748 "samplerate", "Samplerate (Hz)",
08eb955a 1749 "The input data's sample rate in Hz. No default value.",
72903e9d
GS
1750 NULL, NULL,
1751 },
43e1e23a 1752 [OPT_COL_SEP] = {
72903e9d
GS
1753 "column_separator", "Column separator",
1754 "The sequence which separates text columns. Non-empty text, comma by default.",
1755 NULL, NULL,
1756 },
1757 [OPT_COMMENT] = {
1758 "comment_leader", "Comment leader character",
08eb955a 1759 "The text which starts comments at the end of text lines, semicolon by default.",
72903e9d
GS
1760 NULL, NULL,
1761 },
c6aa9870 1762 [OPT_MAX] = ALL_ZERO,
41d214f6
BV
1763};
1764
2c240774 1765static const struct sr_option *get_options(void)
41d214f6 1766{
31c41782
UH
1767 GSList *l;
1768
41d214f6 1769 if (!options[0].def) {
1a920e33 1770 options[OPT_COL_FMTS].def = g_variant_ref_sink(g_variant_new_string(""));
e53f32d2 1771 options[OPT_SINGLE_COL].def = g_variant_ref_sink(g_variant_new_uint32(0));
72903e9d 1772 options[OPT_FIRST_COL].def = g_variant_ref_sink(g_variant_new_uint32(1));
e53f32d2 1773 options[OPT_NUM_LOGIC].def = g_variant_ref_sink(g_variant_new_uint32(0));
43e1e23a 1774 options[OPT_SINGLE_FMT].def = g_variant_ref_sink(g_variant_new_string("bin"));
31c41782
UH
1775 l = NULL;
1776 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("bin")));
1777 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("hex")));
1778 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct")));
43e1e23a
GS
1779 options[OPT_SINGLE_FMT].values = l;
1780 options[OPT_START_LINE].def = g_variant_ref_sink(g_variant_new_uint32(1));
72903e9d 1781 options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
43e1e23a
GS
1782 options[OPT_SAMPLERATE].def = g_variant_ref_sink(g_variant_new_uint64(0));
1783 options[OPT_COL_SEP].def = g_variant_ref_sink(g_variant_new_string(","));
72903e9d 1784 options[OPT_COMMENT].def = g_variant_ref_sink(g_variant_new_string(";"));
41d214f6
BV
1785 }
1786
1787 return options;
1788}
1789
d4c93774 1790SR_PRIV struct sr_input_module input_csv = {
4a35548b 1791 .id = "csv",
41d214f6
BV
1792 .name = "CSV",
1793 .desc = "Comma-separated values",
c7bc82ff 1794 .exts = (const char*[]){"csv", NULL},
cd7c5f96 1795 .metadata = { SR_INPUT_META_FILENAME, SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
41d214f6 1796 .options = get_options,
cd7c5f96 1797 .format_match = format_match,
4a35548b 1798 .init = init,
41d214f6 1799 .receive = receive,
7066fd46 1800 .end = end,
41d214f6 1801 .cleanup = cleanup,
ad93bfb0 1802 .reset = reset,
4a35548b 1803};