mirror of
git://git.psyc.eu/libpsyc
synced 2024-08-15 03:19:02 +00:00
psyc_text + tests
This commit is contained in:
parent
29b8e8c0d5
commit
f8d44070cc
7 changed files with 279 additions and 40 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,6 +7,7 @@ test/testMatch
|
|||
test/testParser
|
||||
test/testRender
|
||||
test/testServer
|
||||
test/testText
|
||||
test/isRoutingVar
|
||||
test/getVarType
|
||||
|
||||
|
|
|
@ -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
71
include/psyc/text.h
Normal 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);
|
|
@ -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
|
||||
|
|
119
src/text.c
119
src/text.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
90
test/testText.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue