changeset 21387:5241d95f5872

lib-settings: Added settings_get_time_msecs()
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Fri, 06 May 2016 21:43:04 +0300
parents aac8169ef34d
children a8fc5f34b8fa
files src/lib-settings/Makefile.am src/lib-settings/settings-parser.c src/lib-settings/settings-parser.h src/lib-settings/test-settings-parser.c
diffstat 4 files changed, 211 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-settings/Makefile.am	Tue Jan 10 14:45:20 2017 +0200
+++ b/src/lib-settings/Makefile.am	Fri May 06 21:43:04 2016 +0300
@@ -1,7 +1,8 @@
 noinst_LTLIBRARIES = libsettings.la
 
 AM_CPPFLAGS = \
-	-I$(top_srcdir)/src/lib
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-test
 
 libsettings_la_SOURCES = \
 	settings.c \
@@ -13,3 +14,23 @@
 
 pkginc_libdir=$(pkgincludedir)
 pkginc_lib_HEADERS = $(headers)
+
+test_programs = \
+	test-settings-parser
+
+noinst_PROGRAMS = $(test_programs)
+
+test_libs = \
+	libsettings.la \
+	../lib-test/libtest.la \
+	../lib/liblib.la
+
+test_settings_parser_SOURCES = test-settings-parser.c
+test_settings_parser_LDADD = $(test_libs)
+test_settings_parser_DEPENDENCIES = $(test_libs)
+
+check: check-am check-test
+check-test: all-am
+	for bin in $(test_programs); do \
+	  if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
+	done
--- a/src/lib-settings/settings-parser.c	Tue Jan 10 14:45:20 2017 +0200
+++ b/src/lib-settings/settings-parser.c	Fri May 06 21:43:04 2016 +0300
@@ -347,10 +347,10 @@
 	return 0;
 }
 
-int settings_get_time(const char *str, unsigned int *secs_r,
-		      const char **error_r)
+static int settings_get_time_full(const char *str, unsigned int *interval_r,
+				  bool milliseconds, const char **error_r)
 {
-	uintmax_t num, multiply = 1;
+	uintmax_t num, multiply = milliseconds ? 1000 : 1;
 	const char *p;
 
 	if (str_parse_uintmax(str, &num, &p) < 0) {
@@ -360,29 +360,47 @@
 	while (*p == ' ') p++;
 	switch (i_toupper(*p)) {
 	case 'S':
-		multiply = 1;
+		multiply *= 1;
 		if (strncasecmp(p, "secs", strlen(p)) == 0 ||
 		    strncasecmp(p, "seconds", strlen(p)) == 0)
 			p = "";
 		break;
 	case 'M':
-		multiply = 60;
+		multiply *= 60;
 		if (strncasecmp(p, "mins", strlen(p)) == 0 ||
 		    strncasecmp(p, "minutes", strlen(p)) == 0)
 			p = "";
+		else if (strncasecmp(p, "msecs", strlen(p)) == 0 ||
+			 strncasecmp(p, "mseconds", strlen(p)) == 0 ||
+			 strncasecmp(p, "millisecs", strlen(p)) == 0 ||
+			 strncasecmp(p, "milliseconds", strlen(p)) == 0) {
+			if (milliseconds || (num % 1000) == 0) {
+				if (!milliseconds) {
+					/* allow ms also for seconds, as long
+					   as it's divisible by seconds */
+					num /= 1000;
+				}
+				multiply = 1;
+				p = "";
+				break;
+			}
+			*error_r = t_strdup_printf(
+				"Milliseconds not supported for this setting: %s", str);
+			return -1;
+		}
 		break;
 	case 'H':
-		multiply = 60*60;
+		multiply *= 60*60;
 		if (strncasecmp(p, "hours", strlen(p)) == 0)
 			p = "";
 		break;
 	case 'D':
-		multiply = 60*60*24;
+		multiply *= 60*60*24;
 		if (strncasecmp(p, "days", strlen(p)) == 0)
 			p = "";
 		break;
 	case 'W':
-		multiply = 60*60*24*7;
+		multiply *= 60*60*24*7;
 		if (strncasecmp(p, "weeks", strlen(p)) == 0)
 			p = "";
 		break;
@@ -397,10 +415,22 @@
 				       str, NULL);
 		return -1;
 	}
-	*secs_r = num * multiply;
+	*interval_r = num * multiply;
 	return 0;
 }
 
+int settings_get_time(const char *str, unsigned int *secs_r,
+		      const char **error_r)
+{
+	return settings_get_time_full(str, secs_r, FALSE, error_r);
+}
+
+int settings_get_time_msecs(const char *str, unsigned int *msecs_r,
+			    const char **error_r)
+{
+	return settings_get_time_full(str, msecs_r, TRUE, error_r);
+}
+
 int settings_get_size(const char *str, uoff_t *bytes_r,
 		      const char **error_r)
 {
--- a/src/lib-settings/settings-parser.h	Tue Jan 10 14:45:20 2017 +0200
+++ b/src/lib-settings/settings-parser.h	Fri May 06 21:43:04 2016 +0300
@@ -240,6 +240,9 @@
 /* Parse time interval string, return as seconds. */
 int settings_get_time(const char *str, unsigned int *secs_r,
 		      const char **error_r);
+/* Parse time interval string, return as milliseconds. */
+int settings_get_time_msecs(const char *str, unsigned int *msecs_r,
+			    const char **error_r);
 /* Parse size string, return as bytes. */
 int settings_get_size(const char *str, uoff_t *bytes_r,
 		      const char **error_r);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-settings/test-settings-parser.c	Fri May 06 21:43:04 2016 +0300
@@ -0,0 +1,147 @@
+/* Copyright (c) 2009-2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "test-common.h"
+
+static void test_settings_get_time(void)
+{
+	struct {
+		const char *input;
+		unsigned int output;
+	} tests[] = {
+		{ "0", 0 },
+
+		{ "59s", 59 },
+		{ "59 s", 59 },
+		{ "59se", 59 },
+		{ "59sec", 59 },
+		{ "59secs", 59 },
+		{ "59seco", 59 },
+		{ "59secon", 59 },
+		{ "59second", 59 },
+		{ "59seconds", 59 },
+		{ "123456   seconds", 123456 },
+
+		{ "123m", 123*60 },
+		{ "123 m", 123*60 },
+		{ "123 mi", 123*60 },
+		{ "123 min", 123*60 },
+		{ "123 mins", 123*60 },
+		{ "123 minu", 123*60 },
+		{ "123 minut", 123*60 },
+		{ "123 minute", 123*60 },
+		{ "123 minutes", 123*60 },
+
+		{ "123h", 123*60*60 },
+		{ "123 h", 123*60*60 },
+		{ "123 ho", 123*60*60 },
+		{ "123 hou", 123*60*60 },
+		{ "123 hour", 123*60*60 },
+		{ "123 hours", 123*60*60 },
+
+		{ "12d", 12*60*60*24 },
+		{ "12 d", 12*60*60*24 },
+		{ "12 da", 12*60*60*24 },
+		{ "12 day", 12*60*60*24 },
+		{ "12 days", 12*60*60*24 },
+
+		{ "3w", 3*60*60*24*7 },
+		{ "3 w", 3*60*60*24*7 },
+		{ "3 we", 3*60*60*24*7 },
+		{ "3 wee", 3*60*60*24*7 },
+		{ "3 week", 3*60*60*24*7 },
+		{ "3 weeks", 3*60*60*24*7 },
+
+		{ "1000ms", 1 },
+		{ "50000ms", 50 },
+	};
+	struct {
+		const char *input;
+		unsigned int output;
+	} msecs_tests[] = {
+		{ "0ms", 0 },
+		{ "1ms", 1 },
+		{ "123456ms", 123456 },
+		{ "123456 ms", 123456 },
+		{ "123456mse", 123456 },
+		{ "123456msec", 123456 },
+		{ "123456msecs", 123456 },
+		{ "123456mseco", 123456 },
+		{ "123456msecon", 123456 },
+		{ "123456msecond", 123456 },
+		{ "123456mseconds", 123456 },
+		{ "123456mil", 123456 },
+		{ "123456mill", 123456 },
+		{ "123456milli", 123456 },
+		{ "123456millis", 123456 },
+		{ "123456millisec", 123456 },
+		{ "123456millisecs", 123456 },
+		{ "123456milliseco", 123456 },
+		{ "123456millisecon", 123456 },
+		{ "123456millisecond", 123456 },
+		{ "123456milliseconds", 123456 },
+		{ "4294967295 ms", 4294967295 },
+	};
+	const char *secs_errors[] = {
+		"-1",
+		/* wrong spellings: */
+		"1ss",
+		"1secss",
+		"1secondss",
+		"1ma",
+		"1minsa",
+		"1hu",
+		"1hoursa",
+		"1dd",
+		"1days?",
+		"1wa",
+		"1weeksb",
+
+		/* milliseconds: */
+		"1ms",
+		"999ms",
+		"1001ms",
+		/* overflows: */
+		"7102 w",
+		"4294967296 s",
+	};
+	const char *msecs_errors[] = {
+		"-1",
+		/* wrong spellings: */
+		"1mis",
+		"1mss",
+		/* overflows: */
+		"8 w",
+		"4294967296 ms",
+	};
+	unsigned int i, secs, msecs;
+	const char *error;
+
+	test_begin("settings_get_time()");
+	for (i = 0; i < N_ELEMENTS(tests); i++) {
+		test_assert_idx(settings_get_time(tests[i].input, &secs, &error) == 0, i);
+		test_assert_idx(secs == tests[i].output, i);
+
+		test_assert_idx(settings_get_time_msecs(tests[i].input, &msecs, &error) == 0, i);
+		test_assert_idx(msecs == tests[i].output*1000, i);
+	}
+	for (i = 0; i < N_ELEMENTS(msecs_tests); i++) {
+		test_assert_idx(settings_get_time_msecs(msecs_tests[i].input, &msecs, &error) == 0, i);
+		test_assert_idx(msecs == msecs_tests[i].output, i);
+	}
+	for (i = 0; i < N_ELEMENTS(secs_errors); i++)
+		test_assert_idx(settings_get_time(secs_errors[i], &secs, &error) < 0, i);
+	for (i = 0; i < N_ELEMENTS(msecs_errors); i++)
+		test_assert_idx(settings_get_time_msecs(msecs_errors[i], &msecs, &error) < 0, i);
+	test_end();
+}
+
+int main(void)
+{
+	static void (*test_functions[])(void) = {
+		test_settings_get_time,
+		NULL
+	};
+	return test_run(test_functions);
+}