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