let user choose extra fields to print and custom date formats - ics2txt - convert icalendar .ics file to plain text
(HTM) 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
(HTM) 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 **/