You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2785 lines
74 KiB

#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "libslpattr.h"
#include "libslpattr_internal.h"
/* The strings used to represent non-string variables. */
#define BOOL_TRUE_STR "true"
#define BOOL_TRUE_STR_LEN 4
#define BOOL_FALSE_STR "false"
#define BOOL_FALSE_STR_LEN 5
/* The preamble to every variable. */
#define VAR_PREFIX '('
#define VAR_PREFIX_LEN 1
#define VAR_INFIX '='
#define VAR_INFIX_LEN 1
#define VAR_SUFFIX ')'
#define VAR_SUFFIX_LEN 1
#define VAR_SEPARATOR ','
#define VAR_SEPARATOR_LEN 1
/* The cost of the '(=)' for a non-keyword attribute. */
#define VAR_NON_KEYWORD_SEMANTIC_LEN VAR_PREFIX_LEN + VAR_INFIX_LEN + VAR_SUFFIX_LEN
/* The character with which to escape other characters. */
#define ESCAPE_CHARACTER '\\'
/* The number of characters required to escape a single character. */
#define ESCAPED_LEN 3
/* The preamble for opaques ('\FF') -- this is only used when the attributes
* are "put on the wire". */
#define OPAQUE_PREFIX "\\FF"
#define OPAQUE_PREFIX_LEN 3
/******************************************************************************
* Utility
*****************************************************************************/
/* Tests a character to see if it reserved (as defined in RFC 2608, p11). */
#define IS_RESERVED(x) \
(((x) == '(' || (x) == ')' || (x) == ',' || (x) == '\\' || (x) == '!' || (x) == '<' \
|| (x) == '=' || (x) == '>' || (x) == '~') || \
((((char)0x01 <= (x)) && ((char)0x1F >= (x))) || ((x) == (char)0x7F)))
#define IS_INVALID_VALUE_CHAR(x) \
IS_RESERVED(x)
#define IS_INVALID_TAG_CHAR(x) \
(IS_RESERVED(x) \
|| ((x) == '*') || \
((x) == (char)0x0D) || ((x) == (char)0x0A) || ((x) == (char)0x09) || ((x) == '_'))
#define IS_VALID_TAG_CHAR(x) !IS_INVALID_TAG_CHAR(x)
/* Tests a character to see if it is in set of known hex characters. */
#define IS_VALID_HEX(x) ( ((x >= '0') && (x <= '9')) /* Number */ \
|| ((x >= 'A') && (x <= 'F')) /* ABCDEF */ \
|| ((x >= 'a') && (x <= 'f')) /* abcdef */ \
)
/* Tests a character to see if it's a digit. */
#define IS_DIGIT(x) ((x) >= '0' && (x) <= '9')
/* Find the end of a tag, while checking that said tag is valid.
*
* Returns: Pointer to the character immediately following the end of the tag,
* or NULL if the tag is improperly formed.
*/
char const *find_tag_end(const char *tag)
{
char const *cur = tag; /* Pointer into the tag for working. */
while(*cur)
{
if(IS_INVALID_TAG_CHAR(*cur))
{
break;
}
cur++;
}
return cur;
}
/* Unescapes an escaped character.
*
* val should point to the escape character starting the value.
*/
char unescape(char d1, char d2)
{
assert(isxdigit((int) d1));
assert(isxdigit((int) d2));
if((d1 >= 'A') && (d1 <= 'F'))
d1 = d1 - 'A' + 0x0A;
else if((d1 >= 'a') && (d1 <= 'f'))
d1 = d1 - 'a' + 0x0A;
else
d1 = d1 - '0';
if((d2 >= 'A') && (d2 <= 'F'))
d2 = d2 - 'A' + 0x0A;
else if((d2 >= 'a') && (d2 <= 'f'))
d2 = d2 - 'a' + 0x0A;
else
d2 = d2 - '0';
return d2 + (d1 * 0x10);
}
/* Unescapes a string.
*
* Params:
* dest -- (IN) Where to write
* src -- (IN) Unescaped string
* len -- (IN) length of src
* unescaped_len -- (OUT) number of characters in unescaped
*
* Returns: Pointer to start of unescaped string. If an error occurs, NULL is
* returned (an error consists of an escaped value being truncated).
*/
char *unescape_into(char *dest, const char *src, int len, int
*unescaped_len)
{
char *start, *write;
int i;
assert(dest);
assert(src);
write = start = dest;
for(i = 0; i < len; i++, write++)
{
if(src[i] == ESCAPE_CHARACTER)
{
/*** Check that the characters are legal, and that the value has
* not been truncated.
***/
if((i + 2 < len) && isxdigit((int) src[i+1])
&& isxdigit((int) src[i+2]))
{
*write = unescape(src[i+1], src[i+2]);
i += 2;
}
else
{
return NULL;
}
}
else
{
*write = src[i];
}
}
/* Report the unescaped size. */
if(unescaped_len != NULL)
{
*unescaped_len = write - start;
}
return start;
}
/* Finds the end of a value list, while checking that the value contains legal
* characters.
*
* PARAMS:
* value -- (IN) The start of the value list
* value_count -- (OUT) The number of values in the value list
* type -- (OUT) The type of the value list
* unescaped_size -- (OUT) The size of the unescaped value list. ASSUMING THAT THE LIST IS EITHER OPAQUE OR STRING!
* cur -- (OUT) End of the parse.
*
* Returns: 0 on parse error. 1 on valid parse.
*/
int find_value_list_end(char const *value, int *value_count, SLPType
*type, int *unescaped_len, char const **cur)
{
enum
{
START_VAL, /* We're at the start of a value */
IN_VAL /* We're in a val. */
} state = START_VAL; /* The state of the current read. */
enum
{
TYPE_UNKNOWN = -12, /* Could be anything. */
TYPE_INT = SLP_INTEGER, /* Either an int or a string. */
TYPE_OPAQUE = SLP_OPAQUE, /* Definitely an opaque. */
TYPE_STR = SLP_STRING, /* Definitely a string. */
TYPE_BOOL = SLP_BOOLEAN /* A bool, but it could be a string. */
} type_guess = TYPE_UNKNOWN; /* The current possible values for the type. */
*value_count = 1;
*unescaped_len = 0;
*cur = value;
while(**cur)
{
if(**cur == '\\')
{
if(state == START_VAL)
{
/*** Test if we're starting an opaque. ***/
(*cur)++;
if((**cur) != '0')
{
/* Panic: truncated escaped value. */
return 0;
}
(*cur)++;
if((**cur) != '0')
{
/* Panic: truncated escaped value. */
return 0;
}
/*** We're starting an opaque. Ensure proper typing. ***/
if(type_guess == TYPE_UNKNOWN)
{
type_guess = TYPE_OPAQUE;
}
else if(type_guess != TYPE_OPAQUE)
{
/* An opaque is mixed in with non-opaques. Fail. */
return 0;
}
}
else
{
/*** We're in the middle of a value. ***/
/** Check that next two characters are valid. **/
(*cur)++;
if(!IS_VALID_HEX(**cur))
{
return 0;
}
(*cur)++;
if(!IS_VALID_HEX(**cur))
{
return 0;
}
(*unescaped_len)++;
}
state = IN_VAL;
}
else if(**cur == VAR_SEPARATOR)
{
/* A separator. */
/** Check for empty values. **/
if(state != IN_VAL)
{
return 0; /* ERROR! commas side-by-side. */
}
state = START_VAL;
/** Type check. **/
if(type_guess == TYPE_BOOL)
{
/* Bools can only have _one_ value. */
/* Devolve to string. */
type_guess = TYPE_STR;
}
(*value_count)++;
}
else if(**cur == VAR_SUFFIX)
{
/* Nous sommes fini. */
break;
}
else if(IS_INVALID_VALUE_CHAR(**cur))
{
/* Bad char. */
return 0;
}
else
{
/* Normal case */
/*** Ensure that the character is consistent with its type. ***/
/** Opaque. **/
if(type_guess == TYPE_OPAQUE)
{
/* Type error! The string starts with a \00, but has a bare character somewhere following. */
return 0;
}
/** Int. **/
else if(type_guess == TYPE_INT)
{
if(!(IS_DIGIT(**cur) || (state == START_VAL && **cur == '-')))
{
/* Devolve to a string. */
type_guess = TYPE_STR;
}
}
/** Bool. **/
else if(type_guess == TYPE_BOOL)
{
if(*unescaped_len < BOOL_TRUE_STR_LEN && **cur ==
BOOL_TRUE_STR[*unescaped_len])
{
/* Do nothing. It's valid. */
}
else if(*unescaped_len < BOOL_FALSE_STR_LEN && **cur ==
BOOL_FALSE_STR[*unescaped_len])
{
/* Do nothing. It's also valid. */
}
else
{
/* Devolve to a string. */
type_guess = TYPE_STR;
}
}
/** Unknown. **/
else if(type_guess == TYPE_UNKNOWN)
{
if(IS_DIGIT(**cur) || (state == START_VAL && **cur == '-'))
{
type_guess = TYPE_INT;
}
else if(state == START_VAL && (BOOL_TRUE_STR[0] == **cur || **cur == BOOL_FALSE_STR[0]))
{
type_guess = TYPE_BOOL;
}
else
{
type_guess = TYPE_STR;
}
}
(*unescaped_len)++;
state = IN_VAL;
}
(*cur)++;
}
if (type_guess == TYPE_UNKNOWN)
{
return 0; /* empty */
}
*type = type_guess;
return 1;
}
/* Finds the end of a value, while checking that the value contains legal
* characters.
*
* Returns: see find_tag_end().
*/
char *find_value_end(char *value)
{
char *cur = value; /* Pointer into the value string. */
while(*cur)
{
if(IS_INVALID_VALUE_CHAR(*cur) && (*cur != '\\'))
{
break;
}
cur++;
}
return cur;
}
/* Find the number of digits (base 10) necessary to represent an integer.
*
* Returns the number of digits.
*/
int count_digits(int number)
{
int count = (number < 0) ? 1 : 0; /* do we need a negative sign ? */
/* special case: 0 */
if (number == 0)
return 1;
/* poor man's abs() function */
number = (number < 0 ) ? -number : number;
/* count number of digits required; this only works with integers */
for ( ; number > 0; number /= 10) {
count++;
}
return count;
}
/* Verifies that a tag contains only legal characters. */
SLPBoolean is_valid_tag(const char *tag)
{
/* TODO Check. */
return SLP_TRUE;
}
/* A boolean expression that tests a character to see if it must be escaped.
*/
#define ATTRIBUTE_RESERVED_TEST(x) \
(x == '(' || x == ')' || x == ',' || x == '\\' || x == '!' || x == '<' \
|| x == '=' || x == '<' || x == '=' || x == '>' || x == '~' || x == '\0')
/* Tests a character to see if it should be escaped. To be used for everything
* but opaques. */
SLPBoolean is_legal_slp_char(const char to_test)
{
if(ATTRIBUTE_RESERVED_TEST(to_test))
{
return SLP_FALSE;
}
return SLP_TRUE;
}
/* Tests a character to see if it should be escaped for an opaque. */
SLPBoolean opaque_test(const char to_test)
{
return SLP_FALSE;
}
/* Find the size of an unescaped string (given the escaped string).
*
* Note that len must be positive.
*
* Returns: If positive, the length of the string. If negative, there is some
* sort of error.
*/
int find_unescaped_size(const char *str, int len)
{
int i;
int size;
assert(len > 0);
size = len;
for(i = 0; i < len; i++)
{
if(str[i] == ESCAPE_CHARACTER)
{
size -= ESCAPED_LEN - 1; /* -1 for the ESCAPE_CHARACTER. */
}
}
return size;
}
/* Find the size of an escaped string.
*
* The "optional" len argument is the length of the string. If it is
* negative, the function treats the string as a null-terminated string. If it
* is positive, the function will read exactly that number of characters.
*/
int find_escaped_size(const char *str, int len)
{
int i; /* Index into str. */
int escaped_size; /* The size of the thingy. */
i =0;
escaped_size = 0;
if(len < 0)
{
/***** String is null-terminated. *****/
for(i = 0; str[i]; i++)
{
if(is_legal_slp_char(str[i]) == SLP_TRUE)
{
escaped_size++;
}
else
{
escaped_size += ESCAPED_LEN;
}
}
}
else
{
for(i = 0; i < len; i++)
{
if(is_legal_slp_char(str[i]) == SLP_TRUE)
{
escaped_size++;
}
else
{
escaped_size += ESCAPED_LEN;
}
}
}
return escaped_size;
}
/* Escape a single character. Writes the escaped value into dest, and
* increments dest.
*
* NOTE: Most of this code is stolen from Dave McCormack's SLPEscape() code.
* (For OpenSLP).
*/
void escape(char to_escape, char **dest, SLPBoolean (test_fn)(const char))
{
char hex_digit;
if(test_fn(to_escape) == SLP_FALSE)
{
/* Insert the escape character. */
**dest = ESCAPE_CHARACTER;
(*dest)++;
/* Do the first digit. */
hex_digit = (to_escape & 0xF0)/0x0F;
if((hex_digit >= 0) && (hex_digit <= 9))
**dest = hex_digit + '0';
else
**dest = hex_digit + 'A' - 0x0A;
(*dest)++;
/* Do the last digit. */
hex_digit = to_escape & 0x0F;
if((hex_digit >= 0) && (hex_digit <= 9))
**dest = hex_digit + '0';
else
**dest = hex_digit + 'A' - 0x0A;
(*dest)++;
}
else
{
/* It's legal. */
**dest = to_escape;
(*dest)++;
}
}
/* Escape the passed string (src), writing it into the other passed string
* (dest).
*
* If the len argument is negative, the src is treated as null-terminated,
* otherwise that length is escaped.
*
* Returns a pointer to where the addition has ended.
*/
char *escape_into(char *dest, char *src, int len)
{
char *cur_dest; /* Current character in dest. */
cur_dest = dest;
if(len < 0)
{
/* Treat as null terminated. */
char *cur_src; /* Current character in src. */
cur_src = src;
for(; *cur_src; cur_src++)
{
escape(*cur_src, &cur_dest, is_legal_slp_char);
}
}
else
{
/* known length. */
int i; /* Index into src. */
for(i = 0; i < len; i++)
{
escape(src[i], &cur_dest, is_legal_slp_char);
}
}
return cur_dest;
}
/* Special case for escaping opaque strings. Escapes _every_ character in the
* string.
*
* Note that the size parameter _must_ be defined.
*
* Returns a pointer to where the addition has ended.
*/
char *escape_opaque_into(char *dest, char *src, int len)
{
int i; /* Index into src. */
char *cur_dest;
cur_dest = dest;
for(i = 0; i < len; i++)
{
escape(src[i], &cur_dest, opaque_test);
}
return cur_dest;
}
/******************************************************************************
*
* Individual values
*
* What is a value?
*
* In SLP an individual attribute can be associated with a list of values. A
* value is the data associated with a tag. Depending on the type of
* attribute, there can be zero, one, or many values associated with a single
* tag.
*****************************************************************************/
/* See libslpattr_internal.h for implementation. */
/* Create and initialize a new value.
*
* Params:
* extra -- amount of memory to allocate in addition to that needed for the value. This memory can be found at (return_value + sizeof(value_t))
*/
value_t *value_new(int extra)
{
value_t *value = NULL;
value = (value_t *)malloc(sizeof(value_t) + extra);
if(value == NULL)
return NULL;
value->next = NULL;
value->data.va_str = NULL;
value->escaped_len = -1;
value->unescaped_len = -1;
value->next_chunk = NULL;
value->last_value_in_chunk = value;
return value;
}
/* Destroy a value. */
void value_free(value_t *value)
{
assert(value->next == NULL);
free(value);
}
/******************************************************************************
*
* Individual attributes (vars)
*
* What is a var?
*
* A var is a variable tag that is associated with a list of values. Zero or
* more vars are kept in a single SLPAttributes object. Each value stored in
* a var is kept in a value struct.
*****************************************************************************/
/* See libslpattr_internal.h for struct. */
/* Create a new variable.
*
* FIXME should take tag_len as an argument
*/
var_t *var_new(char *tag, int tag_len)
{
var_t *var; /* Variable being created. */
assert(tag != NULL);
/***** Allocate. *****/
var = (var_t *)malloc(sizeof(var_t) + tag_len + 1); /* +1 for null. */
if(var == NULL)
return NULL;
/***** Initialize. *****/
var->next = NULL;
var->tag_len = tag_len;
var->tag = ((char *)var) + sizeof(var_t);
memcpy((void *)var->tag, tag, var->tag_len);
((char *)(var->tag))[var->tag_len] = 0;
var->type = -1;
var->list = NULL;
var->list_size = 0;
var->modified = SLP_TRUE;
return var;
}
/* Destroy a value list. Note that the var is not free()'d, only reset. */
void var_list_destroy(var_t *var)
{
value_t *value;
value_t *to_free; /* A pointer back in the value list to free. */
/***** Check for data. *****/
if(var->list == NULL)
{
assert(var->list_size == 0);
return;
}
/***** Burrow through the value list deleting every chunk of memory behind us as we go. *****/
value = var->list;
to_free = NULL;
while(value)
{
to_free = value;
value = value->next_chunk;
free(to_free);
}
/***** Reset the list. *****/
var->list = NULL;
var->list_size = 0;
}
/* Frees a variable. */
void var_free(var_t *var)
{
/***** Sanity check. *****/
assert(var->next == NULL);
/***** Free variable. *****/
var_list_destroy(var);
free(var);
}
/* Adds a value to a variable. */
SLPError var_insert(var_t *var, value_t *value, SLPInsertionPolicy policy)
{
assert(policy == SLP_ADD || policy == SLP_REPLACE);
if(value == NULL)
{
return SLP_OK;
}
if(policy == SLP_REPLACE)
{
var_list_destroy(var);
}
/* Update list. */
value->last_value_in_chunk->next = var->list;
value->next_chunk = var->list; /* Update the memory list too. */
var->list = value;
var->list_size++;
/* Set mod flag.*/
var->modified = SLP_TRUE;
return SLP_OK;
}
/******************************************************************************
*
* All the attributes.
*
*****************************************************************************/
/*
* SLPAttrAlloc() creates and initializes a new instance of SLPAttributes.
*/
SLPError SLPAttrAlloc(
const char *lang,
const FILE *template_h,
const SLPBoolean strict,
SLPAttributes *slp_attr_h
)
{
struct xx_SLPAttributes **slp_attr;
slp_attr = (struct xx_SLPAttributes **)slp_attr_h;
/***** Sanity check *****/
if(strict == SLP_FALSE && template_h != NULL)
{
/* We can't be strict if we don't have a template. */
return SLP_PARAMETER_BAD;
}
if(strict != SLP_FALSE)
{
return SLP_NOT_IMPLEMENTED;
}
if(template_h != NULL)
{
return SLP_NOT_IMPLEMENTED;
}
/***** Create. *****/
(*slp_attr) = (struct xx_SLPAttributes *)malloc( sizeof(struct xx_SLPAttributes) );
if(*slp_attr == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
/***** Initialize *****/
(*slp_attr)->strict = SLP_FALSE; /* FIXME Add templates. */
(*slp_attr)->lang = strdup(lang); /* free()'d in SLPAttrFree(). */
(*slp_attr)->attrs = NULL;
(*slp_attr)->attr_count = 0;
/***** Report. *****/
return SLP_OK;
}
SLPError attr_destringify(struct xx_SLPAttributes *slp_attr, const char *str, SLPInsertionPolicy);
/* Allocates a new attribute list from a string. */
SLPError SLPAttrAllocStr(
const char *lang,
const FILE *template_h,
const SLPBoolean strict,
SLPAttributes *slp_attr_h,
const char *str
)
{
SLPError err;
err = SLPAttrAlloc(lang, template_h, strict, slp_attr_h);
if(err != SLP_OK)
{
return err;
}
err = attr_destringify((struct xx_SLPAttributes*)*slp_attr_h, str, SLP_ADD);
if(err != SLP_OK)
{
SLPAttrFree(*slp_attr_h);
}
return err;
}
/* Destroys the passed SLPAttributes().
*/
void SLPAttrFree(SLPAttributes slp_attr_h)
{
struct xx_SLPAttributes *slp_attr;
slp_attr = (struct xx_SLPAttributes *)slp_attr_h;
/***** Free held resources. *****/
while(slp_attr->attrs)
{
var_t *attr = slp_attr->attrs;
slp_attr->attrs = attr->next;
attr->next = NULL;
var_free(attr);
}
free(slp_attr->lang);
slp_attr->lang = NULL;
/***** Free the handle *****/
free(slp_attr);
slp_attr = NULL;
}
/* Insert a variable into the var list. */
void attr_add(struct xx_SLPAttributes *slp_attr, var_t *var)
{
var->next = slp_attr->attrs;
slp_attr->attrs = var;
slp_attr->attr_count++;
}
/* Find a variable by its tag.
*
* Returns a NULL if the value could not be found.
*/
var_t *attr_val_find_str(struct xx_SLPAttributes *slp_attr, const char *tag, int tag_len)
{
var_t *var;
var = slp_attr->attrs;
while(var)
{
/* Per RFC 2165 (Section 20.5 para 1), RFC 2608 (Section 6.4 para 3),
* attr-tags are supposed to be case insensitive.
* Using strncasecmp() so that comparision of tags are case-insensitive
* atleast inside the ASCII range.
*/
if(var->tag_len == (unsigned)tag_len && strncasecmp(var->tag, tag, tag_len) == 0)
{
return var;
}
var = var->next;
}
return NULL;
}
/* Test a variable's type. Returns SLP_OK if the match is alright, or some
* other error code (meant to be forwarded to the application) if the match is
* bad.
*/
SLPError attr_type_verify(struct xx_SLPAttributes *slp_attr, var_t *var, SLPType type)
{
assert(var->type != -1); /* Check that it's been set. */
if(var->type == type)
{
return SLP_OK;
}
return SLP_TYPE_ERROR; /* FIXME: Check against template. */
}
/******************************************************************************
*
* Setting attributes
*
*****************************************************************************/
/*****************************************************************************/
SLPError generic_set_val(struct xx_SLPAttributes *slp_attr, const char *tag, int tag_len, value_t *value, SLPInsertionPolicy policy, SLPType attr_type)
/* Finds and sets the value named in tag. */
/*
* slp_attr - the attr object to add to.
* tag - the name of the tag to add to.
* value - the already-allocated value object with fields set
* policy - the policy to use when inserting.
* attr_type - the type of the value being added.
*****************************************************************************/
{
var_t *var;
/***** Create a new attribute. *****/
if((var = attr_val_find_str(slp_attr, tag, strlen(tag))) == NULL)
{
/*** Couldn't find a value with this tag. Make a new one. ***/
var = var_new((char *)tag, tag_len);
if(var == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
var->type = attr_type;
/** Add variable to list. **/
attr_add(slp_attr, var);
}
else
{
SLPError err;
/*** The attribute already exists. ***/
/*** Verify type. ***/
err = attr_type_verify(slp_attr, var, attr_type);
if(err == SLP_TYPE_ERROR && policy == SLP_REPLACE)
{
var_list_destroy(var);
var->type = attr_type;
}
else if(err != SLP_OK)
{
value_free(value);
return err;
}
}
/***** Set value *****/
var_insert(var, value, policy);
return SLP_OK;
}
/* Set a boolean attribute. */
SLPError SLPAttrSet_bool(
SLPAttributes attr_h,
const char *tag,
SLPBoolean val
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
value_t *value = NULL;
int escaped_len;
/***** Sanity check. *****/
if(val != SLP_TRUE && val != SLP_FALSE)
{
return SLP_PARAMETER_BAD;
}
if(is_valid_tag(tag) == SLP_FALSE)
{
return SLP_TAG_BAD;
}
/***** Set the initial (and only) value. *****/
/**** Create ****/
value = value_new(0);
assert(value);
/**** Set escaped information. ****/
if(val == SLP_TRUE)
{
escaped_len = BOOL_TRUE_STR_LEN;
}
else
{
escaped_len = BOOL_FALSE_STR_LEN;
}
value->escaped_len = escaped_len;
value->data.va_bool = val;
/**** Set the value and return. ****/
return generic_set_val(slp_attr, tag, (int)strlen(tag), value, SLP_REPLACE, SLP_BOOLEAN);
}
/* Sets a string attribute. */
SLPError SLPAttrSet_str(
SLPAttributes attr_h,
const char *tag,
const char *val,
SLPInsertionPolicy policy
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
value_t *value;
int unescaped_len;
/***** Sanity check. *****/
if(is_valid_tag(tag) == SLP_FALSE)
{
return SLP_TAG_BAD;
}
if(val == NULL)
{
return SLP_PARAMETER_BAD;
}
/***** Create new value. *****/
unescaped_len = strlen(val);
value = value_new(unescaped_len);
assert(value);
/**** Copy data. ****/
value->data.va_str = ((char *)value) + sizeof(value_t);
memcpy(value->data.va_str, val, unescaped_len);
/**** Set lengths. ****/
value->unescaped_len = unescaped_len;
value->escaped_len = find_escaped_size(value->data.va_str, unescaped_len);
return generic_set_val(slp_attr, tag, (int)strlen(tag), value, policy, SLP_STRING);
}
/* Sets a keyword attribute. Takes a non-null terminated string. */
SLPError SLPAttrSet_keyw_len(
SLPAttributes attr_h,
const char *tag,
int tag_len
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
/***** Sanity check. *****/
if(is_valid_tag(tag) == SLP_FALSE)
{
return SLP_TAG_BAD;
}
return generic_set_val(slp_attr, tag, tag_len, NULL, SLP_REPLACE, SLP_KEYWORD);
}
SLPError SLPAttrSet_keyw(
SLPAttributes attr_h,
const char *tag
)
{
return SLPAttrSet_keyw_len(attr_h, tag, (int)strlen(tag));
}
/* Sets an integer attribute. */
SLPError SLPAttrSet_int(
SLPAttributes attr_h,
const char *tag,
int val,
SLPInsertionPolicy policy
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
value_t *value;
/***** Sanity check. *****/
if(is_valid_tag(tag) == SLP_FALSE)
{
return SLP_TAG_BAD;
}
/***** Create new value. *****/
value = value_new(0);
if(value == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
/**** Set ****/
value->data.va_int = val;
value->escaped_len = count_digits(value->data.va_int);
assert(value->escaped_len > 0);
return generic_set_val(slp_attr, tag, (int)strlen(tag), value, policy, SLP_INTEGER);
}
/* Set an opaque attribute. */
SLPError SLPAttrSet_opaque(
SLPAttributes attr_h,
const char *tag,
const char *val,
const unsigned int len,
SLPInsertionPolicy policy
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
value_t *value;
/***** Sanity check. *****/
if(is_valid_tag(tag) == SLP_FALSE)
{
return SLP_TAG_BAD;
}
if(val == NULL)
{
return SLP_PARAMETER_BAD;
}
/***** Create a new attribute. *****/
value = value_new(len);
if(value == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
memcpy((void *)value->data.va_str, val, len);
value->unescaped_len = len;
value->escaped_len = (len * ESCAPED_LEN) + OPAQUE_PREFIX_LEN;
return generic_set_val(slp_attr, tag, (int)strlen(tag), value, policy, SLP_OPAQUE);
}
SLPError SLPAttrStore(struct xx_SLPAttributes *slp_attr,
const char *tag,
const char *val,
int len,
SLPInsertionPolicy policy
);
/* Set an attribute of unknown type.
*
* Note that the policy in this case is a special case: If the policy is
* SLP_REPLACE, we delete the current list and replace it with the passed
* value. If it's a multivalue list, we replace the current value with the
* ENTIRE passed list.
*
* FIXME Should we "elide" whitespace?
*/
SLPError SLPAttrSet_guess(
SLPAttributes attr_h,
const char *tag,
const char *val,
SLPInsertionPolicy policy
)
{
SLPError err;
int len;
const char *cur, *end;
/***** Sanity check. *****/
if(is_valid_tag(tag) == SLP_FALSE)
{
return SLP_TAG_BAD;
}
if(val == NULL)
{
return SLP_PARAMETER_BAD;
}
/*****
* If we have a replace policy and we're inserting a multivalued list,
* the values will clobber each other. Therefore if we have a replace, we
* delete the current list, and use an add policy.
*****/
if(policy == SLP_REPLACE)
{
var_t *var;
var = attr_val_find_str((struct xx_SLPAttributes *)attr_h, tag, strlen(tag));
if(var)
{
var_list_destroy(var);
}
}
/***** Check for multivalue list. *****/
cur = val;
do
{
end = strchr(cur, VAR_SEPARATOR);
if(end == NULL)
{
len = strlen(cur);
}
else
{
/*** It's multivalue. ***/
len = end - cur;
}
err = SLPAttrStore((struct xx_SLPAttributes *)attr_h, tag, cur, len, SLP_ADD);
if(err != SLP_OK)
{
/* FIXME Ummm. Should we return or ignore? */
return err;
}
cur = end + VAR_SEPARATOR_LEN;
} while(end);
/***** Return *****/
return SLP_OK;
}
/******************************************************************************
*
* Getting attributes
*
*****************************************************************************/
/* Get the value of a boolean attribute. Note that it _cannot_ be multivalued.
*/
SLPError SLPAttrGet_bool(
SLPAttributes attr_h,
const char *tag,
SLPBoolean *val
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
var_t *var;
var = attr_val_find_str(slp_attr, tag, strlen(tag));
/***** Check that the tag exists. *****/
if(var == NULL)
{
return SLP_TAG_ERROR;
}
/* TODO Verify type against template. */
/***** Verify type. *****/
if(var->type != SLP_BOOLEAN)
{
return SLP_TYPE_ERROR;
}
assert(var->list != NULL);
*val = var->list->data.va_bool;
return SLP_OK;
}
/* Get the value of a keyword attribute. Since keywords either exist or don't
* exist, no value is passed out. Instead, if the keyword exists, an SLP_OK is
* returned, if it doesn't exist, an SLP_TAG_ERROR is returned. If the tag
* does exist, but is associated with a non-keyword attribute, SLP_TYPE_ERROR
* is returned.
*/
SLPError SLPAttrGet_keyw(
SLPAttributes attr_h,
const char *tag
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
var_t *var;
var = attr_val_find_str(slp_attr, tag, strlen(tag));
/***** Check that the tag exists. *****/
if(var == NULL)
{
return SLP_TAG_ERROR;
}
/* TODO Verify type against template. */
/***** Verify type. *****/
if(var->type != SLP_KEYWORD)
{
return SLP_TYPE_ERROR;
}
assert(var->list == NULL);
return SLP_OK;
}
/* Get an integer value. Since integer attributes can be multivalued, an array
* is returned that contains all values corresponding to the given tag.
*
*
* Note: On success, an array of SLP_INTEGERs is created. It is the caller's
* responsibility to free the memory returned through val.
*
* Returns:
* SLP_OK
* Returned if the attribute is found. The array containing the values is
* placed in val, and size is set to the number of values in val.
* SLP_TYPE_ERROR
* Returned if the tag exists, but the associated value is not of type
* SLP_INTEGER.
* SLP_MEMORY_ALLOC_FAILED
* Memory allocation failed.
*/
SLPError SLPAttrGet_int(
SLPAttributes attr_h,
const char *tag,
int **val,
int *size
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
var_t *var;
value_t *value;
int i;
var = attr_val_find_str(slp_attr, tag, strlen(tag));
/***** Check that the tag exists. *****/
if(var == NULL)
{
return SLP_TAG_ERROR;
}
/* TODO Verify type against template. */
/***** Verify type. *****/
if(var->type != SLP_INTEGER)
{
return SLP_TYPE_ERROR;
}
/***** Create return value. *****/
*size = var->list_size;
*val = (int *)malloc( sizeof(int) * var->list_size );
if(*val == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
/***** Set values *****/
assert(var->list != NULL);
value = var->list;
for(i = 0; i < var->list_size; i++, value = value->next)
{
assert(value != NULL);
(*val)[i] = value->data.va_int;
}
return SLP_OK;
}
/* Get string values. Since string attributes can be multivalued, an array
* is returned that contains all values corresponding to the given tag.
*
*
* Note: On success, an array of SLP_STRINGs is created. It is the caller's
* responsibility to free the memory returned through val. Note that the array
* referencing the strings is allocated separately from each string value,
* meaning that each value must explicitly be deallocated.
*
* Returns:
* SLP_OK
* Returned if the attribute is found. The array containing the values is
* placed in val, and size is set to the number of values in val.
* SLP_TYPE_ERROR
* Returned if the tag exists, but the associated value is not of type
* SLP_INTEGER.
* SLP_MEMORY_ALLOC_FAILED
* Memory allocation failed.
*/
SLPError SLPAttrGet_str(
SLPAttributes attr_h,
const char *tag,
char ***val,
int *size
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
var_t *var;
value_t *value;
int i;
var = attr_val_find_str(slp_attr, tag, strlen(tag));
/***** Check that the tag exists. *****/
if(var == NULL)
{
return SLP_TAG_ERROR;
}
/* TODO Verify type against template. */
/***** Verify type. *****/
if(var->type != SLP_STRING)
{
return SLP_TYPE_ERROR;
}
/***** Create return value. *****/
*size = var->list_size;
*val = (char **)malloc( sizeof(char *) * var->list_size );
if(*val == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
/***** Set values *****/
assert(var->list != NULL);
value = var->list;
for(i = 0; i < var->list_size; i++, value = value->next)
{
assert(value != NULL);
/* (*val)[i] = strdup(value->data.va_str); */
(*val)[i] = malloc(value->unescaped_len + 1);
assert((*val)[i] != NULL);
memcpy((*val)[i], value->data.va_str, value->unescaped_len);
(*val)[i][value->unescaped_len] = 0;
}
return SLP_OK;
}
/* Get opaque values. Since opaque attributes can be multivalued, an array
* is returned that contains all values corresponding to the given tag.
*
*
* Note: On success, an array of SLP_OPAQUEs is created. It is the caller's
* responsibility to free the memory returned through val. Note that the array
* referencing the opaques is allocated separately from each opaque struct,
* and from the corresponding opaque value, meaning that each value must
* explicitly be deallocated, as must each opaque struct.
*
* Returns:
* SLP_OK
* Returned if the attribute is found. The array containing the values is
* placed in val, and size is set to the number of values in val.
* SLP_TYPE_ERROR
* Returned if the tag exists, but the associated value is not of type
* SLP_INTEGER.
* SLP_MEMORY_ALLOC_FAILED
* Memory allocation failed.
*/
SLPError SLPAttrGet_opaque(
SLPAttributes attr_h,
const char *tag,
SLPOpaque ***val,
int *size
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
var_t *var;
value_t *value;
int i;
var = attr_val_find_str(slp_attr, tag, strlen(tag));
/***** Check that the tag exists. *****/
if(var == NULL)
{
return SLP_TAG_ERROR;
}
/* TODO Verify type against template. */
/***** Verify type. *****/
if(var->type != SLP_OPAQUE)
{
return SLP_TYPE_ERROR;
}
/***** Create return value. *****/
*size = var->list_size;
*val = (SLPOpaque **)malloc( sizeof(SLPOpaque *) * var->list_size );
if(*val == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
/***** Set values *****/
assert(var->list != NULL);
value = var->list;
for(i = 0; i < var->list_size; i++, value = value->next)
{
assert(value != NULL);
(*val)[i] = (SLPOpaque *)malloc( sizeof(SLPOpaque) );
if((*val)[i]->data == NULL)
{
/* TODO Deallocate everything and return. */
return SLP_MEMORY_ALLOC_FAILED;
}
(*val)[i]->len = value->unescaped_len;
(*val)[i]->data = (char *)malloc( value->unescaped_len );
if((*val)[i]->data == NULL)
{
/* TODO Deallocate everything and return. */
return SLP_MEMORY_ALLOC_FAILED;
}
memcpy((*val)[i]->data, value->data.va_str, value->unescaped_len);
}
return SLP_OK;
}
/* Finds the type of the given attribute.
*
* Returns:
* SLP_OK
* If the attribute is set. The type is returned through the type
* parameter.
* SLP_TAG_ERROR
* If the attribute is not set.
*/
SLPError SLPAttrGetType_len(SLPAttributes attr_h, const char *tag, int tag_len, SLPType *type)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
var_t *var;
var = attr_val_find_str(slp_attr, tag, tag_len);
/***** Check that the tag exists. *****/
if(var == NULL)
{
return SLP_TAG_ERROR;
}
if(type != NULL)
{
*type = var->type;
}
return SLP_OK;
}
SLPError SLPAttrGetType(SLPAttributes attr_h, const char *tag, SLPType *type)
{
return SLPAttrGetType_len(attr_h, tag, strlen(tag), type);
}
#if 1 /* Jim Meyer's byte allignment code */
/******************************************************************************
*
* Fix memory alignment
*
*****************************************************************************/
char *fix_memory_alignment(char *p)
{
unsigned long address = (unsigned long)p;
address = (address + sizeof(long) - 1) & ~(sizeof(long) - 1);
return (char *)address;
}
#endif
/******************************************************************************
*
* Attribute (En|De)coding
*
*****************************************************************************/
/* Stores a list of serialized attributes. Takes advantage of foreknowledge of
* stuff string sizes, etc.
*
* Params:
* tag -- (IN) the name of the attribute
* tag_len -- (IN) the length of the tag in bytes
* attr_start -- (IN) the start of the attribute string
* attr_end -- (IN) the end of the attribute string
* val_count -- (IN) the number of values in the string
* type -- (IN) the type of the string
* unescaped_len -- (IN) the length of the unescaped data
*
* Returns:
* 0 - Out of mem.
* 1 - Success.
*/
int internal_store( struct xx_SLPAttributes *slp_attr, char const *tag, int tag_len, char const *attr_start, char const *attr_end, int val_count, SLPType type, int unescaped_len)
{
var_t *var;
int block_size;
char *mem_block; /* Pointer into allocated block. */
char const *cur_start; /* Pointer into attribute list (start of current data). */
char const *cur_end; /* Pointer into attribute list (end of current data). */
value_t *val = 0;
value_t **next_val_ptr; /* Pointer from the previous val to the next val. */
assert(type == SLP_BOOLEAN || type == SLP_STRING || type == SLP_OPAQUE || type == SLP_INTEGER); /* Ensure that we're dealing with a known type. */
/***** Allocate space for the variable. *****/
block_size = sizeof(var_t) + (tag_len); /* The var_t */
var = (var_t *)malloc(block_size);
if(var == NULL)
{
return 0;
}
/***** Allocate space for the values. *****/
block_size = (val_count * sizeof(value_t)) /* Size of each value */
+ unescaped_len /* The size of the unescaped data. */
#if 1 /* Jim Meyer's byte allignment code */
+ val_count * (sizeof(long) - 1); /* Padding */
#endif
mem_block = (char *)malloc(block_size);
if(mem_block == NULL)
{
free(val);
return 0;
}
/***** Initialize var_t. *****/
var->tag_len = tag_len;
var->tag = ((char *)var) + sizeof(var_t);
memcpy((char *)var->tag, tag, var->tag_len);
var->type = type;
var->list_size = val_count;
var->modified = SLP_TRUE;
next_val_ptr = &var->list; /* Initialize next_val_ptr */
*next_val_ptr = NULL;
/***** Initialize values. *****/
cur_end = cur_start = attr_start;
while(cur_end < attr_end)
{
/**** Find the size of the data. ****/
cur_end = memchr(cur_start, VAR_SEPARATOR, attr_end - cur_start);
if(cur_end == NULL)
{
cur_end = attr_end;
}
/**** Create the value. ****/
*next_val_ptr = val = (value_t *)mem_block;
val->next = NULL; /* Set forward pointer to null. */
val->next_chunk = NULL; /* This is not the first. */
val->last_value_in_chunk = NULL;
/**** Update kept data. ****/
next_val_ptr = &val->next; /* Book-keeping for next write. */
mem_block += sizeof(value_t); /* Move along. */
/**** FIXME Write the data. ****/
switch(type)
{
case(SLP_BOOLEAN):
assert(val_count == 1);
/* Set value. */
if(*cur_start == 't' || *cur_start == 'T')
{
assert(strncasecmp(cur_start, BOOL_TRUE_STR, BOOL_TRUE_STR_LEN) == 0); /* Make sure that we do, in actual fact, have the string "true". */
val->data.va_bool = SLP_TRUE;
val->escaped_len = BOOL_TRUE_STR_LEN;
}
else if(*cur_start == 'f' || *cur_start == 'F')
{
assert(strncasecmp(cur_start, BOOL_FALSE_STR, BOOL_FALSE_STR_LEN) == 0); /* Make sure that we do, in actual fact, have the string "false". */
val->data.va_bool = SLP_FALSE;
val->escaped_len = BOOL_FALSE_STR_LEN;
}
else
{
assert(0);
}
mem_block += val->unescaped_len;
break;
case(SLP_INTEGER):
val->data.va_int = (int) strtol(cur_start, NULL, 0);
val->escaped_len = count_digits(val->data.va_int);
/* FIXME Check errno. */
break;
case(SLP_OPAQUE):
case(SLP_STRING): {
char *err;
val->data.va_str = mem_block;
val->escaped_len = cur_end - cur_start;
err = unescape_into(val->data.va_str, cur_start, val->escaped_len, &val->unescaped_len);
if(err == NULL)
{
/* FIXME */
}
mem_block += val->unescaped_len;
}
break;
default:
assert(0); /* Unknown type. */
}
#if 1 /* Jim Meyer's byte allignment code */
mem_block = fix_memory_alignment(mem_block);
#endif
cur_start = cur_end + 1; /* +1 to move past comma. */
}
/***** Set pointers for memory management. *****/
var->list->last_value_in_chunk = val;
mem_block = mem_block + sizeof(value_t);
attr_add(slp_attr, var);
return 1; /* Success. */
}
/* Iterates across a set of variables. Either by using a given list of tag
* names, or looping across all list members.
*
* Params:
* slp_attr -- (IN) the attribute list we're working in
* tag_cur -- (IN/OUT) the current position in the tag string. Must be
* initialized to point to the start of a tag list.
* tag_end -- (IN/OUT) the end of the current tag. Must be initialized to
* the same value as tag_cur.
* var -- (IN/OUT) the variable in question. Must be initialized to the first
* variable in a list of attributes.
*
* Returns:
* 1 if there are more vars to be iterated over
* 0 if there are no more vars to be iterated over
*/
int var_iter(struct xx_SLPAttributes *slp_attr, char **tag_cur, char **tag_end, var_t **var)
{
/*** Get the next var. ***/
if(*tag_cur)
{
/*** We're doing a tag perusal: find next tag name. ***/
if(**tag_end)
{
/* There are more tags to be looked at */
if(*tag_end != *tag_cur)
{
/* This is _not_ our first time thru the loop: push pointers ahead. */
*tag_cur = *tag_end + 1;
}
*tag_end = strchr(*tag_cur, ',');
/* This is the last tag. */
if(*tag_end == NULL)
{
*tag_end = *tag_cur + strlen(*tag_cur);
}
}
else
{
/* There are no more tags to be looked at. Stop looping. */
return 0;
}
/*** Get named var. ***/
*var = attr_val_find_str(slp_attr, *tag_cur, *tag_end - *tag_cur);
return 1;
}
else
{
/*** We're getting all vars: get the next var. ***/
if(*var)
{
*var = (*var)->next;
}
else
{
/* First time into the iterator. */
*var = slp_attr->attrs;
}
if(*var == NULL)
{
/* Last var. Stop looping. */
return 0;
}
return 1;
}
}
/* Gets the escaped stringified version of an attribute list.
*
* The string returned must be free()'d by the caller.
*
* Params:
* attr_h -- (IN) Attribute handle to add serialize.
* tags -- (IN) The tags to serialize. If NULL, all tags are serialized.
* out_buffer -- (IN/OUT) A buffer to write the serialized string to. If
* (*out_buffer == NULL), then a new buffer is allocated by
* the API.
* bufferlen -- (IN) The length of the buffer. Ignored if
* (*out_buffer == NULL).
* count -- (OUT) The size needed/used of out_buffer (includes trailing null).
* find_delta -- (IN) If find_delta is set to true, only the attributes that
* have changed since the last serialize are updated.
*
* Returns:
* SLP_OK -- Serialization occured.
* SLP_BUFFER_OVERFLOW -- If (*out_buffer) is defined, but bufferlen is
* smaller than the amount of memory necessary to serialize
* the attr. list.
* SLP_MEMORY_ALLOC_FAILED -- Ran out of memory.
*/
SLPError SLPAttrSerialize(SLPAttributes attr_h,
const char* tags /* NULL terminated */,
char **out_buffer /* Where to write. if *out_buffer == NULL, space is alloc'd */,
int bufferlen, /* Size of buffer. */
int* count, /* Bytes needed/written. */
SLPBoolean find_delta
)
{
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
var_t *var; /* For iterating over attributes to serialize. */
unsigned int size; /* Size of the string to allocate. */
unsigned int var_count; /* To count the number of variables. */
char *build_str; /* The string that is being built. */
char *cur; /* Current location within the already allocated str. */
char *tag_cur; /* Current position within tag string. */
char *tag_end; /* end of current position within tag string. */
size = 0;
var_count = 0;
/***** Decide on our looping mode. *****/
if(tags == NULL || *tags == 0)
{
tag_cur = NULL;
}
else
{
tag_cur = (char *)tags;
}
tag_end = tag_cur;
var = NULL;
/***** Find the size of string needed for the attribute string. *****/
while(var_iter(slp_attr, &tag_cur, &tag_end, &var))
{
/*** Skip bad tags? ***/
if(var == NULL)
{
continue;
}
/*** Skip old attributes. ***/
if(find_delta == SLP_TRUE && var->modified == SLP_FALSE)
{
continue;
}
/*** Get size of tag ***/
size += var->tag_len;
/*** Count the size needed for the value list ***/
if(var->type != SLP_KEYWORD)
{
value_t *value;
/** Get size of data **/
value = var->list;
while(value)
{
size += value->escaped_len;
value = value->next;
}
/** Count number of commas needed for multivalued attributes. **/
assert(var->list_size >= 0);
size += (var->list_size - 1) * VAR_SEPARATOR_LEN;
/** Count the semantics needed to store a multivalue list **/
size += VAR_NON_KEYWORD_SEMANTIC_LEN;
}
else
{
assert(var->list == NULL);
}
/*** Count number of variables ***/
var_count++;
}
/*** Count the number of characters between attributes. ***/
if(var_count > 0)
{
size += (var_count - 1) * VAR_SEPARATOR_LEN;
}
/***** Return the size needed/used. *****/
if(count != NULL)
{
*count = size + 1;
}
/***** Create the string. *****/
if(*out_buffer == NULL)
{
/* We have to locally alloc the string. */
build_str = (char *)malloc( size + 1);
if(build_str == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
}
else
{
/* We write into a pre-alloc'd buffer. */
/**** Check that out_buffer is big enough. ****/
if((int)size + 1 > bufferlen)
{
return SLP_BUFFER_OVERFLOW;
}
build_str = *out_buffer;
}
build_str[0] = '\0';
/***** Add values *****/
/**** Decide on our looping mode. ****/
if(tags == NULL || *tags == 0)
{
tag_cur = NULL;
}
else
{
tag_cur = (char *)tags;
}
tag_end = tag_cur;
var = NULL;
/**** Find the size of string needed for the attribute string. ****/
cur = build_str;
while(var_iter(slp_attr, &tag_cur, &tag_end, &var))
{
/*** Skip bad tags? ***/
if(var == NULL)
{
continue;
}
/*** Skip old attributes. ***/
if(find_delta == SLP_TRUE && var->modified == SLP_FALSE)
{
continue;
}
if(var->type == SLP_KEYWORD)
{
/**** Handle keywords. ****/
memcpy(cur, var->tag, var->tag_len);
cur += var->tag_len;
}
else
{
/**** Handle everything else. ****/
char *to_add;
int to_add_len;
value_t *value;
/*** Add the prefix. ***/
*cur = VAR_PREFIX;
cur += VAR_PREFIX_LEN;
/*** Add the tag. ***/
memcpy(cur, var->tag, var->tag_len);
cur += var->tag_len;
/*** Add the infix. ***/
*cur = VAR_INFIX;
cur += VAR_INFIX_LEN;
/*** Insert value (list) ***/
value = var->list;
assert(value);
while(value)
{ /* foreach member value of an attribute. */
assert(var->type != SLP_KEYWORD);
switch(var->type)
{
case(SLP_BOOLEAN):
assert(value->next == NULL); /* Can't be a multivalued list. */
assert(value->data.va_bool == SLP_TRUE
|| value->data.va_bool == SLP_FALSE);
if(value->data.va_bool == SLP_TRUE)
{
to_add = BOOL_TRUE_STR;
to_add_len = BOOL_TRUE_STR_LEN;
}
else
{
to_add = BOOL_FALSE_STR;
to_add_len = BOOL_FALSE_STR_LEN;
}
memcpy(cur, to_add, to_add_len);
cur += to_add_len;
break;
case(SLP_STRING):
cur = escape_into(cur, value->data.va_str, value->unescaped_len);
break;
case(SLP_INTEGER):
sprintf(cur, "%d", value->data.va_int);
cur += value->escaped_len;
break;
case(SLP_OPAQUE):
memcpy(cur, OPAQUE_PREFIX, OPAQUE_PREFIX_LEN);
cur += OPAQUE_PREFIX_LEN;
cur = escape_opaque_into(cur, value->data.va_str, value->unescaped_len);
break;
default:
printf("Unknown type (%s:%d).\n", __FILE__, __LINE__);
/* TODO Clean up memory leak: the output string */
return SLP_INTERNAL_SYSTEM_ERROR;
}
value = value->next;
/*** Add separator (if necessary) ***/
if(value != NULL)
{
*cur = VAR_SEPARATOR;
cur += VAR_SEPARATOR_LEN;
*cur = 0;
}
}
/*** Add the suffix. ***/
*cur = VAR_SUFFIX;
cur += VAR_SUFFIX_LEN;
*cur = 0;
}
/*** Add separator. This is fixed for the last val outside the loop ***/
/* if (var->next != NULL) { */
if((unsigned)(cur - build_str) < size)
{
*cur = VAR_SEPARATOR;
cur += VAR_SEPARATOR_LEN;
*cur = 0;
}
/*** Reset the modified flag. ***/
var->modified = SLP_FALSE;
}
/**** Shift back to erase the last comma. ****/
/***** Append a null (This is actually done by strcpy, but its better to
* be safe than sorry =) *****/
*cur = '\0';
assert((unsigned)(cur - build_str) == size && size == strlen(build_str));
*out_buffer = build_str;
return SLP_OK;
}
/* Stores an escaped value into an attribute. Determines type of attribute at
* the same time.
*
* tag must be null terminated.
* val must be of length len.
* policy will only be respected where it can be (ints, strings, and opaques).
*
* the contents of tag are NOT verified.
*
* Returns:
* SLP_PARAMETER_BAD - Syntax error in the value.
* SLP_MEMORY_ALLOC_FAILED
*/
SLPError SLPAttrStore(struct xx_SLPAttributes *slp_attr,
const char *tag,
const char *val,
int len,
SLPInsertionPolicy policy
)
{
int i; /* Index into val. */
SLPBoolean is_str; /* Flag used for checking if given is string. */
char *unescaped;
int unescaped_len; /* Length of the unescaped text. */
/***** Check opaque. *****/
if(strncmp(val, OPAQUE_PREFIX, OPAQUE_PREFIX_LEN) == 0)
{
/*** Verify length (ie, that it is the multiple of the size of an
* escaped character). ***/
if(len % ESCAPED_LEN != 0)
{
return SLP_PARAMETER_BAD;
}
unescaped_len = (len / ESCAPED_LEN) - 1; /* -1 to drop the OPAQUE_PREFIX. */
/*** Verify that every character has been escaped. ***/
/* TODO */
/***** Unescape the value. *****/
unescaped = (char *)malloc(unescaped_len);
if(unescaped == NULL)
{
return SLP_MEMORY_ALLOC_FAILED; /* FIXME: Real error code. */
}
if(unescape_into(unescaped, (char *)(val + OPAQUE_PREFIX_LEN), len - OPAQUE_PREFIX_LEN, NULL) != NULL)
{
SLPError err;
err = SLPAttrSet_opaque((SLPAttributes)slp_attr, tag, unescaped, (len - OPAQUE_PREFIX_LEN) / 3, policy);
free(unescaped);/* FIXME This should be put into the val, and free()'d in val_destroy(). */
return err;
}
return SLP_PARAMETER_BAD; /* FIXME Verify. Is this really a bad parameter?*/
}
/***** Check boolean. *****/
if((BOOL_TRUE_STR_LEN == len) && (strncmp(val, BOOL_TRUE_STR, len) == 0))
{
return SLPAttrSet_bool((SLPAttributes)slp_attr, tag, SLP_TRUE);
}
if((BOOL_FALSE_STR_LEN == len) && strncmp(val, BOOL_FALSE_STR, len) == 0)
{
return SLPAttrSet_bool((SLPAttributes)slp_attr, tag, SLP_FALSE);
}
/***** Check integer *****/
if(*val == '-' || isdigit((int)*val))
{
/*** Verify. ***/
SLPBoolean is_int = SLP_TRUE; /* Flag true if the attr is an int. */
for(i = 1; i < len; i++)
{ /* We start at 1 since first char has already been checked. */
if(!isdigit((int)val[i]))
{
is_int = SLP_FALSE;
break;
}
}
/*** Handle the int-ness. ***/
if(is_int == SLP_TRUE)
{
char *end; /* To verify that the correct length was read. */
SLPError err;
err = SLPAttrSet_int((SLPAttributes)slp_attr, tag, (int) strtol(val, &end, 10), policy);
assert(end == val + len);
return err;
}
}
/***** Check string. *****/
is_str = SLP_TRUE;
for(i = 0; i < len; i++)
{
if(IS_RESERVED(val[i]) && (val[i] != '\\'))
{
is_str = SLP_FALSE;
break;
}
}
if(is_str == SLP_TRUE)
{
unescaped_len = find_unescaped_size(val, len);
unescaped = (char *)malloc( unescaped_len + 1 );
if(unescape_into(unescaped, val, len, NULL) != NULL)
{
SLPError err;
unescaped[unescaped_len] = '\0';
err = SLPAttrSet_str((SLPAttributes)slp_attr, tag, unescaped, policy);
free(unescaped); /* FIXME This should be put into the val, and free()'d in val_destroy(). */
return err;
}
return SLP_PARAMETER_BAD;
}
/* We don't bother checking for a keyword attribute since it can't have a
* value.
*/
return SLP_PARAMETER_BAD; /* Could not determine type. */
}
/* Converts an attribute string into an attr struct.
*
* Note that the attribute string is trashed.
*
* Returns:
* SLP_PARAMETER_BAD -- If there is a parse error in the attribute string.
* SLP_OK -- If everything went okay.
*/
SLPError attr_destringify(
struct xx_SLPAttributes *slp_attr,
char const *str,
SLPInsertionPolicy policy
)
{
char const *cur; /* Current index into str. */
enum
{
/* Note: everything contained in []s in this enum is a production from
* RFC 2608's grammar defining attribute lists.
*/
START_ATTR /* The start of an individual [attribute]. */,
START_TAG /* The start of a [attr-tag]. */,
VALUE /* The start of an [attr-val]. */,
STOP_VALUE /* The end of an [attr-val]. */
} state = START_ATTR; /* The current state of the parse. */
char const *tag; /* A tag that has been parsed. (carries data across state changes)*/
int tag_len = 0; /* length of the tag (in bytes) */
assert(str != NULL);
if(strlen(str) == 0)
{
return SLP_OK;
}
tag = NULL;
cur = str;
/***** Pull apart str. *****/
while(*cur)
{
char const *end; /* The end of a parse entity. */
switch(state)
{
case(START_ATTR): /* At the beginning of an attribute. */
if(*cur == VAR_PREFIX)
{
/* At the start of a non-keyword. */
state = START_TAG;
cur += VAR_PREFIX_LEN;
}
else
{
/* At the start of a keyword:
* Gobble up the keyword and set it.
*/
end = find_tag_end(cur);
if(end == NULL)
{
/* FIXME Ummm, I dunno. */
assert(0);
}
/*** Check that the tag ends on a legal ending char. ***/
if(*end == ',')
{
/** Add the keyword. **/
SLPAttrSet_keyw_len((SLPAttributes)slp_attr, cur, end - cur);
cur = end + 1;
break;
}
else if(*end == '\0')
{
SLPAttrSet_keyw_len((SLPAttributes)slp_attr, cur, end - cur);
return SLP_OK; /* FIXME Return success. */
break;
}
else
{
return SLP_PARAMETER_BAD; /* FIXME Return error code. -- Illegal tag char */
}
}
break;
case(START_TAG): /* At the beginning of a tag, in brackets. */
end = find_tag_end(cur);
if(end == NULL)
{
return SLP_PARAMETER_BAD; /* FIXME Err. code -- Illegal char. */
}
if(*end == '\0')
{
return SLP_PARAMETER_BAD; /* FIXME err: Premature end. */
}
/*** Check the the end character is valid. ***/
if(*end == VAR_INFIX)
{
tag_len = (int)(end - cur); /* Note that end is on the character _after_ the last character of the tag (the =). */
assert(tag == NULL);
tag = cur;
cur = end + VAR_INFIX_LEN;
state = VALUE;
}
else
{
/** ERROR! **/
return SLP_PARAMETER_BAD; /* FIXME Err. code.-- Logic error. */
}
break;
case(VALUE): /* At the beginning of the value portion. */
assert(tag != NULL); /* We should not be able to get into this state: is the string is malformed? */
{ /*** Find the end of the entire value list. ***/
int errval;
int val_count;
SLPType type;
char const *start;
int unescaped_len;
start = cur;
errval = find_value_list_end(start, &val_count, &type, &unescaped_len, &cur);
if(errval != 1)
{
return SLP_PARAMETER_BAD;
}
errval = internal_store(slp_attr, tag, tag_len, start, cur, val_count, type, unescaped_len);
if(errval != 1)
{
return SLP_MEMORY_ALLOC_FAILED;
}
state = STOP_VALUE;
}
break;
case(STOP_VALUE): /* At the end of a value. */
/***** Check to see which state we should move into next.*****/
/*** Done? ***/
if(*cur == '\0')
{
return SLP_OK;
}
/*** Another value? (ie, we're in a value list) ***/
// else if (*cur == VAR_SEPARATOR) {
// cur += VAR_SEPARATOR_LEN;
// state = VALUE;
// }
/*** End of the attribute? ***/
else if(*cur == VAR_SUFFIX)
{
assert(tag != NULL);
tag = NULL;
cur += VAR_SUFFIX_LEN;
/*** Are we at string end? ***/
if(*cur == '\0')
{
return SLP_OK;
}
/*** Ensure that there is a seperator ***/
if(*cur != VAR_SEPARATOR)
{
return SLP_PARAMETER_BAD; /* FIXME err -- unexpected character. */
}
cur += VAR_SEPARATOR_LEN;
state = START_ATTR;
}
/*** Error. ***/
else
{
return SLP_PARAMETER_BAD; /* FIXME err -- Illegal char at value end. */
}
break;
default:
printf("Unknown state %d\n", state);
}
}
return SLP_OK;
}
void destringify(SLPAttributes slp_attr_h, const char *str)
{
attr_destringify((struct xx_SLPAttributes*)slp_attr_h, (char *)str, SLP_ADD);
}
/* Adds the tags named in attrs to the receiver. Note that the new attributes
* _replace_ the old ones.
*
* Returns:
* SLP_OK -- Update occured as expected.
* SLP_MEMORY_ALLOC_FAILED -- Guess.
* SLP_PARAMETER_BAD -- Syntax error in the attribute string. Although
* slp_attr_h is still valid, its contents may have arbitrarily changed.
*/
SLPError SLPAttrFreshen(SLPAttributes slp_attr_h, const char *str)
{
SLPError err;
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes*)slp_attr_h;
// char *mangle; /* A copy of the passed in string, since attr_destringify tends to chew data. */
//
// mangle = strdup(str);
// if (str == NULL) {
// return SLP_MEMORY_ALLOC_FAILED;
// }
err = attr_destringify(slp_attr, str, SLP_ADD);
// free(mangle);
return err;
}
/******************************************************************************
*
* SLP Control Functions
*
*****************************************************************************/
/* Register attributes. */
//SLPError SLPRegAttr(
// SLPHandle slp_h,
// const char* srvurl,
// unsigned short lifetime,
// const char* srvtype,
// SLPAttributes attr_h,
// SLPBoolean fresh,
// SLPRegReport callback,
// void* cookie
//) {
// char *str;
// SLPError err;
//
//// err = SLPAttrSerialize(attr_h, int *count, char **str, SLPBoolean find_delta);
//
// if (str == NULL) {
// return SLP_INTERNAL_SYSTEM_ERROR;
// }
//
// err = SLPReg(slp_h, srvurl, lifetime, srvtype, str, fresh, callback, cookie);
//
// free(str);
//
// return err;
//}
struct hop_attr
{
SLPAttrObjCallback *cb;
void *cookie;
};
SLPBoolean attr_callback (
SLPHandle hslp,
const char* attrlist,
SLPError errcode,
void* cookie
)
{
struct hop_attr *hop = (struct hop_attr *)cookie;
SLPAttributes attr;
SLPBoolean result;
assert(errcode == SLP_OK || errcode == SLP_LAST_CALL);
if(errcode == SLP_OK)
{
if(SLPAttrAlloc("en", NULL, SLP_FALSE, &attr) != SLP_OK)
{
/* FIXME Ummm, should prolly tell application about the internal
* error.
*/
return SLP_FALSE;
}
destringify(attr, attrlist);
result = hop->cb(hslp, attr, errcode, hop->cookie);
assert(result == SLP_TRUE || result == SLP_FALSE);
SLPAttrFree(attr);
}
else if(errcode == SLP_LAST_CALL)
{
result = hop->cb(hslp, NULL, errcode, hop->cookie);
}
else
{
result = hop->cb(hslp, NULL, errcode, hop->cookie);
}
return result;
}
/* Find the attributes of a given service. */
//SLPError SLPFindAttrObj(
// SLPHandle hslp,
// const char* srvurlorsrvtype,
// const char* scopelist,
// const char* attrids,
// SLPAttrObjCallback *callback,
// void* cookie
//) {
// struct hop_attr *hop;
// SLPError err;
//
// hop = (struct hop_attr*)malloc(sizeof(struct hop_attr));
// hop->cb = callback;
// hop->cookie = cookie;
//
// err = SLPFindAttrs(hslp, srvurlorsrvtype, scopelist, attrids, attr_callback, hop);
//
// free(hop);
//
// return err;
//}
/******************************************************************************
*
* Iterators
*
*****************************************************************************/
///* An iterator to make for easy looping across the struct. */
//struct xx_SLPAttrIterator {
// int element_count; /* Number of elements. */
// char **tags; /* Array of tags. */
// int current; /* Current index into the attribute iterator. */
// struct xx_SLPAttributes *slp_attr;
//};
//
//
//
///* Allocates a new iterator for the given attribute handle. */
//SLPError SLPAttrIteratorAlloc(SLPAttributes attr_h, SLPAttrIterator *iter_h) {
// struct xx_SLPAttrIterator *iter;
// struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
// var_t *var;
// int i;
//
// assert(slp_attr != NULL);
//
// iter = (struct xx_SLPAttrIterator *)malloc(sizeof(struct xx_SLPAttrIterator)); /* free()'d in SLPAttrIteratorFree(). */
// if (iter == NULL) {
// return SLP_MEMORY_ALLOC_FAILED;
// }
//
// iter->element_count = (int)slp_attr->attr_count;
// iter->current = -1;
// iter->slp_attr = slp_attr;
//
// iter->tags = (char **)malloc(sizeof(char *) * iter->element_count);
// if (iter->tags == NULL) {
// free(iter);
// return SLP_MEMORY_ALLOC_FAILED;
// }
//
// var = slp_attr->attrs;
//
// for (i = 0; i < iter->element_count; i++, var = var->next) {
// assert(var != NULL);
//
// iter->tags[i] = strdup(var->tag);
//
// /***** Check that strdup succeeded. *****/
// if (iter->tags[i] == NULL) {
// /**** Unallocate structure. ****/
// int up_to_i;
// /*** Unallocate the tag list members. ***/
// for (up_to_i = 0; up_to_i < i; up_to_i++) {
// free(iter->tags[up_to_i]);
// }
//
// /*** Unallocate the tag list ***/
// free(iter->tags);
//
// return SLP_MEMORY_ALLOC_FAILED;
// }
// }
//
// *iter_h = (SLPAttrIterator)iter;
//
// return SLP_OK;
//}
//
//
///* Dealloc's an iterator and the associated memory.
// *
// * Everything free()'d here was alloc'd in SLPAttrIteratorAlloc().
// */
//void SLPAttrIteratorFree(SLPAttrIterator iter_h) {
// struct xx_SLPAttrIterator *iter = (struct xx_SLPAttrIterator*)iter_h;
// int i;
//
// /***** Free the tag list. *****/
// for(i = 0; i < iter->element_count; i++) {
// free(iter->tags[i]);
// iter->tags[i] = NULL;
// }
//
// free(iter->tags);
//
// free(iter);
//}
//
//
///* Gets the next tag name (and type).
// *
// * Note: The value of tag _must_ be copied out before the next call to
// * SLPAttrIterNext(). In other words, DO NOT keep pointers to the tag string
// * after the next call to SLPAttrIterNext().
// *
// * Returns SLP_FALSE if there are no tags left to iterate over, or SLP_TRUE.
// */
//SLPBoolean SLPAttrIterNext(SLPAttrIterator iter_h, char const **tag, SLPType *type) {
// struct xx_SLPAttrIterator *iter = (struct xx_SLPAttrIterator*)iter_h;
// SLPError err;
//
// iter->current++;
// if (iter->current >= iter->element_count) {
// *tag = NULL;
// return SLP_FALSE; /* FIXME Return Done. */
// }
// *tag = iter->tags[iter->current];
// err = SLPAttrGetType(iter->slp_attr, *tag, type);
//
// if (err != SLP_OK) {
// return SLP_FALSE; /* FIXME Ummm, try to get the next one. */
// }
//
// return SLP_TRUE;
//}
//
//
//
//
/* An iterator to make for easy looping across the struct. */
struct xx_SLPAttrIterator
{
struct xx_SLPAttributes *slp_attr;
var_t *current;
};
/* Allocates a new iterator for the given attribute handle. */
SLPError SLPAttrIteratorAlloc(SLPAttributes attr_h, SLPAttrIterator *iter_h)
{
struct xx_SLPAttrIterator *iter;
struct xx_SLPAttributes *slp_attr = (struct xx_SLPAttributes *)attr_h;
assert(slp_attr != NULL);
iter = (struct xx_SLPAttrIterator *)malloc(sizeof(struct xx_SLPAttrIterator)); /* free()'d in SLPAttrIteratorFree(). */
if(iter == NULL)
{
return SLP_MEMORY_ALLOC_FAILED;
}
iter->current = NULL;
iter->slp_attr = slp_attr;
*iter_h = (SLPAttrIterator)iter;
return SLP_OK;
}
/* Dealloc's an iterator and the associated memory.
*
* Everything free()'d here was alloc'd in SLPAttrIteratorAlloc().
*/
void SLPAttrIteratorFree(SLPAttrIterator iter_h)
{
struct xx_SLPAttrIterator *iter = (struct xx_SLPAttrIterator*)iter_h;
free(iter);
}
/* Gets the next tag name (and type).
*
* Note: The value of tag _must_ be copied out before the next call to
* SLPAttrIterNext(). In other words, DO NOT keep pointers to the tag string
* after the next call to SLPAttrIterNext().
*
* Returns SLP_FALSE if there are no tags left to iterate over, or SLP_TRUE.
*/
SLPBoolean SLPAttrIterNext(SLPAttrIterator iter_h, char const **tag, SLPType *type)
{
struct xx_SLPAttrIterator *iter = (struct xx_SLPAttrIterator*)iter_h;
if(iter->current == NULL)
{
iter->current = iter->slp_attr->attrs;
}
else
{
iter->current = iter->current->next;
if(iter->current == NULL)
{
return SLP_FALSE; /* Done. */
}
}
*tag = iter->current->tag;
*type = iter->current->type;
return SLP_TRUE;
}