File Coverage

src/yuarel.c
Criterion Covered Total %
statement 58 99 58.5
branch 29 72 40.2
condition n/a
subroutine n/a
pod n/a
total 87 171 50.8


line stmt bran cond sub pod time code
1             /**
2             * Copyright (C) 2016,2017 Jack Engqvist Johansson
3             *
4             * Permission is hereby granted, free of charge, to any person obtaining a copy
5             * of this software and associated documentation files (the "Software"), to deal
6             * in the Software without restriction, including without limitation the rights
7             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8             * copies of the Software, and to permit persons to whom the Software is
9             * furnished to do so, subject to the following conditions:
10             *
11             * The above copyright notice and this permission notice shall be included in all
12             * copies or substantial portions of the Software.
13             *
14             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20             * SOFTWARE.
21             */
22             #include
23             #include
24             #include
25             #include "yuarel.h"
26              
27             /**
28             * Parse a non null terminated string into an integer.
29             *
30             * str: the string containing the number.
31             * len: Number of characters to parse.
32             */
33             static inline int
34 2           natoi(const char *str, size_t len)
35             {
36 2           int r = 0;
37             unsigned long i;
38              
39 10 100         for (i = 0; i < len; i++) {
40 8           r *= 10;
41 8           r += str[i] - '0';
42             }
43              
44 2           return r;
45             }
46              
47             /**
48             * Check if a URL is relative (no scheme and hostname).
49             *
50             * url: the string containing the URL to check.
51             *
52             * Returns 1 if relative, otherwise 0.
53             */
54             static inline int
55 18           is_relative(const char *url)
56             {
57 18           return (*url == '/') ? 1 : 0;
58             }
59              
60             /**
61             * Parse the scheme of a URL by inserting a null terminator after the scheme.
62             *
63             * str: the string containing the URL to parse. Will be modified.
64             *
65             * Returns a pointer to the hostname on success, otherwise NULL.
66             */
67             static inline char *
68 18           parse_scheme(char *str)
69             {
70             char *s;
71              
72             /* If not found or first in string, return error */
73 18           s = strchr(str, ':');
74 18 50         if (s == NULL || s == str) {
    50          
75 0           return NULL;
76             }
77              
78             /* If not followed by two slashes, return error */
79 18 50         if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
    50          
    50          
    50          
80 0           return NULL;
81             }
82              
83 18           *s = '\0'; // Replace ':' with NULL
84              
85 18           return s + 3;
86             }
87              
88             /**
89             * Find a character in a string, replace it with '\0' and return the next
90             * character in the string.
91             *
92             * str: the string to search in.
93             * find: the character to search for.
94             *
95             * Returns a pointer to the character after the one to search for. If not
96             * found, NULL is returned.
97             */
98             static inline char *
99 54           find_and_terminate(char *str, char find)
100             {
101 54           str = strchr(str, find);
102 54 100         if (NULL == str) {
103 37           return NULL;
104             }
105              
106 17           *str = '\0';
107 17           return str + 1;
108             }
109              
110             /* Yes, the following functions could be implemented as preprocessor macros
111             instead of inline functions, but I think that this approach will be more
112             clean in this case. */
113             static inline char *
114 18           find_fragment(char *str)
115             {
116 18           return find_and_terminate(str, '#');
117             }
118              
119             static inline char *
120 18           find_query(char *str)
121             {
122 18           return find_and_terminate(str, '?');
123             }
124              
125             static inline char *
126 18           find_path(char *str)
127             {
128 18           return find_and_terminate(str, '/');
129             }
130              
131             /**
132             * Parse a URL string to a struct.
133             *
134             * url: pointer to the struct where to store the parsed URL parts.
135             * u: the string containing the URL to be parsed.
136             *
137             * Returns 0 on success, otherwise -1.
138             */
139             int
140 18           yuarel_parse(struct yuarel *url, char *u)
141             {
142 18 50         if (NULL == url || NULL == u) {
    50          
143 0           return -1;
144             }
145              
146 18           memset(url, 0, sizeof (struct yuarel));
147              
148             /* (Fragment) */
149 18           url->fragment = find_fragment(u);
150              
151             /* (Query) */
152 18           url->query = find_query(u);
153              
154             /* Relative URL? Parse scheme and hostname */
155 18 50         if (!is_relative(u)) {
156             /* Scheme */
157 18           url->scheme = u;
158 18           u = parse_scheme(u);
159 18 50         if (u == NULL) {
160 0           return -1;
161             }
162              
163             /* Host */
164 18 50         if ('\0' == *u) {
165 0           return -1;
166             }
167 18           url->host = u;
168              
169             /* (Path) */
170 18           url->path = find_path(u);
171              
172             /* (Credentials) */
173 18           u = strchr(url->host, '@');
174 18 100         if (NULL != u) {
175             /* Missing credentials? */
176 6 50         if (u == url->host) {
177 0           return -1;
178             }
179              
180 6           url->username = url->host;
181 6           url->host = u + 1;
182 6           *u = '\0';
183              
184 6           u = strchr(url->username, ':');
185 6 50         if (NULL == u) {
186 0           return -1;
187             }
188              
189 6           url->password = u + 1;
190 6           *u = '\0';
191             }
192              
193             /* Missing hostname? */
194 18 50         if ('\0' == *url->host) {
195 0           return -1;
196             }
197              
198             /* (Port) */
199 18           u = strchr(url->host, ':');
200 18 100         if (NULL != u && (NULL == url->path || u < url->path)) {
    100          
    50          
201 7           *(u++) = '\0';
202 7 50         if ('\0' == *u) {
203 0           return -1;
204             }
205              
206 7 100         if (url->path) {
207 2           url->port = natoi(u, url->path - u - 1);
208             } else {
209 5           url->port = atoi(u);
210             }
211             }
212              
213             /* Missing hostname? */
214 18 50         if ('\0' == *url->host) {
215 0           return -1;
216             }
217             } else {
218             /* (Path) */
219 0           url->path = find_path(u);
220             }
221              
222 18           return 0;
223             }
224              
225             /**
226             * Split a path into several strings.
227             *
228             * No data is copied, the slashed are used as null terminators and then
229             * pointers to each path part will be stored in **parts. Double slashes will be
230             * treated as one.
231             *
232             * path: the path to split.
233             * parts: a pointer to an array of (char *) where to store the result.
234             * max_parts: max number of parts to parse.
235             */
236             int
237 0           yuarel_split_path(char *path, char **parts, int max_parts)
238             {
239 0           int i = 0;
240              
241 0 0         if (NULL == path || '\0' == *path) {
    0          
242 0           return -1;
243             }
244              
245             do {
246             /* Forward to after slashes */
247 0 0         while (*path == '/') path++;
248              
249 0 0         if ('\0' == *path) {
250 0           break;
251             }
252              
253 0           parts[i++] = path;
254              
255 0           path = strchr(path, '/');
256 0 0         if (NULL == path) {
257 0           break;
258             }
259              
260 0           *(path++) = '\0';
261 0 0         } while (i < max_parts);
262              
263 0           return i;
264             }
265              
266             int
267 0           yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params)
268             {
269 0           int i = 0;
270              
271 0 0         if (NULL == query || '\0' == *query) {
    0          
272 0           return -1;
273             }
274              
275 0           params[i++].key = query;
276 0 0         while (i < max_params && NULL != (query = strchr(query, delimiter))) {
    0          
277 0           *query = '\0';
278 0           params[i].key = ++query;
279 0           params[i].val = NULL;
280              
281             /* Go back and split previous param */
282 0 0         if (i > 0) {
283 0 0         if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
284 0           *(params[i - 1].val)++ = '\0';
285             }
286             }
287 0           i++;
288             }
289              
290             /* Go back and split last param */
291 0 0         if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
292 0           *(params[i - 1].val)++ = '\0';
293             }
294              
295 0           return i;
296             }