123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- /* See LICENSE file for copyright and license details. */
- #include <limits.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h>
- #include "util.h"
- enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
- enum caltype { JULIAN, GREGORIAN };
- enum { TRANS_YEAR = 1752, TRANS_MONTH = SEP, TRANS_DAY = 2 };
- static struct tm *ltime;
- static int
- isleap(size_t year, enum caltype cal)
- {
- if (cal == GREGORIAN) {
- if (year % 400 == 0)
- return 1;
- if (year % 100 == 0)
- return 0;
- return (year % 4 == 0);
- }
- else { /* cal == Julian */
- return (year % 4 == 0);
- }
- }
- static int
- monthlength(size_t year, int month, enum caltype cal)
- {
- int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- return (month == FEB && isleap(year, cal)) ? 29 : mdays[month];
- }
- /* From http://www.tondering.dk/claus/cal/chrweek.php#calcdow */
- static int
- dayofweek(size_t year, int month, int dom, enum caltype cal)
- {
- size_t y;
- int m, a;
- a = (13 - month) / 12;
- y = year - a;
- m = month + 12 * a - 1;
- if (cal == GREGORIAN)
- return (dom + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) % 7;
- else /* cal == Julian */
- return (5 + dom + y + y / 4 + (31 * m) / 12) % 7;
- }
- static void
- printgrid(size_t year, int month, int fday, int line)
- {
- enum caltype cal;
- int offset, dom, d = 0, trans; /* are we in the transition from Julian to Gregorian? */
- int today = 0;
- cal = (year < TRANS_YEAR || (year == TRANS_YEAR && month <= TRANS_MONTH)) ? JULIAN : GREGORIAN;
- trans = (year == TRANS_YEAR && month == TRANS_MONTH);
- offset = dayofweek(year, month, 1, cal) - fday;
- if (offset < 0)
- offset += 7;
- if (line == 1) {
- for (; d < offset; ++d)
- printf(" ");
- dom = 1;
- } else {
- dom = 8 - offset + (line - 2) * 7;
- if (trans && !(line == 2 && fday == 3))
- dom += 11;
- }
- if (ltime && year == ltime->tm_year + 1900 && month == ltime->tm_mon)
- today = ltime->tm_mday;
- for (; d < 7 && dom <= monthlength(year, month, cal); ++d, ++dom) {
- if (dom == today)
- printf("\x1b[7m%2d\x1b[0m ", dom); /* highlight today's date */
- else
- printf("%2d ", dom);
- if (trans && dom == TRANS_DAY)
- dom += 11;
- }
- for (; d < 7; ++d)
- printf(" ");
- }
- static void
- drawcal(size_t year, int month, size_t ncols, size_t nmons, int fday)
- {
- char *smon[] = { "January", "February", "March", "April",
- "May", "June", "July", "August",
- "September", "October", "November", "December" };
- char *days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", };
- size_t m, n, col, cur_year, cur_month, dow;
- int line, pad;
- char month_year[sizeof("Su Mo Tu We Th Fr Sa")];
- for (m = 0; m < nmons; ) {
- n = m;
- for (col = 0; m < nmons && col < ncols; ++col, ++m) {
- cur_year = year + m / 12;
- cur_month = month + m % 12;
- if (cur_month > 11) {
- cur_month -= 12;
- cur_year += 1;
- }
- snprintf(month_year, sizeof(month_year), "%s %zu", smon[cur_month], cur_year);
- pad = sizeof(month_year) - 1 - strlen(month_year);
- printf("%*s%s%*s ", pad / 2 + pad % 2, "", month_year, pad / 2, "");
- }
- putchar('\n');
- for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
- for (dow = fday; dow < (fday + 7); ++dow)
- printf("%s ", days[dow % 7]);
- printf(" ");
- }
- putchar('\n');
- for (line = 1; line <= 6; ++line) {
- for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
- cur_year = year + m / 12;
- cur_month = month + m % 12;
- if (cur_month > 11) {
- cur_month -= 12;
- cur_year += 1;
- }
- printgrid(cur_year, cur_month, fday, line);
- printf(" ");
- }
- putchar('\n');
- }
- }
- }
- static void
- usage(void)
- {
- eprintf("usage: %s [-1 | -3 | -y | -n num] "
- "[-s | -m | -f num] [-c num] [[month] year]\n", argv0);
- }
- int
- main(int argc, char *argv[])
- {
- time_t now;
- size_t year, ncols, nmons;
- int fday, month;
- now = time(NULL);
- ltime = localtime(&now);
- year = ltime->tm_year + 1900;
- month = ltime->tm_mon + 1;
- fday = 0;
- if (!isatty(STDOUT_FILENO))
- ltime = NULL; /* don't highlight today's date */
- ncols = 3;
- nmons = 0;
- ARGBEGIN {
- case '1':
- nmons = 1;
- break;
- case '3':
- nmons = 3;
- if (--month == 0) {
- month = 12;
- year--;
- }
- break;
- case 'c':
- ncols = estrtonum(EARGF(usage()), 0, MIN(SIZE_MAX, LLONG_MAX));
- break;
- case 'f':
- fday = estrtonum(EARGF(usage()), 0, 6);
- break;
- case 'm': /* Monday */
- fday = 1;
- break;
- case 'n':
- nmons = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
- break;
- case 's': /* Sunday */
- fday = 0;
- break;
- case 'y':
- month = 1;
- nmons = 12;
- break;
- default:
- usage();
- } ARGEND
- if (nmons == 0) {
- if (argc == 1) {
- month = 1;
- nmons = 12;
- } else {
- nmons = 1;
- }
- }
- switch (argc) {
- case 2:
- month = estrtonum(argv[0], 1, 12);
- argv++;
- case 1: /* fallthrough */
- year = estrtonum(argv[0], 0, INT_MAX);
- break;
- case 0:
- break;
- default:
- usage();
- }
- drawcal(year, month - 1, ncols, nmons, fday);
- return fshut(stdout, "<stdout>");
- }
|