From: Nathaniel Wesley Filardo Date: Sun, 30 Mar 2014 02:56:21 +0000 (-0400) Subject: Add a small string formatting utility to opr X-Git-Tag: openafs-stable-1_8_0pre1~690 X-Git-Url: http://git.openafs.org/?p=openafs.git;a=commitdiff_plain;h=f4ab11a8dd562bd7ee11c45e51814281d64c866c Add a small string formatting utility to opr This is to be used by the (coming next) vos-foreach utility, but it seemed sufficiently general and useful to break out into its own free-standing component. Change-Id: I92c3a615fecb80e1766f78492b229a826a23e18a Reviewed-on: http://gerrit.openafs.org/10965 Tested-by: BuildBot Reviewed-by: Jeffrey Altman Reviewed-by: D Brashear --- diff --git a/src/opr/Makefile.in b/src/opr/Makefile.in index a896dbc..af6a61b 100644 --- a/src/opr/Makefile.in +++ b/src/opr/Makefile.in @@ -3,12 +3,13 @@ include @TOP_OBJDIR@/src/config/Makefile.config include @TOP_OBJDIR@/src/config/Makefile.pthread include @TOP_OBJDIR@/src/config/Makefile.libtool -LT_objs = assert.lo casestrcpy.lo dict.lo proc.lo rbtree.lo uuid.lo +LT_objs = assert.lo casestrcpy.lo dict.lo fmt.lo proc.lo rbtree.lo uuid.lo LT_libs = $(LIB_hcrypto) $(LIB_roken) HEADERS = $(TOP_INCDIR)/afs/opr.h \ $(TOP_INCDIR)/afs/opr_assert.h \ $(TOP_INCDIR)/opr/dict.h \ + $(TOP_INCDIR)/opr/fmt.h \ $(TOP_INCDIR)/opr/jhash.h \ $(TOP_INCDIR)/opr/lock.h \ $(TOP_INCDIR)/opr/lockstub.h \ @@ -35,7 +36,6 @@ $(TOP_LIBDIR)/libopr.a: libopr.a $(TOP_LIBDIR)/libopr_pic.a: libopr_pic.la $(INSTALL_DATA) .libs/libopr_pic.a $@ - $(TOP_INCDIR)/afs/opr.h: opr.h $(INSTALL_DATA) $? $@ @@ -45,6 +45,9 @@ $(TOP_INCDIR)/afs/opr_assert.h: ${srcdir}/opr_assert.h $(TOP_INCDIR)/opr/dict.h: ${srcdir}/dict.h $(INSTALL_DATA) $? $@ +$(TOP_INCDIR)/opr/fmt.h: ${srcdir}/fmt.h + $(INSTALL_DATA) $? $@ + $(TOP_INCDIR)/opr/jhash.h: ${srcdir}/jhash.h $(INSTALL_DATA) $? $@ diff --git a/src/opr/NTMakefile b/src/opr/NTMakefile index 9fc9b09..7f49d3b 100644 --- a/src/opr/NTMakefile +++ b/src/opr/NTMakefile @@ -14,6 +14,7 @@ INCFILES = \ $(INCFILEDIR)\opr.h \ $(INCFILEDIR)\opr_assert.h \ $(DESTDIR)\include\opr\dict.h \ + $(DESTDIR)\include\opr\fmt.h \ $(DESTDIR)\include\opr\jhash.h \ $(DESTDIR)\include\opr\proc.h \ $(DESTDIR)\include\opr\queue.h \ @@ -35,6 +36,7 @@ LIBOBJS = \ $(OUT)\assert.obj \ $(OUT)\casestrcpy.obj \ $(OUT)\dict.obj \ + $(OUT)\fmt.obj \ $(OUT)\proc.obj \ $(OUT)\rbtree.obj \ $(OUT)\uuid.obj \ diff --git a/src/opr/fmt.c b/src/opr/fmt.c new file mode 100644 index 0000000..2bd8ee8 --- /dev/null +++ b/src/opr/fmt.c @@ -0,0 +1,142 @@ +/* + * Copyright 2014, Nathaniel Wesley Filardo. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "fmt.h" + +struct opr_fmt_ctx_priv_s { + const char *inp; + char *outp; + int outleft; + int written; +}; + +static void +opr_fmt_cb(opr_fmt_ctx *x, char c) { + x->priv->written++; + + if(x->priv->outleft > 0) { + *(x->priv->outp++) = c; + x->priv->outleft--; + } +} + +static int +opr_fmt_internal(opr_fmt_ctx *ctx, va_list va) +{ + opr_fmt_ctx_priv *priv = ctx->priv; + char c; + char sawpct = 0; + while((c = *(priv->inp++))) { + if(sawpct == 0) { + if(c != '%') { + opr_fmt_cb(ctx, c); + } else { + sawpct = 1; + } + } else { + opr_fmtr fmtr; + if((fmtr = ctx->fmtrs[(unsigned int) c]) != NULL) { + switch(fmtr(ctx, c, va)) { + case 1: break; + case 0: sawpct = 0; break; + case -1: return -1; + default: opr_Assert(0); + } + } else { + opr_fmt_cb(ctx, c); + sawpct = 0; + } + } + } + opr_fmt_cb(ctx, '\0'); priv->written--; + + return priv->written; +} + +/** + * Carry out a %-escaped variable interpolation according to a given set of + * callbacks, user data, and format string. Output is placed into the given + * buffer, and no more than the indicated number of bytes will be written. + * + * Each formatter callback receives + * - the formatting context + * - the escape character being processed + * - the suffix of the varargs passed to opr_fmt + * To write to the output buffer, it should invoke ctx->put with ctx and the + * desired emission character. It should return one of 0 to indicate + * success and exit from escape state; 1 to indicate success and a desire + * to continue in escape state (e.g. while scanning for modifiers as in + * %0x); or -1 to indicate failure, which will abort the whole opr_fmt call. + * + * @param fmtrs Is a 256-entry array of callbacks, one for each possible + * unsigned char value. (char is cast to an unsigned int, + * internally.) + * @param user Passed through to the callbacks for callee's use. + * @param out Output buffer. Will always be properly nul-terminated on + * successful (i.e. non-negative) return. + * @param n Maximum number of bytes to write to the output buffer. + * @param fstr The format string. Additional arguments are passed to the + * callbacks for their use ala printf. + * + * @return The number of bytes that were (if >= 0 and < n) or would have been + * (if >= n) written to the buffer, excluding the terminating nul + * byte; in the latter case, the buffer is full of the first n-1 + * bytes of the string (and is properly nul-terminated). (This + * follows C99's printf conventions and may allow user code to + * repeat the call with a big enough buffer the next time, rather + * than needing to probe for a large enough buffer.) If the return + * value is < 0, an error occurred during one of the callbacks' + * operation. + */ +int +opr_fmt(opr_fmtr *fmtrs, void *user, char *out, int n, const char *fstr, ...) +{ + opr_fmt_ctx_priv priv; + opr_fmt_ctx ctx; + int ret; + va_list va; + + priv.inp = fstr; + priv.outp = out; + priv.outleft = n; + priv.written = 0; + + ctx.fmtrs = fmtrs; + ctx.user = user ; + ctx.put = opr_fmt_cb; + ctx.priv = &priv; + + va_start(va, fstr); + ret = opr_fmt_internal(&ctx, va); + va_end(va); + + if (ret > 0 && ret >= n) { + out[n-1] = '\0'; + } + + return ret; +} diff --git a/src/opr/fmt.h b/src/opr/fmt.h new file mode 100644 index 0000000..68f676b --- /dev/null +++ b/src/opr/fmt.h @@ -0,0 +1,42 @@ +/* + * Copyright 2014, Nathaniel Wesley Filardo. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _OPR_FMT_H_ +#define _OPR_FMT_H_ + +typedef struct opr_fmt_ctx_priv_s opr_fmt_ctx_priv; +typedef struct opr_fmt_ctx_s opr_fmt_ctx; + +typedef int (*opr_fmtr)(opr_fmt_ctx *, char, va_list); + +struct opr_fmt_ctx_s { + opr_fmtr *fmtrs; /* Byte-wise dispatch table; 256 entries */ + void *user; /* User-controlled private data */ + void (*put)(opr_fmt_ctx *, char); /* Write a character to the buffer */ + + opr_fmt_ctx_priv *priv; /* Private for internals of opr_fmt */ +}; + +int opr_fmt(opr_fmtr *fmtrs, void *user, char *out, int n, const char *fstr, ...); + +#endif diff --git a/src/opr/liboafs_opr.la.sym b/src/opr/liboafs_opr.la.sym index d95b316..6ced340 100644 --- a/src/opr/liboafs_opr.la.sym +++ b/src/opr/liboafs_opr.la.sym @@ -2,6 +2,7 @@ opr_AssertionFailed opr_dict_Free opr_dict_Init opr_lcstring +opr_fmt opr_procsize opr_rbtree_first opr_rbtree_init diff --git a/tests/TESTS b/tests/TESTS index 2f30cbb..f8f4e1f 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -6,6 +6,7 @@ auth/authcon auth/realms cmd/command opr/dict +opr/fmt opr/jhash opr/queues opr/rbtree diff --git a/tests/opr/Makefile.in b/tests/opr/Makefile.in index 30a4d95..5e7057c 100644 --- a/tests/opr/Makefile.in +++ b/tests/opr/Makefile.in @@ -7,13 +7,16 @@ MODULE_CFLAGS = -I$(srcdir)/../.. LIBS=../tap/libtap.a $(abs_top_builddir)/src/opr/liboafs_opr.la -tests = dict-t jhash-t queues-t rbtree-t time-t uuid-t +tests = dict-t fmt-t jhash-t queues-t rbtree-t time-t uuid-t all check test tests: $(tests) dict-t: dict-t.o $(LT_LDRULE_static) dict-t.o $(LIBS) $(XLIBS) +fmt-t: fmt-t.o + $(LT_LDRULE_static) fmt-t.o $(LIBS) $(XLIBS) + queues-t: queues-t.o $(LT_LDRULE_static) queues-t.o ../tap/libtap.a $(XLIBS) diff --git a/tests/opr/fmt-t.c b/tests/opr/fmt-t.c new file mode 100644 index 0000000..4aa57d5 --- /dev/null +++ b/tests/opr/fmt-t.c @@ -0,0 +1,56 @@ +/* + * Copyright 2014, Nathaniel Wesley Filardo. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +#include +#include +#include +#include +#include + +int +testfmtr(opr_fmt_ctx *ctx, char c, va_list va) { + const char *str = va_arg(va, const char *); + while(*str) { ctx->put(ctx, *str++); } + return 0; +} + +int +testfmtr2(opr_fmt_ctx *ctx, char c, va_list va) { + char *str = ctx->user; + while (*str) { ctx->put(ctx, *str++); } + return 0; +} + +int main() +{ + static opr_fmtr fmtrs[256] = { ['s'] = testfmtr, ['t'] = testfmtr2 }; + static char buf[100]; + + plan(1); + + opr_fmt(fmtrs, "EHLO", buf, 100, "FOO %s %t", "abc"); + ok(0 == strcmp(buf,"FOO abc EHLO"), "Basic substitution test"); + + return 0; +}