cal.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       cal.c (5009B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <limits.h>
            3 #include <stdint.h>
            4 #include <stdio.h>
            5 #include <stdlib.h>
            6 #include <string.h>
            7 #include <time.h>
            8 #include <unistd.h>
            9 
           10 #include "util.h"
           11 
           12 enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
           13 enum caltype { JULIAN, GREGORIAN };
           14 enum { TRANS_YEAR = 1752, TRANS_MONTH = SEP, TRANS_DAY = 2 };
           15 
           16 static struct tm *ltime;
           17 
           18 static int
           19 isleap(size_t year, enum caltype cal)
           20 {
           21         if (cal == GREGORIAN) {
           22                 if (year % 400 == 0)
           23                         return 1;
           24                 if (year % 100 == 0)
           25                         return 0;
           26                 return (year % 4 == 0);
           27         }
           28         else { /* cal == Julian */
           29                 return (year % 4 == 0);
           30         }
           31 }
           32 
           33 static int
           34 monthlength(size_t year, int month, enum caltype cal)
           35 {
           36         int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
           37 
           38         return (month == FEB && isleap(year, cal)) ? 29 : mdays[month];
           39 }
           40 
           41 /* From http://www.tondering.dk/claus/cal/chrweek.php#calcdow */
           42 static int
           43 dayofweek(size_t year, int month, int dom, enum caltype cal)
           44 {
           45         size_t y;
           46         int m, a;
           47 
           48         a = (13 - month) / 12;
           49         y = year - a;
           50         m = month + 12 * a - 1;
           51 
           52         if (cal == GREGORIAN)
           53                 return (dom + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) % 7;
           54         else  /* cal == Julian */
           55                 return (5 + dom + y + y / 4 + (31 * m) / 12) % 7;
           56 }
           57 
           58 static void
           59 printgrid(size_t year, int month, int fday, int line)
           60 {
           61         enum caltype cal;
           62         int offset, dom, d = 0, trans; /* are we in the transition from Julian to Gregorian? */
           63         int today = 0;
           64 
           65         cal = (year < TRANS_YEAR || (year == TRANS_YEAR && month <= TRANS_MONTH)) ? JULIAN : GREGORIAN;
           66         trans = (year == TRANS_YEAR && month == TRANS_MONTH);
           67         offset = dayofweek(year, month, 1, cal) - fday;
           68 
           69         if (offset < 0)
           70                 offset += 7;
           71         if (line == 1) {
           72                 for (; d < offset; ++d)
           73                         printf("   ");
           74                 dom = 1;
           75         } else {
           76                 dom = 8 - offset + (line - 2) * 7;
           77                 if (trans && !(line == 2 && fday == 3))
           78                         dom += 11;
           79         }
           80         if (ltime && year == ltime->tm_year + 1900 && month == ltime->tm_mon)
           81                 today = ltime->tm_mday;
           82         for (; d < 7 && dom <= monthlength(year, month, cal); ++d, ++dom) {
           83                 if (dom == today)
           84                         printf("\x1b[7m%2d\x1b[0m ", dom); /* highlight today's date */
           85                 else
           86                         printf("%2d ", dom);
           87                 if (trans && dom == TRANS_DAY)
           88                         dom += 11;
           89         }
           90         for (; d < 7; ++d)
           91                 printf("   ");
           92 }
           93 
           94 static void
           95 drawcal(size_t year, int month, size_t ncols, size_t nmons, int fday)
           96 {
           97         char *smon[] = { "January", "February", "March", "April",
           98                          "May", "June", "July", "August",
           99                          "September", "October", "November", "December" };
          100         char *days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", };
          101         size_t m, n, col, cur_year, cur_month, dow;
          102         int line, pad;
          103         char month_year[sizeof("Su Mo Tu We Th Fr Sa")];
          104 
          105         for (m = 0; m < nmons; ) {
          106                 n = m;
          107                 for (col = 0; m < nmons && col < ncols; ++col, ++m) {
          108                         cur_year = year + m / 12;
          109                         cur_month = month + m % 12;
          110                         if (cur_month > 11) {
          111                                 cur_month -= 12;
          112                                 cur_year += 1;
          113                         }
          114                         snprintf(month_year, sizeof(month_year), "%s %zu", smon[cur_month], cur_year);
          115                         pad = sizeof(month_year) - 1 - strlen(month_year);
          116                         printf("%*s%s%*s   ", pad / 2 + pad % 2, "", month_year, pad / 2, "");
          117                 }
          118                 putchar('\n');
          119                 for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
          120                         for (dow = fday; dow < (fday + 7); ++dow)
          121                                 printf("%s ", days[dow % 7]);
          122                         printf("  ");
          123                 }
          124                 putchar('\n');
          125                 for (line = 1; line <= 6; ++line) {
          126                         for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
          127                                 cur_year = year + m / 12;
          128                                 cur_month = month + m % 12;
          129                                 if (cur_month > 11) {
          130                                         cur_month -= 12;
          131                                         cur_year += 1;
          132                                 }
          133                                 printgrid(cur_year, cur_month, fday, line);
          134                                 printf("  ");
          135                         }
          136                         putchar('\n');
          137                 }
          138         }
          139 }
          140 
          141 static void
          142 usage(void)
          143 {
          144         eprintf("usage: %s [-1 | -3 | -y | -n num] "
          145                 "[-s | -m | -f num] [-c num] [[month] year]\n", argv0);
          146 }
          147 
          148 int
          149 main(int argc, char *argv[])
          150 {
          151         time_t now;
          152         size_t year, ncols, nmons;
          153         int fday, month;
          154 
          155         now   = time(NULL);
          156         ltime = localtime(&now);
          157         year  = ltime->tm_year + 1900;
          158         month = ltime->tm_mon + 1;
          159         fday  = 0;
          160 
          161         if (!isatty(STDOUT_FILENO))
          162                 ltime = NULL; /* don't highlight today's date */
          163 
          164         ncols = 3;
          165         nmons = 0;
          166 
          167         ARGBEGIN {
          168         case '1':
          169                 nmons = 1;
          170                 break;
          171         case '3':
          172                 nmons = 3;
          173                 if (--month == 0) {
          174                         month = 12;
          175                         year--;
          176                 }
          177                 break;
          178         case 'c':
          179                 ncols = estrtonum(EARGF(usage()), 0, MIN(SIZE_MAX, LLONG_MAX));
          180                 break;
          181         case 'f':
          182                 fday = estrtonum(EARGF(usage()), 0, 6);
          183                 break;
          184         case 'm': /* Monday */
          185                 fday = 1;
          186                 break;
          187         case 'n':
          188                 nmons = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
          189                 break;
          190         case 's': /* Sunday */
          191                 fday = 0;
          192                 break;
          193         case 'y':
          194                 month = 1;
          195                 nmons = 12;
          196                 break;
          197         default:
          198                 usage();
          199         } ARGEND
          200 
          201         if (nmons == 0) {
          202                 if (argc == 1) {
          203                         month = 1;
          204                         nmons = 12;
          205                 } else {
          206                         nmons = 1;
          207                 }
          208         }
          209 
          210         switch (argc) {
          211         case 2:
          212                 month = estrtonum(argv[0], 1, 12);
          213                 argv++;
          214         case 1: /* fallthrough */
          215                 year = estrtonum(argv[0], 0, INT_MAX);
          216                 break;
          217         case 0:
          218                 break;
          219         default:
          220                 usage();
          221         }
          222 
          223         drawcal(year, month - 1, ncols, nmons, fday);
          224 
          225         return fshut(stdout, "<stdout>");
          226 }