- if (!*link)
- {
- /* Empty LINK points back to BASE, query string and all. */
- constr = xstrdup (base);
- }
- else if (*link == '?')
- {
- /* LINK points to the same location, but changes the query
- string. Examples: */
- /* uri_merge("path", "?new") -> "path?new" */
- /* uri_merge("path?foo", "?new") -> "path?new" */
- /* uri_merge("path?foo#bar", "?new") -> "path?new" */
- /* uri_merge("path#foo", "?new") -> "path?new" */
- int baselength = end - base;
- constr = xmalloc (baselength + linklength + 1);
- memcpy (constr, base, baselength);
- memcpy (constr + baselength, link, linklength);
- constr[baselength + linklength] = '\0';
- }
- else if (*link == '#')
- {
- /* uri_merge("path", "#new") -> "path#new" */
- /* uri_merge("path#foo", "#new") -> "path#new" */
- /* uri_merge("path?foo", "#new") -> "path?foo#new" */
- /* uri_merge("path?foo#bar", "#new") -> "path?foo#new" */
- int baselength;
- const char *end1 = strchr (base, '#');
- if (!end1)
- end1 = base + strlen (base);
- baselength = end1 - base;
- constr = xmalloc (baselength + linklength + 1);
- memcpy (constr, base, baselength);
- memcpy (constr + baselength, link, linklength);
- constr[baselength + linklength] = '\0';
- }
- else if (linklength > 1 && *link == '/' && *(link + 1) == '/')
+ span = start_insert - base;
+ merge = (char *)xmalloc (span + linklength + 1);
+ if (span)
+ memcpy (merge, base, span);
+ memcpy (merge + span, link, linklength);
+ merge[span + linklength] = '\0';
+ }
+ else if (*link == '/')
+ {
+ /* LINK is an absolute path: we need to replace everything
+ after (and including) the FIRST slash with LINK.
+
+ So, if BASE is "http://host/whatever/foo/bar", and LINK is
+ "/qux/xyzzy", our result should be
+ "http://host/qux/xyzzy". */
+ int span;
+ const char *slash;
+ const char *start_insert = NULL; /* for gcc to shut up. */
+ const char *pos = base;
+ int seen_slash_slash = 0;
+ /* 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 = 1;
+ 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 = (char *)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". */
+ int need_explicit_slash = 0;
+ int span;
+ const char *start_insert;
+ const char *last_slash = find_last_char (base, end, '/');
+ if (!last_slash)