diff --git a/Changelog b/Changelog index fd0b12f1..3fdf8d71 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,4 @@ -1.7.2 2019-??-?? +1.7.2 2020-??-?? * bugfix #38: Print "line" before line number when printing zone parse errors. Thanks Petr Špaček. * bugfix: Revert unused variables in ldns-config removal patch. diff --git a/duration.c b/duration.c index 0a8d79b5..abff9a07 100644 --- a/duration.c +++ b/duration.c @@ -190,121 +190,51 @@ ldns_duration_create_from_string(const char* str) /** - * Get the number of digits in a number. - * + * Helper func for ldns_duration2string below. If t > 0, + * scan print t and c on buf, forwarding buf. Return 0 on success. */ -static size_t -digits_in_number(time_t duration) +static inline int dur_scan_print(char **buf, char *eob, char c, time_t t) { - unsigned int i = (unsigned int) duration; - size_t n = 1; - - while (i >= 100000000) { - n += 8; - i /= 100000000; + if (t > 0) { + int r = snprintf(*buf, eob - *buf, "%u%c", (unsigned)t, c); + if (r < 0 || (*buf += r) >= eob) + return -1; } - if (i >= 10000) { n += 4; i /= 10000; } - if (i >= 100 ) { n += 2; i /= 100; } - if (i >= 10 ) { n += 1; } - return n; + return 0; } - /** * Convert a duration to a string. * */ char* -ldns_duration2string(const ldns_duration_type* duration) +ldns_duration2string(const ldns_duration_type* d) { - char* str = NULL; - size_t count = 2; - int T = 0; - char num[sizeof(unsigned int) + 2]; - - if (!duration) { - return NULL; - } - - if (duration->years > 0) { - count = count + 1 + digits_in_number(duration->years); - } - if (duration->months > 0) { - count = count + 1 + digits_in_number(duration->months); - } - if (duration->weeks > 0) { - count = count + 1 + digits_in_number(duration->weeks); - } - if (duration->days > 0) { - count = count + 1 + digits_in_number(duration->days); - } - if (duration->hours > 0) { - count = count + 1 + digits_in_number(duration->hours); - T = 1; - } - if (duration->minutes > 0) { - count = count + 1 + digits_in_number(duration->minutes); - T = 1; - } - if (duration->seconds > 0) { - count = count + 1 + digits_in_number(duration->seconds); - T = 1; - } - if (T) { - count++; - } - - if (!(str = (char*) calloc(count, sizeof(char)))) - return NULL; - str[0] = 'P'; - str[1] = '\0'; - - if (duration->years > 0) { - count = digits_in_number(duration->years); - assert(count <= sizeof(num) - 2); - snprintf(num, count+2, "%uY", (unsigned int) duration->years); - str = strncat(str, num, count+2); - } - if (duration->months > 0) { - count = digits_in_number(duration->months); - assert(count <= sizeof(num) - 2); - snprintf(num, count+2, "%uM", (unsigned int) duration->months); - str = strncat(str, num, count+2); - } - if (duration->weeks > 0) { - count = digits_in_number(duration->weeks); - assert(count <= sizeof(num) - 2); - snprintf(num, count+2, "%uW", (unsigned int) duration->weeks); - str = strncat(str, num, count+2); - } - if (duration->days > 0) { - count = digits_in_number(duration->days); - assert(count <= sizeof(num) - 2); - snprintf(num, count+2, "%uD", (unsigned int) duration->days); - str = strncat(str, num, count+2); - } - if (T) { - str = strcat(str, "T"); - } - if (duration->hours > 0) { - count = digits_in_number(duration->hours); - assert(count <= sizeof(num) - 2); - snprintf(num, count+2, "%uH", (unsigned int) duration->hours); - str = strncat(str, num, count+2); - } - if (duration->minutes > 0) { - count = digits_in_number(duration->minutes); - assert(count <= sizeof(num) - 2); - snprintf(num, count+2, "%uM", (unsigned int) duration->minutes); - str = strncat(str, num, count+2); - } - if (duration->seconds > 0) { - count = digits_in_number(duration->seconds); - assert(count <= sizeof(num) - 2); - snprintf(num, count+2, "%uS", (unsigned int) duration->seconds); - str = strncat(str, num, count+2); - } - return str; + /* Max string size should be 7 * 40 + 3 on a 127 bits machine + * So 300 (< 273) is more than enough. + */ + char buf[300] = "P0D", *eob = buf + sizeof(buf), *p = buf + 1; + + if (!d) + return NULL; + + if (dur_scan_print(&p, eob, 'Y', d->years) + || dur_scan_print(&p, eob, 'M', d->months) + || dur_scan_print(&p, eob, 'W', d->weeks) + || dur_scan_print(&p, eob, 'D', d->days)) + return NULL; + + if (d->hours || d->minutes || d->seconds) { + if (p > (eob - 2)) + return NULL; /* Error; no space left on buf for 'T' */ + + *p++ = 'T'; *p = 0; + if (dur_scan_print(&p, eob, 'H', d->hours) + || dur_scan_print(&p, eob, 'M', d->minutes) + || dur_scan_print(&p, eob, 'S', d->seconds)) + return NULL; + } + return strdup(buf); } diff --git a/test/13-unit-tests-base.tpkg/13-unit-tests-base.c b/test/13-unit-tests-base.tpkg/13-unit-tests-base.c index 0fd4b5d9..74108a40 100644 --- a/test/13-unit-tests-base.tpkg/13-unit-tests-base.c +++ b/test/13-unit-tests-base.tpkg/13-unit-tests-base.c @@ -5,6 +5,57 @@ #include +int test_duration(void) +{ + ldns_duration_type *d1 = NULL, *d2 = NULL; + char *s1 = NULL, *s2 = NULL, *s3 = NULL; + int r = -1; + + if (!(d1 = ldns_duration_create())) + fprintf(stderr, "ldns_duration_create() returned NULL\n"); + + else if (!(s1 = ldns_duration2string(d1))) + fprintf(stderr, "ldns_duration2string() returned NULL\n"); + + else if (!(d2 = ldns_duration_create_from_string("PT0S"))) + fprintf( stderr + , "ldns_duration_create_from_string(\"P0D\") returned NULL\n"); + + else if (ldns_duration_compare(d1, d2)) + fprintf(stderr, "0 durations not equal\n"); + + else if ((d1->years = 1), (d1->months = 3), 0) + ; /* pass */ + + else if (!(s2 = ldns_duration2string(d1))) + fprintf(stderr, "ldns_duration2string() returned NULL\n"); + + else if (strcmp(s2, "P1Y3M")) + fprintf(stderr, "\"%s\" should have been \"P1Y3M\"\n", s2); + + else if ((d1->minutes = 3), 0) + ; /* pass */ + + else if (!(s3 = ldns_duration2string(d1))) + fprintf(stderr, "ldns_duration2string() returned NULL\n"); + + else if (strcmp(s3, "P1Y3MT3M")) + fprintf(stderr, "\"%s\" should have been \"P1Y3MT3M\"\n", s3); + + else if (ldns_duration_compare(d1, d2) <= 0) + fprintf(stderr, "ldns_duration_compare() error\n"); + else + r = 0; + + if (d1) ldns_duration_cleanup(d1); + if (d2) ldns_duration_cleanup(d2); + if (s1) free(s1); + if (s2) free(s2); + if (s3) free(s3); + return r; +} + + void print_data_ar(const uint8_t *data, const size_t len) { size_t i; @@ -823,6 +874,9 @@ main(void) } free(data); + if (test_duration()) + result = EXIT_FAILURE; + printf("unit test is %s\n", result==EXIT_SUCCESS?"ok":"fail"); exit(result); }