Home | History | Annotate | Download | only in posix
      1 /* cal.c - show calendar.
      2  *
      3  * Copyright 2011 Rob Landley <rob (at) 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 
     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 
     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 
     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