Major overhaul of appointment/event input routines.

When setting start time for a new appointment, a date is disallowed
(in all other cases both time and date are still allowed).

Both date and time are displayed for the user to correct when an
appointment is changed or moved.

Built-in help in the status bar for display of input formats.

Several bug fixes that resulted in data inconsistencies (end time
before start time).

The routines use the enhanced parsing funtions to validate input:
ui_day_item_add(void)
	parse_datetime()
	parse_duration()
	parse_datetime()
ui_day_item_repeat(void)
	parse_date_duration()
	parse_date()
ui_day_item_edit(void)
	update_start_time()
		day_edit_time()
			parse_datetime()
	update_duration()
		parse_duration()
		parse_datetime()
	update_rept()
		parse_date_duration()
		parse_date()

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
This commit is contained in:
Lars Henriksen 2017-11-06 19:54:55 +01:00 committed by Lukas Fleischer
parent fd635150a8
commit 223810cf0e

View File

@ -58,109 +58,151 @@ void ui_day_set_selitem(struct day_item *day)
ui_day_set_selitem_by_aptev_ptr(day->item); ui_day_set_selitem_by_aptev_ptr(day->item);
} }
/* Request the user to enter a new time. */ /*
static int day_edit_time(int time) * Request the user to enter a new start time.
* Input: start time and duration in seconds.
* Output: return value is new start time.
* If move = 1, the new start time is for a move, and duration is passed on
* for validation of the new end time.
* If move = 0, the new end time is calculated by the caller.
*/
static int day_edit_time(int start, int duration, int move)
{ {
char *input = date_sec2date_str(time, "%H:%M"); const char *msg_time = _("Enter start date [%s] and/or time ([hh:mm] or [hhmm]):");
const char *msg_time =
_("Enter start time ([hh:mm] or [hhmm]) or date:");
const char *enter_str = _("Press [Enter] to continue"); const char *enter_str = _("Press [Enter] to continue");
const char *fmt_msg = const char *fmt_msg = _("Invalid date or time.");
_("You entered an invalid time, should be [hh:mm] or [hhmm]"); char *input, *outstr;
long ts = time; time_t ts;
int ret;
asprintf(&outstr, "%s %s", DATEFMT(conf.input_datefmt), "%H:%M");
input = date_sec2date_str(start, outstr);
mem_free(outstr);
for (;;) { for (;;) {
status_mesg(msg_time, ""); asprintf(&outstr, msg_time, DATEFMT_DESC(conf.input_datefmt));
if (updatestring(win[STA].p, &input, 0, 1) != GETSTRING_VALID) status_mesg(outstr, "");
return 0; mem_free(outstr);
if (parse_datetime(input, &ts)) if (updatestring(win[STA].p, &input, 0, 1) != GETSTRING_VALID) {
return ts; ret = 0;
break;
}
ts = start;
if (parse_datetime(input, &ts, move ? duration : 0)) {
ret = ts;
break;
}
status_mesg(fmt_msg, enter_str); status_mesg(fmt_msg, enter_str);
keys_wait_for_any_key(win[KEY].p); keys_wait_for_any_key(win[KEY].p);
} }
mem_free(input);
return ret;
} }
/* Request the user to enter a new end time or duration. */ /*
static void update_start_time(long *start, long *dur, int update_dur) * Change start time or move an item.
* Input/output: start and dur.
* If move = 0, end time is fixed, and the new duration is calculated
* when the new start time is known.
* If move = 1, duration is fixed, but passed on for validation of new end time.
*/
static void update_start_time(long *start, long *dur, int move)
{ {
long newtime; long newtime;
int valid_date;
const char *msg_wrong_time = const char *msg_wrong_time =
_("Invalid time: start time must be before end time!"); _("Invalid time: start time must come before end time!");
const char *msg_enter = _("Press [Enter] to continue"); const char *msg_enter = _("Press [Enter] to continue");
do { for (;;) {
newtime = day_edit_time(*start); newtime = day_edit_time(*start, *dur, move);
if (!newtime) if (!newtime)
break; break;
if (!update_dur) { if (move) {
*start = newtime; *start = newtime;
return; break;
}
if (newtime < *start + *dur) {
*dur -= (newtime - *start);
*start = newtime;
valid_date = 1;
} else { } else {
status_mesg(msg_wrong_time, msg_enter); if (newtime <= *start + *dur) {
keys_wait_for_any_key(win[KEY].p); *dur -= (newtime - *start);
valid_date = 0; *start = newtime;
break;
}
} }
status_mesg(msg_wrong_time, msg_enter);
keys_wgetch(win[KEY].p);
} }
while (valid_date == 0); return;
} }
/* Request the user to enter a new end time or duration. */ /* Request the user to enter a new end time or duration. */
static void update_duration(long *start, long *dur) static void update_duration(long *start, long *dur)
{ {
char *timestr;
const char *msg_time = const char *msg_time =
_("Enter end time ([hh:mm], [hhmm]) or duration ([+hh:mm], [+xxxdxxhxxm]):"); _("Enter end date (and/or time) or duration ('?' for input formats):");
const char *msg_help_1 =
_("Date: %s, year or month may be omitted.");
const char *msg_help_2 =
_("Time: hh:mm (hh: or :mm) or hhmm. Duration: +mm, +hh:mm, +??d??h??m.");
const char *enter_str = _("Press [Enter] to continue"); const char *enter_str = _("Press [Enter] to continue");
const char *fmt_msg = const char *fmt_msg_1 = _("Invalid time or duration.");
_("You entered an invalid time, should be [hh:mm] or [hhmm]"); const char *fmt_msg_2 = _("Invalid date: end time must come after start time.");
long end; long end;
unsigned newdur; unsigned newdur;
char *timestr, *outstr;
if (*dur > 0) if (*dur > 0) {
timestr = date_sec2date_str(*start + *dur, "%H:%M"); end = *start + *dur;
else asprintf(&outstr, "%s %s", DATEFMT(conf.input_datefmt), "%H:%M");
timestr = date_sec2date_str(end, outstr);
mem_free(outstr);
} else {
timestr = mem_strdup(""); timestr = mem_strdup("");
}
for (;;) { for (;;) {
int ret; int ret, early = 0;
status_mesg(msg_time, ""); status_mesg(msg_time, "");
ret = updatestring(win[STA].p, &timestr, 0, 1); ret = updatestring(win[STA].p, &timestr, 0, 1);
if (ret == GETSTRING_ESC) if (ret == GETSTRING_ESC)
return; return;
if (*(timestr + strlen(timestr) - 1) == '?') {
asprintf(&outstr, "%s %s", DATEFMT(conf.input_datefmt), "%H:%M");
timestr = date_sec2date_str(end, outstr);
asprintf(&outstr, msg_help_1, DATEFMT_DESC(conf.input_datefmt));
status_mesg(outstr, msg_help_2);
mem_free(outstr);
keys_wgetch(win[KEY].p);
continue;
}
if (ret == GETSTRING_RET) { if (ret == GETSTRING_RET) {
newdur = 0; newdur = 0;
break; break;
} }
if (*timestr == '+') { if (*timestr == '+') {
if (parse_duration(timestr + 1, &newdur)) { if (parse_duration(timestr + 1, &newdur, *start)) {
newdur *= MININSEC; newdur *= MININSEC;
break; break;
} }
} else {
int val = 1;
ret = parse_datetime(timestr, &end, 0);
/*
* If same day and end time is earlier than start time,
* assume that it belongs to the next day.
*/
if (ret == PARSE_DATETIME_HAS_TIME && end < *start) {
end = date_sec_change(end, 0, 1);
/* Still valid? */
val = check_sec(&end);
}
if (ret && val && *start <= end) {
newdur = end - *start;
break;
}
/* Valid format, but too early? */
early = ret && val && end < *start;
} }
end = *start + *dur; status_mesg(early ? fmt_msg_2 : fmt_msg_1 , enter_str);
ret = parse_datetime(timestr, &end); keys_wgetch(win[KEY].p);
/*
* If the user enters a end time which is smaller than
* the start time, assume that the time belongs to the
* next day.
*/
if (!(ret & PARSE_DATETIME_HAS_DATE) && end < *start)
end = date_sec_change(end, 0, 1);
if (ret) {
newdur = end - *start;
break;
}
status_mesg(fmt_msg, enter_str);
wgetch(win[KEY].p);
} }
mem_free(timestr); mem_free(timestr);
*dur = newdur; *dur = newdur;
} }
@ -173,9 +215,8 @@ static void update_desc(char **desc)
static void update_rept(struct rpt **rpt, const long start) static void update_rept(struct rpt **rpt, const long start)
{ {
int newtype, newfreq; /* Update repetition type. */
time_t newuntil; int newtype;
char *freqstr = NULL, *timstr = NULL, *outstr;
const char *msg_rpt_prefix = _("Enter the new repetition type:"); const char *msg_rpt_prefix = _("Enter the new repetition type:");
const char *msg_rpt_daily = _("(d)aily"); const char *msg_rpt_daily = _("(d)aily");
const char *msg_rpt_weekly = _("(w)eekly"); const char *msg_rpt_weekly = _("(w)eekly");
@ -202,23 +243,12 @@ static void update_rept(struct rpt **rpt, const long start)
/* NOTREACHED, but makes the compiler happier. */ /* NOTREACHED, but makes the compiler happier. */
rpt_current = msg_rpt_daily; rpt_current = msg_rpt_daily;
} }
asprintf(&msg_rpt_current, _("(currently using %s)"), rpt_current); asprintf(&msg_rpt_current, _("(currently using %s)"), rpt_current);
char *msg_rpt_asktype; char *msg_rpt_asktype;
asprintf(&msg_rpt_asktype, "%s %s, %s, %s, %s ? %s", msg_rpt_prefix, asprintf(&msg_rpt_asktype, "%s %s, %s, %s, %s? %s", msg_rpt_prefix,
msg_rpt_daily, msg_rpt_weekly, msg_rpt_monthly, msg_rpt_daily, msg_rpt_weekly, msg_rpt_monthly,
msg_rpt_yearly, msg_rpt_current); msg_rpt_yearly, msg_rpt_current);
const char *msg_rpt_choice = _("[dwmy]"); const char *msg_rpt_choice = _("[dwmy]");
const char *msg_wrong_freq = _("Invalid frequency.");
const char *msg_wrong_time =
_("Invalid time: start time must be before end time!");
const char *msg_wrong_date = _("The entered date is not valid.");
const char *msg_fmts =
_("Possible formats are [%s] or '0' for an endless repetition.");
const char *msg_enter = _("Press [Enter] to continue");
switch (status_ask_choice(msg_rpt_asktype, msg_rpt_choice, 4)) { switch (status_ask_choice(msg_rpt_asktype, msg_rpt_choice, 4)) {
case 1: case 1:
newtype = 'D'; newtype = 'D';
@ -236,8 +266,13 @@ static void update_rept(struct rpt **rpt, const long start)
goto cleanup; goto cleanup;
} }
/* Update frequency. */
int newfreq;
char *freqstr = NULL;
const char *msg_wrong_freq = _("Invalid frequency.");
const char *msg_enter = _("Press [Enter] to continue");
do { do {
status_mesg(_("Enter the new repetition frequency:"), ""); status_mesg(_("Enter the repetition frequency:"), "");
mem_free(freqstr); mem_free(freqstr);
asprintf(&freqstr, "%d", (*rpt)->freq); asprintf(&freqstr, "%d", (*rpt)->freq);
if (updatestring(win[STA].p, &freqstr, 0, 1) != if (updatestring(win[STA].p, &freqstr, 0, 1) !=
@ -252,65 +287,67 @@ static void update_rept(struct rpt **rpt, const long start)
} }
while (newfreq == 0); while (newfreq == 0);
for (;;) { /* Update end date. */
struct tm lt; char *timstr = NULL;
time_t t; char *outstr = NULL;
struct date new_date; time_t newuntil;
int newmonth, newday, newyear; const char *msg_until_1 =
unsigned days; _("Enter end date or duration ('?' for input formats):");
const char *msg_help_1 =
_("Date: %s (year or month may be omitted). Endless duration: 0.");
const char *msg_help_2 =
_("Duration in days: +dd. Duration in weeks and days: +??w??d.");
const char *msg_wrong_time =
_("Invalid date: end date must come after start date (%s).");
const char *msg_wrong_date = _("Invalid date.");
asprintf(&outstr, for (;;) {
_("Enter end date ([%s]), duration ([+xxwxxd]) or '0':"), mem_free(timstr);
DATEFMT_DESC(conf.input_datefmt)); timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt));
status_mesg(outstr, ""); status_mesg(msg_until_1, "");
mem_free(outstr);
timstr =
date_sec2date_str((*rpt)->until,
DATEFMT(conf.input_datefmt));
if (updatestring(win[STA].p, &timstr, 0, 1) != if (updatestring(win[STA].p, &timstr, 0, 1) !=
GETSTRING_VALID) { GETSTRING_VALID) {
goto cleanup; goto cleanup;
} }
if (*(timstr + strlen(timstr) - 1) == '?') {
mem_free(outstr);
asprintf(&outstr, msg_help_1, DATEFMT_DESC(conf.input_datefmt));
status_mesg(outstr, msg_help_2);
keys_wgetch(win[KEY].p);
continue;
}
if (strcmp(timstr, "0") == 0) { if (strcmp(timstr, "0") == 0) {
newuntil = 0; newuntil = 0;
break; break;
} }
if (*timstr == '+') { if (*timstr == '+') {
if (!parse_date_duration(timstr + 1, &days)) { unsigned days;
asprintf(&outstr, msg_fmts, if (!parse_date_duration(timstr + 1, &days, start)) {
DATEFMT_DESC(conf.input_datefmt)); status_mesg(msg_wrong_date, msg_enter);
status_mesg(msg_wrong_date, outstr); keys_wgetch(win[KEY].p);
mem_free(outstr);
keys_wait_for_any_key(win[KEY].p);
continue; continue;
} }
t = start; newuntil = start + days * DAYINSEC;
localtime_r(&t, &lt);
date_change(&lt, 0, days);
new_date.dd = lt.tm_mday;
new_date.mm = lt.tm_mon + 1;
new_date.yyyy = lt.tm_year + 1900;
} else { } else {
if (!parse_date_interactive(timstr, &newyear, int year, month, day;
&newmonth, &newday)) { if (!parse_date(timstr, conf.input_datefmt, &year,
asprintf(&outstr, msg_fmts, &month, &day, ui_calendar_get_slctd_day())) {
DATEFMT_DESC(conf.input_datefmt)); status_mesg(msg_wrong_date, msg_enter);
status_mesg(msg_wrong_date, outstr); keys_wgetch(win[KEY].p);
mem_free(outstr);
keys_wait_for_any_key(win[KEY].p);
continue; continue;
} }
t = start; struct date d = { day, month, year };
localtime_r(&t, &lt); newuntil = date2sec(d, 0, 0);
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) if (newuntil >= start)
break; break;
status_mesg(msg_wrong_time, msg_enter);
keys_wait_for_any_key(win[KEY].p); mem_free(timstr);
mem_free(outstr);
timstr = date_sec2date_str(start, DATEFMT(conf.input_datefmt));
asprintf(&outstr, msg_wrong_time, timstr);
status_mesg(outstr, msg_enter);
keys_wgetch(win[KEY].p);
} }
(*rpt)->type = recur_char2def(newtype); (*rpt)->type = recur_char2def(newtype);
@ -322,6 +359,7 @@ cleanup:
mem_free(msg_rpt_asktype); mem_free(msg_rpt_asktype);
mem_free(freqstr); mem_free(freqstr);
mem_free(timstr); mem_free(timstr);
mem_free(outstr);
} }
/* Edit an already existing item. */ /* Edit an already existing item. */
@ -377,7 +415,7 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_recur_appt, 5)) { (_("Edit: "), choice_recur_appt, 5)) {
case 1: case 1:
need_check_notify = 1; need_check_notify = 1;
update_start_time(&ra->start, &ra->dur, ra->dur > 0); update_start_time(&ra->start, &ra->dur, ra->dur == 0);
io_set_modified(); io_set_modified();
break; break;
case 2: case 2:
@ -398,7 +436,7 @@ void ui_day_item_edit(void)
break; break;
case 5: case 5:
need_check_notify = 1; need_check_notify = 1;
update_start_time(&ra->start, &ra->dur, 0); update_start_time(&ra->start, &ra->dur, 1);
io_set_modified(); io_set_modified();
break; break;
default: default:
@ -417,7 +455,7 @@ void ui_day_item_edit(void)
(_("Edit: "), choice_appt, 4)) { (_("Edit: "), choice_appt, 4)) {
case 1: case 1:
need_check_notify = 1; need_check_notify = 1;
update_start_time(&a->start, &a->dur, a->dur > 0); update_start_time(&a->start, &a->dur, a->dur == 0);
io_set_modified(); io_set_modified();
break; break;
case 2: case 2:
@ -433,7 +471,7 @@ void ui_day_item_edit(void)
break; break;
case 4: case 4:
need_check_notify = 1; need_check_notify = 1;
update_start_time(&a->start, &a->dur, 0); update_start_time(&a->start, &a->dur, 1);
io_set_modified(); io_set_modified();
break; break;
default: default:
@ -507,19 +545,21 @@ void ui_day_item_add(void)
const char *mesg_1 = const char *mesg_1 =
_("Enter start time ([hh:mm] or [hhmm]), leave blank for an all-day event:"); _("Enter start time ([hh:mm] or [hhmm]), leave blank for an all-day event:");
const char *mesg_2 = const char *mesg_2 =
_("Enter end time ([hh:mm], [hhmm]) or duration ([+hh:mm], [+xxxdxxhxxm]):"); _("Enter end time as date (and/or time) or duration ('?' for input formats):");
const char *mesg_3 = _("Enter description:"); const char *mesg_3 = _("Enter description:");
const char *format_message_1 = const char *mesg_help_1 =
_("You entered an invalid start time, should be [hh:mm] or [hhmm]"); _("Date: %s (and/or time), year or month may be omitted.");
const char *format_message_2 = const char *mesg_help_2 =
_("Invalid end time/duration, should be [hh:mm], [hhmm], [+hh:mm], [+xxxdxxhxxm] or [+mm]"); _("Time: hh:mm (hh: or :mm) or hhmm. Duration: +mm, +hh:mm, +??d??h??m.");
const char *format_message_1 = _("Invalid start time.");
const char *format_message_2 = _("Invalid time or duration.");
const char *format_message_3 = _("Invalid date: end time must come after start time.");
const char *enter_str = _("Press [Enter] to continue"); const char *enter_str = _("Press [Enter] to continue");
char item_time[LDUR] = ""; char item_time[LDUR] = "";
char item_mesg[BUFSIZ] = ""; char item_mesg[BUFSIZ] = "";
time_t start = date2sec(*ui_calendar_get_slctd_day(), 0, 0), end; time_t start = date2sec(*ui_calendar_get_slctd_day(), 0, 0), end, saved = start;
unsigned dur; unsigned dur;
int is_appointment = 1; int is_appointment = 1;
int ret;
union aptev_ptr item; union aptev_ptr item;
/* Get the starting time */ /* Get the starting time */
@ -532,9 +572,10 @@ void ui_day_item_add(void)
is_appointment = 0; is_appointment = 0;
break; break;
} }
if ((ret = parse_datetime(item_time, &start))) { start = saved;
if (!(ret & PARSE_DATETIME_HAS_TIME)) /* Only time, no date, allowed. */
is_appointment = 0; if (parse_datetime(item_time, &start, 0) ==
PARSE_DATETIME_HAS_TIME) {
break; break;
} }
status_mesg(format_message_1, enter_str); status_mesg(format_message_1, enter_str);
@ -549,42 +590,58 @@ void ui_day_item_add(void)
if (is_appointment) { /* Get the appointment duration */ if (is_appointment) { /* Get the appointment duration */
item_time[0] = '\0'; item_time[0] = '\0';
for (;;) { for (;;) {
int early = 0;
status_mesg(mesg_2, ""); status_mesg(mesg_2, "");
if (getstring(win[STA].p, item_time, LDUR, 0, 1) == if (getstring(win[STA].p, item_time, LDUR, 0, 1) ==
GETSTRING_ESC) GETSTRING_ESC)
return; return;
if (*item_time == '?') {
char *outstr;
item_time[0] = '\0';
asprintf(&outstr, mesg_help_1, DATEFMT_DESC(conf.input_datefmt));
status_mesg(outstr, mesg_help_2);
mem_free(outstr);
wgetch(win[KEY].p);
continue;
}
if (strlen(item_time) == 0) { if (strlen(item_time) == 0) {
dur = 0; dur = 0;
break; break;
} }
if (*item_time == '+') { if (*item_time == '+') {
if (parse_duration(item_time + 1, &dur)) { if (parse_duration(item_time + 1, &dur, start)) {
dur *= MININSEC; dur *= MININSEC;
break; break;
} }
} else {
int ret, val = 1;
/* Same day? */
end = start;
ret = parse_datetime(item_time, &end, 0);
/*
* If same day and end time is earlier than start time,
* assume that it belongs to the next day.
*/
if (ret == PARSE_DATETIME_HAS_TIME && end < start) {
end = date_sec_change(end, 0, 1);
/* Still valid? */
val = check_sec(&end);
}
if (ret && val && start <= end) {
dur = end - start;
break;
}
/* Valid format, but too early? */
early = ret && val && end < start;
} }
end = start; status_mesg(early ? format_message_3 : format_message_2 , enter_str);
ret = parse_datetime(item_time, &end); keys_wgetch(win[KEY].p);
/*
* If the user enters no date and an end time which is
* smaller than the start time, assume that the time
* belongs to the next day.
*/
if (!(ret & PARSE_DATETIME_HAS_DATE) && end < start)
end = date_sec_change(end, 0, 1);
/* Always check that the end comes after the start. */
if (ret && start <= end) {
dur = end - start;
break;
}
status_mesg(format_message_2, enter_str);
keys_wait_for_any_key(win[KEY].p);
} }
} }
status_mesg(mesg_3, ""); status_mesg(mesg_3, "");
if (getstring(win[STA].p, item_mesg, BUFSIZ, 0, 1) == if (getstring(win[STA].p, item_mesg, BUFSIZ, 0, 1) == GETSTRING_VALID) {
GETSTRING_VALID) {
if (is_appointment) { if (is_appointment) {
item.apt = apoint_new(item_mesg, 0L, start, dur, 0L); item.apt = apoint_new(item_mesg, 0L, start, dur, 0L);
if (notify_bar()) if (notify_bar())
@ -680,11 +737,6 @@ void ui_day_item_delete(unsigned reg)
*/ */
void ui_day_item_repeat(void) void ui_day_item_repeat(void)
{ {
struct tm lt;
time_t t;
int year = 0, month = 0, day = 0;
struct date until_date;
char *outstr;
char user_input[BUFSIZ] = ""; char user_input[BUFSIZ] = "";
const char *msg_rpt_prefix = _("Enter the repetition type:"); const char *msg_rpt_prefix = _("Enter the repetition type:");
const char *msg_rpt_daily = _("(d)aily"); const char *msg_rpt_daily = _("(d)aily");
@ -694,16 +746,14 @@ void ui_day_item_repeat(void)
const char *msg_type_choice = _("[dwmy]"); const char *msg_type_choice = _("[dwmy]");
const char *mesg_freq_1 = _("Enter the repetition frequency:"); const char *mesg_freq_1 = _("Enter the repetition frequency:");
const char *mesg_wrong_freq = _("Invalid frequency."); const char *mesg_wrong_freq = _("Invalid frequency.");
const char *mesg_until_1 = const char *mesg_until_1 = _("Enter end date or duration ('?' for input formats):");
_("Enter end date ([%s]), duration ([+xxwxxd]) or '0' for endless repetition:"); const char *mesg_help_1 = _("Date: %s (year or month may be omitted). Endless duration: '0'.");
const char *mesg_wrong_1 = _("The entered date is not valid."); const char *mesg_help_2 = _("Duration in days: +dd. Duration in weeks and days: +??w??d.");
const char *mesg_wrong_2 = const char *mesg_wrong_1 = _("Invalid date.");
_("Possible formats are [%s], [+xxwxxd] or '0'."); const char *mesg_wrong_2 = _("Press [ENTER] to continue.");
const char *wrong_type_1 = const char *wrong_type_1 = _("This item is already a repeated one.");
_("This item is already a repeated one.");
const char *wrong_type_2 = _("Press [ENTER] to continue."); const char *wrong_type_2 = _("Press [ENTER] to continue.");
const char *mesg_older = const char *mesg_older = _("Invalid date: end date must come after start date (%s).");
_("Sorry, the date you entered is older than the item start time.");
char *msg_asktype; char *msg_asktype;
asprintf(&msg_asktype, "%s %s, %s, %s, %s", msg_rpt_prefix, asprintf(&msg_asktype, "%s %s, %s, %s, %s", msg_rpt_prefix,
@ -758,54 +808,51 @@ void ui_day_item_repeat(void)
user_input[0] = '\0'; user_input[0] = '\0';
} }
char *outstr, *datestr;
for (;;) { for (;;) {
asprintf(&outstr, mesg_until_1, status_mesg(mesg_until_1, "");
DATEFMT_DESC(conf.input_datefmt));
status_mesg(outstr, "");
mem_free(outstr);
if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) != if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) !=
GETSTRING_VALID) GETSTRING_VALID)
goto cleanup; goto cleanup;
if (strlen(user_input) == 1 && strcmp(user_input, "0") == 0) { if (*user_input == '?') {
user_input[0] = '\0';
asprintf(&outstr, mesg_help_1, DATEFMT_DESC(conf.input_datefmt));
status_mesg(outstr, mesg_help_2);
mem_free(outstr);
wgetch(win[KEY].p);
continue;
}
if (strcmp(user_input, "0") == 0) {
until = 0; until = 0;
break; break;
} }
if (*user_input == '+') { if (*user_input == '+') {
if (!parse_date_duration(user_input + 1, &days)) { if (!parse_date_duration(user_input + 1, &days, p->start)) {
asprintf(&outstr, mesg_wrong_2, status_mesg(mesg_wrong_1, mesg_wrong_2);
DATEFMT_DESC(conf.input_datefmt)); keys_wgetch(win[KEY].p);
status_mesg(mesg_wrong_1, outstr);
mem_free(outstr);
keys_wait_for_any_key(win[KEY].p);
continue; continue;
} }
t = p->start; until = p->start + days * DAYINSEC;
localtime_r(&t, &lt);
date_change(&lt, 0, days);
until_date.dd = lt.tm_mday;
until_date.mm = lt.tm_mon + 1;
until_date.yyyy = lt.tm_year + 1900;
} else if (parse_date_interactive(user_input, &year, &month,
&day)) {
t = p->start;
localtime_r(&t, &lt);
until_date.dd = day;
until_date.mm = month;
until_date.yyyy = year;
} else { } else {
asprintf(&outstr, mesg_wrong_2, int year, month, day;
DATEFMT_DESC(conf.input_datefmt)); if (!parse_date(user_input, conf.input_datefmt,
status_mesg(mesg_wrong_1, outstr); &year, &month, &day, ui_calendar_get_slctd_day())) {
mem_free(outstr); status_mesg(mesg_wrong_1, mesg_wrong_2);
keys_wait_for_any_key(win[KEY].p); keys_wgetch(win[KEY].p);
continue; continue;
}
struct date d = { day, month, year };
until = date2sec(d, 0, 0);
} }
until = date2sec(until_date, lt.tm_hour, lt.tm_min);
if (until >= p->start) if (until >= p->start)
break; break;
status_mesg(mesg_older, wrong_type_2);
keys_wait_for_any_key(win[KEY].p); datestr = date_sec2date_str(p->start, DATEFMT(conf.input_datefmt));
asprintf(&outstr, mesg_older, datestr);
status_mesg(outstr, wrong_type_2);
mem_free(datestr);
mem_free(outstr);
keys_wgetch(win[KEY].p);
} }
date = ui_calendar_get_slctd_day_sec(); date = ui_calendar_get_slctd_day_sec();