simplify the tcal format a lot, and add a manpage for it - ics2txt - convert ic… | |
git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
--- | |
commit fef052ed4c0f485f6c87b75555dc2df664bb602e | |
parent 708175eee823de7ce8690d23f9a5adf1b0839a71 | |
Author: Josuah Demangeon <[email protected]> | |
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(-) | |
--- | |
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 | |
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) | |
} | |
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 [email protected] | |
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 | |
} | |
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("") | |
} |