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