Puzzle #2: cal(1)

(I’ve been fixing little smf(5) bugs, as well as revising our documentation, presentations and–most importantly–more block diagrams for this blog. But I bumped into an annoyance and thought I should share.)

As an young old-school Unix developer, I tend to live in terminal windows. One of my favourite commands is cal(1), which has a great default mode:

$ cal
September 2004
S  M Tu  W Th  F  S
1  2  3  4
5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

But if you want to see an October calendar, you might get confused:

$ cal 10
10
Jan                    Feb                    Mar
S  M Tu  W Th  F  S    S  M Tu  W Th  F  S    S  M Tu  W Th  F  S
1  2  3  4                      1                      1
5  6  7  8  9 10 11    2  3  4  5  6  7  8    2  3  4  5  6  7  8
12 13 14 15 16 17 18    9 10 11 12 13 14 15    9 10 11 12 13 14 15
19 20 21 22 23 24 25   16 17 18 19 20 21 22   16 17 18 19 20 21 22
26 27 28 29 30 31      23 24 25 26 27 28      23 24 25 26 27 28 29
30 31
Apr                    May                    Jun
S  M Tu  W Th  F  S    S  M Tu  W Th  F  S    S  M Tu  W Th  F  S
1  2  3  4  5                1  2  3    1  2  3  4  5  6  7
6  7  8  9 10 11 12    4  5  6  7  8  9 10    8  9 10 11 12 13 14
13 14 15 16 17 18 19   11 12 13 14 15 16 17   15 16 17 18 19 20 21
20 21 22 23 24 25 26   18 19 20 21 22 23 24   22 23 24 25 26 27 28
27 28 29 30            25 26 27 28 29 30 31   29 30
Jul                    Aug                    Sep
S  M Tu  W Th  F  S    S  M Tu  W Th  F  S    S  M Tu  W Th  F  S
1  2  3  4  5                   1  2       1  2  3  4  5  6
6  7  8  9 10 11 12    3  4  5  6  7  8  9    7  8  9 10 11 12 13
13 14 15 16 17 18 19   10 11 12 13 14 15 16   14 15 16 17 18 19 20
20 21 22 23 24 25 26   17 18 19 20 21 22 23   21 22 23 24 25 26 27
27 28 29 30 31         24 25 26 27 28 29 30   28 29 30
31
Oct                    Nov                    Dec
S  M Tu  W Th  F  S    S  M Tu  W Th  F  S    S  M Tu  W Th  F  S
1  2  3  4                      1       1  2  3  4  5  6
5  6  7  8  9 10 11    2  3  4  5  6  7  8    7  8  9 10 11 12 13
12 13 14 15 16 17 18    9 10 11 12 13 14 15   14 15 16 17 18 19 20
19 20 21 22 23 24 25   16 17 18 19 20 21 22   21 22 23 24 25 26 27
26 27 28 29 30 31      23 24 25 26 27 28 29   28 29 30 31
30

It’s an interesting UI choice to assume that anyone would want the calendar for the year 10 C.E. Certainly I never do, and I’m pretty sure someone would have told me if UNIX systems were the professional historian’s first choice for computing…

So today’s puzzle is to make cal(1) more usable. If I enter cal month_num, give me the current month; If I enter “cal now” give me the 3-month window around the current month, like so:

$ cal now
August 2004             September 2004          October 2004
S  M Tu  W Th  F  S     S  M Tu  W Th  F  S     S  M Tu  W Th  F  S
1  2  3  4  5  6  7              1  2  3  4                    1  2
8  9 10 11 12 13 14     5  6  7  8  9 10 11     3  4  5  6  7  8  9
15 16 17 18 19 20 21    12 13 14 15 16 17 18    10 11 12 13 14 15 16
22 23 24 25 26 27 28    19 20 21 22 23 24 25    17 18 19 20 21 22 23
29 30 31                26 27 28 29 30          24 25 26 27 28 29 30
31

Other than that, all other standard invocations of cal(1) should work as usual.

My example solution is a couple dozen line ksh(1) shell function, and I’ll post it along with the best submissions. (Perl folks: no non-core modules, please.)