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