Introduction
Introduction Statistics Contact Development Disclaimer Help
remove unused utilities and flatten the source some more - ics2txt - convert ic…
git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws…
Log
Files
Refs
Tags
README
---
commit 24ae7d2759496b7907cce29f0c26697950453ff5
parent 742516775b1d9b12e4c8893114b7cc5a363884ad
Author: Josuah Demangeon <[email protected]>
Date: Sun, 20 Jun 2021 18:37:15 +0200
remove unused utilities and flatten the source some more
Diffstat:
M .gitignore | 1 +
M Makefile | 19 ++++++++++++-------
D bin/ics2tsv | 141 -----------------------------…
D bin/ics2txt | 2 --
D bin/tsv2ics | 104 -----------------------------…
M ics2tsv.c | 57 ++++++++++++++++-------------…
M tsv2agenda.c | 249 ++++++++++++++++++-----------…
A tsv2ics.awk | 106 ++++++++++++++++++++++++++++++
M util.c | 14 ++++++++++++++
M util.h | 4 ++++
10 files changed, 310 insertions(+), 387 deletions(-)
---
diff --git a/.gitignore b/.gitignore
@@ -1,5 +1,6 @@
*.o
/ics2tsv
/ics2tree
+/tsv2ics
/tsv2agenda
/ics2txt-[0-9]*
diff --git a/Makefile b/Makefile
@@ -10,31 +10,36 @@ MANPREFIX = ${PREFIX}/man
SRC = ical.c base64.c util.c
HDR = ical.h base64.h util.h
OBJ = ${SRC:.c=.o}
+AWK = tsv2ics.awk
BIN = ics2tree ics2tsv tsv2agenda
MAN1 = ics2txt.1 ics2tsv.1
-MAN5 = tcal.5
all: ${BIN}
.c.o:
${CC} -c ${CFLAGS} -o $@ $<
+${AWK:.awk=}:
+ cp [email protected] $@
+ chmod +x $@
+
${OBJ}: ${HDR}
${BIN}: ${OBJ} ${BIN:=.o}
${CC} ${LDFLAGS} -o $@ [email protected] ${OBJ}
clean:
- rm -rf *.o ${BIN} ${NAME}-${VERSION} *.gz
+ rm -rf *.o ${BIN} ${AWK:.awk} ${NAME}-${VERSION} *.gz
-install:
+install: ${BIN} ${AWK:.awk=}
mkdir -p ${DESTDIR}$(PREFIX)/bin
- cp bin/* $(BIN) ${DESTDIR}$(PREFIX)/bin
+ cp $(BIN) ${AWK:.awk=} ${DESTDIR}$(PREFIX)/bin
mkdir -p ${DESTDIR}$(MANPREFIX)/man1
cp ${MAN1} ${DESTDIR}$(MANPREFIX)/man1
- mkdir -p ${DESTDIR}$(MANPREFIX)/man5
- cp ${MAN5} ${DESTDIR}$(MANPREFIX)/man5
dist: clean
mkdir -p ${NAME}-${VERSION}
- cp -r README Makefile bin ${MAN1} ${MAN5} ${SRC} ${NAME}-${VERSION}
+ cp -r README Makefile ${AWK} ${MAN1} ${SRC} ${NAME}-${VERSION}
tar -cf - ${NAME}-${VERSION} | gzip -c >${NAME}-${VERSION}.tar.gz
+
+.SUFFIXES: .awk
+.PHONY: ${AWK}
diff --git a/bin/ics2tsv b/bin/ics2tsv
@@ -1,141 +0,0 @@
-#!/usr/bin/awk -f
-
-function isleap(year)
-{
- return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)
-}
-
-function mdays(mon, year)
-{
- return (mon == 2) ? (28 + isleap(year)) : (30 + (mon + (mon > 7)) % 2)
-}
-
-function timegm(tm,
- sec, mon, day)
-{
- sec = tm["sec"] + tm["min"] * 60 + tm["hour"] * 3600
-
- day = tm["mday"] - 1
-
- for (mon = tm["mon"] - 1; mon > 0; mon--)
- day = day + mdays(mon, tm["year"])
-
- # constants: x * 365 + x / 400 - x / 100 + x / 4
- day = day + int(tm["year"] / 400) * 146097
- day = day + int(tm["year"] % 400 / 100) * 36524
- day = day + int(tm["year"] % 100 / 4) * 1461
- day = day + int(tm["year"] % 4 / 1) * 365
-
- return sec + (day - 719527) * 86400
-}
-
-function print_vevent(ev, fields,
- i)
-{
- for (i = 1; i in fields; i++)
- printf("%s%s", (i > 1 ? "\t" : ""), ev[fields[i]])
- printf("\n")
-}
-
-function ical_parse_line(str, content, params,
- i, eq)
-{
- if ((i = index(str, ":")) == 0)
- return -1
- content["value"] = substr(str, i + 1)
- str = substr(str, 1, i - 1)
-
- if ((i = index(str, ";")) == 0) {
- content["name"] = str
- return 0
- }
- content["name"] = substr(str, 1, i - 1)
- str = substr(str, i + 1)
-
- while ((i = index(str, ";")) > 0) {
- if ((eq = index(str, "=")) == 0)
- return -1
- param[substr(str, 1, eq - 1)] = substr(str, eq + 1, i - 1)
- str = substr(str, eq + 1)
- }
- if ((eq = index(str, "=")) == 0)
- return -1
- params[substr(str, 1, eq - 1)] = substr(str, eq + 1)
- return 0
-}
-
-function ical_set_tz(tzid)
-{
- gsub("'", "", tzid)
- cmd = "TZ='" tzid "' exec date +%z"
- cmd | getline tzid
- close(cmd)
- TZ = substr(tzid, 1, 1) substr(tzid, 2, 2)*3600 + substr(tzid, 4, 2)*60
-}
-
-function ical_to_epoch(content, param,
- tz, cmd)
-{
- if (param["TZID"])
- ical_set_tz(param["TZID"])
-
- tm["year"] = substr(content["value"], 1, 4)
- tm["mon"] = substr(content["value"], 5, 2)
- tm["mday"] = substr(content["value"], 7, 2)
- tm["hour"] = substr(content["value"], 10, 2)
- tm["min"] = substr(content["value"], 12, 2)
- tm["sec"] = substr(content["value"], 14, 2)
-
- return timegm(tm) + TZ
-}
-
-BEGIN {
- split("DTSTART DTEND CATEGORIES LOCATION SUMMARY DESCRIPTION URL",
- FIELDS, " ")
- DT["DTSTART"] = DT["DTEND"] = DT["DUE"] = 1
-
- # by default: "CATEGORIES" -> "cat", "LOCATION" -> "loc"...
- translate["DTSTART"] = "beg"
- translate["DTEND"] = "end"
-
- for (i = 1; i in FIELDS; i++) {
- if (!(s = translate[FIELDS[i]]))
- s = tolower(substr(FIELDS[i], 1, 3))
- printf("%s%s", (i > 1 ? "\t" : ""), s)
- }
- printf("\n")
-
- FS = "[:;]"
-}
-
-{
- gsub("\r", "")
- gsub("\t", "\\\\t")
-}
-
-sub("^ ", "") {
- content["value"] = content["value"] $0
- next
-}
-
-{
- delete content
- delete param
-
- if (ical_parse_line($0, content, params) < 0)
- next
-
- if (content["name"] == "TZID") {
- ical_set_tz(content["value"])
- } else if (DT[content["name"]]) {
- vevent[content["name"]] = ical_to_epoch(content, params)
- } else {
- vevent[content["name"]] = content["value"]
- }
-}
-
-/^END:VEVENT/ {
- print_vevent(vevent, FIELDS)
- delete vevent
- next
-}
diff --git a/bin/ics2txt b/bin/ics2txt
@@ -1,2 +0,0 @@
-#!/bin/sh -e
-exec ics2tsv "$@" | exec tsv2tcal
diff --git a/bin/tsv2ics b/bin/tsv2ics
@@ -1,104 +0,0 @@
-#!/usr/bin/awk -f
-
-function isleap(year)
-{
- return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)
-}
-
-function mdays(mon, year)
-{
- return (mon == 2) ? (28 + isleap(year)) : (30 + (mon + (mon > 7)) % 2)
-}
-
-# Split the time in seconds since epoch into a table, with fields
-# named as with gmtime(3): tm["year"], tm["mon"], tm["mday"],
-# tm["hour"], tm["min"], tm["sec"]
-function gmtime(sec, tm,
- s)
-{
- tm["year"] = 1970
- while (sec >= (s = 86400 * (365 + isleap(tm["year"])))) {
- tm["year"]++
- sec -= s
- }
-
- tm["mon"] = 1
- while (sec >= (s = 86400 * mdays(tm["mon"], tm["year"]))) {
- tm["mon"]++
- sec -= s
- }
-
- tm["mday"] = 1
- while (sec >= (s = 86400)) {
- tm["mday"]++
- sec -= s
- }
-
- tm["hour"] = 0
- while (sec >= 3600) {
- tm["hour"]++
- sec -= 3600
- }
-
- tm["min"] = 0
- while (sec >= 60) {
- tm["min"]++
- sec -= 60
- }
-
- tm["sec"] = sec
-}
-
-function print_fold(prefix, s, n)
-{
- while (s != "") {
- line = substr(s, 1, n)
- if (length(s) > n) sub(" +[^ \t\r\n]*$", "", line)
- print prefix line
- s = substr(s, length(line) + 2)
- }
-}
-
-BEGIN {
- FS = "\t"
-
- print "BEGIN:VCALENDAR"
- print "VERSION:2.0"
- print "CALSCALE:GREGORIAN"
- print "METHOD:PUBLISH"
-}
-
-NR == 1 {
- for (i = 1; i <= NF; i++)
- name[i] = $i
- next
-}
-
-{
- for (i = 1; i <= NF; i++)
- ev[name[i]] = $i
-
- print ""
- print "BEGIN:VEVENT"
-
- gmtime(ev["beg"] + offset, ev)
- printf "DTSTART:%04d%02d%02dT%02d%02d00Z\n",
- ev["year"], ev["mon"], ev["mday"], ev["hour"], ev["min"]
-
- gmtime(ev["end"] + offset, ev)
- printf "DTEND:%04d%02d%02dT%02d%02d00Z\n",
- ev["year"], ev["mon"], ev["mday"], ev["hour"], ev["min"]
-
- print "SUMMARY:" ev["sum"]
- print "DESCRIPTION:" ev["des"]
- print "CATEGORIES:" ev["cat"]
- print "LOCATION:" ev["loc"]
- print "END:VEVENT"
-
- delete ev
-}
-
-END {
- print ""
- print "END:VCALENDAR"
-}
diff --git a/ics2tsv.c b/ics2tsv.c
@@ -27,11 +27,11 @@ struct Block {
char *fields[FIELDS_MAX];
};
-static int flag_1 = 0;
-static char default_fields[] = "CATEGORIES,LOCATION,SUMMARY,DESCRIPTION";
-static char *flag_s = ",";
-static char *flag_t = NULL;
-static char *flag_f = default_fields;
+static int flag_header = 1;
+static char default_fields[] = "SUMMARY,DESCRIPTION,CATEGORIES,LOCATION";
+static char *flag_sep = ",";
+static char *flag_timefmt = NULL;
+static char *flag_fields = default_fields;
static char *fields[FIELDS_MAX];
static Block block;
@@ -60,9 +60,6 @@ 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)
@@ -70,12 +67,18 @@ fn_block_end(IcalParser *p, char *name)
fputs(p->current->name, stdout);
/* printing dates with %s is much much slower than %lld */
- if (flag_t == NULL) {
+ if (flag_timefmt == NULL) {
printf("\t%lld\t%lld", block.beg, block.end);
} else {
- strftime(buf, sizeof buf, flag_t, localtime_r(&block.beg, &tm)…
+ char buf[128];
+ struct tm tm = {0};
+
+ localtime_r(&block.beg, &tm);
+ strftime(buf, sizeof buf, flag_timefmt, &tm);
printf("\t%s", buf);
- strftime(buf, sizeof buf, flag_t, localtime_r(&block.end, &tm)…
+
+ localtime_r(&block.end, &tm);
+ strftime(buf, sizeof buf, flag_timefmt, &tm);
printf("\t%s", buf);
}
@@ -131,7 +134,7 @@ fn_field_value(IcalParser *p, char *name, char *value)
if ((block.fields[i] = strdup(value)) == NULL)
return ical_err(p, strerror(errno));
} else {
- if (strappend(&block.fields[i], flag_s) == NUL…
+ if (strappend(&block.fields[i], flag_sep) == N…
strappend(&block.fields[i], value) == NULL)
return ical_err(p, strerror(errno));
}
@@ -144,7 +147,7 @@ fn_field_value(IcalParser *p, char *name, char *value)
static void
usage(void)
{
- fprintf(stderr,"usage: %s [-1] [-f fields] [-s subsep] [-t timefmt]"
+ fprintf(stderr,"usage: %s [-1] [-f fields] [-s separator] [-t timefmt]"
" [file...]\n", arg0);
exit(1);
}
@@ -153,7 +156,6 @@ int
main(int argc, char **argv)
{
IcalParser p = {0};
- size_t i;
int c;
arg0 = *argv;
@@ -167,19 +169,22 @@ main(int argc, char **argv)
p.fn_param_value = fn_param_value;
p.fn_field_value = fn_field_value;
- while ((c = getopt(argc, argv, "1f:s:t:")) != -1) {
+ while ((c = getopt(argc, argv, "01f:s:t:")) != -1) {
switch (c) {
+ case '0':
+ flag_header = 0;
+ break;
case '1':
- flag_1 = 1;
+ flag_header = 1;
break;
case 'f':
- flag_f = optarg;
+ flag_fields = optarg;
break;
case 's':
- flag_s = optarg;
+ flag_sep = optarg;
break;
case 't':
- flag_t = optarg;
+ flag_timefmt = optarg;
break;
case '?':
usage();
@@ -189,16 +194,12 @@ main(int argc, char **argv)
argv += optind;
argc -= optind;
- i = 0;
- do {
- if (i >= sizeof fields / sizeof *fields - 1)
- err(1, "too many fields specified with -o flag");
- } while ((fields[i++] = strsep(&flag_f, ",")) != NULL);
- fields[i] = NULL;
+ if (strsplit(flag_fields, fields, LEN(fields), ",") < 0)
+ err(1, "too many fields specified with -f flag");
- if (flag_1) {
- printf("%s\t%s\t%s\t%s", "TYPE", "BEG", "END", "RECUR");
- for (i = 0; fields[i] != NULL; i++)
+ if (flag_header) {
+ printf("%s\t%s\t%s\t%s", "TYPE", "START", "END", "RECUR");
+ for (size_t i = 0; fields[i] != NULL; i++)
printf("\t%s", fields[i]);
fputc('\n', stdout);
}
diff --git a/tsv2agenda.c b/tsv2agenda.c
@@ -1,35 +1,38 @@
#include <assert.h>
+#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
+#include <time.h>
#include "util.h"
#ifndef __OpenBSD__
#define pledge(...) 0
#endif
-#define FIELDS_MAX 128
-
enum {
FIELD_TYPE,
FIELD_BEG,
FIELD_END,
FIELD_RECUR,
FIELD_OTHER,
+ FIELD_MAX = 128,
};
typedef struct {
struct tm beg, end;
+ char *fieldnames[FIELD_MAX];
+ size_t fieldnum;
+ size_t linenum;
} AgendaCtx;
-static size_t field_categories = 0;
-static size_t field_location = 0;
-static size_t field_summary = 0;
+static time_t flag_from = INT64_MIN;
+static time_t flag_to = INT64_MAX;
-void
+static void
print_date(struct tm *tm)
{
if (tm == NULL) {
@@ -42,7 +45,7 @@ print_date(struct tm *tm)
}
}
-void
+static void
print_time(struct tm *tm)
{
if (tm == NULL) {
@@ -55,139 +58,175 @@ print_time(struct tm *tm)
}
}
-void
-print(AgendaCtx *ctx, char **fields, size_t n)
+static void
+print_header1(struct tm *old, struct tm *new)
+{
+ int same;
+
+ same = (old->tm_year == new->tm_year && old->tm_mon == new->tm_mon &&
+ old->tm_mday == new->tm_mday);
+ print_date(same ? NULL : new);
+ print_time(new);
+}
+
+static void
+print_header2(struct tm *beg, struct tm *end)
+{
+ int same;
+
+ same = (beg->tm_year == end->tm_year && beg->tm_mon == end->tm_mon &&
+ beg->tm_mday == end->tm_mday);
+ print_date(same ? NULL : end);
+
+ same = (beg->tm_hour == end->tm_hour && beg->tm_min == end->tm_min);
+ print_time(same ? NULL : end);
+}
+
+static void
+print_header3(void)
+{
+ print_date(NULL);
+ print_time(NULL);
+}
+
+static void
+print_row(AgendaCtx *ctx, char **fields, size_t i)
+{
+ if (i > ctx->fieldnum || *fields[i] == '\0')
+ return;
+ fprintf(stdout, "%s\n", fields[i]);
+}
+
+static void
+print(AgendaCtx *ctx, char **fields)
{
struct tm beg = {0}, end = {0};
time_t t;
+ size_t i = FIELD_OTHER;
char const *e;
- int rows, samedate;
- t = strtonum(fields[FIELD_BEG], 0, UINT32_MAX, &e);
+ t = strtonum(fields[FIELD_BEG], INT64_MIN, INT64_MAX, &e);
if (e != NULL)
err(1, "start time %s is %s", fields[FIELD_BEG], e);
+ if (t > flag_to)
+ return;
localtime_r(&t, &beg);
- t = strtonum(fields[FIELD_END], 0, UINT32_MAX, &e);
+ t = strtonum(fields[FIELD_END], INT64_MIN, INT64_MAX, &e);
if (e != NULL)
err(1, "end time %s is %s", fields[FIELD_END], e);
+ if (t < flag_from)
+ return;
localtime_r(&t, &end);
- fputc('\n', stdout);
-
- samedate = (ctx->beg.tm_year != beg.tm_year || ctx->beg.tm_mon != beg.…
- ctx->beg.tm_mday != beg.tm_mday);
- print_date(samedate ? &beg : NULL);
- print_time(&beg);
-
- assert(field_summary < n);
- assert(field_summary > FIELD_OTHER);
- fprintf(stdout, "%s\n", fields[field_summary]);
-
- samedate = (beg.tm_year != end.tm_year || beg.tm_mon != end.tm_mon ||
- beg.tm_mday != end.tm_mday);
- print_date(samedate ? &end : NULL);
- print_time(&end);
-
- rows = 0;
-
- assert(field_location < n);
- if (field_location > 0 && fields[field_location][0] != '\0') {
- assert(field_summary > FIELD_OTHER);
- fprintf(stdout, "%s\n", fields[field_location]);
- rows++;
- }
-
- assert(field_categories < n);
- if (field_categories > 0 && fields[field_categories][0] != '\0') {
- assert(field_summary > FIELD_OTHER);
- if (rows > 0) {
- print_date(NULL);
- print_time(NULL);
- }
- fprintf(stdout, "%s\n", fields[field_categories]);
+ print_header1(&ctx->beg, &beg);
+ print_row(ctx, fields, i++);
+ print_header2(&beg, &end);
+ print_row(ctx, fields, i++);
+ while (i < ctx->fieldnum) {
+ print_header3();
+ print_row(ctx, fields, i++);
}
ctx->beg = beg;
ctx->end = end;
}
-void
-set_fields_num(char **fields, size_t n)
+static void
+tsv_to_agenda(AgendaCtx *ctx, FILE *fp)
{
- struct { char *name; size_t *var; } map[] = {
- { "CATEGORIES", &field_categories },
- { "LOCATION", &field_location },
- { "SUMMARY", &field_summary },
- { NULL, NULL }
- };
-
- debug("n=%zd", n);
- for (size_t i1 = FIELD_OTHER; i1 < n; i1++)
- for (size_t i2 = 0; map[i2].name != NULL; i2++)
- if (strcasecmp(fields[i1], map[i2].name) == 0)
- *map[i2].var = i1;
- if (field_summary < FIELD_OTHER)
- err(1, "missing column SUMMARY");
-}
+ char *ln1 = NULL, *ln2 = NULL;
+ size_t sz1 = 0, sz2 = 0;
+
+ if (ctx->linenum == 0) {
+ char *fields[FIELD_MAX];
+
+ ctx->linenum++;
+ if (getline(&ln1, &sz1, fp) < 0)
+ err(1, "reading stdin: %s", strerror(errno));
+ if (feof(fp))
+ err(1, "empty input");
+
+ strchomp(ln1);
+ ctx->fieldnum = strsplit(ln1, fields, FIELD_MAX, "\t");
+ if (ctx->fieldnum == FIELD_MAX)
+ err(1, "line 1: too many fields");
+ if (ctx->fieldnum < FIELD_OTHER)
+ err(1, "line 1: not enough input columns");
+ if (strcasecmp(fields[0], "TYPE") != 0)
+ err(1, "line 1: 1st column is not \"TYPE\"");
+ if (strcasecmp(fields[1], "START") != 0)
+ err(1, "line 1: 2nd column is not \"START\"");
+ if (strcasecmp(fields[2], "END") != 0)
+ err(1, "line 1: 3rd column is not \"END\"");
+ if (strcasecmp(fields[3], "RECUR") != 0)
+ err(1, "line 1: 4th column is not \"RECUR\"");
+ }
-ssize_t
-tsv_getline(char **fields, size_t max, char **line, size_t *sz, FILE *fp)
-{
- char *s;
- size_t n = 0;
+ for (;;) {
+ char *fields[FIELD_MAX];
+
+ ctx->linenum++;
+ if (getline(&ln2, &sz2, fp) < 0)
+ err(1, "reading stdin: %s", strerror(errno));
+ if (feof(fp))
+ break;
+
+ strchomp(ln2);
+ if (strsplit(ln2, fields, FIELD_MAX, "\t") != ctx->fieldnum)
+ err(1, "line %zd: bad number of columns",
+ ctx->linenum, strerror(errno));
- if (getline(line, sz, fp) <= 0)
- return ferror(fp) ? -1 : 0;
- s = *line;
- strchomp(s);
+ fputc('\n', stdout);
+ print(ctx, fields);
+ }
+ fputc('\n', stdout);
- do {
- if (n >= max)
- return errno=E2BIG, -1;
- } while ((fields[n++] = strsep(&s, "\t")) != NULL);
+ free(ln1);
+ free(ln2);
+}
- return n - 1;
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-f fromdate] [-t todate]\n", arg0);
+ exit(1);
}
int
main(int argc, char **argv)
{
AgendaCtx ctx = {0};
- ssize_t nfield, n;
- size_t sz = 0;
- char *line = NULL, *fields[FIELDS_MAX];
+ char c;
- arg0 = *argv;
-
- if (pledge("stdio", "") < 0)
- err(1, "pledge: %s", strerror(errno));
+ if ((flag_from = time(NULL)) == (time_t)-1)
+ err(1, "time: %s", strerror(errno));
- nfield = tsv_getline(fields, FIELDS_MAX, &line, &sz, stdin);
- if (nfield == -1)
- err(1, "reading stdin: %s", strerror(errno));
- if (nfield == 0)
- err(1, "empty input");
- if (nfield < FIELD_OTHER)
- err(1, "not enough input columns");
-
- set_fields_num(fields, nfield);
-
- for (size_t num = 1;; num++) {
- n = tsv_getline(fields, FIELDS_MAX, &line, &sz, stdin);
- if (n < 0)
- err(1, "line %zd: reading stdin: %s", num, strerror(er…
- if (n == 0)
+ arg0 = *argv;
+ while ((c = getopt(argc, argv, "f:t:")) > 0) {
+ char const *e;
+
+ switch (c) {
+ case 'f':
+ flag_from = strtonum(optarg, INT64_MIN, INT64_MAX, &e);
+ if (e != NULL)
+ err(1, "fromdate value %s is %s", optarg, e);
break;
- if (n != nfield)
- err(1, "line %zd: had %lld columns, wanted %lld",
- num, n, nfield);
-
- print(&ctx, fields, n);
+ case 't':
+ flag_to = strtonum(optarg, INT64_MIN, INT64_MAX, &e);
+ if (e != NULL)
+ err(1, "todate value %s is %s", optarg, e);
+ break;
+ default:
+ usage();
+ }
}
- fputc('\n', stdout);
+ argc -= optind;
+ argv += optind;
- free(line);
+ if (pledge("stdio", "") < 0)
+ err(1, "pledge: %s", strerror(errno));
+ tsv_to_agenda(&ctx, stdin);
return 0;
}
diff --git a/tsv2ics.awk b/tsv2ics.awk
@@ -0,0 +1,106 @@
+#!/usr/bin/awk -f
+
+function isleap(year)
+{
+ return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)
+}
+
+function mdays(mon, year)
+{
+ return (mon == 2) ? (28 + isleap(year)) : (30 + (mon + (mon > 7)) % 2)
+}
+
+# Split the time in seconds since epoch into a table, with fields
+# named as with gmtime(3): tm["year"], tm["mon"], tm["mday"],
+# tm["hour"], tm["min"], tm["sec"]
+function gmtime(sec, tm,
+ s)
+{
+ tm["year"] = 1970
+ while (sec >= (s = 86400 * (365 + isleap(tm["year"])))) {
+ tm["year"]++
+ sec -= s
+ }
+ tm["mon"] = 1
+ while (sec >= (s = 86400 * mdays(tm["mon"], tm["year"]))) {
+ tm["mon"]++
+ sec -= s
+ }
+ tm["mday"] = 1
+ while (sec >= (s = 86400)) {
+ tm["mday"]++
+ sec -= s
+ }
+ tm["hour"] = 0
+ while (sec >= 3600) {
+ tm["hour"]++
+ sec -= 3600
+ }
+ tm["min"] = 0
+ while (sec >= 60) {
+ tm["min"]++
+ sec -= 60
+ }
+ tm["sec"] = sec
+}
+
+BEGIN {
+ FS = "\t"
+
+ DTSTART["VEVENT"] = "DTSTART"
+ DTEND["VEVENT"] = "DTEND"
+
+ DTEND["VTODO"] = "DUE"
+
+ DTSTART["VJOURNAL"] = "DTSTAMP"
+
+ DTSTART["VFREEBUSY"] = "DTSTART"
+ DTEND["VFREEBUSY"] = "DTEND"
+
+ DTSTART["VALARM"] = "DTSTART"
+
+ print "BEGIN:VCALENDAR"
+ print "VERSION:2.0"
+ print "CALSCALE:GREGORIAN"
+ print "METHOD:PUBLISH"
+}
+
+NR == 1 {
+ if ($1 != "TYPE" || $2 != "START" || $3 != "END" || $4 != "RECUR") {
+ print "tsv2ics: invalid column names on first line" >/dev/stde…
+ exit(EXIT = 1)
+ }
+ for (i = 1; i <= NF; i++) {
+ FIELD[$i] = i
+ NAME[i] = $i
+ }
+ next
+}
+
+{
+ type = $FIELD["TYPE"]
+ print "BEGIN:"type
+
+ if (type in DTSTART) {
+ gmtime($FIELD["START"] + offset, tm)
+ printf "%s:%04d%02d%02dT%02d%02d00Z\n", DTSTART[type],
+ tm["year"], tm["mon"], tm["mday"], tm["hour"], tm["min"]
+ }
+
+ if (type in DTEND) {
+ gmtime($FIELD["END"] + offset, tm)
+ printf "%s:%04d%02d%02dT%02d%02d00Z\n", DTEND[type],
+ tm["year"], tm["mon"], tm["mday"], tm["hour"], tm["min"]
+ }
+
+ for (i = 5; i in NAME; i++)
+ print$NAME[i]":"$i
+
+ print "END:"type
+}
+
+END {
+ if (EXIT) exit(EXIT)
+ print ""
+ print "END:VCALENDAR"
+}
diff --git a/util.c b/util.c
@@ -1,4 +1,5 @@
#include "util.h"
+#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
@@ -123,6 +124,19 @@ strappend(char **dp, char const *s)
return *dp;
}
+size_t
+strsplit(char *s, char **array, size_t len, char const *sep)
+{
+ size_t i;
+
+ assert(len > 0);
+ for (i = 0; i < len; i++)
+ if ((array[i] = strsep(&s, sep)) == NULL)
+ break;
+ array[len - 1] = NULL;
+ return i;
+}
+
/** memory **/
void *
diff --git a/util.h b/util.h
@@ -2,9 +2,12 @@
#define UTIL_H
#include <stddef.h>
+#include <stdio.h>
#include <stdarg.h>
#include <time.h>
+#define LEN(x) (sizeof (x) / sizeof *(x))
+
/** logging **/
extern char *arg0;
void err(int, char const *fmt, ...);
@@ -18,6 +21,7 @@ void strchomp(char *);
char *strappend(char **, char const *);
size_t strlcat(char *, char const *, size_t);
long long strtonum(const char *, long long, long long, const char **);
+size_t strsplit(char *, char **, size_t, char const *);
/** memory **/
void *reallocarray(void *, size_t, size_t);
You are viewing proxied material from bitreich.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.