chiark / gitweb /
tree-wide: remove Lennart's copyright lines
[elogind.git] / src / basic / hexdecoct.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <stdint.h>
6 #include <stdlib.h>
7
8 #include "alloc-util.h"
9 #include "hexdecoct.h"
10 #include "macro.h"
11 #include "string-util.h"
12 #include "util.h"
13
14 char octchar(int x) {
15         return '0' + (x & 7);
16 }
17
18 int unoctchar(char c) {
19
20         if (c >= '0' && c <= '7')
21                 return c - '0';
22
23         return -EINVAL;
24 }
25
26 char decchar(int x) {
27         return '0' + (x % 10);
28 }
29
30 int undecchar(char c) {
31
32         if (c >= '0' && c <= '9')
33                 return c - '0';
34
35         return -EINVAL;
36 }
37
38 char hexchar(int x) {
39         static const char table[16] = "0123456789abcdef";
40
41         return table[x & 15];
42 }
43
44 int unhexchar(char c) {
45
46         if (c >= '0' && c <= '9')
47                 return c - '0';
48
49         if (c >= 'a' && c <= 'f')
50                 return c - 'a' + 10;
51
52         if (c >= 'A' && c <= 'F')
53                 return c - 'A' + 10;
54
55         return -EINVAL;
56 }
57
58 char *hexmem(const void *p, size_t l) {
59         const uint8_t *x;
60         char *r, *z;
61
62         z = r = new(char, l * 2 + 1);
63         if (!r)
64                 return NULL;
65
66         for (x = p; x < (const uint8_t*) p + l; x++) {
67                 *(z++) = hexchar(*x >> 4);
68                 *(z++) = hexchar(*x & 15);
69         }
70
71         *z = 0;
72         return r;
73 }
74
75 static int unhex_next(const char **p, size_t *l) {
76         int r;
77
78         assert(p);
79         assert(l);
80
81         /* Find the next non-whitespace character, and decode it. We
82          * greedily skip all preceeding and all following whitespace. */
83
84         for (;;) {
85                 if (*l == 0)
86                         return -EPIPE;
87
88                 if (!strchr(WHITESPACE, **p))
89                         break;
90
91                 /* Skip leading whitespace */
92                 (*p)++, (*l)--;
93         }
94
95         r = unhexchar(**p);
96         if (r < 0)
97                 return r;
98
99         for (;;) {
100                 (*p)++, (*l)--;
101
102                 if (*l == 0 || !strchr(WHITESPACE, **p))
103                         break;
104
105                 /* Skip following whitespace */
106         }
107
108         return r;
109 }
110
111 int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
112         _cleanup_free_ uint8_t *buf = NULL;
113         const char *x;
114         uint8_t *z;
115
116         assert(ret);
117         assert(ret_len);
118         assert(p || l == 0);
119
120         if (l == (size_t) -1)
121                 l = strlen(p);
122
123         /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */
124         buf = malloc((l + 1) / 2 + 1);
125         if (!buf)
126                 return -ENOMEM;
127
128         for (x = p, z = buf;;) {
129                 int a, b;
130
131                 a = unhex_next(&x, &l);
132                 if (a == -EPIPE) /* End of string */
133                         break;
134                 if (a < 0)
135                         return a;
136
137                 b = unhex_next(&x, &l);
138                 if (b < 0)
139                         return b;
140
141                 *(z++) = (uint8_t) a << 4 | (uint8_t) b;
142         }
143
144         *z = 0;
145
146         *ret_len = (size_t) (z - buf);
147         *ret = TAKE_PTR(buf);
148
149         return 0;
150 }
151
152 /* https://tools.ietf.org/html/rfc4648#section-6
153  * Notice that base32hex differs from base32 in the alphabet it uses.
154  * The distinction is that the base32hex representation preserves the
155  * order of the underlying data when compared as bytestrings, this is
156  * useful when representing NSEC3 hashes, as one can then verify the
157  * order of hashes directly from their representation. */
158 char base32hexchar(int x) {
159         static const char table[32] = "0123456789"
160                                       "ABCDEFGHIJKLMNOPQRSTUV";
161
162         return table[x & 31];
163 }
164
165 int unbase32hexchar(char c) {
166         unsigned offset;
167
168         if (c >= '0' && c <= '9')
169                 return c - '0';
170
171         offset = '9' - '0' + 1;
172
173         if (c >= 'A' && c <= 'V')
174                 return c - 'A' + offset;
175
176         return -EINVAL;
177 }
178
179 char *base32hexmem(const void *p, size_t l, bool padding) {
180         char *r, *z;
181         const uint8_t *x;
182         size_t len;
183
184         assert(p || l == 0);
185
186         if (padding)
187                 /* five input bytes makes eight output bytes, padding is added so we must round up */
188                 len = 8 * (l + 4) / 5;
189         else {
190                 /* same, but round down as there is no padding */
191                 len = 8 * l / 5;
192
193                 switch (l % 5) {
194                 case 4:
195                         len += 7;
196                         break;
197                 case 3:
198                         len += 5;
199                         break;
200                 case 2:
201                         len += 4;
202                         break;
203                 case 1:
204                         len += 2;
205                         break;
206                 }
207         }
208
209         z = r = malloc(len + 1);
210         if (!r)
211                 return NULL;
212
213         for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
214                 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
215                  * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
216                 *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
217                 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
218                 *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
219                 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);  /* 000YZZZZ */
220                 *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
221                 *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
222                 *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5);  /* 000QQWWW */
223                 *(z++) = base32hexchar((x[4] & 31));                  /* 000WWWWW */
224         }
225
226         switch (l % 5) {
227         case 4:
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);              /* 000QQ000 */
235                 if (padding)
236                         *(z++) = '=';
237
238                 break;
239
240         case 3:
241                 *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
242                 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
243                 *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
244                 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
245                 *(z++) = base32hexchar((x[2] & 15) << 1);            /* 000ZZZZ0 */
246                 if (padding) {
247                         *(z++) = '=';
248                         *(z++) = '=';
249                         *(z++) = '=';
250                 }
251
252                 break;
253
254         case 2:
255                 *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
256                 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
257                 *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
258                 *(z++) = base32hexchar((x[1] & 1) << 4);             /* 000Y0000 */
259                 if (padding) {
260                         *(z++) = '=';
261                         *(z++) = '=';
262                         *(z++) = '=';
263                         *(z++) = '=';
264                 }
265
266                 break;
267
268         case 1:
269                 *(z++) = base32hexchar(x[0] >> 3);       /* 000XXXXX */
270                 *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
271                 if (padding) {
272                         *(z++) = '=';
273                         *(z++) = '=';
274                         *(z++) = '=';
275                         *(z++) = '=';
276                         *(z++) = '=';
277                         *(z++) = '=';
278                 }
279
280                 break;
281         }
282
283         *z = 0;
284         return r;
285 }
286
287 int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
288         _cleanup_free_ uint8_t *r = NULL;
289         int a, b, c, d, e, f, g, h;
290         uint8_t *z;
291         const char *x;
292         size_t len;
293         unsigned pad = 0;
294
295         assert(p || l == 0);
296         assert(mem);
297         assert(_len);
298
299         if (l == (size_t) -1)
300                 l = strlen(p);
301
302         /* padding ensures any base32hex input has input divisible by 8 */
303         if (padding && l % 8 != 0)
304                 return -EINVAL;
305
306         if (padding) {
307                 /* strip the padding */
308                 while (l > 0 && p[l - 1] == '=' && pad < 7) {
309                         pad++;
310                         l--;
311                 }
312         }
313
314         /* a group of eight input bytes needs five output bytes, in case of
315          * padding we need to add some extra bytes */
316         len = (l / 8) * 5;
317
318         switch (l % 8) {
319         case 7:
320                 len += 4;
321                 break;
322         case 5:
323                 len += 3;
324                 break;
325         case 4:
326                 len += 2;
327                 break;
328         case 2:
329                 len += 1;
330                 break;
331         case 0:
332                 break;
333         default:
334                 return -EINVAL;
335         }
336
337         z = r = malloc(len + 1);
338         if (!r)
339                 return -ENOMEM;
340
341         for (x = p; x < p + (l / 8) * 8; x += 8) {
342                 /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
343                  * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
344                 a = unbase32hexchar(x[0]);
345                 if (a < 0)
346                         return -EINVAL;
347
348                 b = unbase32hexchar(x[1]);
349                 if (b < 0)
350                         return -EINVAL;
351
352                 c = unbase32hexchar(x[2]);
353                 if (c < 0)
354                         return -EINVAL;
355
356                 d = unbase32hexchar(x[3]);
357                 if (d < 0)
358                         return -EINVAL;
359
360                 e = unbase32hexchar(x[4]);
361                 if (e < 0)
362                         return -EINVAL;
363
364                 f = unbase32hexchar(x[5]);
365                 if (f < 0)
366                         return -EINVAL;
367
368                 g = unbase32hexchar(x[6]);
369                 if (g < 0)
370                         return -EINVAL;
371
372                 h = unbase32hexchar(x[7]);
373                 if (h < 0)
374                         return -EINVAL;
375
376                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
377                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
378                 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
379                 *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
380                 *(z++) = (uint8_t) g << 5 | (uint8_t) h;                         /* VVVRRRRR */
381         }
382
383         switch (l % 8) {
384         case 7:
385                 a = unbase32hexchar(x[0]);
386                 if (a < 0)
387                         return -EINVAL;
388
389                 b = unbase32hexchar(x[1]);
390                 if (b < 0)
391                         return -EINVAL;
392
393                 c = unbase32hexchar(x[2]);
394                 if (c < 0)
395                         return -EINVAL;
396
397                 d = unbase32hexchar(x[3]);
398                 if (d < 0)
399                         return -EINVAL;
400
401                 e = unbase32hexchar(x[4]);
402                 if (e < 0)
403                         return -EINVAL;
404
405                 f = unbase32hexchar(x[5]);
406                 if (f < 0)
407                         return -EINVAL;
408
409                 g = unbase32hexchar(x[6]);
410                 if (g < 0)
411                         return -EINVAL;
412
413                 /* g == 000VV000 */
414                 if (g & 7)
415                         return -EINVAL;
416
417                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
418                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
419                 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
420                 *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
421
422                 break;
423         case 5:
424                 a = unbase32hexchar(x[0]);
425                 if (a < 0)
426                         return -EINVAL;
427
428                 b = unbase32hexchar(x[1]);
429                 if (b < 0)
430                         return -EINVAL;
431
432                 c = unbase32hexchar(x[2]);
433                 if (c < 0)
434                         return -EINVAL;
435
436                 d = unbase32hexchar(x[3]);
437                 if (d < 0)
438                         return -EINVAL;
439
440                 e = unbase32hexchar(x[4]);
441                 if (e < 0)
442                         return -EINVAL;
443
444                 /* e == 000SSSS0 */
445                 if (e & 1)
446                         return -EINVAL;
447
448                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
449                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
450                 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
451
452                 break;
453         case 4:
454                 a = unbase32hexchar(x[0]);
455                 if (a < 0)
456                         return -EINVAL;
457
458                 b = unbase32hexchar(x[1]);
459                 if (b < 0)
460                         return -EINVAL;
461
462                 c = unbase32hexchar(x[2]);
463                 if (c < 0)
464                         return -EINVAL;
465
466                 d = unbase32hexchar(x[3]);
467                 if (d < 0)
468                         return -EINVAL;
469
470                 /* d == 000W0000 */
471                 if (d & 15)
472                         return -EINVAL;
473
474                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
475                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
476
477                 break;
478         case 2:
479                 a = unbase32hexchar(x[0]);
480                 if (a < 0)
481                         return -EINVAL;
482
483                 b = unbase32hexchar(x[1]);
484                 if (b < 0)
485                         return -EINVAL;
486
487                 /* b == 000YYY00 */
488                 if (b & 3)
489                         return -EINVAL;
490
491                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
492
493                 break;
494         case 0:
495                 break;
496         default:
497                 return -EINVAL;
498         }
499
500         *z = 0;
501
502         *mem = TAKE_PTR(r);
503         *_len = len;
504
505         return 0;
506 }
507
508 /* https://tools.ietf.org/html/rfc4648#section-4 */
509 char base64char(int x) {
510         static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
511                                       "abcdefghijklmnopqrstuvwxyz"
512                                       "0123456789+/";
513         return table[x & 63];
514 }
515
516 int unbase64char(char c) {
517         unsigned offset;
518
519         if (c >= 'A' && c <= 'Z')
520                 return c - 'A';
521
522         offset = 'Z' - 'A' + 1;
523
524         if (c >= 'a' && c <= 'z')
525                 return c - 'a' + offset;
526
527         offset += 'z' - 'a' + 1;
528
529         if (c >= '0' && c <= '9')
530                 return c - '0' + offset;
531
532         offset += '9' - '0' + 1;
533
534         if (c == '+')
535                 return offset;
536
537         offset++;
538
539         if (c == '/')
540                 return offset;
541
542         return -EINVAL;
543 }
544
545 ssize_t base64mem(const void *p, size_t l, char **out) {
546         char *r, *z;
547         const uint8_t *x;
548
549         assert(p || l == 0);
550         assert(out);
551
552         /* three input bytes makes four output bytes, padding is added so we must round up */
553         z = r = malloc(4 * (l + 2) / 3 + 1);
554         if (!r)
555                 return -ENOMEM;
556
557         for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
558                 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
559                 *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
560                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
561                 *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
562                 *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
563         }
564
565         switch (l % 3) {
566         case 2:
567                 *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
568                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
569                 *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
570                 *(z++) = '=';
571
572                 break;
573         case 1:
574                 *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
575                 *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
576                 *(z++) = '=';
577                 *(z++) = '=';
578
579                 break;
580         }
581
582         *z = 0;
583         *out = r;
584         return z - r;
585 }
586
587 static int base64_append_width(
588                 char **prefix, int plen,
589                 const char *sep, int indent,
590                 const void *p, size_t l,
591                 int width) {
592
593         _cleanup_free_ char *x = NULL;
594         char *t, *s;
595         ssize_t slen, len, avail;
596         int line, lines;
597
598         len = base64mem(p, l, &x);
599         if (len <= 0)
600                 return len;
601
602         lines = DIV_ROUND_UP(len, width);
603
604         slen = strlen_ptr(sep);
605         t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
606         if (!t)
607                 return -ENOMEM;
608
609         memcpy_safe(t + plen, sep, slen);
610
611         for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
612                 int act = MIN(width, avail);
613
614                 if (line > 0 || sep) {
615                         memset(s, ' ', indent);
616                         s += indent;
617                 }
618
619                 memcpy(s, x + width * line, act);
620                 s += act;
621                 *(s++) = line < lines - 1 ? '\n' : '\0';
622                 avail -= act;
623         }
624         assert(avail == 0);
625
626         *prefix = t;
627         return 0;
628 }
629
630 int base64_append(
631                 char **prefix, int plen,
632                 const void *p, size_t l,
633                 int indent, int width) {
634
635         if (plen > width / 2 || plen + indent > width)
636                 /* leave indent on the left, keep last column free */
637                 return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
638         else
639                 /* leave plen on the left, keep last column free */
640                 return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
641 }
642
643 static int unbase64_next(const char **p, size_t *l) {
644         int ret;
645
646         assert(p);
647         assert(l);
648
649         /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
650          * greedily skip all preceeding and all following whitespace. */
651
652         for (;;) {
653                 if (*l == 0)
654                         return -EPIPE;
655
656                 if (!strchr(WHITESPACE, **p))
657                         break;
658
659                 /* Skip leading whitespace */
660                 (*p)++, (*l)--;
661         }
662
663         if (**p == '=')
664                 ret = INT_MAX; /* return padding as INT_MAX */
665         else {
666                 ret = unbase64char(**p);
667                 if (ret < 0)
668                         return ret;
669         }
670
671         for (;;) {
672                 (*p)++, (*l)--;
673
674                 if (*l == 0)
675                         break;
676                 if (!strchr(WHITESPACE, **p))
677                         break;
678
679                 /* Skip following whitespace */
680         }
681
682         return ret;
683 }
684
685 int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
686         _cleanup_free_ uint8_t *buf = NULL;
687         const char *x;
688         uint8_t *z;
689         size_t len;
690
691         assert(p || l == 0);
692         assert(ret);
693         assert(ret_size);
694
695         if (l == (size_t) -1)
696                 l = strlen(p);
697
698         /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
699          * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
700         len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
701
702         buf = malloc(len + 1);
703         if (!buf)
704                 return -ENOMEM;
705
706         for (x = p, z = buf;;) {
707                 int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
708
709                 a = unbase64_next(&x, &l);
710                 if (a == -EPIPE) /* End of string */
711                         break;
712                 if (a < 0)
713                         return a;
714                 if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
715                         return -EINVAL;
716
717                 b = unbase64_next(&x, &l);
718                 if (b < 0)
719                         return b;
720                 if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
721                         return -EINVAL;
722
723                 c = unbase64_next(&x, &l);
724                 if (c < 0)
725                         return c;
726
727                 d = unbase64_next(&x, &l);
728                 if (d < 0)
729                         return d;
730
731                 if (c == INT_MAX) { /* Padding at the third character */
732
733                         if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
734                                 return -EINVAL;
735
736                         /* b == 00YY0000 */
737                         if (b & 15)
738                                 return -EINVAL;
739
740                         if (l > 0) /* Trailing rubbish? */
741                                 return -ENAMETOOLONG;
742
743                         *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
744                         break;
745                 }
746
747                 if (d == INT_MAX) {
748                         /* c == 00ZZZZ00 */
749                         if (c & 3)
750                                 return -EINVAL;
751
752                         if (l > 0) /* Trailing rubbish? */
753                                 return -ENAMETOOLONG;
754
755                         *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
756                         *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
757                         break;
758                 }
759
760                 *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
761                 *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
762                 *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
763         }
764
765         *z = 0;
766
767         *ret_size = (size_t) (z - buf);
768         *ret = TAKE_PTR(buf);
769
770         return 0;
771 }
772
773 void hexdump(FILE *f, const void *p, size_t s) {
774         const uint8_t *b = p;
775         unsigned n = 0;
776
777         assert(b || s == 0);
778
779         if (!f)
780                 f = stdout;
781
782         while (s > 0) {
783                 size_t i;
784
785                 fprintf(f, "%04x  ", n);
786
787                 for (i = 0; i < 16; i++) {
788
789                         if (i >= s)
790                                 fputs("   ", f);
791                         else
792                                 fprintf(f, "%02x ", b[i]);
793
794                         if (i == 7)
795                                 fputc(' ', f);
796                 }
797
798                 fputc(' ', f);
799
800                 for (i = 0; i < 16; i++) {
801
802                         if (i >= s)
803                                 fputc(' ', f);
804                         else
805                                 fputc(isprint(b[i]) ? (char) b[i] : '.', f);
806                 }
807
808                 fputc('\n', f);
809
810                 if (s < 16)
811                         break;
812
813                 n += 16;
814                 b += 16;
815                 s -= 16;
816         }
817 }