+ uint64_t p, q;
+
+ /*
+ * The standard allows for values 1, 10 or 100
+ * and units s, ms, us, ns, ps and fs.
+ */
+ if (sr_parse_period(contents, &p, &q) != SR_OK) {
+ sr_err("Parsing $timescale failed.");
+ return SR_ERR_DATA;
+ }
+
+ inc->samplerate = q / p;
+ sr_dbg("Samplerate: %" PRIu64, inc->samplerate);
+ if (q % p != 0) {
+ /* Does not happen unless time value is non-standard */
+ sr_warn("Inexact rounding of samplerate, %" PRIu64 " / %" PRIu64 " to %" PRIu64 " Hz.",
+ q, p, inc->samplerate);
+ }
+
+ return SR_OK;
+}
+
+/*
+ * Handle '$scope' and '$upscope' sections in the input file. Assume that
+ * input signals have a "base name", which may be ambiguous within the
+ * file. These names get declared within potentially nested scopes, which
+ * this implementation uses to create longer but hopefully unique and
+ * thus more usable sigrok channel names.
+ *
+ * Track the currently effective scopes in a string variable to simplify
+ * the channel name creation. Start from an empty string, then append the
+ * scope name and a separator when a new scope opens, and remove the last
+ * scope name when a scope closes. This allows to simply prefix basenames
+ * with the current scope to get a full name.
+ *
+ * It's an implementation detail to keep the trailing NUL here in the
+ * GString member, to simplify the g_strconcat() call in the channel name
+ * creation.
+ *
+ * TODO
+ * - Check whether scope types must get supported, this implementation
+ * does not distinguish between 'module' and 'begin' and what else
+ * may be seen. The first word simply gets ignored.
+ * - Check the allowed alphabet for scope names. This implementation
+ * assumes "programming language identifier" style (alphanumeric with
+ * underscores, plus brackets since we've seen them in example files).
+ */
+static int parse_scope(struct context *inc, char *contents, gboolean is_up)
+{
+ char *sep_pos, *name_pos;
+ char **parts;
+ size_t length;
+
+ /*
+ * The 'upscope' case, drop one scope level (if available). Accept
+ * excess 'upscope' calls, assume that a previous 'scope' section
+ * was ignored because it referenced our software package's name.
+ */
+ if (is_up) {
+ /*
+ * Check for a second right-most separator (and position
+ * right behind that, which is the start of the last
+ * scope component), or fallback to the start of string.
+ * g_string_erase() from that positon to the end to drop
+ * the last component.
+ */
+ name_pos = inc->scope_prefix->str;
+ do {
+ sep_pos = strrchr(name_pos, SCOPE_SEP);
+ if (!sep_pos)
+ break;
+ *sep_pos = '\0';
+ sep_pos = strrchr(name_pos, SCOPE_SEP);
+ if (!sep_pos)
+ break;
+ name_pos = ++sep_pos;
+ } while (0);
+ length = name_pos - inc->scope_prefix->str;
+ g_string_truncate(inc->scope_prefix, length);
+ g_string_append_c(inc->scope_prefix, '\0');
+ sr_dbg("$upscope, prefix now: \"%s\"", inc->scope_prefix->str);
+ return SR_OK;
+ }
+
+ /*
+ * The 'scope' case, add another scope level. But skip our own
+ * package name, assuming that this is an artificial node which
+ * was emitted by libsigrok's VCD output module.
+ */
+ sr_spew("$scope, got: \"%s\"", contents);
+ parts = g_strsplit_set(contents, " \r\n\t", 0);
+ remove_empty_parts(parts);
+ length = g_strv_length(parts);
+ if (length != 2) {
+ sr_err("Unsupported 'scope' syntax: %s", contents);
+ g_strfreev(parts);
+ return SR_ERR_DATA;
+ }
+ name_pos = parts[1];
+ if (strcmp(name_pos, PACKAGE_NAME) == 0) {
+ sr_info("Skipping scope with application's package name: %s",
+ name_pos);
+ *name_pos = '\0';
+ }
+ if (*name_pos) {
+ /* Drop NUL, append scope name and separator, and re-add NUL. */
+ g_string_truncate(inc->scope_prefix, inc->scope_prefix->len - 1);
+ g_string_append_printf(inc->scope_prefix,
+ "%s%c%c", name_pos, SCOPE_SEP, '\0');
+ }
+ g_strfreev(parts);
+ sr_dbg("$scope, prefix now: \"%s\"", inc->scope_prefix->str);
+
+ return SR_OK;
+}
+
+/**
+ * Parse a $var section which describes a VCD signal ("variable").
+ *
+ * @param[in] inc Input module context.
+ * @param[in] contents Input text, content of $var section.
+ */
+static int parse_header_var(struct context *inc, char *contents)
+{
+ char **parts;
+ size_t length;
+ char *type, *size_txt, *id, *ref, *idx;
+ gboolean is_reg, is_wire, is_real, is_int;
+ enum sr_channeltype ch_type;
+ size_t size, next_size;