/* Extra room for the ending NUL-byte, necessary even when the buffer has * SOL_BUFFER_FLAGS_NO_NUL_BYTE because vsnprintf always write the '\0'. */ SOL_API int sol_buffer_append_vprintf(struct sol_buffer *buf, const char *fmt, va_list args) { va_list args_copy; size_t space; char *p; ssize_t done; int r; SOL_NULL_CHECK(buf, -EINVAL); SOL_NULL_CHECK(fmt, -EINVAL); /* We need to copy args since we first try to do one vsnprintf() * to the existing space and if it doesn't fit we do a second * one. The second one would fail since args may be changed by * vsnprintf(), so the copy. */ va_copy(args_copy, args); space = buf->capacity - buf->used; p = sol_buffer_at_end(buf); done = vsnprintf(p, space, fmt, args_copy); if (done < 0) { r = -errno; goto end; } else if ((size_t)done >= space) { size_t new_size; r = sol_util_size_add(buf->used, done, &new_size); if (r < 0) goto end; /* Extra room for the ending NUL-byte. */ if (new_size >= SIZE_MAX - 1) { r = -EOVERFLOW; goto end; } r = sol_buffer_ensure(buf, new_size + 1); if (r < 0) goto end; space = buf->capacity - buf->used; p = sol_buffer_at_end(buf); done = vsnprintf(p, space, fmt, args); if (done < 0) { r = -errno; goto end; } } buf->used += done; r = 0; end: va_end(args_copy); return r; }
SOL_API int sol_buffer_append_char(struct sol_buffer *buf, const char c) { char *p; size_t new_size; int err; SOL_NULL_CHECK(buf, -EINVAL); err = sol_util_size_add(buf->used, 1, &new_size); if (err < 0) return err; err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at_end(buf); *p = c; buf->used++; if (nul_byte_size(buf)) return sol_buffer_ensure_nul_byte(buf); return 0; }
SOL_API int sol_buffer_append_slice(struct sol_buffer *buf, const struct sol_str_slice slice) { const size_t nul_size = nul_byte_size(buf); char *p; size_t new_size; int err; SOL_NULL_CHECK(buf, -EINVAL); err = sol_util_size_add(buf->used, slice.len, &new_size); if (err < 0) return err; /* Extra room for the ending NUL-byte. */ if (new_size >= SIZE_MAX - nul_size) return -EOVERFLOW; err = sol_buffer_ensure(buf, new_size + nul_size); if (err < 0) return err; p = sol_buffer_at_end(buf); memcpy(p, slice.data, slice.len); if (nul_size) p[slice.len] = '\0'; buf->used += slice.len; return 0; }
SOL_API const char * sol_network_link_addr_to_str(const struct sol_network_link_addr *addr, struct sol_buffer *buf) { char *p; int i; bool sep = false, skipping = false, treated_zeroes = false; SOL_NULL_CHECK(addr, NULL); SOL_NULL_CHECK(buf, NULL); if (sol_bluetooth_is_family(addr->family)) return sol_bluetooth_addr_to_str(addr, buf); if (addr->family != SOL_NETWORK_FAMILY_INET6) return NULL; if (buf->capacity - buf->used < SOL_NETWORK_INET_ADDR_STR_LEN) return NULL; p = sol_buffer_at_end(buf); for (i = 0; i < 16; i += 2) { uint16_t part; part = ((uint16_t)addr->addr.in6[i] << 8) | addr->addr.in6[i + 1]; if (part && skipping) { skipping = false; treated_zeroes = true; sep = true; } if (!part && !treated_zeroes) { if (!skipping) { skipping = true; sep = true; } } if (sep) { sol_buffer_append_char(buf, ':'); if (skipping) sep = false; } if (skipping) continue; sol_buffer_append_printf(buf, "%" PRIx16, part); sep = true; } if (skipping) sol_buffer_append_char(buf, ':'); return p; }
SOL_API int sol_buffer_append_from_base16(struct sol_buffer *buf, const struct sol_str_slice slice, enum sol_decode_case decode_case) { char *p; size_t new_size; ssize_t decoded_size, r; const size_t nul_size = nul_byte_size(buf); int err; SOL_NULL_CHECK(buf, -EINVAL); if (slice.len == 0) return 0; decoded_size = sol_util_base16_calculate_decoded_len(slice); if (decoded_size < 0) return decoded_size; err = sol_util_size_add(buf->used, decoded_size, &new_size); if (err < 0) return err; if (nul_size) { err = sol_util_size_add(new_size, nul_size, &new_size); if (err < 0) return err; } err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at_end(buf); r = sol_util_base16_decode(p, decoded_size, slice, decode_case); if (r != decoded_size) { if (nul_size) sol_buffer_ensure_nul_byte(buf); if (r < 0) return r; else return -EINVAL; } buf->used += decoded_size; if (nul_size) return sol_buffer_ensure_nul_byte(buf); return 0; }
SOL_API int sol_buffer_append_as_base64(struct sol_buffer *buf, const struct sol_str_slice slice, const char base64_map[SOL_STATIC_ARRAY_SIZE(65)]) { char *p; size_t new_size; ssize_t encoded_size, r; const size_t nul_size = nul_byte_size(buf); int err; SOL_NULL_CHECK(buf, -EINVAL); if (slice.len == 0) return 0; encoded_size = sol_util_base64_calculate_encoded_len(slice, base64_map); if (encoded_size < 0) return encoded_size; err = sol_util_size_add(buf->used, encoded_size, &new_size); if (err < 0) return err; if (nul_size) { err = sol_util_size_add(new_size, nul_size, &new_size); if (err < 0) return err; } err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at_end(buf); r = sol_util_base64_encode(p, encoded_size, slice, base64_map); if (r != encoded_size) { if (nul_size) sol_buffer_ensure_nul_byte(buf); if (r < 0) return r; else return -EINVAL; } buf->used += encoded_size; if (nul_size) return sol_buffer_ensure_nul_byte(buf); return 0; }
SOL_API int sol_buffer_ensure_nul_byte(struct sol_buffer *buf) { SOL_NULL_CHECK(buf, -EINVAL); if (buf->flags & SOL_BUFFER_FLAGS_NO_NUL_BYTE) return -EINVAL; if (buf->used && *((char *)sol_buffer_at_end(buf) - 1) == '\0') return 0; if (buf->used >= SIZE_MAX - 1 || sol_buffer_ensure(buf, buf->used + 1) < 0) return -ENOMEM; *((char *)buf->data + buf->used) = '\0'; return 0; }
SOL_API char * sol_util_strerror(int errnum, struct sol_buffer *buf) { char *ret; int err; SOL_NULL_CHECK(buf, NULL); #define CHUNK_SIZE (512) #ifdef HAVE_XSI_STRERROR_R /* XSI-compliant strerror_r returns int */ while (1) { errno = 0; ret = sol_buffer_at_end(buf); err = strerror_r(errnum, ret, buf->capacity - buf->used); if (err == ERANGE || (err < 0 && errno == ERANGE)) { err = sol_buffer_expand(buf, CHUNK_SIZE); SOL_INT_CHECK(err, < 0, NULL); } else if (err != 0)
SOL_API char * sol_json_escape_string(const char *str, struct sol_buffer *buf) { char *out, *r_str; size_t i, escaped_len; int r; SOL_NULL_CHECK(str, NULL); SOL_NULL_CHECK(buf, NULL); escaped_len = sol_json_calculate_escaped_string_len(str); r = sol_buffer_expand(buf, escaped_len); SOL_INT_CHECK(r, < 0, NULL); r_str = out = sol_buffer_at_end(buf); for (i = 0; *str && i < escaped_len; str++, i++) { if (memchr(sol_json_escapable_chars, *str, sizeof(sol_json_escapable_chars))) { *out++ = '\\'; switch (*str) { case '"': *out++ = '"'; break; case '\\': *out++ = '\\'; break; case '/': *out++ = '/'; break; case '\b': *out++ = 'b'; break; case '\f': *out++ = 'f'; break; case '\n': *out++ = 'n'; break; case '\r': *out++ = 'r'; break; case '\t': *out++ = 't'; break; } } else { *out++ = *str; } } *out++ = '\0'; buf->used += escaped_len - 1; return r_str; }
SOL_API int sol_buffer_insert_slice(struct sol_buffer *buf, size_t pos, const struct sol_str_slice slice) { const size_t nul_size = nul_byte_size(buf); char *p; size_t new_size; int err; SOL_NULL_CHECK(buf, -EINVAL); SOL_INT_CHECK(pos, > buf->used, -EINVAL); if (pos == buf->used) return sol_buffer_append_slice(buf, slice); err = sol_util_size_add(buf->used, slice.len, &new_size); if (err < 0) return err; /* Extra room for the ending NUL-byte. */ if (new_size >= SIZE_MAX - nul_size) return -EOVERFLOW; err = sol_buffer_ensure(buf, new_size + nul_size); if (err < 0) return err; p = sol_buffer_at(buf, pos); memmove(p + slice.len, p, buf->used - pos); memcpy(p, slice.data, slice.len); buf->used += slice.len; if (nul_size) { p = sol_buffer_at_end(buf); p[0] = '\0'; } return 0; }
const char *r; SOL_NULL_CHECK(addr, NULL); SOL_NULL_CHECK(buf, NULL); if (addr->family != SOL_NETWORK_FAMILY_INET6) return NULL; if (buf->capacity - buf->used < IPV6_ADDR_MAX_STR_LEN) { int err; err = sol_buffer_expand(buf, IPV6_ADDR_MAX_STR_LEN); SOL_INT_CHECK(err, < 0, NULL); } r = ipv6_addr_to_str(sol_buffer_at_end(buf), (ipv6_addr_t *)&addr->addr, IPV6_ADDR_MAX_STR_LEN); if (r) buf->used += strlen(r); return r; #else return NULL; #endif } SOL_API const struct sol_network_link_addr * sol_network_link_addr_from_str(struct sol_network_link_addr *addr, const char *buf) {