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