/*
- * 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.
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;
/* 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;
/* 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) {
#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, 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;
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,
*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);
} 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;
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)
{
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;
#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
}
#endif
-void attr_free(struct attrset **setp)
+void attr_free(struct attrset **setp safe)
{
struct attrset *set = *setp;
*setp = NULL;
* 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;
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;
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'
*/
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);
}
if (*v == '\0')
v = NULL;
- attr_set_str(&newset, e, v, -1);
+ if (!e) e = "FIXME";
+ attr_set_str(&newset, e, v);
}
}
done:
#endif
/*
-Iterator for set.
-*/
+ * Iterator for set.
+ */