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

Reply via email to