1 /* cal.c - show calendar.
2 *
3 * Copyright 2011 Rob Landley <rob@landley.net>
4 *
5 * See http://opengroup.org/onlinepubs/9699919799/utilities/cal.html
6
7 USE_CAL(NEWTOY(cal, ">2", TOYFLAG_USR|TOYFLAG_BIN))
8
9 config CAL
10 bool "cal"
11 default y
12 help
13 usage: cal [[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 */
20
21 #include "toys.h"
22
23 // Write calendar into buffer: each line is 20 chars wide, end indicated
24 // by empty string.
25
calstrings(char * buf,struct tm * tm)26 static char *calstrings(char *buf, struct tm *tm)
27 {
28 char temp[21];
29 int wday, mday, start, len, line;
30
31 // header
32 len = strftime(temp, 21, "%B %Y", tm);
33 len += (20-len)/2;
34 buf += sprintf(buf, "%*s%*s ", len, temp, 20-len, "");
35 buf++;
36 buf += sprintf(buf, "Su Mo Tu We Th Fr Sa ");
37 buf++;
38
39 // What day of the week does this month start on?
40 if (tm->tm_mday>1)
41 start = (36+tm->tm_wday-tm->tm_mday)%7;
42 else start = tm->tm_wday;
43
44 // What day does this month end on? Alas, libc doesn't tell us...
45 len = 31;
46 if (tm->tm_mon == 1) {
47 int year = tm->tm_year;
48 len = 28;
49 if (!(year & 3) && !((year&100) && !(year&400))) len++;
50 } else if ((tm->tm_mon+(tm->tm_mon>6 ? 1 : 0)) & 1) len = 30;
51
52 for (mday=line=0;line<6;line++) {
53 for (wday=0; wday<7; wday++) {
54 char *pat = " ";
55 if (!mday ? wday==start : mday<len) {
56 pat = "%2d ";
57 mday++;
58 }
59 buf += sprintf(buf, pat, mday);
60 }
61 buf++;
62 }
63
64 return buf;
65 }
66
xcheckrange(long val,long low,long high)67 void xcheckrange(long val, long low, long high)
68 {
69 char *err = "%ld %s than %ld";
70
71 if (val < low) error_exit(err, val, "less", low);
72 if (val > high) error_exit(err, val, "greater", high);
73 }
74
75 // Worst case scenario toybuf usage: sizeof(struct tm) plus 21 bytes/line
76 // plus 8 lines/month plus 12 months, comes to a bit over 2k of our 4k buffer.
77
cal_main(void)78 void cal_main(void)
79 {
80 struct tm *tm;
81 char *buf = toybuf;
82
83 if (toys.optc) {
84 // Conveniently starts zeroed
85 tm = (struct tm *)toybuf;
86 buf += sizeof(struct tm);
87
88 // Last argument is year, one before that (if any) is month.
89 xcheckrange(tm->tm_year = atol(toys.optargs[--toys.optc]),1,9999);
90 tm->tm_year -= 1900;
91 tm->tm_mday = 1;
92 tm->tm_hour = 12; // noon to avoid timezone weirdness
93 if (toys.optc) {
94 xcheckrange(tm->tm_mon = atol(toys.optargs[--toys.optc]),1,12);
95 tm->tm_mon--;
96
97 // Print 12 months of the year
98
99 } else {
100 char *bufs[12];
101 int i, j, k;
102
103 for (i=0; i<12; i++) {
104 tm->tm_mon=i;
105 mktime(tm);
106 buf = calstrings(bufs[i]=buf, tm);
107 }
108
109 // 4 rows, 6 lines each, 3 columns
110 for (i=0; i<4; i++) {
111 for (j=0; j<8; j++) {
112 for(k=0; k<3; k++) {
113 char **b = bufs+(k+i*3);
114 *b += printf("%s ", *b);
115 }
116 puts("");
117 }
118 }
119 return;
120 }
121
122 // What day of the week does that start on?
123 mktime(tm);
124
125 } else {
126 time_t now;
127 time(&now);
128 tm = localtime(&now);
129 }
130
131 calstrings(buf, tm);
132 while (*buf) buf += printf("%s\n", buf);
133 }
134