📄 libslpattr.c
字号:
#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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -