+ return flt_value;
+}
+
+/*
+ * Set a logic channel's level depending on the VCD signal's identifier
+ * and parsed value. Multi-bit VCD values will affect several sigrok
+ * channels. One VCD signal name can translate to several sigrok channels.
+ */
+static void process_bits(struct context *inc, char *identifier,
+ uint8_t *in_bits_data, size_t in_bits_count)
+{
+ size_t size;
+ gboolean have_int;
+ GSList *l;
+ struct vcd_channel *vcd_ch;
+ float int_val;
+ size_t bit_idx;
+ uint8_t *in_bit_ptr, in_bit_mask;
+ uint8_t *out_bit_ptr, out_bit_mask;
+ uint8_t bit_val;
+
+ size = 0;
+ have_int = FALSE;
+ int_val = 0;
+ for (l = inc->channels; l; l = l->next) {
+ vcd_ch = l->data;
+ if (g_strcmp0(identifier, vcd_ch->identifier) != 0)
+ continue;
+ if (vcd_ch->type == SR_CHANNEL_ANALOG) {
+ /* Special case for 'integer' VCD signal types. */
+ size = vcd_ch->size; /* Flag for "VCD signal found". */
+ if (!have_int) {
+ int_val = get_int_val(in_bits_data, in_bits_count);
+ have_int = TRUE;
+ }
+ inc->current_floats[vcd_ch->array_index] = int_val;
+ continue;
+ }
+ if (vcd_ch->type != SR_CHANNEL_LOGIC)
+ continue;
+ sr_spew("Processing %s data, id '%s', ch %zu sz %zu",
+ (size == 1) ? "bit" : "vector",
+ identifier, vcd_ch->array_index, vcd_ch->size);
+
+ /* Found our (logic) channel. Setup in/out bit positions. */
+ size = vcd_ch->size;
+ in_bit_ptr = in_bits_data;
+ in_bit_mask = 1 << 0;
+ out_bit_ptr = &inc->current_logic[vcd_ch->byte_idx];
+ out_bit_mask = vcd_ch->bit_mask;
+
+ /*
+ * Pass VCD input bit(s) to sigrok logic bits. Conversion
+ * must be done repeatedly because one VCD signal name
+ * can translate to several sigrok channels, and shifting
+ * a previously computed bit field to another channel's
+ * position in the buffer would be nearly as expensive,
+ * and certain would increase complexity of the code.
+ */
+ for (bit_idx = 0; bit_idx < size; bit_idx++) {
+ /* Get the bit value from input data. */
+ bit_val = 0;
+ if (bit_idx < in_bits_count) {
+ bit_val = *in_bit_ptr & in_bit_mask;
+ in_bit_mask <<= 1;
+ if (!in_bit_mask) {
+ in_bit_mask = 1 << 0;
+ in_bit_ptr++;
+ }
+ }
+ /* Manipulate the sample buffer data image. */
+ if (bit_val)
+ *out_bit_ptr |= out_bit_mask;
+ else
+ *out_bit_ptr &= ~out_bit_mask;
+ /* Update output position after bitmap update. */
+ out_bit_mask <<= 1;
+ if (!out_bit_mask) {
+ out_bit_mask = 1 << 0;
+ out_bit_ptr++;
+ }
+ }
+ }
+ if (!size && !is_ignored(inc, identifier))
+ sr_warn("VCD signal not found for ID '%s'.", identifier);
+}
+
+/*
+ * Set an analog channel's value from a floating point number. One
+ * VCD signal name can translate to several sigrok channels.
+ */
+static void process_real(struct context *inc, char *identifier, float real_val)
+{
+ gboolean found;
+ GSList *l;
+ struct vcd_channel *vcd_ch;
+
+ found = FALSE;
+ for (l = inc->channels; l; l = l->next) {
+ vcd_ch = l->data;
+ if (vcd_ch->type != SR_CHANNEL_ANALOG)
+ continue;
+ if (g_strcmp0(identifier, vcd_ch->identifier) != 0)
+ continue;
+
+ /* Found our (analog) channel. */
+ found = TRUE;
+ sr_spew("Processing real data, id '%s', ch %zu, val %.16g",
+ identifier, vcd_ch->array_index, real_val);
+ inc->current_floats[vcd_ch->array_index] = real_val;
+ }
+ if (!found && !is_ignored(inc, identifier))
+ sr_warn("VCD signal not found for ID '%s'.", identifier);
+}
+
+/*
+ * Converts a bit position's text character to a number value.
+ *
+ * TODO Check for complete coverage of Verilog's standard logic values
+ * (IEEE-1364). The set is said to be “01XZHUWL-”, which only a part of
+ * is handled here. What would be the complete mapping?
+ * - 0/L -> bit value 0
+ * - 1/H -> bit value 1
+ * - X "don't care" -> TODO
+ * - Z "high impedance" -> TODO
+ * - W "weak(?)" -> TODO
+ * - U "undefined" -> TODO
+ * - '-' "TODO" -> TODO
+ *
+ * For simplicity, this input module implementation maps "known low"
+ * values to 0, and "known high" values to 1. All other values will
+ * end up assuming "low" (return number 0), while callers might warn.
+ * It's up to users to provide compatible input data, or accept the
+ * warnings. Silently accepting unknown input data is not desirable.
+ */
+static uint8_t vcd_char_to_value(char bit_char, int *warn)
+{
+
+ bit_char = g_ascii_tolower(bit_char);
+
+ /* Convert the "undisputed" variants. */
+ if (bit_char == '0' || bit_char == 'l')
+ return 0;
+ if (bit_char == '1' || bit_char == 'h')
+ return 1;
+
+ /* Convert the "uncertain" variants. */
+ if (warn)
+ *warn = 1;
+ if (bit_char == 'x' || bit_char == 'z')
+ return 0;
+ if (bit_char == 'u')
+ return 0;
+ if (bit_char == '-')
+ return 0;
+
+ /* Unhandled input text. */
+ return ~0;
+}
+
+/*
+ * Check the validity of a VCD string value. It's essential to reliably
+ * accept valid data which the community uses in the field, yet robustly
+ * reject invalid data for users' awareness. Since IEEE 1800-2017 would
+ * not discuss the representation of this data type, it's assumed to not
+ * be an official feature of the VCD file format. This implementation is
+ * an educated guess after inspection of other arbitrary implementations,
+ * not backed by any specification or public documentation.
+ *
+ * A quick summary of the implemented assumptions: Must be a sequence of
+ * ASCII printables. Must not contain whitespace. Might contain escape
+ * sequences: A backslash followed by a single character, like '\n' or
+ * '\\'. Or a backslash and the letter x followed by two hex digits,
+ * like '\x20'. Or a backslash followed by three octal digits, like
+ * '\007'. As an exception also accepts a single digit '\0' but only at
+ * the text end. The string value may be empty, but must not be NULL.
+ *
+ * This implementation assumes an ASCII based platform for simplicity
+ * and readability. Should be a given on sigrok supported platforms.
+ */
+static gboolean vcd_string_valid(const char *s)
+{
+ char c;
+
+ if (!s)
+ return FALSE;
+
+ while (*s) {
+ c = *s++;
+ /* Reject non-printable ASCII chars including DEL. */
+ if (c < ' ')
+ return FALSE;
+ if (c > '~')
+ return FALSE;
+ /* Deeper inspection of escape sequences. */
+ if (c == '\\') {
+ c = *s++;
+ switch (c) {
+ case 'a': /* BEL, bell aka "alarm" */
+ case 'b': /* BS, back space */
+ case 't': /* TAB, tabulator */
+ case 'n': /* NL, newline */
+ case 'v': /* VT, vertical tabulator */
+ case 'f': /* FF, form feed */
+ case 'r': /* CR, carriage return */
+ case '"': /* double quotes */
+ case '\'': /* tick, single quote */
+ case '?': /* question mark */
+ case '\\': /* backslash */
+ continue;
+ case 'x': /* \xNN two hex digits */
+ c = *s++;
+ if (!g_ascii_isxdigit(c))
+ return FALSE;
+ c = *s++;
+ if (!g_ascii_isxdigit(c))
+ return FALSE;
+ continue;
+ case '0': /* \NNN three octal digits */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ /* Special case '\0' at end of text. */
+ if (c == '0' && !*s)
+ return TRUE;
+ /*
+ * First digit was covered by the outer
+ * switch(). Two more digits to check.
+ */
+ c = *s++;
+ if (!g_ascii_isdigit(c) || c > '7')
+ return FALSE;
+ c = *s++;
+ if (!g_ascii_isdigit(c) || c > '7')
+ return FALSE;
+ continue;
+ default:
+ return FALSE;
+ }
+ }