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 }