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