sbase

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

cal.c (4827B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <limits.h>
      3 #include <stdint.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <time.h>
      7 #include <unistd.h>
      8 
      9 #include "util.h"
     10 
     11 enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
     12 enum caltype { JULIAN, GREGORIAN };
     13 enum { TRANS_YEAR = 1752, TRANS_MONTH = SEP, TRANS_DAY = 2 };
     14 
     15 static struct tm *ltime;
     16 
     17 static int
     18 isleap(size_t year, enum caltype cal)
     19 {
     20 	if (cal == GREGORIAN) {
     21 		if (year % 400 == 0)
     22 			return 1;
     23 		if (year % 100 == 0)
     24 			return 0;
     25 		return (year % 4 == 0);
     26 	}
     27 	else { /* cal == Julian */
     28 		return (year % 4 == 0);
     29 	}
     30 }
     31 
     32 static int
     33 monthlength(size_t year, int month, enum caltype cal)
     34 {
     35 	int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
     36 
     37 	return (month == FEB && isleap(year, cal)) ? 29 : mdays[month];
     38 }
     39 
     40 /* From http://www.tondering.dk/claus/cal/chrweek.php#calcdow */
     41 static int
     42 dayofweek(size_t year, int month, int dom, enum caltype cal)
     43 {
     44 	size_t y;
     45 	int m, a;
     46 
     47 	a = (13 - month) / 12;
     48 	y = year - a;
     49 	m = month + 12 * a - 1;
     50 
     51 	if (cal == GREGORIAN)
     52 		return (dom + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) % 7;
     53 	else  /* cal == Julian */
     54 		return (5 + dom + y + y / 4 + (31 * m) / 12) % 7;
     55 }
     56 
     57 static void
     58 printgrid(size_t year, int month, int fday, int line)
     59 {
     60 	enum caltype cal;
     61 	int offset, dom, d = 0, trans; /* are we in the transition from Julian to Gregorian? */
     62 	int today = 0;
     63 
     64 	cal = (year < TRANS_YEAR || (year == TRANS_YEAR && month <= TRANS_MONTH)) ? JULIAN : GREGORIAN;
     65 	trans = (year == TRANS_YEAR && month == TRANS_MONTH);
     66 	offset = dayofweek(year, month, 1, cal) - fday;
     67 
     68 	if (offset < 0)
     69 		offset += 7;
     70 	if (line == 1) {
     71 		for (; d < offset; ++d)
     72 			printf("   ");
     73 		dom = 1;
     74 	} else {
     75 		dom = 8 - offset + (line - 2) * 7;
     76 		if (trans && !(line == 2 && fday == 3))
     77 			dom += 11;
     78 	}
     79 	if (ltime && year == ltime->tm_year + 1900 && month == ltime->tm_mon)
     80 		today = ltime->tm_mday;
     81 	for (; d < 7 && dom <= monthlength(year, month, cal); ++d, ++dom) {
     82 		if (dom == today)
     83 			printf("\x1b[7m%2d\x1b[0m ", dom); /* highlight today's date */
     84 		else
     85 			printf("%2d ", dom);
     86 		if (trans && dom == TRANS_DAY)
     87 			dom += 11;
     88 	}
     89 	for (; d < 7; ++d)
     90 		printf("   ");
     91 }
     92 
     93 static void
     94 drawcal(size_t year, int month, size_t ncols, size_t nmons, int fday)
     95 {
     96 	char *smon[] = {"  January", " February", "    March", "    April",
     97 	                "      May", "     June", "     July", "   August",
     98 	                "September", "  October", " November", " December" };
     99 	char *days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", };
    100 	size_t m, n, col, cur_year, cur_month, dow;
    101 	int line;
    102 
    103 	for (m = 0; m < nmons; ) {
    104 		n = m;
    105 		for (col = 0; m < nmons && col < ncols; ++col, ++m) {
    106 			cur_year = year + m / 12;
    107 			cur_month = month + m % 12;
    108 			if (cur_month > 11) {
    109 				cur_month -= 12;
    110 				cur_year += 1;
    111 			}
    112 			printf("   %s %zu    ", smon[cur_month], cur_year);
    113 			printf("  ");
    114 		}
    115 		putchar('\n');
    116 		for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
    117 			for (dow = fday; dow < (fday + 7); ++dow)
    118 				printf("%s ", days[dow % 7]);
    119 			printf("  ");
    120 		}
    121 		putchar('\n');
    122 		for (line = 1; line <= 6; ++line) {
    123 			for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
    124 				cur_year = year + m / 12;
    125 				cur_month = month + m % 12;
    126 				if (cur_month > 11) {
    127 					cur_month -= 12;
    128 					cur_year += 1;
    129 				}
    130 				printgrid(cur_year, cur_month, fday, line);
    131 				printf("  ");
    132 			}
    133 			putchar('\n');
    134 		}
    135 	}
    136 }
    137 
    138 static void
    139 usage(void)
    140 {
    141 	eprintf("usage: %s [-1 | -3 | -y | -n num] "
    142 	        "[-s | -m | -f num] [-c num] [[month] year]\n", argv0);
    143 }
    144 
    145 int
    146 main(int argc, char *argv[])
    147 {
    148 	time_t now;
    149 	size_t year, ncols, nmons;
    150 	int fday, month;
    151 
    152 	now   = time(NULL);
    153 	ltime = localtime(&now);
    154 	year  = ltime->tm_year + 1900;
    155 	month = ltime->tm_mon + 1;
    156 	fday  = 0;
    157 
    158 	if (!isatty(STDOUT_FILENO))
    159 		ltime = NULL; /* don't highlight today's date */
    160 
    161 	ncols = 3;
    162 	nmons = 0;
    163 
    164 	ARGBEGIN {
    165 	case '1':
    166 		nmons = 1;
    167 		break;
    168 	case '3':
    169 		nmons = 3;
    170 		if (--month == 0) {
    171 			month = 12;
    172 			year--;
    173 		}
    174 		break;
    175 	case 'c':
    176 		ncols = estrtonum(EARGF(usage()), 0, MIN(SIZE_MAX, LLONG_MAX));
    177 		break;
    178 	case 'f':
    179 		fday = estrtonum(EARGF(usage()), 0, 6);
    180 		break;
    181 	case 'm': /* Monday */
    182 		fday = 1;
    183 		break;
    184 	case 'n':
    185 		nmons = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
    186 		break;
    187 	case 's': /* Sunday */
    188 		fday = 0;
    189 		break;
    190 	case 'y':
    191 		month = 1;
    192 		nmons = 12;
    193 		break;
    194 	default:
    195 		usage();
    196 	} ARGEND
    197 
    198 	if (nmons == 0) {
    199 		if (argc == 1) {
    200 			month = 1;
    201 			nmons = 12;
    202 		} else {
    203 			nmons = 1;
    204 		}
    205 	}
    206 
    207 	switch (argc) {
    208 	case 2:
    209 		month = estrtonum(argv[0], 1, 12);
    210 		argv++;
    211 	case 1: /* fallthrough */
    212 		year = estrtonum(argv[0], 0, INT_MAX);
    213 		break;
    214 	case 0:
    215 		break;
    216 	default:
    217 		usage();
    218 	}
    219 
    220 	drawcal(year, month - 1, ncols, nmons, fday);
    221 
    222 	return fshut(stdout, "<stdout>");
    223 }