17aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley/* cal.c - show calendar.
282895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley *
382895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley * Copyright 2011 Rob Landley <rob@landley.net>
482895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley *
582895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley * See http://opengroup.org/onlinepubs/9699919799/utilities/cal.html
682895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
782895f6cf412a958d71ed1a0eefcebd435e06f31Rob LandleyUSE_CAL(NEWTOY(cal, ">2", TOYFLAG_USR|TOYFLAG_BIN))
882895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
982895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landleyconfig CAL
107aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  bool "cal"
117aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  default y
127aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  help
137aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    usage: cal [[month] year]
14314f19e4d2099eb19824df49d71953946486f78fRob Landley
157aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    Print a calendar.
167aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
177aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    With one argument, prints all months of the specified year.
187aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    With two arguments, prints calendar for month and year.
1982895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley*/
2082895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
2182895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley#include "toys.h"
2282895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
2382895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley// Write calendar into buffer: each line is 20 chars wide, end indicated
2482895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley// by empty string.
2582895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
2682895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landleystatic char *calstrings(char *buf, struct tm *tm)
2782895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley{
287aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  char temp[21];
297aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  int wday, mday, start, len, line;
307aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
317aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // header
327aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  len = strftime(temp, 21, "%B %Y", tm);
337aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  len += (20-len)/2;
347aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  buf += sprintf(buf, "%*s%*s ", len, temp, 20-len, "");
357aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  buf++;
367aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  buf += sprintf(buf, "Su Mo Tu We Th Fr Sa ");
377aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  buf++;
387aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
397aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // What day of the week does this month start on?
407aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  if (tm->tm_mday>1)
417aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    start = (36+tm->tm_wday-tm->tm_mday)%7;
427aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  else start = tm->tm_wday;
437aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
447aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // What day does this month end on?  Alas, libc doesn't tell us...
457aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  len = 31;
467aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  if (tm->tm_mon == 1) {
477aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    int year = tm->tm_year;
487aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    len = 28;
497aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    if (!(year & 3) && !((year&100) && !(year&400))) len++;
507aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  } else if ((tm->tm_mon+(tm->tm_mon>6 ? 1 : 0)) & 1) len = 30;
517aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
527aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  for (mday=line=0;line<6;line++) {
537aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    for (wday=0; wday<7; wday++) {
547aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      char *pat = "   ";
557aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      if (!mday ? wday==start : mday<len) {
567aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        pat = "%2d ";
577aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        mday++;
587aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      }
597aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      buf += sprintf(buf, pat, mday);
607aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    }
617aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    buf++;
627aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  }
637aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
647aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  return buf;
6582895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley}
6682895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
6782895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landleyvoid xcheckrange(long val, long low, long high)
6882895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley{
697aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  char *err = "%ld %s than %ld";
7082895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
717aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  if (val < low) error_exit(err, val, "less", low);
727aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  if (val > high) error_exit(err, val, "greater", high);
7382895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley}
7482895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
7582895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley// Worst case scenario toybuf usage: sizeof(struct tm) plus 21 bytes/line
7682895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley// plus 8 lines/month plus 12 months, comes to a bit over 2k of our 4k buffer.
7782895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley
7882895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landleyvoid cal_main(void)
7982895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley{
807aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  struct tm *tm;
817aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  char *buf = toybuf;
827aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
837aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  if (toys.optc) {
847aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Conveniently starts zeroed
857aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    tm = (struct tm *)toybuf;
867aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    buf += sizeof(struct tm);
877aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
887aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Last argument is year, one before that (if any) is month.
897aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    xcheckrange(tm->tm_year = atol(toys.optargs[--toys.optc]),1,9999);
907aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    tm->tm_year -= 1900;
917aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    tm->tm_mday = 1;
927aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    tm->tm_hour = 12;  // noon to avoid timezone weirdness
937aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    if (toys.optc) {
947aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      xcheckrange(tm->tm_mon = atol(toys.optargs[--toys.optc]),1,12);
957aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      tm->tm_mon--;
967aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
977aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Print 12 months of the year
987aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
997aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    } else {
1007aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      char *bufs[12];
1017aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      int i, j, k;
1027aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1037aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      for (i=0; i<12; i++) {
1047aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        tm->tm_mon=i;
1057aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        mktime(tm);
1067aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        buf = calstrings(bufs[i]=buf, tm);
1077aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      }
1087aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1097aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      // 4 rows, 6 lines each, 3 columns
1107aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      for (i=0; i<4; i++) {
1117aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        for (j=0; j<8; j++) {
1127aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley          for(k=0; k<3; k++) {
1137aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley            char **b = bufs+(k+i*3);
1147aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley            *b += printf("%s ", *b);
1157aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley          }
1167aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley          puts("");
1177aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        }
1187aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      }
1197aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      return;
1207aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    }
1217aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1227aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // What day of the week does that start on?
1237aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    mktime(tm);
1247aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1257aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  } else {
1267aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    time_t now;
1277aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    time(&now);
1287aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    tm = localtime(&now);
1297aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  }
1307aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1317aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  calstrings(buf, tm);
1327aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  while (*buf) buf += printf("%s\n", buf);
13382895f6cf412a958d71ed1a0eefcebd435e06f31Rob Landley}
134