From 4ff3bb9d4fc5dcd38c4129b8e83b543212ee2a49 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Wed, 5 Oct 2011 11:19:17 +0200 Subject: src/utils.c: Support more powerful duration strings Add support for "1d23h42m"-like duration strings to parse_duration(). Also, switch to using a deterministic finite automaton to parse duration strings, as things tend to get unclear. Signed-off-by: Lukas Fleischer --- src/utils.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 23 deletions(-) (limited to 'src/utils.c') diff --git a/src/utils.c b/src/utils.c index ce4b0f7..2501d9b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -696,50 +696,127 @@ parse_time (const char *string, unsigned *hour, unsigned *minute) } /* - * Converts a duration string into minutes. Both "hh:mm" (e.g. "23:42") and - * "mmm" (e.g. "100") formats are accepted. Short forms like "23:" (23:00) or - * ":45" (0:45) are allowed as well. + * Converts a duration string into minutes. + * + * Allowed formats (noted as regular expressions): + * + * - \d*:\d* + * - (\d*m|\d*h(|\d*m)|\d*d(|\d*m|\d*h(|\d*m))) + * - \d+ + * + * "\d" is used as a placeholder for "(0|1|2|3|4|5|6|7|8|9)". + * + * Note that this function performs an additional range check on each token to + * ensure we do not accept semantically invalid strings such as "42:23". * * Returns 1 on success and 0 on failure. */ int parse_duration (const char *string, unsigned *duration) { + enum { + STATE_INITIAL, + STATE_HHMM_MM, + STATE_DDHHMM_HH, + STATE_DDHHMM_MM, + STATE_DONE + } state = STATE_INITIAL; + const char *p; - unsigned in[2] = {0, 0}, n = 0; + unsigned in = 0; + unsigned dur = 0; if (!string || *string == '\0') return 0; - /* parse string into in[], read up to two integers */ + /* parse string using a simple state machine */ for (p = string; *p; p++) { - if (*p == ':') + if ((*p >= '0') && (*p <= '9')) { - if ((++n) > 1) + if (state == STATE_DONE) return 0; + else + in = in * 10 + (int)(*p - '0'); } - else if ((*p >= '0') && (*p <= '9')) - in[n] = in[n] * 10 + (int)(*p - '0'); else - return 0; - } + { + switch (state) + { + case STATE_INITIAL: + if (*p == ':') + { + dur += in * HOURINMIN; + state = STATE_HHMM_MM; + } + else if (*p == 'd') + { + dur += in * DAYINMIN; + state = STATE_DDHHMM_HH; + } + else if (*p == 'h') + { + if (in >= DAYINHOURS) + return 0; + dur += in * HOURINMIN; + state = STATE_DDHHMM_MM; + } + else if (*p == 'm') + { + if (in >= HOURINMIN) + return 0; + dur += in; + state = STATE_DONE; + } + else + return 0; + break; + case STATE_DDHHMM_HH: + if (*p == 'h') + { + if (in >= DAYINHOURS) + return 0; + dur += in * HOURINMIN; + state = STATE_DDHHMM_MM; + } + else if (*p == 'm') + { + if (in >= HOURINMIN) + return 0; + dur += in; + state = STATE_DONE; + } + else + return 0; + break; + case STATE_DDHHMM_MM: + if (*p == 'm') + { + if (in >= HOURINMIN) + return 0; + dur += in; + state = STATE_DONE; + } + else + return 0; + break; + case STATE_HHMM_MM: + case STATE_DONE: + return 0; + break; + } - if (n == 0) - { - if (in[0] > 999) - return 0; - *duration = in[0]; - } - else if (n == 1) - { - if (in[0] > DAYINHOURS || in[1] >= HOURINMIN) - return 0; - *duration = in[0] * HOURINMIN + in[1]; + in = 0; + } } - else + + if ((state == STATE_HHMM_MM && in >= HOURINMIN) || + ((state == STATE_DDHHMM_HH || state == STATE_DDHHMM_MM) && in > 0)) return 0; + dur += in; + *duration = dur; + return 1; } -- cgit v1.2.3-54-g00ecf