chiark / gitweb /
Add trackdb_deinit() stub.
[disorder] / lib / kvp.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
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  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
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  */
25
26 #include "common.h"
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
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  */
41 int 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
66 static 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
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  */
87 struct 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
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  */
116 int 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
149 /** @brief URL-encode @p s
150  * @param s String to encode
151  * @return Encoded string
152  */
153 char *urlencodestring(const char *s) {
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
162 /** @brief URL-decode @p s
163  * @param s String to decode
164  * @param ns Length of string
165  * @return Decoded string or NULL
166  */
167 char *urldecodestring(const char *s, size_t ns) {
168   struct dynstr d;
169
170   dynstr_init(&d);
171   if(urldecode(sink_dynstr(&d), s, ns))
172     return NULL;
173   dynstr_terminate(&d);
174   return d.vec;
175 }
176
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  */
182 char *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
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  */
216 int 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
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  */
251 const 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
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  */
266 struct 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;
276     k->value = value ? xstrdup(value) : "";
277     k->next = kvp;
278     kvp = k;
279     name = va_arg(ap, const char *);
280   }
281   va_end(ap);
282   return kvp;
283 }
284
285 /*
286 Local Variables:
287 c-basic-offset:2
288 comment-column:40
289 End:
290 */