chiark / gitweb /
Doxygen for C test infrastructure
[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 int urldecode(struct sink *sink, const char *ptr, size_t n) {
36   int c, d1, d2;
37   
38   while(n-- > 0) {
39     switch(c = *ptr++) {
40     case '%':
41       if((d1 = unhexdigit(ptr[0])) == -1
42          || (d2 = unhexdigit(ptr[1])) == -1)
43         return -1;
44       c  = d1 * 16 + d2;
45       ptr += 2;
46       n -= 2;
47       break;
48     case '+':
49       c = ' ';
50       break;
51     default:
52       break;
53     }
54     if(sink_writec(sink,c) < 0)
55       return -1;
56   }
57   return 0;
58 }
59
60 static char *decode(const char *ptr, size_t n) {
61   struct dynstr d;
62   struct sink *s;
63
64   dynstr_init(&d);
65   s = sink_dynstr(&d);
66   if(urldecode(s, ptr, n))
67     return 0;
68   dynstr_terminate(&d);
69   return d.vec;
70 }
71
72 struct kvp *kvp_urldecode(const char *ptr, size_t n) {
73   struct kvp *kvp, **kk = &kvp, *k;
74   const char *q, *r, *top = ptr + n, *next;
75
76   while(ptr < top) {
77     *kk = k = xmalloc(sizeof *k);
78     if(!(q = memchr(ptr, '=', top - ptr)))
79       break;
80     if(!(k->name = decode(ptr, q - ptr))) break;
81     if((r = memchr(ptr, '&', top - ptr)))
82       next = r + 1;
83     else
84       next = r = top;
85     if(r < q)
86       break;
87     if(!(k->value = decode(q + 1, r - (q + 1)))) break;
88     kk = &k->next;
89     ptr = next;
90   }
91   *kk = 0;
92   return kvp;
93 }
94
95 int urlencode(struct sink *sink, const char *s, size_t n) {
96   unsigned char c;
97
98   while(n > 0) {
99     c = *s++;
100     n--;
101     switch(c) {
102     default:
103       if((c >= '0' && c <= '9')
104          || (c >= 'a' && c <= 'z')
105          || (c >= 'A' && c <= 'Z')) {
106         /* RFC2396 2.3 unreserved characters */
107       case '-':
108       case '_':
109       case '.':
110       case '!':
111       case '~':
112       case '*':
113       case '\'':
114       case '(':
115       case ')':
116         /* additional unreserved characters */
117       case '/':
118         if(sink_writec(sink, c) < 0)
119           return -1;
120       } else
121         if(sink_printf(sink, "%%%02x", (unsigned int)c) < 0)
122           return -1;
123     }
124   }
125   return 0;
126 }
127
128 /** @brief URL-encode @p s
129  * @param s String to encode
130  * @return Encoded string
131  */
132 char *urlencodestring(const char *s) {
133   struct dynstr d;
134
135   dynstr_init(&d);
136   urlencode(sink_dynstr(&d), s, strlen(s));
137   dynstr_terminate(&d);
138   return d.vec;
139 }
140
141 /** @brief URL-decode @p s
142  * @param s String to decode
143  * @param ns Length of string
144  * @return Decoded string or NULL
145  */
146 char *urldecodestring(const char *s, size_t ns) {
147   struct dynstr d;
148
149   dynstr_init(&d);
150   if(urldecode(sink_dynstr(&d), s, ns))
151     return NULL;
152   dynstr_terminate(&d);
153   return d.vec;
154 }
155
156 char *kvp_urlencode(const struct kvp *kvp, size_t *np) {
157   struct dynstr d;
158   struct sink *sink;
159
160   dynstr_init(&d);
161   sink = sink_dynstr(&d);
162   while(kvp) {
163     urlencode(sink, kvp->name, strlen(kvp->name));
164     dynstr_append(&d, '=');
165     urlencode(sink, kvp->value, strlen(kvp->value));
166     if((kvp = kvp->next))
167       dynstr_append(&d, '&');
168     
169   }
170   dynstr_terminate(&d);
171   if(np)
172     *np = d.nvec;
173   return d.vec;
174 }
175
176 int kvp_set(struct kvp **kvpp, const char *name, const char *value) {
177   struct kvp *k, **kk;
178
179   for(kk = kvpp; (k = *kk) && strcmp(name, k->name); kk = &k->next)
180     ;
181   if(k) {
182     if(value) {
183       if(strcmp(k->value, value)) {
184         k->value = xstrdup(value);
185         return 1;
186       } else
187         return 0;
188     } else {
189       *kk = k->next;
190       return 1;
191     }
192   } else {
193     if(value) {
194       *kk = k = xmalloc(sizeof *k);
195       k->name = xstrdup(name);
196       k->value = xstrdup(value);
197       return 1;
198     } else
199       return 0;
200   }
201 }
202
203 const char *kvp_get(const struct kvp *kvp, const char *name) {
204   for(;kvp && strcmp(kvp->name, name); kvp = kvp->next)
205     ;
206   return kvp ? kvp->value : 0;
207 }
208
209 struct kvp *kvp_make(const char *name, ...) {
210   const char *value;
211   struct kvp *kvp = 0, *k;
212   va_list ap;
213
214   va_start(ap, name);
215   while(name) {
216     value = va_arg(ap, const char *);
217     k = xmalloc(sizeof *k);
218     k->name = name;
219     k->value = value ? xstrdup(value) : value;
220     k->next = kvp;
221     kvp = k;
222     name = va_arg(ap, const char *);
223   }
224   va_end(ap);
225   return kvp;
226 }
227
228 /*
229 Local Variables:
230 c-basic-offset:2
231 comment-column:40
232 End:
233 */