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