]> git.proxmox.com Git - mirror_frr.git/blob - lib/strformat.c
Merge pull request #8348 from chiragshah6/mdev
[mirror_frr.git] / lib / strformat.c
1 /*
2 * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "compiler.h"
22
23 #include <string.h>
24 #include <ctype.h>
25
26 #include "printfrr.h"
27
28 printfrr_ext_autoreg_p("HX", printfrr_hexdump)
29 static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea,
30 const void *ptr)
31 {
32 ssize_t ret = 0;
33 ssize_t input_len = printfrr_ext_len(ea);
34 char sep = ' ';
35 const uint8_t *pos, *end;
36
37 if (ea->fmt[0] == 'c') {
38 ea->fmt++;
39 sep = ':';
40 } else if (ea->fmt[0] == 'n') {
41 ea->fmt++;
42 sep = '\0';
43 }
44
45 if (input_len < 0)
46 return 0;
47
48 for (pos = ptr, end = pos + input_len; pos < end; pos++) {
49 if (sep && pos != ptr)
50 ret += bputch(buf, sep);
51 ret += bputhex(buf, *pos);
52 }
53
54 return ret;
55 }
56
57 /* string analog for hexdumps / the "this." in ("74 68 69 73 0a |this.|") */
58
59 printfrr_ext_autoreg_p("HS", printfrr_hexdstr)
60 static ssize_t printfrr_hexdstr(struct fbuf *buf, struct printfrr_eargs *ea,
61 const void *ptr)
62 {
63 ssize_t ret = 0;
64 ssize_t input_len = printfrr_ext_len(ea);
65 const uint8_t *pos, *end;
66
67 if (input_len < 0)
68 return 0;
69
70 for (pos = ptr, end = pos + input_len; pos < end; pos++) {
71 if (*pos >= 0x20 && *pos < 0x7f)
72 ret += bputch(buf, *pos);
73 else
74 ret += bputch(buf, '.');
75 }
76
77 return ret;
78 }
79
80 enum escape_flags {
81 ESC_N_R_T = (1 << 0), /* use \n \r \t instead of \x0a ...*/
82 ESC_SPACE = (1 << 1), /* \ */
83 ESC_BACKSLASH = (1 << 2), /* \\ */
84 ESC_DBLQUOTE = (1 << 3), /* \" */
85 ESC_SGLQUOTE = (1 << 4), /* \' */
86 ESC_BACKTICK = (1 << 5), /* \` */
87 ESC_DOLLAR = (1 << 6), /* \$ */
88 ESC_CLBRACKET = (1 << 7), /* \] for RFC5424 syslog */
89 ESC_OTHER = (1 << 8), /* remaining non-alpha */
90
91 ESC_ALL = ESC_N_R_T | ESC_SPACE | ESC_BACKSLASH | ESC_DBLQUOTE
92 | ESC_SGLQUOTE | ESC_DOLLAR | ESC_OTHER,
93 ESC_QUOTSTRING = ESC_N_R_T | ESC_BACKSLASH | ESC_DBLQUOTE,
94 /* if needed: ESC_SHELL = ... */
95 };
96
97 static ssize_t bquote(struct fbuf *buf, const uint8_t *pos, size_t len,
98 unsigned int flags)
99 {
100 ssize_t ret = 0;
101 const uint8_t *end = pos + len;
102
103 for (; pos < end; pos++) {
104 /* here's to hoping this might be a bit faster... */
105 if (__builtin_expect(!!isalnum(*pos), 1)) {
106 ret += bputch(buf, *pos);
107 continue;
108 }
109
110 switch (*pos) {
111 case '%':
112 case '+':
113 case ',':
114 case '-':
115 case '.':
116 case '/':
117 case ':':
118 case '@':
119 case '_':
120 ret += bputch(buf, *pos);
121 continue;
122
123 case '\r':
124 if (!(flags & ESC_N_R_T))
125 break;
126 ret += bputch(buf, '\\');
127 ret += bputch(buf, 'r');
128 continue;
129 case '\n':
130 if (!(flags & ESC_N_R_T))
131 break;
132 ret += bputch(buf, '\\');
133 ret += bputch(buf, 'n');
134 continue;
135 case '\t':
136 if (!(flags & ESC_N_R_T))
137 break;
138 ret += bputch(buf, '\\');
139 ret += bputch(buf, 't');
140 continue;
141
142 case ' ':
143 if (flags & ESC_SPACE)
144 ret += bputch(buf, '\\');
145 ret += bputch(buf, *pos);
146 continue;
147
148 case '\\':
149 if (flags & ESC_BACKSLASH)
150 ret += bputch(buf, '\\');
151 ret += bputch(buf, *pos);
152 continue;
153
154 case '"':
155 if (flags & ESC_DBLQUOTE)
156 ret += bputch(buf, '\\');
157 ret += bputch(buf, *pos);
158 continue;
159
160 case '\'':
161 if (flags & ESC_SGLQUOTE)
162 ret += bputch(buf, '\\');
163 ret += bputch(buf, *pos);
164 continue;
165
166 case '`':
167 if (flags & ESC_BACKTICK)
168 ret += bputch(buf, '\\');
169 ret += bputch(buf, *pos);
170 continue;
171
172 case '$':
173 if (flags & ESC_DOLLAR)
174 ret += bputch(buf, '\\');
175 ret += bputch(buf, *pos);
176 continue;
177
178 case ']':
179 if (flags & ESC_CLBRACKET)
180 ret += bputch(buf, '\\');
181 ret += bputch(buf, *pos);
182 continue;
183
184 /* remaining: !#&'()*;<=>?[^{|}~ */
185
186 default:
187 if (*pos >= 0x20 && *pos < 0x7f) {
188 if (flags & ESC_OTHER)
189 ret += bputch(buf, '\\');
190 ret += bputch(buf, *pos);
191 continue;
192 }
193 }
194 ret += bputch(buf, '\\');
195 ret += bputch(buf, 'x');
196 ret += bputhex(buf, *pos);
197 }
198
199 return ret;
200 }
201
202 printfrr_ext_autoreg_p("SE", printfrr_escape)
203 static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea,
204 const void *vptr)
205 {
206 ssize_t len = printfrr_ext_len(ea);
207 const uint8_t *ptr = vptr;
208 bool null_is_empty = false;
209
210 if (ea->fmt[0] == 'n') {
211 null_is_empty = true;
212 ea->fmt++;
213 }
214
215 if (!ptr) {
216 if (null_is_empty)
217 return 0;
218 return bputs(buf, "(null)");
219 }
220
221 if (len < 0)
222 len = strlen((const char *)ptr);
223
224 return bquote(buf, ptr, len, ESC_ALL);
225 }
226
227 printfrr_ext_autoreg_p("SQ", printfrr_quote)
228 static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea,
229 const void *vptr)
230 {
231 ssize_t len = printfrr_ext_len(ea);
232 const uint8_t *ptr = vptr;
233 ssize_t ret = 0;
234 bool null_is_empty = false;
235 bool do_quotes = false;
236 unsigned int flags = ESC_QUOTSTRING;
237
238 while (ea->fmt[0]) {
239 switch (ea->fmt[0]) {
240 case 'n':
241 null_is_empty = true;
242 ea->fmt++;
243 continue;
244 case 'q':
245 do_quotes = true;
246 ea->fmt++;
247 continue;
248 case 's':
249 flags |= ESC_CLBRACKET;
250 flags &= ~ESC_N_R_T;
251 ea->fmt++;
252 continue;
253 }
254 break;
255 }
256
257 if (!ptr) {
258 if (null_is_empty)
259 return bputs(buf, do_quotes ? "\"\"" : "");
260 return bputs(buf, "(null)");
261 }
262
263 if (len < 0)
264 len = strlen((const char *)ptr);
265
266 if (do_quotes)
267 ret += bputch(buf, '"');
268 ret += bquote(buf, ptr, len, flags);
269 if (do_quotes)
270 ret += bputch(buf, '"');
271 return ret;
272 }