diff options
Diffstat (limited to 'wsutil/to_str.c')
-rw-r--r-- | wsutil/to_str.c | 989 |
1 files changed, 989 insertions, 0 deletions
diff --git a/wsutil/to_str.c b/wsutil/to_str.c new file mode 100644 index 0000000..d15576d --- /dev/null +++ b/wsutil/to_str.c @@ -0,0 +1,989 @@ +/* wsutil/to_str.c + * Routines for utilities to convert various other types to strings. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" +#include "to_str.h" + +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <wsutil/utf8_entities.h> +#include <wsutil/wslog.h> +#include <wsutil/inet_addr.h> +#include <wsutil/pint.h> +#include <wsutil/time_util.h> + +/* + * If a user _does_ pass in a too-small buffer, this is probably + * going to be too long to fit. However, even a partial string + * starting with "[Buf" should provide enough of a clue to be + * useful. + */ +#define _return_if_nospace(str_len, buf, buf_len) \ + do { \ + if ((str_len) > (buf_len)) { \ + (void)g_strlcpy(buf, "[Buffer too small]", buf_len); \ + return; \ + } \ + } while (0) + +static const char fast_strings[][4] = { + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15", + "16", "17", "18", "19", "20", "21", "22", "23", + "24", "25", "26", "27", "28", "29", "30", "31", + "32", "33", "34", "35", "36", "37", "38", "39", + "40", "41", "42", "43", "44", "45", "46", "47", + "48", "49", "50", "51", "52", "53", "54", "55", + "56", "57", "58", "59", "60", "61", "62", "63", + "64", "65", "66", "67", "68", "69", "70", "71", + "72", "73", "74", "75", "76", "77", "78", "79", + "80", "81", "82", "83", "84", "85", "86", "87", + "88", "89", "90", "91", "92", "93", "94", "95", + "96", "97", "98", "99", "100", "101", "102", "103", + "104", "105", "106", "107", "108", "109", "110", "111", + "112", "113", "114", "115", "116", "117", "118", "119", + "120", "121", "122", "123", "124", "125", "126", "127", + "128", "129", "130", "131", "132", "133", "134", "135", + "136", "137", "138", "139", "140", "141", "142", "143", + "144", "145", "146", "147", "148", "149", "150", "151", + "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", + "168", "169", "170", "171", "172", "173", "174", "175", + "176", "177", "178", "179", "180", "181", "182", "183", + "184", "185", "186", "187", "188", "189", "190", "191", + "192", "193", "194", "195", "196", "197", "198", "199", + "200", "201", "202", "203", "204", "205", "206", "207", + "208", "209", "210", "211", "212", "213", "214", "215", + "216", "217", "218", "219", "220", "221", "222", "223", + "224", "225", "226", "227", "228", "229", "230", "231", + "232", "233", "234", "235", "236", "237", "238", "239", + "240", "241", "242", "243", "244", "245", "246", "247", + "248", "249", "250", "251", "252", "253", "254", "255" +}; + +static inline char +low_nibble_of_octet_to_hex(uint8_t oct) +{ + /* At least one version of Apple's C compiler/linker is buggy, causing + a complaint from the linker about the "literal C string section" + not ending with '\0' if we initialize a 16-element "char" array with + a 16-character string, the fact that initializing such an array with + such a string is perfectly legitimate ANSI C nonwithstanding, the 17th + '\0' byte in the string nonwithstanding. */ + static const char hex_digits[16] = + { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + return hex_digits[oct & 0xF]; +} + +static inline char * +byte_to_hex(char *out, uint32_t dword) +{ + *out++ = low_nibble_of_octet_to_hex(dword >> 4); + *out++ = low_nibble_of_octet_to_hex(dword); + return out; +} + +char * +guint8_to_hex(char *out, uint8_t val) +{ + return byte_to_hex(out, val); +} + +char * +word_to_hex(char *out, uint16_t word) +{ + out = byte_to_hex(out, word >> 8); + out = byte_to_hex(out, word); + return out; +} + +char * +word_to_hex_punct(char *out, uint16_t word, char punct) +{ + out = byte_to_hex(out, word >> 8); + *out++ = punct; + out = byte_to_hex(out, word); + return out; +} + +char * +word_to_hex_npad(char *out, uint16_t word) +{ + if (word >= 0x1000) + *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 12)); + if (word >= 0x0100) + *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 8)); + if (word >= 0x0010) + *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 4)); + *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 0)); + return out; +} + +char * +dword_to_hex(char *out, uint32_t dword) +{ + out = word_to_hex(out, dword >> 16); + out = word_to_hex(out, dword); + return out; +} + +char * +dword_to_hex_punct(char *out, uint32_t dword, char punct) +{ + out = word_to_hex_punct(out, dword >> 16, punct); + *out++ = punct; + out = word_to_hex_punct(out, dword, punct); + return out; +} + +char * +qword_to_hex(char *out, uint64_t qword) +{ + out = dword_to_hex(out, (uint32_t)(qword >> 32)); + out = dword_to_hex(out, (uint32_t)(qword & 0xffffffff)); + return out; +} + +char * +qword_to_hex_punct(char *out, uint64_t qword, char punct) +{ + out = dword_to_hex_punct(out, (uint32_t)(qword >> 32), punct); + *out++ = punct; + out = dword_to_hex_punct(out, (uint32_t)(qword & 0xffffffff), punct); + return out; +} + +/* + * This does *not* null-terminate the string. It returns a pointer + * to the position in the string following the last character it + * puts there, so that the caller can either put the null terminator + * in or can append more stuff to the buffer. + * + * There needs to be at least len * 2 bytes left in the buffer. + */ +char * +bytes_to_hexstr(char *out, const uint8_t *ad, size_t len) +{ + size_t i; + + ws_return_val_if(!ad, NULL); + + for (i = 0; i < len; i++) + out = byte_to_hex(out, ad[i]); + return out; +} + +/* + * This does *not* null-terminate the string. It returns a pointer + * to the position in the string following the last character it + * puts there, so that the caller can either put the null terminator + * in or can append more stuff to the buffer. + * + * There needs to be at least len * 3 - 1 bytes left in the buffer. + */ +char * +bytes_to_hexstr_punct(char *out, const uint8_t *ad, size_t len, char punct) +{ + size_t i; + + ws_return_val_if(!ad, NULL); + + out = byte_to_hex(out, ad[0]); + for (i = 1; i < len; i++) { + *out++ = punct; + out = byte_to_hex(out, ad[i]); + } + return out; +} + +/* Routine to convert a sequence of bytes to a hex string, one byte/two hex + * digits at a time, with a specified punctuation character between + * the bytes. + * + * If punct is '\0', no punctuation is applied (and thus + * the resulting string is (len-1) bytes shorter) + */ +char * +bytes_to_str_punct_maxlen(wmem_allocator_t *scope, + const uint8_t *src, size_t src_size, + char punct, size_t max_bytes_len) +{ + char *buf; + size_t max_char_size; + char *buf_ptr; + int truncated = 0; + + ws_return_str_if(!src, scope); + ws_return_str_if(!src_size, scope); + + if (!punct) + return bytes_to_str_maxlen(scope, src, src_size, max_bytes_len); + + if (max_bytes_len == 0 || max_bytes_len > src_size) { + max_bytes_len = src_size; + } + else if (max_bytes_len < src_size) { + truncated = 1; + } + + /* Include space for ellipsis and '\0'. Optional extra punct + * at the end is already accounted for. */ + max_char_size = max_bytes_len * 3 + strlen(UTF8_HORIZONTAL_ELLIPSIS) + 1; + + buf = wmem_alloc(scope, max_char_size); + buf_ptr = bytes_to_hexstr_punct(buf, src, max_bytes_len, punct); + + if (truncated) { + *buf_ptr++ = punct; + buf_ptr = g_stpcpy(buf_ptr, UTF8_HORIZONTAL_ELLIPSIS); + } + + *buf_ptr = '\0'; + return buf; +} + +char * +bytes_to_str_maxlen(wmem_allocator_t *scope, + const uint8_t *src, size_t src_size, + size_t max_bytes_len) +{ + char *buf; + size_t max_char_size; + char *buf_ptr; + int truncated = 0; + + ws_return_str_if(!src, scope); + ws_return_str_if(!src_size, scope); + + if (max_bytes_len == 0 || max_bytes_len > src_size) { + max_bytes_len = src_size; + } + else if (max_bytes_len < src_size) { + truncated = 1; + } + + max_char_size = max_bytes_len * 2 + strlen(UTF8_HORIZONTAL_ELLIPSIS) + 1; + + buf = wmem_alloc(scope, max_char_size); + buf_ptr = bytes_to_hexstr(buf, src, max_bytes_len); + + if (truncated) + buf_ptr = g_stpcpy(buf_ptr, UTF8_HORIZONTAL_ELLIPSIS); + + *buf_ptr = '\0'; + return buf; +} + +/* + * The *_to_str_back() functions measured approx. a x7.5 speed-up versus + * snprintf() on my Linux system with GNU libc. + */ + +char * +oct_to_str_back(char *ptr, uint32_t value) +{ + while (value) { + *(--ptr) = '0' + (value & 0x7); + value >>= 3; + } + + *(--ptr) = '0'; + return ptr; +} + +char * +oct64_to_str_back(char *ptr, uint64_t value) +{ + while (value) { + *(--ptr) = '0' + (value & 0x7); + value >>= 3; + } + + *(--ptr) = '0'; + return ptr; +} + +char * +hex_to_str_back_len(char *ptr, uint32_t value, int len) +{ + do { + *(--ptr) = low_nibble_of_octet_to_hex(value); + value >>= 4; + len--; + } while (value); + + /* pad */ + while (len > 0) { + *(--ptr) = '0'; + len--; + } + + *(--ptr) = 'x'; + *(--ptr) = '0'; + + return ptr; +} + +char * +hex64_to_str_back_len(char *ptr, uint64_t value, int len) +{ + do { + *(--ptr) = low_nibble_of_octet_to_hex(value & 0xF); + value >>= 4; + len--; + } while (value); + + /* pad */ + while (len > 0) { + *(--ptr) = '0'; + len--; + } + + *(--ptr) = 'x'; + *(--ptr) = '0'; + + return ptr; +} + +char * +uint_to_str_back(char *ptr, uint32_t value) +{ + char const *p; + + /* special case */ + if (value == 0) + *(--ptr) = '0'; + + while (value >= 10) { + p = fast_strings[100 + (value % 100)]; + + value /= 100; + + *(--ptr) = p[2]; + *(--ptr) = p[1]; + } + + if (value) + *(--ptr) = (value) | '0'; + + return ptr; +} + +char * +uint64_to_str_back(char *ptr, uint64_t value) +{ + char const *p; + + /* special case */ + if (value == 0) + *(--ptr) = '0'; + + while (value >= 10) { + p = fast_strings[100 + (value % 100)]; + + value /= 100; + + *(--ptr) = p[2]; + *(--ptr) = p[1]; + } + + /* value will be 0..9, so using '& 0xF' is safe, and faster than '% 10' */ + if (value) + *(--ptr) = (value & 0xF) | '0'; + + return ptr; +} + +char * +uint_to_str_back_len(char *ptr, uint32_t value, int len) +{ + char *new_ptr; + + new_ptr = uint_to_str_back(ptr, value); + + /* substract from len number of generated characters */ + len -= (int)(ptr - new_ptr); + + /* pad remaining with '0' */ + while (len > 0) + { + *(--new_ptr) = '0'; + len--; + } + + return new_ptr; +} + +char * +uint64_to_str_back_len(char *ptr, uint64_t value, int len) +{ + char *new_ptr; + + new_ptr = uint64_to_str_back(ptr, value); + + /* substract from len number of generated characters */ + len -= (int)(ptr - new_ptr); + + /* pad remaining with '0' */ + while (len > 0) + { + *(--new_ptr) = '0'; + len--; + } + + return new_ptr; +} + +char * +int_to_str_back(char *ptr, int32_t value) +{ + if (value < 0) { + ptr = uint_to_str_back(ptr, -value); + *(--ptr) = '-'; + } else + ptr = uint_to_str_back(ptr, value); + + return ptr; +} + +char * +int64_to_str_back(char *ptr, int64_t value) +{ + if (value < 0) { + ptr = uint64_to_str_back(ptr, -value); + *(--ptr) = '-'; + } else + ptr = uint64_to_str_back(ptr, value); + + return ptr; +} + +static size_t +guint32_to_str_buf_len(const uint32_t u) +{ + /* ((2^32)-1) == 2147483647 */ + if (u >= 1000000000)return 10; + if (u >= 100000000) return 9; + if (u >= 10000000) return 8; + if (u >= 1000000) return 7; + if (u >= 100000) return 6; + if (u >= 10000) return 5; + if (u >= 1000) return 4; + if (u >= 100) return 3; + if (u >= 10) return 2; + + return 1; +} + +void +guint32_to_str_buf(uint32_t u, char *buf, size_t buf_len) +{ + size_t str_len = guint32_to_str_buf_len(u)+1; + + char *bp = &buf[str_len]; + + _return_if_nospace(str_len, buf, buf_len); + + *--bp = '\0'; + + uint_to_str_back(bp, u); +} + +static size_t +guint64_to_str_buf_len(const uint64_t u) +{ + /* ((2^64)-1) == 18446744073709551615 */ + + if (u >= G_GUINT64_CONSTANT(10000000000000000000)) return 20; + if (u >= G_GUINT64_CONSTANT(1000000000000000000)) return 19; + if (u >= G_GUINT64_CONSTANT(100000000000000000)) return 18; + if (u >= G_GUINT64_CONSTANT(10000000000000000)) return 17; + if (u >= G_GUINT64_CONSTANT(1000000000000000)) return 16; + if (u >= G_GUINT64_CONSTANT(100000000000000)) return 15; + if (u >= G_GUINT64_CONSTANT(10000000000000)) return 14; + if (u >= G_GUINT64_CONSTANT(1000000000000)) return 13; + if (u >= G_GUINT64_CONSTANT(100000000000)) return 12; + if (u >= G_GUINT64_CONSTANT(10000000000)) return 11; + if (u >= G_GUINT64_CONSTANT(1000000000)) return 10; + if (u >= G_GUINT64_CONSTANT(100000000)) return 9; + if (u >= G_GUINT64_CONSTANT(10000000)) return 8; + if (u >= G_GUINT64_CONSTANT(1000000)) return 7; + if (u >= G_GUINT64_CONSTANT(100000)) return 6; + if (u >= G_GUINT64_CONSTANT(10000)) return 5; + if (u >= G_GUINT64_CONSTANT(1000)) return 4; + if (u >= G_GUINT64_CONSTANT(100)) return 3; + if (u >= G_GUINT64_CONSTANT(10)) return 2; + + return 1; +} + +void +guint64_to_str_buf(uint64_t u, char *buf, size_t buf_len) +{ + size_t str_len = guint64_to_str_buf_len(u)+1; + + char *bp = &buf[str_len]; + + _return_if_nospace(str_len, buf, buf_len); + + *--bp = '\0'; + + uint64_to_str_back(bp, u); +} + +/* + This function is very fast and this function is called a lot. + XXX update the address_to_str stuff to use this function. + */ +void +ip_to_str_buf(const uint8_t *ad, char *buf, const int buf_len) +{ + register char const *p; + register char *b=buf; + + _return_if_nospace(WS_INET_ADDRSTRLEN, buf, buf_len); + + p=fast_strings[*ad++]; + do { + *b++=*p; + p++; + } while(*p); + *b++='.'; + + p=fast_strings[*ad++]; + do { + *b++=*p; + p++; + } while(*p); + *b++='.'; + + p=fast_strings[*ad++]; + do { + *b++=*p; + p++; + } while(*p); + *b++='.'; + + p=fast_strings[*ad]; + do { + *b++=*p; + p++; + } while(*p); + *b=0; +} + +char *ip_to_str(wmem_allocator_t *scope, const uint8_t *ad) +{ + char *buf = wmem_alloc(scope, WS_INET_ADDRSTRLEN * sizeof(char)); + + ip_to_str_buf(ad, buf, WS_INET_ADDRSTRLEN); + + return buf; +} + +void +ip6_to_str_buf(const ws_in6_addr *addr, char *buf, size_t buf_size) +{ + /* + * If there is not enough space then ws_inet_ntop6() will leave + * an error message in the buffer, we don't need + * to use _return_if_nospace(). + */ + ws_inet_ntop6(addr, buf, (unsigned)buf_size); +} + +char *ip6_to_str(wmem_allocator_t *scope, const ws_in6_addr *ad) +{ + char *buf = wmem_alloc(scope, WS_INET6_ADDRSTRLEN * sizeof(char)); + + ws_inet_ntop6(ad, buf, WS_INET6_ADDRSTRLEN); + + return buf; +} + +char * +ipxnet_to_str_punct(wmem_allocator_t *allocator, const uint32_t ad, const char punct) +{ + char *buf = (char *)wmem_alloc(allocator, 12); + + *dword_to_hex_punct(buf, ad, punct) = '\0'; + return buf; +} + +#define WS_EUI64_STRLEN 24 + +char * +eui64_to_str(wmem_allocator_t *scope, const uint64_t ad) { + char *buf, *tmp; + uint8_t *p_eui64; + + p_eui64=(uint8_t *)wmem_alloc(NULL, 8); + buf=(char *)wmem_alloc(scope, WS_EUI64_STRLEN); + + /* Copy and convert the address to network byte order. */ + *(uint64_t *)(void *)(p_eui64) = pntoh64(&(ad)); + + tmp = bytes_to_hexstr_punct(buf, p_eui64, 8, ':'); + *tmp = '\0'; /* NULL terminate */ + wmem_free(NULL, p_eui64); + return buf; +} + +/* + * Number of characters required by a 64-bit signed number. + */ +#define CHARS_64_BIT_SIGNED 20 /* sign plus 19 digits */ + +/* + * Number of characters required by a fractional part, in nanoseconds, + * not counting the decimal point. + */ +#define CHARS_NANOSECONDS 9 /* 000000001 */ + +/* + * Format the fractional part of a time, with the specified precision. + * Returns the number of bytes formatted. + */ +int +format_fractional_part_nsecs(char *buf, size_t buflen, uint32_t nsecs, const char *decimal_point, int precision) +{ + char *ptr; + size_t remaining; + int num_bytes; + size_t decimal_point_len; + uint32_t frac_part; + int8_t num_buf[CHARS_NANOSECONDS]; + int8_t *num_end = &num_buf[CHARS_NANOSECONDS]; + int8_t *num_ptr; + size_t num_len; + + ws_assert(precision != 0); + + if (buflen == 0) { + /* + * No room in the buffer for anything, including + * a terminating '\0'. + */ + return 0; + } + + /* + * If the fractional part is >= 1, don't show it as a + * fractional part. + */ + if (nsecs >= 1000000000U) { + num_bytes = snprintf(buf, buflen, "%s(%u nanoseconds)", + decimal_point, nsecs); + if ((unsigned int)num_bytes >= buflen) { + /* + * That filled up or would have overflowed + * the buffer. Nothing more to do; return + * the remaining space in the buffer, minus + * one byte for the terminating '\0',* as + * that's the number of bytes we copied. + */ + return (int)(buflen - 1); + } + return num_bytes; + } + + ptr = buf; + remaining = buflen; + num_bytes = 0; + + /* + * Copy the decimal point. + * (We assume here that the locale's decimal point does + * not contain so many characters that its size doesn't + * fit in an int. :-)) + */ + decimal_point_len = g_strlcpy(buf, decimal_point, buflen); + if (decimal_point_len >= buflen) { + /* + * The decimal point didn't fit in the buffer + * and was truncated. Nothing more to do; + * return the remaining space in the buffer, + * minus one byte for the terminating '\0', + * as that's the number of bytes we copied. + */ + return (int)(buflen - 1); + } + ptr += decimal_point_len; + remaining -= decimal_point_len; + num_bytes += (int)decimal_point_len; + + /* + * Fill in num_buf with the nanoseconds value, padded with + * leading zeroes, to the specified precision. + * + * We scale the fractional part in advance, as that just + * takes one division by a constant (which may be + * optimized to a faster multiplication by a constant) + * and gets rid of some divisions and remainders by 100 + * done to generate the digits. + * + * We pass preciions as the last argument to + * uint_to_str_back_len(), as that might mean that + * all of the cases end up using common code to + * do part of the call to uint_to_str_back_len(). + */ + switch (precision) { + + case 1: + /* + * Scale down to units of 1/10 second. + */ + frac_part = nsecs / 100000000U; + break; + + case 2: + /* + * Scale down to units of 1/100 second. + */ + frac_part = nsecs / 10000000U; + break; + + case 3: + /* + * Scale down to units of 1/1000 second. + */ + frac_part = nsecs / 1000000U; + break; + + case 4: + /* + * Scale down to units of 1/10000 second. + */ + frac_part = nsecs / 100000U; + break; + + case 5: + /* + * Scale down to units of 1/100000 second. + */ + frac_part = nsecs / 10000U; + break; + + case 6: + /* + * Scale down to units of 1/1000000 second. + */ + frac_part = nsecs / 1000U; + break; + + case 7: + /* + * Scale down to units of 1/10000000 second. + */ + frac_part = nsecs / 100U; + break; + + case 8: + /* + * Scale down to units of 1/100000000 second. + */ + frac_part = nsecs / 10U; + break; + + case 9: + /* + * We're already in units of 1/1000000000 second. + */ + frac_part = nsecs; + break; + + default: + ws_assert_not_reached(); + break; + } + + num_ptr = uint_to_str_back_len(num_end, frac_part, precision); + + /* + * The length of the string that we want to copy to the buffer + * is the minimum of: + * + * the length of the digit string; + * the remaining space in the buffer, minus 1 for the + * terminating '\0'. + */ + num_len = MIN((size_t)(num_end - num_ptr), remaining - 1); + if (num_len == 0) { + /* + * Not enough room to copy anything. + * Return the number of bytes we've generated. + */ + return num_bytes; + } + + /* + * Copy over the fractional part. + * (We assume here that the fractional part does not contain + * so many characters that its size doesn't fit in an int. :-)) + */ + memcpy(ptr, num_ptr, num_len); + ptr += num_len; + num_bytes += (int)num_len; + + /* + * '\0'-terminate it. + */ + *ptr = '\0'; + return num_bytes; +} + +void +display_epoch_time(char *buf, size_t buflen, const nstime_t *ns, int precision) +{ + display_signed_time(buf, buflen, ns, precision); +} + +void +display_signed_time(char *buf, size_t buflen, const nstime_t *ns, int precision) +{ + int nsecs; + /* this buffer is not NUL terminated */ + int8_t num_buf[CHARS_64_BIT_SIGNED]; + int8_t *num_end = &num_buf[CHARS_64_BIT_SIGNED]; + int8_t *num_ptr; + size_t num_len; + + if (buflen < 1) + return; + + /* If the fractional part of the time stamp is negative, + print its absolute value and, if the seconds part isn't + (the seconds part should be zero in that case), stick + a "-" in front of the entire time stamp. */ + nsecs = ns->nsecs; + if (nsecs < 0) { + nsecs = -nsecs; + if (ns->secs >= 0) { + buf[0] = '-'; + buf++; + buflen--; + } + } + + /* + * Fill in num_buf with the seconds value. + */ + num_ptr = int64_to_str_back(num_end, ns->secs); + + /* + * The length of the string that we want to copy to the buffer + * is the minimum of: + * + * the length of the digit string; + * the size of the buffer, minus 1 for the terminating + * '\0'. + */ + num_len = MIN((size_t)(num_end - num_ptr), buflen - 1); + if (num_len == 0) { + /* + * Not enough room to copy anything. + */ + return; + } + + /* + * Copy over the seconds value. + */ + memcpy(buf, num_ptr, num_len); + buf += num_len; + buflen -= num_len; + + if (precision == 0) { + /* + * Seconds precision, so no nanosecond. + * Nothing more to do other than to + * '\0'-terminate the string. + */ + *buf = '\0'; + return; + } + + /* + * Append the fractional part. + */ + format_fractional_part_nsecs(buf, buflen, (uint32_t)nsecs, ".", precision); +} + +void +format_nstime_as_iso8601(char *buf, size_t buflen, const nstime_t *ns, + char *decimal_point, bool local, int precision) +{ + struct tm tm, *tmp; + char *ptr; + size_t remaining; + int num_bytes; + + if (local) + tmp = ws_localtime_r(&ns->secs, &tm); + else + tmp = ws_gmtime_r(&ns->secs, &tm); + if (tmp == NULL) { + snprintf(buf, buflen, "Not representable"); + return; + } + ptr = buf; + remaining = buflen; + num_bytes = snprintf(ptr, remaining, + "%04d-%02d-%02d %02d:%02d:%02d", + tmp->tm_year + 1900, + tmp->tm_mon + 1, + tmp->tm_mday, + tmp->tm_hour, + tmp->tm_min, + tmp->tm_sec); + if (num_bytes < 0) { + /* + * That got an error. + * Not much else we can do. + */ + snprintf(buf, buflen, "snprintf() failed"); + return; + } + if ((unsigned int)num_bytes >= remaining) { + /* + * That filled up or would have overflowed the buffer. + * Nothing more we can do. + */ + return; + } + ptr += num_bytes; + remaining -= num_bytes; + + if (precision != 0) { + /* + * Append the fractional part. + * Get the nsecs as a 32-bit unsigned value, as it should + * never be negative, so we treat it as unsigned. + */ + format_fractional_part_nsecs(ptr, remaining, (uint32_t)ns->nsecs, decimal_point, precision); + } +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ |