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