Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024, 2025 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 :
15 : #include "msettings.h"
16 : #include "msettings_internal.h"
17 :
18 : struct outbuf {
19 : char *buffer;
20 : char *end;
21 : char *pos;
22 : size_t logical_size;
23 : };
24 :
25 : static void
26 309668 : ob_append1(struct outbuf *ob, char c)
27 : {
28 309668 : ob->logical_size += 1;
29 309668 : if (ob->pos < ob->end)
30 104810 : *ob->pos++ = c;
31 : }
32 :
33 : static void
34 61691 : ob_append(struct outbuf *ob, const char *s)
35 : {
36 61691 : size_t len = strlen(s);
37 61691 : ob->logical_size += len;
38 61691 : size_t avail = ob->end - ob->pos;
39 61691 : if (avail < len)
40 : len = avail;
41 61691 : memcpy(ob->pos, s, len);
42 61691 : ob->pos += len;
43 61691 : }
44 :
45 : static void
46 24648 : ob_append_escaped(struct outbuf *ob, const char *text, bool escape_colon)
47 : {
48 24648 : const char *hex = "0123456789abcdef";
49 269971 : for (const char *s = text; *s; s++) {
50 245323 : int c = *s;
51 245323 : bool must_escape = false;
52 245323 : switch (c) {
53 : case '#':
54 : case '&':
55 : case '=':
56 : case '/':
57 : case '?':
58 : case '[':
59 : case ']':
60 : case '@':
61 : case '%':
62 : must_escape = true;
63 : break;
64 : case ':':
65 6946 : must_escape = escape_colon;
66 6946 : break;
67 : default:
68 : break;
69 : }
70 6946 : if (must_escape) {
71 14064 : int lo = (c & 0x0F);
72 14064 : int hi = (c & 0xF0) >> 4;
73 14064 : ob_append1(ob, '%');
74 14064 : ob_append1(ob, hex[hi]);
75 14064 : ob_append1(ob, hex[lo]);
76 : } else {
77 322624 : ob_append1(ob, c);
78 : }
79 : }
80 24648 : }
81 :
82 : static void ob_printf(struct outbuf *ob, const char *fmt, ...)
83 : __attribute__((__format__(__printf__, 2, 3)));
84 :
85 : static void
86 3536 : ob_printf(struct outbuf *ob, const char *fmt, ...)
87 : {
88 3536 : va_list ap;
89 3536 : size_t avail = ob->end - ob->pos;
90 :
91 : // vsnprintf wants to write the NUL so we use avail+1.
92 : // (we left room for that)
93 3536 : va_start(ap, fmt);
94 3536 : int n = vsnprintf(ob->pos, avail + 1, fmt, ap);
95 3536 : va_end(ap);
96 3536 : assert(n >= 0);
97 3536 : size_t delta = (size_t)n;
98 3536 : ob->logical_size += delta;
99 3536 : ob->pos += (delta <= avail ? delta : avail);
100 3536 : }
101 :
102 : static void
103 16826 : format_url(struct outbuf *ob, const msettings *mp)
104 : {
105 28114 : ob_append(ob, msetting_bool(mp, MP_TLS) ? "monetdbs": "monetdb");
106 16826 : ob_append(ob, "://");
107 :
108 16826 : const char *host = msetting_string(mp, MP_HOST);
109 16826 : if (*host == '\0') {
110 12553 : ob_append(ob, "localhost");
111 4273 : } else if (strcmp(host, "localhost") == 0) {
112 1784 : ob_append(ob, "localhost.");
113 2489 : } else if (strchr(host, ':')) {
114 178 : ob_append1(ob, '[');
115 178 : ob_append_escaped(ob, host, false);
116 178 : ob_append1(ob, ']');
117 : } else {
118 2311 : ob_append_escaped(ob, host, true);
119 : }
120 :
121 16826 : long port = msetting_long(mp, MP_PORT);
122 16826 : if (port > 0 && port < 65536 && port != 50000) {
123 3536 : ob_printf(ob, ":%ld", port);
124 : }
125 :
126 16826 : const char *database = msetting_string(mp, MP_DATABASE);
127 16826 : const char *table_schema = msetting_string(mp, MP_TABLESCHEMA);
128 16826 : const char *table_name = msetting_string(mp, MP_TABLE);
129 16826 : bool include_table_name = (table_name[0] != '\0');
130 16826 : bool include_table_schema = (table_schema[0] != '\0') || include_table_name;
131 16826 : bool include_database = (database[0] != '\0') || include_table_schema;
132 : if (include_database) {
133 6194 : ob_append1(ob, '/');
134 6194 : ob_append_escaped(ob, database, true);
135 : }
136 16826 : if (include_table_schema) {
137 1438 : ob_append1(ob, '/');
138 1438 : ob_append_escaped(ob, table_schema, true);
139 : }
140 16826 : if (include_table_name) {
141 825 : ob_append1(ob, '/');
142 825 : ob_append_escaped(ob, table_name, true);
143 : }
144 :
145 : char sep = '?';
146 : char scratch1[40], scratch2[40];
147 : mparm parm;
148 521606 : for (int i = 0; (parm = mparm_enumerate(i)) != MP_UNKNOWN; i++) {
149 504780 : if (parm == MP_IGNORE || mparm_is_core(parm))
150 134608 : continue;
151 370172 : const char *value = msetting_as_string(mp, parm, scratch1, sizeof(scratch1));
152 370172 : const char *default_value = msetting_as_string(msettings_default, parm, scratch2, sizeof(scratch2));
153 370172 : if (strcmp(value, default_value) == 0)
154 356470 : continue;
155 : // render it
156 13702 : ob_append1(ob, sep);
157 13702 : sep = '&';
158 13702 : ob_append(ob, mparm_name(parm));
159 13702 : ob_append1(ob, '=');
160 13702 : ob_append_escaped(ob, value, true);
161 : }
162 16826 : }
163 :
164 : size_t
165 16826 : msettings_write_url(const msettings *mp, char *buffer, size_t buffer_size)
166 : {
167 16826 : char scratch[10];
168 16826 : if (buffer_size == 0) {
169 0 : buffer = scratch;
170 0 : buffer_size = sizeof(scratch);
171 : }
172 :
173 16826 : struct outbuf ob = {
174 : .buffer = buffer,
175 16826 : .end = buffer + buffer_size - 1,
176 : .pos = buffer,
177 : .logical_size = 0,
178 : };
179 : // to ease debugging
180 16826 : *ob.end = '\0';
181 :
182 16826 : format_url(&ob, mp);
183 :
184 16826 : assert(ob.pos <= ob.end);
185 16826 : *ob.pos = '\0';
186 16826 : return ob.logical_size;
187 : }
|