cal.c 4.9 KB

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