Add yaz_array-to_uri_c
[yaz-moved-to-github.git] / src / uri.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file uri.c
7  * \brief Implements URI utilities.
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdlib.h>
14 #include <yaz/srw.h>
15 #include <yaz/matchstr.h>
16 #include <yaz/yaz-iconv.h>
17
18 static int hex_digit (int ch)
19 {
20     if (ch >= '0' && ch <= '9')
21         return ch - '0';
22     else if (ch >= 'a' && ch <= 'f')
23         return ch - 'a'+10;
24     else if (ch >= 'A' && ch <= 'F')
25         return ch - 'A'+10;
26     return -1;
27 }
28
29 static void encode_uri_char(char *dst, char ch)
30 {
31     /*  mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" */
32     if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
33         (ch >= '0' && ch <= '9') || strchr("-_.!~*'(|)", ch))
34     {
35         dst[0] = ch;
36         dst[1] = '\0';
37     }
38     else
39     {
40         dst[0] = '%';
41         sprintf(dst+1, "%02X", (unsigned char ) ch);
42     }
43 }
44
45 void yaz_encode_uri_component(char *dst, const char *uri)
46 {
47     for (; *uri; uri++)
48     {
49         encode_uri_char(dst, *uri);
50         dst += strlen(dst);
51     }
52     *dst = '\0';
53 }
54
55 static unsigned char decode_uri_char(const char *path, size_t *len)
56 {
57     unsigned char ch;
58     if (*path == '+')
59     {
60         ch = ' ';
61         *len = 1;
62     }
63     else if (*path == '%' && *len >= 3)
64     {
65         int d1 = hex_digit(path[1]);
66         int d2 = hex_digit(path[2]);
67         if (d1 >= 0 && d2 >= 0)
68         {
69             ch = d1 * 16 + d2;
70             *len = 3;
71         }
72         else
73         {
74             ch = *path;
75             *len = 1;
76         }
77     }
78     else
79     {
80         ch = *path;
81         *len = 1;
82     }
83     return ch;
84 }
85
86 void yaz_decode_uri_component(char *dst, const char *uri, size_t len)
87 {
88     while (len)
89     {
90         size_t sz = len;
91         *dst++ = decode_uri_char(uri, &sz);
92         uri += sz;
93         len = len - sz;
94     }
95     *dst = '\0';
96 }
97
98 void yaz_array_to_uri_c(char **path, ODR o, const char **name, const char **value)
99 {
100     size_t i, szp = 0, sz = 1;
101     for(i = 0; name[i]; i++)
102         sz += strlen(name[i]) + 3 + strlen(value[i]) * 3;
103     *path = (char *) odr_malloc(o, sz);
104
105     for(i = 0; name[i]; i++)
106     {
107         size_t ilen;
108         if (i)
109             (*path)[szp++] = '&';
110         ilen = strlen(name[i]);
111         memcpy(*path+szp, name[i], ilen);
112         szp += ilen;
113         (*path)[szp++] = '=';
114
115         yaz_encode_uri_component(*path + szp, value[i]);
116         szp += strlen(*path + szp);
117     }
118     (*path)[szp] = '\0';
119 }
120
121 void yaz_array_to_uri(char **path, ODR o, char **name, char **value)
122 {
123     yaz_array_to_uri_c(path, o, (const char **) name, (const char **)value);
124 }
125
126 int yaz_uri_to_array(const char *path, ODR o, char ***name, char ***val)
127 {
128     int no = 2;
129     const char *cp;
130     *name = 0;
131     if (*path == '?')
132         path++;
133     if (!*path)
134         return 0;
135     cp = path;
136     while ((cp = strchr(cp, '&')))
137     {
138         cp++;
139         no++;
140         while (*cp && *cp != '=' && *cp != '&')
141         {
142             /* check that x-form names looks sane */
143             if (*cp <= ' ' || *cp >= 127)
144                 return 0;
145             cp++;
146         }
147     }
148     *name = (char **) odr_malloc(o, no * sizeof(char*));
149     *val = (char **) odr_malloc(o, no * sizeof(char*));
150
151     for (no = 0; *path; no++)
152     {
153         while (*path == '&')
154             path++;
155         if (!*path)
156             break;
157
158         for (cp = path; *cp && *cp != '=' && *cp != '&'; cp++)
159             ;
160
161         (*name)[no] = odr_strdupn(o, path, cp - path);
162         path = cp;
163         if (*path == '=')
164         {
165             size_t i = 0;
166             char *ret;
167             path++;
168             for (cp = path; *cp && *cp != '&'; cp++)
169                 ;
170             (*val)[no] = ret = (char *) odr_malloc(o, cp - path + 1);
171             while (*path && *path != '&')
172             {
173                 size_t l = 3;
174                 ret[i++] = decode_uri_char(path, &l);
175                 path += l;
176             }
177             ret[i] = '\0';
178         }
179         else
180             (*val)[no] = odr_strdup(o, "");
181     }
182     (*name)[no] = 0;
183     (*val)[no] = 0;
184     return no;
185 }
186
187
188 /*
189  * Local variables:
190  * c-basic-offset: 4
191  * c-file-style: "Stroustrup"
192  * indent-tabs-mode: nil
193  * End:
194  * vim: shiftwidth=4 tabstop=8 expandtab
195  */
196