📄 url.c
字号:
/* We're looking for the first slash, but want to ignore double slash. */ again: slash = memchr (pos, '/', end - pos); if (slash && !seen_slash_slash) if (*(slash + 1) == '/') { pos = slash + 2; seen_slash_slash = true; goto again; } /* At this point, SLASH is the location of the first / after "//", or the first slash altogether. START_INSERT is the pointer to the location where LINK will be inserted. When examining the last two examples, keep in mind that LINK begins with '/'. */ if (!slash && !seen_slash_slash) /* example: "foo" */ /* ^ */ start_insert = base; else if (!slash && seen_slash_slash) /* example: "http://foo" */ /* ^ */ start_insert = end; else if (slash && !seen_slash_slash) /* example: "foo/bar" */ /* ^ */ start_insert = base; else if (slash && seen_slash_slash) /* example: "http://something/" */ /* ^ */ start_insert = slash; span = start_insert - base; merge = xmalloc (span + linklength + 1); if (span) memcpy (merge, base, span); memcpy (merge + span, link, linklength); merge[span + linklength] = '\0'; } else { /* LINK is a relative URL: we need to replace everything after last slash (possibly empty) with LINK. So, if BASE is "whatever/foo/bar", and LINK is "qux/xyzzy", our result should be "whatever/foo/qux/xyzzy". */ bool need_explicit_slash = false; int span; const char *start_insert; const char *last_slash = find_last_char (base, end, '/'); if (!last_slash) { /* No slash found at all. Replace what we have with LINK. */ start_insert = base; } else if (last_slash && last_slash >= base + 2 && last_slash[-2] == ':' && last_slash[-1] == '/') { /* example: http://host" */ /* ^ */ start_insert = end + 1; need_explicit_slash = true; } else { /* example: "whatever/foo/bar" */ /* ^ */ start_insert = last_slash + 1; } span = start_insert - base; merge = xmalloc (span + linklength + 1); if (span) memcpy (merge, base, span); if (need_explicit_slash) merge[span - 1] = '/'; memcpy (merge + span, link, linklength); merge[span + linklength] = '\0'; } return merge;}#define APPEND(p, s) do { \ int len = strlen (s); \ memcpy (p, s, len); \ p += len; \} while (0)/* Use this instead of password when the actual password is supposed to be hidden. We intentionally use a generic string without giving away the number of characters in the password, like previous versions did. */#define HIDDEN_PASSWORD "*password*"/* Recreate the URL string from the data in URL. If HIDE is true (as it is when we're calling this on a URL we plan to print, but not when calling it to canonicalize a URL for use within the program), password will be hidden. Unsafe characters in the URL will be quoted. */char *url_string (const struct url *url, enum url_auth_mode auth_mode){ int size; char *result, *p; char *quoted_host, *quoted_user = NULL, *quoted_passwd = NULL; int scheme_port = supported_schemes[url->scheme].default_port; const char *scheme_str = supported_schemes[url->scheme].leading_string; int fplen = full_path_length (url); bool brackets_around_host; assert (scheme_str != NULL); /* Make sure the user name and password are quoted. */ if (url->user) { if (auth_mode != URL_AUTH_HIDE) { quoted_user = url_escape_allow_passthrough (url->user); if (url->passwd) { if (auth_mode == URL_AUTH_HIDE_PASSWD) quoted_passwd = HIDDEN_PASSWORD; else quoted_passwd = url_escape_allow_passthrough (url->passwd); } } } /* In the unlikely event that the host name contains non-printable characters, quote it for displaying to the user. */ quoted_host = url_escape_allow_passthrough (url->host); /* Undo the quoting of colons that URL escaping performs. IPv6 addresses may legally contain colons, and in that case must be placed in square brackets. */ if (quoted_host != url->host) unescape_single_char (quoted_host, ':'); brackets_around_host = strchr (quoted_host, ':') != NULL; size = (strlen (scheme_str) + strlen (quoted_host) + (brackets_around_host ? 2 : 0) + fplen + 1); if (url->port != scheme_port) size += 1 + numdigit (url->port); if (quoted_user) { size += 1 + strlen (quoted_user); if (quoted_passwd) size += 1 + strlen (quoted_passwd); } p = result = xmalloc (size); APPEND (p, scheme_str); if (quoted_user) { APPEND (p, quoted_user); if (quoted_passwd) { *p++ = ':'; APPEND (p, quoted_passwd); } *p++ = '@'; } if (brackets_around_host) *p++ = '['; APPEND (p, quoted_host); if (brackets_around_host) *p++ = ']'; if (url->port != scheme_port) { *p++ = ':'; p = number_to_string (p, url->port); } full_path_write (url, p); p += fplen; *p++ = '\0'; assert (p - result == size); if (quoted_user && quoted_user != url->user) xfree (quoted_user); if (quoted_passwd && auth_mode == URL_AUTH_SHOW && quoted_passwd != url->passwd) xfree (quoted_passwd); if (quoted_host != url->host) xfree (quoted_host); return result;}/* Return true if scheme a is similar to scheme b. Schemes are similar if they are equal. If SSL is supported, schemes are also similar if one is http (SCHEME_HTTP) and the other is https (SCHEME_HTTPS). */boolschemes_are_similar_p (enum url_scheme a, enum url_scheme b){ if (a == b) return true;#ifdef HAVE_SSL if ((a == SCHEME_HTTP && b == SCHEME_HTTPS) || (a == SCHEME_HTTPS && b == SCHEME_HTTP)) return true;#endif return false;}static intgetchar_from_escaped_string (const char *str, char *c){ const char *p = str; assert (str && *str); assert (c); if (p[0] == '%') { if (!ISXDIGIT(p[1]) || !ISXDIGIT(p[2])) { *c = '%'; return 1; } else { if (p[2] == 0) return 0; /* error: invalid string */ *c = X2DIGITS_TO_NUM (p[1], p[2]); if (URL_RESERVED_CHAR(*c)) { *c = '%'; return 1; } else return 3; } } else { *c = p[0]; } return 1;}boolare_urls_equal (const char *u1, const char *u2){ const char *p, *q; int pp, qq; char ch1, ch2; assert(u1 && u2); p = u1; q = u2; while (*p && *q && (pp = getchar_from_escaped_string (p, &ch1)) && (qq = getchar_from_escaped_string (q, &ch2)) && (TOLOWER(ch1) == TOLOWER(ch2))) { p += pp; q += qq; } return (*p == 0 && *q == 0 ? true : false);}#if 0/* Debugging and testing support for path_simplify. *//* Debug: run path_simplify on PATH and return the result in a new string. Useful for calling from the debugger. */static char *ps (char *path){ char *copy = xstrdup (path); path_simplify (copy); return copy;}static voidrun_test (char *test, char *expected_result, bool expected_change){ char *test_copy = xstrdup (test); bool modified = path_simplify (test_copy); if (0 != strcmp (test_copy, expected_result)) { printf ("Failed path_simplify(\"%s\"): expected \"%s\", got \"%s\".\n", test, expected_result, test_copy); } if (modified != expected_change) { if (expected_change) printf ("Expected modification with path_simplify(\"%s\").\n", test); else printf ("Expected no modification with path_simplify(\"%s\").\n", test); } xfree (test_copy);}static voidtest_path_simplify (void){ static struct { char *test, *result; bool should_modify; } tests[] = { { "", "", false }, { ".", "", true }, { "./", "", true }, { "..", "", true }, { "../", "", true }, { "foo", "foo", false }, { "foo/bar", "foo/bar", false }, { "foo///bar", "foo///bar", false }, { "foo/.", "foo/", true }, { "foo/./", "foo/", true }, { "foo./", "foo./", false }, { "foo/../bar", "bar", true }, { "foo/../bar/", "bar/", true }, { "foo/bar/..", "foo/", true }, { "foo/bar/../x", "foo/x", true }, { "foo/bar/../x/", "foo/x/", true }, { "foo/..", "", true }, { "foo/../..", "", true }, { "foo/../../..", "", true }, { "foo/../../bar/../../baz", "baz", true }, { "a/b/../../c", "c", true }, { "./a/../b", "b", true } }; int i; for (i = 0; i < countof (tests); i++) { char *test = tests[i].test; char *expected_result = tests[i].result; bool expected_change = tests[i].should_modify; run_test (test, expected_result, expected_change); }}#endif#ifdef TESTINGconst char *test_append_uri_pathel(){ int i; struct { char *original_url; char *input; bool escaped; char *expected_result; } test_array[] = { { "http://www.yoyodyne.com/path/", "somepage.html", false, "http://www.yoyodyne.com/path/somepage.html" }, }; for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i) { struct growable dest; const char *p = test_array[i].input; memset (&dest, 0, sizeof (dest)); append_string (test_array[i].original_url, &dest); append_uri_pathel (p, p + strlen(p), test_array[i].escaped, &dest); append_char ('\0', &dest); mu_assert ("test_append_uri_pathel: wrong result", strcmp (dest.base, test_array[i].expected_result) == 0); } return NULL;}const char*test_are_urls_equal(){ int i; struct { char *url1; char *url2; bool expected_result; } test_array[] = { { "http://www.adomain.com/apath/", "http://www.adomain.com/apath/", true }, { "http://www.adomain.com/apath/", "http://www.adomain.com/anotherpath/", false }, { "http://www.adomain.com/apath/", "http://www.anotherdomain.com/path/", false }, { "http://www.adomain.com/~path/", "http://www.adomain.com/%7epath/", true }, { "http://www.adomain.com/longer-path/", "http://www.adomain.com/path/", false }, { "http://www.adomain.com/path%2f", "http://www.adomain.com/path/", false }, }; for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i) { mu_assert ("test_are_urls_equal: wrong result", are_urls_equal (test_array[i].url1, test_array[i].url2) == test_array[i].expected_result); } return NULL;}#endif /* TESTING *//* * vim: et ts=2 sw=2 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -