URI: 
       let user choose extra fields to print and custom date formats - 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 8894359aa6ad4ccc485901a8af9db03d1a2b4d5f
   DIR parent 65778fa74c2e72ca67a8dc4f6c1f0021f8ce2de4
  HTML Author: Josuah Demangeon <me@josuah.net>
       Date:   Fri, 18 Jun 2021 08:58:21 +0200
       
       let user choose extra fields to print and custom date formats
       
       Diffstat:
         M ical.c                              |       2 +-
         M ics2tsv.c                           |     111 ++++++++++++++++++++++++++++---
         M util.c                              |       8 +++-----
         M util.h                              |       2 +-
       
       4 files changed, 108 insertions(+), 15 deletions(-)
       ---
   DIR diff --git a/ical.c b/ical.c
       @@ -297,7 +297,7 @@ ical_getline(char **contentline, char **line, size_t *sz, FILE *fp)
                        num++;
                        strchomp(*line);
        
       -                if (strappend(contentline, *line) < 0)
       +                if (strappend(contentline, *line) == NULL)
                                return -1;
                        if ((c = fgetc(fp)) == EOF)
                                goto end;
   DIR diff --git a/ics2tsv.c b/ics2tsv.c
       @@ -3,31 +3,49 @@
        #include <stdlib.h>
        #include <string.h>
        #include <strings.h>
       +#include <time.h>
       +#include <unistd.h>
        
        #include "ical.h"
        #include "util.h"
        
       -#define FIELDS_MAX 64
       +#define FIELDS_MAX 128
        
        typedef struct Field Field;
        typedef struct Block Block;
        
       +struct Field {
       +        char *key;
       +        char *value;
       +};
       +
        struct Block {
                time_t beg, end;
                char *fields[FIELDS_MAX];
        };
        
       -Block block;
       +static char default_fields[] = "CATEGORIES,LOCATION,SUMMARY,DESCRIPTION";
       +static char *flag_s = ",";
       +static char *flag_t = NULL;
       +static char *flag_f = default_fields;
       +static char *fields[FIELDS_MAX];
       +static Block block;
        
        static int
        fn_field_name(IcalParser *p, char *name)
        {
       +        (void)p;
       +        (void)name;
       +
                return 0;
        }
        
        static int
        fn_block_begin(IcalParser *p, char *name)
        {
       +        (void)p;
       +        (void)name;
       +
                memset(&block, 0, sizeof block);
                return 0;
        }
       @@ -35,9 +53,30 @@ fn_block_begin(IcalParser *p, char *name)
        static int
        fn_block_end(IcalParser *p, char *name)
        {
       +        char buf[128];
       +        struct tm tm = {0};
       +
       +        (void)name;
       +
                if (p->blocktype == ICAL_BLOCK_OTHER)
                        return 0;
       -        printf("%s\t%lld\t%lld", p->current->name, block.beg, block.end);
       +        fputs(p->current->name, stdout);
       +
       +        /* printing dates with %s is much much slower than %lld */
       +        if (flag_t == NULL) {
       +                printf("\t%lld\t%lld", block.beg, block.end);
       +        } else {
       +                strftime(buf, sizeof buf, flag_t, gmtime_r(&block.beg, &tm));
       +                printf("\t%s", buf);
       +                strftime(buf, sizeof buf, flag_t, gmtime_r(&block.end, &tm));
       +                printf("\t%s", buf);
       +        }
       +
       +        for (int i = 0; fields[i] != NULL; i++) {
       +                fputc('\t', stdout);
       +                if (block.fields[i] != NULL)
       +                        fputs(block.fields[i], stdout);
       +        }
                printf("\n");
                return 0;
        }
       @@ -45,13 +84,17 @@ fn_block_end(IcalParser *p, char *name)
        static int
        fn_param_value(IcalParser *p, char *name, char *value)
        {
       +        (void)p;
       +        (void)name;
       +        (void)value;
       +
                return 0;
        }
        
        static int
        fn_field_value(IcalParser *p, char *name, char *value)
        {
       -        static char *fieldmap[][2] = {
       +        static char *map[][2] = {
                        [ICAL_BLOCK_VEVENT]        = { "DTSTART",        "DTEND" },
                        [ICAL_BLOCK_VTODO]        = { NULL,        "DUE" },
                        [ICAL_BLOCK_VJOURNAL]        = { "DTSTAMP",        NULL },
       @@ -61,22 +104,48 @@ fn_field_value(IcalParser *p, char *name, char *value)
                };
                char *beg, *end;
        
       -        beg = fieldmap[p->blocktype][0];
       +        /* fill the date fields */
       +        beg = map[p->blocktype][0];
                if (beg != NULL && strcasecmp(name, beg) == 0)
                        if (ical_get_time(p, value, &block.beg) != 0)
                                return -1;
       -        end = fieldmap[p->blocktype][1];
       +        end = map[p->blocktype][1];
                if (end != NULL && strcasecmp(name, end) == 0)
                        if (ical_get_time(p, value, &block.end) != 0)
                                return -1;
       +
       +        /* fill text fields as requested with -o F1,F2... */
       +        for (int i = 0; fields[i] != NULL; i++) {
       +                if (strcasecmp(name, fields[i]) == 0) {
       +                        if (block.fields[i] == NULL) {
       +                                if ((block.fields[i] = strdup(value)) == NULL)
       +                                        return ical_err(p, strerror(errno));
       +                        } else {
       +                                if (strappend(&block.fields[i], flag_s) == NULL ||
       +                                    strappend(&block.fields[i], value) == NULL)
       +                                        return ical_err(p, strerror(errno));
       +                        }
       +                }
       +        }
       +
                return 0;
        }
        
       +static void
       +usage(void)
       +{
       +        fprintf(stderr, "usage: %s [-f fields] [-s subsep] [-t timefmt] [file...]", arg0);
       +        exit(1);
       +}
       +
        int
        main(int argc, char **argv)
        {
                IcalParser p = {0};
       -        arg0 = *argv++;
       +        size_t i;
       +        int c;
       +
       +        arg0 = *argv;
        
                p.fn_field_name = fn_field_name;
                p.fn_block_begin = fn_block_begin;
       @@ -84,7 +153,33 @@ main(int argc, char **argv)
                p.fn_param_value = fn_param_value;
                p.fn_field_value = fn_field_value;
        
       -        if (*argv == NULL) {
       +        while ((c = getopt(argc, argv, "f:s:t:")) != -1) {
       +                switch (c) {
       +                case 'f':
       +                        flag_f = optarg;
       +                        break;
       +                case 's':
       +                        flag_s = optarg;
       +                        break;
       +                case 't':
       +                        flag_t = optarg;
       +                        break;
       +                case '?':
       +                        usage();
       +                        break;
       +                }
       +        }
       +        argv += optind;
       +        argc -= optind;
       +
       +        i = 0;
       +        do {
       +                if (i >= sizeof fields / sizeof *fields - 1)
       +                        err("too many fields specified with -o flag");
       +        } while ((fields[i++] = strsep(&flag_f, ",")) != NULL);
       +        fields[i] = NULL;
       +
       +        if (*argv == NULL || strcmp(*argv, "-") == 0) {
                        debug("converting *stdin*");
                        if (ical_parse(&p, stdin) < 0)
                                err("parsing *stdin*:%d: %s", p.linenum, p.errmsg);
   DIR diff --git a/util.c b/util.c
       @@ -110,7 +110,7 @@ strchomp(char *line)
                        line[--len] = '\0';
        }
        
       -int
       +char *
        strappend(char **dp, char const *s)
        {
                size_t dlen, slen;
       @@ -118,13 +118,11 @@ strappend(char **dp, char const *s)
        
                dlen = (*dp == NULL) ? 0 : strlen(*dp);
                slen = strlen(s);
       -
                if ((mem = realloc(*dp, dlen + slen + 1)) == NULL)
       -                return -1;
       +                return NULL;
                *dp = mem;
       -
                memcpy(*dp + dlen, s, slen + 1);
       -        return 0;
       +        return *dp;
        }
        
        /** memory **/
   DIR diff --git a/util.h b/util.h
       @@ -15,7 +15,7 @@ void         debug(char const *fmt, ...);
        size_t         strlcpy(char *, char const *, size_t);
        char        *strsep(char **, char const *);
        void         strchomp(char *);
       -int         strappend(char **, char const *);
       +char        *strappend(char **, char const *);
        size_t         strlcat(char *, char const *, size_t);
        
        /** memory **/