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
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;
|
|
}
|
|
|
|
|
|
|
|
|