chiark / gitweb /
Add many comments to server/play.c, in advance of possible
[disorder] / lib / kvp.c
CommitLineData
460b9539 1/*
2 * This file is part of DisOrder.
5aff007d 3 * Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
460b9539 4 *
e7eb3a27 5 * This program is free software: you can redistribute it and/or modify
460b9539 6 * it under the terms of the GNU General Public License as published by
e7eb3a27 7 * the Free Software Foundation, either version 3 of the License, or
460b9539 8 * (at your option) any later version.
e7eb3a27
RK
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
460b9539 15 * You should have received a copy of the GNU General Public License
e7eb3a27 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
460b9539 17 */
132a5a4a
RK
18/** @file lib/kvp.c
19 * @brief Linked list of key-value pairs
20 *
21 * Also supports URL encoding/decoding (of raw strings and kvp lists).
22 *
23 * For large sets of keys, see @ref lib/hash.c.
24 */
460b9539 25
05b75f8d 26#include "common.h"
460b9539 27
28#include "mem.h"
29#include "kvp.h"
30#include "log.h"
31#include "vector.h"
32#include "hex.h"
33#include "sink.h"
34
031e275e
RK
35/** @brief Decode a URL-encoded string to a ink
36 * @param sink Where to store result
37 * @param ptr Start of string
38 * @param n Length of string
39 * @return 0 on success, non-0 if string could not be decoded or sink write failed
40 */
460b9539 41int urldecode(struct sink *sink, const char *ptr, size_t n) {
42 int c, d1, d2;
43
44 while(n-- > 0) {
45 switch(c = *ptr++) {
46 case '%':
47 if((d1 = unhexdigit(ptr[0])) == -1
48 || (d2 = unhexdigit(ptr[1])) == -1)
49 return -1;
50 c = d1 * 16 + d2;
51 ptr += 2;
52 n -= 2;
53 break;
54 case '+':
55 c = ' ';
56 break;
57 default:
58 break;
59 }
60 if(sink_writec(sink,c) < 0)
61 return -1;
62 }
63 return 0;
64}
65
66static char *decode(const char *ptr, size_t n) {
67 struct dynstr d;
68 struct sink *s;
69
70 dynstr_init(&d);
71 s = sink_dynstr(&d);
72 if(urldecode(s, ptr, n))
73 return 0;
74 dynstr_terminate(&d);
75 return d.vec;
76}
77
031e275e
RK
78/** @brief Decode a URL-decoded key-value pair list
79 * @param ptr Start of input string
80 * @param n Length of input string
81 * @return @ref kvp of values from input
82 *
83 * The KVP is in the same order as the original input.
84 *
85 * If the original input contains duplicates names, so will the KVP.
86 */
460b9539 87struct kvp *kvp_urldecode(const char *ptr, size_t n) {
88 struct kvp *kvp, **kk = &kvp, *k;
89 const char *q, *r, *top = ptr + n, *next;
90
91 while(ptr < top) {
92 *kk = k = xmalloc(sizeof *k);
93 if(!(q = memchr(ptr, '=', top - ptr)))
94 break;
95 if(!(k->name = decode(ptr, q - ptr))) break;
96 if((r = memchr(ptr, '&', top - ptr)))
97 next = r + 1;
98 else
99 next = r = top;
100 if(r < q)
101 break;
102 if(!(k->value = decode(q + 1, r - (q + 1)))) break;
103 kk = &k->next;
104 ptr = next;
105 }
106 *kk = 0;
107 return kvp;
108}
109
031e275e
RK
110/** @brief URL-encode a string to a sink
111 * @param sink Where to send output
112 * @param s String to encode
113 * @param n Length of string to encode
114 * @return 0 on success or non-0 if sink write failed
115 */
460b9539 116int urlencode(struct sink *sink, const char *s, size_t n) {
117 unsigned char c;
118
119 while(n > 0) {
120 c = *s++;
121 n--;
122 switch(c) {
123 default:
124 if((c >= '0' && c <= '9')
125 || (c >= 'a' && c <= 'z')
126 || (c >= 'A' && c <= 'Z')) {
127 /* RFC2396 2.3 unreserved characters */
128 case '-':
129 case '_':
130 case '.':
131 case '!':
132 case '~':
133 case '*':
134 case '\'':
135 case '(':
136 case ')':
137 /* additional unreserved characters */
138 case '/':
139 if(sink_writec(sink, c) < 0)
140 return -1;
141 } else
142 if(sink_printf(sink, "%%%02x", (unsigned int)c) < 0)
143 return -1;
144 }
145 }
146 return 0;
147}
148
b12be54a
RK
149/** @brief URL-encode @p s
150 * @param s String to encode
151 * @return Encoded string
152 */
36bde473 153char *urlencodestring(const char *s) {
460b9539 154 struct dynstr d;
155
156 dynstr_init(&d);
157 urlencode(sink_dynstr(&d), s, strlen(s));
158 dynstr_terminate(&d);
159 return d.vec;
160}
161
b12be54a
RK
162/** @brief URL-decode @p s
163 * @param s String to decode
164 * @param ns Length of string
36bde473 165 * @return Decoded string or NULL
b12be54a 166 */
36bde473 167char *urldecodestring(const char *s, size_t ns) {
b12be54a
RK
168 struct dynstr d;
169
170 dynstr_init(&d);
36bde473 171 if(urldecode(sink_dynstr(&d), s, ns))
172 return NULL;
b12be54a
RK
173 dynstr_terminate(&d);
174 return d.vec;
175}
176
031e275e
RK
177/** @brief URL-encode a KVP
178 * @param kvp Linked list to encode
179 * @param np Where to store length (or NULL)
180 * @return Newly created string
181 */
460b9539 182char *kvp_urlencode(const struct kvp *kvp, size_t *np) {
183 struct dynstr d;
184 struct sink *sink;
185
186 dynstr_init(&d);
187 sink = sink_dynstr(&d);
188 while(kvp) {
189 urlencode(sink, kvp->name, strlen(kvp->name));
190 dynstr_append(&d, '=');
191 urlencode(sink, kvp->value, strlen(kvp->value));
192 if((kvp = kvp->next))
193 dynstr_append(&d, '&');
194
195 }
196 dynstr_terminate(&d);
197 if(np)
198 *np = d.nvec;
199 return d.vec;
200}
201
031e275e
RK
202/** @brief Set or remove a value in a @ref kvp
203 * @param kvpp Address of KVP head to modify
204 * @param name Key to search for
205 * @param value New value or NULL to delete
206 * @return 1 if any change was made otherwise 0
207 *
208 * If @p value is not NULL then the first matching key is replaced; if
209 * there was no matching key a new one is added at the end.
210 *
211 * If @p value is NULL then the first matching key is removed.
212 *
213 * If anything actually changes the return value is 1. If no actual
214 * change is made then 0 is returned instead.
215 */
460b9539 216int kvp_set(struct kvp **kvpp, const char *name, const char *value) {
217 struct kvp *k, **kk;
218
219 for(kk = kvpp; (k = *kk) && strcmp(name, k->name); kk = &k->next)
220 ;
221 if(k) {
222 if(value) {
223 if(strcmp(k->value, value)) {
224 k->value = xstrdup(value);
225 return 1;
226 } else
227 return 0;
228 } else {
229 *kk = k->next;
230 return 1;
231 }
232 } else {
233 if(value) {
234 *kk = k = xmalloc(sizeof *k);
235 k->name = xstrdup(name);
236 k->value = xstrdup(value);
237 return 1;
238 } else
239 return 0;
240 }
241}
242
031e275e
RK
243/** @brief Look up a value in a @ref kvp
244 * @param kvp Head of KVP linked list
245 * @param name Key to search for
246 * @return Value or NULL
247 *
248 * The returned value is owned by the KVP so must not be modified or
249 * freed.
250 */
460b9539 251const char *kvp_get(const struct kvp *kvp, const char *name) {
252 for(;kvp && strcmp(kvp->name, name); kvp = kvp->next)
253 ;
254 return kvp ? kvp->value : 0;
255}
256
031e275e
RK
257/** @brief Construct a KVP from arguments
258 * @param name First name
259 * @return Newly created KVP
260 *
261 * Arguments must come in name/value pairs and must be followed by a (char *)0.
262 *
263 * The order of the new KVP is not formally defined though the test
264 * programs rely on it nonetheless so update them if you change it.
265 */
b5b7e0bf
RK
266struct kvp *kvp_make(const char *name, ...) {
267 const char *value;
268 struct kvp *kvp = 0, *k;
269 va_list ap;
270
271 va_start(ap, name);
272 while(name) {
273 value = va_arg(ap, const char *);
274 k = xmalloc(sizeof *k);
275 k->name = name;
031e275e 276 k->value = value ? xstrdup(value) : "";
b5b7e0bf
RK
277 k->next = kvp;
278 kvp = k;
279 name = va_arg(ap, const char *);
280 }
281 va_end(ap);
282 return kvp;
283}
284
460b9539 285/*
286Local Variables:
287c-basic-offset:2
288comment-column:40
289End:
290*/