+}
+
+php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent)
+{
+ php_http_url_t *cpy;
+ const char *end = NULL, *url_ptr = (const char *) url;
+ char *cpy_ptr;
+
+ end = MAX(url->scheme, end);
+ end = MAX(url->pass, end);
+ end = MAX(url->user, end);
+ end = MAX(url->host, end);
+ end = MAX(url->path, end);
+ end = MAX(url->query, end);
+ end = MAX(url->fragment, end);
+
+ if (end) {
+ end += strlen(end) + 1;
+ cpy_ptr = pecalloc(1, end - url_ptr, persistent);
+ cpy = (php_http_url_t *) cpy_ptr;
+
+ memcpy(cpy_ptr + sizeof(*cpy), url_ptr + sizeof(*url), end - url_ptr - sizeof(*url));
+
+ cpy->scheme = url->scheme ? cpy_ptr + (url->scheme - url_ptr) : NULL;
+ cpy->pass = url->pass ? cpy_ptr + (url->pass - url_ptr) : NULL;
+ cpy->user = url->user ? cpy_ptr + (url->user - url_ptr) : NULL;
+ cpy->host = url->host ? cpy_ptr + (url->host - url_ptr) : NULL;
+ cpy->path = url->path ? cpy_ptr + (url->path - url_ptr) : NULL;
+ cpy->query = url->query ? cpy_ptr + (url->query - url_ptr) : NULL;
+ cpy->fragment = url->fragment ? cpy_ptr + (url->fragment - url_ptr) : NULL;
+ } else {
+ cpy = ecalloc(1, sizeof(*url));
+ }
+
+ cpy->port = url->port;
+
+ return cpy;
+}
+
+static size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end)
+{
+ unsigned wchar;
+ size_t consumed = utf8towc(&wchar, (const unsigned char *) ptr, end - ptr);
+
+ if (!consumed || consumed == (size_t) -1) {
+ return 0;
+ }
+
+ if (wc) {
+ *wc = wchar;
+ }
+ return consumed;
+}
+
+#ifdef PHP_HTTP_HAVE_WCHAR
+static size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end)
+{
+ wchar_t wchar;
+ size_t consumed = 0;
+#if defined(HAVE_MBRTOWC)
+ mbstate_t ps = {0};
+
+ consumed = mbrtowc(&wchar, ptr, end - ptr, &ps);
+#elif defined(HAVE_MBTOWC)
+ consumed = mbtowc(&wchar, ptr, end - ptr);
+#endif
+
+ if (!consumed || consumed == (size_t) -1) {
+ return 0;
+ }
+
+ if (wc) {
+ *wc = wchar;
+ }
+ return consumed;
+}
+#endif
+
+typedef enum parse_mb_what {
+ PARSE_SCHEME,
+ PARSE_USERINFO,
+ PARSE_HOSTINFO,
+ PARSE_PATH,
+ PARSE_QUERY,
+ PARSE_FRAGMENT
+} parse_mb_what_t;
+
+static const char * const parse_what[] = {
+ "scheme",
+ "userinfo",
+ "hostinfo",
+ "path",
+ "query",
+ "fragment"
+};
+
+static const char parse_xdigits[] = "0123456789ABCDEF";
+
+static size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const char *ptr, const char *end, const char *begin, zend_bool silent)
+{
+ unsigned wchar;
+ size_t consumed = 0;
+
+ if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+ consumed = parse_mb_utf8(&wchar, ptr, end);
+ }
+#ifdef PHP_HTTP_HAVE_WCHAR
+ else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+ consumed = parse_mb_loc(&wchar, ptr, end);
+ }
+#endif
+
+ while (consumed) {
+ if (!(state->flags & PHP_HTTP_URL_PARSE_TOPCT) || what == PARSE_HOSTINFO || what == PARSE_SCHEME) {
+ if (what == PARSE_HOSTINFO && (state->flags & PHP_HTTP_URL_PARSE_TOIDN)) {
+ /* idna */
+ } else if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+ if (!isualnum(wchar)) {
+ break;
+ }
+#ifdef PHP_HTTP_HAVE_WCHAR
+ } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+ if (!iswalnum(wchar)) {
+ break;
+ }
+#endif
+ }
+ PHP_HTTP_DUFF(consumed, state->buffer[state->offset++] = *ptr++);
+ } else {
+ int i = 0;
+
+ PHP_HTTP_DUFF(consumed,
+ state->buffer[state->offset++] = '%';
+ state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) >> 4];
+ state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) & 0xf];
+ ++i;
+ );