psyc_text + tests

This commit is contained in:
tg(x) 2011-05-03 22:18:35 +02:00
parent 29b8e8c0d5
commit f8d44070cc
7 changed files with 279 additions and 40 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ test/testMatch
test/testParser
test/testRender
test/testServer
test/testText
test/isRoutingVar
test/getVarType

View File

@ -149,7 +149,7 @@ typedef struct
psycListFlag flag;
} psycList;
/* intermediate struct for a PSYC packet */
/** intermediate struct for a PSYC packet */
typedef struct
{
psycHeader routing; ///< Routing header.
@ -234,34 +234,4 @@ int psyc_inherits(char *sho, size_t slen,
int psyc_matches(char *sho, size_t slen,
char *lon, size_t llen);
/**
* Callback for psyc_text() that produces a value for a match.
*
* The application looks up a match such as _fruit from [_fruit] and
* if found writes its current value from its variable store into the
* outgoing buffer.. "Apple" for example. The template returns the
* 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)(char *match, size_t mlen,
char **buffer, size_t *blen);
/**
* Fills out text templates by asking a callback for content.
*
* Copies the contents of the template into the buffer while looking
* for braceOpen and braceClose strings and calling the callback for
* each enclosed string between these braces. Should the callback
* return -1, the original template text is copied as is.
*
* By default PSYC's "[" and "]" are used but you can provide any other
* brace strings such as "${" and "}" or "<!--" and "-->".
*
* See also http://about.psyc.eu/psyctext
*/
int psyc_text(char *template, size_t tlen,
char **buffer, size_t *blen,
psyctextCB lookupValue,
char *braceOpen, char *braceClose);
#endif // PSYC_H

71
include/psyc/text.h Normal file
View File

@ -0,0 +1,71 @@
/**
* The return value definitions for the PSYC text parsing function.
* @see psyc_text()
*/
typedef enum
{
PSYC_TEXT_NO_SUBST = -1,
PSYC_TEXT_COMPLETE = 0,
PSYC_TEXT_INCOMPLETE = 1,
} psycTextRC;
typedef enum
{
PSYC_TEXT_VALUE_NOT_FOUND = -1,
PSYC_TEXT_VALUE_FOUND = 0,
} psycTextValueRC;
/**
* Struct for keeping PSYC text parser state.
*/
typedef struct
{
size_t cursor; ///< current position in the template
size_t written; ///< number of bytes written to buffer
psycString template; ///< template to parse
psycString buffer; ///< buffer for writing to
psycString open;
psycString close;
} psycTextState;
/**
* Callback for psyc_text() that produces a value for a match.
*
* The application looks up a match such as _fruit from [_fruit] and
* if found writes its current value from its variable store into the
* outgoing buffer.. "Apple" for example. The template returns the
* 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 psycTextValueRC (*psycTextCB)(char *name, size_t len, psycString *value);
inline void psyc_initTextState (psycTextState *state,
char *template, size_t tlen,
char *buffer, size_t blen);
inline void psyc_initTextState2 (psycTextState* state,
char *template, size_t tlen,
char *buffer, size_t blen,
char *open, size_t openlen,
char *close, size_t closelen);
inline void psyc_setTextBuffer (psycTextState *state, psycString buffer);
inline void psyc_setTextBuffer2 (psycTextState *state, char *buffer, size_t length);
/**
* Fills out text templates by asking a callback for content.
*
* Copies the contents of the template into the buffer while looking
* for braceOpen and braceClose strings and calling the callback for
* each enclosed string between these braces. Should the callback
* return -1, the original template text is copied as is.
*
* By default PSYC's "[" and "]" are used but you can provide any other
* brace strings such as "${" and "}" or "<!--" and "-->".
*
* See also http://about.psyc.eu/psyctext
*/
psycTextRC psyc_text(psycTextState *state, psycTextCB getValue);

View File

@ -3,8 +3,8 @@ DEBUG = 2
CFLAGS = -I../include -Wall ${OPT}
DIET = diet
S = packet.c misc.c parser.c match.c render.c memmem.c itoa.c variable.c
O = packet.o misc.o parser.o match.o render.o memmem.o itoa.o variable.o
S = packet.c misc.c parser.c match.c render.c memmem.c itoa.c variable.c text.c
O = packet.o misc.o parser.o match.o render.o memmem.o itoa.o variable.o text.o
all: CC := ${WRAPPER} ${CC}
all: lib

View File

@ -1,9 +1,116 @@
/* psyc_text() */
#include <psyc/lib.h>
#include <psyc/text.h>
int psyc_text(char *template, size_t tlen,
char **buffer, size_t *blen,
psyctextCB lookupValue,
char *braceOpen, char *braceClose)
inline void psyc_initTextState (psycTextState *state,
char *template, size_t tlen,
char *buffer, size_t blen)
{
state->cursor = state->written = 0;
state->template = psyc_newString(template, tlen);
state->buffer = psyc_newString(buffer, blen);
state->open = psyc_newString("[", 1);
state->close = psyc_newString("]", 1);
}
inline void psyc_initTextState2 (psycTextState* state,
char *template, size_t tlen,
char *buffer, size_t blen,
char *open, size_t openlen,
char *close, size_t closelen)
{
state->template = psyc_newString(template, tlen);
state->buffer = psyc_newString(buffer, blen);
state->open = psyc_newString(open, openlen);
state->close = psyc_newString(close, closelen);
}
inline void psyc_setTextBuffer (psycTextState* state, psycString buffer)
{
state->buffer = buffer;
state->written = 0;
}
inline void psyc_setTextBuffer2 (psycTextState* state, char *buffer, size_t length)
{
psyc_setTextBuffer(state, psyc_newString(buffer, length));
}
psycTextRC psyc_text (psycTextState *state, psycTextCB getValue)
{
char *start = state->template.ptr, *end; // start & end of variable name
char *prev = state->template.ptr + state->cursor;
psycString value;
int ret;
size_t len;
uint8_t no_subst = (state->cursor == 0); // whether we can return NO_SUBST
while (state->cursor < state->template.length)
{
start = memmem(state->template.ptr + state->cursor,
state->template.length - state->cursor,
state->open.ptr, state->open.length);
if (!start)
break;
state->cursor = (start - state->template.ptr) + state->open.length;
if (state->cursor >= state->template.length)
break; // [ at the end
end = memmem(state->template.ptr + state->cursor,
state->template.length - state->cursor,
state->close.ptr, state->close.length);
state->cursor = (end - state->template.ptr) + state->close.length;
if (!end)
break; // ] not found
if (start + state->open.length == end)
{
state->cursor += state->close.length;
continue; // [] is invalid, name can't be empty
}
ret = getValue(start + state->open.length, end - start - state->open.length, &value);
if (ret < 0)
continue; // value not found, no substitution
// first copy the part in the template from the previous subst. to the current one
// if there's enough buffer space for that
len = start - prev;
if (state->written + len > state->buffer.length)
{
state->cursor = prev - state->template.ptr;
return PSYC_TEXT_INCOMPLETE;
}
memcpy((void *)(state->buffer.ptr + state->written), prev, len);
state->written += len;
// now substitute the value if there's enough buffer space
if (state->written + value.length > state->buffer.length)
{
state->cursor = start - state->template.ptr;
return PSYC_TEXT_INCOMPLETE;
}
memcpy((void *)(state->buffer.ptr + state->written), value.ptr, value.length);
state->written += value.length;
// mark the start of the next chunk of text in the template
prev = state->template.ptr + state->cursor;
no_subst = 0;
}
if (no_subst)
return PSYC_TEXT_NO_SUBST;
// copy the rest of the template after the last var
len = state->template.length - (prev - state->template.ptr);
if (state->written + len > state->buffer.length)
return PSYC_TEXT_INCOMPLETE;
memcpy((void *)(state->buffer.ptr + state->written), prev, len);
state->written += len;
return PSYC_TEXT_COMPLETE;
}

View File

@ -3,7 +3,7 @@ DEBUG = 2
CFLAGS = -I../include -Wall ${OPT}
LDFLAGS = -L../lib
LOADLIBES = -lpsyc -lm
TARGETS = testServer testParser testMatch testRender isRoutingVar getVarType
TARGETS = testServer testParser testMatch testRender testText isRoutingVar getVarType
WRAPPER =
DIET = diet
PORT = 4440

90
test/testText.c Normal file
View File

@ -0,0 +1,90 @@
#include <psyc/lib.h>
#include <psyc/text.h>
#define BUFSIZE 512
uint8_t verbose;
psycTextValueRC getValueFooBar (char *name, size_t len, psycString *value)
{
value->ptr = "Foo Bar";
value->length = 7;
return PSYC_TEXT_VALUE_FOUND;
}
psycTextValueRC getValueEmpty (char *name, size_t len, psycString *value)
{
value->ptr = "";
value->length = 0;
return PSYC_TEXT_VALUE_FOUND;
}
psycTextValueRC getValueNotFound (char *name, size_t len, psycString *value)
{
return PSYC_TEXT_VALUE_NOT_FOUND;
}
int testText (char *template, size_t tmplen, char *buffer, size_t buflen, psycString *result, psycTextCB getValue)
{
psycTextState state;
size_t length = 0;
psycTextRC ret;
psyc_initTextState(&state, template, tmplen, buffer, buflen);
do
{
ret = psyc_text(&state, getValue);
length += state.written;
switch (ret)
{
case PSYC_TEXT_INCOMPLETE:
if (verbose)
printf("# %.*s...\n", (int)length, buffer);
psyc_setTextBuffer2(&state, buffer + length, BUFSIZE - length);
break;
case PSYC_TEXT_COMPLETE:
if (verbose)
printf("%.*s\n", (int)length, buffer);
result->length = length;
result->ptr = buffer;
return ret;
case PSYC_TEXT_NO_SUBST:
if (verbose)
printf("%.*s\n", (int)tmplen, template);
return ret;
}
}
while (ret == PSYC_TEXT_INCOMPLETE);
return -2; // shouldn't be reached
}
int main(int argc, char **argv)
{
verbose = argc > 1;
char buffer[BUFSIZE];
psycString result;
char *str = "Hello [_foo] & [_bar]!";
size_t len = strlen(str);
int i;
testText(str, len, buffer, BUFSIZE, &result, &getValueFooBar);
if (memcmp(result.ptr, PSYC_C2ARG("Hello Foo Bar & Foo Bar!")))
return 1;
testText(str, len, buffer, BUFSIZE, &result, &getValueEmpty);
if (memcmp(result.ptr, PSYC_C2ARG("Hello & !")))
return 2;
if (testText(str, len, buffer, BUFSIZE, &result, &getValueNotFound) != PSYC_TEXT_NO_SUBST)
return 3;
for (i = 1; i < 22; i++)
{
testText(str, len, buffer, i, &result, &getValueFooBar);
if (memcmp(result.ptr, PSYC_C2ARG("Hello Foo Bar & Foo Bar!")))
return 10 + i;
}
return 0;
}