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