]> git.neil.brown.name Git - edlib.git/blobdiff - core-attr.c
TODO: clean out done items.
[edlib.git] / core-attr.c
index 7cc9081547034289f69987ee85ba50245f70c2ad..ffce247fb245203b93b7bd6619bb83546be1940c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright Neil Brown ©2015-2019 <neil@brown.name>
+ * Copyright Neil Brown ©2015-2021 <neil@brown.name>
  * May be distributed under terms of GPLv2 - see file:COPYING
  *
  * Attributes.
@@ -42,6 +42,11 @@ 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
@@ -62,9 +67,9 @@ static struct attrset *safe 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 safe)
+static int getcmptok(const char **ap safe)
 {
-       char *a safe;
+       const char *a safe;
        char c;
        int i;
 
@@ -89,7 +94,7 @@ static int getcmptok(char **ap safe)
 /* 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 safe, char *b safe, int bnum)
+static int attr_cmp(const char *a safe, const char *b safe, int bnum)
 {
        while (*a && (*b || bnum >= 0)) {
                int ai, bi;
@@ -116,7 +121,7 @@ static int attr_cmp(char *a safe, char *b safe, 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},
@@ -143,7 +148,7 @@ int main(int argc, char *argv[])
 
 #endif
 
-static int __attr_find(struct attrset ***setpp safe, char *key safe,
+static int _attr_find(struct attrset ***setpp safe, const char *key safe,
                       int *offsetp safe, int keynum)
 {
        struct attrset **setp safe;
@@ -179,18 +184,11 @@ static int __attr_find(struct attrset ***setpp safe, char *key safe,
        return 1;
 }
 
-int attr_del(struct attrset * *setp safe, char *key safe)
+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 = safe_cast *setp;
        len = strlen(set->attrs + offset) + 1;
        len += strlen(set->attrs + offset + len) + 1;
@@ -202,14 +200,58 @@ int attr_del(struct attrset * *setp safe, char *key safe)
                *setp = set->next;
                free(set);
        }
-       return 1;
 }
 
-char *attr_get_str(struct attrset *set, char *key safe, 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 || !*setp)
                return NULL;
@@ -218,18 +260,18 @@ char *attr_get_str(struct attrset *set, char *key safe, int keynum)
        return set->attrs + offset;
 }
 
-char *attr_find(struct attrset *set, char *key safe)
+char *attr_find(struct attrset *set, const char *key safe)
 {
        return attr_get_str(set, key, -1);
 }
 
-char *attr_get_next_key(struct attrset *set, char *key safe, int keynum,
-                       char **valp safe)
+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);
-       char *val;
+       int cmp = _attr_find(&setp, key, &offset, keynum);
+       const char *val;
 
        if (cmp < 0)
                return NULL;
@@ -258,42 +300,43 @@ char *attr_get_next_key(struct attrset *set, char *key safe, int keynum,
        return key;
 }
 
-int attr_set_str_key(struct attrset **setp safe, char *key safe, char *val,
+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 = safe_cast *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);
        } else
                nkey[0] = 0;
-       len = nkeylen + strlen(key) + 1 + strlen(val) + 1;
-       if (set == NULL || set->len + len > set->size) {
+       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);
@@ -301,17 +344,23 @@ int attr_set_str_key(struct attrset **setp safe, char *key safe, char *val,
                } 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 */
+                       /* offset must be > 0.
+                        * Split off following entries and try again there.
+                        */
                        struct attrset *new = newattr(NULL,
-                                                     set->len - offset + len);
+                                                     set->len - offset);
 
                        new->next = set->next;
                        set->next = new;
@@ -319,20 +368,22 @@ int attr_set_str_key(struct attrset **setp safe, char *key safe, char *val,
                        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);
-       strcpy(set->attrs + offset, nkey);
-       strcpy(set->attrs + offset + nkeylen, key);
-       strcpy(set->attrs + offset + nkeylen + strlen(key) + 1, val);
+       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, char *key safe, char *val)
+int attr_set_str(struct attrset **setp safe,
+                const char *key safe, const char *val)
 {
        return attr_set_str_key(setp, key, val, -1);
 }
@@ -390,7 +441,7 @@ int main(int argc, char *argv[])
                case Add:
                        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;
@@ -415,9 +466,9 @@ 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 safe)
+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;
 
@@ -429,7 +480,7 @@ int attr_find_int(struct attrset *set, char *key safe)
        return rv;
 }
 
-int attr_set_int(struct attrset **setp safe, char *key safe, int val)
+int attr_set_int(struct attrset **setp safe, const char *key safe, int val)
 {
        /* 3 digits per bytes, +1 for sign and +1 for trailing nul */
        char sval[sizeof(int)*3+2];
@@ -486,7 +537,7 @@ void attr_trim(struct attrset **setp safe, int nkey)
        int offset;
        struct attrset *set;
 
-       __attr_find(&setp, "", &offset, nkey);
+       _attr_find(&setp, "", &offset, nkey);
        set = *setp;
        if (!set)
                return;
@@ -497,7 +548,7 @@ void attr_trim(struct attrset **setp safe, int nkey)
 }
 
 /* make a copy of 'set', keeping only attributes from 'nkey'
- * onwards.  'nkey' will be the new starting offset.
+ * onwards.
  */
 struct attrset *attr_copy_tail(struct attrset *set, int nkey)
 {
@@ -520,13 +571,30 @@ struct attrset *attr_copy_tail(struct attrset *set, int nkey)
                        if (*k == ' ')
                                k++;
 
-                       attr_set_str_key(&newset, k, v, n-nkey);
+                       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'
  */
@@ -594,5 +662,5 @@ int main(int argc, char *argv[])
 #endif
 
 /*
-Iterator for set.
-*/
+ * Iterator for set.
+ */