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