]> git.neil.brown.name Git - edlib.git/blobdiff - core-attr.c
TODO: clean out done items.
[edlib.git] / core-attr.c
index d0b5b7f32ac5dff882f6d2bc7b68fd9d6f4939b5..ffce247fb245203b93b7bd6619bb83546be1940c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright Neil Brown ©2015 <neil@brown.name>
+ * Copyright Neil Brown ©2015-2021 <neil@brown.name>
  * May be distributed under terms of GPLv2 - see file:COPYING
  *
  * Attributes.
@@ -42,13 +42,18 @@ struct attrset {
        char            attrs[0];
 };
 
+/* NOTE: this MAX is the largest sized allocation which
+ * can be shared by multiple attrs.  The size is the sum
+ * of keys and values including nul terminator.
+ * If an attr is larger, it gets an attrset all of its own.
+ */
 #if defined(TEST_ATTR_ADD_DEL)
 #define MAX_ATTR_SIZE (64 - sizeof(struct attrset))
 #else
 #define MAX_ATTR_SIZE (512 - sizeof(struct attrset))
 #endif
 
-static struct attrset *newattr(struct attrset *old, int size)
+static struct attrset *safe newattr(struct attrset *old, int size)
 {
        struct attrset *set = realloc(old, sizeof(struct attrset) + size);
        set->size = size;
@@ -62,11 +67,17 @@ static struct attrset *newattr(struct attrset *old, int size)
 /* attr_cmp just deals with bytes and ASCII digits, so it is
  * not aware for wchars
  */
-static int getcmptok(char **ap)
+static int getcmptok(const char **ap safe)
 {
-       char *a = *ap;
-       char c = *a++;
+       const char *a safe;
+       char c;
        int i;
+
+       if (!*ap)
+               /* FIXME smatch should handle "char * safe *ap safe" */
+               return 0;
+       a = *ap;
+       c = *a++;
        if (!isdigit(c)) {
                *ap = a;
                return c;
@@ -83,9 +94,9 @@ static int getcmptok(char **ap)
 /* Compare 'a' and 'b' treating strings of digits as numbers.
  * If bnum >= 0, it is used as a leading number on 'b'.
  */
-static int attr_cmp(char *a, char *b, int bnum)
+static int attr_cmp(const char *a safe, const char *b safe, int bnum)
 {
-       while (*a && *b) {
+       while (*a && (*b || bnum >= 0)) {
                int ai, bi;
                ai = getcmptok(&a);
                if (bnum >= 0) {
@@ -110,7 +121,7 @@ static int attr_cmp(char *a, char *b, int bnum)
 #ifdef TEST_ATTR_CMP
 #include <stdlib.h>
 #include <stdio.h>
-struct {char *a, *b; int result;} test[] = {
+struct {const char *a, *b; int result;} test[] = {
        { "hello", "there", -1},
        { "6hello", "10world", -1},
        { "0005six", "5six", 0},
@@ -137,12 +148,18 @@ int main(int argc, char *argv[])
 
 #endif
 
-static int __attr_find(struct attrset ***setpp, char *key, int *offsetp, int keynum)
+static int _attr_find(struct attrset ***setpp safe, const char *key safe,
+                      int *offsetp safe, int keynum)
 {
-       struct attrset **setp = *setpp;
-       struct attrset *set = *setp;
+       struct attrset **setp safe;
+       struct attrset *set;
        int i;
 
+       if (!*setpp)
+               /* FIXME smatch should check this */
+               return -1;
+       setp = *setpp;
+       set = *setp;
        if (!set)
                return -1;
        *offsetp = 0;
@@ -167,19 +184,12 @@ static int __attr_find(struct attrset ***setpp, char *key, int *offsetp, int key
        return 1;
 }
 
-int attr_del(struct attrset **setp, char *key)
+static void do_del(struct attrset * *setp safe, int offset)
 {
-       int offset = 0;
-       int cmp;
        int len;
        struct attrset *set;
 
-       cmp = __attr_find(&setp, key, &offset, -1);
-
-       if (cmp)
-               /* Not found */
-               return 0;
-       set = *setp;
+       set = safe_cast *setp;
        len = strlen(set->attrs + offset) + 1;
        len += strlen(set->attrs + offset + len) + 1;
        memmove(set->attrs + offset,
@@ -190,61 +200,143 @@ int attr_del(struct attrset **setp, char *key)
                *setp = set->next;
                free(set);
        }
-       return 1;
 }
 
-char *attr_get_str(struct attrset *set, char *key, int keynum)
+bool attr_del(struct attrset * *setp safe, const char *key safe)
+{
+       int offset = 0;
+       int cmp;
+
+       cmp = _attr_find(&setp, key, &offset, -1);
+
+       if (cmp)
+               /* Not found */
+               return False;
+       do_del(setp, offset);
+       return True;
+}
+
+void attr_del_all(struct attrset * *setp safe, const char *key safe,
+                 int low, int high)
+{
+       int offset = 0;
+       /* Delete all attrs 'key' with keynum from 'low' to 'high' */
+       while (low <= high) {
+               struct attrset *set;
+               int n;
+               int cmp = _attr_find(&setp, key, &offset, low);
+
+               if (cmp < 0)
+                       /* Nothing more to find */
+                       return;
+               low += 1;
+               if (cmp == 0) {
+                       /* Found, better delete */
+                       do_del(setp, offset);
+                       continue;
+               }
+               /* found something higher, possibly update 'low'
+                * to skip over gaps.
+                */
+               set = *setp;
+               if (!set || offset >= set->len)
+                       continue;
+               n = atoi(set->attrs + offset);
+               if (n > low)
+                       low = n;
+       }
+}
+
+char *attr_get_str(struct attrset *set, const char *key safe, int keynum)
 {
        struct attrset **setp = &set;
        int offset = 0;
-       int cmp = __attr_find(&setp, key, &offset, keynum);
+       int cmp = _attr_find(&setp, key, &offset, keynum);
 
-       if (cmp != 0)
+       if (cmp != 0 || !*setp)
                return NULL;
        set = *setp;
        offset += strlen(set->attrs + offset) + 1;
        return set->attrs + offset;
 }
 
-char *attr_find(struct attrset *set, char *key)
+char *attr_find(struct attrset *set, const char *key safe)
 {
        return attr_get_str(set, key, -1);
 }
 
-int attr_set_str(struct attrset **setp, char *key, char *val, int keynum)
+const char *attr_get_next_key(struct attrset *set, const char *key safe,
+                             int keynum, const char **valp safe)
+{
+       struct attrset **setp = &set;
+       int offset = 0;
+       int cmp = _attr_find(&setp, key, &offset, keynum);
+       const char *val;
+
+       if (cmp < 0)
+               return NULL;
+       set = safe_cast *setp;
+       if (cmp == 0) {
+               /* Skip the matching key, then value */
+               offset += strlen(set->attrs + offset) + 1;
+               offset += strlen(set->attrs + offset) + 1;
+       }
+       if (offset >= set->len) {
+               set = set->next;
+               offset = 0;
+       }
+       if (!set)
+               return NULL;
+       key = set->attrs + offset;
+       val = key + strlen(key) + 1;
+       if (keynum >= 0) {
+               int kn = getcmptok(&key);
+               if (kn != keynum + 256)
+                       return NULL;
+               if (*key == ' ')
+                       key += 1;
+       }
+       *valp = val;
+       return key;
+}
+
+int attr_set_str_key(struct attrset **setp safe,
+                    const char *key safe, const char *val,
+                    int keynum)
 {
        int offset = 0;
        int cmp;
        struct attrset *set;
-       unsigned int len;
+       unsigned int len, keylen, vallen;
        char nkey[22];
        int nkeylen = 0;
 
-       cmp = __attr_find(&setp, key, &offset, keynum);
+       cmp = _attr_find(&setp, key, &offset, keynum);
 
-       if (cmp == 0) {
+       if (cmp == 0)
                /* Remove old value */
-               set = *setp;
-               len = strlen(set->attrs + offset) + 1;
-               len += strlen(set->attrs + offset + len) + 1;
-               memmove(set->attrs + offset,
-                       set->attrs + offset + len,
-                       set->len - (offset + len));
-               set->len -= len;
-       }
+               do_del(setp, offset);
+
        if (!val)
                return cmp;
+
        set = *setp;
        if (keynum >= 0) {
                snprintf(nkey, sizeof(nkey), "%d ", keynum);
                nkeylen = strlen(nkey);
-       }
-       len = nkeylen + strlen(key) + 1 + strlen(val) + 1;
-       if (set == NULL || set->len + len > set->size) {
+       } else
+               nkey[0] = 0;
+       keylen = strlen(key);
+       vallen = strlen(val);
+       len = nkeylen + keylen + 1 + vallen + 1;
+       while (set == NULL || set->len + len > set->size) {
                /* Need to re-alloc or alloc new */
-               if (!set) {
-                       set = newattr(NULL, len);
-                       *setp = set;
+               if (!set || (offset == 0 && len + set->len > MAX_ATTR_SIZE)) {
+                       /* Create a new set - which may exceed MAX_ATTR_SIZE */
+                       struct attrset *new = newattr(NULL, len);
+                       new->next = set;
+                       *setp = new;
+                       set = new;
                } else if (set->len + len <= MAX_ATTR_SIZE) {
                        /* Just make this block bigger */
                        set = newattr(set, set->len + len);
@@ -252,16 +344,23 @@ int attr_set_str(struct attrset **setp, char *key, char *val, int keynum)
                } else if (offset + len <= MAX_ATTR_SIZE) {
                        /* split following entries in separate block */
                        struct attrset *new = newattr(NULL, set->len - offset);
-
                        new->next = set->next;
                        set->next = new;
                        new->len = set->len - offset;
                        set->len = offset;
                        memcpy(new->attrs, set->attrs + offset,
                               new->len);
+               } else if (offset == set->len) {
+                       /* Need to add after here */
+                       setp = &set->next;
+                       set = *setp;
+                       offset = 0;
                } else {
-                       /* Split follow in entries and store new entry there */
-                       struct attrset *new = newattr(NULL, set->len - offset + len);
+                       /* offset must be > 0.
+                        * Split off following entries and try again there.
+                        */
+                       struct attrset *new = newattr(NULL,
+                                                     set->len - offset);
 
                        new->next = set->next;
                        set->next = new;
@@ -269,18 +368,26 @@ int attr_set_str(struct attrset **setp, char *key, char *val, int keynum)
                        set->len = offset;
                        memcpy(new->attrs, set->attrs + offset,
                               new->len);
+                       setp = &set->next;
                        set = new;
                        offset = 0;
                }
        }
-       memmove(set->attrs + offset + len, set->attrs + offset, set->len - offset);
-       strncpy(set->attrs + offset, nkey, nkeylen);
-       strcpy(set->attrs + offset + nkeylen, key);
-       strcpy(set->attrs + offset + nkeylen + strlen(key) + 1, val);
+       memmove(set->attrs + offset + len, set->attrs + offset,
+               set->len - offset);
+       memcpy(set->attrs + offset, nkey, nkeylen);
+       memcpy(set->attrs + offset + nkeylen, key, keylen + 1);
+       memcpy(set->attrs + offset + nkeylen + keylen + 1, val, vallen + 1);
        set->len += len;
        return cmp;
 }
 
+int attr_set_str(struct attrset **setp safe,
+                const char *key safe, const char *val)
+{
+       return attr_set_str_key(setp, key, val, -1);
+}
+
 #if defined(TEST_ATTR_ADD_DEL) || defined(TEST_ATTR_TRIM)
 void attr_dump(struct attrset *set)
 {
@@ -332,9 +439,9 @@ int main(int argc, char *argv[])
                char *v;
                switch(a->act) {
                case Add:
-                       attr_set_str(&set, a->key, a->val, -1); continue;
+                       attr_set_str(&set, a->key, a->val); continue;
                case Remove:
-                       if (attr_del(&set, a->key) == 0) {
+                       if (!attr_del(&set, a->key)) {
                                printf("Action %d: Remove %s: failed\n",
                                       i, a->key);
                                rv = 1;
@@ -359,26 +466,27 @@ int main(int argc, char *argv[])
 #endif
 
 /* Have versions that take and return numbers, '-1' for 'not found' */
-int attr_find_int(struct attrset *set, char *key)
+int attr_find_int(struct attrset *set, const char *key safe)
 {
-       char *val = attr_find(set, key);
+       const char *val = attr_find(set, key);
        unsigned long rv;
        char *end;
 
        if (!val)
                return -1;
        rv = strtoul(val, &end, 10);
-       if (end == val || *end)
+       if (!end || end == val || *end)
                return -1;
        return rv;
 }
 
-int attr_set_int(struct attrset **setp, char *key, int val)
+int attr_set_int(struct attrset **setp safe, const char *key safe, int val)
 {
-       char sval[22];
+       /* 3 digits per bytes, +1 for sign and +1 for trailing nul */
+       char sval[sizeof(int)*3+2];
 
-       sprintf(sval, "%d", val);
-       return attr_set_str(setp, key, sval, -1);
+       snprintf(sval, sizeof(sval), "%d", val);
+       return attr_set_str(setp, key, sval);
 }
 
 #ifdef TEST_ATTR_INT
@@ -404,7 +512,7 @@ int main(int argc, char *argv[])
 }
 #endif
 
-void attr_free(struct attrset **setp)
+void attr_free(struct attrset **setp safe)
 {
        struct attrset *set = *setp;
        *setp = NULL;
@@ -424,14 +532,12 @@ void attr_free(struct attrset **setp)
  *     At offset '0', an empty value deletes the key.
  */
 
-void attr_trim(struct attrset **setp, int nkey)
+void attr_trim(struct attrset **setp safe, int nkey)
 {
-       char key[22];
        int offset;
        struct attrset *set;
 
-       sprintf(key, "%d", nkey);
-       __attr_find(&setp, key, &offset, 0);
+       _attr_find(&setp, "", &offset, nkey);
        set = *setp;
        if (!set)
                return;
@@ -441,6 +547,9 @@ void attr_trim(struct attrset **setp, int nkey)
        attr_free(setp);
 }
 
+/* make a copy of 'set', keeping only attributes from 'nkey'
+ * onwards.
+ */
 struct attrset *attr_copy_tail(struct attrset *set, int nkey)
 {
        struct attrset *newset = NULL;
@@ -455,16 +564,37 @@ struct attrset *attr_copy_tail(struct attrset *set, int nkey)
                        v = set->attrs + i;
                        i += strlen(v) + 1;
                        n = atoi(k);
-
-                       if (n <= nkey && *v == '\0')
-                               v = NULL;
-                       attr_set_str(&newset, k, v, nkey);
+                       if (n < nkey)
+                               continue;
+                       while (*k && *k != ' ')
+                               k++;
+                       if (*k == ' ')
+                               k++;
+
+                       attr_set_str_key(&newset, k, v, n);
                }
        }
 
        return newset;
 }
 
+/* make a copy of 'set' */
+struct attrset *attr_copy(struct attrset *set)
+{
+       struct attrset *newset, **ep = &newset;
+
+       for (; set ; set = set->next) {
+               struct attrset *n = newattr(NULL, set->size);
+               memcpy(n->attrs, set->attrs, set->len);
+               n->len = set->len;
+               *ep = n;
+               ep = &n->next;
+       }
+       *ep = NULL;
+
+       return newset;
+}
+
 /* Collect the attributes in effect at a given pos and return
  * a new set with the new alternate numeric prefix, or nothing if '-1'
  */
@@ -487,7 +617,8 @@ struct attrset *attr_collect(struct attrset *set, unsigned int pos,
 
                        if (n > pos)
                                goto done;
-                       while (*e == ' ')
+                       /* FIXME shouldn't need to test 'e' */
+                       while (e && *e == ' ')
                                e++;
                        if (prefix >= 0) {
                                snprintf(kbuf, 512, "%d %s", prefix, e);
@@ -495,7 +626,8 @@ struct attrset *attr_collect(struct attrset *set, unsigned int pos,
                        }
                        if (*v == '\0')
                                v = NULL;
-                       attr_set_str(&newset, e, v, -1);
+                       if (!e) e = "FIXME";
+                       attr_set_str(&newset, e, v);
                }
        }
 done:
@@ -530,5 +662,5 @@ int main(int argc, char *argv[])
 #endif
 
 /*
-Iterator for set.
-*/
+ * Iterator for set.
+ */