Skip to content

Commit 82b4ec4

Browse files
committed
hexdecoct: optionally, line break base64 encoded data
1 parent 7b0da71 commit 82b4ec4

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

src/basic/hexdecoct.c

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -565,38 +565,79 @@ int unbase64char(char c) {
565565
return -EINVAL;
566566
}
567567

568-
ssize_t base64mem(const void *p, size_t l, char **out) {
569-
char *r, *z;
568+
static void maybe_line_break(char **x, char *start, size_t line_break) {
569+
size_t n;
570+
571+
assert(x);
572+
assert(*x);
573+
assert(start);
574+
assert(*x >= start);
575+
576+
if (line_break == SIZE_MAX)
577+
return;
578+
579+
n = *x - start;
580+
581+
if (n % (line_break + 1) == line_break)
582+
*((*x)++) = '\n';
583+
}
584+
585+
ssize_t base64mem_full(
586+
const void *p,
587+
size_t l,
588+
size_t line_break,
589+
char **out) {
590+
570591
const uint8_t *x;
592+
char *r, *z;
593+
size_t m;
571594

572595
assert(p || l == 0);
573596
assert(out);
597+
assert(line_break > 0);
574598

575599
/* three input bytes makes four output bytes, padding is added so we must round up */
576-
z = r = malloc(4 * (l + 2) / 3 + 1);
600+
m = 4 * (l + 2) / 3 + 1;
601+
602+
if (line_break != SIZE_MAX)
603+
m += m / line_break;
604+
605+
z = r = malloc(m);
577606
if (!r)
578607
return -ENOMEM;
579608

580609
for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
581610
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
611+
maybe_line_break(&z, r, line_break);
582612
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
613+
maybe_line_break(&z, r, line_break);
583614
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
615+
maybe_line_break(&z, r, line_break);
584616
*(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
617+
maybe_line_break(&z, r, line_break);
585618
*(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
586619
}
587620

588621
switch (l % 3) {
589622
case 2:
623+
maybe_line_break(&z, r, line_break);
590624
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
625+
maybe_line_break(&z, r, line_break);
591626
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
627+
maybe_line_break(&z, r, line_break);
592628
*(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
629+
maybe_line_break(&z, r, line_break);
593630
*(z++) = '=';
594631

595632
break;
596633
case 1:
634+
maybe_line_break(&z, r, line_break);
597635
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
636+
maybe_line_break(&z, r, line_break);
598637
*(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
638+
maybe_line_break(&z, r, line_break);
599639
*(z++) = '=';
640+
maybe_line_break(&z, r, line_break);
600641
*(z++) = '=';
601642

602643
break;

src/basic/hexdecoct.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ int unbase64char(char c) _const_;
3333
char *base32hexmem(const void *p, size_t l, bool padding);
3434
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
3535

36-
ssize_t base64mem(const void *p, size_t l, char **out);
36+
ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
37+
static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
38+
return base64mem_full(p, l, SIZE_MAX, ret);
39+
}
40+
3741
int base64_append(char **prefix, int plen,
3842
const void *p, size_t l,
3943
int margin, int width);

src/test/test-hexdecoct.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "alloc-util.h"
66
#include "hexdecoct.h"
77
#include "macro.h"
8+
#include "random-util.h"
89
#include "string-util.h"
910

1011
static void test_hexchar(void) {
@@ -275,6 +276,37 @@ static void test_base64mem(void) {
275276
free(b64);
276277
}
277278

279+
static void test_base64mem_linebreak(void) {
280+
uint8_t data[4096];
281+
282+
for (size_t i = 0; i < 20; i++) {
283+
_cleanup_free_ char *encoded = NULL;
284+
_cleanup_free_ void *decoded = NULL;
285+
size_t decoded_size;
286+
uint64_t n, m;
287+
ssize_t l;
288+
289+
/* Try a bunch of differently sized blobs */
290+
n = random_u64_range(sizeof(data));
291+
random_bytes(data, n);
292+
293+
/* Break at various different columns */
294+
m = 1 + random_u64_range(n + 5);
295+
296+
l = base64mem_full(data, n, m, &encoded);
297+
assert_se(l >= 0);
298+
assert_se(encoded);
299+
assert_se((size_t) l == strlen(encoded));
300+
301+
assert_se(unbase64mem(encoded, SIZE_MAX, &decoded, &decoded_size) >= 0);
302+
assert_se(decoded_size == n);
303+
assert_se(memcmp(data, decoded, n) == 0);
304+
305+
for (size_t j = 0; j < (size_t) l; j++)
306+
assert_se((encoded[j] == '\n') == (j % (m + 1) == m));
307+
}
308+
}
309+
278310
static void test_unbase64mem_one(const char *input, const char *output, int ret) {
279311
_cleanup_free_ void *buffer = NULL;
280312
size_t size = 0;
@@ -348,6 +380,7 @@ int main(int argc, char *argv[]) {
348380
test_base32hexmem();
349381
test_unbase32hexmem();
350382
test_base64mem();
383+
test_base64mem_linebreak();
351384
test_unbase64mem();
352385
test_hexdump();
353386

0 commit comments

Comments
 (0)