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