[pbs-devel] [RFC PATCH proxmox-backup] ui: implement quoted strings in parsePropertyString

Dominik Csapak d.csapak at proxmox.com
Wed Mar 9 15:16:46 CET 2022


like we do in our rust propertystring parser.
code is heavily inspired by the rust code.

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
if we want to go the js route and not use wasm + rust code for this

i sent it as rfc for pbs (since there we have that property string
implementation), but we actually want this to live in wt, and use it in
pve and pbs both when pve gains support for these.

@thomas, the code is my attempt to write the parser as close to the
rust code as it was sensible, but i'm sure there are some js specific
improvements to be done here. i'll look over it again tomorrow
with fresh eyes and mind, but if you see some things, don't hold back ;)

 www/Utils.js | 100 +++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 82 insertions(+), 18 deletions(-)

diff --git a/www/Utils.js b/www/Utils.js
index 36a94211..87809364 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -62,32 +62,96 @@ Ext.define('PBS.Utils', {
 	return path.indexOf(PBS.Utils.dataStorePrefix) === 0;
     },
 
+    parseQuotedString: function(value) {
+	let data = "";
+	let was_backslash = false;
+	if (value[0] !== '"') {
+	    throw "not a quoted string";
+	}
+	value = value.slice(1);
+	for (let i = 0; ; i++) {
+	    if (i === value.length) {
+		throw "invalid quoted string";
+	    }
+	    if (was_backslash) {
+		was_backslash = false;
+		switch (value[i]) {
+		    case '"': data += '"'; break;
+		    case '\\': data += '\\'; break;
+		    case 'n': data += '\n'; break;
+		    default:
+			throw "unsupported escape sequence";
+		}
+	    } else {
+		switch (value[i]) {
+		    case '"': return [data, i+1];
+		    case '\\': was_backslash = true; break;
+		    default:
+			data += value[i];
+		}
+	    }
+	}
+    },
+
     parsePropertyString: function(value, defaultKey) {
-	var res = {},
-	    error;
+	let res = {};
 
 	if (typeof value !== 'string' || value === '') {
 	    return res;
 	}
 
-	Ext.Array.each(value.split(','), function(p) {
-	    var kv = p.split('=', 2);
-	    if (Ext.isDefined(kv[1])) {
-		res[kv[0]] = kv[1];
-	    } else if (Ext.isDefined(defaultKey)) {
-		if (Ext.isDefined(res[defaultKey])) {
-		    error = 'defaultKey may be only defined once in propertyString';
-		    return false; // break
+	try {
+	    while (value.length > 0) {
+		let key, current;
+		if (value[0] !== '"') {
+		    let idx = value.search(/[,=]/);
+		    if (idx !== -1 && value[idx] === '=') {
+			key = value.slice(0, idx);
+			value = value.slice(idx + 1);
+			if (Ext.isDefined(res[key])) {
+			    throw `duplicate key ${key} found`;
+			}
+		    }
+
+		    if (value[0] !== '"') {
+			let next_idx = value.search(/,/);
+			if (next_idx === -1) {
+			    current = value;
+			    value = "";
+			} else {
+			    current = value.slice(0, next_idx);
+			    value = value.slice(next_idx + 1);
+			}
+		    }
 		}
-		res[defaultKey] = kv[0];
-	    } else {
-		error = 'invalid propertyString, not a key=value pair and no defaultKey defined';
-		return false; // break
-	    }
-	    return true;
-	});
 
-	if (error !== undefined) {
+		if (key === undefined) {
+		    if (Ext.isDefined(defaultKey)) {
+			if (Ext.isDefined(res[defaultKey])) {
+			    throw 'defaultKey may be only defined once in propertyString';
+			}
+			key = defaultKey;
+		    } else {
+			throw "value without key and no defaultKey";
+		    }
+		}
+		if (current === undefined) {
+		    let [val, idx] = PVE.Parser.parseQuotedString(value);
+		    current = val;
+		    value = value.slice(idx + 1);
+		}
+
+		res[key] = current;
+
+		if (value.length > 0) {
+		    if (value[0] === ',') {
+			value = value.slice(1);
+		    } else {
+			throw "garbage after value";
+		    }
+		}
+	    }
+	} catch (error) {
 	    console.error(error);
 	    return null;
 	}
-- 
2.30.2





More information about the pbs-devel mailing list