1 /* 2 * Parsing KEY=VALUE,... strings 3 * 4 * Copyright (C) 2017 Red Hat Inc. 5 * 6 * Authors: 7 * Markus Armbruster <armbru@redhat.com>, 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 */ 12 13 /* 14 * KEY=VALUE,... syntax: 15 * 16 * key-vals = [ key-val { ',' key-val } [ ',' ] ] 17 * key-val = key '=' val 18 * key = key-fragment { '.' key-fragment } 19 * key-fragment = / [^=,.]* / 20 * val = { / [^,]* / | ',,' } 21 * 22 * Semantics defined by reduction to JSON: 23 * 24 * key-vals defines a tree of objects rooted at R 25 * where for each key-val = key-fragment . ... = val in key-vals 26 * R op key-fragment op ... = val' 27 * where (left-associative) op is member reference L.key-fragment 28 * val' is val with ',,' replaced by ',' 29 * and only R may be empty. 30 * 31 * Duplicate keys are permitted; all but the last one are ignored. 32 * 33 * The equations must have a solution. Counter-example: a.b=1,a=2 34 * doesn't have one, because R.a must be an object to satisfy a.b=1 35 * and a string to satisfy a=2. 36 * 37 * The length of any key-fragment must be between 1 and 127. 38 * 39 * Design flaw: there is no way to denote an empty non-root object. 40 * While interpreting "key absent" as empty object seems natural 41 * (removing a key-val from the input string removes the member when 42 * there are more, so why not when it's the last), it doesn't work: 43 * "key absent" already means "optional object absent", which isn't 44 * the same as "empty object present". 45 * 46 * Additional syntax for use with an implied key: 47 * 48 * key-vals-ik = val-no-key [ ',' key-vals ] 49 * val-no-key = / [^=,]* / 50 * 51 * where no-key is syntactic sugar for implied-key=val-no-key. 52 * 53 * TODO support lists 54 * TODO support key-fragment with __RFQDN_ prefix (downstream extensions) 55 */ 56 57 #include "qemu/osdep.h" 58 #include "qapi/error.h" 59 #include "qapi/qmp/qstring.h" 60 #include "qemu/option.h" 61 62 /* 63 * Ensure @cur maps @key_in_cur the right way. 64 * If @value is null, it needs to map to a QDict, else to this 65 * QString. 66 * If @cur doesn't have @key_in_cur, put an empty QDict or @value, 67 * respectively. 68 * Else, if it needs to map to a QDict, and already does, do nothing. 69 * Else, if it needs to map to this QString, and already maps to a 70 * QString, replace it by @value. 71 * Else, fail because we have conflicting needs on how to map 72 * @key_in_cur. 73 * In any case, take over the reference to @value, i.e. if the caller 74 * wants to hold on to a reference, it needs to QINCREF(). 75 * Use @key up to @key_cursor to identify the key in error messages. 76 * On success, return the mapped value. 77 * On failure, store an error through @errp and return NULL. 78 */ 79 static QObject *keyval_parse_put(QDict *cur, 80 const char *key_in_cur, QString *value, 81 const char *key, const char *key_cursor, 82 Error **errp) 83 { 84 QObject *old, *new; 85 86 old = qdict_get(cur, key_in_cur); 87 if (old) { 88 if (qobject_type(old) != (value ? QTYPE_QSTRING : QTYPE_QDICT)) { 89 error_setg(errp, "Parameters '%.*s.*' used inconsistently", 90 (int)(key_cursor - key), key); 91 QDECREF(value); 92 return NULL; 93 } 94 if (!value) { 95 return old; /* already QDict, do nothing */ 96 } 97 new = QOBJECT(value); /* replacement */ 98 } else { 99 new = value ? QOBJECT(value) : QOBJECT(qdict_new()); 100 } 101 qdict_put_obj(cur, key_in_cur, new); 102 return new; 103 } 104 105 /* 106 * Parse one KEY=VALUE from @params, store result in @qdict. 107 * The first fragment of KEY applies to @qdict. Subsequent fragments 108 * apply to nested QDicts, which are created on demand. @implied_key 109 * is as in keyval_parse(). 110 * On success, return a pointer to the next KEY=VALUE, or else to '\0'. 111 * On failure, return NULL. 112 */ 113 static const char *keyval_parse_one(QDict *qdict, const char *params, 114 const char *implied_key, 115 Error **errp) 116 { 117 const char *key, *key_end, *s; 118 size_t len; 119 char key_in_cur[128]; 120 QDict *cur; 121 QObject *next; 122 QString *val; 123 124 key = params; 125 len = strcspn(params, "=,"); 126 if (implied_key && len && key[len] != '=') { 127 /* Desugar implied key */ 128 key = implied_key; 129 len = strlen(implied_key); 130 } 131 key_end = key + len; 132 133 /* 134 * Loop over key fragments: @s points to current fragment, it 135 * applies to @cur. @key_in_cur[] holds the previous fragment. 136 */ 137 cur = qdict; 138 s = key; 139 for (;;) { 140 for (len = 0; s + len < key_end && s[len] != '.'; len++) { 141 } 142 if (!len) { 143 assert(key != implied_key); 144 error_setg(errp, "Invalid parameter '%.*s'", 145 (int)(key_end - key), key); 146 return NULL; 147 } 148 if (len >= sizeof(key_in_cur)) { 149 assert(key != implied_key); 150 error_setg(errp, "Parameter%s '%.*s' is too long", 151 s != key || s + len != key_end ? " fragment" : "", 152 (int)len, s); 153 return NULL; 154 } 155 156 if (s != key) { 157 next = keyval_parse_put(cur, key_in_cur, NULL, 158 key, s - 1, errp); 159 if (!next) { 160 return NULL; 161 } 162 cur = qobject_to_qdict(next); 163 assert(cur); 164 } 165 166 memcpy(key_in_cur, s, len); 167 key_in_cur[len] = 0; 168 s += len; 169 170 if (*s != '.') { 171 break; 172 } 173 s++; 174 } 175 176 if (key == implied_key) { 177 assert(!*s); 178 s = params; 179 } else { 180 if (*s != '=') { 181 error_setg(errp, "Expected '=' after parameter '%.*s'", 182 (int)(s - key), key); 183 return NULL; 184 } 185 s++; 186 } 187 188 val = qstring_new(); 189 for (;;) { 190 if (!*s) { 191 break; 192 } else if (*s == ',') { 193 s++; 194 if (*s != ',') { 195 break; 196 } 197 } 198 qstring_append_chr(val, *s++); 199 } 200 201 if (!keyval_parse_put(cur, key_in_cur, val, key, key_end, errp)) { 202 return NULL; 203 } 204 return s; 205 } 206 207 /* 208 * Parse @params in QEMU's traditional KEY=VALUE,... syntax. 209 * If @implied_key, the first KEY= can be omitted. @implied_key is 210 * implied then, and VALUE can't be empty or contain ',' or '='. 211 * On success, return a dictionary of the parsed keys and values. 212 * On failure, store an error through @errp and return NULL. 213 */ 214 QDict *keyval_parse(const char *params, const char *implied_key, 215 Error **errp) 216 { 217 QDict *qdict = qdict_new(); 218 const char *s; 219 220 s = params; 221 while (*s) { 222 s = keyval_parse_one(qdict, s, implied_key, errp); 223 if (!s) { 224 QDECREF(qdict); 225 return NULL; 226 } 227 implied_key = NULL; 228 } 229 230 return qdict; 231 } 232