Commit | Line | Data |
---|---|---|
805e021f CE |
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 | } |