/* * dcal - a Discordian calendar. Hail Eris! * Copyright 2006 Alexey Toptygin * Released under GPL Version 2, June 1991 and WITHOUT ANY WARRANTY */ #include #include #include #include /* You heard me! Yold off! */ #define YOLD_OFF 1166 #define SEASON_LEN 73 #define STTIBS_DAY 59 static const char *seasons[] = { "Chaos", "Discord", "Confusion", "Bureaucracy", "Aftermath" }; static const char *days = "SM BT PD PP SO"; /* BUF = MAX ( sprintf("%s %d", seasons[3], MIN_INT), * sprintf(" %s ", days), * strlen("56 57 58 59\033[7mT\033[0m60") * ) + 1 */ #define BUF 24 struct dtm { int yold; /* whatever */ int season; /* 0-4 */ int day; /* 0-72, and -1 for St. Tib's day */ int leap; /* will we get a visit from Tib this year? */ }; static inline int leapp(const int yold) { int y = yold - YOLD_OFF; return (!(y % 4) && ((y % 100) || !(y % 400))); } static inline int cmp(const struct dtm *a, const struct dtm *b) { if (a->yold > b->yold) return 1; if (a->yold < b->yold) return -1; if (a->season > b->season) return 1; if (a->season < b->season) return -1; if (a->day > b->day) return 1; if (a->day < b->day) return -1; return 0; } static void nonlocaltime(struct dtm *dt, const time_t *time) { struct tm *t = localtime(time); dt->yold = t->tm_year + 1900 + YOLD_OFF; dt->leap = leapp(dt->yold); if (dt->leap && (t->tm_yday >= STTIBS_DAY)) { dt->season = (t->tm_yday - 1) / SEASON_LEN; if (t->tm_yday == STTIBS_DAY) { dt->day = -1; } else { dt->day = (t->tm_yday - 1) % SEASON_LEN; } } else { dt->season = t->tm_yday / SEASON_LEN; dt->day = t->tm_yday % SEASON_LEN; } } static size_t needwidth(const struct dtm *dt) { char t[BUF]; snprintf(t, BUF, "%s %d", seasons[dt->season], dt->yold); if (strlen(t) > (strlen(days) + 2)) return strlen(t); else return strlen(days) + 2; } static const char *season_string(const struct dtm *dt, const size_t len) { static char s[BUF]; char t[BUF]; size_t blanklen; snprintf(t, BUF, "%s %d", seasons[dt->season], dt->yold); blanklen = len - strlen(t); snprintf(s, BUF, "%*s%s%*s", (blanklen / 2), "", t, ((blanklen + 1) / 2), ""); return s; } static const char *weekday_string(const size_t len) { static char s[BUF]; size_t blanklen; blanklen = len - strlen(days); snprintf(s, BUF, "%*s%s%*s", (blanklen / 2), "", days, ((blanklen + 1) / 2), ""); return s; } /* increments then->day */ static const char *week_string(struct dtm *then, const struct dtm *now, const size_t len) { static char s[BUF]; char t[BUF]; int i; size_t blanklen; blanklen = len - 16; snprintf(s, BUF, "%*s", ((blanklen / 2) + 1), ""); for (i = 0; i < 5; i++) { if ((then->day >= 0) && (then->day < SEASON_LEN)) { if (!cmp(then, now)) { /* perhaps we ought to use curses, * but if it's good enough for ncal, * it's good enough for dcal! */ snprintf(t, BUF, "\033[7m%2d\033[0m", ++then->day); } else { snprintf(t, BUF, "%2d", ++then->day); } } else { t[0] = ' '; t[1] = ' '; t[2] = '\0'; ++then->day; } strcat(s, t); if (then->leap && !then->season && (STTIBS_DAY == then->day)) { if (now->day == -1) strcat(s, "\033[7mT\033[0m"); else strcat(s, "T"); } else { strcat(s, " "); } } snprintf(t, BUF, "%*s", ((blanklen + 1) / 2), ""); strcat(s, t); return s; } static void print_usage(const char *prog) { fprintf(stderr, "Usage: %s [-3] [season yold]\n", prog); } static void print_one(struct dtm *then, const struct dtm *now) { size_t col; col = needwidth(then); printf("%s\n", season_string(then, col)); printf("%s\n", weekday_string(col)); then->day = -((then->season * 3) % 5); while (then->day < SEASON_LEN) printf("%s\n", week_string(then, now, col)); } static void print_three(struct dtm *then, const struct dtm *now) { struct dtm prev, next; size_t col, tmp; prev = next = *then; prev.season--; if (prev.season < 0) { prev.yold--; prev.season = 4; prev.leap = leapp(prev.yold); } next.season++; if (next.season > 4) { next.yold++; next.season = 0; next.leap = leapp(next.yold); } col = needwidth(&prev); tmp = needwidth(then); if (tmp > col) col = tmp; tmp = needwidth(&next); if (tmp > col) col = tmp; printf("%s ", season_string(&prev, col)); printf("%s ", season_string(then, col)); printf("%s\n", season_string(&next, col)); printf("%s ", weekday_string(col)); printf("%s ", weekday_string(col)); printf("%s\n", weekday_string(col)); prev.day = -((prev.season * 3) % 5); then->day = -((then->season * 3) % 5); next.day = -((next.season * 3) % 5); while ((prev.day < SEASON_LEN) || (then->day < SEASON_LEN) || (next.day < SEASON_LEN)) { printf("%s ", week_string(&prev, now, col)); printf("%s ", week_string(then, now, col)); printf("%s\n", week_string(&next, now, col)); } } int main(int argc, char **argv) { struct dtm then, now; char *prog; int mode = 0; time_t tnow = time(NULL); nonlocaltime(&now, &tnow); prog = *argv; argc--; argv++; while (argc && (**argv == '-')) { (*argv)++; while (**argv != '\0') { switch (**argv) { case '3': mode = 1; break; default: fprintf(stderr, "Unknown option: %c\n", **argv); print_usage(prog); exit(1); } (*argv)++; } argc--; argv++; } if (argc == 2) { then.season = atoi(argv[0]) - 1; if ((then.season < 0) || (then.season > 4)) { fprintf(stderr, "I've never heard of season number %s\n", argv[0]); print_usage(prog); exit(1); } then.yold = atoi(argv[1]); then.leap = leapp(then.yold); then.day = 0; } else if (argc == 0) { nonlocaltime(&then, &tnow); } else { print_usage(prog); exit(1); } if (mode) { print_three(&then, &now); } else { print_one(&then, &now); } return 0; }