sfeed_ass.c - randomcrap - random crap programs of varying quality
 (HTM) git clone git://git.codemadness.org/randomcrap
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       sfeed_ass.c (5099B)
       ---
            1 /* Actually Simple Syndication specification: https://tilde.town/~dzwdz/ass/ */
            2 #include <stdio.h>
            3 #include <string.h>
            4 #include <stdlib.h>
            5 
            6 enum { FieldTime, FieldLink, FieldTitle, FieldLast }; /* ass fields */
            7 
            8 /* ctype-like macros, but always compatible with ASCII / UTF-8 */
            9 #define ISCNTRL(c) ((c) < ' ' || (c) == 0x7f)
           10 #define ISDIGIT(c) (((unsigned)c) - '0' < 10)
           11 #define ISSPACE(c) ((c) == ' ' || ((((unsigned)c) - '\t') < 5))
           12 
           13 /* Convert time fields. Returns a UNIX timestamp. */
           14 long long
           15 datetounix(long long year, int mon, int day, int hour, int min, int sec)
           16 {
           17         static const long secs_through_month[] = {
           18                 0, 31 * 86400, 59 * 86400, 90 * 86400,
           19                 120 * 86400, 151 * 86400, 181 * 86400, 212 * 86400,
           20                 243 * 86400, 273 * 86400, 304 * 86400, 334 * 86400 };
           21         int is_leap = 0, cycles, centuries = 0, leaps = 0, rem;
           22         long long t;
           23 
           24         if (year - 2ULL <= 136) {
           25                 leaps = (year - 68) >> 2;
           26                 if (!((year - 68) & 3)) {
           27                         leaps--;
           28                         is_leap = 1;
           29                 } else {
           30                         is_leap = 0;
           31                 }
           32                 t = 31536000 * (year - 70) + 86400 * leaps;
           33         } else {
           34                 cycles = (year - 100) / 400;
           35                 rem = (year - 100) % 400;
           36                 if (rem < 0) {
           37                         cycles--;
           38                         rem += 400;
           39                 }
           40                 if (!rem) {
           41                         is_leap = 1;
           42                 } else {
           43                         if (rem >= 300)
           44                                 centuries = 3, rem -= 300;
           45                         else if (rem >= 200)
           46                                 centuries = 2, rem -= 200;
           47                         else if (rem >= 100)
           48                                 centuries = 1, rem -= 100;
           49                         if (rem) {
           50                                 leaps = rem / 4U;
           51                                 rem %= 4U;
           52                                 is_leap = !rem;
           53                         }
           54                 }
           55                 leaps += 97 * cycles + 24 * centuries - is_leap;
           56                 t = (year - 100) * 31536000LL + leaps * 86400LL + 946684800 + 86400;
           57         }
           58         t += secs_through_month[mon];
           59         if (is_leap && mon >= 2)
           60                 t += 86400;
           61         return t + (86400LL * (day - 1)) + (3600LL * hour) + (60LL * min) + sec;
           62 }
           63 
           64 /* Get timezone from string, return time offset in seconds from UTC */
           65 long
           66 gettzoffset(const char *s)
           67 {
           68         const char *p;
           69         long tzhour = 0, tzmin = 0;
           70         size_t i;
           71 
           72         for (; ISSPACE((unsigned char)*s); s++)
           73                 ;
           74         if (*s != '-' && *s != '+')
           75                 return 0;
           76 
           77         for (i = 0, p = s + 1; i < 2 && ISDIGIT((unsigned char)*p); i++, p++)
           78                 tzhour = (tzhour * 10) + (*p - '0');
           79         if (*p == ':')
           80                 p++;
           81         for (i = 0; i < 2 && ISDIGIT((unsigned char)*p); i++, p++)
           82                 tzmin = (tzmin * 10) + (*p - '0');
           83         return ((tzhour * 3600) + (tzmin * 60)) * (s[0] == '-' ? -1 : 1);
           84 }
           85 
           86 /* Parse time string `s` into the UNIX timestamp `tp`.
           87    Returns 0 on success or -1 on failure. */
           88 int
           89 parsetime(const char *s, long long *tp)
           90 {
           91         int va[6] = { 0 }, i, v, vi = 0;
           92 
           93         for (; ISSPACE((unsigned char)*s); s++)
           94                 ;
           95 
           96         if (!ISDIGIT((unsigned char)s[0]) ||
           97             !ISDIGIT((unsigned char)s[1]) ||
           98             !ISDIGIT((unsigned char)s[2]) ||
           99             !ISDIGIT((unsigned char)s[3])) {
          100                 /* formats "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S" or "%Y%m%d%H%M%S" */
          101                 return -1;
          102         }
          103 
          104         /* parse time parts (and possibly remaining date parts) */
          105         for (; *s && vi < 6; vi++) {
          106                 for (i = 0, v = 0; i < ((vi == 0) ? 4 : 2) &&
          107                                    ISDIGIT((unsigned char)*s); s++, i++) {
          108                         v = (v * 10) + (*s - '0');
          109                 }
          110                 va[vi] = v;
          111 
          112                 if ((vi < 2 && *s == '-') ||
          113                     (vi == 2 && (*s == 'T' || ISSPACE((unsigned char)*s))) ||
          114                     (vi > 2 && *s == ':'))
          115                         s++;
          116         }
          117 
          118         /* skip milliseconds in for example: "%Y-%m-%dT%H:%M:%S.000Z" */
          119         if (*s == '.') {
          120                 for (s++; ISDIGIT((unsigned char)*s); s++)
          121                         ;
          122         }
          123 
          124         /* invalid range */
          125         if (va[0] < 0 || va[0] > 9999 ||
          126             va[1] < 1 || va[1] > 12 ||
          127             va[2] < 1 || va[2] > 31 ||
          128             va[3] < 0 || va[3] > 23 ||
          129             va[4] < 0 || va[4] > 59 ||
          130             va[5] < 0 || va[5] > 60) /* allow leap second */
          131                 return -1;
          132 
          133         *tp = datetounix(va[0] - 1900, va[1] - 1, va[2], va[3], va[4], va[5]) -
          134               gettzoffset(s);
          135 
          136         return 0;
          137 }
          138 
          139 /* Splits fields in the line buffer by replacing TAB separators with NUL ('\0')
          140  * terminators and assign these fields as pointers. If there are less fields
          141  * than expected then the field is an empty string constant. */
          142 void
          143 parseline(char *line, char *fields[FieldLast])
          144 {
          145         char *prev, *s;
          146         size_t i;
          147 
          148         for (prev = line, i = 0;
          149             (s = strchr(prev, '\t')) && i < FieldLast - 1;
          150             i++) {
          151                 *s = '\0';
          152                 fields[i] = prev;
          153                 prev = s + 1;
          154         }
          155         fields[i++] = prev;
          156         for (; i < FieldLast; i++)
          157                 fields[i] = "";
          158 }
          159 
          160 /* print sfeed(5) field (non-content), it may not contain a TAB or newline */
          161 void
          162 printfield(const char *s)
          163 {
          164         for (; *s; s++)
          165                 if (!ISCNTRL((unsigned char)*s))
          166                         putchar(*s);
          167 }
          168 
          169 int
          170 main(void)
          171 {
          172         char *fields[FieldLast], *line = NULL;
          173         size_t linesiz = 0;
          174         ssize_t linelen;
          175         long long t;
          176 
          177         while ((linelen = getline(&line, &linesiz, stdin)) > 0 &&
          178                !ferror(stdout)) {
          179                 if (line[linelen - 1] == '\n')
          180                         line[--linelen] = '\0';
          181                 if (line[0] == '#' || line[0] == '\0' || linelen == 0)
          182                         continue; /* skip comments or empty lines */
          183 
          184                 /* parse a TAB-separated line into fields */
          185                 parseline(line, fields);
          186 
          187                 /* parse and print a valid time */
          188                 if (parsetime(fields[FieldTime], &t) != -1)
          189                         printf("%lld", t);
          190                 putchar('\t');
          191                 printfield(fields[FieldTitle]);
          192                 putchar('\t');
          193                 printfield(fields[FieldLink]);
          194                 putchar('\n');
          195         }
          196 
          197         /* error on reading input or writing output: exit code is non-zero */
          198         return ferror(stdin) || fflush(stdout) || ferror(stdout);
          199 }