URI: 
       tsv2agenda.c - 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
       ---
       tsv2agenda.c (5483B)
       ---
            1 #include <assert.h>
            2 #include <ctype.h>
            3 #include <errno.h>
            4 #include <stdio.h>
            5 #include <stdlib.h>
            6 #include <stdint.h>
            7 #include <string.h>
            8 #include <unistd.h>
            9 #include <time.h>
           10 #include "util.h"
           11 
           12 #ifndef __OpenBSD__
           13 #define pledge(...) 0
           14 #endif
           15 
           16 enum {
           17         FIELD_TYPE,
           18         FIELD_BEG,
           19         FIELD_END,
           20         FIELD_RECUR,
           21         FIELD_OTHER,
           22         FIELD_MAX = 128,
           23 };
           24 
           25 typedef struct {
           26         struct tm beg, end;
           27         char *fieldnames[FIELD_MAX];
           28         size_t fieldnum;
           29         size_t linenum;
           30 } AgendaCtx;
           31 
           32 static time_t flag_from = INT64_MIN;
           33 static time_t flag_to = INT64_MAX;
           34 
           35 static void
           36 print_date(struct tm *tm)
           37 {
           38          if (tm == NULL) {
           39                 fprintf(stdout, "%11s", "");
           40         } else {
           41                 char buf[128];
           42                 if (strftime(buf, sizeof buf, "%Y-%m-%d", tm) == 0)
           43                         err(1, "strftime: %s", strerror(errno));
           44                 fprintf(stdout, "%s ", buf);
           45         }
           46 }
           47 
           48 static void
           49 print_time(struct tm *tm)
           50 {
           51         if (tm == NULL) {
           52                 fprintf(stdout, "%5s ", "");
           53         } else {
           54                 char buf[128];
           55                 if (strftime(buf, sizeof buf, "%H:%M", tm) == 0)
           56                         err(1, "strftime: %s", strerror(errno));
           57                 fprintf(stdout, "%5s ", buf);
           58         }
           59 }
           60 
           61 static void
           62 print_header0(struct tm *old, struct tm *new)
           63 {
           64         int same;
           65 
           66         same = (old->tm_year == new->tm_year && old->tm_mon == new->tm_mon &&
           67             old->tm_mday == new->tm_mday);
           68         print_date(same ? NULL : new);
           69         print_time(new);
           70 }
           71 
           72 static void
           73 print_header1(struct tm *beg, struct tm *end)
           74 {
           75         int same;
           76 
           77         same = (beg->tm_year == end->tm_year && beg->tm_mon == end->tm_mon &&
           78             beg->tm_mday == end->tm_mday);
           79         print_date(same ? NULL : end);
           80 
           81         same = (beg->tm_hour == end->tm_hour && beg->tm_min == end->tm_min);
           82         print_time(same ? NULL : end);
           83 }
           84 
           85 static void
           86 print_headerN(void)
           87 {
           88         print_date(NULL);
           89         print_time(NULL);
           90 }
           91 
           92 static void
           93 print_header(AgendaCtx *ctx, struct tm *beg, struct tm *end, size_t *num)
           94 {
           95         switch ((*num)++) {
           96         case 0:
           97                 print_header0(&ctx->beg, beg);
           98                 break;
           99         case 1:
          100                 print_header1(beg, end);
          101                 break;
          102         default:
          103                 print_headerN();
          104                 break;
          105         }
          106 }
          107 
          108 static void
          109 unescape(char const *s, char *d)
          110 {
          111         for (; *s != '\0'; s++) {
          112                 if (*s == '\\') {
          113                         s++;
          114                         *d++ = (*s == 'n') ? '\n' : (*s == 't') ? ' ' : *s;
          115                 } else {
          116                         if (*s == '\\')
          117                                 debug("s='%c'", *s);
          118                         *d++ = *s;
          119                 }
          120         }
          121         *d = '\0';
          122 }
          123 
          124 static void
          125 print_row(AgendaCtx *ctx, char *s, struct tm *beg, struct tm *end, size_t *num)
          126 {
          127         unescape(s, s);
          128 
          129         print_header(ctx, beg, end, num);
          130         for (size_t i, n = 0; *s != '\0'; s++) {
          131                 switch (*s) {
          132                 case '\n':
          133 newline:
          134                         fputc('\n', stdout);
          135                         print_header(ctx, beg, end, num);
          136                         fputs(": ", stdout);
          137                         n = 0;
          138                         break;
          139                 case ' ':
          140                 case '\t':
          141                         i = strcspn(s + 1, " \t\n");
          142                         if (n + i > 70)
          143                                 goto newline;
          144                         fputc(' ', stdout);
          145                         n++;
          146                         break;
          147                 default:
          148                         fputc(*s, stdout);
          149                         n++;
          150                 }
          151         }
          152         fputc('\n', stdout);
          153 }
          154 
          155 static void
          156 print(AgendaCtx *ctx, char **fields)
          157 {
          158         struct tm beg = {0}, end = {0};
          159         time_t t;
          160         char const *e;
          161 
          162         t = strtonum(fields[FIELD_BEG], INT64_MIN, INT64_MAX, &e);
          163         if (e != NULL)
          164                 err(1, "start time %s is %s", fields[FIELD_BEG], e);
          165         if (t > flag_to)
          166                 return;
          167         localtime_r(&t, &beg);
          168 
          169         t = strtonum(fields[FIELD_END], INT64_MIN, INT64_MAX, &e);
          170         if (e != NULL)
          171                 err(1, "end time %s is %s", fields[FIELD_END], e);
          172         if (t < flag_from)
          173                 return;
          174         localtime_r(&t, &end);
          175 
          176         fputc('\n', stdout);
          177         for (size_t i = FIELD_OTHER, row = 0; i < ctx->fieldnum; i++) {
          178                 if (fields[i][strspn(fields[i], " \\n")] == '\0')
          179                         continue;
          180                 print_row(ctx, fields[i], &beg, &end, &row);
          181         }
          182 
          183         ctx->beg = beg;
          184         ctx->end = end;
          185 }
          186 
          187 static void
          188 tsv2agenda(FILE *fp)
          189 {
          190         AgendaCtx ctx = {0};
          191         char *line = NULL;
          192         size_t sz1 = 0, sz2 = 0;
          193 
          194         if (ctx.linenum == 0) {
          195                 char *fields[FIELD_MAX];
          196 
          197                 ctx.linenum++;
          198                 getline(&line, &sz1, fp);
          199                 if (ferror(fp))
          200                         err(1, "reading stdin: %s", strerror(errno));
          201                 if (feof(fp))
          202                         err(1, "empty input");
          203                 strchomp(line);
          204                 ctx.fieldnum = strsplit(line, fields, FIELD_MAX, "\t");
          205                 if (ctx.fieldnum == FIELD_MAX)
          206                         err(1, "line 1: too many fields");
          207                 if (ctx.fieldnum < FIELD_OTHER)
          208                         err(1, "line 1: not enough input columns");
          209                 if (strcasecmp(fields[0], "TYPE") != 0)
          210                         err(1, "line 1: 1st column is not \"TYPE\"");
          211                 if (strcasecmp(fields[1], "START") != 0)
          212                         err(1, "line 1: 2nd column is not \"START\"");
          213                 if (strcasecmp(fields[2], "END") != 0)
          214                         err(1, "line 1: 3rd column is not \"END\"");
          215                 if (strcasecmp(fields[3], "RECUR") != 0)
          216                         err(1, "line 1: 4th column is not \"RECUR\"");
          217 
          218                 free(line);
          219                 line = NULL;
          220         }
          221 
          222         for (;;) {
          223                 char *fields[FIELD_MAX];
          224 
          225                 ctx.linenum++;
          226                 getline(&line, &sz2, fp);
          227                 if (ferror(fp))
          228                         err(1, "reading stdin: %s", strerror(errno));
          229                 if (feof(fp))
          230                         break;
          231 
          232                 strchomp(line);
          233 
          234                 if (strsplit(line, fields, FIELD_MAX, "\t") != ctx.fieldnum)
          235                         err(1, "line %zd: bad number of columns",
          236                             ctx.linenum, strerror(errno));
          237 
          238                 print(&ctx, fields);
          239         }
          240         fputc('\n', stdout);
          241 
          242         free(line);
          243         line = NULL;
          244 }
          245 
          246 static void
          247 usage(void)
          248 {
          249         fprintf(stderr, "usage: %s [-f fromdate] [-t todate]\n", arg0);
          250         exit(1);
          251 }
          252 
          253 int
          254 main(int argc, char **argv)
          255 {
          256         char c;
          257 
          258         if (pledge("stdio", "") < 0)
          259                 err(1, "pledge: %s", strerror(errno));
          260 
          261         arg0 = *argv;
          262         while ((c = getopt(argc, argv, "f:t:")) > 0) {
          263                 char const *e;
          264 
          265                 switch (c) {
          266                 case 'f':
          267                         flag_from = strtonum(optarg, INT64_MIN, INT64_MAX, &e);
          268                         if (e != NULL)
          269                                 err(1, "fromdate value %s is %s", optarg, e);
          270                         break;
          271                 case 't':
          272                         flag_to = strtonum(optarg, INT64_MIN, INT64_MAX, &e);
          273                         if (e != NULL)
          274                                 err(1, "todate value %s is %s", optarg, e);
          275                         break;
          276                 default:
          277                         usage();
          278                 }
          279         }
          280         argc -= optind;
          281         argv += optind;
          282 
          283         tsv2agenda(stdin);
          284         return 0;
          285 }