Browse Source

Rewrite query string decoding

Santtu Lakkala 1 month ago
parent
commit
0155ccfa52
1 changed files with 69 additions and 34 deletions
  1. 69 34
      xs_url.h

+ 69 - 34
xs_url.h

@@ -11,6 +11,39 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
 
 #ifdef XS_IMPLEMENTATION
 
+char *xs_url_dec_in(char *str, int qs)
+{
+    char *w = str;
+    char *r;
+
+    for (r = str; *r != '\0'; r++) {
+        switch (*r) {
+        case '%': {
+            unsigned hex;
+            if (!r[1] || !r[2])
+                return NULL;
+            if (sscanf(r + 1, "%2x", &hex) != 1)
+                return NULL;
+            *w++ = hex;
+            r += 2;
+            break;
+        }
+
+        case '+':
+            if (qs) {
+                *w++ = ' ';
+                break;
+            }
+            /* fall-through */
+        default:
+            *w++ = *r;
+        }
+    }
+
+    *w++ = '\0';
+    return str;
+}
+
 xs_str *xs_url_dec(const char *str)
 /* decodes an URL */
 {
@@ -76,42 +109,44 @@ xs_dict *xs_url_vars(const char *str)
     vars = xs_dict_new();
 
     if (xs_is_string(str)) {
-        /* split by arguments */
-        xs *args = xs_split(str, "&");
-
-        const xs_val *v;
-
-        xs_list_foreach(args, v) {
-            xs *dv = xs_url_dec(v);
-            xs *kv = xs_split_n(dv, "=", 1);
-
-            if (xs_list_len(kv) == 2) {
-                const char *key = xs_list_get(kv, 0);
-                const char *pv  = xs_dict_get(vars, key);
-
-                if (!xs_is_null(pv)) {
-                    /* there is a previous value: convert to a list and append */
-                    xs *vlist = NULL;
-                    if (xs_type(pv) == XSTYPE_LIST)
-                        vlist = xs_dup(pv);
-                    else {
-                        vlist = xs_list_new();
-                        vlist = xs_list_append(vlist, pv);
-                    }
-
-                    vlist = xs_list_append(vlist, xs_list_get(kv, 1));
-                    vars  = xs_dict_set(vars, key, vlist);
-                }
+        xs *dup = xs_dup(str);
+        char *k;
+        char *saveptr;
+        for (k = strtok_r(dup, "&", &saveptr);
+             k;
+             k = strtok_r(NULL, "&", &saveptr)) {
+            char *v = strchr(k, '=');
+            if (!v)
+                continue;
+            *v++ = '\0';
+            k = xs_url_dec_in(k, 1);
+            v = xs_url_dec_in(v, 1);
+            if (!xs_is_string(k) || !xs_is_string(v))
+                continue;
+
+            const char *pv  = xs_dict_get(vars, k);
+            if (!xs_is_null(pv)) {
+                /* there is a previous value: convert to a list and append */
+                xs *vlist = NULL;
+                if (xs_type(pv) == XSTYPE_LIST)
+                    vlist = xs_dup(pv);
                 else {
-                    /* ends with []? force to always be a list */
-                    if (xs_endswith(key, "[]")) {
-                        xs *vlist = xs_list_new();
-                        vlist = xs_list_append(vlist, xs_list_get(kv, 1));
-                        vars = xs_dict_append(vars, key, vlist);
-                    }
-                    else
-                        vars = xs_dict_append(vars, key, xs_list_get(kv, 1));
+                    vlist = xs_list_new();
+                    vlist = xs_list_append(vlist, pv);
+                }
+
+                vlist = xs_list_append(vlist, v);
+                vars  = xs_dict_set(vars, k, vlist);
+            }
+            else {
+                /* ends with []? force to always be a list */
+                if (xs_endswith(k, "[]")) {
+                    xs *vlist = xs_list_new();
+                    vlist = xs_list_append(vlist, v);
+                    vars = xs_dict_append(vars, k, vlist);
                 }
+                else
+                    vars = xs_dict_append(vars, k, v);
             }
         }
     }