Add timespec -- add tests for it too.
This commit is contained in:
		
							
								
								
									
										193
									
								
								src/timespec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								src/timespec.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
#include "timespec.h"
 | 
			
		||||
 | 
			
		||||
/* Private prototypes and declarations */
 | 
			
		||||
struct mgos_timespec_spec {
 | 
			
		||||
  uint8_t start_h, start_m, start_s;
 | 
			
		||||
  uint8_t stop_h, stop_m, stop_s;
 | 
			
		||||
  SLIST_ENTRY(mgos_timespec_spec) entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgos_timespec {
 | 
			
		||||
  SLIST_HEAD(mgos_timespec_specs, mgos_timespec_spec) specs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool timespec_spec_parse(const char *spec, struct mgos_timespec_spec *out) {
 | 
			
		||||
  uint8_t start_h = 0, start_m = 0, start_s = 0;
 | 
			
		||||
  uint8_t stop_h = 0, stop_m = 0, stop_s = 0;
 | 
			
		||||
  char *  p;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  if (!spec || strlen(spec) == 0) {
 | 
			
		||||
    LOG(LL_ERROR, ("spec cannot be NULL or empty"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  for (i = 1; i < (int) strlen(spec); i++) {
 | 
			
		||||
    if (spec[i] != ':' && spec[i] != '-' && !(spec[i] >= '0' && spec[i] <= '9')) {
 | 
			
		||||
      LOG(LL_ERROR, ("spec='%s': illegal chars, only want [:\\-0-9]", spec));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  p = (char *)spec;
 | 
			
		||||
  if (!isdigit((int)*p)) {
 | 
			
		||||
    LOG(LL_ERROR, ("spec='%s': start time must begin with a digit", spec));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // start_h
 | 
			
		||||
  i = strtol(p, &p, 10);
 | 
			
		||||
  if (i < 0 || i > 23) {
 | 
			
		||||
    LOG(LL_ERROR, ("spec='%s': start hour must be [0..23]", spec));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  start_h = i;
 | 
			
		||||
  if (*p == ':') {
 | 
			
		||||
    // start_m
 | 
			
		||||
    i = strtol(p + 1, &p, 10);
 | 
			
		||||
    if (i < 0 || i > 59) {
 | 
			
		||||
      LOG(LL_ERROR, ("spec='%s': start minute must be [0..59]", spec));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    start_m = i;
 | 
			
		||||
  }
 | 
			
		||||
  if (*p == ':') {
 | 
			
		||||
    // start_s
 | 
			
		||||
    i = strtol(p + 1, &p, 10);
 | 
			
		||||
    if (i < 0 || i > 59) {
 | 
			
		||||
      LOG(LL_ERROR, ("spec='%s': start second must be [0..59]", spec));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    start_s = i;
 | 
			
		||||
  }
 | 
			
		||||
  if (*p != '-') {
 | 
			
		||||
    LOG(LL_ERROR, ("spec='%s': No separator '-' found after start", spec));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // stop_h
 | 
			
		||||
  if (!isdigit((int)*(p + 1))) {
 | 
			
		||||
    LOG(LL_ERROR, ("spec='%s': stop time must begin with a digit", spec));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  i = strtol(p + 1, &p, 10);
 | 
			
		||||
  if (i < 0 || i > 23) {
 | 
			
		||||
    LOG(LL_ERROR, ("spec='%s': stop hour must be [0..23]", spec));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  stop_h = i;
 | 
			
		||||
  if (*p == ':') {
 | 
			
		||||
    // start_m
 | 
			
		||||
    i = strtol(p + 1, &p, 10);
 | 
			
		||||
    if (i < 0 || i > 59) {
 | 
			
		||||
      LOG(LL_ERROR, ("spec='%s': stop minute must be [0..59]", spec));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    stop_m = i;
 | 
			
		||||
  }
 | 
			
		||||
  if (*p == ':') {
 | 
			
		||||
    // stop_s
 | 
			
		||||
    i = strtol(p + 1, &p, 10);
 | 
			
		||||
    if (i < 0 || i > 59) {
 | 
			
		||||
      LOG(LL_ERROR, ("spec='%s': stop second must be [0..59]", spec));
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    stop_s = i;
 | 
			
		||||
  }
 | 
			
		||||
  if (*p) {
 | 
			
		||||
    LOG(LL_ERROR, ("spec='%s': dangling characters after stop time", spec));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
//	LOG(LL_DEBUG, ("start=%d stop=%d", start, stop));
 | 
			
		||||
  if (out) {
 | 
			
		||||
    out->start_h = start_h;
 | 
			
		||||
    out->start_m = start_m;
 | 
			
		||||
    out->start_s = start_s;
 | 
			
		||||
    out->stop_h  = stop_h;
 | 
			
		||||
    out->stop_m  = stop_m;
 | 
			
		||||
    out->stop_s  = stop_s;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgos_timespec *timespec_create() {
 | 
			
		||||
  struct mgos_timespec *ts = calloc(1, sizeof(struct mgos_timespec));
 | 
			
		||||
 | 
			
		||||
  if (!ts) {
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool timespec_destroy(struct mgos_timespec **ts) {
 | 
			
		||||
  if (!ts) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  while (!SLIST_EMPTY(&(*ts)->specs)) {
 | 
			
		||||
    struct mgos_timespec_spec *t_spec;
 | 
			
		||||
 | 
			
		||||
    t_spec = SLIST_FIRST(&(*ts)->specs);
 | 
			
		||||
    SLIST_REMOVE_HEAD(&(*ts)->specs, entries);
 | 
			
		||||
    if (t_spec) {
 | 
			
		||||
//			LOG(LL_DEBUG, ("Removed mgos_timespec_spec"));
 | 
			
		||||
      free(t_spec);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  free(*ts);
 | 
			
		||||
  *ts = NULL;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool timespec_add_spec(struct mgos_timespec *ts, const char *spec) {
 | 
			
		||||
  struct mgos_timespec_spec *t_spec = calloc(1, sizeof(struct mgos_timespec_spec));
 | 
			
		||||
 | 
			
		||||
  if (!ts) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!t_spec) {
 | 
			
		||||
    LOG(LL_ERROR, ("Could not alloc memory for struct mgos_timespec_spec"));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (!timespec_spec_parse(spec, t_spec)) {
 | 
			
		||||
    LOG(LL_ERROR, ("spec='%s' is malformed, refusing to add", spec));
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  SLIST_INSERT_HEAD(&ts->specs, t_spec, entries);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool timespec_match(const struct mgos_timespec *ts, const struct tm *tm) {
 | 
			
		||||
  struct mgos_timespec_spec *t_spec;
 | 
			
		||||
 | 
			
		||||
  if (!ts) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SLIST_FOREACH(t_spec, &ts->specs, entries) {
 | 
			
		||||
    uint32_t start, stop, target;
 | 
			
		||||
 | 
			
		||||
    start  = t_spec->start_h * 3600 + t_spec->start_m * 60 + t_spec->start_s;
 | 
			
		||||
    stop   = t_spec->stop_h * 3600 + t_spec->stop_m * 60 + t_spec->stop_s;
 | 
			
		||||
    target = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
 | 
			
		||||
    if (start == stop) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (stop >= start) {
 | 
			
		||||
      if (target >= start && target < stop) {
 | 
			
		||||
//				LOG(LL_DEBUG, ("start=%d stop=%d target=%d matched", start, stop, target));
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      if (target >= start || target < stop) {
 | 
			
		||||
//				LOG(LL_DEBUG, ("start=%d stop=%d target=%d matched", start, stop, target));
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
//		LOG(LL_DEBUG, ("start=%d stop=%d target=%d did not match", start, stop, target));
 | 
			
		||||
  }
 | 
			
		||||
  (void)tm;
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user