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 }