]> sigrok.org Git - libsigrok.git/blame - src/input/csv.c
input/csv: move channel creation to after format parsing
[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).
4a35548b
MS
130 */
131
ccff468b
GS
132/*
133 * TODO
134 *
3f1f63f0 135 * - Extend support for analog input data.
43bdef26
GS
136 * - Determine why analog samples of 'double' data type get scrambled
137 * in sigrok-cli screen output. Is analog.encoding->unitsize not
138 * handled properly? A sigrok-cli or libsigrok (src/output) issue?
3f1f63f0
GS
139 * - Reconsider the channel creation after format processing. Current
140 * logic may "bleed" channel names into the analog group when logic
141 * channels' columns follow analog columns (seen with "-,2a,x8").
142 * Trying to sort it out, a naive change used to map logic channels'
143 * data to incorrect bitmap positions. The whole channel numbering
144 * needs reconsideration. Probably it's easiest to first create _all_
145 * logic channels so that they have adjacent numbers starting at 0
146 * (addressing logic bits), then all analog channels (again adjacent)
147 * to simplify the calculation of their index in the sample set as
148 * well as their sdi channel index from the "analog column index".
5a971176
GS
149 * - Optionally get sample rate from timestamp column. Just best-effort
150 * approach, not necessarily reliable. Users can always specify rates.
151 * - Add a test suite for input modules in general, and CSV in specific?
152 * Becomes more important with the multitude of options and their
153 * interaction. Could cover edge cases (BOM presence, line termination
154 * absence, etc) and auto-stuff as well (channel names, channel counts,
155 * samplerates, etc).
ccff468b
GS
156 */
157
43bdef26
GS
158typedef float csv_analog_t; /* 'double' currently is flawed. */
159
4a35548b 160/* Single column formats. */
ad6a2bee 161enum single_col_format {
e53f32d2
GS
162 FORMAT_NONE, /* Ignore this column. */
163 FORMAT_BIN, /* Bin digits for a set of bits (or just one bit). */
164 FORMAT_HEX, /* Hex digits for a set of bits. */
165 FORMAT_OCT, /* Oct digits for a set of bits. */
43bdef26 166 FORMAT_ANALOG, /* Floating point number for an analog channel. */
7e4e65bf 167 FORMAT_TIME, /* Timestamps. */
e53f32d2
GS
168};
169
170static const char *col_format_text[] = {
171 [FORMAT_NONE] = "unknown",
172 [FORMAT_BIN] = "binary",
173 [FORMAT_HEX] = "hexadecimal",
174 [FORMAT_OCT] = "octal",
43bdef26 175 [FORMAT_ANALOG] = "analog",
7e4e65bf 176 [FORMAT_TIME] = "timestamp",
e53f32d2
GS
177};
178
1a920e33
GS
179static const char col_format_char[] = {
180 [FORMAT_NONE] = '?',
181 [FORMAT_BIN] = 'b',
182 [FORMAT_HEX] = 'x',
183 [FORMAT_OCT] = 'o',
43bdef26 184 [FORMAT_ANALOG] = 'a',
7e4e65bf 185 [FORMAT_TIME] = 't',
1a920e33
GS
186};
187
fc3b42e9
GS
188static gboolean format_is_ignore(enum single_col_format fmt)
189{
190 return fmt == FORMAT_NONE;
191}
192
193static gboolean format_is_logic(enum single_col_format fmt)
194{
195 return fmt >= FORMAT_BIN && fmt <= FORMAT_OCT;
196}
197
198static gboolean format_is_analog(enum single_col_format fmt)
199{
200 return fmt == FORMAT_ANALOG;
201}
202
7e4e65bf
GS
203static gboolean format_is_timestamp(enum single_col_format fmt)
204{
205 return fmt == FORMAT_TIME;
206}
207
e53f32d2
GS
208struct column_details {
209 size_t col_nr;
210 enum single_col_format text_format;
211 size_t channel_offset;
212 size_t channel_count;
a267bf45 213 int analog_digits;
05719d75 214 GString **channel_names;
4a35548b
MS
215};
216
217struct context {
41d214f6
BV
218 gboolean started;
219
7e4e65bf 220 /* Current samplerate, optionally determined from input data. */
4a35548b 221 uint64_t samplerate;
7e4e65bf 222 double prev_timestamp;
246aca5f 223 gboolean samplerate_sent;
4a35548b 224
a267bf45 225 /* Number of channels. */
836fac9c 226 size_t logic_channels;
43bdef26 227 size_t analog_channels;
4a35548b 228
836fac9c 229 /* Column delimiter (actually separator), comment leader, EOL sequence. */
4a35548b 230 GString *delimiter;
4a35548b 231 GString *comment;
41d214f6
BV
232 char *termination;
233
1a920e33
GS
234 /* Format specs for input columns, and processing state. */
235 size_t column_seen_count;
236 const char *column_formats;
e53f32d2
GS
237 size_t column_want_count;
238 struct column_details *column_details;
239
4a35548b 240 /* Line number to start processing. */
6433156c 241 size_t start_line;
4a35548b
MS
242
243 /*
244 * Determines if the first line should be treated as header and used for
ba7dd8bb 245 * channel names in multi column mode.
4a35548b 246 */
de8fe3b5
GS
247 gboolean use_header;
248 gboolean header_seen;
4a35548b 249
cd59e6ec
GS
250 size_t sample_unit_size; /**!< Byte count for a single sample. */
251 uint8_t *sample_buffer; /**!< Buffer for a single sample. */
43bdef26 252 csv_analog_t *analog_sample_buffer; /**!< Buffer for one set of analog values. */
4a35548b 253
cd59e6ec
GS
254 uint8_t *datafeed_buffer; /**!< Queue for datafeed submission. */
255 size_t datafeed_buf_size;
256 size_t datafeed_buf_fill;
43bdef26
GS
257 /* "Striped" layout, M samples for N channels each. */
258 csv_analog_t *analog_datafeed_buffer; /**!< Queue for analog datafeed. */
259 size_t analog_datafeed_buf_size;
260 size_t analog_datafeed_buf_fill;
a267bf45 261 int *analog_datafeed_digits;
05719d75 262 GSList **analog_datafeed_channels;
4a35548b 263
4a35548b 264 /* Current line number. */
6433156c 265 size_t line_number;
affaf540
GS
266
267 /* List of previously created sigrok channels. */
268 GSList *prev_sr_channels;
4a35548b
MS
269};
270
626c388a
GS
271/*
272 * Primitive operations to handle sample sets:
273 * - Keep a buffer for datafeed submission, capable of holding many
274 * samples (reduces call overhead, improves throughput).
275 * - Have a "current sample set" pointer reference one position in that
276 * large samples buffer.
277 * - Clear the current sample set before text line inspection, then set
278 * the bits which are found active in the current line of text input.
279 * Phrase the API such that call sites can be kept simple. Advance to
280 * the next sample set between lines, flush the larger buffer as needed
281 * (when it is full, or upon EOF).
282 */
283
43bdef26
GS
284static int flush_samplerate(const struct sr_input *in)
285{
286 struct context *inc;
287 struct sr_datafeed_packet packet;
288 struct sr_datafeed_meta meta;
289 struct sr_config *src;
290
291 inc = in->priv;
292 if (inc->samplerate && !inc->samplerate_sent) {
293 packet.type = SR_DF_META;
294 packet.payload = &meta;
295 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
296 meta.config = g_slist_append(NULL, src);
297 sr_session_send(in->sdi, &packet);
298 g_slist_free(meta.config);
299 sr_config_free(src);
300 inc->samplerate_sent = TRUE;
301 }
302
303 return SR_OK;
304}
305
626c388a
GS
306static void clear_logic_samples(struct context *inc)
307{
43bdef26
GS
308 if (!inc->logic_channels)
309 return;
626c388a
GS
310 inc->sample_buffer = &inc->datafeed_buffer[inc->datafeed_buf_fill];
311 memset(inc->sample_buffer, 0, inc->sample_unit_size);
312}
313
314static void set_logic_level(struct context *inc, size_t ch_idx, int on)
315{
316 size_t byte_idx, bit_idx;
317 uint8_t bit_mask;
318
836fac9c 319 if (ch_idx >= inc->logic_channels)
626c388a
GS
320 return;
321 if (!on)
322 return;
323
324 byte_idx = ch_idx / 8;
325 bit_idx = ch_idx % 8;
326 bit_mask = 1 << bit_idx;
327 inc->sample_buffer[byte_idx] |= bit_mask;
328}
329
330static int flush_logic_samples(const struct sr_input *in)
331{
332 struct context *inc;
333 struct sr_datafeed_packet packet;
334 struct sr_datafeed_logic logic;
335 int rc;
336
337 inc = in->priv;
338 if (!inc->datafeed_buf_fill)
339 return SR_OK;
340
43bdef26
GS
341 rc = flush_samplerate(in);
342 if (rc != SR_OK)
343 return rc;
246aca5f 344
626c388a
GS
345 memset(&packet, 0, sizeof(packet));
346 memset(&logic, 0, sizeof(logic));
347 packet.type = SR_DF_LOGIC;
348 packet.payload = &logic;
349 logic.unitsize = inc->sample_unit_size;
350 logic.length = inc->datafeed_buf_fill;
351 logic.data = inc->datafeed_buffer;
352
353 rc = sr_session_send(in->sdi, &packet);
354 if (rc != SR_OK)
355 return rc;
356
357 inc->datafeed_buf_fill = 0;
358 return SR_OK;
359}
360
361static int queue_logic_samples(const struct sr_input *in)
362{
363 struct context *inc;
364 int rc;
365
366 inc = in->priv;
836fac9c
GS
367 if (!inc->logic_channels)
368 return SR_OK;
626c388a
GS
369
370 inc->datafeed_buf_fill += inc->sample_unit_size;
371 if (inc->datafeed_buf_fill == inc->datafeed_buf_size) {
372 rc = flush_logic_samples(in);
373 if (rc != SR_OK)
374 return rc;
375 }
376 return SR_OK;
377}
378
43bdef26
GS
379static void set_analog_value(struct context *inc, size_t ch_idx, csv_analog_t value);
380
381static void clear_analog_samples(struct context *inc)
382{
383 size_t idx;
384
385 if (!inc->analog_channels)
386 return;
387 inc->analog_sample_buffer = &inc->analog_datafeed_buffer[inc->analog_datafeed_buf_fill];
388 for (idx = 0; idx < inc->analog_channels; idx++)
389 set_analog_value(inc, idx, 0.0);
390}
391
392static void set_analog_value(struct context *inc, size_t ch_idx, csv_analog_t value)
393{
394 if (ch_idx >= inc->analog_channels)
395 return;
396 if (!value)
397 return;
398 inc->analog_sample_buffer[ch_idx * inc->analog_datafeed_buf_size] = value;
399}
400
401static int flush_analog_samples(const struct sr_input *in)
402{
43bdef26
GS
403 struct context *inc;
404 struct sr_datafeed_packet packet;
405 struct sr_datafeed_analog analog;
406 struct sr_analog_encoding encoding;
407 struct sr_analog_meaning meaning;
408 struct sr_analog_spec spec;
409 csv_analog_t *samples;
410 size_t ch_idx;
a267bf45 411 int digits;
43bdef26
GS
412 int rc;
413
414 inc = in->priv;
415 if (!inc->analog_datafeed_buf_fill)
416 return SR_OK;
417
418 rc = flush_samplerate(in);
419 if (rc != SR_OK)
420 return rc;
421
422 samples = inc->analog_datafeed_buffer;
423 for (ch_idx = 0; ch_idx < inc->analog_channels; ch_idx++) {
a267bf45 424 digits = inc->analog_datafeed_digits[ch_idx];
43bdef26
GS
425 sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
426 memset(&packet, 0, sizeof(packet));
427 packet.type = SR_DF_ANALOG;
428 packet.payload = &analog;
429 analog.num_samples = inc->analog_datafeed_buf_fill;
430 analog.data = samples;
431 analog.meaning->channels = inc->analog_datafeed_channels[ch_idx];
432 analog.meaning->mq = 0;
433 analog.meaning->mqflags = 0;
434 analog.meaning->unit = 0;
435 analog.encoding->unitsize = sizeof(samples[0]);
436 analog.encoding->is_signed = TRUE;
437 analog.encoding->is_float = TRUE;
438#ifdef WORDS_BIGENDIAN
439 analog.encoding->is_bigendian = TRUE;
440#else
441 analog.encoding->is_bigendian = FALSE;
442#endif
443 analog.encoding->digits = spec.spec_digits;
444 rc = sr_session_send(in->sdi, &packet);
445 if (rc != SR_OK)
446 return rc;
447 samples += inc->analog_datafeed_buf_size;
448 }
449
450 inc->analog_datafeed_buf_fill = 0;
451 return SR_OK;
452}
453
454static int queue_analog_samples(const struct sr_input *in)
455{
456 struct context *inc;
457 int rc;
458
459 inc = in->priv;
460 if (!inc->analog_channels)
461 return SR_OK;
462
463 inc->analog_datafeed_buf_fill++;
464 if (inc->analog_datafeed_buf_fill == inc->analog_datafeed_buf_size) {
465 rc = flush_analog_samples(in);
466 if (rc != SR_OK)
467 return rc;
468 }
469 return SR_OK;
470}
471
2142a79b
GS
472/* Helpers for "column processing". */
473
474static int split_column_format(const char *spec,
475 size_t *column_count, enum single_col_format *format, size_t *bit_count)
476{
477 size_t count;
478 char *endp, format_char;
479 enum single_col_format format_code;
480
481 if (!spec || !*spec)
482 return SR_ERR_ARG;
483
1a920e33 484 /* Get the (optional, decimal, default 1) column count. Accept '*'. */
2142a79b 485 endp = NULL;
1a920e33 486 if (*spec == '*') {
5ada72fc 487 /* Workaround, strtoul("*") won't always yield expected endp. */
1a920e33
GS
488 count = 0;
489 endp = (char *)&spec[1];
490 } else {
491 count = strtoul(spec, &endp, 10);
492 }
2142a79b
GS
493 if (!endp)
494 return SR_ERR_ARG;
495 if (endp == spec)
496 count = 1;
497 if (column_count)
498 *column_count = count;
499 spec = endp;
500
501 /* Get the (mandatory, single letter) type spec (-/xob/l). */
502 format_char = *spec++;
503 switch (format_char) {
5ada72fc 504 case '-':
2142a79b
GS
505 case '/':
506 format_char = '-';
507 format_code = FORMAT_NONE;
508 break;
509 case 'x':
510 format_code = FORMAT_HEX;
511 break;
512 case 'o':
513 format_code = FORMAT_OCT;
514 break;
515 case 'b':
516 case 'l':
517 format_code = FORMAT_BIN;
518 break;
43bdef26
GS
519 case 'a':
520 format_code = FORMAT_ANALOG;
521 break;
7e4e65bf
GS
522 case 't':
523 format_code = FORMAT_TIME;
524 break;
2142a79b
GS
525 default: /* includes NUL */
526 return SR_ERR_ARG;
527 }
528 if (format)
529 *format = format_code;
530
531 /* Get the (optional, decimal, default 1) bit count. */
532 endp = NULL;
533 count = strtoul(spec, &endp, 10);
534 if (!endp)
535 return SR_ERR_ARG;
536 if (endp == spec)
fc3b42e9
GS
537 count = format_is_analog(format_code) ? 3 : 1;
538 if (format_is_ignore(format_code))
2142a79b
GS
539 count = 0;
540 if (format_char == 'l')
541 count = 1;
542 if (bit_count)
543 *bit_count = count;
544 spec = endp;
545
546 /* Input spec must have been exhausted. */
547 if (*spec)
548 return SR_ERR_ARG;
549
550 return SR_OK;
551}
552
9e7af34e
GS
553static int make_column_details_from_format(const struct sr_input *in,
554 const char *column_format, char **column_texts)
2142a79b 555{
9e7af34e 556 struct context *inc;
2142a79b 557 char **formats, *format;
43bdef26 558 size_t format_count, column_count, logic_count, analog_count;
1a920e33 559 size_t auto_column_count;
43bdef26 560 size_t format_idx, c, b, column_idx, channel_idx, analog_idx;
2142a79b
GS
561 enum single_col_format f;
562 struct column_details *detail;
9e7af34e
GS
563 GString *channel_name;
564 size_t create_idx;
565 char *column;
566 const char *caption;
43bdef26 567 int channel_type, channel_sdi_nr;
05719d75 568 void *channel;
2142a79b
GS
569 int ret;
570
9e7af34e
GS
571 inc = in->priv;
572 inc->column_seen_count = g_strv_length(column_texts);
573
05719d75 574 /* Split the input spec, count involved columns and channels. */
2142a79b
GS
575 formats = g_strsplit(column_format, ",", 0);
576 if (!formats) {
577 sr_err("Cannot parse columns format %s (comma split).", column_format);
578 return SR_ERR_ARG;
579 }
580 format_count = g_strv_length(formats);
581 if (!format_count) {
582 sr_err("Cannot parse columns format %s (field count).", column_format);
583 g_strfreev(formats);
584 return SR_ERR_ARG;
585 }
43bdef26 586 column_count = logic_count = analog_count = 0;
1a920e33 587 auto_column_count = 0;
2142a79b
GS
588 for (format_idx = 0; format_idx < format_count; format_idx++) {
589 format = formats[format_idx];
590 ret = split_column_format(format, &c, &f, &b);
591 sr_dbg("fmt %s -> %zu cols, %s fmt, %zu bits, rc %d", format, c, col_format_text[f], b, ret);
592 if (ret != SR_OK) {
593 sr_err("Cannot parse columns format %s (field split, %s).", column_format, format);
594 g_strfreev(formats);
595 return SR_ERR_ARG;
596 }
1a920e33
GS
597 if (f && !c) {
598 /* User requested "auto-count", must be last format. */
599 if (formats[format_idx + 1]) {
600 sr_err("Auto column count must be last format field.");
601 g_strfreev(formats);
602 return SR_ERR_ARG;
603 }
604 auto_column_count = inc->column_seen_count - column_count;
605 c = auto_column_count;
606 }
2142a79b 607 column_count += c;
fc3b42e9 608 if (format_is_analog(f))
43bdef26 609 analog_count += c;
fc3b42e9 610 else if (format_is_logic(f))
43bdef26 611 logic_count += c * b;
2142a79b 612 }
43bdef26
GS
613 sr_dbg("Column format %s -> %zu columns, %zu logic, %zu analog channels.",
614 column_format, column_count, logic_count, analog_count);
2142a79b 615
05719d75 616 /* Allocate and fill in "column processing" details. */
2142a79b 617 inc->column_want_count = column_count;
9e7af34e
GS
618 if (inc->column_seen_count < inc->column_want_count) {
619 sr_err("Insufficient input text width for desired data amount, got %zu but want %zu columns.",
620 inc->column_seen_count, inc->column_want_count);
621 g_strfreev(formats);
622 return SR_ERR_ARG;
623 }
05719d75
GS
624 inc->logic_channels = logic_count;
625 inc->analog_channels = analog_count;
626 inc->analog_datafeed_digits = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_digits[0]));
627 inc->analog_datafeed_channels = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_channels[0]));
2142a79b 628 inc->column_details = g_malloc0_n(column_count, sizeof(inc->column_details[0]));
43bdef26 629 column_idx = channel_idx = analog_idx = 0;
9e7af34e 630 channel_name = g_string_sized_new(64);
2142a79b 631 for (format_idx = 0; format_idx < format_count; format_idx++) {
9e7af34e 632 /* Process a format field, which can span multiple columns. */
2142a79b
GS
633 format = formats[format_idx];
634 (void)split_column_format(format, &c, &f, &b);
1a920e33
GS
635 if (f && !c)
636 c = auto_column_count;
2142a79b 637 while (c-- > 0) {
9e7af34e 638 /* Fill in a column's processing details. */
2142a79b
GS
639 detail = &inc->column_details[column_idx++];
640 detail->col_nr = column_idx;
641 detail->text_format = f;
fc3b42e9 642 if (format_is_analog(detail->text_format)) {
43bdef26
GS
643 detail->channel_offset = analog_idx;
644 detail->channel_count = 1;
a267bf45 645 detail->analog_digits = b;
43bdef26 646 analog_idx += detail->channel_count;
fc3b42e9 647 } else if (format_is_logic(detail->text_format)) {
2142a79b
GS
648 detail->channel_offset = channel_idx;
649 detail->channel_count = b;
43bdef26 650 channel_idx += detail->channel_count;
fc3b42e9
GS
651 } else if (format_is_ignore(detail->text_format)) {
652 /* EMPTY */
9e7af34e 653 continue;
fc3b42e9
GS
654 } else {
655 /*
656 * Neither logic nor analog data, nor ignore.
657 * Format was noted. No channel creation involved.
658 */
659 continue;
660 }
9e7af34e 661 /*
08eb955a 662 * Pick most appropriate channel names. Optionally
9e7af34e
GS
663 * use text from a header line (when requested by the
664 * user). In the absence of header text, channels are
665 * assigned rather generic names.
666 *
667 * Manipulation of the column's caption (when a header
668 * line is seen) is acceptable, because this header
669 * line won't get processed another time.
670 */
671 column = column_texts[detail->col_nr - 1];
672 if (inc->use_header && column && *column)
673 caption = sr_scpi_unquote_string(column);
674 else
675 caption = NULL;
676 if (!caption || !*caption)
677 caption = NULL;
3f1f63f0 678 /*
05719d75
GS
679 * Collect channel creation details here, but defer
680 * actual creation of the channels such that all
681 * logic channels can get created first and analog
682 * channels only get created afterwards.
3f1f63f0 683 */
05719d75 684 detail->channel_names = g_malloc0(detail->channel_count * sizeof(detail->channel_names[0]));
9e7af34e
GS
685 for (create_idx = 0; create_idx < detail->channel_count; create_idx++) {
686 if (caption && detail->channel_count == 1) {
687 g_string_assign(channel_name, caption);
688 } else if (caption) {
689 g_string_printf(channel_name, "%s[%zu]",
690 caption, create_idx);
691 } else {
692 g_string_printf(channel_name, "%zu",
693 detail->channel_offset + create_idx);
694 }
05719d75 695 detail->channel_names[create_idx] = g_string_new_len(channel_name->str, channel_name->len);
9e7af34e 696 }
2142a79b
GS
697 }
698 }
9e7af34e 699 g_string_free(channel_name, TRUE);
2142a79b
GS
700 g_strfreev(formats);
701
05719d75
GS
702 /* Create channels in strict logic to analog order. */
703 channel_type = SR_CHANNEL_LOGIC;
704 for (column_idx = 0; column_idx < inc->column_want_count; column_idx++) {
705 detail = &inc->column_details[column_idx];
706 if (!format_is_logic(detail->text_format))
707 continue;
708 for (create_idx = 0; create_idx < detail->channel_count; create_idx++) {
709 caption = detail->channel_names[create_idx]->str;
710 channel_sdi_nr = g_slist_length(in->sdi->channels);
711 sr_channel_new(in->sdi, channel_sdi_nr, channel_type, TRUE, caption);
712 }
713 }
714 channel_type = SR_CHANNEL_ANALOG;
715 for (column_idx = 0; column_idx < inc->column_want_count; column_idx++) {
716 detail = &inc->column_details[column_idx];
717 if (!format_is_analog(detail->text_format))
718 continue;
719 caption = detail->channel_names[0]->str;
720 channel_sdi_nr = g_slist_length(in->sdi->channels);
721 channel = sr_channel_new(in->sdi, channel_sdi_nr, channel_type, TRUE, caption);
722 channel_idx = channel_sdi_nr - inc->logic_channels;
723 inc->analog_datafeed_digits[channel_idx] = detail->analog_digits;
724 inc->analog_datafeed_channels[channel_idx] = g_slist_append(NULL, channel);
725 }
726
2142a79b
GS
727 return SR_OK;
728}
729
e53f32d2
GS
730static const struct column_details *lookup_column_details(struct context *inc, size_t nr)
731{
732 if (!inc || !inc->column_details)
733 return NULL;
734 if (!nr || nr > inc->column_want_count)
735 return NULL;
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/**
e53f32d2 759 * @brief 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/**
e53f32d2 774 * @brief 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
GS
883/**
884 * @brief Parse a floating point text into an analog value.
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
GS
932/**
933 * @brief Parse a timestamp text, auto-determine samplerate.
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 */
984 if (inc->samplerate)
985 return SR_OK;
986 ret = sr_atod_ascii(column, &ts);
987 if (ret != SR_OK)
988 ts = 0.0;
989 if (!ts) {
990 sr_warn("Cannot convert timestamp text %s in line %zu (or zero value).",
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);
1015 inc->samplerate = rate;
1016 inc->prev_timestamp = 0.0;
1017
1018 return SR_OK;
1019}
1020
836fac9c
GS
1021/**
1022 * @brief Parse routine which ignores the input text.
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;
1033 return SR_OK;
1034}
1035
1036typedef int (*col_parse_cb)(const char *column, struct context *inc,
1037 const struct column_details *details);
1038
1039static const col_parse_cb col_parse_funcs[] = {
1040 [FORMAT_NONE] = parse_ignore,
1041 [FORMAT_BIN] = parse_logic,
1042 [FORMAT_OCT] = parse_logic,
1043 [FORMAT_HEX] = parse_logic,
43bdef26 1044 [FORMAT_ANALOG] = parse_analog,
7e4e65bf 1045 [FORMAT_TIME] = parse_timestamp,
836fac9c
GS
1046};
1047
cd7c5f96
GS
1048/*
1049 * BEWARE! Implementor's notes. Sync with feature set and default option
1050 * values required during maintenance of the input module implementation.
1051 *
1052 * When applications invoke .format_match() routines, trying automatic
1053 * determination of an input file's format handler, then no options are
1054 * in effect. Because specifying options requires selection of an input
1055 * module to pass the options to, which obsoletes the format-match check.
1056 *
1057 * Which means that we only need to deal with the default format here,
1058 * which happens to be the simple multi-column format without header
1059 * lines or leading garbage. Which means that the check can be rather
1060 * strict, resulting in high levels of confidence upon match, never
1061 * "accidently" winning for unreadable or unsupported-by-default formats.
1062 *
1063 * This .format_match() logic only needs to become more involved when
1064 * default option values change, or when automatic detection of column
1065 * data types improves. Then the supported-by-default types of input
1066 * data must be considered acceptable here in the format-match check
1067 * as well.
1068 *
1069 * Notice that the format check cannot re-use regular processing logic
1070 * when their implementation assumes proper input data and wll generate
1071 * diagnostics for unexpected input data. Failure to match the format is
1072 * non-fatal here, mismatch must remain silent. It's up to applications
1073 * how large a chunk of data gets passed here (start of the file's
1074 * content). But inspection of the first few hundred bytes will usually
1075 * be GoodEnough(TM) for the format-match purpose. Notice that filenames
1076 * need not necessarily be available to the format-match routine.
1077 *
1078 * This implementation errs on the safe side. Users can always select
1079 * the CSV input module when automatic format detection fails.
1080 */
1081static int format_match(GHashTable *metadata, unsigned int *confidence)
1082{
1083 const int match_confidence = 100;
1084 const char *default_extension = ".csv";
1085 const char *line_termination = "\n";
1086 const char *comment_leader = ";";
1087 const char *column_separator = ",";
1088 const char *binary_charset = "01";
1089
1090 const char *fn;
1091 GString *buf;
1092 size_t fn_len;
1093 GString *tmpbuf;
1094 gboolean status;
1095 size_t line_idx, col_idx;
1096 char *rdptr, **lines, *line;
1097 char **cols, *col;
1098
1099 /* Get the application provided input data properties. */
1100 fn = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_FILENAME));
1101 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
1102
1103 /* Filenames are a strong hint. Use then when available. */
1104 if (fn && *fn && (fn_len = strlen(fn)) >= strlen(default_extension)) {
1105 if (strcasecmp(&fn[fn_len - strlen(default_extension)], default_extension) == 0) {
1106 *confidence = 10;
1107 return SR_OK;
1108 }
1109 }
1110
1111 /*
1112 * Check file content for compatibility with the input module's
1113 * default format. Which translates to:
1114 * - Must be at least one text line worth of input data. Ignore
1115 * incomplete lines at the end of the available buffer.
1116 * - Must be LF terminated text lines, optional CR-LF sequence.
1117 * (Drop CR-only for simplicity since that's rare and users
1118 * can override the automatic detection.)
1119 * - Strip comments and skip empty lines.
1120 * - Data lines must be binary input (potentially multiple bits
1121 * per column which then get ignored). Presence of comma is
1122 * optional but then must be followed by another data column.
1123 * - No other content is acceptable, there neither are ignored
1124 * columns nor analog data nor timestamps in the default layout.
1125 * (See the above "sync format match with default options"
1126 * comment though during maintenance!)
1127 * Run the check on a copy to not affect the caller's buffer.
1128 */
1129 if (!buf || !buf->len || !buf->str || !*buf->str)
1130 return SR_ERR;
1131 rdptr = g_strstr_len(buf->str, buf->len, line_termination);
1132 if (!rdptr)
1133 return SR_ERR;
1134 tmpbuf = g_string_new_len(buf->str, rdptr + 1 - buf->str);
1135 tmpbuf->str[tmpbuf->len - 1] = '\0';
1136 status = TRUE;
1137 *confidence = match_confidence;
1138 lines = g_strsplit(tmpbuf->str, line_termination, 0);
1139 for (line_idx = 0; status && (line = lines[line_idx]); line_idx++) {
1140 rdptr = strstr(line, comment_leader);
1141 if (rdptr)
1142 *rdptr = '\0';
1143 line = g_strstrip(line);
1144 if (!line || !*line)
1145 continue;
1146 cols = g_strsplit(line, column_separator, 0);
1147 if (!cols) {
1148 status = FALSE;
1149 break;
1150 }
1151 for (col_idx = 0; status && (col = cols[col_idx]); col_idx++) {
1152 if (strspn(col, binary_charset) != strlen(col)) {
1153 status = FALSE;
1154 break;
1155 }
1156 }
1157 g_strfreev(cols);
1158 }
1159 g_strfreev(lines);
1160 g_string_free(tmpbuf, TRUE);
1161
1162 if (!status)
1163 return SR_ERR;
1164 return SR_OK;
1165}
1166
41d214f6 1167static int init(struct sr_input *in, GHashTable *options)
4a35548b 1168{
41d214f6 1169 struct context *inc;
1a920e33 1170 size_t single_column, first_column, logic_channels;
41d214f6 1171 const char *s;
836fac9c 1172 enum single_col_format format;
1a920e33 1173 char format_char;
4a35548b 1174
836fac9c
GS
1175 in->sdi = g_malloc0(sizeof(*in->sdi));
1176 in->priv = inc = g_malloc0(sizeof(*inc));
4a35548b 1177
72903e9d 1178 single_column = g_variant_get_uint32(g_hash_table_lookup(options, "single_column"));
72903e9d 1179 logic_channels = g_variant_get_uint32(g_hash_table_lookup(options, "logic_channels"));
41d214f6 1180 inc->delimiter = g_string_new(g_variant_get_string(
72903e9d 1181 g_hash_table_lookup(options, "column_separator"), NULL));
836fac9c 1182 if (!inc->delimiter->len) {
72903e9d 1183 sr_err("Column separator cannot be empty.");
41d214f6 1184 return SR_ERR_ARG;
4a35548b 1185 }
72903e9d 1186 s = g_variant_get_string(g_hash_table_lookup(options, "single_format"), NULL);
836fac9c
GS
1187 if (g_ascii_strncasecmp(s, "bin", 3) == 0) {
1188 format = FORMAT_BIN;
1189 } else if (g_ascii_strncasecmp(s, "hex", 3) == 0) {
1190 format = FORMAT_HEX;
1191 } else if (g_ascii_strncasecmp(s, "oct", 3) == 0) {
1192 format = FORMAT_OCT;
41d214f6 1193 } else {
72903e9d 1194 sr_err("Invalid single-column format: '%s'", s);
41d214f6 1195 return SR_ERR_ARG;
4a35548b 1196 }
41d214f6 1197 inc->comment = g_string_new(g_variant_get_string(
72903e9d 1198 g_hash_table_lookup(options, "comment_leader"), NULL));
41d214f6 1199 if (g_string_equal(inc->comment, inc->delimiter)) {
e53f32d2
GS
1200 /*
1201 * Using the same sequence as comment leader and column
72903e9d
GS
1202 * separator won't work. The user probably specified ';'
1203 * as the column separator but did not adjust the comment
e53f32d2
GS
1204 * leader. Try DWIM, drop comment strippin support here.
1205 */
72903e9d 1206 sr_warn("Comment leader and column separator conflict, disabling comment support.");
41d214f6 1207 g_string_truncate(inc->comment, 0);
4a35548b 1208 }
6e8d95a5 1209 inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
72903e9d 1210 first_column = g_variant_get_uint32(g_hash_table_lookup(options, "first_column"));
de8fe3b5 1211 inc->use_header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
72903e9d 1212 inc->start_line = g_variant_get_uint32(g_hash_table_lookup(options, "start_line"));
41d214f6 1213 if (inc->start_line < 1) {
6433156c 1214 sr_err("Invalid start line %zu.", inc->start_line);
41d214f6 1215 return SR_ERR_ARG;
4a35548b
MS
1216 }
1217
e53f32d2 1218 /*
1a920e33
GS
1219 * Scan flexible, to get prefered format specs which describe
1220 * the input file's data formats. As well as some simple specs
1221 * for backwards compatibility and user convenience.
1222 *
1223 * This logic ends up with a copy of the format string, either
1224 * user provided or internally derived. Actual creation of the
1225 * column processing details gets deferred until the first line
1226 * of input data was seen. To support automatic determination of
1227 * e.g. channel counts from column counts.
e53f32d2 1228 */
72903e9d 1229 s = g_variant_get_string(g_hash_table_lookup(options, "column_formats"), NULL);
2142a79b 1230 if (s && *s) {
1a920e33 1231 inc->column_formats = g_strdup(s);
72903e9d 1232 sr_dbg("User specified column_formats: %s.", s);
1a920e33
GS
1233 } else if (single_column && logic_channels) {
1234 format_char = col_format_char[format];
1235 if (single_column == 1) {
1236 inc->column_formats = g_strdup_printf("%c%zu",
1237 format_char, logic_channels);
e53f32d2 1238 } else {
1a920e33
GS
1239 inc->column_formats = g_strdup_printf("%zu-,%c%zu",
1240 single_column - 1,
1241 format_char, logic_channels);
e53f32d2 1242 }
72903e9d 1243 sr_dbg("Backwards compat single_column, col %zu, fmt %s, bits %zu -> %s.",
1a920e33
GS
1244 single_column, col_format_text[format], logic_channels,
1245 inc->column_formats);
1246 } else if (!single_column) {
1247 if (first_column > 1) {
1248 inc->column_formats = g_strdup_printf("%zu-,%zul",
1249 first_column - 1, logic_channels);
1250 } else {
1251 inc->column_formats = g_strdup_printf("%zul",
1252 logic_channels);
1253 }
1254 sr_dbg("Backwards compat multi-column, col %zu, chans %zu -> %s.",
1255 first_column, logic_channels,
1256 inc->column_formats);
e53f32d2 1257 } else {
72903e9d 1258 sr_warn("Unknown or unsupported columns layout spec, assuming simple multi-column mode.");
1a920e33 1259 inc->column_formats = g_strdup("*l");
4a35548b
MS
1260 }
1261
41d214f6
BV
1262 return SR_OK;
1263}
4a35548b 1264
affaf540
GS
1265/*
1266 * Check the channel list for consistency across file re-import. See
1267 * the VCD input module for more details and motivation.
1268 */
1269
1270static void keep_header_for_reread(const struct sr_input *in)
1271{
1272 struct context *inc;
1273
1274 inc = in->priv;
1275 g_slist_free_full(inc->prev_sr_channels, sr_channel_free_cb);
1276 inc->prev_sr_channels = in->sdi->channels;
1277 in->sdi->channels = NULL;
1278}
1279
1280static int check_header_in_reread(const struct sr_input *in)
1281{
1282 struct context *inc;
1283
1284 if (!in)
1285 return FALSE;
1286 inc = in->priv;
1287 if (!inc)
1288 return FALSE;
1289 if (!inc->prev_sr_channels)
1290 return TRUE;
1291
1292 if (sr_channel_lists_differ(inc->prev_sr_channels, in->sdi->channels)) {
1293 sr_err("Channel list change not supported for file re-read.");
1294 return FALSE;
1295 }
1296 g_slist_free_full(in->sdi->channels, sr_channel_free_cb);
1297 in->sdi->channels = inc->prev_sr_channels;
1298 inc->prev_sr_channels = NULL;
1299
1300 return TRUE;
1301}
1302
492dfa90
GS
1303static const char *delim_set = "\r\n";
1304
329733d9 1305static const char *get_line_termination(GString *buf)
41d214f6 1306{
329733d9 1307 const char *term;
4a35548b 1308
41d214f6
BV
1309 term = NULL;
1310 if (g_strstr_len(buf->str, buf->len, "\r\n"))
1311 term = "\r\n";
1312 else if (memchr(buf->str, '\n', buf->len))
1313 term = "\n";
1314 else if (memchr(buf->str, '\r', buf->len))
1315 term = "\r";
4a35548b 1316
41d214f6
BV
1317 return term;
1318}
4a35548b 1319
41d214f6
BV
1320static int initial_parse(const struct sr_input *in, GString *buf)
1321{
1322 struct context *inc;
9e7af34e 1323 size_t num_columns;
3f1f63f0 1324 size_t line_number, line_idx;
41d214f6 1325 int ret;
9e7af34e 1326 char **lines, *line, **columns;
41d214f6
BV
1327
1328 ret = SR_OK;
1329 inc = in->priv;
1330 columns = NULL;
1331
9e7af34e 1332 /* Search for the first line to process (header or data). */
41d214f6 1333 line_number = 0;
ef0b9935
GS
1334 if (inc->termination)
1335 lines = g_strsplit(buf->str, inc->termination, 0);
1336 else
1337 lines = g_strsplit_set(buf->str, delim_set, 0);
e53f32d2 1338 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6
BV
1339 line_number++;
1340 if (inc->start_line > line_number) {
e53f32d2 1341 sr_spew("Line %zu skipped (before start).", line_number);
4a35548b
MS
1342 continue;
1343 }
df0db9fd 1344 if (line[0] == '\0') {
41d214f6
BV
1345 sr_spew("Blank line %zu skipped.", line_number);
1346 continue;
1347 }
df0db9fd
GS
1348 strip_comment(line, inc->comment);
1349 if (line[0] == '\0') {
41d214f6 1350 sr_spew("Comment-only line %zu skipped.", line_number);
4a35548b
MS
1351 continue;
1352 }
1353
41d214f6
BV
1354 /* Reached first proper line. */
1355 break;
1356 }
e53f32d2 1357 if (!line) {
41d214f6 1358 /* Not enough data for a proper line yet. */
60107497 1359 ret = SR_ERR_NA;
41d214f6 1360 goto out;
4a35548b
MS
1361 }
1362
9e7af34e 1363 /* Get the number of columns in the line. */
e53f32d2 1364 columns = split_line(line, inc);
df0db9fd 1365 if (!columns) {
41d214f6
BV
1366 sr_err("Error while parsing line %zu.", line_number);
1367 ret = SR_ERR;
1368 goto out;
4a35548b 1369 }
4a35548b 1370 num_columns = g_strv_length(columns);
4a35548b 1371 if (!num_columns) {
e53f32d2 1372 sr_err("Error while parsing line %zu.", line_number);
41d214f6
BV
1373 ret = SR_ERR;
1374 goto out;
4a35548b 1375 }
e53f32d2
GS
1376 sr_dbg("DIAG Got %zu columns in text line: %s.", num_columns, line);
1377
1a920e33 1378 /*
9e7af34e
GS
1379 * Interpret the user provided column format specs. This might
1380 * involve inspection of the now received input text, to support
1381 * e.g. automatic detection of channel counts in the absence of
1382 * user provided specs. Optionally a header line is used to get
1383 * channels' names.
1384 *
1385 * Check the then created channels for consistency across .reset
1386 * and .receive sequences (file re-load).
1a920e33 1387 */
9e7af34e 1388 ret = make_column_details_from_format(in, inc->column_formats, columns);
1a920e33
GS
1389 if (ret != SR_OK) {
1390 sr_err("Cannot parse columns format using line %zu.", line_number);
1391 goto out;
4a35548b 1392 }
affaf540
GS
1393 if (!check_header_in_reread(in)) {
1394 ret = SR_ERR_DATA;
1395 goto out;
1396 }
4a35548b
MS
1397
1398 /*
9e7af34e 1399 * Allocate buffer memory for datafeed submission of sample data.
cd59e6ec
GS
1400 * Calculate the minimum buffer size to store the set of samples
1401 * of all channels (unit size). Determine a larger buffer size
1402 * for datafeed submission that is a multiple of the unit size.
626c388a
GS
1403 * Allocate the larger buffer, the "sample buffer" will point
1404 * to a location within that large buffer later.
fc3b42e9
GS
1405 *
1406 * TODO Move channel creation here, and just store required
1407 * parameters in the format parser above? Could simplify the
1408 * arrangement that logic and analog channels get created in
1409 * strict sequence in their respective groups.
4a35548b 1410 */
43bdef26
GS
1411 if (inc->logic_channels) {
1412 inc->sample_unit_size = (inc->logic_channels + 7) / 8;
1413 inc->datafeed_buf_size = CHUNK_SIZE;
1414 inc->datafeed_buf_size *= inc->sample_unit_size;
1415 inc->datafeed_buffer = g_malloc(inc->datafeed_buf_size);
1416 if (!inc->datafeed_buffer) {
1417 sr_err("Cannot allocate datafeed send buffer (logic).");
1418 ret = SR_ERR_MALLOC;
1419 goto out;
1420 }
1421 inc->datafeed_buf_fill = 0;
1422 }
1423
1424 if (inc->analog_channels) {
1425 size_t sample_size, sample_count;
1426 sample_size = sizeof(inc->analog_datafeed_buffer[0]);
1427 inc->analog_datafeed_buf_size = CHUNK_SIZE;
1428 inc->analog_datafeed_buf_size /= sample_size;
1429 inc->analog_datafeed_buf_size /= inc->analog_channels;
1430 sample_count = inc->analog_channels * inc->analog_datafeed_buf_size;
1431 inc->analog_datafeed_buffer = g_malloc0(sample_count * sample_size);
1432 if (!inc->analog_datafeed_buffer) {
1433 sr_err("Cannot allocate datafeed send buffer (analog).");
1434 ret = SR_ERR_MALLOC;
1435 goto out;
1436 }
1437 inc->analog_datafeed_buf_fill = 0;
43bdef26 1438 }
4a35548b 1439
41d214f6
BV
1440out:
1441 if (columns)
1442 g_strfreev(columns);
1443 g_strfreev(lines);
4a35548b 1444
41d214f6 1445 return ret;
4a35548b
MS
1446}
1447
4439363a
GS
1448/*
1449 * Gets called from initial_receive(), which runs until the end-of-line
1450 * encoding of the input stream could get determined. Assumes that this
1451 * routine receives enough buffered initial input data to either see the
1452 * BOM when there is one, or that no BOM will follow when a text line
1453 * termination sequence was seen. Silently drops the UTF-8 BOM sequence
1454 * from the input buffer if one was seen. Does not care to protect
1455 * against multiple execution or dropping the BOM multiple times --
1456 * there should be at most one in the input stream.
1457 */
1458static void initial_bom_check(const struct sr_input *in)
1459{
1460 static const char *utf8_bom = "\xef\xbb\xbf";
1461
1462 if (in->buf->len < strlen(utf8_bom))
1463 return;
1464 if (strncmp(in->buf->str, utf8_bom, strlen(utf8_bom)) != 0)
1465 return;
1466 g_string_erase(in->buf, 0, strlen(utf8_bom));
1467}
1468
41d214f6 1469static int initial_receive(const struct sr_input *in)
4a35548b 1470{
41d214f6
BV
1471 struct context *inc;
1472 GString *new_buf;
1473 int len, ret;
329733d9
UH
1474 char *p;
1475 const char *termination;
4a35548b 1476
4439363a
GS
1477 initial_bom_check(in);
1478
41d214f6 1479 inc = in->priv;
4a35548b 1480
df0db9fd
GS
1481 termination = get_line_termination(in->buf);
1482 if (!termination)
41d214f6 1483 /* Don't have a full line yet. */
d0181813 1484 return SR_ERR_NA;
4a35548b 1485
df0db9fd
GS
1486 p = g_strrstr_len(in->buf->str, in->buf->len, termination);
1487 if (!p)
41d214f6 1488 /* Don't have a full line yet. */
d0181813 1489 return SR_ERR_NA;
41d214f6
BV
1490 len = p - in->buf->str - 1;
1491 new_buf = g_string_new_len(in->buf->str, len);
1492 g_string_append_c(new_buf, '\0');
4a35548b 1493
41d214f6
BV
1494 inc->termination = g_strdup(termination);
1495
1496 if (in->buf->str[0] != '\0')
1497 ret = initial_parse(in, new_buf);
1498 else
1499 ret = SR_OK;
1500
1501 g_string_free(new_buf, TRUE);
1502
1503 return ret;
1504}
1505
7f4c3a62 1506static int process_buffer(struct sr_input *in, gboolean is_eof)
41d214f6 1507{
41d214f6
BV
1508 struct context *inc;
1509 gsize num_columns;
e53f32d2 1510 size_t line_idx, col_idx, col_nr;
836fac9c
GS
1511 const struct column_details *details;
1512 col_parse_cb parse_func;
ad6a2bee 1513 int ret;
e53f32d2 1514 char *p, **lines, *line, **columns, *column;
41d214f6 1515
41d214f6 1516 inc = in->priv;
d0181813 1517 if (!inc->started) {
bee2b016 1518 std_session_send_df_header(in->sdi);
d0181813 1519 inc->started = TRUE;
4a35548b
MS
1520 }
1521
4555d3bd
GS
1522 /*
1523 * Consider empty input non-fatal. Keep accumulating input until
1524 * at least one full text line has become available. Grab the
1525 * maximum amount of accumulated data that consists of full text
1526 * lines, and process what has been received so far, leaving not
1527 * yet complete lines for the next invocation.
7f4c3a62
GS
1528 *
1529 * Enforce that all previously buffered data gets processed in
1530 * the "EOF" condition. Do not insist in the presence of the
1531 * termination sequence for the last line (may often be missing
1532 * on Windows). A present termination sequence will just result
1533 * in the "execution of an empty line", and does not harm.
4555d3bd
GS
1534 */
1535 if (!in->buf->len)
1536 return SR_OK;
7f4c3a62
GS
1537 if (is_eof) {
1538 p = in->buf->str + in->buf->len;
1539 } else {
1540 p = g_strrstr_len(in->buf->str, in->buf->len, inc->termination);
1541 if (!p)
1542 return SR_ERR;
1543 *p = '\0';
1544 p += strlen(inc->termination);
1545 }
41d214f6 1546 g_strstrip(in->buf->str);
4a35548b 1547
18078d05 1548 ret = SR_OK;
ef0b9935 1549 lines = g_strsplit(in->buf->str, inc->termination, 0);
e53f32d2 1550 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6 1551 inc->line_number++;
ef0b9935
GS
1552 if (inc->line_number < inc->start_line) {
1553 sr_spew("Line %zu skipped (before start).", inc->line_number);
1554 continue;
1555 }
df0db9fd 1556 if (line[0] == '\0') {
41d214f6 1557 sr_spew("Blank line %zu skipped.", inc->line_number);
4a35548b
MS
1558 continue;
1559 }
1560
1561 /* Remove trailing comment. */
df0db9fd
GS
1562 strip_comment(line, inc->comment);
1563 if (line[0] == '\0') {
41d214f6 1564 sr_spew("Comment-only line %zu skipped.", inc->line_number);
4a35548b
MS
1565 continue;
1566 }
1567
160691b9 1568 /* Skip the header line, its content was used as the channel names. */
de8fe3b5 1569 if (inc->use_header && !inc->header_seen) {
160691b9 1570 sr_spew("Header line %zu skipped.", inc->line_number);
de8fe3b5 1571 inc->header_seen = TRUE;
160691b9
JS
1572 continue;
1573 }
1574
e53f32d2
GS
1575 /* Split the line into columns, check for minimum length. */
1576 columns = split_line(line, inc);
df0db9fd 1577 if (!columns) {
41d214f6 1578 sr_err("Error while parsing line %zu.", inc->line_number);
2355d229 1579 g_strfreev(lines);
4a35548b
MS
1580 return SR_ERR;
1581 }
4a35548b 1582 num_columns = g_strv_length(columns);
e53f32d2
GS
1583 if (num_columns < inc->column_want_count) {
1584 sr_err("Insufficient column count %zu in line %zu.",
1585 num_columns, inc->line_number);
4a35548b 1586 g_strfreev(columns);
2355d229 1587 g_strfreev(lines);
4a35548b
MS
1588 return SR_ERR;
1589 }
1590
836fac9c 1591 /* Have the columns of the current text line processed. */
626c388a 1592 clear_logic_samples(inc);
43bdef26 1593 clear_analog_samples(inc);
e53f32d2
GS
1594 for (col_idx = 0; col_idx < inc->column_want_count; col_idx++) {
1595 column = columns[col_idx];
1596 col_nr = col_idx + 1;
836fac9c
GS
1597 details = lookup_column_details(inc, col_nr);
1598 if (!details || !details->text_format)
1599 continue;
1600 parse_func = col_parse_funcs[details->text_format];
1601 if (!parse_func)
1602 continue;
1603 ret = parse_func(column, inc, details);
e53f32d2
GS
1604 if (ret != SR_OK) {
1605 g_strfreev(columns);
1606 g_strfreev(lines);
1607 return SR_ERR;
1608 }
4a35548b
MS
1609 }
1610
626c388a
GS
1611 /* Send sample data to the session bus (buffered). */
1612 ret = queue_logic_samples(in);
43bdef26 1613 ret += queue_analog_samples(in);
41d214f6 1614 if (ret != SR_OK) {
4a35548b 1615 sr_err("Sending samples failed.");
cd59e6ec 1616 g_strfreev(columns);
2355d229 1617 g_strfreev(lines);
4a35548b
MS
1618 return SR_ERR;
1619 }
cd59e6ec 1620
41d214f6
BV
1621 g_strfreev(columns);
1622 }
1623 g_strfreev(lines);
241c386a 1624 g_string_erase(in->buf, 0, p - in->buf->str);
41d214f6 1625
7066fd46 1626 return ret;
41d214f6
BV
1627}
1628
7066fd46 1629static int receive(struct sr_input *in, GString *buf)
41d214f6
BV
1630{
1631 struct context *inc;
7066fd46
BV
1632 int ret;
1633
1634 g_string_append_len(in->buf, buf->str, buf->len);
41d214f6
BV
1635
1636 inc = in->priv;
1a920e33 1637 if (!inc->column_seen_count) {
df0db9fd
GS
1638 ret = initial_receive(in);
1639 if (ret == SR_ERR_NA)
7066fd46
BV
1640 /* Not enough data yet. */
1641 return SR_OK;
1642 else if (ret != SR_OK)
1643 return SR_ERR;
1644
1645 /* sdi is ready, notify frontend. */
1646 in->sdi_ready = TRUE;
41d214f6 1647 return SR_OK;
7066fd46
BV
1648 }
1649
7f4c3a62 1650 ret = process_buffer(in, FALSE);
7066fd46
BV
1651
1652 return ret;
1653}
1654
1655static int end(struct sr_input *in)
1656{
1657 struct context *inc;
7066fd46 1658 int ret;
41d214f6 1659
7066fd46 1660 if (in->sdi_ready)
7f4c3a62 1661 ret = process_buffer(in, TRUE);
7066fd46
BV
1662 else
1663 ret = SR_OK;
cd59e6ec
GS
1664 if (ret != SR_OK)
1665 return ret;
1666
626c388a 1667 ret = flush_logic_samples(in);
43bdef26 1668 ret += flush_analog_samples(in);
cd59e6ec
GS
1669 if (ret != SR_OK)
1670 return ret;
7066fd46
BV
1671
1672 inc = in->priv;
3be42bc2 1673 if (inc->started)
bee2b016 1674 std_session_send_df_end(in->sdi);
4a35548b 1675
7066fd46
BV
1676 return ret;
1677}
1678
d5cc282f 1679static void cleanup(struct sr_input *in)
7066fd46
BV
1680{
1681 struct context *inc;
1682
affaf540
GS
1683 keep_header_for_reread(in);
1684
7066fd46
BV
1685 inc = in->priv;
1686
b1f83103 1687 g_free(inc->termination);
539188e5 1688 inc->termination = NULL;
cd59e6ec 1689 g_free(inc->datafeed_buffer);
539188e5 1690 inc->datafeed_buffer = NULL;
43bdef26
GS
1691 g_free(inc->analog_datafeed_buffer);
1692 inc->analog_datafeed_buffer = NULL;
4a35548b
MS
1693}
1694
ad93bfb0
SA
1695static int reset(struct sr_input *in)
1696{
1697 struct context *inc = in->priv;
1698
1699 cleanup(in);
1700 inc->started = FALSE;
1701 g_string_truncate(in->buf, 0);
1702
1703 return SR_OK;
1704}
1705
c6aa9870 1706enum option_index {
2142a79b 1707 OPT_COL_FMTS,
c6aa9870 1708 OPT_SINGLE_COL,
72903e9d 1709 OPT_FIRST_COL,
c6aa9870 1710 OPT_NUM_LOGIC,
43e1e23a
GS
1711 OPT_SINGLE_FMT,
1712 OPT_START_LINE,
72903e9d 1713 OPT_HEADER,
43e1e23a
GS
1714 OPT_SAMPLERATE,
1715 OPT_COL_SEP,
72903e9d 1716 OPT_COMMENT,
c6aa9870
GS
1717 OPT_MAX,
1718};
1719
41d214f6 1720static struct sr_option options[] = {
72903e9d
GS
1721 [OPT_COL_FMTS] = {
1722 "column_formats", "Column format specs",
43e1e23a 1723 "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
1724 NULL, NULL,
1725 },
1726 [OPT_SINGLE_COL] = {
1727 "single_column", "Single column",
43e1e23a 1728 "Simple single-column mode, exclusively use text from the specified column (number starting at 1). Obsoleted by 'column_formats=4-,x16'.",
72903e9d
GS
1729 NULL, NULL,
1730 },
1731 [OPT_FIRST_COL] = {
1732 "first_column", "First column",
43e1e23a 1733 "First column with logic data in simple multi-column mode (number starting at 1, default 1). Obsoleted by 'column_formats=4-,*l'.",
72903e9d
GS
1734 NULL, NULL,
1735 },
1736 [OPT_NUM_LOGIC] = {
1737 "logic_channels", "Number of logic channels",
43e1e23a 1738 "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
1739 NULL, NULL,
1740 },
43e1e23a 1741 [OPT_SINGLE_FMT] = {
72903e9d 1742 "single_format", "Data format for simple single-column mode.",
43e1e23a 1743 "The input text number format of simple single-column mode: bin, hex, oct. Obsoleted by 'column_formats=x8'.",
72903e9d
GS
1744 NULL, NULL,
1745 },
43e1e23a 1746 [OPT_START_LINE] = {
72903e9d
GS
1747 "start_line", "Start line",
1748 "The line number at which to start processing input text (default: 1).",
1749 NULL, NULL,
1750 },
1751 [OPT_HEADER] = {
1752 "header", "Get channel names from first line.",
08eb955a 1753 "Use the first processed line's column captions (when available) as channel names. Off by default",
72903e9d
GS
1754 NULL, NULL,
1755 },
43e1e23a 1756 [OPT_SAMPLERATE] = {
72903e9d 1757 "samplerate", "Samplerate (Hz)",
08eb955a 1758 "The input data's sample rate in Hz. No default value.",
72903e9d
GS
1759 NULL, NULL,
1760 },
43e1e23a 1761 [OPT_COL_SEP] = {
72903e9d
GS
1762 "column_separator", "Column separator",
1763 "The sequence which separates text columns. Non-empty text, comma by default.",
1764 NULL, NULL,
1765 },
1766 [OPT_COMMENT] = {
1767 "comment_leader", "Comment leader character",
08eb955a 1768 "The text which starts comments at the end of text lines, semicolon by default.",
72903e9d
GS
1769 NULL, NULL,
1770 },
c6aa9870 1771 [OPT_MAX] = ALL_ZERO,
41d214f6
BV
1772};
1773
2c240774 1774static const struct sr_option *get_options(void)
41d214f6 1775{
31c41782
UH
1776 GSList *l;
1777
41d214f6 1778 if (!options[0].def) {
1a920e33 1779 options[OPT_COL_FMTS].def = g_variant_ref_sink(g_variant_new_string(""));
e53f32d2 1780 options[OPT_SINGLE_COL].def = g_variant_ref_sink(g_variant_new_uint32(0));
72903e9d 1781 options[OPT_FIRST_COL].def = g_variant_ref_sink(g_variant_new_uint32(1));
e53f32d2 1782 options[OPT_NUM_LOGIC].def = g_variant_ref_sink(g_variant_new_uint32(0));
43e1e23a 1783 options[OPT_SINGLE_FMT].def = g_variant_ref_sink(g_variant_new_string("bin"));
31c41782
UH
1784 l = NULL;
1785 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("bin")));
1786 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("hex")));
1787 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct")));
43e1e23a
GS
1788 options[OPT_SINGLE_FMT].values = l;
1789 options[OPT_START_LINE].def = g_variant_ref_sink(g_variant_new_uint32(1));
72903e9d 1790 options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
43e1e23a
GS
1791 options[OPT_SAMPLERATE].def = g_variant_ref_sink(g_variant_new_uint64(0));
1792 options[OPT_COL_SEP].def = g_variant_ref_sink(g_variant_new_string(","));
72903e9d 1793 options[OPT_COMMENT].def = g_variant_ref_sink(g_variant_new_string(";"));
41d214f6
BV
1794 }
1795
1796 return options;
1797}
1798
d4c93774 1799SR_PRIV struct sr_input_module input_csv = {
4a35548b 1800 .id = "csv",
41d214f6
BV
1801 .name = "CSV",
1802 .desc = "Comma-separated values",
c7bc82ff 1803 .exts = (const char*[]){"csv", NULL},
cd7c5f96 1804 .metadata = { SR_INPUT_META_FILENAME, SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
41d214f6 1805 .options = get_options,
cd7c5f96 1806 .format_match = format_match,
4a35548b 1807 .init = init,
41d214f6 1808 .receive = receive,
7066fd46 1809 .end = end,
41d214f6 1810 .cleanup = cleanup,
ad93bfb0 1811 .reset = reset,
4a35548b 1812};