Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / opr / fmt.c
1 /*
2 * Copyright 2014, Nathaniel Wesley Filardo.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to permit
9 * persons to whom the Software is furnished to do so, subject to the
10 * following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
18 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <afsconfig.h>
25 #include <afs/param.h>
26 #include <roken.h>
27 #include <afs/opr.h>
28 #include "fmt.h"
29
30 struct opr_fmt_ctx_priv_s {
31 const char *inp;
32 char *outp;
33 int outleft;
34 int written;
35 };
36
37 static void
38 opr_fmt_cb(opr_fmt_ctx *x, char c) {
39 x->priv->written++;
40
41 if(x->priv->outleft > 0) {
42 *(x->priv->outp++) = c;
43 x->priv->outleft--;
44 }
45 }
46
47 static int
48 opr_fmt_internal(opr_fmt_ctx *ctx, va_list va)
49 {
50 opr_fmt_ctx_priv *priv = ctx->priv;
51 char c;
52 char sawpct = 0;
53 while((c = *(priv->inp++))) {
54 if(sawpct == 0) {
55 if(c != '%') {
56 opr_fmt_cb(ctx, c);
57 } else {
58 sawpct = 1;
59 }
60 } else {
61 opr_fmtr fmtr;
62 if((fmtr = ctx->fmtrs[(unsigned int) c]) != NULL) {
63 switch(fmtr(ctx, c, va)) {
64 case 1: break;
65 case 0: sawpct = 0; break;
66 case -1: return -1;
67 default: opr_Assert(0);
68 }
69 } else {
70 opr_fmt_cb(ctx, c);
71 sawpct = 0;
72 }
73 }
74 }
75 opr_fmt_cb(ctx, '\0'); priv->written--;
76
77 return priv->written;
78 }
79
80 /**
81 * Carry out a %-escaped variable interpolation according to a given set of
82 * callbacks, user data, and format string. Output is placed into the given
83 * buffer, and no more than the indicated number of bytes will be written.
84 *
85 * Each formatter callback receives
86 * - the formatting context
87 * - the escape character being processed
88 * - the suffix of the varargs passed to opr_fmt
89 * To write to the output buffer, it should invoke ctx->put with ctx and the
90 * desired emission character. It should return one of 0 to indicate
91 * success and exit from escape state; 1 to indicate success and a desire
92 * to continue in escape state (e.g. while scanning for modifiers as in
93 * %0x); or -1 to indicate failure, which will abort the whole opr_fmt call.
94 *
95 * @param fmtrs Is a 256-entry array of callbacks, one for each possible
96 * unsigned char value. (char is cast to an unsigned int,
97 * internally.)
98 * @param user Passed through to the callbacks for callee's use.
99 * @param out Output buffer. Will always be properly nul-terminated on
100 * successful (i.e. non-negative) return.
101 * @param n Maximum number of bytes to write to the output buffer.
102 * @param fstr The format string. Additional arguments are passed to the
103 * callbacks for their use ala printf.
104 *
105 * @return The number of bytes that were (if >= 0 and < n) or would have been
106 * (if >= n) written to the buffer, excluding the terminating nul
107 * byte; in the latter case, the buffer is full of the first n-1
108 * bytes of the string (and is properly nul-terminated). (This
109 * follows C99's printf conventions and may allow user code to
110 * repeat the call with a big enough buffer the next time, rather
111 * than needing to probe for a large enough buffer.) If the return
112 * value is < 0, an error occurred during one of the callbacks'
113 * operation.
114 */
115 int
116 opr_fmt(opr_fmtr *fmtrs, void *user, char *out, int n, const char *fstr, ...)
117 {
118 opr_fmt_ctx_priv priv;
119 opr_fmt_ctx ctx;
120 int ret;
121 va_list va;
122
123 priv.inp = fstr;
124 priv.outp = out;
125 priv.outleft = n;
126 priv.written = 0;
127
128 ctx.fmtrs = fmtrs;
129 ctx.user = user ;
130 ctx.put = opr_fmt_cb;
131 ctx.priv = &priv;
132
133 va_start(va, fstr);
134 ret = opr_fmt_internal(&ctx, va);
135 va_end(va);
136
137 if (ret > 0 && ret >= n) {
138 out[n-1] = '\0';
139 }
140
141 return ret;
142 }