1 /* cal.c - show calendar.
2 *
3 * Copyright 2011 Rob Landley <[email protected]>
4 *
5 * See http://opengroup.org/onlinepubs/9699919799/utilities/cal.html
6
7 USE_CAL(NEWTOY(cal, ">3h", TOYFLAG_USR|TOYFLAG_BIN))
8
9 config CAL
10 bool "cal"
11 default y
12 help
13 usage: cal [-h] [[[DAY] MONTH] YEAR]
14
15 Print a calendar.
16
17 With one argument, prints all months of the specified year.
18 With two arguments, prints calendar for month and year.
19 With three arguments, highlights day within month and year.
20
21 -h Don't highlight today
22 */
23
24 #define FOR_cal
25 #include "toys.h"
26
GLOBALS(struct tm * now;)27 GLOBALS(
28 struct tm *now;
29 )
30
31 // Thirty days hath september april june and november. February is just weird.
32 static int monthlen(struct tm *tm)
33 {
34 int len = 31, month = tm->tm_mon, year = tm->tm_year;
35
36 if (tm->tm_mon==1) {
37 len = 28;
38 if (!(year&3) && !((year%100) && !(year%400))) len++;
39 } else if ((month+(month>6))&1) len = 30;
40
41 return len;
42 }
43
44 // Write calendar into buffer: each line is 20 chars wide, end indicated
45 // by empty string.
46
calstrings(char * buf,struct tm * tm)47 static char *calstrings(char *buf, struct tm *tm)
48 {
49 int wday, mday, start, len, line;
50 char temp[21];
51
52 // header
53 len = strftime(temp, 21, "%B %Y", tm);
54 len += (20-len)/2;
55 buf += sprintf(buf, "%*s%*s ", len, temp, 20-len, "")+1;
56 buf += sprintf(buf, "Su Mo Tu We Th Fr Sa ")+1;
57
58 // What day of the week does this month start on?
59 if (tm->tm_mday>1) start = (36+tm->tm_wday-tm->tm_mday)%7;
60 else start = tm->tm_wday;
61
62 // What day does this month end on? Alas, libc doesn't tell us...
63 len = monthlen(tm);
64
65 for (mday = line = 0; line<6; line++) {
66 for (wday=0; wday<7; wday++) {
67 char *pat = " ";
68 if (!mday ? wday==start : mday<len) {
69 pat = "%2d ";
70 if (!FLAG(h) && tm->tm_year == TT.now->tm_year &&
71 tm->tm_mon == TT.now->tm_mon && mday == TT.now->tm_mday-1) {
72 pat = "\x1b[7m%2d\x1b[m ";
73 }
74 mday++;
75 }
76 buf += sprintf(buf, pat, mday);
77 }
78 buf++;
79 }
80
81 return buf;
82 }
83
84 // Worst case scenario toybuf usage: sizeof(struct tm) plus 21 bytes/line
85 // plus 8 lines/month plus 12 months, plus the escape sequences to highlight
86 // today comes to a bit over 2k of our 4k buffer.
87
cal_main(void)88 void cal_main(void)
89 {
90 time_t now = time(0);
91 struct tm *tm = localtime(&now);
92 char *buf = toybuf;
93
94 TT.now = tm;
95 if (!isatty(1)) toys.optflags |= FLAG_h;
96
97 if (toys.optc) {
98 // Conveniently starts zeroed
99 tm = (struct tm *)toybuf;
100 buf += sizeof(struct tm);
101
102 // Last argument is year, one before that (if any) is month.
103 tm->tm_year = atolx_range(toys.optargs[--toys.optc], 1, 9999) - 1900;
104 tm->tm_mday = 1;
105 tm->tm_hour = 12; // noon to avoid timezone weirdness
106 if (toys.optc) {
107 tm->tm_mon = atolx_range(toys.optargs[--toys.optc], 1, 12)-1;
108 if (toys.optc) {
109 tm->tm_mday = atolx_range(toys.optargs[--toys.optc], 1, monthlen(tm));
110 TT.now = tm;
111 }
112
113 // Print 12 months of the year
114
115 } else {
116 char *bufs[12];
117 int i, j, k;
118
119 for (i=0; i<12; i++) {
120 tm->tm_mon=i;
121 mktime(tm);
122 buf = calstrings(bufs[i]=buf, tm);
123 }
124
125 // 4 rows, 6 lines each, 3 columns
126 for (i=0; i<4; i++) {
127 for (j=0; j<8; j++) {
128 for(k=0; k<3; k++) {
129 char **b = bufs+(k+i*3);
130 *b += printf("%s ", *b);
131 }
132 puts("");
133 }
134 }
135 return;
136 }
137
138 // What day of the week does that start on?
139 mktime(tm);
140 }
141
142 calstrings(buf, tm);
143 while (*buf) buf += printf("%s\n", buf);
144 }
145