summaryrefslogtreecommitdiffstats
path: root/console/calendar.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/calendar.c')
-rw-r--r--console/calendar.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/console/calendar.c b/console/calendar.c
new file mode 100644
index 00000000..b87c774a
--- /dev/null
+++ b/console/calendar.c
@@ -0,0 +1,362 @@
+/* $Id$ */
+#include "bbs.h"
+
+typedef struct event_t {
+ int year, month, day, days;
+ int color;
+ char *content;
+ struct event_t *next;
+} event_t;
+
+static int
+MonthDay(int m, int leap)
+{
+ int day[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ assert(1<=m && m<=12);
+ return leap && m == 2 ? 29 : day[m - 1];
+}
+
+static int
+Days(int y, int m, int d)
+{
+ int i, w;
+
+ w = 1 + 365 * (y - 1)
+ + ((y - 1) / 4) - ((y - 1) / 100) + ((y - 1) / 400)
+ + d - 1;
+ for (i = 1; i < m; i++)
+ w += MonthDay(i, is_leap_year(y));
+ return w;
+}
+
+/**
+ * return 1 if date is invalid
+ */
+int ParseDate(const char *date, int *year, int *month, int *day)
+{
+ char *y, *m, *d;
+ char buf[128];
+ char *strtok_pos;
+
+ strlcpy(buf, date, sizeof(buf));
+ y = strtok_r(buf, "/", &strtok_pos); if (!y) return 1;
+ m = strtok_r(NULL, "/", &strtok_pos);if (!m) return 1;
+ d = strtok_r(NULL, "", &strtok_pos); if (!d) return 1;
+
+ *year = atoi(y);
+ *month = atoi(m);
+ *day = atoi(d);
+ if (*year < 1 || *month < 1 || *month > 12 ||
+ *day < 1 || *day > MonthDay(*month, is_leap_year(*year)))
+ return 1;
+ return 0;
+}
+
+/**
+ * return 1 if date is invalid
+ */
+static int
+ParseEventDate(const char *date, event_t * t)
+{
+ int retval = ParseDate(date, &t->year, &t->month, &t->day);
+ if (retval)
+ return retval;
+ t->days = Days(t->year, t->month, t->day);
+ return retval;
+}
+
+static int
+ParseColor(const char *color)
+{
+ struct {
+ char *str;
+ int val;
+ } c[] = {
+ {
+ "black", 0
+ },
+ {
+ "red", 1
+ },
+ {
+ "green", 2
+ },
+ {
+ "yellow", 3
+ },
+ {
+ "blue", 4
+ },
+ {
+ "magenta", 5
+ },
+ {
+ "cyan", 6
+ },
+ {
+ "white", 7
+ }
+ };
+ int i;
+
+ for (i = 0; (unsigned)i < sizeof(c) / sizeof(c[0]); i++)
+ if (strcasecmp(color, c[i].str) == 0)
+ return c[i].val;
+ return 7;
+}
+
+static void
+InsertEvent(event_t * head, event_t * t)
+{
+ event_t *p;
+
+ for (p = head; p->next && p->next->days < t->days; p = p->next);
+ t->next = p->next;
+ p->next = t;
+}
+
+static void
+FreeEvent(event_t * e)
+{
+ event_t *n;
+
+ while (e) {
+ n = e->next;
+ free(e->content); /* from strdup() */
+ free(e);
+ e = n;
+ }
+}
+
+static event_t *
+ReadEvent(int today)
+{
+ FILE *fp;
+ char buf[256];
+ static event_t head;
+
+ head.next = NULL;
+ sethomefile(buf, cuser.userid, "calendar");
+ fp = fopen(buf, "r");
+ if (fp) {
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *date, *color, *content;
+ event_t *t;
+ char *strtok_pos;
+
+ if (buf[0] == '#')
+ continue;
+
+ date = strtok_r(buf, " \t\n", &strtok_pos);
+ color = strtok_r(NULL, " \t\n", &strtok_pos);
+ content = strtok_r(NULL, "\n", &strtok_pos);
+ if (!date || !color || !content)
+ continue;
+
+ t = malloc(sizeof(event_t));
+ if (ParseEventDate(date, t) || t->days < today) {
+ free(t);
+ continue;
+ }
+ t->color = ParseColor(color) + 30;
+ for (; *content == ' ' || *content == '\t'; content++);
+ t->content = strdup(content);
+ InsertEvent(&head, t);
+ }
+ fclose(fp);
+ }
+ return head.next;
+}
+
+static char **
+AllocCalBuffer(int line, int len)
+{
+ int i;
+ char **p;
+
+ p = malloc(sizeof(char *) * line);
+ p[0] = malloc(sizeof(char) * line * len);
+ for (i = 1; i < line; i++)
+ p[i] = p[i - 1] + len;
+ return p;
+}
+
+static void
+FreeCalBuffer(char **buf)
+{
+ free(buf[0]);
+ free(buf);
+}
+
+#define CALENDAR_COLOR ANSI_COLOR(0;30;47)
+#define HEADER_COLOR ANSI_COLOR(1;44)
+#define HEADER_SUNDAY_COLOR ANSI_COLOR(31)
+#define HEADER_DAY_COLOR ANSI_COLOR(33)
+
+static int
+GenerateCalendar(char **buf, int y, int m, int today, event_t * e)
+{
+ char *week_str[7] = {"日", "一", "二", "三", "四", "五", "六"};
+ char *month_color[12] = {
+ ANSI_COLOR(1;32), ANSI_COLOR(1;33), ANSI_COLOR(1;35), ANSI_COLOR(1;36),
+ ANSI_COLOR(1;32), ANSI_COLOR(1;33), ANSI_COLOR(1;35), ANSI_COLOR(1;36),
+ ANSI_COLOR(1;32), ANSI_COLOR(1;33), ANSI_COLOR(1;35), ANSI_COLOR(1;36)
+ };
+ char *month_str[12] = {
+ "一月 ", "二月 ", "三月 ", "四月 ", "五月 ", "六月 ",
+ "七月 ", "八月 ", "九月 ", "十月 ", "十一月", "十二月"
+ };
+
+ char *p, attr1[16], *attr2;
+ int i, d, w, line = 0, first_day = Days(y, m, 1);
+
+
+ /* week day banner */
+ p = buf[line];
+ p += sprintf(p, " %s%s%s%s", HEADER_COLOR, HEADER_SUNDAY_COLOR,
+ week_str[0], HEADER_DAY_COLOR);
+ for (i = 1; i < 7; i++)
+ p += sprintf(p, " %s", week_str[i]);
+ p += sprintf(p, ANSI_RESET);
+
+ /* indent for first line */
+ p = buf[++line];
+ p += sprintf(p, " %s", CALENDAR_COLOR);
+ for (i = 0, w = first_day % 7; i < w; i++)
+ p += sprintf(p, " ");
+
+ /* initial event */
+ for (; e && e->days < first_day; e = e->next);
+
+ d = MonthDay(m, is_leap_year(y));
+ for (i = 1; i <= d; i++, w = (w + 1) % 7) {
+ attr1[0] = 0;
+ attr2 = "";
+ while (e && e->days == first_day + i - 1) {
+ sprintf(attr1, ANSI_COLOR(1;%d), e->color);
+ attr2 = CALENDAR_COLOR;
+ e = e->next;
+ }
+ if (today == first_day + i - 1) {
+ strlcpy(attr1, ANSI_COLOR(1;37;42), sizeof(attr1));
+ attr2 = CALENDAR_COLOR;
+ }
+ p += sprintf(p, "%s%2d%s", attr1, i, attr2);
+
+ if (w == 6) {
+ p += sprintf(p, ANSI_RESET);
+ p = buf[++line];
+ /* show month */
+ if (line >= 2 && line <= 4)
+ p += sprintf(p, "%s%2.2s\33[m %s", month_color[m - 1],
+ month_str[m - 1] + (line - 2) * 2,
+ CALENDAR_COLOR);
+ else if (i < d)
+ p += sprintf(p, " %s", CALENDAR_COLOR);
+ } else
+ *p++ = ' ';
+ }
+
+ /* fill up the last line */
+ if (w) {
+ for (w = 7 - w; w; w--)
+ p += sprintf(p, w == 1 ? " " : " ");
+ p += sprintf(p, ANSI_RESET);
+ }
+ return line + 1;
+}
+
+int
+u_editcalendar(void)
+{
+ char genbuf[200];
+
+ getdata(b_lines - 1, 0, "行事曆 (D)刪除 (E)編輯 (H)說明 [Q]取消?[Q] ",
+ genbuf, 3, LCECHO);
+
+ if (genbuf[0] == 'e') {
+ int aborted;
+
+ setutmpmode(EDITPLAN);
+ sethomefile(genbuf, cuser.userid, "calendar");
+ aborted = vedit(genbuf, NA, NULL);
+ if (aborted != -1)
+ vmsg("行事曆更新完畢");
+ return 0;
+ } else if (genbuf[0] == 'd') {
+ sethomefile(genbuf, cuser.userid, "calendar");
+ unlink(genbuf);
+ vmsg("行事曆刪除完畢");
+ } else if (genbuf[0] == 'h') {
+ move(1, 0);
+ clrtoln(b_lines);
+ move(3, 0);
+ prints("行事曆格式說明:\n編輯時以一行為單位,如:\n\n# 井號開頭的是註解\n2006/05/04 red 上批踢踢!\n\n其中的 red 是指表示的顏色。");
+ pressanykey();
+ }
+ return 0;
+}
+
+int
+calendar(void)
+{
+ char **buf;
+ struct tm snow;
+ int i, y, m, today, lines = 0;
+ event_t *head = NULL, *e = NULL;
+
+ /* initialize date */
+ memcpy(&snow, localtime4(&now), sizeof(struct tm));
+ today = Days(snow.tm_year + 1900, snow.tm_mon + 1, snow.tm_mday);
+ y = snow.tm_year + 1900, m = snow.tm_mon + 1;
+
+ /* read event */
+ head = e = ReadEvent(today);
+
+ /* generate calendar */
+ buf = AllocCalBuffer(22, 256);
+ for (i = 0; i < 22; i++)
+ sprintf(buf[i], "%24s", "");
+ for (i = 0; i < 3; i++) {
+ lines += GenerateCalendar(buf + lines, y, m, today, e) + 1;
+ if (m == 12)
+ y++, m = 1;
+ else
+ m++;
+ }
+
+ /* output */
+ clear();
+ outc('\n');
+ for (i = 0; i < 22; i++) {
+ outs(buf[i]);
+ if (i == 0) {
+ prints("\t" ANSI_COLOR(1;37)
+ "現在是 %d.%02d.%02d %2d:%02d:%02d%cm" ANSI_RESET,
+ snow.tm_year + 1900, snow.tm_mon + 1, snow.tm_mday,
+ (snow.tm_hour == 0 || snow.tm_hour == 12) ?
+ 12 : snow.tm_hour % 12, snow.tm_min, snow.tm_sec,
+ snow.tm_hour >= 12 ? 'p' : 'a');
+ } else if (i >= 2 && e) {
+ prints("\t" ANSI_COLOR(1;37)
+ "(尚有 " ANSI_COLOR(%d) "%3d"
+ ANSI_COLOR(37) " 天)"
+ ANSI_RESET " %02d/%02d %s",
+ e->color, e->days - today,
+ e->month, e->day, e->content);
+ e = e->next;
+ }
+ outc('\n');
+ }
+ FreeEvent(head);
+ FreeCalBuffer(buf);
+ i = vmsg("請按 e 編輯行事曆,或其它任意鍵離開。");
+ i = tolower(((unsigned char)i) & 0xFF);
+ if (i == 'e')
+ {
+ u_editcalendar();
+ }
+ return 0;
+}
+