/*
- * 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.
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
/* 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;
/* 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;
#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},
#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;
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;
*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;
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;
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);
} 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;
}
-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);
}
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;
#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;
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];
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'
*/
#endif
/*
-Iterator for set.
-*/
+ * Iterator for set.
+ */