Merge branch 'master' of supraverse.net:libpsyc

This commit is contained in:
Marenz 2011-04-30 12:57:39 +02:00
commit 3265b2fea9
53 changed files with 670 additions and 150 deletions

2
.gitignore vendored
View File

@ -6,7 +6,9 @@ src/match
test/testMatch
test/testParser
test/testRender
test/testServer
test/isRoutingVar
test/getVarType
.config
~$*

View File

@ -875,7 +875,7 @@ HTML_FOOTER =
# the style sheet file to the HTML output directory, so don't put your own
# stylesheet in the HTML output directory as well, or it will be erased!
HTML_STYLESHEET = doc/psyc.css
#HTML_STYLESHEET = doc/psyc.css
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
# Doxygen will adjust the colors in the stylesheet and background images

View File

@ -1,3 +0,0 @@
#projectlogo img {
height: 50px;
}

View File

@ -8,13 +8,13 @@
xmlns="http://www.w3.org/2000/svg"
id="svg4025"
version="1.1"
width="715.59717"
height="145.17674"
width="234"
height="50"
xml:space="preserve"><g
id="g4035"
transform="matrix(1.25,0,0,-1.25,-0.93316256,156.86281)"><g
transform="matrix(1,0,0,-1,2,52)"><g
id="g4037"
transform="scale(0.1,0.1)"><path
transform="scale(0.04,0.04)"><path
d="m 223.621,1244.61 5292.459,0 c 113.7,0 205.87,-92.17 205.87,-205.86 l 0,-729.109 c 0,-113.696 -92.17,-205.86 -205.87,-205.86 l -5292.459,0 c -113.695,0 -205.8632,92.164 -205.8632,205.86 l 0,729.109 c 0,113.69 92.1682,205.86 205.8632,205.86"
style="fill:#e45e3b;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4039" /><path

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -107,6 +107,12 @@ typedef struct
const char *ptr;
} psycString;
typedef struct
{
psycString name;
int value;
} psycMatchVar;
/**
* Shortcut for creating a psycString.
*
@ -146,13 +152,13 @@ typedef struct
/* intermediate struct for a PSYC packet */
typedef struct
{
psycHeader routing; ///< Routing header.
psycHeader entity; ///< Entitiy header.
psycHeader routing; ///< Routing header.
psycHeader entity; ///< Entity header.
psycString method;
psycString data;
size_t routingLength; ///< Length of routing part.
size_t contentLength; ///< Length of content part.
size_t length; ///< Total length of packet.
size_t routingLength; ///< Length of routing part.
size_t contentLength; ///< Length of content part.
size_t length; ///< Total length of packet.
psycPacketFlag flag;
} psycPacket;
@ -204,8 +210,7 @@ inline psycPacket psyc_newPacket2(psycModifier *routing, size_t routinglen,
/// Routing vars in alphabetical order.
extern const psycString PSYC_routingVars[];
/// Number of routing vars.
extern const size_t PSYC_routingVarsNum;
extern const psycMatchVar PSYC_varTypes[];
/**
* Get the type of variable name.
@ -215,7 +220,7 @@ psycBool psyc_isRoutingVar(const char *name, size_t len);
/**
* Get the type of variable name.
*/
psycType psyc_getVarType(char *name, size_t len);
psycType psyc_getVarType(const char *name, size_t len);
/**
* Checks if long keyword string inherits from short keyword string.
@ -238,8 +243,8 @@ int psyc_matches(char *sho, size_t slen,
* number of bytes written. 0 is a legal return value. Should the
* callback return -1, psyc_text leaves the original template text as is.
*/
typedef int (*psyctextCB)(uint8_t *match, size_t mlen,
uint8_t **buffer, size_t *blen);
typedef int (*psyctextCB)(char *match, size_t mlen,
char **buffer, size_t *blen);
/**
* Fills out text templates by asking a callback for content.
@ -254,8 +259,8 @@ typedef int (*psyctextCB)(uint8_t *match, size_t mlen,
*
* See also http://about.psyc.eu/psyctext
*/
int psyc_text(uint8_t *template, size_t tlen,
uint8_t **buffer, size_t *blen,
int psyc_text(char *template, size_t tlen,
char **buffer, size_t *blen,
psyctextCB lookupValue,
char *braceOpen, char *braceClose);

View File

@ -30,11 +30,13 @@ typedef enum
*/
typedef enum
{
PSYC_PARSE_ERROR_END = -7,
PSYC_PARSE_ERROR_METHOD = -6,
PSYC_PARSE_ERROR_VAR_LEN = -5,
PSYC_PARSE_ERROR_VAR_TAB = -4,
PSYC_PARSE_ERROR_VAR_NAME = -3,
PSYC_PARSE_ERROR_END = -9,
PSYC_PARSE_ERROR_BODY = -8,
PSYC_PARSE_ERROR_METHOD = -7,
PSYC_PARSE_ERROR_MOD_NL = -6,
PSYC_PARSE_ERROR_MOD_LEN = -5,
PSYC_PARSE_ERROR_MOD_TAB = -4,
PSYC_PARSE_ERROR_MOD_NAME = -3,
PSYC_PARSE_ERROR_LENGTH = -2,
PSYC_PARSE_ERROR = -1,
PSYC_PARSE_SUCCESS = 0,
@ -42,13 +44,13 @@ typedef enum
/// Fill another buffer and concatenate it with the end of the current buffer,
/// from the cursor position to the end.
PSYC_PARSE_INSUFFICIENT = 1,
/// Routing variable parsing done.
/// Routing modifier parsing done.
/// Operator, name & value contains the respective parts.
PSYC_PARSE_ROUTING = 2,
/// Entity variable parsing done.
/// Entity modifier parsing done.
/// Operator, name & value contains the respective parts.
PSYC_PARSE_ENTITY = 3,
/// Entity variable parsing is incomplete.
/// Entity modifier parsing is incomplete.
/// Operator & name are complete, value is incomplete.
PSYC_PARSE_ENTITY_INCOMPLETE = 4,
/// Body parsing done, name contains method, value contains body.
@ -91,6 +93,7 @@ typedef struct
uint8_t flags; ///< flags for the parser, see psycParseFlag
psycPart part; ///< part of the packet being parsed currently
size_t routingLength; ///< length of routing part parsed so far
size_t contentParsed; ///< number of bytes parsed from the content so far
size_t contentLength; ///< expected length of the content
psycBool contentLengthFound; ///< is there a length given for this packet?

View File

@ -7,8 +7,9 @@ int psyc_inherits(char* sho, size_t slen,
// the length.. but we would be faster here if we expected the callee
// to always use the PSYC_C2ARG() macro instead. additionally, the
// empty string would then be fully supported (in case you want that)
if (!slen) slen = strlen(sho);
if (!llen) llen = strlen(lon);
// Disabled this, let's use that macro rather.
//if (!slen) slen = strlen(sho);
//if (!llen) llen = strlen(lon);
if (slen == 0 || *sho != '_' ||
llen == 0 || *lon != '_') {
@ -45,8 +46,8 @@ int psyc_matches(char* sho, size_t slen,
char* lon, size_t llen) {
char *s, *l, *se, *le;
if (!slen) slen = strlen(sho);
if (!llen) llen = strlen(lon);
//if (!slen) slen = strlen(sho);
//if (!llen) llen = strlen(lon);
if (slen == 0 || *sho != '_' ||
llen == 0 || *lon != '_') {

View File

@ -146,9 +146,14 @@ inline size_t psyc_setPacketLength(psycPacket *p)
p->contentLength += p->data.length + 1; // data\n
// set total length: routing-header \n content |\n
p->length = p->routingLength + 1 + p->contentLength + sizeof(PSYC_PACKET_DELIMITER) - 2;
if (p->flag == PSYC_PACKET_NEED_LENGTH) // add length of length if needed
p->length += log10((double)p->data.length) + 1;
p->length = p->routingLength + p->contentLength + sizeof(PSYC_PACKET_DELIMITER) - 2;
if (p->contentLength > 0)
{
p->contentLength--; // subtract the \n from the delimiter, as that doesn't belong to the content part
p->length++; // add \n at the start of the content part
if (p->flag == PSYC_PACKET_NEED_LENGTH) // add length of length if needed
p->length += log10((double)p->data.length) + 1;
}
return p->length;
}

View File

@ -133,13 +133,15 @@ inline psycParseRC psyc_parseBinaryValue(psycParseState* state, psycString* valu
if (state->cursor + remaining > state->buffer.length) // is the length larger than this buffer?
{
value->length = state->buffer.length - state->cursor;
state->cursor += value->length;
*parsed += value->length;
return PSYC_PARSE_INCOMPLETE;
}
value->length += remaining;
value->length = remaining;
state->cursor += remaining;
*parsed += value->length;
*parsed += remaining;
assert(*parsed == *length);
return PSYC_PARSE_COMPLETE;
}
@ -153,9 +155,13 @@ inline psycParseRC psyc_parseModifier(psycParseState* state, char* oper, psycStr
*oper = *(state->buffer.ptr + state->cursor);
ADVANCE_CURSOR_OR_RETURN(PSYC_PARSE_INSUFFICIENT);
if (psyc_parseName(state, name) != PSYC_PARSE_SUCCESS)
return PSYC_PARSE_ERROR_VAR_NAME;
psycParseRC ret = psyc_parseName(state, name);
if (ret == PSYC_PARSE_ERROR)
return PSYC_PARSE_ERROR_MOD_NAME;
else if (ret != PSYC_PARSE_SUCCESS)
return ret;
size_t length = 0;
value->length = 0;
state->valueLength = 0;
state->valueParsed = 0;
@ -170,25 +176,26 @@ inline psycParseRC psyc_parseModifier(psycParseState* state, char* oper, psycStr
{
do
{
state->valueLength = 10 * state->valueLength + state->buffer.ptr[state->cursor] - '0';
length = 10 * length + state->buffer.ptr[state->cursor] - '0';
ADVANCE_CURSOR_OR_RETURN(PSYC_PARSE_INSUFFICIENT);
}
while (isNumeric(state->buffer.ptr[state->cursor]));
state->valueLength = length;
}
else
return PSYC_PARSE_ERROR_VAR_LEN;
return PSYC_PARSE_ERROR_MOD_LEN;
// After the length a TAB follows.
if (state->buffer.ptr[state->cursor] != '\t')
return PSYC_PARSE_ERROR_VAR_TAB;
return PSYC_PARSE_ERROR_MOD_TAB;
if (state->buffer.length <= ++(state->cursor)) // Incremented cursor inside length?
return PSYC_PARSE_ENTITY_INCOMPLETE;
if (++(state->cursor) >= state->buffer.length)
return PSYC_PARSE_INCOMPLETE;
if (psyc_parseBinaryValue(state, value, &(state->valueLength), &(state->valueParsed)) == PSYC_PARSE_INCOMPLETE)
return PSYC_PARSE_ENTITY_INCOMPLETE;
ret = psyc_parseBinaryValue(state, value, &(state->valueLength), &(state->valueParsed));
if (ret == PSYC_PARSE_INCOMPLETE)
return ret;
state->cursor++;
return PSYC_PARSE_SUCCESS;
}
else if (state->buffer.ptr[state->cursor] == '\t') // simple arg
@ -201,11 +208,11 @@ inline psycParseRC psyc_parseModifier(psycParseState* state, char* oper, psycStr
value->length++;
ADVANCE_CURSOR_OR_RETURN(PSYC_PARSE_INSUFFICIENT);
}
state->cursor++;
return PSYC_PARSE_SUCCESS;
}
else
return PSYC_PARSE_ERROR_VAR_TAB;
return PSYC_PARSE_ERROR_MOD_TAB;
}
/**
@ -214,7 +221,7 @@ inline psycParseRC psyc_parseModifier(psycParseState* state, char* oper, psycStr
*/
psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psycString* value)
{
int ret; // a return value
psycParseRC ret; // a return value
size_t pos; // a cursor position
// Start position of the current line in the buffer
@ -230,6 +237,7 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
case PSYC_PART_RESET: // New packet starts here, reset state.
state->valueParsed = 0;
state->valueLength = 0;
state->routingLength = 0;
state->contentParsed = 0;
state->contentLength = 0;
state->contentLengthFound = 0;
@ -237,17 +245,27 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
// fall thru
case PSYC_PART_ROUTING:
pos = state->cursor;
if (state->routingLength > 0)
{
if (state->buffer.ptr[state->cursor] != '\n')
return PSYC_PARSE_ERROR_MOD_NL;
ADVANCE_CURSOR_OR_RETURN(PSYC_PARSE_INSUFFICIENT);
}
// Each line of the header starts with a glyph,
// i.e. :_name, -_name +_name etc,
// so just test if the first char is a glyph.
if (isGlyph(state->buffer.ptr[state->cursor])) // is the first char a glyph?
{ // it is a glyph, so a variable starts here
ret = psyc_parseModifier(state, oper, name, value);
state->routingLength += state->cursor - pos;
return ret == PSYC_PARSE_SUCCESS ? PSYC_PARSE_ROUTING : ret;
}
else // not a glyph
{
state->part = PSYC_PART_LENGTH;
state->startc = state->cursor;
// fall thru
}
@ -284,8 +302,8 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
goto PSYC_PART_END;
}
state->startc = state->cursor + 1;
ADVANCE_CURSOR_OR_RETURN(PSYC_PARSE_INSUFFICIENT);
state->startc = state->cursor;
// fall thru
case PSYC_PART_CONTENT:
@ -293,7 +311,20 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
if (state->valueParsed < state->valueLength) {
ret = psyc_parseBinaryValue(state, value, &(state->valueLength), &(state->valueParsed));
state->contentParsed += value->length;
return ret == PSYC_PARSE_COMPLETE ? PSYC_PARSE_ENTITY : PSYC_PARSE_ENTITY_INCOMPLETE;
if (ret == PSYC_PARSE_INCOMPLETE)
return PSYC_PARSE_ENTITY_INCOMPLETE;
return PSYC_PARSE_ENTITY;
}
pos = state->cursor;
if (state->contentParsed > 0)
{
if (state->buffer.ptr[state->cursor] != '\n')
return PSYC_PARSE_ERROR_MOD_NL;
ADVANCE_CURSOR_OR_RETURN(PSYC_PARSE_INSUFFICIENT);
}
// Each line of the header starts with a glyph,
@ -303,31 +334,45 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
// method does not start with a glyph.
if (isGlyph(state->buffer.ptr[state->cursor]))
{
pos = state->cursor;
ret = psyc_parseModifier(state, oper, name, value);
state->contentParsed += state->cursor - pos;
return ret == PSYC_PARSE_SUCCESS ? PSYC_PARSE_ENTITY : ret;
if (ret == PSYC_PARSE_INCOMPLETE)
return PSYC_PARSE_ENTITY_INCOMPLETE;
else if (ret == PSYC_PARSE_SUCCESS)
return PSYC_PARSE_ENTITY;
return ret;
}
else
{
state->part = PSYC_PART_METHOD;
state->contentParsed += state->cursor - pos;
state->startc = state->cursor;
state->part = PSYC_PART_METHOD;
// fall thru
}
case PSYC_PART_METHOD:
pos = state->cursor;
if (psyc_parseName(state, name) == PSYC_PARSE_SUCCESS)
ret = psyc_parseName(state, name);
if (ret == PSYC_PARSE_INSUFFICIENT)
return ret;
else if (ret == PSYC_PARSE_SUCCESS)
{ // the method ends with a \n then the data follows
if (state->buffer.ptr[state->cursor] != '\n')
return PSYC_PARSE_ERROR_METHOD;
state->cursor++;
state->startc = state->cursor;
state->contentParsed += state->cursor - pos;
state->part = PSYC_PART_DATA;
if (state->cursor >= state->buffer.length)
return PSYC_PARSE_INSUFFICIENT;
if (state->contentLengthFound)
{ // if length was found set start position to the beginning of data
state->cursor++;
state->startc = state->cursor;
state->contentParsed += state->cursor - pos;
state->part = PSYC_PART_DATA;
}
else // otherwise keep it at the beginning of method
ADVANCE_CURSOR_OR_RETURN(PSYC_PARSE_INSUFFICIENT);
// fall thru
}
else // No method, which means the packet should end now.
@ -343,20 +388,28 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
if (state->contentLengthFound) // We know the length of the packet.
{
if (psyc_parseBinaryValue(state, value, &(state->contentLength), &(state->contentParsed)) == PSYC_PARSE_INCOMPLETE)
if (state->contentParsed < state->contentLength &&
psyc_parseBinaryValue(state, value, &(state->contentLength), &(state->contentParsed)) == PSYC_PARSE_INCOMPLETE)
return PSYC_PARSE_BODY_INCOMPLETE;
if (state->cursor >= state->buffer.length)
return PSYC_PARSE_BODY;
if (state->buffer.ptr[state->cursor] != '\n')
return PSYC_PARSE_ERROR_BODY;
state->cursor++;
state->part = PSYC_PART_END;
return PSYC_PARSE_BODY;
}
else // Search for the terminator.
{
size_t datac = state->cursor; // start of data
while (1)
{
uint8_t nl = state->buffer.ptr[state->cursor] == '\n';
// check for |\n if we're at the start of data or we have found a \n
if (state->cursor == state->startc || nl)
if (state->cursor == datac || nl)
{
if (state->cursor+1+nl >= state->buffer.length) // incremented cursor inside length?
{
@ -367,6 +420,7 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
if (state->buffer.ptr[state->cursor+nl] == '|' &&
state->buffer.ptr[state->cursor+1+nl] == '\n') // packet ends here
{
state->contentParsed += state->cursor - pos;
state->cursor += nl;
state->part = PSYC_PART_END;
return PSYC_PARSE_BODY;
@ -385,7 +439,7 @@ psycParseRC psyc_parse(psycParseState* state, char* oper, psycString* name, psyc
return PSYC_PARSE_INSUFFICIENT;
if (state->buffer.ptr[state->cursor] == '|' &&
state->buffer.ptr[state->cursor+1] == '\n') // packet ends here
state->buffer.ptr[state->cursor+1] == '\n') // packet ends here
{
state->cursor += 2;
state->part = PSYC_PART_RESET;
@ -414,7 +468,7 @@ psycParseListRC psyc_parseList(psycParseListState* state, psycString *name, psyc
if (!state->type) // If type is not set we're at the start
{
if (name->length < 5 || memcmp(name->ptr, "_list", 5) != 0 ||
(name->length > 5 && name->ptr[5] != '_')) // name should be _list or should start with _list_
(name->length > 5 && name->ptr[5] != '_')) // name should be _list or should start with _list_
return PSYC_PARSE_LIST_ERROR_NAME;
// First character is either | for text lists, or a number for binary lists

View File

@ -76,7 +76,8 @@ psycRenderRC psyc_render(psycPacket *packet, char *buffer, size_t buflen)
cur += itoa(packet->contentLength, buffer + cur, 10);
}
buffer[cur++] = '\n'; // start of content part
if (packet->entity.lines || packet->method.length || packet->data.length)
buffer[cur++] = '\n'; // start of content part if there's content
// render entity modifiers
for (i = 0; i < packet->entity.lines; i++)

View File

@ -1,62 +1,83 @@
#include <psyc.h>
#include <psyc/lib.h>
#include <stdint.h>
/// Routing variables in alphabetical order.
const psycString PSYC_routingVars[] =
const psycString psyc_routingVars[] =
{
PSYC_C2STR("_amount_fragments"),
PSYC_C2STR("_context"),
//PSYC_C2STR("_count"), // older PSYC
PSYC_C2STR("_counter"), // the name for this is supposed to be _count, not _counter
PSYC_C2STR("_counter"), // the name for this is supposed to be _count, not _counter
PSYC_C2STR("_fragment"),
//PSYC_C2STR("_length"), // older PSYC
PSYC_C2STR("_source"),
PSYC_C2STR("_source_identification"),
//PSYC_C2STR("_source_identification"), // older PSYC
PSYC_C2STR("_source_identity"),
PSYC_C2STR("_source_relay"),
PSYC_C2STR("_source_relay_relay"), // until you have a better idea.. is this really in use?
PSYC_C2STR("_source_relay_relay"), // until you have a better idea.. is this really in use?
PSYC_C2STR("_tag"),
PSYC_C2STR("_tag_relay"),
//PSYC_C2STR("_tag_reply"), // older PSYC
PSYC_C2STR("_target"),
PSYC_C2STR("_target_forward"),
PSYC_C2STR("_target_relay"),
//PSYC_C2STR(19, "_understand_modules"), // older PSYC
//PSYC_C2STR(14, "_using_modules"), // older PSYC
//PSYC_C2STR("_understand_modules"), // older PSYC
//PSYC_C2STR("_using_modules"), // older PSYC
};
const size_t PSYC_routingVarsNum = sizeof(PSYC_routingVars) / sizeof(*PSYC_routingVars);
const psycMatchVar psyc_varTypes[] =
{
{PSYC_C2STR("_amount"), PSYC_TYPE_AMOUNT},
{PSYC_C2STR("_color"), PSYC_TYPE_COLOR},
{PSYC_C2STR("_date"), PSYC_TYPE_DATE},
{PSYC_C2STR("_degree"), PSYC_TYPE_DEGREE},
{PSYC_C2STR("_entity"), PSYC_TYPE_ENTITY},
{PSYC_C2STR("_flag"), PSYC_TYPE_FLAG},
{PSYC_C2STR("_language"), PSYC_TYPE_LANGUAGE},
{PSYC_C2STR("_list"), PSYC_TYPE_LIST},
{PSYC_C2STR("_nick"), PSYC_TYPE_NICK},
{PSYC_C2STR("_page"), PSYC_TYPE_PAGE},
{PSYC_C2STR("_uniform"), PSYC_TYPE_UNIFORM},
{PSYC_C2STR("_time"), PSYC_TYPE_TIME},
};
const size_t psyc_routingVarsNum = PSYC_NUM_ELEM(psyc_routingVars);
const size_t psyc_varTypesNum = PSYC_NUM_ELEM(psyc_varTypes);
/**
* Get the type of variable name.
*/
psycBool psyc_isRoutingVar(const char *name, size_t len)
{
//return psyc_matchArray(psyc_routingVars, PSYC_NUM_ELEM(psyc_routingVars), name, len, 0);
size_t cursor = 1;
int8_t matching[PSYC_routingVarsNum]; // indexes of matching vars
memset(&matching, -1, sizeof(matching));
uint8_t i, m = 0;
int8_t matching[psyc_routingVarsNum]; // indexes of matching vars
if (len < 2 || name[0] != '_')
return PSYC_FALSE;
// first find the vars with matching length
for (i=0; i<PSYC_routingVarsNum; i++)
if (len == PSYC_routingVars[i].length)
for (i=0; i<psyc_routingVarsNum; i++)
if (len == psyc_routingVars[i].length)
matching[m++] = i;
matching[m] = -1; // mark the end of matching indexes
while (cursor < len && matching[0] >= 0)
{
for (i = m = 0; i < PSYC_routingVarsNum; i++)
for (i = m = 0; i < psyc_routingVarsNum; i++)
{
if (matching[i] < 0)
break;
if (PSYC_routingVars[matching[i]].ptr[cursor] == name[cursor])
break; // reached the end of possible matches
if (psyc_routingVars[matching[i]].ptr[cursor] == name[cursor])
matching[m++] = matching[i]; // found a match, update matching indexes
else if (PSYC_routingVars[matching[i]].ptr[cursor] > name[cursor])
break; // passed the possible matches in alphabetical order
else if (psyc_routingVars[matching[i]].ptr[cursor] > name[cursor])
break; // passed the possible matches in alphabetical order in the array
}
if (m < PSYC_routingVarsNum)
if (m < psyc_routingVarsNum)
matching[m] = -1; // mark the end of matching indexes
cursor++;
@ -68,7 +89,43 @@ psycBool psyc_isRoutingVar(const char *name, size_t len)
/**
* Get the type of variable name.
*/
psycType psyc_getVarType(char *name, size_t len)
psycType psyc_getVarType(const char *name, size_t len)
{
return PSYC_TYPE_UNKNOWN;
//return psyc_matchArray(psyc_varTypes, PSYC_NUM_ELEM(psyc_varTypes), name, len, 1);
size_t cursor = 1;
uint8_t i, m = 0;
int8_t matching[psyc_varTypesNum]; // indexes of matching vars
if (len < 2 || name[0] != '_')
return 0;
// first find the vars with matching length
for (i=0; i<psyc_varTypesNum; i++)
if (len == psyc_varTypes[i].name.length || (len > psyc_varTypes[i].name.length && name[psyc_varTypes[i].name.length] == '_'))
matching[m++] = i;
matching[m] = -1; // mark the end of matching indexes
while (cursor < len && matching[0] >= 0)
{
for (i = m = 0; i < psyc_varTypesNum; i++)
{
if (matching[i] < 0)
break; // reached the end of possible matches
if (cursor < psyc_varTypes[matching[i]].name.length && psyc_varTypes[matching[i]].name.ptr[cursor] == name[cursor])
matching[m++] = matching[i]; // found a match, update matching indexes
else if (cursor == psyc_varTypes[matching[i]].name.length && name[cursor] == '_')
return psyc_varTypes[matching[0]].value; // _ after the end of a matching prefix
else if (psyc_varTypes[matching[i]].name.ptr[cursor] > name[cursor])
break; // passed the possible matches in alphabetical order in the array
}
if (m < psyc_varTypesNum)
matching[m] = -1; // mark the end of matching indexes
cursor++;
}
// return first match if found
return matching[0] >= 0 ? psyc_varTypes[matching[0]].value : 0;
}

View File

@ -1,15 +1,38 @@
CFLAGS=-I../include -DDEBUG -g -O0 -Wall
LDFLAGS=-L../src
LOADLIBES=-lpsyc -lm
TARGETS=testParser testMatch testRender isRoutingVar
TARGETS=testServer testParser testMatch testRender isRoutingVar getVarType
PORT=4440
all: $(TARGETS)
./testRender
./testMatch
./isRoutingVar
./getVarType
test: $(TARGETS)
for f in packets/*; do echo ">> $$f"; ./testParser $$f; done
for f in packets/full-* packets/error-*; do echo ">> $$f"; ./testParser $$f; done
netstart:
./testServer $(PORT)
netstartv:
./testServer $(PORT) -v
nettest:
for f in packets/full-*; do echo ">> $$f"; cat $$f | nc localhost $(PORT) | diff -u $$f -; done
nettesterr:
for f in packets/error-*; do echo ">> $$f"; cat $$f | nc localhost $(PORT); done
splittest:
for f in packets/full-*; do echo ">> $$f"; ./splittest.pl $$f $(PORT) | diff -u $$f -; done
nettestp1:
(for f in packets/part-1-p*; do cat $$f; done) | nc localhost $(PORT) | diff -u packets/full-1 -
nettestp2:
(for f in packets/part-1-length-p*; do cat $$f; done) | nc localhost $(PORT) | diff -u packets/full-1-length -
clean:
rm -f $(TARGETS)

21
test/getVarType.c Normal file
View File

@ -0,0 +1,21 @@
#include <psyc.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include "../include/psyc/lib.h"
int main() {
unless (psyc_getVarType(PSYC_C2ARG("_list"))) return 1;
unless (psyc_getVarType(PSYC_C2ARG("_list_foo"))) return 2;
unless (psyc_getVarType(PSYC_C2ARG("_color_red"))) return 3;
if (psyc_getVarType(PSYC_C2ARG("_last"))) return 4;
if (psyc_getVarType(PSYC_C2ARG("_lost_foo"))) return 5;
if (psyc_getVarType(PSYC_C2ARG("_colorful"))) return 6;
if (psyc_getVarType(PSYC_C2ARG("_foo"))) return 7;
if (psyc_getVarType(PSYC_C2ARG("bar"))) return 8;
if (psyc_getVarType(PSYC_C2ARG("______"))) return 9;
if (psyc_getVarType(PSYC_C2ARG("_"))) return 10;
puts("psyc_getVarType passed all tests.");
return 0; // passed all tests
}

View File

@ -24,14 +24,14 @@ int main() {
printf("%s: %d\n", vars[i], psyc_isRoutingVar(vars[i], strlen(vars[i])));
}
#else
unless (psyc_isRoutingVar(PSYC_C2ARG("_source"))) return -1;
unless (psyc_isRoutingVar(PSYC_C2ARG("_source_relay"))) return -2;
if (psyc_isRoutingVar(PSYC_C2ARG("_source_foo"))) return -3;
if (psyc_isRoutingVar(PSYC_C2ARG("_sourcherry"))) return -4;
if (psyc_isRoutingVar(PSYC_C2ARG("_sour"))) return -5;
if (psyc_isRoutingVar(PSYC_C2ARG("_foo"))) return -6;
if (psyc_isRoutingVar(PSYC_C2ARG("bar"))) return -7;
if (psyc_isRoutingVar(PSYC_C2ARG("_"))) return -8;
unless (psyc_isRoutingVar(PSYC_C2ARG("_source"))) return 1;
unless (psyc_isRoutingVar(PSYC_C2ARG("_source_relay"))) return 2;
if (psyc_isRoutingVar(PSYC_C2ARG("_source_foo"))) return 3;
if (psyc_isRoutingVar(PSYC_C2ARG("_sourcherry"))) return 4;
if (psyc_isRoutingVar(PSYC_C2ARG("_sour"))) return 5;
if (psyc_isRoutingVar(PSYC_C2ARG("_foo"))) return 6;
if (psyc_isRoutingVar(PSYC_C2ARG("bar"))) return 7;
if (psyc_isRoutingVar(PSYC_C2ARG("_"))) return 8;
puts("psyc_isRoutingVar passed all tests.");
#endif

View File

@ -1,7 +0,0 @@
:_source some_source
:_length
_method
1234567
|

View File

@ -10,4 +10,3 @@ _message_foo_bar
ohai there!
\o/
|
|

View File

@ -17,9 +17,3 @@ _message_foo_bar
ohai there!
\o/
|
:_target psyc://foo.bar/~baz
:_test 123
_message_test
ohai!
|

View File

@ -10,4 +10,3 @@ _message_foo_bar
ohai there!
\o/
|
|

View File

@ -10,4 +10,3 @@ _message_foo_bar
ohai there!
\o/
|
|

View File

@ -10,4 +10,3 @@ _message_foo_bar
ohai there!
\o/
|
|

View File

@ -16,9 +16,3 @@ _message_foo_bar
ohai there!
\o/
|
:_target psyc://foo.bar/~baz
:_test 123
_message_test
ohai!
|

View File

@ -18,16 +18,3 @@ _message_foo_bar
ohai there!
\o/
|
:_target psyc://foo.bar/~baz
:_test 123
_message_test
ohai!
|
:_target psyc://foo.bar/~baz
+_list_foo yay
-_amount_x 2
_notice_test
test!
|

View File

@ -2,4 +2,3 @@
_request_some_things
|

View File

@ -0,0 +1,5 @@
:_target psyc://p5B084547.dip.t-dialin.net/@test
:_source something
_request_context_enter
|

View File

@ -1,9 +1,4 @@
:_target psyc://p5B084547.dip.t-dialin.net/@test
:_source something
_request_context_enter
|
:_target psyc://p5B084547.dip.t-dialin.net/@test
:_more vars
:_bin 6 "1

View File

@ -0,0 +1 @@
part-1-p1

View File

@ -0,0 +1 @@
part-1-p2

View File

@ -0,0 +1,3 @@
hu3r2cm
85
:_foo bar baz

View File

@ -0,0 +1 @@
part-1-p4

View File

@ -0,0 +1 @@
part-1-p5

View File

@ -0,0 +1 @@
part-1-p6

View File

@ -0,0 +1 @@
part-1-p7

View File

@ -0,0 +1 @@
part-1-p8

View File

@ -0,0 +1 @@
part-1-p9

2
test/packets/part-1-p1 Normal file
View File

@ -0,0 +1,2 @@
:_source psyc://foo/~bar
:_tar

2
test/packets/part-1-p2 Normal file
View File

@ -0,0 +1,2 @@
get psyc://bar/~baz
:_tag sch1828

3
test/packets/part-1-p3 Normal file
View File

@ -0,0 +1,3 @@
hu3r2cm
:_foo bar baz

2
test/packets/part-1-p4 Normal file
View File

@ -0,0 +1,2 @@
:_abc_def 1

1
test/packets/part-1-p5 Normal file
View File

@ -0,0 +1 @@
1 foo

4
test/packets/part-1-p6 Normal file
View File

@ -0,0 +1,4 @@
bar
baz
:_foo_bar yay
_message

2
test/packets/part-1-p7 Normal file
View File

@ -0,0 +1,2 @@
_foo_bar
ohai

2
test/packets/part-1-p8 Normal file
View File

@ -0,0 +1,2 @@
there!
\o/

1
test/packets/part-1-p9 Normal file
View File

@ -0,0 +1 @@
|

28
test/splittest.pl Executable file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket;
$| = 1;
print "Usage: splittest.pl <packet file> [<port> [<chunk length> [-v]]]\n" and exit unless @ARGV;
my $port = $ARGV[1] || 4440;
my $length = int($ARGV[2] || 1); $length = 1 if $length < 1;
my $verbose = $ARGV[3];
open FILE, '<', $ARGV[0] or die "$ARGV[0]: $!\n";
my $file = ''; $file .= $_ while <FILE>;
close FILE;
my $s = IO::Socket::INET->new(Proto => "tcp", PeerAddr => "localhost", PeerPort => $port) or die "localhost:$port: $!\n";
$s->autoflush(1);
my $c = 0;
while ($c < length $file) {
my $chunk = substr $file, $c, $length;
print "[$chunk]" if $verbose;
print $s $chunk;
$c += $length;
}
print while <$s>;
close $s;

View File

@ -2,17 +2,17 @@
#include "../include/psyc/lib.h"
int main() {
if (psyc_matches("_failure_delivery", 0, "_failure_unsuccessful_delivery_death", 0)) return -1;
if (psyc_matches("_failure_trash", 8, "_failure_unsuccessful_delivery_death", 0)) return -2;
if (psyc_matches("_unsuccessful", 0, "_failure_unsuccessful_delivery_death", 0)) return -3;
unless (psyc_matches("_fail", 0, "_failure_unsuccessful_delivery_death", 0)) return -4;
unless (psyc_matches("_truthahn", 0, "_failure_unsuccessful_delivery_death", 0)) return -5;
if (psyc_matches(PSYC_C2ARG("_failure_delivery"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 1;
if (psyc_matches(PSYC_C2ARG("_failure"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 2;
if (psyc_matches(PSYC_C2ARG("_unsuccessful"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 3;
unless (psyc_matches(PSYC_C2ARG("_fail"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 4;
unless (psyc_matches(PSYC_C2ARG("_truthahn"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 5;
puts("psyc_matches passed all tests.");
unless (psyc_inherits("_failure_delivery", 0, "_failure_unsuccessful_delivery_death", 0)) return -11;
if (psyc_inherits("_failure_unsuccessful", 0, "_failure_unsuccessful_delivery_death", 0)) return -12;
unless (psyc_inherits("_fail", 0, "_failure_unsuccessful_delivery_death", 0)) return -13;
unless (psyc_inherits(PSYC_C2ARG("_failure_delivery"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 11;
if (psyc_inherits(PSYC_C2ARG("_failure_unsuccessful"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 12;
unless (psyc_inherits(PSYC_C2ARG("_fail"), PSYC_C2ARG("_failure_unsuccessful_delivery_death"))) return 13;
puts("psyc_inherits passed all tests.");

View File

@ -83,7 +83,7 @@ int main(int argc, char **argv)
continue;
case PSYC_PARSE_INSUFFICIENT:
printf("Insufficient data.\n");
return -1;
return 1;
default:
printf("Error while parsing: %i\n", ret);
return 1;

View File

@ -94,7 +94,7 @@ int main(int argc, char **argv) {
=_description_presence\tI'm omnipresent right now\n\
_notice_presence\n\
|\n", verbose))
return -1;
return 1;
if (testList("\
:_source psyc://10.100.1000/~ludwig\n\
@ -106,7 +106,7 @@ qux\n\
_test_list\n\
list test\n\
|\n", verbose))
return -2;
return 2;
puts("psyc_render passed all tests.");

330
test/testServer.c Normal file
View File

@ -0,0 +1,330 @@
/**
* libpsyc test server for packet parsing & rendering
*
* based on selectserver.c from http://beej.us/guide/bgnet/
* "The C source code presented in this document is hereby granted to the public domain, and is completely free of any license restriction."
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <math.h>
#include <psyc/lib.h>
#include <psyc/parser.h>
#include <psyc/render.h>
#include <psyc/syntax.h>
const size_t RECV_BUF_SIZE = 256;
const size_t CONT_BUF_SIZE = 512;
const size_t SEND_BUF_SIZE = 1024;
const size_t NUM_PARSERS = 100;
// max size for routing & entity header
const size_t ROUTING_LINES = 16;
const size_t ENTITY_LINES = 32;
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char **argv)
{
char *port = argc > 1 ? argv[1] : "4440";
uint8_t verbose = argc > 2;
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[CONT_BUF_SIZE + RECV_BUF_SIZE]; // cont buf + recv buf: [ ccrrrr]
char *recvbuf = buf + CONT_BUF_SIZE; // recv buf: ^^^^
char *parsebuf; // parse buf: ^^^^^^
char sendbuf[SEND_BUF_SIZE];
size_t nbytes, contbytes = 0;
char remoteIP[INET6_ADDRSTRLEN];
int yes = 1; // for setsockopt() SO_REUSEADDR, below
int i, rv;
struct addrinfo hints, *ai, *p;
psycParseState parsers[NUM_PARSERS];
psycPacket packets[NUM_PARSERS];
psycModifier routing[NUM_PARSERS][ROUTING_LINES];
psycModifier entity[NUM_PARSERS][ENTITY_LINES];
psycModifier *mod;
int ret, retl;
char oper;
psycString name, value, elem;
psycString *pname, *pvalue;
psycParseListState listState;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, port, &hints, &ai)) != 0) {
fprintf(stderr, "error: %s\n", gai_strerror(rv));
exit(1);
}
for (p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for (;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for (i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
if (fdmax == NUM_PARSERS - 1)
continue; // ignore if there's too many
addrlen = sizeof remoteaddr;
newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
// reset parser state & packet
psyc_initParseState(&parsers[newfd]);
memset(&packets[newfd], 0, sizeof(psycPacket));
memset(&routing[newfd], 0, sizeof(psycModifier) * ROUTING_LINES);
memset(&entity[newfd], 0, sizeof(psycModifier) * ENTITY_LINES);
packets[newfd].routing.modifiers = routing[newfd];
packets[newfd].entity.modifiers = entity[newfd];
printf("# New connection from %s on socket %d\n",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
}
} else {
// handle data from a client
if ((nbytes = recv(i, recvbuf, RECV_BUF_SIZE, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
} else {
// we got some data from a client
parsebuf = recvbuf - contbytes;
psyc_nextParseBuffer(&parsers[i], psyc_newString(parsebuf, contbytes + nbytes));
contbytes = 0;
oper = 0;
name.length = 0;
value.length = 0;
do {
ret = psyc_parse(&parsers[i], &oper, &name, &value);
switch (ret) {
case PSYC_PARSE_ROUTING:
assert(packets[i].routing.lines < ROUTING_LINES);
mod = &(packets[i].routing.modifiers[packets[i].routing.lines]);
pname = &mod->name;
pvalue = &mod->value;
mod->flag = PSYC_MODIFIER_ROUTING;
packets[i].routing.lines++;
break;
case PSYC_PARSE_ENTITY_INCOMPLETE:
case PSYC_PARSE_ENTITY:
assert(packets[i].entity.lines < ENTITY_LINES);
mod = &(packets[i].entity.modifiers[packets[i].entity.lines]);
pname = &mod->name;
pvalue = &mod->value;
if (ret == PSYC_PARSE_ENTITY) {
packets[i].entity.lines++;
mod->flag = parsers[i].valueLength ? PSYC_MODIFIER_NEED_LENGTH : PSYC_MODIFIER_NO_LENGTH;
}
break;
case PSYC_PARSE_BODY_INCOMPLETE:
case PSYC_PARSE_BODY:
pname = &(packets[i].method);
pvalue = &(packets[i].data);
break;
case PSYC_PARSE_COMPLETE:
printf("# Done parsing.\n");
packets[i].flag = parsers[i].contentLengthFound ? PSYC_PACKET_NEED_LENGTH : PSYC_PACKET_NO_LENGTH;
psyc_setPacketLength(&packets[i]);
psyc_render(&packets[i], sendbuf, SEND_BUF_SIZE);
if (send(i, sendbuf, packets[i].length, 0) == -1) {
perror("send");
}
ret = -1;
break;
case PSYC_PARSE_INSUFFICIENT:
if (verbose)
printf("# Insufficient data.\n");
contbytes = parsers[i].buffer.length - parsers[i].cursor;
if (contbytes > 0) { // copy end of parsebuf before start of recvbuf
assert(recvbuf - contbytes >= buf); // make sure it's still in the buffer
memcpy(recvbuf - contbytes, parsebuf + parsers[i].cursor, contbytes);
}
ret = 0;
break;
default:
printf("# Error while parsing: %i\n", ret);
ret = -1;
}
switch (ret) {
case PSYC_PARSE_ENTITY_INCOMPLETE:
case PSYC_PARSE_BODY_INCOMPLETE:
ret = 0;
case PSYC_PARSE_ENTITY:
case PSYC_PARSE_ROUTING:
case PSYC_PARSE_BODY:
if (oper) {
mod->oper = oper;
if (verbose)
printf("%c", oper);
}
if (name.length) {
pname->ptr = malloc(name.length);
pname->length = name.length;
assert(pname->ptr != NULL);
memcpy((void*)pname->ptr, name.ptr, name.length);
name.length = 0;
if (verbose) {
printf("%.*s = ", (int)pname->length, pname->ptr);
}
}
if (value.length) {
if (!pvalue->ptr)
pvalue->ptr = malloc(parsers[i].valueLength ? parsers[i].valueLength : value.length);
assert(pvalue->ptr != NULL);
memcpy((void*)pvalue->ptr + pvalue->length, value.ptr, value.length);
pvalue->length += value.length;
value.length = 0;
if (verbose) {
printf("%.*s", (int)pvalue->length, pvalue->ptr);
if (parsers[i].valueLength > pvalue->length)
printf("...");
printf("\n");
}
} else if (verbose) {
printf("\n");
}
if (verbose)
printf("\t\t\t\t\t\t\t\t# n:%ld v:%ld c:%ld r:%ld\n", pname->length, pvalue->length, parsers[i].contentParsed, parsers[i].routingLength);
}
switch (ret) {
case PSYC_PARSE_ROUTING:
case PSYC_PARSE_ENTITY:
if (pname->length >= 5 && memcmp(pname->ptr, "_list", 5) == 0) {
if (verbose)
printf("## LIST START\n");
psyc_initParseListState(&listState);
psyc_nextParseListBuffer(&listState, *pvalue);
do {
retl = psyc_parseList(&listState, pname, pvalue, &elem);
switch (retl) {
case PSYC_PARSE_LIST_END:
retl = 0;
case PSYC_PARSE_LIST_ELEM:
if (verbose) {
printf("|%.*s\n", (int)elem.length, elem.ptr);
if (ret == PSYC_PARSE_LIST_END)
printf("## LIST END");
}
break;
default:
printf("# Error while parsing list: %i\n", ret);
ret = retl = -1;
}
} while (retl > 0);
}
}
} while (ret > 0);
if (ret < 0) {
printf("# Closing connection: %i\n", i);
close(i); // bye!
FD_CLR(i, &master); // remove from master set
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
}