modules/tmrec/period.c
18107462
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include <sys/time.h>
 #include <stdlib.h>
 
 enum scales {
 	SCALE_YEAR = 0,
 	SCALE_MONTH,
 	SCALE_WEEK,
 	SCALE_MDAY,
 	SCALE_WDAY,
 	SCALE_YDAY,
 	SCALE_HOUR,
 	SCALE_MINUTE,
 	SCALE_SECOND,
 
 	SCALE_MAX
 };
 
 #define MAX_CODES 2
 #define MAX_VALUE_LEN 16 /* for spelled out month names, longest is "September" */
 
 typedef int (*scale_match_func)(const int time_var, const char *from, const char *to);
 
 struct scale_definition {
 	scale_match_func	func;
 	const char		*codes[MAX_CODES];
 	int			flags;
 };
 
 #define FLAG_INTEGER_ARGS		0x1
 
 static int year_fn(const int time_var, const char *from, const char *to);
 static int month_fn(const int time_var, const char *from, const char *to);
 static int week_fn(const int time_var, const char *from, const char *to);
 static int mday_fn(const int time_var, const char *from, const char *to);
 static int wday_fn(const int time_var, const char *from, const char *to);
 static int yday_fn(const int time_var, const char *from, const char *to);
 static int hour_fn(const int time_var, const char *from, const char *to);
 static int minute_fn(const int time_var, const char *from, const char *to);
 static int second_fn(const int time_var, const char *from, const char *to);
 
 static const struct scale_definition defs[SCALE_MAX + 1] = {
 	[SCALE_YEAR  ] = { year_fn,   { "year",   "yr"  }, FLAG_INTEGER_ARGS },
 	[SCALE_MONTH ] = { month_fn,  { "month",  "mo"  }, 0                 },
 	[SCALE_WEEK  ] = { week_fn,   { "week",   "wk"  }, FLAG_INTEGER_ARGS },	/* week of the month */
 	[SCALE_MDAY  ] = { mday_fn,   { "mday",   "md"  }, FLAG_INTEGER_ARGS },	/* day of the month */
 	[SCALE_WDAY  ] = { wday_fn,   { "wday",   "wd"  }, 0                 },	/* day of the week */
 	[SCALE_YDAY  ] = { yday_fn,   { "yday",   "yd"  }, FLAG_INTEGER_ARGS },	/* day of the year */
 	[SCALE_HOUR  ] = { hour_fn,   { "hour",   "hr"  }, FLAG_INTEGER_ARGS },
 	[SCALE_MINUTE] = { minute_fn, { "minute", "min" }, FLAG_INTEGER_ARGS },
 	[SCALE_SECOND] = { second_fn, { "second", "sec" }, FLAG_INTEGER_ARGS },
 	/* XXX week of the year? */
 
 	[SCALE_MAX   ] = { NULL, { NULL, } },
 };
 
 static const char *months[12] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug",
 	"sep", "oct", "nov", "dec" };
 static const char *weekdays[7] = { "su", "mo", "tu", "we", "th", "fr", "sa" };
 
 static void get_time_vars(int time_vars[SCALE_MAX], time_t t) {
 	struct tm tm;
 
 	localtime_r(&t, &tm);
 
 	time_vars[SCALE_YEAR]   = tm.tm_year + 1900;
 	time_vars[SCALE_MONTH]  = tm.tm_mon + 1;
 	time_vars[SCALE_WEEK]   = (tm.tm_mday - 1 + (tm.tm_wday - tm.tm_mday + 1) % 7) / 7 + 1;
 	time_vars[SCALE_MDAY]   = tm.tm_mday;
5d03575b
 	time_vars[SCALE_WDAY]   = tm.tm_wday + 1;
18107462
 	time_vars[SCALE_YDAY]   = tm.tm_yday + 1;
 	time_vars[SCALE_HOUR]   = tm.tm_hour;
 	time_vars[SCALE_MINUTE] = tm.tm_min;
 	time_vars[SCALE_SECOND] = tm.tm_sec;
 }
 
 #define WS_SKIP() while (*p == ' ') p++;
 
 int in_period(time_t t, const char *p) {
 	int time_vars[SCALE_MAX];
 	int scale_results[SCALE_MAX];
 	int scale, j, len, res;
 	const char *c, *a1, *a2;
 	char from[MAX_VALUE_LEN], to[MAX_VALUE_LEN], *str;
 
 	/* If no period is given or string is empty, the time always matches */
 	if (!p)
 		return 1;
 	WS_SKIP();
 	if (!*p)
 		return 1;
 
 	/* If "none" or "never" is given, time never matches */
 	if (!strcasecmp(p, "none") || !strcasecmp(p, "never"))
 		return 0;
 
 	get_time_vars(time_vars, t);
 
 	/* Loop through all sub-periods, separated by commas.
 	   string :=  PERIOD [, PERIOD ... ] */
 	while (1) {
 		memset(scale_results, -1, sizeof(scale_results));
 
 		/* Each sub-period consists of one or more scales, separated by spaces.
 		   PERIOD := SCALE { VALUES } [ SCALE { VALUES } ... ] */
 		while (1) {
 			/* XXX could do some hashing here */
 			for (scale = 0; scale < SCALE_MAX; scale++) {
 				for (j = 0; j < MAX_CODES; j++) {
 					c = defs[scale].codes[j];
 					len = strlen(c);
 					if (strncasecmp(p, c, len))
 						continue;
 					if (p[len] == ' ' || p[len] == '{')
 						goto found_scale;
 				}
 			}
 
 			/* No valid scale definition found, syntax error */
 			return -1;
 
 found_scale:
 			/* Skip over scale name, whitespace and brace */
 			p += len;
 			WS_SKIP();
 			if (*p != '{')
 				return -1; /* Syntax error */
 			p++;
 			WS_SKIP();
 
 			/* Keep track of what we've found */
 			if (scale_results[scale] == -1)
 				scale_results[scale] = 0;
 			else if (scale_results[scale] == 1) {
 				/* We already have a valid match for this scale. Skip
 				   over all this nonsense then. */
 				while (*p && *p != '}')
 					p++;
 				if (!*p)
 					return -1; /* Syntax error */
 				goto close_brace;
 			}
 
 			/* We're inside the braces now. Values are separated
 			   by spaces, possibly given as ranges.
 			   VALUES := ( VALUE | RANGE ) [ ( VALUE | RANGE ) ... ]
 			   RANGE := VALUE - VALUE */
 
 			while (1) {
 				str = from;
 				len = sizeof(from) - 1;
 				*from = *to = '\0';
 				while (1) {
 					switch (*p) {
 						case '\0':
 							return -1; /* Syntax error */
 
 						case ' ':
 							WS_SKIP();
 							/* Here, it's either a hyphen or end of value/range */
 							if (*p == '-')
 								goto hyphen;
 							break;
 
 						case '-':
 hyphen:
 							if (!*from)
 								return -1; /* Range given as "-foo", syntax error */
 							if (*to)
 								return -1; /* Range given as "foo-bar-baz", syntax error */
 							/* Terminate "from" string and init for "to" */
 							*str = '\0';
 							str = to;
 							len = sizeof(to) - 1;
 							p++;
 							WS_SKIP();
 							continue;
 
 						case '}':
 							break;
 
 						default:
 							/* everything else gets copied and lowercased */
 							if (len <= 0)
 								return -1; /* String too long, syntax error */
 							*str++ = *p++ | 0x20;	/* works for letters and digits */
 							len--;
 							continue;
 					}
 					break;
 				}
 
 				*str = '\0';
 
 				/* Finished parsing out value or range. An empty result
 				   is valid, e.g. at the end of the list for the scale */
 				if (!*from) {
 					if (*p == '}')
 						break;
 					continue;
 				}
 				a1 = from;
 				a2 = *to ? to : NULL;
 				if ((defs[scale].flags & FLAG_INTEGER_ARGS)) {
 					a1 = (void *) atol(a1);
 					a2 = (void *) (a2 ? atol(a2) : -1);
 				}
 				res = defs[scale].func(time_vars[scale], a1, a2);
 				printf("result: %i\n", res);
 
 				if (res == -1)
 					return -1; /* Syntax error */
 				else if (res == 1)
 					scale_results[scale] = 1;
 			}
 
 close_brace:
 			p++;
 
 			/* Finished with one scale, braces closed. See if there's any more */
 			WS_SKIP();
 			if (!*p || *p == ',') {
 				/* Nope! Evaluate our result */
 				for (scale = 0; scale < SCALE_MAX; scale++) {
 					if (scale_results[scale] == 0)
 						goto no_match;
 				}
 
 				/* All scales that were given matched! We're done! */
 				return 1;
 
 no_match:
 				/* No luck, try next one if there are any more */
 				if (*p == ',') {
 					p++;
 					WS_SKIP();
 					break;
 				}
 
 				return 0;
 			}
 
 			continue;
 		}
 	}
 }
 
 static int generic_fn(const int time_var, const long f, long t, const long min, const long max) {
 	if (t == -1)
 		t = f;
 
 	if (f < min || f > max)
 		return -1;
 	if (t < min || t > max)
 		return -1;
 
 	if (f > t) {
 		if (f <= time_var || t >= time_var)
 			return 1;
 	}
 	else if (f <= time_var && time_var <= t)
 		return 1;
 	return 0;
 }
 
 static int generic_named_fn(const int time_var, const char *from, const char *to,
 		const char **array, int arr_len, int str_len) {
 
 	int i, f = 0, t = 0;
 
 	f = atoi(from);
 	if (!f)
 		for (i = 0; i < arr_len; i++)
 			if (!strncmp(array[i], from, str_len)) {
 				f = i + 1;
 				break;
 			}
 	if (!f)
 		return -1;
 
 	if (!to)
 		t = f;
 	else {
 		t = atoi(to);
 		if (!t)
 			for (i = 0; i < arr_len; i++)
 				if (!strncmp(array[i], to, str_len)) {
 					t = i + 1;
 					break;
 				}
 		if (!t)
 			return -1;
 	}
 
 	return generic_fn(time_var, f, t, 1, arr_len);
 }
 
 static int year_fn(const int time_var, const char *from, const char *to) {
 	long f, t, c;
 	printf("%s %i %li %li\n", __FUNCTION__, time_var, (long) from, (long) to);
 
 	f = (long) from;
 	t = (long) to;
 
 	if (t == -1)
 		t = f;
 
 	c = time_var / 100;	
 
 	if (t < 0)
 		return -1;
 	else if (t <= 99)
 		t += c;
 	else if (t < 1970)
 		return -1;
 
 	if (f < 0)
 		return -1;
 	else if (f <= 99)
 		f += c;
 	else if (f < 1970)
 		return -1;
 
 	if (time_var >= f && time_var <= t)
 		return 1;
 	return 0;
 }
 
 static int month_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i '%s' '%s'\n", __FUNCTION__, time_var, from, to);
 	return generic_named_fn(time_var, from, to, months, 12, 3);
 }
 
 static int week_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i %li %li\n", __FUNCTION__, time_var, (long) from, (long) to);
 	return generic_fn(time_var, (long) from, (long) to, 1, 6);
 }
 
 static int mday_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i %li %li\n", __FUNCTION__, time_var, (long) from, (long) to);
 	return generic_fn(time_var, (long) from, (long) to, 1, 31);
 }
 
 static int wday_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i '%s' '%s'\n", __FUNCTION__, time_var, from, to);
 	return generic_named_fn(time_var, from, to, weekdays, 7, 2);
 }
 
 static int yday_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i %li %li\n", __FUNCTION__, time_var, (long) from, (long) to);
 	return generic_fn(time_var, (long) from, (long) to, 1, 366);
 }
 
 static int hour_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i %li %li\n", __FUNCTION__, time_var, (long) from, (long) to);
 	return generic_fn(time_var, (long) from, (long) to, 0, 23);
 }
 
 static int minute_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i %li %li\n", __FUNCTION__, time_var, (long) from, (long) to);
 	return generic_fn(time_var, (long) from, (long) to, 0, 59);
 }
 
 static int second_fn(const int time_var, const char *from, const char *to) {
 	printf("%s %i %li %li\n", __FUNCTION__, time_var, (long) from, (long) to);
 	return generic_fn(time_var, (long) from, (long) to, 0, 60); /* allow for leap seconds */
 }