]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/hexdecoct.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[thirdparty/systemd.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 preceding 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 len, slen, avail, line, lines;
596
597 len = base64mem(p, l, &x);
598 if (len <= 0)
599 return len;
600
601 lines = DIV_ROUND_UP(len, width);
602
603 slen = strlen_ptr(sep);
604 if (lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1))
605 return -ENOMEM;
606
607 t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
608 if (!t)
609 return -ENOMEM;
610
611 memcpy_safe(t + plen, sep, slen);
612
613 for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
614 int act = MIN(width, avail);
615
616 if (line > 0 || sep) {
617 memset(s, ' ', indent);
618 s += indent;
619 }
620
621 memcpy(s, x + width * line, act);
622 s += act;
623 *(s++) = line < lines - 1 ? '\n' : '\0';
624 avail -= act;
625 }
626 assert(avail == 0);
627
628 *prefix = t;
629 return 0;
630 }
631
632 int base64_append(
633 char **prefix, int plen,
634 const void *p, size_t l,
635 int indent, int width) {
636
637 if (plen > width / 2 || plen + indent > width)
638 /* leave indent on the left, keep last column free */
639 return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
640 else
641 /* leave plen on the left, keep last column free */
642 return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
643 }
644
645 static int unbase64_next(const char **p, size_t *l) {
646 int ret;
647
648 assert(p);
649 assert(l);
650
651 /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
652 * greedily skip all preceding and all following whitespace. */
653
654 for (;;) {
655 if (*l == 0)
656 return -EPIPE;
657
658 if (!strchr(WHITESPACE, **p))
659 break;
660
661 /* Skip leading whitespace */
662 (*p)++, (*l)--;
663 }
664
665 if (**p == '=')
666 ret = INT_MAX; /* return padding as INT_MAX */
667 else {
668 ret = unbase64char(**p);
669 if (ret < 0)
670 return ret;
671 }
672
673 for (;;) {
674 (*p)++, (*l)--;
675
676 if (*l == 0)
677 break;
678 if (!strchr(WHITESPACE, **p))
679 break;
680
681 /* Skip following whitespace */
682 }
683
684 return ret;
685 }
686
687 int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
688 _cleanup_free_ uint8_t *buf = NULL;
689 const char *x;
690 uint8_t *z;
691 size_t len;
692
693 assert(p || l == 0);
694 assert(ret);
695 assert(ret_size);
696
697 if (l == (size_t) -1)
698 l = strlen(p);
699
700 /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
701 * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
702 len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
703
704 buf = malloc(len + 1);
705 if (!buf)
706 return -ENOMEM;
707
708 for (x = p, z = buf;;) {
709 int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
710
711 a = unbase64_next(&x, &l);
712 if (a == -EPIPE) /* End of string */
713 break;
714 if (a < 0)
715 return a;
716 if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
717 return -EINVAL;
718
719 b = unbase64_next(&x, &l);
720 if (b < 0)
721 return b;
722 if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
723 return -EINVAL;
724
725 c = unbase64_next(&x, &l);
726 if (c < 0)
727 return c;
728
729 d = unbase64_next(&x, &l);
730 if (d < 0)
731 return d;
732
733 if (c == INT_MAX) { /* Padding at the third character */
734
735 if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
736 return -EINVAL;
737
738 /* b == 00YY0000 */
739 if (b & 15)
740 return -EINVAL;
741
742 if (l > 0) /* Trailing rubbish? */
743 return -ENAMETOOLONG;
744
745 *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
746 break;
747 }
748
749 if (d == INT_MAX) {
750 /* c == 00ZZZZ00 */
751 if (c & 3)
752 return -EINVAL;
753
754 if (l > 0) /* Trailing rubbish? */
755 return -ENAMETOOLONG;
756
757 *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
758 *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
759 break;
760 }
761
762 *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
763 *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
764 *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
765 }
766
767 *z = 0;
768
769 *ret_size = (size_t) (z - buf);
770 *ret = TAKE_PTR(buf);
771
772 return 0;
773 }
774
775 void hexdump(FILE *f, const void *p, size_t s) {
776 const uint8_t *b = p;
777 unsigned n = 0;
778
779 assert(b || s == 0);
780
781 if (!f)
782 f = stdout;
783
784 while (s > 0) {
785 size_t i;
786
787 fprintf(f, "%04x ", n);
788
789 for (i = 0; i < 16; i++) {
790
791 if (i >= s)
792 fputs(" ", f);
793 else
794 fprintf(f, "%02x ", b[i]);
795
796 if (i == 7)
797 fputc(' ', f);
798 }
799
800 fputc(' ', f);
801
802 for (i = 0; i < 16; i++) {
803
804 if (i >= s)
805 fputc(' ', f);
806 else
807 fputc(isprint(b[i]) ? (char) b[i] : '.', f);
808 }
809
810 fputc('\n', f);
811
812 if (s < 16)
813 break;
814
815 n += 16;
816 b += 16;
817 s -= 16;
818 }
819 }