udev: String substitutions can be done in ENV, too
[systemd_ALT.git] / src / basic / hexdecoct.c
blob898ed83f862ac6dd759febcd4cefb446bbf39918
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <ctype.h>
4 #include <errno.h>
5 #include <stdint.h>
6 #include <stdlib.h>
8 #include "alloc-util.h"
9 #include "hexdecoct.h"
10 #include "macro.h"
11 #include "memory-util.h"
12 #include "string-util.h"
14 char octchar(int x) {
15 return '0' + (x & 7);
18 int unoctchar(char c) {
20 if (c >= '0' && c <= '7')
21 return c - '0';
23 return -EINVAL;
26 char decchar(int x) {
27 return '0' + (x % 10);
30 int undecchar(char c) {
32 if (c >= '0' && c <= '9')
33 return c - '0';
35 return -EINVAL;
38 char hexchar(int x) {
39 static const char table[16] = "0123456789abcdef";
41 return table[x & 15];
44 int unhexchar(char c) {
46 if (c >= '0' && c <= '9')
47 return c - '0';
49 if (c >= 'a' && c <= 'f')
50 return c - 'a' + 10;
52 if (c >= 'A' && c <= 'F')
53 return c - 'A' + 10;
55 return -EINVAL;
58 char *hexmem(const void *p, size_t l) {
59 const uint8_t *x;
60 char *r, *z;
62 assert(p || l == 0);
64 z = r = new(char, l * 2 + 1);
65 if (!r)
66 return NULL;
68 for (x = p; x && x < (const uint8_t*) p + l; x++) {
69 *(z++) = hexchar(*x >> 4);
70 *(z++) = hexchar(*x & 15);
73 *z = 0;
74 return r;
77 static int unhex_next(const char **p, size_t *l) {
78 int r;
80 assert(p);
81 assert(l);
83 /* Find the next non-whitespace character, and decode it. We
84 * greedily skip all preceding and all following whitespace. */
86 for (;;) {
87 if (*l == 0)
88 return -EPIPE;
90 if (!strchr(WHITESPACE, **p))
91 break;
93 /* Skip leading whitespace */
94 (*p)++, (*l)--;
97 r = unhexchar(**p);
98 if (r < 0)
99 return r;
101 for (;;) {
102 (*p)++, (*l)--;
104 if (*l == 0 || !strchr(WHITESPACE, **p))
105 break;
107 /* Skip following whitespace */
110 return r;
113 int unhexmem_full(
114 const char *p,
115 size_t l,
116 bool secure,
117 void **ret,
118 size_t *ret_len) {
120 _cleanup_free_ uint8_t *buf = NULL;
121 size_t buf_size;
122 const char *x;
123 uint8_t *z;
125 assert(p || l == 0);
127 if (l == SIZE_MAX)
128 l = strlen(p);
130 /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */
131 buf_size = (l + 1) / 2 + 1;
132 buf = malloc(buf_size);
133 if (!buf)
134 return -ENOMEM;
136 CLEANUP_ERASE_PTR(secure ? &buf : NULL, buf_size);
138 for (x = p, z = buf;;) {
139 int a, b;
141 a = unhex_next(&x, &l);
142 if (a == -EPIPE) /* End of string */
143 break;
144 if (a < 0)
145 return a;
147 b = unhex_next(&x, &l);
148 if (b < 0)
149 return b;
151 *(z++) = (uint8_t) a << 4 | (uint8_t) b;
154 *z = 0;
156 if (ret_len)
157 *ret_len = (size_t) (z - buf);
158 if (ret)
159 *ret = TAKE_PTR(buf);
161 return 0;
164 /* https://tools.ietf.org/html/rfc4648#section-6
165 * Notice that base32hex differs from base32 in the alphabet it uses.
166 * The distinction is that the base32hex representation preserves the
167 * order of the underlying data when compared as bytestrings, this is
168 * useful when representing NSEC3 hashes, as one can then verify the
169 * order of hashes directly from their representation. */
170 char base32hexchar(int x) {
171 static const char table[32] = "0123456789"
172 "ABCDEFGHIJKLMNOPQRSTUV";
174 return table[x & 31];
177 int unbase32hexchar(char c) {
178 unsigned offset;
180 if (c >= '0' && c <= '9')
181 return c - '0';
183 offset = '9' - '0' + 1;
185 if (c >= 'A' && c <= 'V')
186 return c - 'A' + offset;
188 return -EINVAL;
191 char *base32hexmem(const void *p, size_t l, bool padding) {
192 char *r, *z;
193 const uint8_t *x;
194 size_t len;
196 assert(p || l == 0);
198 if (padding)
199 /* five input bytes makes eight output bytes, padding is added so we must round up */
200 len = 8 * (l + 4) / 5;
201 else {
202 /* same, but round down as there is no padding */
203 len = 8 * l / 5;
205 switch (l % 5) {
206 case 4:
207 len += 7;
208 break;
209 case 3:
210 len += 5;
211 break;
212 case 2:
213 len += 4;
214 break;
215 case 1:
216 len += 2;
217 break;
221 z = r = malloc(len + 1);
222 if (!r)
223 return NULL;
225 for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
226 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
227 * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
228 *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
229 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
230 *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
231 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
232 *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
233 *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
234 *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */
235 *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */
238 switch (l % 5) {
239 case 4:
240 *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
241 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
242 *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
243 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
244 *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
245 *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */
246 *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */
247 if (padding)
248 *(z++) = '=';
250 break;
252 case 3:
253 *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
254 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
255 *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
256 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
257 *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */
258 if (padding) {
259 *(z++) = '=';
260 *(z++) = '=';
261 *(z++) = '=';
264 break;
266 case 2:
267 *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
268 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
269 *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
270 *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */
271 if (padding) {
272 *(z++) = '=';
273 *(z++) = '=';
274 *(z++) = '=';
275 *(z++) = '=';
278 break;
280 case 1:
281 *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
282 *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
283 if (padding) {
284 *(z++) = '=';
285 *(z++) = '=';
286 *(z++) = '=';
287 *(z++) = '=';
288 *(z++) = '=';
289 *(z++) = '=';
292 break;
295 *z = 0;
296 return r;
299 int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
300 _cleanup_free_ uint8_t *r = NULL;
301 int a, b, c, d, e, f, g, h;
302 uint8_t *z;
303 const char *x;
304 size_t len;
305 unsigned pad = 0;
307 assert(p || l == 0);
308 assert(mem);
309 assert(_len);
311 if (l == SIZE_MAX)
312 l = strlen(p);
314 /* padding ensures any base32hex input has input divisible by 8 */
315 if (padding && l % 8 != 0)
316 return -EINVAL;
318 if (padding) {
319 /* strip the padding */
320 while (l > 0 && p[l - 1] == '=' && pad < 7) {
321 pad++;
322 l--;
326 /* a group of eight input bytes needs five output bytes, in case of
327 * padding we need to add some extra bytes */
328 len = (l / 8) * 5;
330 switch (l % 8) {
331 case 7:
332 len += 4;
333 break;
334 case 5:
335 len += 3;
336 break;
337 case 4:
338 len += 2;
339 break;
340 case 2:
341 len += 1;
342 break;
343 case 0:
344 break;
345 default:
346 return -EINVAL;
349 z = r = malloc(len + 1);
350 if (!r)
351 return -ENOMEM;
353 for (x = p; x < p + (l / 8) * 8; x += 8) {
354 /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
355 * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
356 a = unbase32hexchar(x[0]);
357 if (a < 0)
358 return -EINVAL;
360 b = unbase32hexchar(x[1]);
361 if (b < 0)
362 return -EINVAL;
364 c = unbase32hexchar(x[2]);
365 if (c < 0)
366 return -EINVAL;
368 d = unbase32hexchar(x[3]);
369 if (d < 0)
370 return -EINVAL;
372 e = unbase32hexchar(x[4]);
373 if (e < 0)
374 return -EINVAL;
376 f = unbase32hexchar(x[5]);
377 if (f < 0)
378 return -EINVAL;
380 g = unbase32hexchar(x[6]);
381 if (g < 0)
382 return -EINVAL;
384 h = unbase32hexchar(x[7]);
385 if (h < 0)
386 return -EINVAL;
388 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
389 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
390 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
391 *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
392 *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */
395 switch (l % 8) {
396 case 7:
397 a = unbase32hexchar(x[0]);
398 if (a < 0)
399 return -EINVAL;
401 b = unbase32hexchar(x[1]);
402 if (b < 0)
403 return -EINVAL;
405 c = unbase32hexchar(x[2]);
406 if (c < 0)
407 return -EINVAL;
409 d = unbase32hexchar(x[3]);
410 if (d < 0)
411 return -EINVAL;
413 e = unbase32hexchar(x[4]);
414 if (e < 0)
415 return -EINVAL;
417 f = unbase32hexchar(x[5]);
418 if (f < 0)
419 return -EINVAL;
421 g = unbase32hexchar(x[6]);
422 if (g < 0)
423 return -EINVAL;
425 /* g == 000VV000 */
426 if (g & 7)
427 return -EINVAL;
429 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
430 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
431 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
432 *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
434 break;
435 case 5:
436 a = unbase32hexchar(x[0]);
437 if (a < 0)
438 return -EINVAL;
440 b = unbase32hexchar(x[1]);
441 if (b < 0)
442 return -EINVAL;
444 c = unbase32hexchar(x[2]);
445 if (c < 0)
446 return -EINVAL;
448 d = unbase32hexchar(x[3]);
449 if (d < 0)
450 return -EINVAL;
452 e = unbase32hexchar(x[4]);
453 if (e < 0)
454 return -EINVAL;
456 /* e == 000SSSS0 */
457 if (e & 1)
458 return -EINVAL;
460 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
461 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
462 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */
464 break;
465 case 4:
466 a = unbase32hexchar(x[0]);
467 if (a < 0)
468 return -EINVAL;
470 b = unbase32hexchar(x[1]);
471 if (b < 0)
472 return -EINVAL;
474 c = unbase32hexchar(x[2]);
475 if (c < 0)
476 return -EINVAL;
478 d = unbase32hexchar(x[3]);
479 if (d < 0)
480 return -EINVAL;
482 /* d == 000W0000 */
483 if (d & 15)
484 return -EINVAL;
486 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
487 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
489 break;
490 case 2:
491 a = unbase32hexchar(x[0]);
492 if (a < 0)
493 return -EINVAL;
495 b = unbase32hexchar(x[1]);
496 if (b < 0)
497 return -EINVAL;
499 /* b == 000YYY00 */
500 if (b & 3)
501 return -EINVAL;
503 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
505 break;
506 case 0:
507 break;
508 default:
509 return -EINVAL;
512 *z = 0;
514 *mem = TAKE_PTR(r);
515 *_len = len;
517 return 0;
520 /* https://tools.ietf.org/html/rfc4648#section-4 */
521 char base64char(int x) {
522 static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
523 "abcdefghijklmnopqrstuvwxyz"
524 "0123456789+/";
525 return table[x & 63];
528 /* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet,
529 * since we don't want "/" appear in interface names (since interfaces appear in sysfs as filenames).
530 * See section #5 of RFC 4648. */
531 char urlsafe_base64char(int x) {
532 static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
533 "abcdefghijklmnopqrstuvwxyz"
534 "0123456789-_";
535 return table[x & 63];
538 int unbase64char(char c) {
539 unsigned offset;
541 if (c >= 'A' && c <= 'Z')
542 return c - 'A';
544 offset = 'Z' - 'A' + 1;
546 if (c >= 'a' && c <= 'z')
547 return c - 'a' + offset;
549 offset += 'z' - 'a' + 1;
551 if (c >= '0' && c <= '9')
552 return c - '0' + offset;
554 offset += '9' - '0' + 1;
556 if (c == '+')
557 return offset;
559 offset++;
561 if (c == '/')
562 return offset;
564 return -EINVAL;
567 static void maybe_line_break(char **x, char *start, size_t line_break) {
568 size_t n;
570 assert(x);
571 assert(*x);
572 assert(start);
573 assert(*x >= start);
575 if (line_break == SIZE_MAX)
576 return;
578 n = *x - start;
580 if (n % (line_break + 1) == line_break)
581 *((*x)++) = '\n';
584 ssize_t base64mem_full(
585 const void *p,
586 size_t l,
587 size_t line_break,
588 char **ret) {
590 const uint8_t *x;
591 char *b, *z;
592 size_t m;
594 assert(p || l == 0);
595 assert(line_break > 0);
596 assert(ret);
598 /* three input bytes makes four output bytes, padding is added so we must round up */
599 m = 4 * (l + 2) / 3 + 1;
600 if (line_break != SIZE_MAX)
601 m += m / line_break;
603 z = b = malloc(m);
604 if (!b)
605 return -ENOMEM;
607 for (x = p; x && x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
608 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
609 maybe_line_break(&z, b, line_break);
610 *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
611 maybe_line_break(&z, b, line_break);
612 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
613 maybe_line_break(&z, b, line_break);
614 *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
615 maybe_line_break(&z, b, line_break);
616 *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
619 switch (l % 3) {
620 case 2:
621 maybe_line_break(&z, b, line_break);
622 *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
623 maybe_line_break(&z, b, line_break);
624 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
625 maybe_line_break(&z, b, line_break);
626 *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
627 maybe_line_break(&z, b, line_break);
628 *(z++) = '=';
629 break;
631 case 1:
632 maybe_line_break(&z, b, line_break);
633 *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
634 maybe_line_break(&z, b, line_break);
635 *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
636 maybe_line_break(&z, b, line_break);
637 *(z++) = '=';
638 maybe_line_break(&z, b, line_break);
639 *(z++) = '=';
640 break;
643 *z = 0;
644 *ret = b;
646 assert(z >= b); /* Let static analyzers know that the answer is non-negative. */
647 return z - b;
650 static ssize_t base64_append_width(
651 char **prefix,
652 size_t plen,
653 char sep,
654 size_t indent,
655 const void *p,
656 size_t l,
657 size_t width) {
659 _cleanup_free_ char *x = NULL;
660 char *t, *s;
661 size_t lines;
662 ssize_t len;
664 assert(prefix);
665 assert(*prefix || plen == 0);
666 assert(p || l == 0);
668 len = base64mem(p, l, &x);
669 if (len < 0)
670 return len;
671 if (len == 0)
672 return plen;
674 lines = DIV_ROUND_UP(len, width);
676 if (plen >= SSIZE_MAX - 1 - 1 ||
677 lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1))
678 return -ENOMEM;
680 t = realloc(*prefix, plen + 1 + 1 + (indent + width + 1) * lines);
681 if (!t)
682 return -ENOMEM;
684 s = t + plen;
685 for (size_t line = 0; line < lines; line++) {
686 size_t act = MIN(width, (size_t) len);
688 if (line > 0)
689 sep = '\n';
691 if (s > t) {
692 *s++ = sep;
693 if (sep == '\n')
694 s = mempset(s, ' ', indent);
697 s = mempcpy(s, x + width * line, act);
698 len -= act;
700 assert(len == 0);
702 *s = '\0';
703 *prefix = t;
704 return s - t;
707 ssize_t base64_append(
708 char **prefix,
709 size_t plen,
710 const void *p,
711 size_t l,
712 size_t indent,
713 size_t width) {
715 if (plen > width / 2 || plen + indent > width)
716 /* leave indent on the left, keep last column free */
717 return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent);
718 else
719 /* leave plen on the left, keep last column free */
720 return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1);
723 static int unbase64_next(const char **p, size_t *l) {
724 int ret;
726 assert(p);
727 assert(l);
729 /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
730 * greedily skip all preceding and all following whitespace. */
732 for (;;) {
733 if (*l == 0)
734 return -EPIPE;
736 if (!strchr(WHITESPACE, **p))
737 break;
739 /* Skip leading whitespace */
740 (*p)++, (*l)--;
743 if (**p == '=')
744 ret = INT_MAX; /* return padding as INT_MAX */
745 else {
746 ret = unbase64char(**p);
747 if (ret < 0)
748 return ret;
751 for (;;) {
752 (*p)++, (*l)--;
754 if (*l == 0)
755 break;
756 if (!strchr(WHITESPACE, **p))
757 break;
759 /* Skip following whitespace */
762 return ret;
765 int unbase64mem_full(
766 const char *p,
767 size_t l,
768 bool secure,
769 void **ret,
770 size_t *ret_size) {
772 _cleanup_free_ uint8_t *buf = NULL;
773 const char *x;
774 uint8_t *z;
775 size_t len;
777 assert(p || l == 0);
779 if (l == SIZE_MAX)
780 l = strlen(p);
782 /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
783 * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
784 len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
786 buf = malloc(len + 1);
787 if (!buf)
788 return -ENOMEM;
790 CLEANUP_ERASE_PTR(secure ? &buf : NULL, len);
792 for (x = p, z = buf;;) {
793 int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
795 a = unbase64_next(&x, &l);
796 if (a == -EPIPE) /* End of string */
797 break;
798 if (a < 0)
799 return a;
800 if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
801 return -EINVAL;
803 b = unbase64_next(&x, &l);
804 if (b < 0)
805 return b;
806 if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
807 return -EINVAL;
809 c = unbase64_next(&x, &l);
810 if (c < 0)
811 return c;
813 d = unbase64_next(&x, &l);
814 if (d < 0)
815 return d;
817 if (c == INT_MAX) { /* Padding at the third character */
819 if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
820 return -EINVAL;
822 /* b == 00YY0000 */
823 if (b & 15)
824 return -EINVAL;
826 if (l > 0) /* Trailing rubbish? */
827 return -ENAMETOOLONG;
829 *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
830 break;
833 if (d == INT_MAX) {
834 /* c == 00ZZZZ00 */
835 if (c & 3)
836 return -EINVAL;
838 if (l > 0) /* Trailing rubbish? */
839 return -ENAMETOOLONG;
841 *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
842 *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
843 break;
846 *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
847 *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
848 *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
851 *z = 0;
853 assert((size_t) (z - buf) <= len);
855 if (ret_size)
856 *ret_size = (size_t) (z - buf);
857 if (ret)
858 *ret = TAKE_PTR(buf);
860 return 0;
863 void hexdump(FILE *f, const void *p, size_t s) {
864 const uint8_t *b = p;
865 unsigned n = 0;
867 assert(b || s == 0);
869 if (!f)
870 f = stdout;
872 while (s > 0) {
873 size_t i;
875 fprintf(f, "%04x ", n);
877 for (i = 0; i < 16; i++) {
879 if (i >= s)
880 fputs(" ", f);
881 else
882 fprintf(f, "%02x ", b[i]);
884 if (i == 7)
885 fputc(' ', f);
888 fputc(' ', f);
890 for (i = 0; i < 16; i++) {
892 if (i >= s)
893 fputc(' ', f);
894 else
895 fputc(isprint(b[i]) ? (char) b[i] : '.', f);
898 fputc('\n', f);
900 if (s < 16)
901 break;
903 n += 16;
904 b += 16;
905 s -= 16;