✓ProveIt
Examples / Report #3465094 Saved analysis

Path Traversal Bypass in file:// URLs Due to Incomplete URL-Encoded Path Normalization

curl · 2025-12-1523.9s analysis · $0.029 in model calls

What the report claims

Path traversal bypass in file:// URLs due to incomplete URL-encoded path normalization in dedotdotify()

Compare with curl's recorded outcome

Labeled AI slop

curl's maintainer lists this report as AI slop

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Judgment withdrawn. The answer failed the evidence check.
See the withdrawn model reasoning

The following reasoning failed the citation check and does not support the final finding.

  • dedotdotify() uses ISSLASH macro that only matches literal '/', so %2f%2e%2e%2f (encoded /../) bypasses normalization entirely
  • Unit test unit1395.c:44 confirms this behavior: input "%2f%2e%2e%2f" produces identical output
  • After bypassing dedotdotify, Curl_urldecode in file.c:192-193 decodes the path to /../../etc/passwd
  • The decoded path is passed directly to curlx_open at file.c:263 with no validation for '..' sequences
  • This enables arbitrary file read via file:// URLs when libcurl processes user-controlled URLs (SSRF scenario)
  • RFC 3986 normalization order requires percent-decoding before dot-segment removal; curl does it in reverse
How this analysis ran 6 claims · 10 citations verified

Evidence check: judge quoted an external source that does not say that (https://rfc-editor.org/errata/eid2525)

Checked against curl master on 2025-12-15 (6f3124eaa2c4). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
partially grounded
no model · 25 references checked out, 1 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
6 claims · 3993 tokens · $0.0004
3 · Evidence
11 code snippets
real curl source at 6f3124eaa2c4 · 6 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
21248 tokens · 13.8s · $0.0290
5 · Receipts
10 of 11 citations verified
every citation must point at code the judge was shown
Inspect the withdrawn draft reply

This draft belongs to the withdrawn judgment. It may contain unsupported claims.

Thank you for the detailed report. The code confirms that dedotdotify() only checks for literal '/' via ISSLASH, so URL-encoded traversal sequences like %2f%2e%2e%2f bypass normalization. The unit test at unit1395.c:44 documents this behavior. After decoding in file.c, the path is used in curlx_open with no validation. This is a valid security issue because it bypasses curl's documented path traversal protection and enables arbitrary file read in SSRF scenarios. We will address this by either running dedotdotify after percent-decoding or making it handle encoded slashes.

Inspect all claims and source code 6 claims · 11 code excerpts

This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.

Report claims

C1flaw supported

dedotdotify() only recognizes literal forward slashes and does not handle URL-encoded slashes (%2f or %2F)

Read the reasoning

The ISSLASH macro at line 777 explicitly checks only for literal '/'. The dedotdotify loop at line 836 uses this macro, so URL-encoded slashes (%2f/%2F) are not treated as segment delimiters and the traversal sequences pass through unchanged.

C2location supported

lib/urlapi.c:836 in dedotdotify() function

Read the reasoning

Line 836 is the exact location where dedotdotify checks for slashes using ISSLASH, confirming the claim's location reference.

C3trigger supported

Attacker provides a file:// URL with URL-encoded path traversal sequences such as %2f%2e%2e%2f

Read the reasoning

The attack vector is plausible: an attacker provides a file:// URL with percent-encoded traversal sequences. The path reaches dedotdotify while still encoded (urlapi.c:1233), bypasses normalization, then gets decoded in file.c:192-193.

C4impact supported

URL-encoded traversal sequences bypass normalization and result in arbitrary file read after URL decoding

Read the reasoning

The flow is confirmed: dedotdotify runs on encoded path (misses %2f%2e%2e%2f), then Curl_urldecode decodes it to /../../etc/passwd, and curlx_open uses it directly with no intermediate validation.

C5reproduction supported

Unit test tests/unit/unit1395.c line 44 shows that "%2f%2e%2e%2f" is not normalized and passes through unchanged

Read the reasoning

The deterministic check confirms this test case exists and shows the encoded traversal sequence passes through dedotdotify unchanged.

tests/unit/unit1395.c:44
C6reproduction supported

After URL decoding in lib/file.c:192-193 the path becomes /../../etc/passwd and is used directly in curlx_open() at line 263 with no validation

Read the reasoning

Lines 192-193 decode the path, and line 263 uses the decoded path directly in curlx_open. No validation (strstr, strchr, or custom checks for '..') exists in file.c between these points.

Deterministic checks that failed

  • line none of ISSLASH, while, Curl_urldecode, curlx_open appear near line 44 at 6f3124eaa2c4; it reads: '{ "%2f%2e%2e%2f", "%2f%2e%2e%2f" },'

What the report says about other projects

confirmed The `dedotdotify()` function is described as removing path traversal sequences according to RFC 3986.

“The `dedotdotify()` function is described as removing path traversal sequences according to RFC 3986.” source

confirmed RFC 3986 section 6.2 specifies that percent-encoding normalization should happen before path segment normalization during URI comparison.

“Percent encoding normalization: Next, any percent-encoded triplets that correspond to unreserved characters can be decoded. Path segment normalization: Any dot-segments can be removed from the path.” source

Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: The `dedotdotify()` function in `lib/urlapi.c` is responsible for removing path traversal sequences (`../` and `./`) from URLs according to RFC 3986. However, the function only recognizes literal forward slashes (`/`) when identifying path segments and does not handle URL-encoded slashes (`%2f` or `%2F`). This allows an attacker to bypass path traversal protection in file:// URLs by using URL-encoded path traversal sequences. **Vulnerability Flow:** 1. Attacker provides a file:// URL with URL-encoded path traversal: `file:///%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd` 2. The path `%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd` is processed by `dedotdotify()` while still URL-encoded 3. `dedotdotify()` uses `ISSLASH(x)` macro defined as `((x) == '/')` which only checks for literal `/`, not `%2f` 4. The URL-encoded traversal sequence `%2f%2e%2e%2f` passes through `dedotdotify()` unchanged 5. In `file.c:192-193`, `Curl_urldecode()` decodes the path, converting `%2f%2e%2e%2f` to `/../` 6. The decoded path `/../../etc/passwd` is used directly in `curlx_open()` at line 263 with **no additional validation** 7. Path traversal succeeds, allowing access to arbitrary files **Proof of No Mitigation:** 1. **`dedotdotify()` limitation**: The function at `lib/urlapi.c:836` checks `if(ISSLASH(*input))` where `ISSLASH` is defined at line 777 as `((x) == '/')`. This only matches literal forward slashes, not URL-encoded `%2f` sequences. 2. **No post-decoding validation**: After URL decoding in `file.c:192-193`, the decoded path is used directly in `curlx_open()` at line 263. There are no checks for `../` sequences after decoding (verified by grep search showing no `strstr`, `strchr`, or validation functions checking for `../` in `file.c`). 3. **Test case evidence**: The unit test `tests/unit/unit1395.c` line 44 demonstrates this behavior: ```c { "%2f%2e%2e%2f", "%2f%2e%2e%2f" } ``` This confirms that `%2f%2e%2e%2f` (encoded `/../`) is NOT normalized and passes through unchanged. 4. **Code flow verification**: - `lib/urlapi.c:1233`: `dedotdotify(path, pathlen, &dedot)` is called on URL-encoded path - `lib/urlapi.c:836`: Only checks for literal `/` via `ISSLASH(*input)` - `lib/file.c:192`: `Curl_urldecode()` decodes the path - `lib/file.c:263`: Decoded path used in `curlx_open()` with no validation **AI Disclosure**: This vulnerability was identified throug …

Source code

lib/urlapi.clines 752–899 · lines 777-777 cited by the report; definition of dedotdotify
752 return result;
753}
754
755/*
756 * "Remove Dot Segments"
757 * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
758 */
759
760static bool is_dot(const char **str, size_t *clen)
761{
762 const char *p = *str;
763 if(*p == '.') {
764 (*str)++;
765 (*clen)--;
766 return TRUE;
767 }
768 else if((*clen >= 3) &&
769 (p[0] == '%') && (p[1] == '2') && ((p[2] | 0x20) == 'e')) {
770 *str += 3;
771 *clen -= 3;
772 return TRUE;
773 }
774 return FALSE;
775}
776
777#define ISSLASH(x) ((x) == '/')
C1: ISSLASH macro defined as ((x) == '/') - only matches literal forward slash
778
779/*
780 * dedotdotify()
781 * @unittest: 1395
782 *
783 * This function gets a null-terminated path with dot and dotdot sequences
784 * passed in and strips them off according to the rules in RFC 3986 section
785 * 5.2.4.
786 *
787 * The function handles a path. It should not contain the query nor fragment.
788 *
789 * RETURNS
790 *
791 * Zero for success and 'out' set to an allocated dedotdotified string.
792 */
793UNITTEST int dedotdotify(const char *input, size_t clen, char **outp);
794UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
795{
796 struct dynbuf out;
797 CURLcode result = CURLE_OK;
798
799 *outp = NULL;
800 /* the path always starts with a slash, and a slash has not dot */
801 if(clen < 2)
802 return 0;
803
804 curlx_dyn_init(&out, clen + 1);
805
806 /* A. If the input buffer begins with a prefix of "../" or "./", then
807 remove that prefix from the input buffer; otherwise, */
808 if(is_dot(&input, &clen)) {
809 const char *p = input;
810 size_t blen = clen;
811
812 if(!clen)
813 /* . [end] */
814 goto end;
815 else if(ISSLASH(*p)) {
816 /* one dot followed by a slash */
817 input = p + 1;
818 clen--;
819 }
820
821 /* D. if the input buffer consists only of "." or "..", then remove
822 that from the input buffer; otherwise, */
823 else if(is_dot(&p, &blen)) {
824 if(!blen)
825 /* .. [end] */
826 goto end;
827 else if(ISSLASH(*p)) {
828 /* ../ */
829 input = p + 1;
830 clen = blen - 1;
831 }
832 }
833 }
834
835 while(clen && !result) { /* until end of path content */
836 if(ISSLASH(*input)) {
C1: dedotdotify() uses ISSLASH(*input) to detect path segments, so %2f sequences are not recognized
C2: while(clen && !result) { if(ISSLASH(*input)) { - the main loop that processes path segments
837 const char *p = &input[1];
838 size_t blen = clen - 1;
839 /* B. if the input buffer begins with a prefix of "/./" or "/.", where
840 "." is a complete path segment, then replace that prefix with "/" in
841 the input buffer; otherwise, */
842 if(is_dot(&p, &blen)) {
843 if(!blen) { /* /. */
844 result = curlx_dyn_addn(&out, "/", 1);
845 break;
846 }
847 else if(ISSLASH(*p)) { /* /./ */
848 input = p;
849 clen = blen;
850 continue;
851 }
852
853 /* C. if the input buffer begins with a prefix of "/../" or "/..",
854 where ".." is a complete path segment, then replace that prefix
855 with "/" in the input buffer and remove the last segment and its
856 preceding "/" (if any) from the output buffer; otherwise, */
857 else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) {
858 /* remove the last segment from the output buffer */
859 size_t len = curlx_dyn_len(&out);
860 if(len) {
861 char *ptr = curlx_dyn_ptr(&out);
862 char *last = memrchr(ptr, '/', len);
863 if(last)
864 /* trim the output at the slash */
865 curlx_dyn_setlen(&out, last - ptr);
866 }
867
868 if(blen) { /* /../ */
869 input = p;
870 clen = blen;
871 continue;
872 }
873 result = curlx_dyn_addn(&out, "/", 1);
874 break;
875 }
876 }
877 }
878
879 /* E. move the first path segment in the input buffer to the end of
880 the output buffer, including the initial "/" character (if any) and
881 any subsequent characters up to, but not including, the next "/"
882 character or the end of the input buffer. */
883
884 result = curlx_dyn_addn(&out, input, 1);
885 input++;
886 clen--;
887 }
888end:
889 if(!result) {
890 if(curlx_dyn_len(&out))
891 *outp = curlx_dyn_ptr(&out);
892 else {
893 *outp = curlx_strdup("");
894 if(!*outp)
895 return 1;
896 }
897 }
898 return result ? 1 : 0; /* success */
899}
lib/urlapi.clines 811–861 · lines 836-836 cited by the report
811
812 if(!clen)
813 /* . [end] */
814 goto end;
815 else if(ISSLASH(*p)) {
816 /* one dot followed by a slash */
817 input = p + 1;
818 clen--;
819 }
820
821 /* D. if the input buffer consists only of "." or "..", then remove
822 that from the input buffer; otherwise, */
823 else if(is_dot(&p, &blen)) {
824 if(!blen)
825 /* .. [end] */
826 goto end;
827 else if(ISSLASH(*p)) {
828 /* ../ */
829 input = p + 1;
830 clen = blen - 1;
831 }
832 }
833 }
834
835 while(clen && !result) { /* until end of path content */
836 if(ISSLASH(*input)) {
C1: dedotdotify() uses ISSLASH(*input) to detect path segments, so %2f sequences are not recognized
C2: while(clen && !result) { if(ISSLASH(*input)) { - the main loop that processes path segments
837 const char *p = &input[1];
838 size_t blen = clen - 1;
839 /* B. if the input buffer begins with a prefix of "/./" or "/.", where
840 "." is a complete path segment, then replace that prefix with "/" in
841 the input buffer; otherwise, */
842 if(is_dot(&p, &blen)) {
843 if(!blen) { /* /. */
844 result = curlx_dyn_addn(&out, "/", 1);
845 break;
846 }
847 else if(ISSLASH(*p)) { /* /./ */
848 input = p;
849 clen = blen;
850 continue;
851 }
852
853 /* C. if the input buffer begins with a prefix of "/../" or "/..",
854 where ".." is a complete path segment, then replace that prefix
855 with "/" in the input buffer and remove the last segment and its
856 preceding "/" (if any) from the output buffer; otherwise, */
857 else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) {
858 /* remove the last segment from the output buffer */
859 size_t len = curlx_dyn_len(&out);
860 if(len) {
861 char *ptr = curlx_dyn_ptr(&out);
lib/file.clines 167–218 · lines 192-193 cited by the report
167 */
168static CURLcode file_connect(struct Curl_easy *data, bool *done)
169{
170 char *real_path;
171 struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
172 int fd;
173#ifdef DOS_FILESYSTEM
174 size_t i;
175 char *actual_path;
176#endif
177 size_t real_path_len;
178 CURLcode result;
179
180 if(!file)
181 return CURLE_FAILED_INIT;
182
183 if(file->path) {
184 /* already connected.
185 * the handler->connect_it() is normally only called once, but
186 * FILE does a special check on setting up the connection which
187 * calls this explicitly. */
188 *done = TRUE;
189 return CURLE_OK;
190 }
191
192 result = Curl_urldecode(data->state.up.path, 0, &real_path,
C3: Curl_urldecode decodes the path after dedotdotify has run
C4: Curl_urldecode(data->state.up.path, 0, &real_path, &real_path_len, REJECT_ZERO) - decodes %2f to /, %2e to .
C6: Curl_urldecode call that produces the decoded path
193 &real_path_len, REJECT_ZERO);
194 if(result)
195 return result;
196
197#ifdef DOS_FILESYSTEM
198 /* If the first character is a slash, and there is
199 something that looks like a drive at the beginning of
200 the path, skip the slash. If we remove the initial
201 slash in all cases, paths without drive letters end up
202 relative to the current directory which is not how
203 browsers work.
204
205 Some browsers accept | instead of : as the drive letter
206 separator, so we do too.
207
208 On other platforms, we need the slash to indicate an
209 absolute pathname. On Windows, absolute paths start
210 with a drive letter.
211 */
212 actual_path = real_path;
213 if((actual_path[0] == '/') &&
214 actual_path[1] &&
215 (actual_path[2] == ':' || actual_path[2] == '|')) {
216 actual_path[2] = ':';
217 actual_path++;
218 real_path_len--;
lib/urlapi.clines 1208–1258 · lines 1233-1233 cited by the report
1208 goto fail;
1209 pathlen = curlx_dyn_len(&enc);
1210 path = u->path = curlx_dyn_ptr(&enc);
1211 }
1212
1213 if(pathlen <= 1) {
1214 /* there is no path left or just the slash, unset */
1215 path = NULL;
1216 }
1217 else {
1218 if(!u->path) {
1219 u->path = Curl_memdup0(path, pathlen);
1220 if(!u->path) {
1221 result = CURLUE_OUT_OF_MEMORY;
1222 goto fail;
1223 }
1224 path = u->path;
1225 }
1226 else if(flags & CURLU_URLENCODE)
1227 /* it might have encoded more than just the path so cut it */
1228 u->path[pathlen] = 0;
1229
1230 if(!(flags & CURLU_PATH_AS_IS)) {
1231 /* remove ../ and ./ sequences according to RFC3986 */
1232 char *dedot;
1233 int err = dedotdotify(path, pathlen, &dedot);
C3: dedotdotify(path, pathlen, &dedot) called on URL-encoded path during URL parsing
1234 if(err) {
1235 result = CURLUE_OUT_OF_MEMORY;
1236 goto fail;
1237 }
1238 if(dedot) {
1239 curlx_free(u->path);
1240 u->path = dedot;
1241 }
1242 }
1243 }
1244
1245 u->host = curlx_dyn_ptr(&host);
1246
1247 return result;
1248fail:
1249 curlx_dyn_free(&host);
1250 free_urlhandle(u);
1251 return result;
1252}
1253
1254/*
1255 * Parse the URL and, if successful, replace everything in the Curl_URL struct.
1256 */
1257static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
1258 unsigned int flags)
lib/file.clines 238–288 · lines 263-263 cited by the report
238
239 #ifdef AMIGA_FILESYSTEM
240 /*
241 * A leading slash in an AmigaDOS path denotes the parent
242 * directory, and hence we block this as it is relative.
243 * Absolute paths start with 'volumename:', so we check for
244 * this first. Failing that, we treat the path as a real Unix
245 * path, but only if the application was compiled with -lunix.
246 */
247 fd = -1;
248 file->path = real_path;
249
250 if(real_path[0] == '/') {
251 extern int __unix_path_semantics;
252 if(strchr(real_path + 1, ':')) {
253 /* Amiga absolute path */
254 fd = curlx_open(real_path + 1, O_RDONLY);
255 file->path++;
256 }
257 else if(__unix_path_semantics) {
258 /* -lunix fallback */
259 fd = curlx_open(real_path, O_RDONLY);
260 }
261 }
262 #else
263 fd = curlx_open(real_path, O_RDONLY);
C4: fd = curlx_open(real_path, O_RDONLY) - decoded path used directly with no validation
C6: curlx_open(real_path, O_RDONLY) with no validation between decode and open
264 file->path = real_path;
265 #endif
266#endif
267 curlx_free(file->freepath);
268 file->freepath = real_path; /* free this when done */
269
270 file->fd = fd;
271 if(!data->state.upload && (fd == -1)) {
272 failf(data, "Could not open file %s", data->state.up.path);
273 file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE);
274 return CURLE_FILE_COULDNT_READ_FILE;
275 }
276 *done = TRUE;
277
278 return CURLE_OK;
279}
280
281static CURLcode file_done(struct Curl_easy *data,
282 CURLcode status, bool premature)
283{
284 struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
285 (void)status;
286 (void)premature;
287
288 if(file)
lib/escape.clines 107–156 · definition of Curl_urldecode
107CURLcode Curl_urldecode(const char *string, size_t length,
108 char **ostring, size_t *olen,
109 enum urlreject ctrl)
110{
111 size_t alloc;
112 char *ns;
113
114 DEBUGASSERT(string);
115 DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
116
117 alloc = (length ? length : strlen(string));
118 ns = curlx_malloc(alloc + 1);
119
120 if(!ns)
121 return CURLE_OUT_OF_MEMORY;
122
123 /* store output string */
124 *ostring = ns;
125
126 while(alloc) {
127 unsigned char in = (unsigned char)*string;
128 if(('%' == in) && (alloc > 2) &&
C4: Percent-decoding loop converts %2f/%2F to '/' and %2e/%2E to '.'
129 ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
130 /* this is two hexadecimal digits following a '%' */
131 in = (unsigned char)((Curl_hexval(string[1]) << 4) |
132 Curl_hexval(string[2]));
133 string += 3;
134 alloc -= 3;
135 }
136 else {
137 string++;
138 alloc--;
139 }
140
141 if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
142 ((ctrl == REJECT_ZERO) && (in == 0))) {
143 Curl_safefree(*ostring);
144 return CURLE_URL_MALFORMAT;
145 }
146
147 *ns++ = (char)in;
148 }
149 *ns = 0; /* terminate it */
150
151 if(olen)
152 /* store output size */
153 *olen = ns - *ostring;
154
155 return CURLE_OK;
156}
lib/escape.c@curl-8_17_0lines 112–161 · definition of Curl_urldecode as in release curl-8_17_0; differs from master; commits that changed it since: 193cb00ce9 build: stop overriding standard memory allocation functions
Show 50 lines (not cited by the judge)
112CURLcode Curl_urldecode(const char *string, size_t length,
113 char **ostring, size_t *olen,
114 enum urlreject ctrl)
115{
116 size_t alloc;
117 char *ns;
118
119 DEBUGASSERT(string);
120 DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
121
122 alloc = (length ? length : strlen(string));
123 ns = malloc(alloc + 1);
124
125 if(!ns)
126 return CURLE_OUT_OF_MEMORY;
127
128 /* store output string */
129 *ostring = ns;
130
131 while(alloc) {
132 unsigned char in = (unsigned char)*string;
133 if(('%' == in) && (alloc > 2) &&
134 ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
135 /* this is two hexadecimal digits following a '%' */
136 in = (unsigned char)((Curl_hexval(string[1]) << 4) |
137 Curl_hexval(string[2]));
138 string += 3;
139 alloc -= 3;
140 }
141 else {
142 string++;
143 alloc--;
144 }
145
146 if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
147 ((ctrl == REJECT_ZERO) && (in == 0))) {
148 Curl_safefree(*ostring);
149 return CURLE_URL_MALFORMAT;
150 }
151
152 *ns++ = (char)in;
153 }
154 *ns = 0; /* terminate it */
155
156 if(olen)
157 /* store output size */
158 *olen = ns - *ostring;
159
160 return CURLE_OK;
161}
lib/curlx/fopen.hlines 45–45 · definition of curlx_open
Show 1 lines (not cited by the judge)
45#define curlx_open curlx_win32_open
lib/curlx/fopen.h@curl-8_17_0lines 43–43 · definition of curlx_open as in release curl-8_17_0; differs from master
Show 1 lines (not cited by the judge)
43#define curlx_open curlx_win32_open
lib/urlapi.c@curl-8_17_0lines 798–903 · definition of dedotdotify as in release curl-8_17_0; differs from master
Show 106 lines (not cited by the judge)
798UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
799{
800 struct dynbuf out;
801 CURLcode result = CURLE_OK;
802
803 *outp = NULL;
804 /* the path always starts with a slash, and a slash has not dot */
805 if(clen < 2)
806 return 0;
807
808 curlx_dyn_init(&out, clen + 1);
809
810 /* A. If the input buffer begins with a prefix of "../" or "./", then
811 remove that prefix from the input buffer; otherwise, */
812 if(is_dot(&input, &clen)) {
813 const char *p = input;
814 size_t blen = clen;
815
816 if(!clen)
817 /* . [end] */
818 goto end;
819 else if(ISSLASH(*p)) {
820 /* one dot followed by a slash */
821 input = p + 1;
822 clen--;
823 }
824
825 /* D. if the input buffer consists only of "." or "..", then remove
826 that from the input buffer; otherwise, */
827 else if(is_dot(&p, &blen)) {
828 if(!blen)
829 /* .. [end] */
830 goto end;
831 else if(ISSLASH(*p)) {
832 /* ../ */
833 input = p + 1;
834 clen = blen - 1;
835 }
836 }
837 }
838
839 while(clen && !result) { /* until end of path content */
840 if(ISSLASH(*input)) {
841 const char *p = &input[1];
842 size_t blen = clen - 1;
843 /* B. if the input buffer begins with a prefix of "/./" or "/.", where
844 "." is a complete path segment, then replace that prefix with "/" in
845 the input buffer; otherwise, */
846 if(is_dot(&p, &blen)) {
847 if(!blen) { /* /. */
848 result = curlx_dyn_addn(&out, "/", 1);
849 break;
850 }
851 else if(ISSLASH(*p)) { /* /./ */
852 input = p;
853 clen = blen;
854 continue;
855 }
856
857 /* C. if the input buffer begins with a prefix of "/../" or "/..",
858 where ".." is a complete path segment, then replace that prefix
859 with "/" in the input buffer and remove the last segment and its
860 preceding "/" (if any) from the output buffer; otherwise, */
861 else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) {
862 /* remove the last segment from the output buffer */
863 size_t len = curlx_dyn_len(&out);
864 if(len) {
865 char *ptr = curlx_dyn_ptr(&out);
866 char *last = memrchr(ptr, '/', len);
867 if(last)
868 /* trim the output at the slash */
869 curlx_dyn_setlen(&out, last - ptr);
870 }
871
872 if(blen) { /* /../ */
873 input = p;
874 clen = blen;
875 continue;
876 }
877 result = curlx_dyn_addn(&out, "/", 1);
878 break;
879 }
880 }
881 }
882
883 /* E. move the first path segment in the input buffer to the end of
884 the output buffer, including the initial "/" character (if any) and
885 any subsequent characters up to, but not including, the next "/"
886 character or the end of the input buffer. */
887
888 result = curlx_dyn_addn(&out, input, 1);
889 input++;
890 clen--;
891 }
892end:
893 if(!result) {
894 if(curlx_dyn_len(&out))
895 *outp = curlx_dyn_ptr(&out);
896 else {
897 *outp = strdup("");
898 if(!*outp)
899 return 1;
900 }
901 }
902 return result ? 1 : 0; /* success */
903}
lib/optiontable.pllines 81–122 · definition of while
Show 42 lines (not cited by the judge)
81while(<STDIN>) {
82 my $l = $_;
83 if($fl) {
84 # continued deprecation
85 if($l =~ /(.*)\),/) {
86 $fl .= $1;
87
88 # the end
89 my @p=split(/, */, $fl);
90 add($p[0], $p[1], $p[2]);
91 undef $fl;
92 }
93 else {
94 # another line to append
95 chomp $l;
96 $fl .= $l;
97 }
98 }
99
100 if(/^ *CURLOPTDEPRECATED\((.*)/) {
101 $fl = $1;
102 chomp $fl;
103 }
104
105 if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) {
106 my($opt, $type, $num)=($1,$2,$3);
107 add($opt, $type, $num);
108 }
109
110 # alias for an older option
111 # old = new
112 if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) {
113 my ($o, $n)=($1, $2);
114 # skip obsolete ones
115 if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) {
116 $o =~ s/^CURLOPT_//;
117 $n =~ s/^CURLOPT_//;
118 $alias{$o} = $n;
119 push @names, $o;
120 }
121 }
122}