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