imported project from ~/bin repo - 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 c2ed3821e4f01043c1e8b3c9572c36ee6ff49af3
 (HTM) Author: Josuah Demangeon <mail@josuah.net>
       Date:   Thu, 19 Apr 2018 03:41:16 +0200
       
       imported project from ~/bin repo
       
       Diffstat:
         A Makefile                            |      10 ++++++++++
         A agenda                              |     182 +++++++++++++++++++++++++++++++
         A agenda.1                            |      80 +++++++++++++++++++++++++++++++
       
       3 files changed, 272 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -0,0 +1,10 @@
       +BIN        = agenda
       +MAN1        = agenda.1
       +
       +all:
       +
       +install:
       +        mkdir -p $(PREFIX)/bin
       +        cp $(BIN) $(PREFIX)/bin
       +        mkdir -p $(PREFIX)/share/man/man1
       +        cp $(MAN1) $(PREFIX)/share/man/man1
 (DIR) diff --git a/agenda b/agenda
       @@ -0,0 +1,182 @@
       +#!/usr/bin/awk -f
       +
       +# handle ical agenda and display them in plain text
       +
       +function leap(yrs)
       +{
       +        return (yrs % 4 == 0) && (yrs % 100 != 0) || (yrs % 400 == 0);
       +}
       +
       +function days_per_month(mth, yrs)
       +{
       +        if (mth == 2)
       +                return 28 + leap(yrs);
       +        else
       +                return 30 + (mth - (mth > 7)) % 2;
       +}
       +
       +function to_sec(yrs, mth, day, hrs, min, sec)
       +{
       +        while (--mth >= 1)
       +                day += days_per_month(mth, yrs);
       +        while (--yrs >= 1970)
       +                day += 365 + leap(yrs);
       +        return (((((day - 1) * 24) + hrs) * 60) + min) * 60 + sec;
       +}
       +
       +function to_date(fmt, sec)
       +{
       +        for (yrs = 1970; sec >= (s = 3600 * 24 * (365 + leap(yrs))); yrs++)
       +                sec -= s;
       +        for (mth = 1; sec >= (s = 3600 * 24 * days_per_month(mth, yrs)); mth++)
       +                sec -= s;
       +        for (day = 1; sec >= (s = 3600 * 24); day++)
       +                sec -= s;
       +        for (hrs = 0; sec >= 3600; hrs++)
       +                sec -= 3600;
       +        for (min = 0; sec >= 60; min++)
       +                sec -= 60;
       +        return sprintf(fmt, yrs, mth, day, hrs, min, sec);
       +}
       +
       +function date_ical(str, off) {
       +        yrs = substr(str,  1, 4);
       +        mth = substr(str,  5, 2);
       +        day = substr(str,  7, 2);
       +        hrs = substr(str, 10, 2);
       +        min = substr(str, 12, 2);
       +        return to_sec(yrs, mth, day, hrs, min, 0) - off;
       +}
       +
       +function date_iso8601(date, off)
       +{
       +        yrs = substr(date,  1, 4);
       +        mth = substr(date,  6, 2);
       +        day = substr(date,  9, 2);
       +        hrs = substr(date, 12, 2);
       +        min = substr(date, 15, 2);
       +        return to_sec(yrs, mth, day, hrs, min, 0) - off;
       +}
       +
       +function swap(array, a, b)
       +{
       +        tmp = array[a];
       +        array[a] = array[b];
       +        array[b] = tmp;
       +}
       +
       +function sort(array, beg, end)
       +{
       +        if (beg >= end)                                        # end recursion
       +                return;
       +
       +        a = beg + 1;                                        # #1 is the pivot
       +        b = end;
       +        while (a < b) {
       +                while (a < b && array[a] <= array[beg])        # beg: skip lesser
       +                        a++;
       +                while (a < b && array[b] > array[beg])        # end: skip greater
       +                        b--;
       +                swap(array, a, b);                        # found 2 misplaced
       +        }
       +
       +        if (array[beg] > array[a])                        # put the pivot back
       +                swap(array, beg, a);
       +
       +        sort(array, beg, a - 1);                        # sort lower half
       +        sort(array, a, end);                                # sort higher half
       +}
       +
       +function parse_ical(list, off)
       +{
       +        FS = "[:;]";
       +
       +        while (getline) {
       +                gsub("\\\\[ntr]", "  ");
       +                gsub("[ \t]*<[a-zA-Z0-9/]*>*[ \t]*", "");
       +                gsub("\\\\", "");
       +
       +                if (match($0, "^ ")) {
       +                        event[type] = event[type] substr($0, 2, length($0) - 1);
       +                } else {
       +                        type = $1;
       +                        i = index($0, ":");
       +                        event[type] = substr($0, i + 1, length($0) - i);
       +                }
       +
       +                if ($0 ~ /END:VEVENT/)
       +                        list[++nb] = sprintf("%d\t%d\t%s\t%s\t%s\t%s",
       +                            date_ical(event["DTSTART"], off),
       +                            date_ical(event["DTEND"], off),
       +                            event["CATEGORIES"],
       +                            event["SUMMARY"],
       +                            event["LOCATION"],
       +                            event["DESCRIPTION"]);
       +        }
       +        sort(list, 1, nb);
       +        return nb;
       +}
       +
       +function txt(off)
       +{
       +        nb = parse_ical(list, off);
       +        for (i = 1; i <= nb; i++) {
       +                split(list[i], arr, "\t");
       +                txt_one(arr[1], arr[2], arr[3], arr[4], arr[5], arr[6]);
       +        }
       +}
       +
       +function txt_one(beg, end, cat, sum, loc, des, off) {
       +        b = to_date("%04d/%02d/%02d %02d:%02d", beg + off);
       +        e = to_date("%04d/%02d/%02d %02d:%02d", end + off);
       +        b_mth = substr(b, 1, 7);
       +        b_day = substr(b, 9, 2);
       +        e_day = substr(e, 9, 2);
       +        b_hrs = substr(b, 12);
       +        e_hrs = substr(e, 12);
       +
       +        printf("%s\n%2s  %2s  %s\n%2s  %2s  %s%s\n",
       +            (b_mth != l_mth)        ? ("\n[" b_mth "]\n")        : (""),
       +            (b_day != l_day)        ? (b_day)                : (""), b_hrs, sum,
       +            (b_day != e_day)        ? (e_day)                : (""), e_hrs,
       +            (cat)                 ? ("[" cat "] ")        : (""), loc);
       +
       +        while ((line = substr(des, 1, 66)) != "") {
       +                if (length(line) == 66)
       +                        sub(" +[^ ]*$", "", line);
       +                printf("           %s\n", line);
       +                des = substr(des, length(line) + 1);
       +                sub("^ *", "", des);
       +        }
       +        l_mth = b_mth;
       +        l_day = b_day;
       +}
       +
       +function tsv(off)
       +{
       +        n = parse_ical(list, off);
       +        for (i = 0; i < n; i++)
       +                print(list[i]);
       +}
       +
       +function usage()
       +{
       +        print("usage: agenda txt file.ics...");
       +        print("       agenda tsv file.ics...");
       +}
       +
       +BEGIN {
       +        "date +%z" | getline off;
       +        close("date +%z");
       +        off = substr(off, 1, 3) * 3600;
       +
       +        if (ARGV[1] == "txt") {
       +                ARGV[1] = ARGV[--ARGC];
       +                txt(off);
       +        } else if (ARGV[1] == "tsv") {
       +                ARGV[1] = ARGV[--ARGC];
       +                tsv(off);
       +        } else {
       +                usage();
       +        }
       +}
 (DIR) diff --git a/agenda.1 b/agenda.1
       @@ -0,0 +1,80 @@
       +.Dd $Mdocdate: February 23 2018$
       +.Dt AGENDA 1
       +.Os
       +.
       +.
       +.Sh NAME
       +.
       +.Nm agenda
       +.Nd plain text agenda with ical support
       +.
       +.
       +.Sh SYNOPSIS
       +.
       +.Nm Ic txt Op +- Ar offset Op Ar ics file...
       +.Nm Ic tsv Op +- Ar offset Op Ar ics file...
       +.
       +.
       +.Sh DESCRIPTION
       +.
       +.Nm
       +displays iCalendar 
       +.Pq ical, Pa .ics
       +files created by.
       +.
       +.Bl -tag -width indent
       +.
       +.It Nm Ic txt Op +- Ar offset Op Ar ics file...
       +Display the agenda(s)
       +.Ar file
       +as plain text sorted by date.
       +.
       +.It Nm Ic tsv Op +- Ar offset Op Ar ics file...
       +Display the agenda(s)
       +.Ar file
       +as a tab-separated values
       +.Pq tsv
       +one entry per line, with the following fields in order:
       +.
       +.Bl -tag -width xDESCRIPTIONx -compact
       +.
       +.It Dq Li DTSTART
       +begin date as an UNIX timestamp
       +.
       +.It Dq Li DTEND
       +end date as an UNIX timestamp
       +.
       +.It Dq Li CATEGORY
       +category
       +.
       +.It Dq Li SUMMARY
       +symmary
       +.
       +.It Dq Li LOCATION
       +location
       +.
       +.It Dq Li DESCRIPTION
       +description
       +.
       +.El
       +.
       +.
       +.Sh ENVIRONMENT
       +.
       +.Bl -tag -width 6n
       +.
       +.It Ev TZ
       +Timezone to use for printing the dates.
       +.
       +.El
       +.
       +.
       +.Sh SEE ALSO
       +.
       +.Xr calendar 1 ,
       +.Xr date 1 ,
       +.
       +.
       +.Sh AUTHORS
       +.
       +.An Josuah Demangeon Aq Mt mail@josuah.net