Home
       implement base64 data in-place decoding - ics2txt - convert icalendar .ics file to plain text
  HTML git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
       ---
   DIR commit b72092250747c7443e20fee06bee232b236f441e
   DIR parent 917f5b056d0b1241f0816bfd41276a36b5727fb1
  HTML Author: Josuah Demangeon <me@josuah.net>
       Date:   Sun, 13 Jun 2021 13:47:25 +0200
       
       implement base64 data in-place decoding
       
       This is not done implicitly in case base64 decoding is not needed
       every time, but instead available as a ical_get_value() function that
       decodes the content if it is base64 data.
       
       Diffstat:
         M Makefile                            |       4 ++--
         A base64.c                            |     107 +++++++++++++++++++++++++++++++
         A base64.h                            |      19 +++++++++++++++++++
         M ical.c                              |      22 +++++++++++++++++++++-
         M ical.h                              |       9 +++++----
         M ics2tree.c                          |       5 ++++-
         D ics2tsv.c                           |      59 -------------------------------
         M util.c                              |       2 +-
       
       8 files changed, 159 insertions(+), 68 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -7,8 +7,8 @@ CFLAGS = $D $W -g
        PREFIX = /usr/local
        MANPREFIX = ${PREFIX}/man
        
       -SRC = ical.c util.c
       -HDR = ical.h util.h
       +SRC = ical.c base64.c util.c
       +HDR = ical.h base64.h util.h
        OBJ = ${SRC:.c=.o}
        BIN = ics2tree
        MAN1 = ics2txt.1
   DIR diff --git a/base64.c b/base64.c
       @@ -0,0 +1,107 @@
       +#include "base64.h"
       +
       +#include <assert.h>
       +#include <stddef.h>
       +#include <stdint.h>
       +#include <string.h>
       +
       +#include <stdio.h>
       +
       +static char encode_map[64] =
       +    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
       +
       +void
       +base64_encode(char const *s, size_t slen, char *d, size_t *dlen)
       +{
       +        char const *sbeg = s, *send = s + slen, *dbeg = d;
       +        unsigned char x;
       +
       +        while (s < send) {
       +                switch ((s - sbeg) % 3) {
       +                case 0: /* AAAAAABB bbbbcccc ccdddddd */
       +                        assert((size_t)(d - dbeg) + 1 < *dlen);
       +                        *d++ = encode_map[*s >> 2];
       +                        x = *s << 4 & 0x3f;
       +                        break;
       +                case 1: /* aaaaaabb BBBBCCCC ccdddddd */
       +                        assert((size_t)(d - dbeg) + 1 < *dlen);
       +                        *d++ = encode_map[x | (*s >> 4)];
       +                        x = (*s << 2) & 0x3f;
       +                        break;
       +                case 2: /* aaaaaabb bbbbcccc CCDDDDDD */
       +                        assert((size_t)(d - dbeg) + 2 < *dlen);
       +                        *d++ = encode_map[x | (*s >> 6)];
       +                        *d++ = encode_map[*s & 0x3f];
       +                        break;
       +                }
       +                s++;
       +        }
       +
       +        /* flush extra content in 'x' */
       +        assert((size_t)(d - dbeg) + 1 < *dlen);
       +        if ((s - sbeg) % 3 != 2)
       +                *d++ = encode_map[x];
       +
       +        /* pad the end with '=' */
       +        while ((d - dbeg) % 4 != 0) {
       +                assert((size_t)(d - dbeg) + 1 < *dlen);
       +                *d++ = '=';
       +        }
       +
       +        *dlen = d - dbeg;
       +}
       +
       +static int8_t decode_map[256] = {
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
       +        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,  0, -1, -1,
       +        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
       +        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
       +        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
       +        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       +};
       +
       +int
       +base64_decode(char const *s, size_t *slen, char *d, size_t *dlen)
       +{
       +        char const *sbeg = s, *send = sbeg + *slen, *dbeg = d;
       +
       +        for (; s + 3 < send; s += 4) {
       +                int8_t x0 = decode_map[(unsigned)s[0]];
       +                int8_t x1 = decode_map[(unsigned)s[1]];
       +                int8_t x2 = decode_map[(unsigned)s[2]];
       +                int8_t x3 = decode_map[(unsigned)s[3]];
       +                uint32_t x = (x0 << 18) | (x1 << 12) | (x2 << 6) | (x3 << 0);
       +
       +                assert((size_t)(d - dbeg) + 3 < *dlen);
       +                *d++ = x >> 16;
       +                *d++ = x >> 8 & 0xff;
       +                *d++ = x & 0xff;
       +
       +                /* only "xxxx" or "xxx=" or "xx==" allowed  */
       +                if (s[0] == '=' || s[1] == '=' || (s[2] == '=' && s[3] != '='))
       +                        return -2;
       +                if (s[2] == '=')
       +                        d--;
       +                if (s[3] == '=') {
       +                        d--;
       +                        break;
       +                }
       +
       +                if (x0 < 0 || x1 < 0 || x2 < 0 || x3 < 0)
       +                        return -1;
       +        }
       +
       +        *slen = s - sbeg;
       +        *dlen = d - dbeg;
       +        return 0;
       +}
   DIR diff --git a/base64.h b/base64.h
       @@ -0,0 +1,19 @@
       +#ifndef BASE64_H
       +#define BASE64_H
       +
       +#include <stddef.h>
       +
       +void        base64_encode(char const *, size_t, char *, size_t *);
       +
       +/*
       + * It is possible to use the same variables for both source and
       + * destination. Then the base64 will overwrite the source buffer
       + * with the destination data.
       + *
       + * If the same pointer is passed as both source and destination
       + * size, the source size will be inaccurate but the destination
       + * will be correct.
       + */
       +int        base64_decode(char const *, size_t *, char *, size_t *);
       +
       +#endif
   DIR diff --git a/ical.c b/ical.c
       @@ -9,14 +9,31 @@
        #include <strings.h>
        
        #include "util.h"
       +#include "base64.h"
        
       -static int
       +int
        ical_error(IcalParser *p, char const *msg)
        {
                p->errmsg = msg;
                return -1;
        }
        
       +int
       +ical_get_value(IcalParser *p, char *s, size_t *len)
       +{
       +        *len = strlen(s);
       +        if (p->base64)
       +                if (base64_decode(s, len, s, len) < 0)
       +                        return ical_error(p, "invalid base64 data");
       +        return 0;
       +}
       +
       +int
       +ical_get_time(IcalParser *p, char *s, time_t *t)
       +{
       +        return -1;
       +}
       +
        #define CALL(p, fn, ...) ((p)->fn ? (p)->fn((p), __VA_ARGS__) : 0)
        
        static int
       @@ -40,6 +57,8 @@ ical_parse_value(IcalParser *p, char **sp, char *name)
                c = *s, *s = '\0';
                if ((err = CALL(p, fn_param_value, name, val)) != 0)
                        return err;
       +        if (strcasecmp(name, "ENCODING") == 0)
       +                p->base64 = (strcasecmp(val, "BASE64") == 0);
                *s = c;
        
                *sp = s;
       @@ -90,6 +109,7 @@ ical_parse_contentline(IcalParser *p, char *line)
                *s = c;
                end = s;
        
       +        p->base64 = 0;
                while (*s == ';') {
                        s++;
                        if ((err = ical_parse_param(p, &s)) != 0)
   DIR diff --git a/ical.h b/ical.h
       @@ -15,7 +15,7 @@ struct IcalParser {
                int (*fn_block_end)(IcalParser *, char *);
                /* if returning non-zero then halt the parser */
        
       -        int         base64encoded;
       +        int         base64;
                char const *errmsg;
                size_t         line;
        
       @@ -24,8 +24,9 @@ struct IcalParser {
                char         stack[1024];
        };
        
       -int         ical_parse(IcalParser *, FILE *);
       -//TODO: char        *ical_get_time(char *);
       -//TODO: char        *ical_get_value(IcalCtx *, char *);
       +int        ical_parse(IcalParser *, FILE *);
       +int        ical_get_time(IcalParser *, char *, time_t *);
       +int        ical_get_value(IcalParser *, char *, size_t *);
       +int        ical_error(IcalParser *, char const *);
        
        #endif
   DIR diff --git a/ics2tree.c b/ics2tree.c
       @@ -39,8 +39,11 @@ fn_param_value(IcalParser *p, char *name, char *value)
        static int
        fn_entry_value(IcalParser *p, char *name, char *value)
        {
       +        size_t len;
                (void)name;
        
       +        if (ical_get_value(p, value, &len) < 0)
       +                return -1;
                print_ruler(p->level + 1);
                printf("value %s\n", value);
                return 0;
       @@ -59,7 +62,7 @@ main(int argc, char **argv)
        
                if (*argv == NULL) {
                        if (ical_parse(&p, stdin) < 0)
       -                        err("parsing stdin:%d %s", p.line, p.errmsg);
       +                        err("parsing stdin:%d: %s", p.line, p.errmsg);
                }
        
                for (; *argv != NULL; argv++, argc--) {
   DIR diff --git a/ics2tsv.c b/ics2tsv.c
       @@ -1,59 +0,0 @@
       -#include <stdio.h>
       -#include <stdlib.h>
       -#include <string.h>
       -
       -#include "ical.h"
       -#include "log.h"
       -#include "util.h"
       -
       -int
       -print_ical_tsv(FILE *fp)
       -{
       -        struct ical_vcalendar vcal;
       -        int e;
       -
       -        if ((e = ical_read_vcalendar(&vcal, fp)) < 0)
       -                die("reading ical file: %s", ical_strerror(e));
       -
       -        ical_free_vcalendar(&vcal);
       -        return 0;
       -}
       -
       -void
       -print_header(void)
       -{
       -        char *fields[] = { "", NULL };
       -
       -        printf("%s\t%s", "beg", "end");
       -
       -        for (char **f = fields; *f != NULL; f++) {
       -                fprintf(stdout, "\t%s", *f);
       -        }
       -        fprintf(stdout, "\n");
       -}
       -
       -int
       -main(int argc, char **argv)
       -{
       -        print_header();
       -
       -        log_arg0 = *argv++;
       -
       -        if (*argv == NULL) {
       -                if (print_ical_tsv(stdin) < 0)
       -                        die("converting stdin");
       -        }
       -
       -        for (; *argv != NULL; argv++, argc--) {
       -                FILE *fp;
       -
       -                info("converting \"%s\"", *argv);
       -                if ((fp = fopen(*argv, "r")) == NULL)
       -                        die("opening %s", *argv);
       -                if (print_ical_tsv(fp) < 0)
       -                        die("converting %s", *argv);
       -                fclose(fp);
       -        }
       -
       -        return 0;
       -}
   DIR diff --git a/util.c b/util.c
       @@ -49,7 +49,7 @@ debug(char const *fmt, ...)
                va_list va;
        
                if (verbose < 0)
       -                verbose = (getenv("DEBUG") == NULL);
       +                verbose = (getenv("DEBUG") != NULL);
                if (!verbose)
                        return;
                va_start(va, fmt);