11 static char reserved[] = "%:/?#[]@!$&'()*+,;=";
16 if('0' <= c && c <= '9')
18 if('a' <= c && c <= 'f')
20 if('A' <= c && c <= 'F')
26 unescape(char *s, char *spec)
33 for(r=w=s; x = *r; r++){
34 if(x == '%' && isxdigit(r[1]) && isxdigit(r[2])){
35 x = (dhex(r[1])<<4)|dhex(r[2]);
36 if(spec && strchr(spec, x)){
58 s2 = va_arg(f->args, Str2);
62 if(*s == '%' && isxdigit(s[1]) && isxdigit(s[2])){
63 fmtprint(f, "%%%c%c", toupper(s[1]), toupper(s[2]));
66 else if(isalnum(*s) || strchr(".-_~!$&'()*,;=", *s) || strchr(spec, *s))
67 fmtprint(f, "%c", *s);
69 fmtprint(f, "%%%.2X", *s & 0xff);
78 s = va_arg(f->args, char*);
79 if(utf2idn(s, d, sizeof(d)) >= 0)
88 char *s = va_arg(f->args, char*);
89 fmtprint(f, (*s != '[' && strchr(s, ':') != nil)? "[%s]" : "%s", s);
99 if((u = va_arg(f->args, Url*)) == nil)
100 return fmtprint(f, "nil");
102 fmtprint(f, "%s:", u->scheme);
103 if(u->user || u->host)
106 fmtprint(f, "%E", (Str2){u->user, ""});
108 fmtprint(f, ":%E", (Str2){u->pass, ""});
112 fmtprint(f, "%]", u->host);
114 fmtprint(f, ":%s", u->port);
117 fmtprint(f, "%E", (Str2){s, "/:@+"});
119 fmtprint(f, "?%E", (Str2){u->query, "/:@"});
121 fmtprint(f, "#%E", (Str2){u->fragment, "/:@?+"});
131 if(u->user || u->host)
148 if(p = strchr(s, '/'))
150 if(*s == '.' && ((s[1] == 0) || (s[1] == '.' && s[2] == 0))){
159 if((n = strlen(s)) > 0)
171 abspath(char *s, char *b)
176 if(s == nil || *s == 0)
178 if(*s != '/' && (x = strrchr(b, '/'))){
179 a = emalloc((x - b) + strlen(s) + 4);
180 sprint(a, "%.*s/%s", utfnlen(b, x - b), b, s);
187 a = emalloc(strlen(s) + 4);
197 if(p == nil || *p == nil)
218 cp += runetochar(cp, &r);
233 u = emalloc(sizeof(*u));
244 if((p == s) || ((!isdigit(*p) && strchr("+-.", *p) == nil)))
251 memmove(u, b, sizeof(*u));
254 memmove(u, b, sizeof(*u));
258 memmove(u, b, sizeof(*u));
259 u->fragment = u->query = nil;
263 u->scheme = b->scheme;
268 memmove(u, b, sizeof(*u));
269 u->fragment = u->query = u->path = nil;
274 if(x = strchr(p, '#')){
278 if(x = strchr(p, '?')){
282 if(p[0] == '/' && p[1] == '/'){
284 if(x = strchr(p, '/')){
285 u->path = t = abspath(x, Upath(b));
288 if(x = strchr(p, '@')){
290 if(y = strchr(p, ':')){
297 if((x = strrchr(p, ']')) == nil)
299 if(x = strrchr(x, ':')){
303 if(x = strchr(p, '[')){
305 if(y = strchr(p, ']'))
310 u->path = t = abspath(p, Upath(b));
320 pstrdup(&u->fragment);
324 /* the + character encodes space only in query part */
326 while(s = strchr(s, '+'))
331 if(idn2utf(s, t, Domlen) >= 0){
332 u->host = estrdup(t);
338 unescape(u->user, nil);
339 unescape(u->pass, nil);
340 unescape(u->path, reserved);
341 unescape(u->query, reserved);
342 unescape(u->fragment, reserved);
343 mklowcase(u->scheme);
353 if(u == nil || u->scheme == nil || u->host == nil || Upath(u) == nil){
358 /* remove default ports */
359 switch(atoi(u->port)){
360 case 21: if(!strcmp(u->scheme, "ftp")) goto Defport; break;
361 case 70: if(!strcmp(u->scheme, "gopher"))goto Defport; break;
362 case 80: if(!strcmp(u->scheme, "http")) goto Defport; break;
363 case 443: if(!strcmp(u->scheme, "https")) goto Defport; break;
364 default: if(!strcmp(u->scheme, u->port)) goto Defport; break;
374 matchurl(Url *u, Url *s)
381 if(u->scheme && (s->scheme == nil || strcmp(u->scheme, s->scheme)))
383 if(u->user && (s->user == nil || strcmp(u->user, s->user)))
385 if(u->host && (s->host == nil || strcmp(u->host, s->host)))
387 if(u->port && (s->port == nil || strcmp(u->port, s->port)))
391 if(b == nil || strncmp(a, b, strlen(a)))