a[0] = ((uint16_t) (val)) & 0xff;\
} while (0)
-static char const *months[] = {
- "jan", "feb", "mar", "apr", "may", "jun",
- "jul", "aug", "sep", "oct", "nov", "dec" };
-
/** Sets a signal handler using sigaction if available, else signal
*
* @param sig to set handler for.
return strlcpy(out, p, outlen);
}
-
-/*
- * Sort of strtok/strsep function.
- */
-static char *mystrtok(char **ptr, char const *sep)
-{
- char *res;
-
- if (**ptr == '\0') return NULL;
-
- while (**ptr && strchr(sep, **ptr)) (*ptr)++;
-
- if (**ptr == '\0') return NULL;
-
- res = *ptr;
- while (**ptr && strchr(sep, **ptr) == NULL) (*ptr)++;
-
- if (**ptr != '\0') *(*ptr)++ = '\0';
-
- return res;
-}
-
-/*
- * Helper function to get a 2-digit date. With a maximum value,
- * and a terminating character.
- */
-static int get_part(char **str, int *date, int min, int max, char term, char const *name)
-{
- char *p = *str;
-
- if (!isdigit((int) *p) || !isdigit((int) p[1])) return -1;
- *date = (p[0] - '0') * 10 + (p[1] - '0');
-
- if (*date < min) {
- fr_strerror_printf("Invalid %s (too small)", name);
- return -1;
- }
-
- if (*date > max) {
- fr_strerror_printf("Invalid %s (too large)", name);
- return -1;
- }
-
- p += 2;
- if (!term) {
- *str = p;
- return 0;
- }
-
- if (*p != term) {
- fr_strerror_printf("Expected '%c' after %s, got '%c'",
- term, name, *p);
- return -1;
- }
- p++;
-
- *str = p;
- return 0;
-}
-
-/** Convert string in various formats to a fr_unix_time_t
- *
- * @param date_str input date string.
- * @param date time_t to write result to.
- * @param[in] hint scale for the parsing. Default is "seconds"
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint)
-{
- int i;
- int64_t tmp;
- struct tm *tm, s_tm;
- char buf[64];
- char *p;
- char *f[4];
- char *tail = NULL;
- fr_time_delta_t gmtoff = fr_time_delta_wrap(0);
-
- /*
- * Test for unix timestamp, which is just a number and
- * nothing else.
- */
- tmp = strtoul(date_str, &tail, 10);
- if (*tail == '\0') {
- *date = fr_unix_time_from_nsec(fr_time_scale(tmp, hint));
- return 0;
- }
-
- tm = &s_tm;
- memset(tm, 0, sizeof(*tm));
- tm->tm_isdst = -1; /* don't know, and don't care about DST */
-
- /*
- * Check for RFC 3339 dates. Note that we only support
- * dates in a ~1000 year period. If the server is being
- * used after 3000AD, someone can patch it then.
- *
- * %Y-%m-%dT%H:%M:%S
- * [.%d] sub-seconds
- * Z | (+/-)%H:%M time zone offset
- *
- */
- if ((tmp > 1900) && (tmp < 3000) && *tail == '-') {
- unsigned long subseconds;
- int tz, tz_hour, tz_min;
-
- p = tail + 1;
- s_tm.tm_year = tmp - 1900; /* 'struct tm' starts years in 1900 */
-
- if (get_part(&p, &s_tm.tm_mon, 1, 13, '-', "month") < 0) return -1;
- s_tm.tm_mon--; /* ISO is 1..12, where 'struct tm' is 0..11 */
-
- if (get_part(&p, &s_tm.tm_mday, 1, 31, 'T', "day") < 0) return -1;
- if (get_part(&p, &s_tm.tm_hour, 0, 23, ':', "hour") < 0) return -1;
- if (get_part(&p, &s_tm.tm_min, 0, 59, ':', "minute") < 0) return -1;
- if (get_part(&p, &s_tm.tm_sec, 0, 60, '\0', "seconds") < 0) return -1;
-
- if (*p == '.') {
- p++;
- subseconds = strtoul(p, &tail, 10);
- if (subseconds > NSEC) {
- fr_strerror_const("Invalid nanosecond specifier");
- return -1;
- }
-
- /*
- * Scale subseconds to nanoseconds by how
- * many digits were parsed/
- */
- if ((tail - p) < 9) {
- for (i = 0; i < 9 - (tail -p); i++) {
- subseconds *= 10;
- }
- }
-
- p = tail;
- } else {
- subseconds = 0;
- }
-
- /*
- * Time zone is GMT. Leave well enough
- * alone.
- */
- if (*p == 'Z') {
- if (p[1] != '\0') {
- fr_strerror_printf("Unexpected text '%c' after time zone", p[1]);
- return -1;
- }
- tz = 0;
- goto done;
- }
-
- if ((*p != '+') && (*p != '-')) {
- fr_strerror_printf("Invalid time zone specifier '%c'", *p);
- return -1;
- }
- tail = p; /* remember sign for later */
- p++;
-
- if (get_part(&p, &tz_hour, 0, 23, ':', "hour in time zone") < 0) return -1;
- if (get_part(&p, &tz_min, 0, 59, '\0', "minute in time zone") < 0) return -1;
-
- if (*p != '\0') {
- fr_strerror_printf("Unexpected text '%c' after time zone", *p);
- return -1;
- }
-
- /*
- * We set the time zone, but the timegm()
- * function ignores it. Note also that mktime()
- * ignores it too, and treats the time zone as
- * local.
- *
- * We can't store this value in s_tm.gtmoff,
- * because the timegm() function helpfully zeros
- * it out.
- *
- * So insyead of using stupid C library
- * functions, we just roll our own.
- */
- tz = tz_hour * 3600 + tz_min;
- if (*tail == '-') tz *= -1;
-
- done:
- tm->tm_gmtoff = tz;
- *date = fr_unix_time_add(fr_unix_time_from_tm(tm), fr_time_delta_wrap(subseconds));
- return 0;
- }
-
- /*
- * Try to parse dates via locale-specific names,
- * using the same format string as strftime().
- *
- * If that fails, then we fall back to our parsing
- * routine, which is much more forgiving.
- */
-
-#ifdef __APPLE__
- /*
- * OSX "man strptime" says it only accepts the local time zone, and GMT.
- *
- * However, when printing dates via strftime(), it prints
- * "UTC" instead of "GMT". So... we have to fix it up
- * for stupid nonsense.
- */
- {
- char const *tz = strstr(date_str, "UTC");
- if (tz) {
- char *my_str;
-
- my_str = talloc_strdup(NULL, date_str);
- if (my_str) {
- p = my_str + (tz - date_str);
- memcpy(p, "GMT", 3);
-
- p = strptime(my_str, "%b %e %Y %H:%M:%S %Z", tm);
- if (p && (*p == '\0')) {
- talloc_free(my_str);
- *date = fr_unix_time_from_tm(tm);
- return 0;
- }
- talloc_free(my_str);
- }
- }
- }
-#endif
-
- p = strptime(date_str, "%b %e %Y %H:%M:%S %Z", tm);
- if (p && (*p == '\0')) {
- *date = fr_unix_time_from_tm(tm);
- return 0;
- }
-
- strlcpy(buf, date_str, sizeof(buf));
-
- p = buf;
- f[0] = mystrtok(&p, " \t");
- f[1] = mystrtok(&p, " \t");
- f[2] = mystrtok(&p, " \t");
- f[3] = mystrtok(&p, " \t"); /* may, or may not, be present */
- if (!f[0] || !f[1] || !f[2]) {
- fr_strerror_const("Too few fields");
- return -1;
- }
-
- /*
- * Try to parse the time zone. If it's GMT / UTC or a
- * local time zone we're OK.
- *
- * Otherwise, ignore errors and assume GMT.
- */
- if (*p != '\0') {
- fr_skip_whitespace(p);
- (void) fr_time_delta_from_time_zone(p, &gmtoff);
- }
-
- /*
- * The time has a colon, where nothing else does.
- * So if we find it, bubble it to the back of the list.
- */
- if (f[3]) {
- for (i = 0; i < 3; i++) {
- if (strchr(f[i], ':')) {
- p = f[3];
- f[3] = f[i];
- f[i] = p;
- break;
- }
- }
- }
-
- /*
- * The month is text, which allows us to find it easily.
- */
- tm->tm_mon = 12;
- for (i = 0; i < 3; i++) {
- if (isalpha((int) *f[i])) {
- int j;
-
- /*
- * Bubble the month to the front of the list
- */
- p = f[0];
- f[0] = f[i];
- f[i] = p;
-
- for (j = 0; j < 12; j++) {
- if (strncasecmp(months[j], f[0], 3) == 0) {
- tm->tm_mon = j;
- break;
- }
- }
- }
- }
-
- /* month not found? */
- if (tm->tm_mon == 12) {
- fr_strerror_const("No month found");
- return -1;
- }
-
- /*
- * The year may be in f[1], or in f[2]
- */
- tm->tm_year = atoi(f[1]);
- tm->tm_mday = atoi(f[2]);
-
- if (tm->tm_year >= 1900) {
- tm->tm_year -= 1900;
-
- } else {
- /*
- * We can't use 2-digit years any more, they make it
- * impossible to tell what's the day, and what's the year.
- */
- if (tm->tm_mday < 1900) {
- fr_strerror_const("Invalid year < 1900");
- return -1;
- }
-
- /*
- * Swap the year and the day.
- */
- i = tm->tm_year;
- tm->tm_year = tm->tm_mday - 1900;
- tm->tm_mday = i;
- }
-
- /*
- * If the day is out of range, die.
- */
- if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
- fr_strerror_const("Invalid day of month");
- return -1;
- }
-
- /*
- * There may be %H:%M:%S. Parse it in a hacky way.
- */
- if (f[3]) {
- f[0] = f[3]; /* HH */
- f[1] = strchr(f[0], ':'); /* find : separator */
- if (!f[1]) {
- fr_strerror_const("No ':' after hour");
- return -1;
- }
-
- *(f[1]++) = '\0'; /* nuke it, and point to MM:SS */
-
- f[2] = strchr(f[1], ':'); /* find : separator */
- if (f[2]) {
- *(f[2]++) = '\0'; /* nuke it, and point to SS */
- tm->tm_sec = atoi(f[2]);
- } /* else leave it as zero */
-
- tm->tm_hour = atoi(f[0]);
- tm->tm_min = atoi(f[1]);
- }
-
- *date = fr_unix_time_add(fr_unix_time_from_tm(tm), gmtoff);
-
- return 0;
-}
-
int fr_size_from_str(size_t *out, char const *str)
{
char *q = NULL;
ssize_t fr_writev(int fd, struct iovec vector[], int iovcnt, fr_time_delta_t timeout);
ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen);
size_t fr_snprint_uint128(char *out, size_t outlen, uint128_t const num);
-int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint);
bool fr_multiply(uint64_t *result, uint64_t lhs, uint64_t rhs);
uint64_t fr_multiply_mod(uint64_t lhs, uint64_t rhs, uint64_t mod);
return t * scale;
}
+
+
+/*
+ * Sort of strtok/strsep function.
+ */
+static char *mystrtok(char **ptr, char const *sep)
+{
+ char *res;
+
+ if (**ptr == '\0') return NULL;
+
+ while (**ptr && strchr(sep, **ptr)) (*ptr)++;
+
+ if (**ptr == '\0') return NULL;
+
+ res = *ptr;
+ while (**ptr && strchr(sep, **ptr) == NULL) (*ptr)++;
+
+ if (**ptr != '\0') *(*ptr)++ = '\0';
+
+ return res;
+}
+
+/*
+ * Helper function to get a 2-digit date. With a maximum value,
+ * and a terminating character.
+ */
+static int get_part(char **str, int *date, int min, int max, char term, char const *name)
+{
+ char *p = *str;
+
+ if (!isdigit((int) *p) || !isdigit((int) p[1])) return -1;
+ *date = (p[0] - '0') * 10 + (p[1] - '0');
+
+ if (*date < min) {
+ fr_strerror_printf("Invalid %s (too small)", name);
+ return -1;
+ }
+
+ if (*date > max) {
+ fr_strerror_printf("Invalid %s (too large)", name);
+ return -1;
+ }
+
+ p += 2;
+ if (!term) {
+ *str = p;
+ return 0;
+ }
+
+ if (*p != term) {
+ fr_strerror_printf("Expected '%c' after %s, got '%c'",
+ term, name, *p);
+ return -1;
+ }
+ p++;
+
+ *str = p;
+ return 0;
+}
+
+static char const *months[] = {
+ "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec" };
+
+
+/** Convert string in various formats to a fr_unix_time_t
+ *
+ * @param date_str input date string.
+ * @param date time_t to write result to.
+ * @param[in] hint scale for the parsing. Default is "seconds"
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint)
+{
+ int i;
+ int64_t tmp;
+ struct tm *tm, s_tm;
+ char buf[64];
+ char *p;
+ char *f[4];
+ char *tail = NULL;
+ fr_time_delta_t gmt_delta = fr_time_delta_wrap(0);
+
+ /*
+ * Test for unix timestamp, which is just a number and
+ * nothing else.
+ */
+ tmp = strtoul(date_str, &tail, 10);
+ if (*tail == '\0') {
+ *date = fr_unix_time_from_nsec(fr_time_scale(tmp, hint));
+ return 0;
+ }
+
+ tm = &s_tm;
+ memset(tm, 0, sizeof(*tm));
+ tm->tm_isdst = -1; /* don't know, and don't care about DST */
+
+ /*
+ * Check for RFC 3339 dates. Note that we only support
+ * dates in a ~1000 year period. If the server is being
+ * used after 3000AD, someone can patch it then.
+ *
+ * %Y-%m-%dT%H:%M:%S
+ * [.%d] sub-seconds
+ * Z | (+/-)%H:%M time zone offset
+ *
+ */
+ if ((tmp > 1900) && (tmp < 3000) && *tail == '-') {
+ unsigned long subseconds;
+ int tz, tz_hour, tz_min;
+
+ p = tail + 1;
+ s_tm.tm_year = tmp - 1900; /* 'struct tm' starts years in 1900 */
+
+ if (get_part(&p, &s_tm.tm_mon, 1, 13, '-', "month") < 0) return -1;
+ s_tm.tm_mon--; /* ISO is 1..12, where 'struct tm' is 0..11 */
+
+ if (get_part(&p, &s_tm.tm_mday, 1, 31, 'T', "day") < 0) return -1;
+ if (get_part(&p, &s_tm.tm_hour, 0, 23, ':', "hour") < 0) return -1;
+ if (get_part(&p, &s_tm.tm_min, 0, 59, ':', "minute") < 0) return -1;
+ if (get_part(&p, &s_tm.tm_sec, 0, 60, '\0', "seconds") < 0) return -1;
+
+ if (*p == '.') {
+ p++;
+ subseconds = strtoul(p, &tail, 10);
+ if (subseconds > NSEC) {
+ fr_strerror_const("Invalid nanosecond specifier");
+ return -1;
+ }
+
+ /*
+ * Scale subseconds to nanoseconds by how
+ * many digits were parsed/
+ */
+ if ((tail - p) < 9) {
+ for (i = 0; i < 9 - (tail -p); i++) {
+ subseconds *= 10;
+ }
+ }
+
+ p = tail;
+ } else {
+ subseconds = 0;
+ }
+
+ /*
+ * Time zone is GMT. Leave well enough
+ * alone.
+ */
+ if (*p == 'Z') {
+ if (p[1] != '\0') {
+ fr_strerror_printf("Unexpected text '%c' after time zone", p[1]);
+ return -1;
+ }
+ tz = 0;
+ goto done;
+ }
+
+ if ((*p != '+') && (*p != '-')) {
+ fr_strerror_printf("Invalid time zone specifier '%c'", *p);
+ return -1;
+ }
+ tail = p; /* remember sign for later */
+ p++;
+
+ if (get_part(&p, &tz_hour, 0, 23, ':', "hour in time zone") < 0) return -1;
+ if (get_part(&p, &tz_min, 0, 59, '\0', "minute in time zone") < 0) return -1;
+
+ if (*p != '\0') {
+ fr_strerror_printf("Unexpected text '%c' after time zone", *p);
+ return -1;
+ }
+
+ /*
+ * We set the time zone, but the timegm()
+ * function ignores it. Note also that mktime()
+ * ignores it too, and treats the time zone as
+ * local.
+ *
+ * We can't store this value in s_tm.gtmoff,
+ * because the timegm() function helpfully zeros
+ * it out.
+ *
+ * So insyead of using stupid C library
+ * functions, we just roll our own.
+ */
+ tz = tz_hour * 3600 + tz_min;
+ if (*tail == '-') tz *= -1;
+
+ done:
+ tm->tm_gmtoff = tz;
+ *date = fr_unix_time_add(fr_unix_time_from_tm(tm), fr_time_delta_wrap(subseconds));
+ return 0;
+ }
+
+ /*
+ * Try to parse dates via locale-specific names,
+ * using the same format string as strftime().
+ *
+ * If that fails, then we fall back to our parsing
+ * routine, which is much more forgiving.
+ */
+
+#ifdef __APPLE__
+ /*
+ * OSX "man strptime" says it only accepts the local time zone, and GMT.
+ *
+ * However, when printing dates via strftime(), it prints
+ * "UTC" instead of "GMT". So... we have to fix it up
+ * for stupid nonsense.
+ */
+ {
+ char const *tz = strstr(date_str, "UTC");
+ if (tz) {
+ char *my_str;
+
+ my_str = talloc_strdup(NULL, date_str);
+ if (my_str) {
+ p = my_str + (tz - date_str);
+ memcpy(p, "GMT", 3);
+
+ p = strptime(my_str, "%b %e %Y %H:%M:%S %Z", tm);
+ if (p && (*p == '\0')) {
+ talloc_free(my_str);
+ *date = fr_unix_time_from_tm(tm);
+ return 0;
+ }
+ talloc_free(my_str);
+ }
+ }
+ }
+#endif
+
+ p = strptime(date_str, "%b %e %Y %H:%M:%S %Z", tm);
+ if (p && (*p == '\0')) {
+ *date = fr_unix_time_from_tm(tm);
+ return 0;
+ }
+
+ strlcpy(buf, date_str, sizeof(buf));
+
+ p = buf;
+ f[0] = mystrtok(&p, " \t");
+ f[1] = mystrtok(&p, " \t");
+ f[2] = mystrtok(&p, " \t");
+ f[3] = mystrtok(&p, " \t"); /* may, or may not, be present */
+ if (!f[0] || !f[1] || !f[2]) {
+ fr_strerror_const("Too few fields");
+ return -1;
+ }
+
+ /*
+ * Try to parse the time zone. If it's GMT / UTC or a
+ * local time zone we're OK.
+ *
+ * Otherwise, ignore errors and assume GMT.
+ */
+ if (*p != '\0') {
+ fr_skip_whitespace(p);
+ (void) fr_time_delta_from_time_zone(p, &gmt_delta);
+ }
+
+ /*
+ * The time has a colon, where nothing else does.
+ * So if we find it, bubble it to the back of the list.
+ */
+ if (f[3]) {
+ for (i = 0; i < 3; i++) {
+ if (strchr(f[i], ':')) {
+ p = f[3];
+ f[3] = f[i];
+ f[i] = p;
+ break;
+ }
+ }
+ }
+
+ /*
+ * The month is text, which allows us to find it easily.
+ */
+ tm->tm_mon = 12;
+ for (i = 0; i < 3; i++) {
+ if (isalpha((int) *f[i])) {
+ int j;
+
+ /*
+ * Bubble the month to the front of the list
+ */
+ p = f[0];
+ f[0] = f[i];
+ f[i] = p;
+
+ for (j = 0; j < 12; j++) {
+ if (strncasecmp(months[j], f[0], 3) == 0) {
+ tm->tm_mon = j;
+ break;
+ }
+ }
+ }
+ }
+
+ /* month not found? */
+ if (tm->tm_mon == 12) {
+ fr_strerror_const("No month found");
+ return -1;
+ }
+
+ /*
+ * The year may be in f[1], or in f[2]
+ */
+ tm->tm_year = atoi(f[1]);
+ tm->tm_mday = atoi(f[2]);
+
+ if (tm->tm_year >= 1900) {
+ tm->tm_year -= 1900;
+
+ } else {
+ /*
+ * We can't use 2-digit years any more, they make it
+ * impossible to tell what's the day, and what's the year.
+ */
+ if (tm->tm_mday < 1900) {
+ fr_strerror_const("Invalid year < 1900");
+ return -1;
+ }
+
+ /*
+ * Swap the year and the day.
+ */
+ i = tm->tm_year;
+ tm->tm_year = tm->tm_mday - 1900;
+ tm->tm_mday = i;
+ }
+
+ /*
+ * If the day is out of range, die.
+ */
+ if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
+ fr_strerror_const("Invalid day of month");
+ return -1;
+ }
+
+ /*
+ * There may be %H:%M:%S. Parse it in a hacky way.
+ */
+ if (f[3]) {
+ f[0] = f[3]; /* HH */
+ f[1] = strchr(f[0], ':'); /* find : separator */
+ if (!f[1]) {
+ fr_strerror_const("No ':' after hour");
+ return -1;
+ }
+
+ *(f[1]++) = '\0'; /* nuke it, and point to MM:SS */
+
+ f[2] = strchr(f[1], ':'); /* find : separator */
+ if (f[2]) {
+ *(f[2]++) = '\0'; /* nuke it, and point to SS */
+ tm->tm_sec = atoi(f[2]);
+ } /* else leave it as zero */
+
+ tm->tm_hour = atoi(f[0]);
+ tm->tm_min = atoi(f[1]);
+ }
+
+ *date = fr_unix_time_add(fr_unix_time_from_tm(tm), gmt_delta);
+
+ return 0;
+}
void fr_time_elapsed_update(fr_time_elapsed_t *elapsed, fr_time_t start, fr_time_t end) CC_HINT(nonnull);
void fr_time_elapsed_fprint(FILE *fp, fr_time_elapsed_t const *elapsed, char const *prefix, int tabs) CC_HINT(nonnull(1,2));
+
fr_unix_time_t fr_unix_time_from_tm(struct tm *tm) CC_HINT(nonnull);
+int fr_unix_time_from_str(fr_unix_time_t *date, char const *date_str, fr_time_res_t hint);
#ifdef __cplusplus
}