okra | C99 URI parser library. |
| download: http://git.y1.nz/archives/okra.tar.gz | |
| README | Files | Log | Refs |
merge.c
1 /* This file implements algorithms on already-parsed URIs. */
2
3 #include <string.h> /* strlen */
4 #include <stdlib.h> /* malloc, calloc */
5 #include <assert.h> /* assert */
6
7 #include "okra.h"
8
9 /* takes any string and allocates a fresh copy of it. */
10 char *
11 okra_strclone(const char *buffer)
12 {
13 if (buffer == NULL) return NULL;
14 char *ret = malloc((strlen(buffer)+1) * sizeof(char));
15 strcpy(ret, buffer);
16 return ret;
17 }
18
19 /* described in section 5.2.3 */
20 char *
21 okra_merge_paths(const OkraUri *base, const char *relative_path)
22 {
23 char *ret;
24 int relative_length, i, last_slash_index = 0;
25
26 relative_length = strlen(relative_path);
27
28 if (base->authority != NULL && base->path[0] == '\0') {
29 ret = malloc((relative_length+1) * sizeof(char));
30 ret[0] = '/';
31 strcpy(ret+1, relative_path);
32 return ret;
33 }
34
35 /* calculate the index of the right-most slash in base->path */
36 i = 0;
37 for (;;) {
38 if (base->path[i] == '/') last_slash_index = i;
39 if (base->path[i] == '\0') break;
40 i++;
41 }
42
43 ret = malloc((last_slash_index + relative_length + 1) * sizeof(char));
44 for (i = 0; i <= last_slash_index; i++)
45 ret[i] = base->path[i];
46
47 strcpy(ret + last_slash_index + 1, relative_path);
48
49 return ret;
50 }
51
52 /* described in section 5.2.4 */
53 #define IS_SEGEND(c)
54 char *
55 okra_remove_dot_segments(char *path)
56 {
57 int length, i, ri;
58 int *starts, start_count = 0;
59 char *ret;
60 char ch, ch1, ch2, ch3;
61
62 length = strlen(path);
63 starts = malloc((length+1) * sizeof(int));
64 ret = calloc(1, (length+1) * sizeof(char));
65 starts[start_count++] = path[0] == '/' ? 1 : 0;
66
67 i = ri = 0;
68 ch = ch1 = ch2 = ch3 = '/';
69 for (;;) {
70 ch3 = ch2; ch2 = ch1; ch1 = ch;
71 ch = path[i++];
72 ret[ri++] = ch;
73 ret[ri] = '\0';
74
75 /* History looks like "/../" or "/..\0": */
76 if (
77 (ch == '/' || ch == '\0') &&
78 ch1 == '.' &&
79 ch2 == '.' &&
80 ch3 == '/'
81 ) {
82 if (start_count > 1) start_count--;
83 ri = starts[start_count-1];
84 ret[ri] = '\0';
85 if (ch == '\0') break;
86 ch3 = ch2 = ch1 = ch = '/';
87 continue;
88 }
89
90 /* History looks like "/./" or "/.\0": */
91 if (
92 (ch == '/' || ch == '\0') &&
93 ch1 == '.' &&
94 ch2 == '/'
95 ) {
96 ri = starts[start_count-1];
97 ret[ri] = '\0';
98 if (ch == '\0') break;
99 ch3 = ch2 = ch1 = ch = '/';
100 continue;
101 }
102
103 if (ch == '/') starts[start_count++] = ri;
104 if (ch == '\0') break;
105 }
106 ret[ri] = '\0';
107 free(starts);
108 return ret;
109 }
110
111 /* refactored from psuedocode provided in section 5.2.2 */
112 OkraUri *
113 okra_merge(const OkraUri *base, const OkraUri *ref)
114 {
115 OkraUri *ret;
116
117 assert(base != NULL);
118 assert(ref != NULL);
119
120 ret = calloc(1, sizeof(OkraUri));
121 ret->fragment = okra_strclone(ref->fragment);
122
123 if (ref->scheme != NULL) {
124 ret->scheme = okra_strclone(ref->scheme);
125
126 ret->authority = okra_strclone(ref->authority);
127 ret->host = okra_strclone(ref->host);
128 ret->port = okra_strclone(ref->port);
129 ret->userinfo = okra_strclone(ref->userinfo);
130
131 ret->path = okra_strclone(ref->path);
132 ret->query = okra_strclone(ref->query);
133 goto finish_merge;
134 }
135 ret->scheme = okra_strclone(base->scheme);
136
137 if (ref->authority != NULL) {
138 ret->authority = okra_strclone(ref->authority);
139 ret->host = okra_strclone(ref->host);
140 ret->port = okra_strclone(ref->port);
141 ret->userinfo = okra_strclone(ref->userinfo);
142
143 ret->path = okra_remove_dot_segments(ref->path);
144 ret->query = okra_strclone(ref->query);
145 goto finish_merge;
146 }
147 ret->authority = okra_strclone(base->authority);
148 ret->host = okra_strclone(base->host);
149 ret->port = okra_strclone(base->port);
150 ret->userinfo = okra_strclone(base->userinfo);
151
152 if (ref->path[0] == '\0') {
153 ret->path = okra_strclone(base->path);
154 ret->query = okra_strclone(
155 ref->query == NULL ? base->query : ref->query
156 );
157 goto finish_merge;
158 }
159
160 ret->query = okra_strclone(ref->query);
161 if (ref->path[0] == '/') {
162 ret->path = okra_remove_dot_segments(ref->path);
163 goto finish_merge;
164 }
165
166 ret->path = okra_merge_paths(base, ref->path);
167 assert(ret->path != NULL);
168 ret->path = okra_remove_dot_segments(ret->path);
169
170 finish_merge:
171 ret->uri = okra_recompose(ret);
172 return ret;
173 }
174
175 /* Refactored from section 5.3 of [RFC3986]. */
176 char *
177 okra_recompose(OkraUri *uri)
178 {
179 char *ret, *cur;
180 int length = 0, i;
181 int scheme_length = 0;
182 int auth_length = 0;
183 int path_length = 0;
184 int query_length = 0;
185 int fragment_length = 0;
186
187 /* calculate length */
188 if (uri->scheme != NULL) {
189 scheme_length = strlen(uri->scheme) + 1; /* extra ":" */
190 length += scheme_length;
191 }
192 if (uri->authority != NULL) {
193 auth_length = strlen(uri->authority) + 2; /* extra "//" */
194 length += auth_length;
195 }
196 path_length = strlen(uri->path);
197 length += path_length;
198 if (uri->query != NULL) {
199 query_length = strlen(uri->query) + 1; /* extra "?" */
200 length += query_length;
201 }
202 if (uri->fragment != NULL) {
203 fragment_length = strlen(uri->fragment) + 1; /* extra "#" */
204 length += fragment_length;
205 }
206
207 ret = malloc((length+1) * sizeof(char));
208 cur = ret;
209
210 /* populate the buffer... */
211 if (uri->scheme != NULL) {
212 strcpy(cur, uri->scheme);
213 cur[scheme_length-1] = ':';
214 cur += scheme_length;
215 }
216 if (uri->authority != NULL) {
217 cur[0] = cur[1] = '/';
218 strcpy(cur+2, uri->authority);
219 cur += auth_length;
220 }
221 strcpy(cur, uri->path);
222 cur += path_length;
223 if (uri->query != NULL) {
224 cur[0] = '?';
225 strcpy(cur+1, uri->query);
226 cur += query_length;
227 }
228 if (uri->fragment != NULL) {
229 cur[0] = '#';
230 strcpy(cur+1, uri->fragment);
231 cur += fragment_length;
232 }
233
234 return ret;
235 }
236
237 void
238 okra_free(OkraUri *uri)
239 {
240 if (uri->scheme == NULL) free(uri->scheme);
241 if (uri->authority == NULL) free(uri->authority);
242 if (uri->host == NULL) free(uri->host);
243 if (uri->userinfo == NULL) free(uri->userinfo);
244 if (uri->port == NULL) free(uri->port);
245 if (uri->path == NULL) free(uri->path);
246 if (uri->query == NULL) free(uri->query);
247 if (uri->fragment == NULL) free(uri->fragment);
248 free(uri);
249 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.