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