simplify the tcal format a lot, and add a manpage for it - 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 fef052ed4c0f485f6c87b75555dc2df664bb602e DIR parent 708175eee823de7ce8690d23f9a5adf1b0839a71 HTML Author: Josuah Demangeon <me@josuah.net> Date: Wed, 24 Jun 2020 22:59:31 +0200 simplify the tcal format a lot, and add a manpage for it Diffstat: M Makefile | 28 ++++++++++++++++++++-------- M ics2tsv | 84 +++++++++++++++++++++---------- M tcal.5 | 67 +++++++++++++++++-------------- M tcal2tsv | 102 ++++++++++++++++--------------- M tsv2tcal | 74 ++++++++++++++----------------- 5 files changed, 201 insertions(+), 154 deletions(-) --- DIR diff --git a/Makefile b/Makefile @@ -1,11 +1,23 @@ -PREFIX = /usr/local -BIN = ics2tsv tsv2tcal tcal2tsv tsv2ics ics2txt -MAN1 = ics2txt.1 +NAME = ics2txt +VERSION = 0.1 -all: +BIN = ics2tsv tsv2tcal tcal2tsv tsv2ics ics2txt + +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/man + +all: ${BIN} + +clean: + rm -rf ${NAME}-${VERSION} *.gz install: - mkdir -p $(PREFIX)/bin - cp $(BIN) $(PREFIX)/bin - mkdir -p $(PREFIX)/share/man/man1 - cp $(MAN1) $(PREFIX)/share/man/man1 + mkdir -p ${DESTDIR}$(PREFIX)/bin + cp $(BIN) ${DESTDIR}$(PREFIX)/bin + mkdir -p ${DESTDIR}$(MANPREFIX)/man1 + cp *.1 ${DESTDIR}$(MANPREFIX)/man1 + +dist: clean + mkdir -p ${NAME}-${VERSION} + cp -r README Makefile doc src ${BIN:=.c} ${NAME}-${VERSION} + tar -cf - ${NAME}-${VERSION} | gzip -c >${NAME}-${VERSION}.tar.gz DIR diff --git a/ics2tsv b/ics2tsv @@ -10,28 +10,53 @@ function mdays(mon, year) return (mon == 2) ? (28 + isleap(year)) : (30 + (mon + (mon > 7)) % 2) } -function timegm(year, mon, mday, hour, min, sec) +function maketime(tm, + sec, mon, day) { - while (--mon >= 1) - mday += mdays(mon, year) - while (--year >= 1970) - mday += 365 + isleap(year) - return (((((mday - 1) * 24) + hour) * 60) + min) * 60 + sec + 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 date_ical(str, offset, - year, mon, mday, hour, min) +function ical_to_epoch(str, offset, + tm) { - year = substr(str, 1, 4) - mon = substr(str, 5, 2) - mday = substr(str, 7, 2) - hour = substr(str, 10, 2) - min = substr(str, 12, 2) + tm["year"] = substr(str, 1, 4) + tm["mon"] = substr(str, 5, 2) + tm["mday"] = substr(str, 7, 2) + tm["hour"] = substr(str, 10, 2) + tm["min"] = substr(str, 12, 2) offset = (substr(str, 16, 1) == "Z" ? 0 : offset) - return timegm(year, mon, mday, hour, min, 0) - offset + return maketime(tm) - offset +} + +function print_event(ev, fields, + i) +{ + for (i = 1; i <= fields["len"]; i++) + printf("%s%s", (i > 1 ? "\t" : ""), ev[fields[i]]) + printf("\n") } BEGIN { + FIELDS = "DTSTART DTEND CATEGORIES LOCATION SUMMARY DESCRIPTION" + fields["len"] = split(FIELDS, fields, " ") + + # by default: "CATEGORIES" -> "cat", "LOCATION" -> "loc"... + translate["DTSTART"] = "beg" + translate["DTEND"] = "end" + "date +%z" | getline offset_str close("date +%z") hour = substr($0, 4, 2) @@ -39,26 +64,33 @@ BEGIN { tzoffset = substr(zone, 3, 1) hour * 3600 + min * 60 FS = "[:;]" + + for (i = 1; i <= fields["len"]; i++) { + if (!(s = translate[fields[i]])) + s = tolower(substr(fields[i], 1, 3)) + printf("%s%s", (i > 1 ? "\t" : ""), s) + } + + printf("\n") } { - gsub("\r", ""); gsub("\t", "\\\\t") - gsub("^ *", ""); gsub(" *$", "") + gsub("\r", "") + gsub("\t", "\\\\t") + gsub("^ *", "") + gsub(" *$", "") if (match($0, "^ ")) { - event[type] = event[type] substr($0, 2, length($0) - 1) + ev[type] = ev[type] substr($0, 2, length($0) - 1) } else { type = $1 i = index($0, ":") - event[type] = substr($0, i + 1, length($0) - i) + ev[type] = substr($0, i + 1, length($0) - i) } +} - if ($0 ~ /^END:VEVENT/) - printf("%d\t%d\t%s\t%s\t%s\t%s\n", - date_ical(event["DTSTART"], offset), - date_ical(event["DTEND"], offset), - event["CATEGORIES"], - event["LOCATION"], - event["SUMMARY"], - event["DESCRIPTION"]) +/^END:VEVENT/ { + ev["DTSTART"] = ical_to_epoch(ev["DTSTART"], offset) + ev["DTEND"] = ical_to_epoch(ev["DTEND"], offset) + print_event(ev, fields) } DIR diff --git a/tcal.5 b/tcal.5 @@ -6,52 +6,57 @@ .Sh NAME . .Nm tcal -.Nd plaintext calendar event notation format -. -. -.Sh SYNOPSIS -. -TZ+0300 +.Nd plaintext calendar for editing by hand on the go . . .Sh DESCRIPTION . -The -.Nm -utility -. -. +The first line contain +.Dq TZ+HHMM +with +.Dq +HHMM +as returned by +.D1 $ date +%z . . -.Sh FILES +.Pp +Then empty line delimited event entries follow, with for each: +One line with the start date, one line with the end date, +formatted like: +.Dq %Y-%m-%d %H:%M . +.Pp +Then one line per attribute, each formatted with: +optional space, attribute name, colon, +optional space, and attribute content, +end of line. . . .Sh EXAMPLES . +.Bd -literal +TZ+0200 + +2020-06-28 00:00 +2020-06-05 00:00 + loc: 950-0994, Chuo Ward, Niigata, Japan + sum: summer holidays + +2020-06-29 13:30 +2020-06-29 15:00 + loc: online, irc.freenode.net, #bitreich-en + sum: bitreich irc invitation + des: at this moment like all other moment, everyone invited on IRC + +.Ed . . .Sh SEE ALSO . -.Xr foobar 1 -. -. -.Sh STANDARDS -. -. -. -.Sh HISTORY -. +.Xr cal 1 , +.Xr calendar 1 . . .Sh AUTHORS . -.An <author-name> -.Aq Mt <author-email> -. -. -.Sh CAVEATS -. -. -. -.Sh BUGS -. +.An Josuah Demangeon +.Aq Mt me@josuah.net DIR diff --git a/tcal2tsv b/tcal2tsv @@ -10,24 +10,45 @@ function mdays(mon, year) return (mon == 2) ? (28 + isleap(year)) : (30 + (mon + (mon > 7)) % 2) } -function timegm(year, mon, mday, hour, min, sec) +function maketime(tm, + sec, mon, day) { - while (--mon >= 1) - mday += mdays(mon, year) - while (--year >= 1970) - mday += 365 + isleap(year) - return (((((mday - 1) * 24) + hour) * 60) + min) * 60 + sec + 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 date_text(str, offset, - year, mon, mday, hour, min) +function text_to_epoch(str, tz, + tm) { - year = substr(str, 1, 4) - mon = substr(str, 6, 2) - mday = substr(str, 9, 2) - hour = substr(str, 12, 2) - min = substr(str, 15, 2) - return timegm(year, mon, mday, hour, min, 0) - offset + tm["year"] = substr(str, 1, 4) + tm["mon"] = substr(str, 6, 2) + tm["mday"] = substr(str, 9, 2) + tm["hour"] = substr(str, 12, 2) + tm["min"] = substr(str, 15, 2) + return maketime(tm) - tz +} + +BEGIN { + FIELDS = "beg end cat loc sum des" + fields["len"] = split(FIELDS, fields, " ") + + for (i = 1; i <= fields["len"]; i++) { + pos[fields[i]] = i + printf("%s%s", (i > 1 ? "\t" : ""), fields[i]) + } + printf("\n") } { @@ -35,47 +56,30 @@ function date_text(str, offset, } /^TZ[+-]/ { - hour = substr($0, 4, 2) - min = substr($0, 6, 2) - tzoffset = substr(zone, 3, 1) hour * 3600 + min * 60 - next + TZOFFSET = substr($1, 3, 1) substr($0, 4, 2)*3600 + substr($0, 6, 2)*60 + while (getline && $0 ~ /^$/) + continue } -/^[0-9]+-[0-9]+-[0-9]+ / { - time = date_text($1 " " $2, tzoffset) - row++ +/^[0-9]+-[0-9]+-[0-9]+/ { + if ("beg" in ev) + ev["end"] = text_to_epoch($0, TZOFFSET) + else + ev["beg"] = text_to_epoch($0, TZOFFSET) + next } /^ / { - d = $0 - sub(/^ */, "", d) - des = des " " d + tag = $1 + sub("^ *[^ :]+: *", "") + sub(":$", "", tag) + ev[tag] = $0 + next } /^$/ { - if (beg) - printf "%d\t%d\t%s\t%s\t%s\t%s\n", beg, end, cat, loc, sum, des - beg = end = cat = loc = sum = des = "" -} - -row == 1 { - beg = time - sum = $0 - sub(/^[^ ]+ +[^ ]+ +/, "", sum) -} - -row == 2 { - end = time - - line = $0 - sub(/^[^ ]+ +[^ ]+ +/, "", line) - - cat = line - sub(/\].*/, "", cat) - sub(/^\[/, "", cat) - - loc = line - sub(/[^]]*\] */, "", loc) - - row = 0 + for (i = 1; i <= fields["len"]; i++) + printf("%s%s", (i > 1 ? "\t" : ""), ev[fields[i]]) + printf("\n") + delete ev } DIR diff --git a/tsv2tcal b/tsv2tcal @@ -10,88 +10,82 @@ 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) +function gmtime(sec, tm) { 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) +function localtime(sec, tm, + tz, h, m) { - 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) - } + return gmtime(sec + TZOFFSET, tm) } BEGIN { - cmd = "date +%z" - cmd | getline zone - close(cmd) + "date +%z" | getline tz + close("date +%z") + TZOFFSET = substr(tz, 1, 1) substr(tz, 2, 2)*3600 + substr(tz, 4, 2)*60 - hour = substr(zone, 2, 2) - min = substr(zone, 4, 2) + print("TZ" tz) - offset = (substr(zone, 1, 1) "1") * (hour * 3600 + min * 60) - print "TZ" zone + FS = "\t" +} + +NR == 1 { + for (i = 1; i <= NF; i++) + name[i] = $i + next } { - split($0, a, "\t") - gmtime(a[1] + offset, beg) - gmtime(a[2] + offset, end) - cat = a[3]; loc = a[4]; sum = a[5]; des = a[6] + for (i = 1; i <= NF; i++) + ev[name[i]] = $i + + print("") - print "" - printf "%04d-%02d-%02d %02d:%02d ", - beg["year"], beg["mon"], beg["mday"], beg["hour"], beg["min"] - print sum + localtime(ev["beg"] + offset, tm) + printf("%04d-%02d-%02d %02d:%02d\n", + tm["year"], tm["mon"], tm["mday"], tm["hour"], tm["min"]) + delete ev["beg"] - printf "%04d-%02d-%02d %02d:%02d ", - end["year"], end["mon"], end["mday"], end["hour"], end["min"] - print "[" cat "] " loc + localtime(ev["end"] + offset, tm) + printf("%04d-%02d-%02d %02d:%02d\n", + tm["year"], tm["mon"], tm["mday"], tm["hour"], tm["min"]) + delete ev["end"] + + for (i = 1; i <= NF; i++) { + if (name[i] in ev && ev[name[i]]) + printf(" %s: %s\n", name[i], ev[name[i]]) + } - sub("^ *", "", des) - sub(" *$", "", des) - if (des) - print_fold(" ", des, 80) + delete ev } END { - print "" + print("") }