From f5d8b5e021a62bf3e36e18aa9aebee331fece8dd Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Thu, 25 Feb 2016 21:31:16 +0100 Subject: Support durations in recurrence ending dates When spending the end date of recurring items, allow date duration specifiers such as "+5d" or "+3w2d". Signed-off-by: Lukas Fleischer --- src/calcurse.h | 2 ++ src/ui-calendar.c | 20 ------------ src/ui-day.c | 46 +++++++++++++++++++--------- src/utils.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 34 deletions(-) diff --git a/src/calcurse.h b/src/calcurse.h index bc3bf11..d337145 100644 --- a/src/calcurse.h +++ b/src/calcurse.h @@ -1113,6 +1113,7 @@ time_t date2sec(struct date, unsigned, unsigned); time_t utcdate2sec(struct date, unsigned, unsigned); char *date_sec2date_str(long, const char *); void date_sec2date_fmt(long, const char *, char *); +int date_change(struct tm *, int, int); long date_sec_change(long, int, int); long update_time_in_date(long, unsigned, unsigned); time_t get_sec_date(struct date); @@ -1131,6 +1132,7 @@ int parse_date(const char *, enum datefmt, int *, int *, int *, int check_time(unsigned, unsigned); int parse_time(const char *, unsigned *, unsigned *); int parse_duration(const char *, unsigned *); +int parse_date_duration(const char *, unsigned *); void file_close(FILE *, const char *); void psleep(unsigned); int fork_exec(int *, int *, const char *, const char *const *); diff --git a/src/ui-calendar.c b/src/ui-calendar.c index 49ccdce..4f61376 100644 --- a/src/ui-calendar.c +++ b/src/ui-calendar.c @@ -256,26 +256,6 @@ static long ymd_to_scalar(unsigned year, unsigned month, unsigned day) return scalar; } -/* - * Used to change date by adding a certain amount of days or weeks. - * Returns 0 on success, 1 otherwise. - */ -static int date_change(struct tm *date, int delta_month, int delta_day) -{ - struct tm t; - - t = *date; - t.tm_mon += delta_month; - t.tm_mday += delta_day; - - if (mktime(&t) == -1) { - return 1; - } else { - *date = t; - return 0; - } -} - void ui_calendar_monthly_view_cache_set_invalid(void) { monthly_view_cache_valid = 0; diff --git a/src/ui-day.c b/src/ui-day.c index 63c5da8..fdb3c09 100644 --- a/src/ui-day.c +++ b/src/ui-day.c @@ -254,6 +254,7 @@ static void update_rept(struct rpt **rpt, const long start) time_t t; struct date new_date; int newmonth, newday, newyear; + unsigned days; asprintf(&outstr, _("Enter the new ending date: [%s] or '0'"), DATEFMT_DESC(conf.input_datefmt)); @@ -270,21 +271,38 @@ static void update_rept(struct rpt **rpt, const long start) newuntil = 0; break; } - if (!parse_date - (timstr, conf.input_datefmt, &newyear, &newmonth, - &newday, ui_calendar_get_slctd_day())) { - asprintf(&outstr, msg_fmts, - DATEFMT_DESC(conf.input_datefmt)); - status_mesg(msg_wrong_date, outstr); - mem_free(outstr); - wgetch(win[KEY].p); - continue; + if (*timstr == '+') { + if (!parse_date_duration(timstr + 1, &days)) { + asprintf(&outstr, msg_fmts, + DATEFMT_DESC(conf.input_datefmt)); + status_mesg(msg_wrong_date, outstr); + mem_free(outstr); + wgetch(win[KEY].p); + continue; + } + t = start; + localtime_r(&t, <); + date_change(<, 0, days); + new_date.dd = lt.tm_mday; + new_date.mm = lt.tm_mon + 1; + new_date.yyyy = lt.tm_year + 1900; + } else { + if (!parse_date(timstr, conf.input_datefmt, &newyear, + &newmonth, &newday, + ui_calendar_get_slctd_day())) { + asprintf(&outstr, msg_fmts, + DATEFMT_DESC(conf.input_datefmt)); + status_mesg(msg_wrong_date, outstr); + mem_free(outstr); + wgetch(win[KEY].p); + continue; + } + t = start; + localtime_r(&t, <); + new_date.dd = newday; + new_date.mm = newmonth; + new_date.yyyy = newyear; } - t = start; - localtime_r(&t, <); - new_date.dd = newday; - new_date.mm = newmonth; - new_date.yyyy = newyear; newuntil = date2sec(new_date, lt.tm_hour, lt.tm_min); if (newuntil >= start) break; diff --git a/src/utils.c b/src/utils.c index 4ce9364..4300b59 100644 --- a/src/utils.c +++ b/src/utils.c @@ -453,6 +453,26 @@ void date_sec2date_fmt(long sec, const char *fmt, char *datef) #endif } +/* + * Used to change date by adding a certain amount of days or weeks. + * Returns 0 on success, 1 otherwise. + */ +int date_change(struct tm *date, int delta_month, int delta_day) +{ + struct tm t; + + t = *date; + t.tm_mon += delta_month; + t.tm_mday += delta_day; + + if (mktime(&t) == -1) { + return 1; + } else { + *date = t; + return 0; + } +} + /* * Used to change date by adding a certain amount of days or weeks. */ @@ -839,6 +859,77 @@ parse_date(const char *date_string, enum datefmt datefmt, int *year, return 1; } +/* + * Convert a date duration string into a number of days. + * + * Returns 1 on success and 0 on failure. + */ +int parse_date_duration(const char *string, unsigned *days) +{ + enum { + STATE_INITIAL, + STATE_WWDD_DD, + STATE_DONE + } state = STATE_INITIAL; + + const char *p; + unsigned in = 0, frac = 0, denom = 1; + unsigned dur = 0; + + if (!string || *string == '\0') + return 0; + + /* parse string using a simple state machine */ + for (p = string; *p; p++) { + if (state == STATE_DONE) { + return 0; + } else if ((*p >= '0') && (*p <= '9')) { + in = in * 10 + (int)(*p - '0'); + if (frac) + denom *= 10; + } else if (*p == '.') { + if (frac) + return 0; + frac++; + } else { + switch (state) { + case STATE_INITIAL: + if (*p == 'w') { + dur += in * WEEKINDAYS / denom; + state = STATE_WWDD_DD; + } else if (*p == 'd') { + dur += in / denom; + state = STATE_DONE; + } else { + return 0; + } + break; + case STATE_WWDD_DD: + if (*p == 'd') { + dur += in / denom; + state = STATE_DONE; + } else { + return 0; + } + break; + default: + break; + } + + in = frac = 0; + denom = 1; + } + } + + if (state == STATE_DONE && in > 0) + return 0; + + dur += in; + *days = dur; + + return 1; +} + /* * Check if time is valid. */ -- cgit v1.2.3-54-g00ecf