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 **/