add a man page and a -1 flag - 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 4bcfcc3e64d33c6e67b3f6f6359e859d2ee0ff6b
 (DIR) parent 8894359aa6ad4ccc485901a8af9db03d1a2b4d5f
 (HTM) Author: Josuah Demangeon <me@josuah.net>
       Date:   Sat, 19 Jun 2021 12:15:14 +0200
       
       add a man page and a -1 flag
       
       Diffstat:
         M Makefile                            |       4 ++--
         A ics2tsv.1                           |     150 +++++++++++++++++++++++++++++++
         M ics2tsv.c                           |      22 ++++++++++++++++++++--
       
       3 files changed, 172 insertions(+), 4 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -11,7 +11,7 @@ SRC = ical.c base64.c util.c
        HDR = ical.h base64.h util.h
        OBJ = ${SRC:.c=.o}
        BIN = ics2tree ics2tsv
       -MAN1 = ics2txt.1
       +MAN1 = ics2txt.1 ics2tsv.1
        MAN5 = tcal.5
        
        all: ${BIN}
       @@ -36,5 +36,5 @@ install:
        
        dist: clean
                mkdir -p ${NAME}-${VERSION}
       -        cp -r README Makefile bin ${SRC} ${NAME}-${VERSION}
       +        cp -r README Makefile bin ${MAN1} ${MAN5} ${SRC} ${NAME}-${VERSION}
                tar -cf - ${NAME}-${VERSION} | gzip -c >${NAME}-${VERSION}.tar.gz
 (DIR) diff --git a/ics2tsv.1 b/ics2tsv.1
       @@ -0,0 +1,150 @@
       +.Dd $Mdocdate: Mar 1 2020$
       +.Dt ICS2TSV 1
       +.Os
       +.
       +.
       +.Sh NAME
       +.
       +.Nm ics2tsv
       +.Nd convert an icalendar.ics file to tsv
       +.
       +.
       +.Sh SYNOPSIS
       +.
       +.Nm ics2tsv
       +.Op Fl 1
       +.Op Fl f Ar fields
       +.Op Fl s Ar subsep
       +.Op Fl t Ar timefmt
       +.Ar [file.ics...] >file.tsv
       +.
       +.Sh DESCRIPTION
       +.
       +.Nm
       +is a converter that parse icalendar format and produces lines of output.
       +Every line represents an element delimited by
       +.Dq BEGIN:
       +and
       +.Dq  END:
       +among
       +.Dq VEVENT ,
       +.Dq VTODO ,
       +.Dq VJOURNAL ,
       +.Dq VFREEBUSY ,
       +and
       +.Dq VALARM .
       +.
       +.Pp
       +The lines are filled with tab-delimited fields, with the first ones:
       +.
       +.Bl -enum
       +.
       +.It
       +Element type, as encountered after
       +.Dq BEGIN
       +and
       +.Dq END ;
       +.
       +.It
       +Start date, present for
       +.Dq VEVENT ,
       +.Dq VJOURNAL ,
       +.Dq VFREEBUSY ,
       +and
       +.Dq VALARM
       +types.
       +.
       +.It
       +End date, present for
       +.Dq VEVENT ,
       +.Dq VTODO ,
       +.Dq VFREEBUSY ,
       +and
       +.Dq VALARM
       +types.
       +.
       +.It
       +Reserved for future use.
       +.
       +.El
       +.
       +.Pp
       +And the other fields starting from
       +.Pq 5.
       +chosen by the
       +.Fl f
       +flag.
       +By default:
       +.Dq "CATEGORIES,LOCATION,SUMMARY,DESCRIPTION" .
       +.
       +.Bl -tag
       +.
       +.It Fl 1
       +Show the name of the columns on the first line before the content.
       +.
       +.It Fl f Ar field1,field2,field3...
       +Chooses the fields from the icalendar to display, in this order,
       +separated by a comma
       +.Pq Sq \&,
       +and case-insensitive
       +.
       +.It Fl s Ar subsep
       +When there are multiple fields with the same value, they are
       +concatenated with
       +.Ar subsep
       +separator, by default a comma
       +.Pq Sq \&, .
       +.
       +.It Fl t Ar timefmt
       +Dates from 2nd and 3rd fields are formatted with a
       +.Xr strftime 3
       +string
       +.Ar timeftm ,
       +by default in seconds since 1970/01/01.
       +.
       +.El
       +.
       +.
       +.Sh EXAMPLES
       +.
       +Convert a calendar from HTTP
       +.Pa .ics
       +to custom
       +.Pa .txt
       +sorted by start date:
       +.Dl curl "$url.ics" | ics2tsv | sort -n -k 1,1 | tsv2tsv
       +.
       +.Pp
       +.
       +.Pp
       +Split an
       +.ics
       +file according to the category, saved as
       +.Pa .tsv :
       +.Bd -literal
       +ics2tsv -f CATEGORIES icalendar.ics | awk -F '\et' '{ print >>($6".tsv") }\'
       +.Ed
       +.
       +.
       +.Sh SEE ALSO
       +.
       +.Xr awk 1 ,
       +.Xr cal 1 ,
       +.Xr calendar 1 ,
       +.Xr date 1 ,
       +.Xr sort 1
       +.
       +.
       +.Sh STANDARDS
       +.
       +.Rs
       +.%A Desruisseaux
       +.%D September 2009
       +.%T Internet Calendaring and Scheduling Core Object Specification (iCalendar)
       +.%R RFC 5545
       +.Re
       +.
       +.
       +.Sh AUTHORS
       +.
       +.An Josuah Demangeon Aq Mt me@josuah.net
 (DIR) diff --git a/ics2tsv.c b/ics2tsv.c
       @@ -24,6 +24,7 @@ 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;
       @@ -46,6 +47,9 @@ fn_block_begin(IcalParser *p, char *name)
                (void)p;
                (void)name;
        
       +        if (p->blocktype == ICAL_BLOCK_OTHER)
       +                return 0;
       +
                memset(&block, 0, sizeof block);
                return 0;
        }
       @@ -72,6 +76,9 @@ fn_block_end(IcalParser *p, char *name)
                        printf("\t%s", buf);
                }
        
       +        /* reserved for recurring events */
       +        printf("\t%s", "(null)");
       +
                for (int i = 0; fields[i] != NULL; i++) {
                        fputc('\t', stdout);
                        if (block.fields[i] != NULL)
       @@ -134,7 +141,8 @@ fn_field_value(IcalParser *p, char *name, char *value)
        static void
        usage(void)
        {
       -        fprintf(stderr, "usage: %s [-f fields] [-s subsep] [-t timefmt] [file...]", arg0);
       +        fprintf(stderr,"usage: %s [-1] [-f fields] [-s subsep] [-t timefmt]"
       +            " [file...]\n", arg0);
                exit(1);
        }
        
       @@ -153,8 +161,11 @@ main(int argc, char **argv)
                p.fn_param_value = fn_param_value;
                p.fn_field_value = fn_field_value;
        
       -        while ((c = getopt(argc, argv, "f:s:t:")) != -1) {
       +        while ((c = getopt(argc, argv, "1f:s:t:")) != -1) {
                        switch (c) {
       +                case '1':
       +                        flag_1 = 1;
       +                        break;
                        case 'f':
                                flag_f = optarg;
                                break;
       @@ -179,6 +190,13 @@ main(int argc, char **argv)
                } while ((fields[i++] = strsep(&flag_f, ",")) != NULL);
                fields[i] = NULL;
        
       +        if (flag_1) {
       +                printf("%s\t%s\t%s", "TYPE", "BEG", "END");
       +                for (i = 0; fields[i] != NULL; i++)
       +                        printf("\t%s", fields[i]);
       +                fputc('\n', stdout);
       +        }
       +
                if (*argv == NULL || strcmp(*argv, "-") == 0) {
                        debug("converting *stdin*");
                        if (ical_parse(&p, stdin) < 0)