1
0
Fork 0
mirror of git://git.psyc.eu/libpsyc synced 2024-07-31 00:21:03 +00:00
libpsyc/src/parser.c

461 lines
12 KiB
C
Raw Normal View History

2010-02-20 16:40:09 +00:00
#include <stdint.h>
2010-02-20 19:31:22 +00:00
#include <stdlib.h>
2010-02-20 16:40:09 +00:00
#ifdef DEBUG
#include <stdio.h>
#endif
#include <psyc/lib.h>
#include <psyc/parser.h>
2010-02-20 19:31:22 +00:00
2011-04-19 17:41:25 +00:00
#define ADVANCE_CURSOR_OR_RETURN(ret) \
if (++(state->cursor) >= state->buffer.length) \
{ \
state->cursor = state->startc; \
2011-04-19 17:41:25 +00:00
return ret; \
}
2011-04-19 19:54:44 +00:00
/**
* Determines if the argument is a glyph.
* Glyphs are: : = + - ? !
2010-02-20 16:40:09 +00:00
*/
inline char isGlyph(uint8_t g)
{
switch(g)
{
case ':':
case '=':
case '+':
case '-':
2011-04-19 17:41:25 +00:00
case '?':
case '!':
2010-02-20 16:40:09 +00:00
return 1;
default:
return 0;
}
}
2011-04-19 19:54:44 +00:00
/**
* Determines if the argument is numeric.
*/
2010-02-20 19:31:22 +00:00
inline char isNumeric(uint8_t c)
{
return c >= '0' && c <= '9';
2010-02-20 19:31:22 +00:00
}
2011-04-19 19:54:44 +00:00
/**
* Determines if the argument is alphanumeric.
*/
2010-02-20 16:40:09 +00:00
inline char isAlphaNumeric(uint8_t c)
{
return
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
isNumeric(c);
2010-02-20 16:40:09 +00:00
}
2011-04-19 19:54:44 +00:00
/**
* Determines if the argument is a keyword character.
* Keyword characters are: alphanumeric and _
*/
inline char isKwChar(uint8_t c)
{
2011-04-19 10:26:51 +00:00
return isAlphaNumeric(c) || c == '_';
}
2010-02-20 19:31:22 +00:00
/**
2011-04-19 19:54:44 +00:00
* Parse variable name or method name.
* It should contain one or more keyword characters.
* @return PSYC_ERROR or PSYC_SUCCESS
*/
2011-04-20 14:28:15 +00:00
inline PSYC_ReturnCode PSYC_parseName(PSYC_State* state, PSYC_Array* name)
2010-02-20 16:40:09 +00:00
{
name->ptr = state->buffer.ptr + state->cursor;
name->length = 0;
2011-04-18 08:09:35 +00:00
while (isKwChar(state->buffer.ptr[state->cursor]))
{
2011-04-19 19:54:44 +00:00
name->length++; // was a valid char, increase length
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
}
2011-04-18 08:09:35 +00:00
return name->length > 0 ? PSYC_SUCCESS : PSYC_ERROR;
}
2010-02-20 16:40:09 +00:00
/**
* Parse binary data.
*
* @param state Parser state.
* @param value Start & length of parsed data is saved here.
* @param length Expected length of the data.
* @param parsed Number of bytes parsed so far.
*
* @return PSYC_COMPLETE or PSYC_INCOMPLETE
*/
2011-04-20 14:28:15 +00:00
inline PSYC_ReturnCode PSYC_parseBinaryValue(PSYC_State* state, PSYC_Array* value, size_t* length, size_t* parsed)
{
2011-04-19 07:42:43 +00:00
size_t remaining = *length - *parsed;
value->ptr = state->buffer.ptr + state->cursor;
2010-02-20 16:40:09 +00:00
2011-04-19 19:54:44 +00:00
if (state->cursor + remaining > state->buffer.length) // is the length larger than this buffer?
2010-02-20 16:40:09 +00:00
{
value->length = state->buffer.length - state->cursor;
*parsed += value->length;
return PSYC_INCOMPLETE;
}
2011-04-17 10:05:14 +00:00
value->length += remaining;
state->cursor += remaining;
*parsed += value->length;
2011-04-17 10:05:14 +00:00
return PSYC_COMPLETE;
}
2011-04-17 10:05:14 +00:00
/**
2011-04-19 19:54:44 +00:00
* Parse simple or binary variable.
* @return PSYC_ERROR or PSYC_SUCCESS
*/
2011-04-20 14:28:15 +00:00
inline PSYC_ReturnCode PSYC_parseVar(PSYC_State* state, uint8_t* modifier, PSYC_Array* name, PSYC_Array* value)
{
*modifier = *(state->buffer.ptr + state->cursor);
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
2011-04-17 10:05:14 +00:00
if (PSYC_parseName(state, name) != PSYC_SUCCESS)
return PSYC_ERROR_VAR_NAME;
2011-04-18 08:09:35 +00:00
value->length = 0;
state->valueLength = 0;
state->valueParsed = 0;
2010-02-20 16:40:09 +00:00
2011-04-19 19:54:44 +00:00
// Parse the value.
// If we're in the content part check if it's a binary var.
if (state->part == PSYC_PART_CONTENT && state->buffer.ptr[state->cursor] == ' ') // binary arg
{ // After SP the length follows.
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
2010-02-20 16:40:09 +00:00
if (isNumeric(state->buffer.ptr[state->cursor]))
2010-02-20 16:40:09 +00:00
{
do
{
state->valueLength = 10 * state->valueLength + state->buffer.ptr[state->cursor] - '0';
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
}
while (isNumeric(state->buffer.ptr[state->cursor]));
}
else
return PSYC_ERROR_VAR_LEN;
2011-04-19 19:54:44 +00:00
// After the length a TAB follows.
if (state->buffer.ptr[state->cursor] != '\t')
return PSYC_ERROR_VAR_TAB;
2011-04-19 19:54:44 +00:00
if (state->buffer.length <= ++(state->cursor)) // Incremented cursor inside length?
return PSYC_ENTITY_INCOMPLETE;
if (PSYC_parseBinaryValue(state, value, &(state->valueLength), &(state->valueParsed)) == PSYC_INCOMPLETE)
return PSYC_ENTITY_INCOMPLETE;
state->cursor++;
return PSYC_SUCCESS;
}
2011-04-19 19:54:44 +00:00
else if (state->buffer.ptr[state->cursor] == '\t') // simple arg
2010-02-20 16:40:09 +00:00
{
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
value->ptr = state->buffer.ptr + state->cursor;
while (state->buffer.ptr[state->cursor] != '\n')
2010-02-20 16:40:09 +00:00
{
value->length++;
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
}
state->cursor++;
return PSYC_SUCCESS;
}
else
return PSYC_ERROR_VAR_TAB;
}
2010-02-20 16:40:09 +00:00
2011-04-19 19:54:44 +00:00
/**
* Parse PSYC packets.
* Generalized line-based parser.
*/
2011-04-20 14:28:15 +00:00
PSYC_ReturnCode PSYC_parse(PSYC_State* state, uint8_t* modifier, PSYC_Array* name, PSYC_Array* value)
{
2011-04-19 19:54:44 +00:00
int ret; // a return value
size_t pos; // a cursor position
2011-04-19 19:54:44 +00:00
// Start position of the current line in the buffer
// in case we return insufficent, we rewind to this position.
state->startc = state->cursor;
2011-04-19 19:54:44 +00:00
// First we test if we can access the first char.
if (state->cursor >= state->buffer.length) // cursor is not inside the length
return PSYC_INSUFFICIENT;
switch (state->part)
{
2011-04-19 19:54:44 +00:00
case PSYC_PART_RESET: // New packet starts here, reset state.
state->valueParsed = 0;
state->valueLength = 0;
state->contentParsed = 0;
state->contentLength = 0;
state->contentLengthFound = 0;
state->part = PSYC_PART_HEADER;
2011-04-19 19:54:44 +00:00
// fall thru
case PSYC_PART_HEADER:
2011-04-19 19:54:44 +00:00
// 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_parseVar(state, modifier, name, value);
return ret == PSYC_SUCCESS ? PSYC_ROUTING : ret;
2011-04-17 11:59:07 +00:00
}
2011-04-19 19:54:44 +00:00
else // not a glyph
2011-04-17 11:59:07 +00:00
{
state->part = PSYC_PART_LENGTH;
2011-04-19 19:54:44 +00:00
// fall thru
2010-02-20 16:40:09 +00:00
}
2011-04-17 11:59:07 +00:00
case PSYC_PART_LENGTH:
2011-04-19 19:54:44 +00:00
// End of header, content starts with an optional length then a NL
if (isNumeric(state->buffer.ptr[state->cursor]))
2011-04-17 11:59:07 +00:00
{
state->contentLengthFound = 1;
state->contentLength = 0;
do
{
state->contentLength = 10 * state->contentLength + state->buffer.ptr[state->cursor] - '0';
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
}
while (isNumeric(state->buffer.ptr[state->cursor]));
2011-04-17 11:59:07 +00:00
}
2011-04-19 19:54:44 +00:00
if (state->buffer.ptr[state->cursor] == '\n') // start of content
{
2011-04-19 19:54:44 +00:00
// If we need to parse the header only and we know the content length,
// then skip content parsing.
if (state->flags & PSYC_HEADER_ONLY && state->contentLengthFound)
state->part = PSYC_PART_DATA;
else
state->part = PSYC_PART_CONTENT;
}
2011-04-19 19:54:44 +00:00
else // Not start of content, this must be the end.
{
2011-04-19 19:54:44 +00:00
// If we have a length then it should've been followed by a \n
if (state->contentLengthFound)
return PSYC_ERROR_LENGTH;
2010-02-20 16:40:09 +00:00
state->part = PSYC_PART_END;
goto PSYC_PART_END;
}
2010-02-20 16:40:09 +00:00
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
state->startc = state->cursor;
2011-04-19 19:54:44 +00:00
// fall thru
2010-02-20 16:40:09 +00:00
case PSYC_PART_CONTENT:
2011-04-19 19:54:44 +00:00
// In case of an incomplete binary variable resume parsing it.
if (state->valueParsed < state->valueLength) {
ret = PSYC_parseBinaryValue(state, value, &(state->valueLength), &(state->valueParsed));
state->contentParsed += value->length;
return ret == PSYC_COMPLETE ? PSYC_ENTITY : PSYC_ENTITY_INCOMPLETE;
}
2010-02-20 16:40:09 +00:00
2011-04-19 19:54:44 +00:00
// 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.
// In the body, the same applies, only that the
// method does not start with a glyph.
if (isGlyph(state->buffer.ptr[state->cursor]))
{
pos = state->cursor;
ret = PSYC_parseVar(state, modifier, name, value);
state->contentParsed += state->cursor - pos;
return ret == PSYC_SUCCESS ? PSYC_ENTITY : ret;
}
else
{
state->part = PSYC_PART_METHOD;
state->startc = state->cursor;
2011-04-19 19:54:44 +00:00
// fall thru
}
2010-02-20 16:40:09 +00:00
case PSYC_PART_METHOD:
pos = state->cursor;
if (PSYC_parseName(state, name) == PSYC_SUCCESS)
2011-04-19 19:54:44 +00:00
{ // the method ends with a \n then the data follows
if (state->buffer.ptr[state->cursor] != '\n')
return PSYC_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_INSUFFICIENT;
2011-04-19 19:54:44 +00:00
// fall thru
}
2011-04-19 19:54:44 +00:00
else // No method, which means the packet should end now.
{
state->part = PSYC_PART_END;
state->startc = state->cursor;
goto PSYC_PART_END;
}
2010-02-20 16:40:09 +00:00
case PSYC_PART_DATA:
value->ptr = state->buffer.ptr + state->cursor;
value->length = 0;
2010-02-20 16:40:09 +00:00
2011-04-19 19:54:44 +00:00
if (state->contentLengthFound) // We know the length of the packet.
{
if (PSYC_parseBinaryValue(state, value, &(state->contentLength), &(state->contentParsed)) == PSYC_INCOMPLETE)
return PSYC_BODY_INCOMPLETE;
state->cursor++;
state->part = PSYC_PART_END;
return PSYC_BODY;
2011-04-15 23:42:36 +00:00
}
2011-04-19 19:54:44 +00:00
else // Search for the terminator.
{
2011-04-15 23:42:36 +00:00
while (1)
2010-02-20 18:50:10 +00:00
{
if (state->buffer.ptr[state->cursor] == '\n')
2010-02-20 21:18:39 +00:00
{
2011-04-19 19:54:44 +00:00
if (state->cursor+2 >= state->buffer.length) // incremented cursor inside length?
2010-02-20 21:18:39 +00:00
{
2011-04-19 17:41:25 +00:00
state->cursor = state->startc;
return PSYC_INSUFFICIENT;
2010-02-20 21:18:39 +00:00
}
if (state->buffer.ptr[state->cursor+1] == '|' &&
2011-04-19 19:54:44 +00:00
state->buffer.ptr[state->cursor+2] == '\n') // packet ends here
{
state->cursor++;
state->part = PSYC_PART_END;
return PSYC_BODY;
}
2010-02-20 18:50:10 +00:00
}
value->length++;
2011-04-19 17:41:25 +00:00
ADVANCE_CURSOR_OR_RETURN(PSYC_INSUFFICIENT);
2010-02-20 22:06:33 +00:00
}
}
case PSYC_PART_END:
PSYC_PART_END:
2011-04-19 19:54:44 +00:00
// End of packet, at this point we have already passed a \n
// and the cursor should point to |
if (state->cursor+1 >= state->buffer.length) // incremented cursor inside length?
2011-04-19 17:41:25 +00:00
return PSYC_INSUFFICIENT;
if (state->buffer.ptr[state->cursor] == '|' &&
2011-04-19 19:54:44 +00:00
state->buffer.ptr[state->cursor+1] == '\n') // packet ends here
2010-02-20 19:31:22 +00:00
{
state->cursor += 2;
state->part = PSYC_PART_RESET;
return PSYC_COMPLETE;
}
2011-04-19 19:54:44 +00:00
else // packet should've ended here, return error
{
state->part = PSYC_PART_RESET;
return PSYC_ERROR_END;
2010-02-20 19:31:22 +00:00
}
2010-02-20 16:40:09 +00:00
}
2011-04-19 19:54:44 +00:00
return PSYC_ERROR; // should not be reached
2011-04-19 17:41:25 +00:00
}
2011-04-19 19:54:44 +00:00
/**
* List value parser.
* @return see PSYC_ListReturnCodes.
*/
2011-04-20 14:28:15 +00:00
PSYC_ListReturnCode PSYC_parseList(PSYC_ListState* state, PSYC_Array *name, PSYC_Array* value, PSYC_Array* elem)
2011-04-19 17:41:25 +00:00
{
if (state->cursor >= state->buffer.length)
return PSYC_LIST_INCOMPLETE;
state->startc = state->cursor;
2011-04-19 19:54:44 +00:00
if (!state->type) // If type is not set we're at the start
2011-04-19 17:41:25 +00:00
{
if (name->length < 5 || memcmp(name->ptr, "_list", 5) != 0 ||
2011-04-19 19:54:44 +00:00
(name->length > 5 && name->ptr[5] != '_')) // name should be _list or should start with _list_
2011-04-19 17:41:25 +00:00
return PSYC_ERROR_LIST_NAME;
2011-04-19 19:54:44 +00:00
// First character is either | for text lists, or a number for binary lists
2011-04-19 17:41:25 +00:00
if (state->buffer.ptr[state->cursor] == '|')
{
state->type = PSYC_LIST_TEXT;
state->cursor++;
}
else if (isNumeric(state->buffer.ptr[state->cursor]))
state->type = PSYC_LIST_BINARY;
else
return PSYC_ERROR_LIST_TYPE;
}
if (state->type == PSYC_LIST_TEXT)
{
elem->ptr = state->buffer.ptr + state->cursor;
elem->length = 0;
if (state->cursor >= state->buffer.length)
return PSYC_LIST_END;
while (state->buffer.ptr[state->cursor] != '|')
{
elem->length++;
if (++(state->cursor) >= state->buffer.length)
return PSYC_LIST_END;
}
state->cursor++;
return PSYC_LIST_ELEM;
}
2011-04-19 19:54:44 +00:00
else // binary list
2011-04-19 17:41:25 +00:00
{
2011-04-19 19:54:44 +00:00
if (!(state->elemParsed < state->elemLength))
{
// Element starts with a number.
2011-04-19 17:41:25 +00:00
if (isNumeric(state->buffer.ptr[state->cursor]))
{
do
{
state->elemLength = 10 * state->elemLength + state->buffer.ptr[state->cursor] - '0';
ADVANCE_CURSOR_OR_RETURN(PSYC_LIST_INCOMPLETE);
}
while (isNumeric(state->buffer.ptr[state->cursor]));
}
else
return PSYC_ERROR_LIST_LEN;
if (state->buffer.ptr[state->cursor] != ' ')
return PSYC_ERROR_LIST_LEN;
state->cursor++;
elem->ptr = state->buffer.ptr + state->cursor;
elem->length = 0;
state->elemParsed = 0;
}
2011-04-19 19:54:44 +00:00
// Start or resume parsing the binary data
if (state->elemParsed < state->elemLength)
{
2011-04-19 17:41:25 +00:00
if (PSYC_parseBinaryValue((PSYC_State*)state, elem, &(state->elemLength), &(state->elemParsed)) == PSYC_INCOMPLETE)
return PSYC_LIST_INCOMPLETE;
state->elemLength = 0;
if (state->cursor >= state->buffer.length)
return PSYC_LIST_END;
if (state->buffer.ptr[state->cursor] != '|')
return PSYC_ERROR_LIST_DELIM;
state->cursor++;
return PSYC_LIST_ELEM;
}
}
2011-04-19 19:54:44 +00:00
return PSYC_ERROR_LIST; // should not be reached
2010-02-20 16:40:09 +00:00
}