Changeset: 61a4b1554f8a for MonetDB URL: https://dev.monetdb.org/hg/MonetDB/rev/61a4b1554f8a Added Files: clients/mapilib/writeurl.c Modified Files: clients/examples/C/testsfile.c clients/mapilib/CMakeLists.txt clients/mapilib/msettings.h Branch: odbc_loader Log Message:
Add msettings_write_url diffs (truncated from 365 to 300 lines): diff --git a/clients/examples/C/testsfile.c b/clients/examples/C/testsfile.c --- a/clients/examples/C/testsfile.c +++ b/clients/examples/C/testsfile.c @@ -28,15 +28,116 @@ static int start_line = -1; static int nstarted = 0; static msettings *mp = NULL; +static +bool verify_roundtrip(const char *location) +{ + const char ch = '*'; + char buffer[1000 + 1]; // + 1 canary byte + memset(buffer, ch, sizeof(buffer)); + const size_t buffer_size = sizeof(buffer) - 1; + + size_t length = msettings_write_url(mp, buffer, buffer_size); + if (length == 0) { + fprintf(stderr, "%s: msettings_write_url returned 0\n", location); + return false; + } + if (length > buffer_size - 1) { + fprintf(stderr, "%s: Reconstructed the URL unexpectedly large: %zu\n", location, length); + return false; + } + if (memchr(buffer, '\0', buffer_size) == NULL) { + fprintf(stderr, "%s: msettings_write_url didn't NUL terminate the result\n", location); + return false; + } + if (buffer[buffer_size] != ch) { + fprintf(stderr, "%s: msettting_write_url wrote beyond the end of the buffer\n", location); + return false; + } + + msettings *tmp = msettings_create(); + if (tmp == NULL) { + fprintf(stderr, "malloc failed\n"); + return false; + } + msettings_error err = msettings_parse_url(tmp, buffer); + if (err) { + fprintf(stderr, "%s: Reconstructed URL <%s> couldn't be parsed: %s", location, buffer, err); + msettings_destroy(tmp); + return false; + } + + mparm parm; + bool ok = true; + for (int i = 0; (parm = mparm_enumerate(i)) != MP_UNKNOWN; i++) { + if (parm == MP_IGNORE) + continue; + char scratch1[100], scratch2[100]; + const char *mp_val = msetting_as_string(mp, parm, scratch1, sizeof(scratch1)); + const char *tmp_val = msetting_as_string(tmp, parm, scratch2, sizeof(scratch2)); + if (strcmp(mp_val, tmp_val) != 0) { + fprintf( + stderr, + "%s: setting %s: reconstructed value <%s> != <%s>\n", + location, mparm_name(parm), tmp_val, mp_val); + ok = false; + } + } + msettings_destroy(tmp); + if (!ok) + return false; + + // check if rendering to a smaller buffer returns the same length + // and writes a prefix of the original. + + assert(length > 0); // we checked this above + + char buffer2[sizeof(buffer)]; + for (size_t shorter = length; shorter > 0; shorter--) { + memset(buffer2, ch, sizeof(buffer)); + size_t n = msettings_write_url(mp, buffer2, shorter); + if (n != length) { + fprintf( + stderr,\ + "%s: writing to buffer of size %zu returns %zu, expected %zu\n", + location, shorter, n, length); + return false; + } + char *first_nul = memchr(buffer2, '\0', shorter); + if (first_nul == NULL) { + fprintf(stderr, "%s: truncated <%zu> msettings_write_url didn't NUL terminate\n", location, shorter); + return false; + } else if (strncmp(buffer2, buffer, shorter - 1) != 0) { + fprintf(stderr, + "%s: truncated <%zu> msettings_write_url wrote <%s> which isn't a prefix of <%s>", + location, shorter, + buffer2, buffer + ); + return false; + } + for (size_t i = shorter + 1; i < sizeof(buffer); i++) { + if (buffer2[i] != ch) { + fprintf( + stderr, + "%s: truncated <%zu> wsettings_write_url wrote beyond end of buffer (pos %zu)\n", + location, shorter, i); + return false; + } + } + } + + return true; +} + static bool handle_parse_command(const char *location, char *url) { const char *errmsg = msettings_parse_url(mp, url); - if (!errmsg) - return true; + if (errmsg) { + fprintf(stderr, "%s: %s\n", location, errmsg); + return false; + } - fprintf(stderr, "%s: %s\n", location, errmsg); - return false; + return verify_roundtrip(location); } static bool @@ -53,7 +154,7 @@ handle_accept_command(const char *locati fprintf(stderr, "%s: URL invalid: %s\n", location, msg); return false; } - return true; + return verify_roundtrip(location); } static bool @@ -79,7 +180,10 @@ handle_set_command(const char *location, fprintf(stderr, "%s: %s\n", location, msg); return false; } - return true; + if (msettings_validate(mp) == NULL) + return verify_roundtrip(location); + else + return true; } static bool diff --git a/clients/mapilib/CMakeLists.txt b/clients/mapilib/CMakeLists.txt --- a/clients/mapilib/CMakeLists.txt +++ b/clients/mapilib/CMakeLists.txt @@ -28,6 +28,7 @@ target_sources(mapi msettings.c msettings_internal.h parseurl.c + writeurl.c $<$<BOOL:${HAVE_SYS_UN_H}>:connect_unix.c> $<$<BOOL:${OPENSSL_FOUND}>:connect_openssl.c> $<$<BOOL:${OPENSSL_FOUND}>:$<$<BOOL:${WIN32}>:openssl_windows.c>> diff --git a/clients/mapilib/msettings.h b/clients/mapilib/msettings.h --- a/clients/mapilib/msettings.h +++ b/clients/mapilib/msettings.h @@ -167,6 +167,13 @@ mapi_export msettings_error msetting_set /* update the msettings from the URL. */ mapi_export msettings_error msettings_parse_url(msettings *mp, const char *url); +/* render the msettings as an URL. The result is always NUL terminated + * even if it's truncated. Returns the number of characters that have been + * written or would have been written if the buffer were large enough, + * excluding the trailing NUL. +*/ +mapi_export size_t msettings_write_url(const msettings *mp, char *buffer, size_t); + /* 1 = true, 0 = false, -1 = could not parse */ mapi_export int msetting_parse_bool(const char *text); diff --git a/clients/mapilib/writeurl.c b/clients/mapilib/writeurl.c new file mode 100644 --- /dev/null +++ b/clients/mapilib/writeurl.c @@ -0,0 +1,187 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2024, 2025 MonetDB Foundation; + * Copyright August 2008 - 2023 MonetDB B.V.; + * Copyright 1997 - July 2008 CWI. + */ + +#include "monetdb_config.h" + +#include "msettings.h" +#include "msettings_internal.h" + +struct outbuf { + char *buffer; + char *end; + char *pos; + size_t logical_size; +}; + +static void +ob_append1(struct outbuf *ob, char c) +{ + ob->logical_size += 1; + if (ob->pos < ob->end) + *ob->pos++ = c; +} + +static void +ob_append(struct outbuf *ob, const char *s) +{ + size_t len = strlen(s); + ob->logical_size += len; + size_t avail = ob->end - ob->pos; + if (avail < len) + len = avail; + memcpy(ob->pos, s, len); + ob->pos += len; +} + +static void +ob_append_escaped(struct outbuf *ob, const char *text, bool escape_colon) +{ + const char *hex = "0123456789abcdef"; + for (const char *s = text; *s; s++) { + int c = *s; + bool must_escape = false; + switch (c) { + case '#': + case '&': + case '=': + case '/': + case '?': + case '[': + case ']': + case '@': + case '%': + must_escape = true; + break; + case ':': + must_escape = escape_colon; + break; + default: + break; + } + if (must_escape) { + int lo = (c & 0x0F); + int hi = (c & 0xF0) >> 4; + ob_append1(ob, '%'); + ob_append1(ob, hex[hi]); + ob_append1(ob, hex[lo]); + } else { + ob_append1(ob, c); + } + } +} + +static void ob_printf(struct outbuf *ob, const char *fmt, ...) + __attribute__((__format__(__printf__, 2, 3))); + +static void +ob_printf(struct outbuf *ob, const char *fmt, ...) +{ + va_list ap; + size_t avail = ob->end - ob->pos; + + // vsnprintf wants to write the NUL so we use avail+1. + // (we left room for that) + va_start(ap, fmt); + int n = vsnprintf(ob->pos, avail + 1, fmt, ap); + va_end(ap); + assert(n >= 0); + size_t delta = (size_t)n; + ob->logical_size += delta; + ob->pos += (delta <= avail ? delta : avail); +} + +static void +format_url(struct outbuf *ob, const msettings *mp) +{ + ob_append(ob, msetting_bool(mp, MP_TLS) ? "monetdbs": "monetdb"); + ob_append(ob, "://"); + + const char *host = msetting_string(mp, MP_HOST); + if (*host == '\0') { + ob_append(ob, "localhost"); + } else if (strcmp(host, "localhost") == 0) { + ob_append(ob, "localhost."); + } else if (strchr(host, ':')) { + ob_append1(ob, '['); + ob_append_escaped(ob, host, false); + ob_append1(ob, ']'); + } else { + ob_append_escaped(ob, host, true); + } + + long port = msetting_long(mp, MP_PORT); + if (port > 0 && port < 65536 && port != 50000) { _______________________________________________ checkin-list mailing list -- checkin-list@monetdb.org To unsubscribe send an email to checkin-list-le...@monetdb.org