/src/util-linux/lib/buffer.c
Line | Count | Source |
1 | | /* |
2 | | * No copyright is claimed. This code is in the public domain; do with |
3 | | * it what you wish. |
4 | | * |
5 | | * Written by Karel Zak <kzak@redhat.com> |
6 | | */ |
7 | | #include "buffer.h" |
8 | | #include "mbsalign.h" |
9 | | #include "strutils.h" |
10 | | |
11 | | void ul_buffer_reset_data(struct ul_buffer *buf) |
12 | 0 | { |
13 | 0 | if (buf->begin) |
14 | 0 | memset(buf->begin, 0, buf->sz); |
15 | 0 | buf->end = buf->begin; |
16 | |
|
17 | 0 | if (buf->ptrs && buf->nptrs) |
18 | 0 | memset(buf->ptrs, 0, buf->nptrs * sizeof(char *)); |
19 | 0 | } |
20 | | |
21 | | static inline char *ul_buffer_free_data_common(struct ul_buffer *buf, bool steal) |
22 | 0 | { |
23 | 0 | char *t = NULL; |
24 | |
|
25 | 0 | assert(buf); |
26 | |
|
27 | 0 | if (steal) |
28 | 0 | t = buf->begin; |
29 | 0 | else |
30 | 0 | free(buf->begin); |
31 | 0 | buf->begin = NULL; |
32 | 0 | buf->end = NULL; |
33 | 0 | buf->sz = 0; |
34 | |
|
35 | 0 | free(buf->ptrs); |
36 | 0 | buf->ptrs = NULL; |
37 | 0 | buf->nptrs = 0; |
38 | |
|
39 | 0 | free(buf->encoded); |
40 | 0 | buf->encoded = NULL; |
41 | 0 | buf->encoded_sz = 0; |
42 | |
|
43 | 0 | return t; |
44 | 0 | } |
45 | | |
46 | | void ul_buffer_free_data(struct ul_buffer *buf) |
47 | 0 | { |
48 | 0 | ul_buffer_free_data_common(buf, false); |
49 | 0 | } |
50 | | |
51 | | char *ul_buffer_steal_string(struct ul_buffer *buf) |
52 | 0 | { |
53 | 0 | if (ul_buffer_is_empty(buf)) { |
54 | 0 | ul_buffer_free_data(buf); |
55 | 0 | return NULL; |
56 | 0 | } |
57 | | |
58 | 0 | return ul_buffer_free_data_common(buf, true); |
59 | 0 | } |
60 | | |
61 | | void ul_buffer_set_chunksize(struct ul_buffer *buf, size_t sz) |
62 | 8.65k | { |
63 | 8.65k | buf->chunksize = sz; |
64 | 8.65k | } |
65 | | |
66 | | int ul_buffer_is_empty(struct ul_buffer *buf) |
67 | 46.1k | { |
68 | 46.1k | return buf->begin == buf->end; |
69 | 46.1k | } |
70 | | |
71 | | int ul_buffer_save_pointer(struct ul_buffer *buf, unsigned short ptr_idx) |
72 | 0 | { |
73 | 0 | if (ptr_idx >= buf->nptrs) { |
74 | 0 | char **tmp = reallocarray(buf->ptrs, ptr_idx + 1, sizeof(char *)); |
75 | |
|
76 | 0 | if (!tmp) |
77 | 0 | return -EINVAL; |
78 | 0 | buf->ptrs = tmp; |
79 | 0 | buf->nptrs = ptr_idx + 1; |
80 | 0 | } |
81 | | |
82 | 0 | buf->ptrs[ptr_idx] = buf->end; |
83 | 0 | return 0; |
84 | 0 | } |
85 | | |
86 | | |
87 | | char *ul_buffer_get_pointer(struct ul_buffer *buf, unsigned short ptr_idx) |
88 | 0 | { |
89 | 0 | if (ptr_idx < buf->nptrs) |
90 | 0 | return buf->ptrs[ptr_idx]; |
91 | 0 | return NULL; |
92 | 0 | } |
93 | | |
94 | | /* returns length from begin to the pointer */ |
95 | | size_t ul_buffer_get_pointer_length(struct ul_buffer *buf, unsigned short ptr_idx) |
96 | 0 | { |
97 | 0 | char *ptr = ul_buffer_get_pointer(buf, ptr_idx); |
98 | |
|
99 | 0 | if (ptr && ptr > buf->begin) |
100 | 0 | return ptr - buf->begin; |
101 | 0 | return 0; |
102 | 0 | } |
103 | | |
104 | | /* returns width of data in safe encoding (from the begin to the pointer) */ |
105 | | size_t ul_buffer_get_safe_pointer_width(struct ul_buffer *buf, unsigned short ptr_idx) |
106 | 0 | { |
107 | 0 | size_t len = ul_buffer_get_pointer_length(buf, ptr_idx); |
108 | |
|
109 | 0 | if (!len) |
110 | 0 | return 0; |
111 | | |
112 | 0 | return mbs_safe_nwidth(buf->begin, len, NULL); |
113 | 0 | } |
114 | | |
115 | | void ul_buffer_refer_string(struct ul_buffer *buf, char *str) |
116 | 0 | { |
117 | 0 | if (buf->sz) |
118 | 0 | ul_buffer_free_data(buf); |
119 | 0 | buf->begin = str; |
120 | 0 | buf->sz = str ? strlen(str) : 0; |
121 | 0 | buf->end = buf->begin ? buf->begin + buf->sz : buf->begin; |
122 | 0 | } |
123 | | |
124 | | int ul_buffer_alloc_data(struct ul_buffer *buf, size_t sz) |
125 | 9.20k | { |
126 | 9.20k | char *tmp; |
127 | 9.20k | size_t len = 0; |
128 | | |
129 | 9.20k | assert(buf); |
130 | | |
131 | 9.20k | if (sz <= buf->sz) |
132 | 0 | return 0; |
133 | | |
134 | 9.20k | if (buf->end && buf->begin) |
135 | 548 | len = buf->end - buf->begin; |
136 | | |
137 | 9.20k | if (buf->chunksize) |
138 | 3.48k | sz = ((sz + buf->chunksize) / buf->chunksize) * buf->chunksize + 1; |
139 | | |
140 | 9.20k | tmp = realloc(buf->begin, sz); |
141 | 9.20k | if (!tmp) |
142 | 0 | return -ENOMEM; |
143 | | |
144 | 9.20k | buf->begin = tmp; |
145 | 9.20k | buf->end = buf->begin + len; |
146 | 9.20k | buf->sz = sz; |
147 | | |
148 | 9.20k | memset(buf->end, '\0', sz - len); |
149 | | |
150 | 9.20k | return 0; |
151 | 9.20k | } |
152 | | |
153 | | int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz) |
154 | 40.8k | { |
155 | 40.8k | size_t maxsz = 0; |
156 | | |
157 | 40.8k | if (!buf) |
158 | 0 | return -EINVAL; |
159 | 40.8k | if (!data) |
160 | 0 | return 0; |
161 | | |
162 | 40.8k | if (buf->begin && buf->end) |
163 | 32.1k | maxsz = buf->sz - (buf->end - buf->begin); |
164 | 40.8k | if (maxsz <= sz + 1) { |
165 | 9.20k | int rc = ul_buffer_alloc_data(buf, buf->sz + sz + 1); |
166 | 9.20k | if (rc) |
167 | 0 | return rc; |
168 | 9.20k | } |
169 | 40.8k | if (!buf->end) |
170 | 0 | return -EINVAL; /* make static analyzers happy */ |
171 | | |
172 | 40.8k | buf->end = mempcpy(buf->end, data, sz); |
173 | 40.8k | *buf->end = '\0'; /* make sure it's terminated */ |
174 | 40.8k | return 0; |
175 | 40.8k | } |
176 | | |
177 | | int ul_buffer_append_char(struct ul_buffer *buf, const char c) |
178 | 0 | { |
179 | 0 | return ul_buffer_append_data(buf, &c, 1); |
180 | 0 | } |
181 | | |
182 | | int ul_buffer_append_string(struct ul_buffer *buf, const char *str) |
183 | 0 | { |
184 | 0 | if (!str) |
185 | 0 | return 0; |
186 | | |
187 | 0 | return ul_buffer_append_data(buf, str, strlen(str)); |
188 | 0 | } |
189 | | |
190 | | int ul_buffer_append_ntimes(struct ul_buffer *buf, size_t n, const char *str) |
191 | 0 | { |
192 | 0 | size_t i; |
193 | 0 | size_t len = strlen(str); |
194 | |
|
195 | 0 | for (i = 0; len && i < n; i++) { |
196 | 0 | int rc = ul_buffer_append_data(buf, str, len); |
197 | 0 | if (rc) |
198 | 0 | return rc; |
199 | 0 | } |
200 | 0 | return 0; |
201 | 0 | } |
202 | | |
203 | | int ul_buffer_appendf(struct ul_buffer *buf, const char *format, ...) |
204 | 0 | { |
205 | 0 | va_list ap; |
206 | 0 | int res; |
207 | |
|
208 | 0 | va_start(ap, format); |
209 | 0 | res = ul_buffer_appendvf(buf, format, ap); |
210 | 0 | va_end(ap); |
211 | |
|
212 | 0 | return res; |
213 | 0 | } |
214 | | |
215 | | int ul_buffer_appendvf(struct ul_buffer *buf, const char *format, va_list ap) |
216 | 0 | { |
217 | 0 | char *val; |
218 | 0 | int sz; |
219 | 0 | int res; |
220 | |
|
221 | 0 | sz = vasprintf(&val, format, ap); |
222 | 0 | if (sz < 0) |
223 | 0 | return -errno; |
224 | | |
225 | | /* Like strlen(), sz doesn't include the last null byte. */ |
226 | 0 | res = ul_buffer_append_data(buf, val, sz); |
227 | 0 | free(val); |
228 | |
|
229 | 0 | return res; |
230 | 0 | } |
231 | | |
232 | | int ul_buffer_set_data(struct ul_buffer *buf, const char *data, size_t sz) |
233 | 0 | { |
234 | 0 | ul_buffer_reset_data(buf); |
235 | 0 | return ul_buffer_append_data(buf, data, sz); |
236 | 0 | } |
237 | | |
238 | | char *ul_buffer_get_data(struct ul_buffer *buf, size_t *sz, size_t *width) |
239 | 27.8k | { |
240 | 27.8k | if (sz) |
241 | 0 | *sz = buf->end - buf->begin; |
242 | 27.8k | if (width) |
243 | 0 | *width = buf->begin && *buf->begin ? mbs_width(buf->begin) : 0; |
244 | 27.8k | return buf->begin; |
245 | 27.8k | } |
246 | | |
247 | | char *ul_buffer_get_string(struct ul_buffer *buf, size_t *sz, size_t *width) |
248 | 0 | { |
249 | 0 | char *ret; |
250 | |
|
251 | 0 | ret = ul_buffer_get_data(buf, sz, width); |
252 | | |
253 | | /* data in buffer is already zero-terminated */ |
254 | 0 | if (sz) |
255 | 0 | *sz = *sz + 1; |
256 | |
|
257 | 0 | return ret; |
258 | 0 | } |
259 | | |
260 | | /* size of allocated area (!= size of stored data */ |
261 | | size_t ul_buffer_get_bufsiz(struct ul_buffer *buf) |
262 | 0 | { |
263 | 0 | return buf->sz; |
264 | 0 | } |
265 | | |
266 | | size_t ul_buffer_get_datasiz(struct ul_buffer *buf) |
267 | 0 | { |
268 | 0 | return buf->end - buf->begin; |
269 | 0 | } |
270 | | |
271 | | /* encode data by mbs_safe_encode() to avoid control and non-printable chars */ |
272 | | char *ul_buffer_get_safe_data(struct ul_buffer *buf, size_t *sz, size_t *width, const char *safechars) |
273 | 0 | { |
274 | 0 | char *data = ul_buffer_get_data(buf, NULL, NULL); |
275 | 0 | size_t encsz, wsz = 0; |
276 | 0 | char *res = NULL; |
277 | |
|
278 | 0 | if (!data) |
279 | 0 | goto nothing; |
280 | | |
281 | 0 | encsz = mbs_safe_encode_size(buf->sz) + 1; |
282 | 0 | if (encsz > buf->encoded_sz) { |
283 | 0 | char *tmp = realloc(buf->encoded, encsz); |
284 | 0 | if (!tmp) |
285 | 0 | goto nothing; |
286 | 0 | buf->encoded = tmp; |
287 | 0 | buf->encoded_sz = encsz; |
288 | 0 | } |
289 | | |
290 | 0 | res = mbs_safe_encode_to_buffer(data, &wsz, buf->encoded, buf->encoded_sz, safechars); |
291 | 0 | if (!res || !wsz || wsz == (size_t) -1) |
292 | 0 | goto nothing; |
293 | | |
294 | 0 | if (width) |
295 | 0 | *width = wsz; |
296 | 0 | if (sz) |
297 | 0 | *sz = strlen(res); |
298 | 0 | return res; |
299 | 0 | nothing: |
300 | 0 | if (width) |
301 | 0 | *width = 0; |
302 | 0 | if (sz) |
303 | 0 | *sz = 0; |
304 | | return NULL; |
305 | 0 | } |
306 | | |
307 | | |
308 | | #ifdef TEST_PROGRAM_BUFFER |
309 | | |
310 | | enum { |
311 | | PTR_AAA = 0, |
312 | | PTR_BBB, |
313 | | }; |
314 | | |
315 | | int main(void) |
316 | | { |
317 | | struct ul_buffer buf = UL_INIT_BUFFER; |
318 | | char *str; |
319 | | size_t sz = 0; |
320 | | |
321 | | ul_buffer_set_chunksize(&buf, 16); |
322 | | |
323 | | ul_buffer_append_string(&buf, "AAA"); |
324 | | ul_buffer_append_data(&buf, "=", 1); |
325 | | ul_buffer_append_string(&buf, "aaa"); |
326 | | ul_buffer_save_pointer(&buf, PTR_AAA); |
327 | | |
328 | | ul_buffer_append_data(&buf, ",", 1); |
329 | | ul_buffer_append_string(&buf, "BBB"); |
330 | | ul_buffer_append_string(&buf, "="); |
331 | | ul_buffer_append_string(&buf, "bbb"); |
332 | | ul_buffer_save_pointer(&buf, PTR_BBB); |
333 | | |
334 | | str = ul_buffer_get_data(&buf, &sz, NULL); |
335 | | printf("data [%zu] '%s'\n", sz, str); |
336 | | |
337 | | printf(" pointer data len: AAA=%zu, BBB=%zu\n", |
338 | | ul_buffer_get_pointer_length(&buf, PTR_AAA), |
339 | | ul_buffer_get_pointer_length(&buf, PTR_BBB)); |
340 | | printf(" pointer data width: AAA=%zu, BBB=%zu\n", |
341 | | ul_buffer_get_safe_pointer_width(&buf, PTR_AAA), |
342 | | ul_buffer_get_safe_pointer_width(&buf, PTR_BBB)); |
343 | | |
344 | | ul_buffer_reset_data(&buf); |
345 | | ul_buffer_append_string(&buf, "This is really long string to test the buffer function."); |
346 | | ul_buffer_save_pointer(&buf, PTR_AAA); |
347 | | ul_buffer_append_string(&buf, " YES!"); |
348 | | str = ul_buffer_get_data(&buf, &sz, NULL); |
349 | | printf("data [%zu] '%s'\n", sz, str); |
350 | | printf(" pointer data len: AAA=%zu\n", ul_buffer_get_pointer_length(&buf, PTR_AAA)); |
351 | | |
352 | | ul_buffer_free_data(&buf); |
353 | | str = strdup("foo"); |
354 | | ul_buffer_refer_string(&buf, str); |
355 | | ul_buffer_append_data(&buf, ",", 1); |
356 | | ul_buffer_append_string(&buf, "bar"); |
357 | | str = ul_buffer_get_data(&buf, &sz, NULL); |
358 | | printf("data [%zu] '%s'\n", sz, str); |
359 | | |
360 | | ul_buffer_free_data(&buf); |
361 | | ul_buffer_appendf(&buf, "a%se%d", "bcd", 10); |
362 | | str = ul_buffer_get_data(&buf, &sz, NULL); |
363 | | printf("data [%zu] '%s'\n", sz, str); |
364 | | |
365 | | ul_buffer_free_data(&buf); |
366 | | ul_buffer_append_char(&buf, 'x'); |
367 | | { |
368 | | char c = 'y'; |
369 | | ul_buffer_append_char(&buf, c); |
370 | | } |
371 | | { |
372 | | const char c = 'z'; |
373 | | ul_buffer_append_char(&buf, c); |
374 | | } |
375 | | str = ul_buffer_get_data(&buf, &sz, NULL); |
376 | | printf("data [%zu] '%s'\n", sz, str); |
377 | | |
378 | | ul_buffer_free_data(&buf); |
379 | | |
380 | | return EXIT_SUCCESS; |
381 | | } |
382 | | #endif /* TEST_PROGRAM_BUFFER */ |