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 7USE_CAL(NEWTOY(cal, ">2", TOYFLAG_USR|TOYFLAG_BIN)) 8 9config 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 26static 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 67void 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 78void 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