diff options
Diffstat (limited to 'src/gmutil.c')
-rw-r--r-- | src/gmutil.c | 138 |
1 files changed, 95 insertions, 43 deletions
diff --git a/src/gmutil.c b/src/gmutil.c index af090574..2008ea36 100644 --- a/src/gmutil.c +++ b/src/gmutil.c | |||
@@ -6,30 +6,36 @@ | |||
6 | 6 | ||
7 | void init_Url(iUrl *d, const iString *text) { | 7 | void init_Url(iUrl *d, const iString *text) { |
8 | iRegExp *absPat = | 8 | iRegExp *absPat = |
9 | new_RegExp("(.+)://([^/:?]*)(:[0-9]+)?([^?]*)(\\?.*)?", caseInsensitive_RegExpOption); | 9 | new_RegExp("([a-z]+:)?(//[^/:?]*)(:[0-9]+)?([^?]*)(\\?.*)?", caseInsensitive_RegExpOption); |
10 | iRegExpMatch m; | 10 | iRegExpMatch m; |
11 | if (matchString_RegExp(absPat, text, &m)) { | 11 | if (matchString_RegExp(absPat, text, &m)) { |
12 | d->protocol = capturedRange_RegExpMatch(&m, 1); | 12 | d->protocol = capturedRange_RegExpMatch(&m, 1); |
13 | d->host = capturedRange_RegExpMatch(&m, 2); | 13 | d->host = capturedRange_RegExpMatch(&m, 2); |
14 | d->port = capturedRange_RegExpMatch(&m, 3); | 14 | if (!isEmpty_Range(&d->host)) { |
15 | d->host.start += 2; /* skip the double slash */ | ||
16 | } | ||
17 | d->port = capturedRange_RegExpMatch(&m, 3); | ||
15 | if (!isEmpty_Range(&d->port)) { | 18 | if (!isEmpty_Range(&d->port)) { |
16 | /* Don't include the colon. */ | 19 | d->port.start++; /* omit the colon */ |
17 | d->port.start++; | ||
18 | } | 20 | } |
19 | d->path = capturedRange_RegExpMatch(&m, 4); | 21 | d->path = capturedRange_RegExpMatch(&m, 4); |
20 | d->query = capturedRange_RegExpMatch(&m, 5); | 22 | d->query = capturedRange_RegExpMatch(&m, 5); |
21 | } | 23 | } |
22 | else { | 24 | else { |
23 | /* Must be a relative path. */ | 25 | /* Must be a relative path. */ |
24 | iZap(*d); | 26 | iZap(*d); |
25 | iRegExp *relPat = new_RegExp("([^?]*)(\\?.*)?", 0); | 27 | iRegExp *relPat = new_RegExp("([a-z]+:)?([^?]*)(\\?.*)?", 0); |
26 | if (matchString_RegExp(relPat, text, &m)) { | 28 | if (matchString_RegExp(relPat, text, &m)) { |
27 | d->path = capturedRange_RegExpMatch(&m, 1); | 29 | d->protocol = capturedRange_RegExpMatch(&m, 1); |
28 | d->query = capturedRange_RegExpMatch(&m, 2); | 30 | d->path = capturedRange_RegExpMatch(&m, 2); |
31 | d->query = capturedRange_RegExpMatch(&m, 3); | ||
29 | } | 32 | } |
30 | iRelease(relPat); | 33 | iRelease(relPat); |
31 | } | 34 | } |
32 | iRelease(absPat); | 35 | iRelease(absPat); |
36 | if (!isEmpty_Range(&d->protocol)) { | ||
37 | d->protocol.end--; /* omit the colon */ | ||
38 | } | ||
33 | } | 39 | } |
34 | 40 | ||
35 | static iRangecc dirPath_(iRangecc path) { | 41 | static iRangecc dirPath_(iRangecc path) { |
@@ -38,51 +44,97 @@ static iRangecc dirPath_(iRangecc path) { | |||
38 | return (iRangecc){ path.start, path.start + pos }; | 44 | return (iRangecc){ path.start, path.start + pos }; |
39 | } | 45 | } |
40 | 46 | ||
47 | iLocalDef iBool isDef_(iRangecc cc) { | ||
48 | return !isEmpty_Range(&cc); | ||
49 | } | ||
50 | |||
51 | static iRangecc prevPathSeg_(const char *end, const char *start) { | ||
52 | iRangecc seg = { end, end }; | ||
53 | do { | ||
54 | seg.start--; | ||
55 | } while (*seg.start != '/' && seg.start != start); | ||
56 | return seg; | ||
57 | } | ||
58 | |||
59 | void cleanUrlPath_String(iString *d) { | ||
60 | iString clean; | ||
61 | init_String(&clean); | ||
62 | iUrl parts; | ||
63 | init_Url(&parts, d); | ||
64 | iRangecc seg = iNullRange; | ||
65 | while (nextSplit_Rangecc(&parts.path, "/", &seg)) { | ||
66 | if (equal_Rangecc(&seg, "..")) { | ||
67 | /* Back up one segment. */ | ||
68 | iRangecc last = prevPathSeg_(constEnd_String(&clean), constBegin_String(&clean)); | ||
69 | truncate_Block(&clean.chars, last.start - constBegin_String(&clean)); | ||
70 | } | ||
71 | else if (equal_Rangecc(&seg, ".")) { | ||
72 | /* Skip it. */ | ||
73 | } | ||
74 | else { | ||
75 | appendCStr_String(&clean, "/"); | ||
76 | appendRange_String(&clean, seg); | ||
77 | } | ||
78 | } | ||
79 | if (endsWith_Rangecc(&parts.path, "/")) { | ||
80 | appendCStr_String(&clean, "/"); | ||
81 | } | ||
82 | /* Replace with the new path. */ | ||
83 | if (cmpCStrNSc_Rangecc(&parts.path, cstr_String(&clean), size_String(&clean), &iCaseSensitive)) { | ||
84 | const size_t pos = parts.path.start - constBegin_String(d); | ||
85 | remove_Block(&d->chars, pos, size_Range(&parts.path)); | ||
86 | insertData_Block(&d->chars, pos, cstr_String(&clean), size_String(&clean)); | ||
87 | } | ||
88 | deinit_String(&clean); | ||
89 | } | ||
90 | |||
41 | const iString *absoluteUrl_String(const iString *d, const iString *urlMaybeRelative) { | 91 | const iString *absoluteUrl_String(const iString *d, const iString *urlMaybeRelative) { |
42 | if (indexOfCStr_String(urlMaybeRelative, "://") != iInvalidPos) { | 92 | iUrl orig; |
43 | /* Already absolute. */ | 93 | iUrl rel; |
94 | init_Url(&orig, d); | ||
95 | init_Url(&rel, urlMaybeRelative); | ||
96 | if (equalCase_Rangecc(&rel.protocol, "data")) { | ||
97 | /* Special case, the contents should be left unparsed. */ | ||
44 | return urlMaybeRelative; | 98 | return urlMaybeRelative; |
45 | } | 99 | } |
46 | iUrl parts; | 100 | const iBool isRelative = !isDef_(rel.host); |
47 | init_Url(&parts, d); | 101 | iRangecc protocol = range_CStr("gemini"); |
48 | iString *absolute = new_String(); | 102 | if (isDef_(rel.protocol)) { |
49 | appendRange_String(absolute, parts.protocol); | 103 | protocol = rel.protocol; |
50 | appendCStr_String(absolute, "://"); | 104 | } |
51 | appendRange_String(absolute, parts.host); | 105 | else if (isRelative && isDef_(orig.protocol)) { |
52 | if (!isEmpty_Range(&parts.port)) { | 106 | protocol = orig.protocol; |
53 | appendCStr_String(absolute, ":"); | ||
54 | appendRange_String(absolute, parts.port); | ||
55 | } | 107 | } |
56 | if (startsWith_String(urlMaybeRelative, "/")) { | 108 | iString *absolute = collectNew_String(); |
57 | append_String(absolute, urlMaybeRelative); | 109 | appendRange_String(absolute, protocol); |
110 | appendCStr_String(absolute, "://"); { | ||
111 | const iUrl *selHost = isDef_(rel.host) ? &rel : &orig; | ||
112 | appendRange_String(absolute, selHost->host); | ||
113 | if (!isEmpty_Range(&selHost->port)) { | ||
114 | appendCStr_String(absolute, ":"); | ||
115 | appendRange_String(absolute, selHost->port); | ||
116 | } | ||
117 | } | ||
118 | if (isDef_(rel.protocol) || isDef_(rel.host) || startsWith_Rangecc(&rel.path, "/")) { | ||
119 | appendRange_String(absolute, rel.path); /* absolute path */ | ||
58 | } | 120 | } |
59 | else { | 121 | else { |
60 | iRangecc relPath = range_String(urlMaybeRelative); | 122 | if (!endsWith_Rangecc(&orig.path, "/")) { |
61 | iRangecc dir = dirPath_(parts.path); | 123 | /* Referencing a file. */ |
62 | for (;;) { | 124 | appendRange_String(absolute, dirPath_(orig.path)); |
63 | if (equal_Rangecc(&relPath, ".")) { | 125 | } |
64 | relPath.start++; | 126 | else { |
65 | } | 127 | /* Referencing a directory. */ |
66 | else if (startsWith_Rangecc(&relPath, "./")) { | 128 | appendRange_String(absolute, orig.path); |
67 | relPath.start += 2; | ||
68 | } | ||
69 | else if (equal_Rangecc(&relPath, "..")) { | ||
70 | relPath.start += 2; | ||
71 | dir = dirPath_(dir); | ||
72 | } | ||
73 | else if (startsWith_Rangecc(&relPath, "../")) { | ||
74 | relPath.start += 3; | ||
75 | dir = dirPath_(dir); | ||
76 | } | ||
77 | else break; | ||
78 | } | 129 | } |
79 | appendRange_String(absolute, dir); | ||
80 | if (!endsWith_String(absolute, "/")) { | 130 | if (!endsWith_String(absolute, "/")) { |
81 | appendCStr_String(absolute, "/"); | 131 | appendCStr_String(absolute, "/"); |
82 | } | 132 | } |
83 | appendRange_String(absolute, relPath); | 133 | appendRange_String(absolute, rel.path); |
84 | } | 134 | } |
85 | return collect_String(absolute); | 135 | appendRange_String(absolute, rel.query); |
136 | cleanUrlPath_String(absolute); | ||
137 | return absolute; | ||
86 | } | 138 | } |
87 | 139 | ||
88 | void urlEncodeSpaces_String(iString *d) { | 140 | void urlEncodeSpaces_String(iString *d) { |