From da94b7a2e4acb9d9bf3436cc30448ce1b322c6ef Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 24 Apr 2017 17:09:01 -0500 Subject: [PATCH] Fix dbConstLink to handle a long-string array properly Added a new parser dbLSConvertJSON for long-string arrays. New test checks that only the first string element is used. --- src/ioc/db/dbConstLink.c | 17 +----- src/ioc/db/dbConvertJSON.c | 93 ++++++++++++++++++++++++++++++-- src/ioc/db/dbConvertJSON.h | 3 +- src/std/rec/test/linkInitTest.c | 4 +- src/std/rec/test/linkInitTest.db | 5 +- 5 files changed, 99 insertions(+), 23 deletions(-) diff --git a/src/ioc/db/dbConstLink.c b/src/ioc/db/dbConstLink.c index 928f7cf94..0bdc70f92 100644 --- a/src/ioc/db/dbConstLink.c +++ b/src/ioc/db/dbConstLink.c @@ -73,26 +73,11 @@ static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen) { const char *pstr = plink->value.constantStr; - size_t len; if (!pstr) return S_db_badField; - if (!size) - return 0; - len = strlen(pstr); - /* FIXME This handles the common case, but not the general one... */ - if (pstr[0] == '[' && pstr[1] == '"' && - pstr[len-2] == '"' && pstr[len-1] == ']') { - pstr += 2; - len -= 4; - } - if (len+1 > size) len = size-1; - - memcpy(pbuffer, pstr, len); - pbuffer[len] = 0; - *plen = len+1; - return 0; + return dbLSConvertJSON(pstr, pbuffer, size, plen); } static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer, diff --git a/src/ioc/db/dbConvertJSON.c b/src/ioc/db/dbConvertJSON.c index c0e1e2c8e..e2a453549 100644 --- a/src/ioc/db/dbConvertJSON.c +++ b/src/ioc/db/dbConvertJSON.c @@ -17,6 +17,7 @@ #define epicsExportSharedSymbols #include "dbAccessDefs.h" #include "dbConvertFast.h" +#include "dbConvertJSON.h" typedef long (*FASTCONVERT)(); @@ -49,6 +50,10 @@ static int dbcj_integer(void *ctx, long num) { return 1; } +static int dblsj_integer(void *ctx, long num) { + return 0; /* Illegal */ +} + static int dbcj_double(void *ctx, double num) { parseContext *parser = (parseContext *) ctx; FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType]; @@ -61,6 +66,10 @@ static int dbcj_double(void *ctx, double num) { return 1; } +static int dblsj_double(void *ctx, double num) { + return 0; /* Illegal */ +} + static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) { parseContext *parser = (parseContext *) ctx; char *pdest = parser->pdest; @@ -69,7 +78,7 @@ static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) { * metadata about the field than we have available at the moment. */ if (parser->dbrType != DBF_STRING) { - errlogPrintf("dbPutConvertJSON: String provided, numeric value(s) expected\n"); + errlogPrintf("dbConvertJSON: String provided, numeric value(s) expected\n"); return 0; /* Illegal */ } @@ -84,8 +93,28 @@ static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) { return 1; } +static int dblsj_string(void *ctx, const unsigned char *val, unsigned int len) { + parseContext *parser = (parseContext *) ctx; + char *pdest = parser->pdest; + + if (parser->dbrType != DBF_STRING) { + errlogPrintf("dbConvertJSON: dblsj_string dbrType error\n"); + return 0; /* Illegal */ + } + + if (parser->elems > 0) { + if (len > parser->dbrSize - 1) + len = parser->dbrSize - 1; + strncpy(pdest, (const char *) val, len); + pdest[len] = 0; + parser->pdest = pdest + len; + parser->elems = 0; + } + return 1; +} + static int dbcj_start_map(void *ctx) { - errlogPrintf("dbPutConvertJSON: Map type not supported\n"); + errlogPrintf("dbConvertJSON: Map type not supported\n"); return 0; /* Illegal */ } @@ -101,7 +130,7 @@ static int dbcj_start_array(void *ctx) { parseContext *parser = (parseContext *) ctx; if (++parser->depth > 1) - errlogPrintf("dbPutConvertJSON: Embedded arrays not supported\n"); + errlogPrintf("dbConvertJSON: Embedded arrays not supported\n"); return (parser->depth == 1); } @@ -157,7 +186,7 @@ long dbPutConvertJSON(const char *json, short dbrType, case yajl_status_error: { unsigned char *err = yajl_get_error(yh, 1, (const unsigned char *) json, (unsigned int) jlen); - fprintf(stderr, "dbPutConvertJSON: %s\n", err); + fprintf(stderr, "dbConvertJSON: %s\n", err); yajl_free_error(yh, err); } /* fall through */ @@ -170,3 +199,59 @@ long dbPutConvertJSON(const char *json, short dbrType, } +static yajl_callbacks dblsj_callbacks = { + dbcj_null, dbcj_boolean, dblsj_integer, dblsj_double, NULL, dblsj_string, + dbcj_start_map, dbcj_map_key, dbcj_end_map, + dbcj_start_array, dbcj_end_array +}; + +long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size, + epicsUInt32 *plen) +{ + parseContext context, *parser = &context; + yajl_alloc_funcs dbcj_alloc; + yajl_handle yh; + yajl_status ys; + size_t jlen = strlen(json); + long status; + + if (!size) { + *plen = 0; + return 0; + } + + parser->depth = 0; + parser->dbrType = DBF_STRING; + parser->dbrSize = size; + parser->pdest = pdest; + parser->elems = 1; + + yajl_set_default_alloc_funcs(&dbcj_alloc); + yh = yajl_alloc(&dblsj_callbacks, &dbcj_config, &dbcj_alloc, parser); + if (!yh) + return S_db_noMemory; + + ys = yajl_parse(yh, (const unsigned char *) json, (unsigned int) jlen); + if (ys == yajl_status_insufficient_data) + ys = yajl_parse_complete(yh); + + switch (ys) { + case yajl_status_ok: + *plen = (char *) parser->pdest - pdest + 1; + status = 0; + break; + + case yajl_status_error: { + unsigned char *err = yajl_get_error(yh, 1, + (const unsigned char *) json, (unsigned int) jlen); + fprintf(stderr, "dbLoadLS_JSON: %s\n", err); + yajl_free_error(yh, err); + } + /* fall through */ + default: + status = S_db_badField; + } + + yajl_free(yh); + return status; +} diff --git a/src/ioc/db/dbConvertJSON.h b/src/ioc/db/dbConvertJSON.h index 11546d9ab..7dd8e4aed 100644 --- a/src/ioc/db/dbConvertJSON.h +++ b/src/ioc/db/dbConvertJSON.h @@ -18,7 +18,8 @@ extern "C" { /* This name should probably be changed to inclue "array" */ epicsShareFunc long dbPutConvertJSON(const char *json, short dbrType, void *pdest, long *psize); - +epicsShareFunc long dbLSConvertJSON(const char *json, char *pdest, + epicsUInt32 size, epicsUInt32 *plen); #ifdef __cplusplus } #endif diff --git a/src/std/rec/test/linkInitTest.c b/src/std/rec/test/linkInitTest.c index ac849ba4a..b99b98c69 100644 --- a/src/std/rec/test/linkInitTest.c +++ b/src/std/rec/test/linkInitTest.c @@ -53,6 +53,8 @@ static void testLongStringInit() testdbGetFieldEqual("longstr3.VAL", DBR_STRING, "!--------------------------------------"); } + testdbGetFieldEqual("longstr4.VAL", DBR_STRING, "One"); + testIocShutdownOk(); testdbCleanup(); @@ -149,7 +151,7 @@ static void testPrintfInit() MAIN(linkInitTest) { - testPlan(30); + testPlan(31); testLongStringInit(); testCalcInit(); testPrintfInit(); diff --git a/src/std/rec/test/linkInitTest.db b/src/std/rec/test/linkInitTest.db index a8063a899..0e9bbf55e 100644 --- a/src/std/rec/test/linkInitTest.db +++ b/src/std/rec/test/linkInitTest.db @@ -1,4 +1,3 @@ - record(lsi, "longstr1") { field(SIZV, "100") field(INP, ["!----------------------------------------------!"]) @@ -14,6 +13,10 @@ record(lsi, "longstr3") { field(INP, {const: "!----------------------------------------------!"}) } +record(stringin, "longstr4") { + field(INP, ["One","Two","Three","Four"]) +} + record(ai, "emptylink" ) { field(INP, {calc: {expr:"0"}}) }