]> sigrok.org Git - libsigrok.git/blame - src/input/csv.c
input/csv: robustness nits in column format dispatching
[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
23#include <glib.h>
4a35548b
MS
24#include <stdlib.h>
25#include <string.h>
e05f1827 26
c1aae900 27#include <libsigrok/libsigrok.h>
4a35548b 28#include "libsigrok-internal.h"
f6dcb320 29#include "scpi.h" /* String un-quote for channel name from header line. */
4a35548b 30
3544f848 31#define LOG_PREFIX "input/csv"
4a35548b 32
9a4fd01a 33#define CHUNK_SIZE (4 * 1024 * 1024)
cd59e6ec 34
4a35548b
MS
35/*
36 * The CSV input module has the following options:
37 *
72903e9d
GS
38 * column_formats: Specifies the data formats and channel counts for the
39 * input file's text columns. Accepts a comma separated list of tuples
40 * with: an optional column repeat count ('*' as a wildcard meaning
41 * "all remaining columns", only applicable to the last field), a format
42 * specifying character ('x' hexadecimal, 'o' octal, 'b' binary, 'l'
43 * single-bit logic), and an optional bit count (translating to: logic
08eb955a
GS
44 * channels communicated in that column). The 'a' format marks analog
45 * data, an optionally following number is the digits count (resolution).
46 * This "column_formats" option is most versatile, other forms of
fc3b42e9
GS
47 * specifying the column layout only exist for backwards compatibility,
48 * and are rather limited. They exclusively support logic input data in
49 * strictly adjacent columns, with further constraints on column layout
50 * for multi-bit data.
4a35548b 51 *
72903e9d
GS
52 * single_column: Specifies the column number which contains the logic data
53 * for single-column mode. All logic data is taken from several bits
54 * which all are kept within that one column. Only exists for backwards
55 * compatibility, see "column_formats" for more flexibility.
4a35548b 56 *
72903e9d
GS
57 * first_column: Specifies the number of the first column with logic data
58 * in simple multi-column mode. Only exists for backwards compatibility,
59 * see "column_formats" for more flexibility.
4a35548b 60 *
72903e9d
GS
61 * logic_channels: Specifies the number of logic channels. Is required in
62 * simple single-column mode. Is optional in simple multi-column mode
63 * (and defaults to all remaining columns). Only exists for backwards
64 * compatibility, see "column_formats" for more flexibility.
4a35548b 65 *
72903e9d
GS
66 * single_format: Specifies the format of the input text in simple single-
67 * column mode. Available formats are: 'bin' (default), 'hex' and 'oct'.
68 * Simple multi-column mode always uses single-bit data per column.
69 * Only exists for backwards compatibility, see "column_formats" for
70 * more flexibility.
4a35548b 71 *
72903e9d
GS
72 * start_line: Specifies at which line to start processing the input file.
73 * Allows to skip leading lines which neither are header nor data lines.
74 * By default all of the input file gets processed.
4a35548b 75 *
72903e9d
GS
76 * header: Boolean option, controls whether the first processed line is used
77 * to determine channel names. Off by default. Generic channel names are
78 * used in the absence of header line content.
4a35548b 79 *
72903e9d
GS
80 * samplerate: Specifies the samplerate of the input data. Defaults to 0.
81 * User specs take precedence over data which optionally gets derived
82 * from input data.
4a35548b 83 *
72903e9d
GS
84 * column_separator: Specifies the sequence which separates the text file
85 * columns. Cannot be empty. Defaults to comma.
86 *
87 * comment_leader: Specifies the sequence which starts comments that run
88 * up to the end of the current text line. Can be empty to disable
89 * comment support. Defaults to semicolon.
90 *
91 * Typical examples of using these options:
92 * - ... -I csv:column_formats=*l ...
93 * All columns are single-bit logic data. Identical to the previous
94 * multi-column mode (the default when no options were given at all).
95 * - ... -I csv:column_formats=3-,*l ...
96 * Ignore the first three columns, get single-bit logic data from all
97 * remaining lines (multi-column mode with first-column above 1).
98 * - ... -I csv:column_formats=3-,4l,x8 ...
99 * Ignore the first three columns, get single-bit logic data from the
100 * next four columns, then eight-bit data in hex format from the next
101 * column. More columns may follow in the input text but won't get
102 * processed. (Mix of previous multi-column as well as single-column
103 * modes.)
104 * - ... -I csv:column_formats=4x8,b16,5l ...
105 * Get eight-bit data in hex format from the first four columns, then
106 * sixteen-bit data in binary format, then five times single-bit data.
107 * - ... -I csv:single_column=2:single_format=bin:logic_channels=8 ...
108 * Get eight logic bits in binary format from column 2. (Simple
109 * single-column mode, corresponds to the "-,b8" format.)
110 * - ... -I csv:first_column=6:logic_channels=4 ...
111 * Get four single-bit logic channels from columns 6 to 9 respectively.
112 * (Simple multi-column mode, corresponds to the "5-,4b" format.)
113 * - ... -I csv:start_line=20:header=yes:...
114 * Skip the first 19 text lines. Use line 20 to derive channel names.
115 * Data starts at line 21.
08eb955a
GS
116 * - ... -I csv:column_formats=*a6 ...
117 * Each column contains an analog value with six significant digits
118 * after the decimal period.
4a35548b
MS
119 */
120
ccff468b
GS
121/*
122 * TODO
123 *
3f1f63f0 124 * - Extend support for analog input data.
43bdef26
GS
125 * - Determine why analog samples of 'double' data type get scrambled
126 * in sigrok-cli screen output. Is analog.encoding->unitsize not
127 * handled properly? A sigrok-cli or libsigrok (src/output) issue?
3f1f63f0
GS
128 * - Reconsider the channel creation after format processing. Current
129 * logic may "bleed" channel names into the analog group when logic
130 * channels' columns follow analog columns (seen with "-,2a,x8").
131 * Trying to sort it out, a naive change used to map logic channels'
132 * data to incorrect bitmap positions. The whole channel numbering
133 * needs reconsideration. Probably it's easiest to first create _all_
134 * logic channels so that they have adjacent numbers starting at 0
135 * (addressing logic bits), then all analog channels (again adjacent)
136 * to simplify the calculation of their index in the sample set as
137 * well as their sdi channel index from the "analog column index".
5a971176
GS
138 * - Optionally get sample rate from timestamp column. Just best-effort
139 * approach, not necessarily reliable. Users can always specify rates.
140 * - Add a test suite for input modules in general, and CSV in specific?
141 * Becomes more important with the multitude of options and their
142 * interaction. Could cover edge cases (BOM presence, line termination
143 * absence, etc) and auto-stuff as well (channel names, channel counts,
144 * samplerates, etc).
ccff468b
GS
145 */
146
43bdef26
GS
147typedef float csv_analog_t; /* 'double' currently is flawed. */
148
4a35548b 149/* Single column formats. */
ad6a2bee 150enum single_col_format {
e53f32d2
GS
151 FORMAT_NONE, /* Ignore this column. */
152 FORMAT_BIN, /* Bin digits for a set of bits (or just one bit). */
153 FORMAT_HEX, /* Hex digits for a set of bits. */
154 FORMAT_OCT, /* Oct digits for a set of bits. */
43bdef26 155 FORMAT_ANALOG, /* Floating point number for an analog channel. */
e53f32d2
GS
156};
157
158static const char *col_format_text[] = {
159 [FORMAT_NONE] = "unknown",
160 [FORMAT_BIN] = "binary",
161 [FORMAT_HEX] = "hexadecimal",
162 [FORMAT_OCT] = "octal",
43bdef26 163 [FORMAT_ANALOG] = "analog",
e53f32d2
GS
164};
165
1a920e33
GS
166static const char col_format_char[] = {
167 [FORMAT_NONE] = '?',
168 [FORMAT_BIN] = 'b',
169 [FORMAT_HEX] = 'x',
170 [FORMAT_OCT] = 'o',
43bdef26 171 [FORMAT_ANALOG] = 'a',
1a920e33
GS
172};
173
fc3b42e9
GS
174static gboolean format_is_ignore(enum single_col_format fmt)
175{
176 return fmt == FORMAT_NONE;
177}
178
179static gboolean format_is_logic(enum single_col_format fmt)
180{
181 return fmt >= FORMAT_BIN && fmt <= FORMAT_OCT;
182}
183
184static gboolean format_is_analog(enum single_col_format fmt)
185{
186 return fmt == FORMAT_ANALOG;
187}
188
e53f32d2
GS
189struct column_details {
190 size_t col_nr;
191 enum single_col_format text_format;
192 size_t channel_offset;
193 size_t channel_count;
3f1f63f0 194 size_t channel_index;
a267bf45 195 int analog_digits;
4a35548b
MS
196};
197
198struct context {
41d214f6
BV
199 gboolean started;
200
4a35548b
MS
201 /* Current selected samplerate. */
202 uint64_t samplerate;
246aca5f 203 gboolean samplerate_sent;
4a35548b 204
a267bf45 205 /* Number of channels. */
836fac9c 206 size_t logic_channels;
43bdef26 207 size_t analog_channels;
4a35548b 208
836fac9c 209 /* Column delimiter (actually separator), comment leader, EOL sequence. */
4a35548b 210 GString *delimiter;
4a35548b 211 GString *comment;
41d214f6
BV
212 char *termination;
213
1a920e33
GS
214 /* Format specs for input columns, and processing state. */
215 size_t column_seen_count;
216 const char *column_formats;
e53f32d2
GS
217 size_t column_want_count;
218 struct column_details *column_details;
219
4a35548b 220 /* Line number to start processing. */
6433156c 221 size_t start_line;
4a35548b
MS
222
223 /*
224 * Determines if the first line should be treated as header and used for
ba7dd8bb 225 * channel names in multi column mode.
4a35548b 226 */
de8fe3b5
GS
227 gboolean use_header;
228 gboolean header_seen;
4a35548b 229
cd59e6ec
GS
230 size_t sample_unit_size; /**!< Byte count for a single sample. */
231 uint8_t *sample_buffer; /**!< Buffer for a single sample. */
43bdef26 232 csv_analog_t *analog_sample_buffer; /**!< Buffer for one set of analog values. */
4a35548b 233
cd59e6ec
GS
234 uint8_t *datafeed_buffer; /**!< Queue for datafeed submission. */
235 size_t datafeed_buf_size;
236 size_t datafeed_buf_fill;
43bdef26
GS
237 /* "Striped" layout, M samples for N channels each. */
238 csv_analog_t *analog_datafeed_buffer; /**!< Queue for analog datafeed. */
239 size_t analog_datafeed_buf_size;
240 size_t analog_datafeed_buf_fill;
a267bf45
GS
241 GSList **analog_datafeed_channels;
242 int *analog_datafeed_digits;
4a35548b 243
4a35548b 244 /* Current line number. */
6433156c 245 size_t line_number;
affaf540
GS
246
247 /* List of previously created sigrok channels. */
248 GSList *prev_sr_channels;
4a35548b
MS
249};
250
626c388a
GS
251/*
252 * Primitive operations to handle sample sets:
253 * - Keep a buffer for datafeed submission, capable of holding many
254 * samples (reduces call overhead, improves throughput).
255 * - Have a "current sample set" pointer reference one position in that
256 * large samples buffer.
257 * - Clear the current sample set before text line inspection, then set
258 * the bits which are found active in the current line of text input.
259 * Phrase the API such that call sites can be kept simple. Advance to
260 * the next sample set between lines, flush the larger buffer as needed
261 * (when it is full, or upon EOF).
262 */
263
43bdef26
GS
264static int flush_samplerate(const struct sr_input *in)
265{
266 struct context *inc;
267 struct sr_datafeed_packet packet;
268 struct sr_datafeed_meta meta;
269 struct sr_config *src;
270
271 inc = in->priv;
272 if (inc->samplerate && !inc->samplerate_sent) {
273 packet.type = SR_DF_META;
274 packet.payload = &meta;
275 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
276 meta.config = g_slist_append(NULL, src);
277 sr_session_send(in->sdi, &packet);
278 g_slist_free(meta.config);
279 sr_config_free(src);
280 inc->samplerate_sent = TRUE;
281 }
282
283 return SR_OK;
284}
285
626c388a
GS
286static void clear_logic_samples(struct context *inc)
287{
43bdef26
GS
288 if (!inc->logic_channels)
289 return;
626c388a
GS
290 inc->sample_buffer = &inc->datafeed_buffer[inc->datafeed_buf_fill];
291 memset(inc->sample_buffer, 0, inc->sample_unit_size);
292}
293
294static void set_logic_level(struct context *inc, size_t ch_idx, int on)
295{
296 size_t byte_idx, bit_idx;
297 uint8_t bit_mask;
298
836fac9c 299 if (ch_idx >= inc->logic_channels)
626c388a
GS
300 return;
301 if (!on)
302 return;
303
304 byte_idx = ch_idx / 8;
305 bit_idx = ch_idx % 8;
306 bit_mask = 1 << bit_idx;
307 inc->sample_buffer[byte_idx] |= bit_mask;
308}
309
310static int flush_logic_samples(const struct sr_input *in)
311{
312 struct context *inc;
313 struct sr_datafeed_packet packet;
314 struct sr_datafeed_logic logic;
315 int rc;
316
317 inc = in->priv;
318 if (!inc->datafeed_buf_fill)
319 return SR_OK;
320
43bdef26
GS
321 rc = flush_samplerate(in);
322 if (rc != SR_OK)
323 return rc;
246aca5f 324
626c388a
GS
325 memset(&packet, 0, sizeof(packet));
326 memset(&logic, 0, sizeof(logic));
327 packet.type = SR_DF_LOGIC;
328 packet.payload = &logic;
329 logic.unitsize = inc->sample_unit_size;
330 logic.length = inc->datafeed_buf_fill;
331 logic.data = inc->datafeed_buffer;
332
333 rc = sr_session_send(in->sdi, &packet);
334 if (rc != SR_OK)
335 return rc;
336
337 inc->datafeed_buf_fill = 0;
338 return SR_OK;
339}
340
341static int queue_logic_samples(const struct sr_input *in)
342{
343 struct context *inc;
344 int rc;
345
346 inc = in->priv;
836fac9c
GS
347 if (!inc->logic_channels)
348 return SR_OK;
626c388a
GS
349
350 inc->datafeed_buf_fill += inc->sample_unit_size;
351 if (inc->datafeed_buf_fill == inc->datafeed_buf_size) {
352 rc = flush_logic_samples(in);
353 if (rc != SR_OK)
354 return rc;
355 }
356 return SR_OK;
357}
358
43bdef26
GS
359static void set_analog_value(struct context *inc, size_t ch_idx, csv_analog_t value);
360
361static void clear_analog_samples(struct context *inc)
362{
363 size_t idx;
364
365 if (!inc->analog_channels)
366 return;
367 inc->analog_sample_buffer = &inc->analog_datafeed_buffer[inc->analog_datafeed_buf_fill];
368 for (idx = 0; idx < inc->analog_channels; idx++)
369 set_analog_value(inc, idx, 0.0);
370}
371
372static void set_analog_value(struct context *inc, size_t ch_idx, csv_analog_t value)
373{
374 if (ch_idx >= inc->analog_channels)
375 return;
376 if (!value)
377 return;
378 inc->analog_sample_buffer[ch_idx * inc->analog_datafeed_buf_size] = value;
379}
380
381static int flush_analog_samples(const struct sr_input *in)
382{
43bdef26
GS
383 struct context *inc;
384 struct sr_datafeed_packet packet;
385 struct sr_datafeed_analog analog;
386 struct sr_analog_encoding encoding;
387 struct sr_analog_meaning meaning;
388 struct sr_analog_spec spec;
389 csv_analog_t *samples;
390 size_t ch_idx;
a267bf45 391 int digits;
43bdef26
GS
392 int rc;
393
394 inc = in->priv;
395 if (!inc->analog_datafeed_buf_fill)
396 return SR_OK;
397
398 rc = flush_samplerate(in);
399 if (rc != SR_OK)
400 return rc;
401
402 samples = inc->analog_datafeed_buffer;
403 for (ch_idx = 0; ch_idx < inc->analog_channels; ch_idx++) {
a267bf45 404 digits = inc->analog_datafeed_digits[ch_idx];
43bdef26
GS
405 sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
406 memset(&packet, 0, sizeof(packet));
407 packet.type = SR_DF_ANALOG;
408 packet.payload = &analog;
409 analog.num_samples = inc->analog_datafeed_buf_fill;
410 analog.data = samples;
411 analog.meaning->channels = inc->analog_datafeed_channels[ch_idx];
412 analog.meaning->mq = 0;
413 analog.meaning->mqflags = 0;
414 analog.meaning->unit = 0;
415 analog.encoding->unitsize = sizeof(samples[0]);
416 analog.encoding->is_signed = TRUE;
417 analog.encoding->is_float = TRUE;
418#ifdef WORDS_BIGENDIAN
419 analog.encoding->is_bigendian = TRUE;
420#else
421 analog.encoding->is_bigendian = FALSE;
422#endif
423 analog.encoding->digits = spec.spec_digits;
424 rc = sr_session_send(in->sdi, &packet);
425 if (rc != SR_OK)
426 return rc;
427 samples += inc->analog_datafeed_buf_size;
428 }
429
430 inc->analog_datafeed_buf_fill = 0;
431 return SR_OK;
432}
433
434static int queue_analog_samples(const struct sr_input *in)
435{
436 struct context *inc;
437 int rc;
438
439 inc = in->priv;
440 if (!inc->analog_channels)
441 return SR_OK;
442
443 inc->analog_datafeed_buf_fill++;
444 if (inc->analog_datafeed_buf_fill == inc->analog_datafeed_buf_size) {
445 rc = flush_analog_samples(in);
446 if (rc != SR_OK)
447 return rc;
448 }
449 return SR_OK;
450}
451
2142a79b
GS
452/* Helpers for "column processing". */
453
454static int split_column_format(const char *spec,
455 size_t *column_count, enum single_col_format *format, size_t *bit_count)
456{
457 size_t count;
458 char *endp, format_char;
459 enum single_col_format format_code;
460
461 if (!spec || !*spec)
462 return SR_ERR_ARG;
463
1a920e33 464 /* Get the (optional, decimal, default 1) column count. Accept '*'. */
2142a79b 465 endp = NULL;
1a920e33 466 if (*spec == '*') {
5ada72fc 467 /* Workaround, strtoul("*") won't always yield expected endp. */
1a920e33
GS
468 count = 0;
469 endp = (char *)&spec[1];
470 } else {
471 count = strtoul(spec, &endp, 10);
472 }
2142a79b
GS
473 if (!endp)
474 return SR_ERR_ARG;
475 if (endp == spec)
476 count = 1;
477 if (column_count)
478 *column_count = count;
479 spec = endp;
480
481 /* Get the (mandatory, single letter) type spec (-/xob/l). */
482 format_char = *spec++;
483 switch (format_char) {
5ada72fc 484 case '-':
2142a79b
GS
485 case '/':
486 format_char = '-';
487 format_code = FORMAT_NONE;
488 break;
489 case 'x':
490 format_code = FORMAT_HEX;
491 break;
492 case 'o':
493 format_code = FORMAT_OCT;
494 break;
495 case 'b':
496 case 'l':
497 format_code = FORMAT_BIN;
498 break;
43bdef26
GS
499 case 'a':
500 format_code = FORMAT_ANALOG;
501 break;
2142a79b
GS
502 default: /* includes NUL */
503 return SR_ERR_ARG;
504 }
505 if (format)
506 *format = format_code;
507
508 /* Get the (optional, decimal, default 1) bit count. */
509 endp = NULL;
510 count = strtoul(spec, &endp, 10);
511 if (!endp)
512 return SR_ERR_ARG;
513 if (endp == spec)
fc3b42e9
GS
514 count = format_is_analog(format_code) ? 3 : 1;
515 if (format_is_ignore(format_code))
2142a79b
GS
516 count = 0;
517 if (format_char == 'l')
518 count = 1;
519 if (bit_count)
520 *bit_count = count;
521 spec = endp;
522
523 /* Input spec must have been exhausted. */
524 if (*spec)
525 return SR_ERR_ARG;
526
527 return SR_OK;
528}
529
9e7af34e
GS
530static int make_column_details_from_format(const struct sr_input *in,
531 const char *column_format, char **column_texts)
2142a79b 532{
9e7af34e 533 struct context *inc;
2142a79b 534 char **formats, *format;
43bdef26 535 size_t format_count, column_count, logic_count, analog_count;
1a920e33 536 size_t auto_column_count;
43bdef26 537 size_t format_idx, c, b, column_idx, channel_idx, analog_idx;
2142a79b
GS
538 enum single_col_format f;
539 struct column_details *detail;
9e7af34e
GS
540 GString *channel_name;
541 size_t create_idx;
542 char *column;
543 const char *caption;
43bdef26 544 int channel_type, channel_sdi_nr;
2142a79b
GS
545 int ret;
546
9e7af34e
GS
547 inc = in->priv;
548 inc->column_seen_count = g_strv_length(column_texts);
549
2142a79b
GS
550 /* Split the input spec, count involved columns and bits. */
551 formats = g_strsplit(column_format, ",", 0);
552 if (!formats) {
553 sr_err("Cannot parse columns format %s (comma split).", column_format);
554 return SR_ERR_ARG;
555 }
556 format_count = g_strv_length(formats);
557 if (!format_count) {
558 sr_err("Cannot parse columns format %s (field count).", column_format);
559 g_strfreev(formats);
560 return SR_ERR_ARG;
561 }
43bdef26 562 column_count = logic_count = analog_count = 0;
1a920e33 563 auto_column_count = 0;
2142a79b
GS
564 for (format_idx = 0; format_idx < format_count; format_idx++) {
565 format = formats[format_idx];
566 ret = split_column_format(format, &c, &f, &b);
567 sr_dbg("fmt %s -> %zu cols, %s fmt, %zu bits, rc %d", format, c, col_format_text[f], b, ret);
568 if (ret != SR_OK) {
569 sr_err("Cannot parse columns format %s (field split, %s).", column_format, format);
570 g_strfreev(formats);
571 return SR_ERR_ARG;
572 }
1a920e33
GS
573 if (f && !c) {
574 /* User requested "auto-count", must be last format. */
575 if (formats[format_idx + 1]) {
576 sr_err("Auto column count must be last format field.");
577 g_strfreev(formats);
578 return SR_ERR_ARG;
579 }
580 auto_column_count = inc->column_seen_count - column_count;
581 c = auto_column_count;
582 }
2142a79b 583 column_count += c;
fc3b42e9 584 if (format_is_analog(f))
43bdef26 585 analog_count += c;
fc3b42e9 586 else if (format_is_logic(f))
43bdef26 587 logic_count += c * b;
2142a79b 588 }
43bdef26
GS
589 sr_dbg("Column format %s -> %zu columns, %zu logic, %zu analog channels.",
590 column_format, column_count, logic_count, analog_count);
2142a79b 591
9e7af34e 592 /* Allocate and fill in "column processing" details. Create channels. */
2142a79b 593 inc->column_want_count = column_count;
9e7af34e
GS
594 if (inc->column_seen_count < inc->column_want_count) {
595 sr_err("Insufficient input text width for desired data amount, got %zu but want %zu columns.",
596 inc->column_seen_count, inc->column_want_count);
597 g_strfreev(formats);
598 return SR_ERR_ARG;
599 }
2142a79b 600 inc->column_details = g_malloc0_n(column_count, sizeof(inc->column_details[0]));
43bdef26 601 column_idx = channel_idx = analog_idx = 0;
9e7af34e 602 channel_name = g_string_sized_new(64);
2142a79b 603 for (format_idx = 0; format_idx < format_count; format_idx++) {
9e7af34e 604 /* Process a format field, which can span multiple columns. */
2142a79b
GS
605 format = formats[format_idx];
606 (void)split_column_format(format, &c, &f, &b);
1a920e33
GS
607 if (f && !c)
608 c = auto_column_count;
2142a79b 609 while (c-- > 0) {
9e7af34e 610 /* Fill in a column's processing details. */
2142a79b
GS
611 detail = &inc->column_details[column_idx++];
612 detail->col_nr = column_idx;
613 detail->text_format = f;
fc3b42e9 614 if (format_is_analog(detail->text_format)) {
43bdef26
GS
615 detail->channel_offset = analog_idx;
616 detail->channel_count = 1;
a267bf45 617 detail->analog_digits = b;
43bdef26 618 analog_idx += detail->channel_count;
fc3b42e9 619 } else if (format_is_logic(detail->text_format)) {
2142a79b
GS
620 detail->channel_offset = channel_idx;
621 detail->channel_count = b;
43bdef26 622 channel_idx += detail->channel_count;
fc3b42e9
GS
623 } else if (format_is_ignore(detail->text_format)) {
624 /* EMPTY */
9e7af34e 625 continue;
fc3b42e9
GS
626 } else {
627 /*
628 * Neither logic nor analog data, nor ignore.
629 * Format was noted. No channel creation involved.
630 */
631 continue;
632 }
9e7af34e 633 /*
08eb955a 634 * Pick most appropriate channel names. Optionally
9e7af34e
GS
635 * use text from a header line (when requested by the
636 * user). In the absence of header text, channels are
637 * assigned rather generic names.
638 *
639 * Manipulation of the column's caption (when a header
640 * line is seen) is acceptable, because this header
641 * line won't get processed another time.
642 */
643 column = column_texts[detail->col_nr - 1];
644 if (inc->use_header && column && *column)
645 caption = sr_scpi_unquote_string(column);
646 else
647 caption = NULL;
648 if (!caption || !*caption)
649 caption = NULL;
3f1f63f0
GS
650 /*
651 * TODO Need we first create _all_ logic channels,
fc3b42e9
GS
652 * before creating analog channels? Just store the
653 * parameters here (index, type, name) and have the
654 * creation sequence done outside of the format
655 * spec parse loop.
3f1f63f0 656 */
9e7af34e
GS
657 for (create_idx = 0; create_idx < detail->channel_count; create_idx++) {
658 if (caption && detail->channel_count == 1) {
659 g_string_assign(channel_name, caption);
660 } else if (caption) {
661 g_string_printf(channel_name, "%s[%zu]",
662 caption, create_idx);
663 } else {
664 g_string_printf(channel_name, "%zu",
665 detail->channel_offset + create_idx);
666 }
fc3b42e9 667 if (format_is_analog(detail->text_format)) {
43bdef26
GS
668 channel_sdi_nr = logic_count + detail->channel_offset + create_idx;
669 channel_type = SR_CHANNEL_ANALOG;
3f1f63f0 670 detail->channel_index = g_slist_length(in->sdi->channels);
fc3b42e9 671 } else if (format_is_logic(detail->text_format)) {
43bdef26
GS
672 channel_sdi_nr = detail->channel_offset + create_idx;
673 channel_type = SR_CHANNEL_LOGIC;
fc3b42e9
GS
674 } else {
675 continue;
43bdef26
GS
676 }
677 sr_channel_new(in->sdi, channel_sdi_nr,
678 channel_type, TRUE, channel_name->str);
9e7af34e 679 }
2142a79b
GS
680 }
681 }
682 inc->logic_channels = channel_idx;
43bdef26 683 inc->analog_channels = analog_idx;
9e7af34e 684 g_string_free(channel_name, TRUE);
2142a79b
GS
685 g_strfreev(formats);
686
687 return SR_OK;
688}
689
e53f32d2
GS
690static const struct column_details *lookup_column_details(struct context *inc, size_t nr)
691{
692 if (!inc || !inc->column_details)
693 return NULL;
694 if (!nr || nr > inc->column_want_count)
695 return NULL;
696 return &inc->column_details[nr - 1];
697}
698
19267272
GS
699/*
700 * Primitive operations for text input: Strip comments off text lines.
701 * Split text lines into columns. Process input text for individual
702 * columns.
703 */
704
41d214f6 705static void strip_comment(char *buf, const GString *prefix)
4a35548b
MS
706{
707 char *ptr;
708
709 if (!prefix->len)
710 return;
711
b2c4dde2 712 if ((ptr = strstr(buf, prefix->str))) {
41d214f6 713 *ptr = '\0';
b2c4dde2
GS
714 g_strstrip(buf);
715 }
4a35548b
MS
716}
717
19267272 718/**
e53f32d2 719 * @brief Splits a text line into a set of columns.
19267272 720 *
e53f32d2 721 * @param[in] buf The input text line to split.
19267272
GS
722 * @param[in] inc The input module's context.
723 *
e53f32d2 724 * @returns An array of strings, representing the columns' text.
19267272 725 *
e53f32d2 726 * This routine splits a text line on previously determined separators.
19267272 727 */
e53f32d2 728static char **split_line(char *buf, struct context *inc)
4a35548b 729{
e53f32d2 730 return g_strsplit(buf, inc->delimiter->str, 0);
4a35548b
MS
731}
732
19267272 733/**
e53f32d2 734 * @brief Parse a multi-bit field into several logic channels.
19267272 735 *
e53f32d2 736 * @param[in] column The input text, a run of bin/hex/oct digits.
19267272 737 * @param[in] inc The input module's context.
836fac9c 738 * @param[in] details The column processing details.
19267272
GS
739 *
740 * @retval SR_OK Success.
741 * @retval SR_ERR Invalid input data (empty, or format error).
742 *
743 * This routine modifies the logic levels in the current sample set,
e53f32d2 744 * based on the text input and a user provided format spec.
19267272 745 */
836fac9c
GS
746static int parse_logic(const char *column, struct context *inc,
747 const struct column_details *details)
4a35548b 748{
e53f32d2
GS
749 size_t length, ch_rem, ch_idx, ch_inc;
750 const char *rdptr;
4a35548b 751 char c;
e53f32d2
GS
752 gboolean valid;
753 const char *type_text;
754 uint8_t bits;
755
e53f32d2
GS
756 /*
757 * Prepare to read the digits from the text end towards the start.
758 * A digit corresponds to a variable number of channels (depending
759 * on the value's radix). Prepare the mapping of text digits to
760 * (a number of) logic channels.
761 */
762 length = strlen(column);
4a35548b 763 if (!length) {
836fac9c 764 sr_err("Column %zu in line %zu is empty.", details->col_nr,
41d214f6 765 inc->line_number);
4a35548b
MS
766 return SR_ERR;
767 }
e53f32d2 768 rdptr = &column[length];
836fac9c
GS
769 ch_idx = details->channel_offset;
770 ch_rem = details->channel_count;
4a35548b 771
e53f32d2
GS
772 /*
773 * Get another digit and derive up to four logic channels' state from
774 * it. Make sure to not process more bits than the column has channels
775 * associated with it.
776 */
777 while (rdptr > column && ch_rem) {
778 /* Check for valid digits according to the input radix. */
779 c = *(--rdptr);
836fac9c 780 switch (details->text_format) {
e53f32d2
GS
781 case FORMAT_BIN:
782 valid = g_ascii_isxdigit(c) && c < '2';
783 ch_inc = 1;
784 break;
785 case FORMAT_OCT:
786 valid = g_ascii_isxdigit(c) && c < '8';
787 ch_inc = 3;
788 break;
789 case FORMAT_HEX:
790 valid = g_ascii_isxdigit(c);
791 ch_inc = 4;
792 break;
793 default:
794 valid = FALSE;
795 break;
4a35548b 796 }
e53f32d2 797 if (!valid) {
836fac9c 798 type_text = col_format_text[details->text_format];
e53f32d2 799 sr_err("Invalid text '%s' in %s type column %zu in line %zu.",
836fac9c 800 column, type_text, details->col_nr, inc->line_number);
4a35548b 801 return SR_ERR;
e53f32d2
GS
802 }
803 /* Use the digit's bits for logic channels' data. */
804 bits = g_ascii_xdigit_value(c);
836fac9c 805 switch (details->text_format) {
e53f32d2
GS
806 case FORMAT_HEX:
807 if (ch_rem >= 4) {
808 ch_rem--;
809 set_logic_level(inc, ch_idx + 3, bits & (1 << 3));
810 }
811 /* FALLTHROUGH */
812 case FORMAT_OCT:
813 if (ch_rem >= 3) {
814 ch_rem--;
815 set_logic_level(inc, ch_idx + 2, bits & (1 << 2));
816 }
817 if (ch_rem >= 2) {
818 ch_rem--;
819 set_logic_level(inc, ch_idx + 1, bits & (1 << 1));
820 }
821 /* FALLTHROUGH */
822 case FORMAT_BIN:
823 ch_rem--;
824 set_logic_level(inc, ch_idx + 0, bits & (1 << 0));
825 break;
fc3b42e9 826 default:
836fac9c
GS
827 /* ShouldNotHappen(TM), but silences compiler warning. */
828 return SR_ERR;
4a35548b 829 }
e53f32d2 830 ch_idx += ch_inc;
4a35548b 831 }
e53f32d2
GS
832 /*
833 * TODO Determine whether the availability of extra input data
834 * for unhandled logic channels is worth warning here. In this
835 * implementation users are in control, and can have the more
836 * significant bits ignored (which can be considered a feature
837 * and not really a limitation).
838 */
4a35548b
MS
839
840 return SR_OK;
841}
842
43bdef26
GS
843/**
844 * @brief Parse a floating point text into an analog value.
845 *
846 * @param[in] column The input text, a floating point number.
847 * @param[in] inc The input module's context.
848 * @param[in] details The column processing details.
849 *
850 * @retval SR_OK Success.
851 * @retval SR_ERR Invalid input data (empty, or format error).
852 *
853 * This routine modifies the analog values in the current sample set,
854 * based on the text input and a user provided format spec.
855 */
856static int parse_analog(const char *column, struct context *inc,
857 const struct column_details *details)
858{
859 size_t length;
860 double dvalue; float fvalue;
861 csv_analog_t value;
862 int ret;
863
fc3b42e9 864 if (!format_is_analog(details->text_format))
43bdef26
GS
865 return SR_ERR_BUG;
866
867 length = strlen(column);
868 if (!length) {
869 sr_err("Column %zu in line %zu is empty.", details->col_nr,
870 inc->line_number);
871 return SR_ERR;
872 }
873 if (sizeof(value) == sizeof(double)) {
874 ret = sr_atod_ascii(column, &dvalue);
875 value = dvalue;
876 } else if (sizeof(value) == sizeof(float)) {
877 ret = sr_atof_ascii(column, &fvalue);
878 value = fvalue;
879 } else {
880 ret = SR_ERR_BUG;
881 }
882 if (ret != SR_OK) {
883 sr_err("Cannot parse analog text %s in column %zu in line %zu.",
884 column, details->col_nr, inc->line_number);
885 return SR_ERR_DATA;
886 }
887 set_analog_value(inc, details->channel_offset, value);
888
889 return SR_OK;
890}
891
836fac9c
GS
892/**
893 * @brief Parse routine which ignores the input text.
894 *
895 * This routine exists to unify dispatch code paths, mapping input file
896 * columns' data types to their respective parse routines.
897 */
898static int parse_ignore(const char *column, struct context *inc,
899 const struct column_details *details)
900{
901 (void)column;
902 (void)inc;
903 (void)details;
904 return SR_OK;
905}
906
907typedef int (*col_parse_cb)(const char *column, struct context *inc,
908 const struct column_details *details);
909
910static const col_parse_cb col_parse_funcs[] = {
911 [FORMAT_NONE] = parse_ignore,
912 [FORMAT_BIN] = parse_logic,
913 [FORMAT_OCT] = parse_logic,
914 [FORMAT_HEX] = parse_logic,
43bdef26 915 [FORMAT_ANALOG] = parse_analog,
836fac9c
GS
916};
917
41d214f6 918static int init(struct sr_input *in, GHashTable *options)
4a35548b 919{
41d214f6 920 struct context *inc;
1a920e33 921 size_t single_column, first_column, logic_channels;
41d214f6 922 const char *s;
836fac9c 923 enum single_col_format format;
1a920e33 924 char format_char;
4a35548b 925
836fac9c
GS
926 in->sdi = g_malloc0(sizeof(*in->sdi));
927 in->priv = inc = g_malloc0(sizeof(*inc));
4a35548b 928
72903e9d 929 single_column = g_variant_get_uint32(g_hash_table_lookup(options, "single_column"));
72903e9d 930 logic_channels = g_variant_get_uint32(g_hash_table_lookup(options, "logic_channels"));
41d214f6 931 inc->delimiter = g_string_new(g_variant_get_string(
72903e9d 932 g_hash_table_lookup(options, "column_separator"), NULL));
836fac9c 933 if (!inc->delimiter->len) {
72903e9d 934 sr_err("Column separator cannot be empty.");
41d214f6 935 return SR_ERR_ARG;
4a35548b 936 }
72903e9d 937 s = g_variant_get_string(g_hash_table_lookup(options, "single_format"), NULL);
836fac9c
GS
938 if (g_ascii_strncasecmp(s, "bin", 3) == 0) {
939 format = FORMAT_BIN;
940 } else if (g_ascii_strncasecmp(s, "hex", 3) == 0) {
941 format = FORMAT_HEX;
942 } else if (g_ascii_strncasecmp(s, "oct", 3) == 0) {
943 format = FORMAT_OCT;
41d214f6 944 } else {
72903e9d 945 sr_err("Invalid single-column format: '%s'", s);
41d214f6 946 return SR_ERR_ARG;
4a35548b 947 }
41d214f6 948 inc->comment = g_string_new(g_variant_get_string(
72903e9d 949 g_hash_table_lookup(options, "comment_leader"), NULL));
41d214f6 950 if (g_string_equal(inc->comment, inc->delimiter)) {
e53f32d2
GS
951 /*
952 * Using the same sequence as comment leader and column
72903e9d
GS
953 * separator won't work. The user probably specified ';'
954 * as the column separator but did not adjust the comment
e53f32d2
GS
955 * leader. Try DWIM, drop comment strippin support here.
956 */
72903e9d 957 sr_warn("Comment leader and column separator conflict, disabling comment support.");
41d214f6 958 g_string_truncate(inc->comment, 0);
4a35548b 959 }
6e8d95a5 960 inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
72903e9d 961 first_column = g_variant_get_uint32(g_hash_table_lookup(options, "first_column"));
de8fe3b5 962 inc->use_header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
72903e9d 963 inc->start_line = g_variant_get_uint32(g_hash_table_lookup(options, "start_line"));
41d214f6 964 if (inc->start_line < 1) {
6433156c 965 sr_err("Invalid start line %zu.", inc->start_line);
41d214f6 966 return SR_ERR_ARG;
4a35548b
MS
967 }
968
e53f32d2 969 /*
1a920e33
GS
970 * Scan flexible, to get prefered format specs which describe
971 * the input file's data formats. As well as some simple specs
972 * for backwards compatibility and user convenience.
973 *
974 * This logic ends up with a copy of the format string, either
975 * user provided or internally derived. Actual creation of the
976 * column processing details gets deferred until the first line
977 * of input data was seen. To support automatic determination of
978 * e.g. channel counts from column counts.
e53f32d2 979 */
72903e9d 980 s = g_variant_get_string(g_hash_table_lookup(options, "column_formats"), NULL);
2142a79b 981 if (s && *s) {
1a920e33 982 inc->column_formats = g_strdup(s);
72903e9d 983 sr_dbg("User specified column_formats: %s.", s);
1a920e33
GS
984 } else if (single_column && logic_channels) {
985 format_char = col_format_char[format];
986 if (single_column == 1) {
987 inc->column_formats = g_strdup_printf("%c%zu",
988 format_char, logic_channels);
e53f32d2 989 } else {
1a920e33
GS
990 inc->column_formats = g_strdup_printf("%zu-,%c%zu",
991 single_column - 1,
992 format_char, logic_channels);
e53f32d2 993 }
72903e9d 994 sr_dbg("Backwards compat single_column, col %zu, fmt %s, bits %zu -> %s.",
1a920e33
GS
995 single_column, col_format_text[format], logic_channels,
996 inc->column_formats);
997 } else if (!single_column) {
998 if (first_column > 1) {
999 inc->column_formats = g_strdup_printf("%zu-,%zul",
1000 first_column - 1, logic_channels);
1001 } else {
1002 inc->column_formats = g_strdup_printf("%zul",
1003 logic_channels);
1004 }
1005 sr_dbg("Backwards compat multi-column, col %zu, chans %zu -> %s.",
1006 first_column, logic_channels,
1007 inc->column_formats);
e53f32d2 1008 } else {
72903e9d 1009 sr_warn("Unknown or unsupported columns layout spec, assuming simple multi-column mode.");
1a920e33 1010 inc->column_formats = g_strdup("*l");
4a35548b
MS
1011 }
1012
41d214f6
BV
1013 return SR_OK;
1014}
4a35548b 1015
affaf540
GS
1016/*
1017 * Check the channel list for consistency across file re-import. See
1018 * the VCD input module for more details and motivation.
1019 */
1020
1021static void keep_header_for_reread(const struct sr_input *in)
1022{
1023 struct context *inc;
1024
1025 inc = in->priv;
1026 g_slist_free_full(inc->prev_sr_channels, sr_channel_free_cb);
1027 inc->prev_sr_channels = in->sdi->channels;
1028 in->sdi->channels = NULL;
1029}
1030
1031static int check_header_in_reread(const struct sr_input *in)
1032{
1033 struct context *inc;
1034
1035 if (!in)
1036 return FALSE;
1037 inc = in->priv;
1038 if (!inc)
1039 return FALSE;
1040 if (!inc->prev_sr_channels)
1041 return TRUE;
1042
1043 if (sr_channel_lists_differ(inc->prev_sr_channels, in->sdi->channels)) {
1044 sr_err("Channel list change not supported for file re-read.");
1045 return FALSE;
1046 }
1047 g_slist_free_full(in->sdi->channels, sr_channel_free_cb);
1048 in->sdi->channels = inc->prev_sr_channels;
1049 inc->prev_sr_channels = NULL;
1050
1051 return TRUE;
1052}
1053
492dfa90
GS
1054static const char *delim_set = "\r\n";
1055
329733d9 1056static const char *get_line_termination(GString *buf)
41d214f6 1057{
329733d9 1058 const char *term;
4a35548b 1059
41d214f6
BV
1060 term = NULL;
1061 if (g_strstr_len(buf->str, buf->len, "\r\n"))
1062 term = "\r\n";
1063 else if (memchr(buf->str, '\n', buf->len))
1064 term = "\n";
1065 else if (memchr(buf->str, '\r', buf->len))
1066 term = "\r";
4a35548b 1067
41d214f6
BV
1068 return term;
1069}
4a35548b 1070
41d214f6
BV
1071static int initial_parse(const struct sr_input *in, GString *buf)
1072{
1073 struct context *inc;
9e7af34e 1074 size_t num_columns;
3f1f63f0 1075 size_t line_number, line_idx;
41d214f6 1076 int ret;
9e7af34e 1077 char **lines, *line, **columns;
41d214f6
BV
1078
1079 ret = SR_OK;
1080 inc = in->priv;
1081 columns = NULL;
1082
9e7af34e 1083 /* Search for the first line to process (header or data). */
41d214f6 1084 line_number = 0;
ef0b9935
GS
1085 if (inc->termination)
1086 lines = g_strsplit(buf->str, inc->termination, 0);
1087 else
1088 lines = g_strsplit_set(buf->str, delim_set, 0);
e53f32d2 1089 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6
BV
1090 line_number++;
1091 if (inc->start_line > line_number) {
e53f32d2 1092 sr_spew("Line %zu skipped (before start).", line_number);
4a35548b
MS
1093 continue;
1094 }
df0db9fd 1095 if (line[0] == '\0') {
41d214f6
BV
1096 sr_spew("Blank line %zu skipped.", line_number);
1097 continue;
1098 }
df0db9fd
GS
1099 strip_comment(line, inc->comment);
1100 if (line[0] == '\0') {
41d214f6 1101 sr_spew("Comment-only line %zu skipped.", line_number);
4a35548b
MS
1102 continue;
1103 }
1104
41d214f6
BV
1105 /* Reached first proper line. */
1106 break;
1107 }
e53f32d2 1108 if (!line) {
41d214f6 1109 /* Not enough data for a proper line yet. */
60107497 1110 ret = SR_ERR_NA;
41d214f6 1111 goto out;
4a35548b
MS
1112 }
1113
9e7af34e 1114 /* Get the number of columns in the line. */
e53f32d2 1115 columns = split_line(line, inc);
df0db9fd 1116 if (!columns) {
41d214f6
BV
1117 sr_err("Error while parsing line %zu.", line_number);
1118 ret = SR_ERR;
1119 goto out;
4a35548b 1120 }
4a35548b 1121 num_columns = g_strv_length(columns);
4a35548b 1122 if (!num_columns) {
e53f32d2 1123 sr_err("Error while parsing line %zu.", line_number);
41d214f6
BV
1124 ret = SR_ERR;
1125 goto out;
4a35548b 1126 }
e53f32d2
GS
1127 sr_dbg("DIAG Got %zu columns in text line: %s.", num_columns, line);
1128
1a920e33 1129 /*
9e7af34e
GS
1130 * Interpret the user provided column format specs. This might
1131 * involve inspection of the now received input text, to support
1132 * e.g. automatic detection of channel counts in the absence of
1133 * user provided specs. Optionally a header line is used to get
1134 * channels' names.
1135 *
1136 * Check the then created channels for consistency across .reset
1137 * and .receive sequences (file re-load).
1a920e33 1138 */
9e7af34e 1139 ret = make_column_details_from_format(in, inc->column_formats, columns);
1a920e33
GS
1140 if (ret != SR_OK) {
1141 sr_err("Cannot parse columns format using line %zu.", line_number);
1142 goto out;
4a35548b 1143 }
affaf540
GS
1144 if (!check_header_in_reread(in)) {
1145 ret = SR_ERR_DATA;
1146 goto out;
1147 }
4a35548b
MS
1148
1149 /*
9e7af34e 1150 * Allocate buffer memory for datafeed submission of sample data.
cd59e6ec
GS
1151 * Calculate the minimum buffer size to store the set of samples
1152 * of all channels (unit size). Determine a larger buffer size
1153 * for datafeed submission that is a multiple of the unit size.
626c388a
GS
1154 * Allocate the larger buffer, the "sample buffer" will point
1155 * to a location within that large buffer later.
fc3b42e9
GS
1156 *
1157 * TODO Move channel creation here, and just store required
1158 * parameters in the format parser above? Could simplify the
1159 * arrangement that logic and analog channels get created in
1160 * strict sequence in their respective groups.
4a35548b 1161 */
43bdef26
GS
1162 if (inc->logic_channels) {
1163 inc->sample_unit_size = (inc->logic_channels + 7) / 8;
1164 inc->datafeed_buf_size = CHUNK_SIZE;
1165 inc->datafeed_buf_size *= inc->sample_unit_size;
1166 inc->datafeed_buffer = g_malloc(inc->datafeed_buf_size);
1167 if (!inc->datafeed_buffer) {
1168 sr_err("Cannot allocate datafeed send buffer (logic).");
1169 ret = SR_ERR_MALLOC;
1170 goto out;
1171 }
1172 inc->datafeed_buf_fill = 0;
1173 }
1174
1175 if (inc->analog_channels) {
1176 size_t sample_size, sample_count;
a267bf45 1177 size_t detail_idx;
3f1f63f0 1178 struct column_details *detail;
a267bf45 1179 int *digits_item;
3f1f63f0 1180 void *channel;
43bdef26
GS
1181 sample_size = sizeof(inc->analog_datafeed_buffer[0]);
1182 inc->analog_datafeed_buf_size = CHUNK_SIZE;
1183 inc->analog_datafeed_buf_size /= sample_size;
1184 inc->analog_datafeed_buf_size /= inc->analog_channels;
1185 sample_count = inc->analog_channels * inc->analog_datafeed_buf_size;
1186 inc->analog_datafeed_buffer = g_malloc0(sample_count * sample_size);
1187 if (!inc->analog_datafeed_buffer) {
1188 sr_err("Cannot allocate datafeed send buffer (analog).");
1189 ret = SR_ERR_MALLOC;
1190 goto out;
1191 }
1192 inc->analog_datafeed_buf_fill = 0;
3f1f63f0 1193 inc->analog_datafeed_channels = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_channels[0]));
a267bf45
GS
1194 inc->analog_datafeed_digits = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_digits[0]));
1195 digits_item = inc->analog_datafeed_digits;
1196 for (detail_idx = 0; detail_idx < inc->column_want_count; detail_idx++) {
3f1f63f0 1197 detail = &inc->column_details[detail_idx];
fc3b42e9 1198 if (!format_is_analog(detail->text_format))
a267bf45 1199 continue;
3f1f63f0
GS
1200 channel = g_slist_nth_data(in->sdi->channels, detail->channel_index);
1201 inc->analog_datafeed_channels[detail->channel_offset] = g_slist_append(NULL, channel);
1202 *digits_item++ = detail->analog_digits;
a267bf45 1203 }
43bdef26 1204 }
4a35548b 1205
41d214f6
BV
1206out:
1207 if (columns)
1208 g_strfreev(columns);
1209 g_strfreev(lines);
4a35548b 1210
41d214f6 1211 return ret;
4a35548b
MS
1212}
1213
4439363a
GS
1214/*
1215 * Gets called from initial_receive(), which runs until the end-of-line
1216 * encoding of the input stream could get determined. Assumes that this
1217 * routine receives enough buffered initial input data to either see the
1218 * BOM when there is one, or that no BOM will follow when a text line
1219 * termination sequence was seen. Silently drops the UTF-8 BOM sequence
1220 * from the input buffer if one was seen. Does not care to protect
1221 * against multiple execution or dropping the BOM multiple times --
1222 * there should be at most one in the input stream.
1223 */
1224static void initial_bom_check(const struct sr_input *in)
1225{
1226 static const char *utf8_bom = "\xef\xbb\xbf";
1227
1228 if (in->buf->len < strlen(utf8_bom))
1229 return;
1230 if (strncmp(in->buf->str, utf8_bom, strlen(utf8_bom)) != 0)
1231 return;
1232 g_string_erase(in->buf, 0, strlen(utf8_bom));
1233}
1234
41d214f6 1235static int initial_receive(const struct sr_input *in)
4a35548b 1236{
41d214f6
BV
1237 struct context *inc;
1238 GString *new_buf;
1239 int len, ret;
329733d9
UH
1240 char *p;
1241 const char *termination;
4a35548b 1242
4439363a
GS
1243 initial_bom_check(in);
1244
41d214f6 1245 inc = in->priv;
4a35548b 1246
df0db9fd
GS
1247 termination = get_line_termination(in->buf);
1248 if (!termination)
41d214f6 1249 /* Don't have a full line yet. */
d0181813 1250 return SR_ERR_NA;
4a35548b 1251
df0db9fd
GS
1252 p = g_strrstr_len(in->buf->str, in->buf->len, termination);
1253 if (!p)
41d214f6 1254 /* Don't have a full line yet. */
d0181813 1255 return SR_ERR_NA;
41d214f6
BV
1256 len = p - in->buf->str - 1;
1257 new_buf = g_string_new_len(in->buf->str, len);
1258 g_string_append_c(new_buf, '\0');
4a35548b 1259
41d214f6
BV
1260 inc->termination = g_strdup(termination);
1261
1262 if (in->buf->str[0] != '\0')
1263 ret = initial_parse(in, new_buf);
1264 else
1265 ret = SR_OK;
1266
1267 g_string_free(new_buf, TRUE);
1268
1269 return ret;
1270}
1271
7f4c3a62 1272static int process_buffer(struct sr_input *in, gboolean is_eof)
41d214f6 1273{
41d214f6
BV
1274 struct context *inc;
1275 gsize num_columns;
e53f32d2 1276 size_t line_idx, col_idx, col_nr;
836fac9c
GS
1277 const struct column_details *details;
1278 col_parse_cb parse_func;
ad6a2bee 1279 int ret;
e53f32d2 1280 char *p, **lines, *line, **columns, *column;
41d214f6 1281
41d214f6 1282 inc = in->priv;
d0181813 1283 if (!inc->started) {
bee2b016 1284 std_session_send_df_header(in->sdi);
d0181813 1285 inc->started = TRUE;
4a35548b
MS
1286 }
1287
4555d3bd
GS
1288 /*
1289 * Consider empty input non-fatal. Keep accumulating input until
1290 * at least one full text line has become available. Grab the
1291 * maximum amount of accumulated data that consists of full text
1292 * lines, and process what has been received so far, leaving not
1293 * yet complete lines for the next invocation.
7f4c3a62
GS
1294 *
1295 * Enforce that all previously buffered data gets processed in
1296 * the "EOF" condition. Do not insist in the presence of the
1297 * termination sequence for the last line (may often be missing
1298 * on Windows). A present termination sequence will just result
1299 * in the "execution of an empty line", and does not harm.
4555d3bd
GS
1300 */
1301 if (!in->buf->len)
1302 return SR_OK;
7f4c3a62
GS
1303 if (is_eof) {
1304 p = in->buf->str + in->buf->len;
1305 } else {
1306 p = g_strrstr_len(in->buf->str, in->buf->len, inc->termination);
1307 if (!p)
1308 return SR_ERR;
1309 *p = '\0';
1310 p += strlen(inc->termination);
1311 }
41d214f6 1312 g_strstrip(in->buf->str);
4a35548b 1313
18078d05 1314 ret = SR_OK;
ef0b9935 1315 lines = g_strsplit(in->buf->str, inc->termination, 0);
e53f32d2 1316 for (line_idx = 0; (line = lines[line_idx]); line_idx++) {
41d214f6 1317 inc->line_number++;
ef0b9935
GS
1318 if (inc->line_number < inc->start_line) {
1319 sr_spew("Line %zu skipped (before start).", inc->line_number);
1320 continue;
1321 }
df0db9fd 1322 if (line[0] == '\0') {
41d214f6 1323 sr_spew("Blank line %zu skipped.", inc->line_number);
4a35548b
MS
1324 continue;
1325 }
1326
1327 /* Remove trailing comment. */
df0db9fd
GS
1328 strip_comment(line, inc->comment);
1329 if (line[0] == '\0') {
41d214f6 1330 sr_spew("Comment-only line %zu skipped.", inc->line_number);
4a35548b
MS
1331 continue;
1332 }
1333
160691b9 1334 /* Skip the header line, its content was used as the channel names. */
de8fe3b5 1335 if (inc->use_header && !inc->header_seen) {
160691b9 1336 sr_spew("Header line %zu skipped.", inc->line_number);
de8fe3b5 1337 inc->header_seen = TRUE;
160691b9
JS
1338 continue;
1339 }
1340
e53f32d2
GS
1341 /* Split the line into columns, check for minimum length. */
1342 columns = split_line(line, inc);
df0db9fd 1343 if (!columns) {
41d214f6 1344 sr_err("Error while parsing line %zu.", inc->line_number);
2355d229 1345 g_strfreev(lines);
4a35548b
MS
1346 return SR_ERR;
1347 }
4a35548b 1348 num_columns = g_strv_length(columns);
e53f32d2
GS
1349 if (num_columns < inc->column_want_count) {
1350 sr_err("Insufficient column count %zu in line %zu.",
1351 num_columns, inc->line_number);
4a35548b 1352 g_strfreev(columns);
2355d229 1353 g_strfreev(lines);
4a35548b
MS
1354 return SR_ERR;
1355 }
1356
836fac9c 1357 /* Have the columns of the current text line processed. */
626c388a 1358 clear_logic_samples(inc);
43bdef26 1359 clear_analog_samples(inc);
e53f32d2
GS
1360 for (col_idx = 0; col_idx < inc->column_want_count; col_idx++) {
1361 column = columns[col_idx];
1362 col_nr = col_idx + 1;
836fac9c
GS
1363 details = lookup_column_details(inc, col_nr);
1364 if (!details || !details->text_format)
1365 continue;
1366 parse_func = col_parse_funcs[details->text_format];
1367 if (!parse_func)
1368 continue;
1369 ret = parse_func(column, inc, details);
e53f32d2
GS
1370 if (ret != SR_OK) {
1371 g_strfreev(columns);
1372 g_strfreev(lines);
1373 return SR_ERR;
1374 }
4a35548b
MS
1375 }
1376
626c388a
GS
1377 /* Send sample data to the session bus (buffered). */
1378 ret = queue_logic_samples(in);
43bdef26 1379 ret += queue_analog_samples(in);
41d214f6 1380 if (ret != SR_OK) {
4a35548b 1381 sr_err("Sending samples failed.");
cd59e6ec 1382 g_strfreev(columns);
2355d229 1383 g_strfreev(lines);
4a35548b
MS
1384 return SR_ERR;
1385 }
cd59e6ec 1386
41d214f6
BV
1387 g_strfreev(columns);
1388 }
1389 g_strfreev(lines);
241c386a 1390 g_string_erase(in->buf, 0, p - in->buf->str);
41d214f6 1391
7066fd46 1392 return ret;
41d214f6
BV
1393}
1394
7066fd46 1395static int receive(struct sr_input *in, GString *buf)
41d214f6
BV
1396{
1397 struct context *inc;
7066fd46
BV
1398 int ret;
1399
1400 g_string_append_len(in->buf, buf->str, buf->len);
41d214f6
BV
1401
1402 inc = in->priv;
1a920e33 1403 if (!inc->column_seen_count) {
df0db9fd
GS
1404 ret = initial_receive(in);
1405 if (ret == SR_ERR_NA)
7066fd46
BV
1406 /* Not enough data yet. */
1407 return SR_OK;
1408 else if (ret != SR_OK)
1409 return SR_ERR;
1410
1411 /* sdi is ready, notify frontend. */
1412 in->sdi_ready = TRUE;
41d214f6 1413 return SR_OK;
7066fd46
BV
1414 }
1415
7f4c3a62 1416 ret = process_buffer(in, FALSE);
7066fd46
BV
1417
1418 return ret;
1419}
1420
1421static int end(struct sr_input *in)
1422{
1423 struct context *inc;
7066fd46 1424 int ret;
41d214f6 1425
7066fd46 1426 if (in->sdi_ready)
7f4c3a62 1427 ret = process_buffer(in, TRUE);
7066fd46
BV
1428 else
1429 ret = SR_OK;
cd59e6ec
GS
1430 if (ret != SR_OK)
1431 return ret;
1432
626c388a 1433 ret = flush_logic_samples(in);
43bdef26 1434 ret += flush_analog_samples(in);
cd59e6ec
GS
1435 if (ret != SR_OK)
1436 return ret;
7066fd46
BV
1437
1438 inc = in->priv;
3be42bc2 1439 if (inc->started)
bee2b016 1440 std_session_send_df_end(in->sdi);
4a35548b 1441
7066fd46
BV
1442 return ret;
1443}
1444
d5cc282f 1445static void cleanup(struct sr_input *in)
7066fd46
BV
1446{
1447 struct context *inc;
1448
affaf540
GS
1449 keep_header_for_reread(in);
1450
7066fd46
BV
1451 inc = in->priv;
1452
b1f83103 1453 g_free(inc->termination);
539188e5 1454 inc->termination = NULL;
cd59e6ec 1455 g_free(inc->datafeed_buffer);
539188e5 1456 inc->datafeed_buffer = NULL;
43bdef26
GS
1457 g_free(inc->analog_datafeed_buffer);
1458 inc->analog_datafeed_buffer = NULL;
4a35548b
MS
1459}
1460
ad93bfb0
SA
1461static int reset(struct sr_input *in)
1462{
1463 struct context *inc = in->priv;
1464
1465 cleanup(in);
1466 inc->started = FALSE;
1467 g_string_truncate(in->buf, 0);
1468
1469 return SR_OK;
1470}
1471
c6aa9870 1472enum option_index {
2142a79b 1473 OPT_COL_FMTS,
c6aa9870 1474 OPT_SINGLE_COL,
72903e9d 1475 OPT_FIRST_COL,
c6aa9870 1476 OPT_NUM_LOGIC,
c6aa9870 1477 OPT_FORMAT,
c6aa9870 1478 OPT_START,
72903e9d
GS
1479 OPT_HEADER,
1480 OPT_RATE,
1481 OPT_DELIM,
1482 OPT_COMMENT,
c6aa9870
GS
1483 OPT_MAX,
1484};
1485
41d214f6 1486static struct sr_option options[] = {
72903e9d
GS
1487 [OPT_COL_FMTS] = {
1488 "column_formats", "Column format specs",
08eb955a 1489 "Specifies text columns data types: A comma separated list of [<cols>]<fmt>[<bits>] items, with - to ignore columns, x/o/b/l for logic data, a (and resolution) for analog data.",
72903e9d
GS
1490 NULL, NULL,
1491 },
1492 [OPT_SINGLE_COL] = {
1493 "single_column", "Single column",
08eb955a 1494 "Enable single-column mode, exclusively use text from the specified column (number starting at 1). Obsoleted by 'column_formats'.",
72903e9d
GS
1495 NULL, NULL,
1496 },
1497 [OPT_FIRST_COL] = {
1498 "first_column", "First column",
08eb955a 1499 "Number of the first column with logic data in simple multi-column mode (number starting at 1, default 1). Obsoleted by 'column_formats'.",
72903e9d
GS
1500 NULL, NULL,
1501 },
1502 [OPT_NUM_LOGIC] = {
1503 "logic_channels", "Number of logic channels",
1504 "Logic channel count, required in simple single-column mode, defaults to \"all remaining columns\" in simple multi-column mode. Obsoleted by 'column_formats'.",
1505 NULL, NULL,
1506 },
1507 [OPT_FORMAT] = {
1508 "single_format", "Data format for simple single-column mode.",
08eb955a 1509 "The number format of single-column mode input data: bin, hex, oct. Obsoleted by 'column_formats'.",
72903e9d
GS
1510 NULL, NULL,
1511 },
1512 [OPT_START] = {
1513 "start_line", "Start line",
1514 "The line number at which to start processing input text (default: 1).",
1515 NULL, NULL,
1516 },
1517 [OPT_HEADER] = {
1518 "header", "Get channel names from first line.",
08eb955a 1519 "Use the first processed line's column captions (when available) as channel names. Off by default",
72903e9d
GS
1520 NULL, NULL,
1521 },
1522 [OPT_RATE] = {
1523 "samplerate", "Samplerate (Hz)",
08eb955a 1524 "The input data's sample rate in Hz. No default value.",
72903e9d
GS
1525 NULL, NULL,
1526 },
1527 [OPT_DELIM] = {
1528 "column_separator", "Column separator",
1529 "The sequence which separates text columns. Non-empty text, comma by default.",
1530 NULL, NULL,
1531 },
1532 [OPT_COMMENT] = {
1533 "comment_leader", "Comment leader character",
08eb955a 1534 "The text which starts comments at the end of text lines, semicolon by default.",
72903e9d
GS
1535 NULL, NULL,
1536 },
c6aa9870 1537 [OPT_MAX] = ALL_ZERO,
41d214f6
BV
1538};
1539
2c240774 1540static const struct sr_option *get_options(void)
41d214f6 1541{
31c41782
UH
1542 GSList *l;
1543
41d214f6 1544 if (!options[0].def) {
1a920e33 1545 options[OPT_COL_FMTS].def = g_variant_ref_sink(g_variant_new_string(""));
e53f32d2 1546 options[OPT_SINGLE_COL].def = g_variant_ref_sink(g_variant_new_uint32(0));
72903e9d 1547 options[OPT_FIRST_COL].def = g_variant_ref_sink(g_variant_new_uint32(1));
e53f32d2 1548 options[OPT_NUM_LOGIC].def = g_variant_ref_sink(g_variant_new_uint32(0));
c6aa9870 1549 options[OPT_FORMAT].def = g_variant_ref_sink(g_variant_new_string("bin"));
31c41782
UH
1550 l = NULL;
1551 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("bin")));
1552 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("hex")));
1553 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string("oct")));
c6aa9870 1554 options[OPT_FORMAT].values = l;
e53f32d2 1555 options[OPT_START].def = g_variant_ref_sink(g_variant_new_uint32(1));
72903e9d
GS
1556 options[OPT_HEADER].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
1557 options[OPT_RATE].def = g_variant_ref_sink(g_variant_new_uint64(0));
1558 options[OPT_DELIM].def = g_variant_ref_sink(g_variant_new_string(","));
1559 options[OPT_COMMENT].def = g_variant_ref_sink(g_variant_new_string(";"));
41d214f6
BV
1560 }
1561
1562 return options;
1563}
1564
d4c93774 1565SR_PRIV struct sr_input_module input_csv = {
4a35548b 1566 .id = "csv",
41d214f6
BV
1567 .name = "CSV",
1568 .desc = "Comma-separated values",
c7bc82ff 1569 .exts = (const char*[]){"csv", NULL},
41d214f6 1570 .options = get_options,
4a35548b 1571 .init = init,
41d214f6 1572 .receive = receive,
7066fd46 1573 .end = end,
41d214f6 1574 .cleanup = cleanup,
ad93bfb0 1575 .reset = reset,
4a35548b 1576};