Home
       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("")
        }