/*
- * Copyright Neil Brown ©2015-2020 <neil@brown.name>
+ * Copyright Neil Brown ©2015-2021 <neil@brown.name>
* May be distributed under terms of GPLv2 - see file:COPYING
*
* Attributes.
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
#endif
-static int __attr_find(struct attrset ***setpp safe, const char *key safe,
+static int _attr_find(struct attrset ***setpp safe, const char *key safe,
int *offsetp safe, int keynum)
{
struct attrset **setp safe;
return 1;
}
-int attr_del(struct attrset * *setp safe, const 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;
*setp = set->next;
free(set);
}
- return 1;
+}
+
+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;
{
struct attrset **setp = &set;
int offset = 0;
- int cmp = __attr_find(&setp, key, &offset, keynum);
+ int cmp = _attr_find(&setp, key, &offset, keynum);
const char *val;
if (cmp < 0)
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);
} 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;
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;
}
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;
int offset;
struct attrset *set;
- __attr_find(&setp, "", &offset, nkey);
+ _attr_find(&setp, "", &offset, nkey);
set = *setp;
if (!set)
return;
}
/* 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)
{
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'
*/