-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuri.c
More file actions
125 lines (110 loc) · 2.85 KB
/
Copy pathuri.c
File metadata and controls
125 lines (110 loc) · 2.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <string.h>
#include "uri.h"
#include "utils.h"
#include "console.h"
static const char* valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%!$&\"()*+,;=:@";
static bool remove_dot_segment(URI* uri, bool last) {
if (uri->segments_count>0) {
if (strcmp(uri->segments[uri->segments_count-1], ".")==0) {
if (last) {
uri->segments[uri->segments_count-1][0] = '\0';
} else {
uri->segments_count--;
}
} else if (strcmp(uri->segments[uri->segments_count-1], "..")==0) {
if (uri->segments_count>1) {
if (last) {
uri->segments_count--;
uri->segments[uri->segments_count-1][0] = '\0';
} else {
uri->segments_count -= 2;
}
} else {
uri->valid = false;
return false;
}
}
}
return true;
}
URI* uri_new(Token token) {
URI* uri = allocate(NULL, sizeof(*uri));
uri->data = allocate(NULL, token.length + 1);
memcpy(uri->data, token.start, token.length);
uri->data[token.length] = '\0';
uri->path = NULL;
uri->path_len = 0;
uri->query = uri->data + token.length;
uri->query_len = 0;
size_t max_segments = 8;
uri->segments = allocate(NULL, max_segments * sizeof(*uri->segments));
uri->segments_count = 0;
uri->valid = true;
// validate URI starts with a forward slash
if (token.length==0 || uri->data[0]!='/') {
uri->valid = false;
return uri;
}
// scan the URI looking for segments (directories), the start of the query and invalid characters
bool in_path = true;
for (int i=0; i<token.length; i++) {
switch (uri->data[i]) {
case '/':
if (in_path) {
if (uri->segments_count == max_segments) {
max_segments *= 2;
uri->segments = allocate(uri->segments, max_segments * sizeof(*uri->segments));
}
uri->data[i] = '\0';
if (!remove_dot_segment(uri, false)) {
return uri;
}
uri->segments[uri->segments_count++] = uri->data + i + 1;
}
break;
case '?':
if (in_path) {
uri->data[i] = '\0';
uri->query = uri->data + i + 1;
uri->query_len = token.length - i + 1;
in_path = false;
}
break;
default:
if (strchr(valid_chars, uri->data[i])==NULL) {
uri->valid = false;
return uri;
}
}
}
// process last segement
if (!remove_dot_segment(uri, true)) {
return uri;
}
// build complete and rationalised path
size_t lens[uri->segments_count];
for (size_t i=0; i<uri->segments_count; i++) {
lens[i] = strlen(uri->segments[i]);
uri->path_len += 1 + lens[i];
}
uri->path = allocate(NULL, uri->path_len + 1);
size_t pos = 0;
for (size_t i=0; i<uri->segments_count; i++) {
uri->path[pos] = '/';
strcpy(uri->path + pos + 1, uri->segments[i]);
pos += 1 + lens[i];
}
uri->path[pos] = '\0';
// return URI
return uri;
}
void uri_free(URI* uri) {
if (uri != NULL) {
free(uri->data);
if (uri->path != NULL) {
free(uri->path);
}
free(uri->segments);
free(uri);
}
}