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