From: Gerhard Sittig Date: Sat, 23 May 2020 09:29:23 +0000 (+0200) Subject: parsers: extend options parser, support optional ID in key-value lists X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=cad0cba6bb76d7aab6304aef41a2dd304ddad974;p=sigrok-cli.git parsers: extend options parser, support optional ID in key-value lists The options parser accepts a colon separated list of key-value pairs, and a special case where the first item is not a pair but a single word which is an identifier. This lets users select a device driver, or input/output format, or decoder. Extend the options parser to also support an optional identifier as the first item in a list of key-value pairs. Check the first item's keyword for a caller specified literal. This change remains backwards compatible (does not affect behaviour) when users don't pass the special keyword. --- diff --git a/decode.c b/decode.c index a4d4652..4ddf34a 100644 --- a/decode.c +++ b/decode.c @@ -139,7 +139,7 @@ static int register_pd(char *opt_pds, char *opt_pd_annotations) pdtokens = g_strsplit(opt_pds, ",", 0); for (pdtok = pdtokens; *pdtok; pdtok++) { - if (!(pd_opthash = parse_generic_arg(*pdtok, TRUE))) { + if (!(pd_opthash = parse_generic_arg(*pdtok, TRUE, NULL))) { g_critical("Invalid protocol decoder option '%s'.", *pdtok); break; } diff --git a/input.c b/input.c index 0c8d486..6c14972 100644 --- a/input.c +++ b/input.c @@ -51,7 +51,7 @@ static void load_input_file_module(struct df_arg_desc *df_arg) mod_id = NULL; mod_args = NULL; if (opt_input_format) { - mod_args = parse_generic_arg(opt_input_format, TRUE); + mod_args = parse_generic_arg(opt_input_format, TRUE, NULL); mod_id = g_hash_table_lookup(mod_args, "sigrok_key"); } diff --git a/main.c b/main.c index 02f69a2..db2bdc0 100644 --- a/main.c +++ b/main.c @@ -140,7 +140,7 @@ static void get_option(void) if (!(ci = sr_key_info_name_get(SR_KEY_CONFIG, opt_get))) g_critical("Unknown option '%s'", opt_get); - if ((devargs = parse_generic_arg(opt_config, FALSE))) + if ((devargs = parse_generic_arg(opt_config, FALSE, NULL))) set_dev_options(sdi, devargs); else devargs = NULL; @@ -186,7 +186,7 @@ static void set_options(void) return; } - if (!(devargs = parse_generic_arg(opt_config, FALSE))) + if (!(devargs = parse_generic_arg(opt_config, FALSE, NULL))) return; if (!(devices = device_scan())) { diff --git a/parsers.c b/parsers.c index 5652b78..4f3b0c5 100644 --- a/parsers.c +++ b/parsers.c @@ -267,11 +267,44 @@ int parse_triggerstring(const struct sr_dev_inst *sdi, const char *s, return !error; } -GHashTable *parse_generic_arg(const char *arg, gboolean sep_first) +/** + * Create hash table from colon separated key-value pairs input text. + * + * Accepts input text as it was specified by users. Splits the colon + * separated key-value pairs and creates a hash table from these items. + * Optionally supports special forms which are useful for different CLI + * features. + * + * Typical form: =[:=]* + * Generic list of key-value pairs, all items being equal. Mere set. + * + * ID form: [:=]* + * First item is not a key-value pair, instead it's an identifier. Used + * to specify a protocol decoder, or a device driver, or an input/output + * file format, optionally followed by more parameters' values. The ID + * part of the input spec is not optional. + * + * Optional ID: [=][:=]* + * All items are key-value pairs. The first item _may_ be an identifier, + * if its key matches a caller specified key name. Otherwise the input + * text is the above typical form, a mere list of key-value pairs while + * none of them is special. + * + * @param[in] arg Input text. + * @param[in] sep_first Boolean, whether ID form is required. + * @param[in] key_first Keyword name if optional ID is applicable. + * + * @returns A hash table which contains the key/value pairs, or #NULL + * when the input is invalid. + */ +GHashTable *parse_generic_arg(const char *arg, + gboolean sep_first, const char *key_first) { GHashTable *hash; int i; char **elements, *e; + int l; + const char *s; if (!arg || !arg[0]) return NULL; @@ -280,9 +313,29 @@ GHashTable *parse_generic_arg(const char *arg, gboolean sep_first) hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); elements = g_strsplit(arg, ":", 0); - if (sep_first) + if (sep_first) { + /* + * Caller requested "[:=]*" case, Consume the + * first item, before processing more key-value pairs below. + */ g_hash_table_insert(hash, g_strdup("sigrok_key"), g_strdup(elements[i++])); + } else if (key_first && *key_first) { + /* + * Caller requested "[=][:=]*" case. + * Optional special handling of the first item, but only + * consume this first item here when its keyword matched + * the caller's specification. + */ + l = strlen(key_first); + s = elements[i]; + e = strchr(s, '='); + if (e && e - s == l && g_str_has_prefix(s, key_first)) { + g_hash_table_insert(hash, + g_strdup("sigrok_key"), g_strdup(++e)); + i++; + } + } for (; elements[i]; i++) { e = strchr(elements[i], '='); if (!e) @@ -463,7 +516,7 @@ int parse_driver(char *arg, struct sr_dev_driver **driver, GSList **drvopts) if (!arg) return FALSE; - drvargs = parse_generic_arg(arg, TRUE); + drvargs = parse_generic_arg(arg, TRUE, NULL); drvname = g_strdup(g_hash_table_lookup(drvargs, "sigrok_key")); g_hash_table_remove(drvargs, "sigrok_key"); diff --git a/session.c b/session.c index b3d39ff..56a4d25 100644 --- a/session.c +++ b/session.c @@ -92,7 +92,7 @@ const struct sr_output *setup_output_format(const struct sr_dev_inst *sdi, FILE } } - fmtargs = parse_generic_arg(opt_output_format, TRUE); + fmtargs = parse_generic_arg(opt_output_format, TRUE, NULL); fmtspec = g_hash_table_lookup(fmtargs, "sigrok_key"); if (!fmtspec) g_critical("Invalid output format."); @@ -138,7 +138,7 @@ const struct sr_transform *setup_transform_module(const struct sr_dev_inst *sdi) GHashTable *fmtargs, *fmtopts; char *fmtspec; - fmtargs = parse_generic_arg(opt_transform_module, TRUE); + fmtargs = parse_generic_arg(opt_transform_module, TRUE, NULL); fmtspec = g_hash_table_lookup(fmtargs, "sigrok_key"); if (!fmtspec) g_critical("Invalid transform module."); @@ -766,7 +766,7 @@ void run_session(void) } if (opt_config) { - if ((devargs = parse_generic_arg(opt_config, FALSE))) { + if ((devargs = parse_generic_arg(opt_config, FALSE, NULL))) { if (set_dev_options(sdi, devargs) != SR_OK) return; g_hash_table_destroy(devargs); diff --git a/sigrok-cli.h b/sigrok-cli.h index a3905fd..b5603c3 100644 --- a/sigrok-cli.h +++ b/sigrok-cli.h @@ -105,7 +105,8 @@ struct sr_channel *find_channel(GSList *channellist, const char *channelname); GSList *parse_channelstring(struct sr_dev_inst *sdi, const char *channelstring); int parse_triggerstring(const struct sr_dev_inst *sdi, const char *s, struct sr_trigger **trigger); -GHashTable *parse_generic_arg(const char *arg, gboolean sep_first); +GHashTable *parse_generic_arg(const char *arg, + gboolean sep_first, const char *key_first); GHashTable *generic_arg_to_opt(const struct sr_option **opts, GHashTable *genargs); GSList *check_unknown_keys(const struct sr_option **avail, GHashTable *used); gboolean warn_unknown_keys(const struct sr_option **avail, GHashTable *used,