📄 url.c
字号:
* a NUL then someone is unescaping too many times. */ if(strstr(url, "%00")){ werrstr("escaped NUL in URI"); return -1; } m[0].sp = m[0].ep = nil; t = &retab[REsplit]; if(!regx(t->prog, url, m, t->size)){ werrstr("malformed URI: %q", url); return -1; } su->url.s = m[0].sp; su->url.e = m[0].ep; su->scheme.s = m[t->ind[0]].sp; su->scheme.e = m[t->ind[0]].ep; su->authority.s = m[t->ind[1]].sp; su->authority.e = m[t->ind[1]].ep; su->path.s = m[t->ind[2]].sp; su->path.e = m[t->ind[2]].ep; su->query.s = m[t->ind[3]].sp; su->query.e = m[t->ind[3]].ep; su->fragment.s = m[t->ind[4]].sp; su->fragment.e = m[t->ind[4]].ep; if(urldebug) fprint(2, "split url %s into %.*q %.*q %.*q %.*q %.*q %.*q\n", url, su->url.s ? utfnlen(su->url.s, su->url.e-su->url.s) : 10, su->url.s ? su->url.s : "", su->scheme.s ? utfnlen(su->scheme.s, su->scheme.e-su->scheme.s) : 10, su->scheme.s ? su->scheme.s : "", su->authority.s ? utfnlen(su->authority.s, su->authority.e-su->authority.s) : 10, su->authority.s ? su->authority.s : "", su->path.s ? utfnlen(su->path.s, su->path.e-su->path.s) : 10, su->path.s ? su->path.s : "", su->query.s ? utfnlen(su->query.s, su->query.e-su->query.s) : 10, su->query.s ? su->query.s : "", su->fragment.s ? utfnlen(su->fragment.s, su->fragment.e-su->fragment.s) : 10, su->fragment.s ? su->fragment.s : ""); return 0;}static intparse_scheme(SplitUrl *su, Url *u){ if(su->scheme.s == nil){ werrstr("missing scheme"); return -1; } u->scheme = estredup(su->scheme.s, su->scheme.e); strlower(u->scheme); if(!ismatch(REscheme, u->scheme, "scheme")) return -1; u->ischeme = ischeme(u->scheme); if(urldebug) fprint(2, "parse_scheme %s => %d\n", u->scheme, u->ischeme); return 0;}static intparse_unknown_part(SplitUrl *su, Url *u){ char *s, *e; assert(u->ischeme == USunknown); assert(su->scheme.e[0] == ':'); s = su->scheme.e+1; if(su->fragment.s){ e = su->fragment.s-1; assert(*e == '#'); }else e = s+strlen(s); u->schemedata = estredup(s, e); if(!ismatch(REunknowndata, u->schemedata, "unknown scheme data")) return -1; return 0;}static intparse_userinfo(char *s, char *e, Url *u){ Resub m[MaxResub]; Retab *t; m[0].sp = s; m[0].ep = e; t = &retab[REuserinfo]; if(!regx(t->prog, nil, m, t->size)){ werrstr("malformed userinfo: %.*q", utfnlen(s, e-s), s); return -1; } if(m[t->ind[0]].sp) u->user = estredup(m[t->ind[0]].sp, m[t->ind[0]].ep); if(m[t->ind[1]].sp) u->user = estredup(m[t->ind[1]].sp, m[t->ind[1]].ep); return 0;}static intparse_host(char *s, char *e, Url *u){ Resub m[MaxResub]; Retab *t; m[0].sp = s; m[0].ep = e; t = &retab[REhost]; if(!regx(t->prog, nil, m, t->size)){ werrstr("malformed host: %.*q", utfnlen(s, e-s), s); return -1; } assert(m[t->ind[0]].sp || m[t->ind[1]].sp); if(m[t->ind[0]].sp) /* regular */ u->host = estredup(m[t->ind[0]].sp, m[t->ind[0]].ep); else u->host = estredup(m[t->ind[1]].sp, m[t->ind[1]].ep); return 0;}static intparse_authority(SplitUrl *su, Url *u){ Resub m[MaxResub]; Retab *t; if(su->authority.s == nil) return 0; u->authority = estredup(su->authority.s, su->authority.e); m[0].sp = m[0].ep = nil; t = &retab[REauthority]; if(!regx(t->prog, u->authority, m, t->size)){ werrstr("malformed authority: %q", u->authority); return -1; } if(m[t->ind[0]].sp) if(parse_userinfo(m[t->ind[0]].sp, m[t->ind[0]].ep, u) < 0) return -1; if(m[t->ind[1]].sp) if(parse_host(m[t->ind[1]].sp, m[t->ind[1]].ep, u) < 0) return -1; if(m[t->ind[2]].sp) u->port = estredup(m[t->ind[2]].sp, m[t->ind[2]].ep); return 0;}static intparse_abspath(SplitUrl *su, Url *u){ if(su->path.s == nil) return 0; u->path = estredup(su->path.s, su->path.e); if(!ismatch(REabspath, u->path, "absolute path")) return -1; return 0;}static intparse_query(SplitUrl *su, Url *u){ if(su->query.s == nil) return 0; u->query = estredup(su->query.s, su->query.e); if(!ismatch(REquery, u->query, "query")) return -1; return 0;}static intparse_fragment(SplitUrl *su, Url *u){ if(su->fragment.s == nil) return 0; u->fragment = estredup(su->fragment.s, su->fragment.e); if(!ismatch(REfragment, u->fragment, "fragment")) return -1; return 0;}static intpostparse_http(Url *u){ u->open = httpopen; u->read = httpread; u->close = httpclose; if(u->authority==nil){ werrstr("missing authority (hostname, port, etc.)"); return -1; } if(u->host == nil){ werrstr("missing host specification"); return -1; } if(u->path == nil){ u->http.page_spec = estrdup("/"); return 0; } if(!ismatch(REhttppath, u->path, "http path")) return -1; if(u->query){ u->http.page_spec = emalloc(strlen(u->path)+1+strlen(u->query)+1); strcpy(u->http.page_spec, u->path); strcat(u->http.page_spec, "?"); strcat(u->http.page_spec, u->query); }else u->http.page_spec = estrdup(u->path); return 0;}static intpostparse_ftp(Url *u){ Resub m[MaxResub]; Retab *t; if(u->authority==nil){ werrstr("missing authority (hostname, port, etc.)"); return -1; } if(u->query){ werrstr("unexpected \"?query\" in ftp path"); return -1; } if(u->host == nil){ werrstr("missing host specification"); return -1; } if(u->path == nil){ u->ftp.path_spec = estrdup("/"); return 0; } m[0].sp = m[0].ep = nil; t = &retab[REftppath]; if(!regx(t->prog, u->path, m, t->size)){ werrstr("malformed ftp path: %q", u->path); return -1; } if(m[t->ind[0]].sp){ u->ftp.path_spec = estredup(m[t->ind[0]].sp, m[t->ind[0]].ep); if(strchr(u->ftp.path_spec, ';')){ werrstr("unexpected \";param\" in ftp path"); return -1; } }else u->ftp.path_spec = estrdup("/"); if(m[t->ind[1]].sp){ u->ftp.type = estredup(m[t->ind[1]].sp, m[t->ind[1]].ep); strlower(u->ftp.type); } return 0;}static intpostparse_file(Url *u){ if(u->user || u->passwd){ werrstr("user information not valid with file scheme"); return -1; } if(u->query){ werrstr("unexpected \"?query\" in file path"); return -1; } if(u->port){ werrstr("port not valid with file scheme"); return -1; } if(u->path == nil){ werrstr("missing path in file scheme"); return -1; } if(strchr(u->path, ';')){ werrstr("unexpected \";param\" in file path"); return -1; } if(!ismatch(REfilepath, u->path, "file path")) return -1; /* "localhost" is equivalent to no host spec, we'll chose the latter */ if(u->host && cistrcmp(u->host, "localhost") == 0){ free(u->host); u->host = nil; } return 0;}static int (*postparse[])(Url*) = { nil, postparse_http, postparse_http, postparse_ftp, postparse_file,};Url*parseurl(char *url, Url *base){ Url *u; SplitUrl su; if(urldebug) fprint(2, "parseurl %s with base %s\n", url, base ? base->url : "<none>"); u = emalloc(sizeof(Url)); u->url = estrdup(url); if(spliturl(u->url, &su) < 0){ Fail: freeurl(u); return nil; } /* RFC2396 sec 3.1 says relative URIs are distinguished by absent scheme */ if(su.scheme.s==nil){ if(urldebug) fprint(2, "parseurl has nil scheme\n"); if(resolve_relative(&su, base, u) < 0 || spliturl(u->url, &su) < 0) goto Fail; if(u->ischeme == UScurrent){ /* 'u.url' refers to current document; set fragment and return */ if(parse_fragment(&su, u) < 0) goto Fail; return u; } } if(parse_scheme(&su, u) < 0 || parse_fragment(&su, u) < 0) goto Fail; if(u->ischeme == USunknown){ if(parse_unknown_part(&su, u) < 0) goto Fail; return u; } if(parse_query(&su, u) < 0 || parse_authority(&su, u) < 0 || parse_abspath(&su, u) < 0) goto Fail; if(u->ischeme < nelem(postparse) && postparse[u->ischeme]) if((*postparse[u->ischeme])(u) < 0) goto Fail; setmalloctag(u, getcallerpc(&url)); return u;}voidfreeurl(Url *u){ if(u == nil) return; free(u->url); free(u->scheme); free(u->schemedata); free(u->authority); free(u->user); free(u->passwd); free(u->host); free(u->port); free(u->path); free(u->query); free(u->fragment); switch(u->ischeme){ case UShttp: free(u->http.page_spec); break; case USftp: free(u->ftp.path_spec); free(u->ftp.type); break; } free(u);}voidrewriteurl(Url *u){ char *s; if(u->schemedata) s = estrmanydup(u->scheme, ":", u->schemedata, nil); else s = estrmanydup(u->scheme, "://", u->user ? u->user : "", u->passwd ? ":" : "", u->passwd ? u->passwd : "", u->user ? "@" : "", u->host ? u->host : "", u->port ? ":" : "", u->port ? u->port : "", u->path, u->query ? "?" : "", u->query ? u->query : "", u->fragment ? "#" : "", u->fragment ? u->fragment : "", nil); free(u->url); u->url = s;}intseturlquery(Url *u, char *query){ if(query == nil){ free(u->query); u->query = nil; return 0; } if(!ismatch(REquery, query, "query")) return -1; free(u->query); u->query = estrdup(query); return 0;}static voiddupp(char **p){ if(*p) *p = estrdup(*p);}Url*copyurl(Url *u){ Url *v; v = emalloc(sizeof(Url)); *v = *u; dupp(&v->url); dupp(&v->scheme); dupp(&v->schemedata); dupp(&v->authority); dupp(&v->user); dupp(&v->passwd); dupp(&v->host); dupp(&v->port); dupp(&v->path); dupp(&v->query); dupp(&v->fragment); switch(v->ischeme){ case UShttp: dupp(&v->http.page_spec); break; case USftp: dupp(&v->ftp.path_spec); dupp(&v->ftp.type); break; } return v;}static intdhex(char c){ if('0' <= c && c <= '9') return c-'0'; if('a' <= c && c <= 'f') return c-'a'+10; if('A' <= c && c <= 'F') return c-'A'+10; return 0;}char*escapeurl(char *s, int (*needesc)(int)){ int n; char *t, *u; Rune r; static char *hex = "0123456789abcdef"; n = 0; for(t=s; *t; t++) if((*needesc)(*t)) n++; u = emalloc(strlen(s)+2*n+1); t = u; for(; *s; s++){ s += chartorune(&r, s); if(r >= 0xFF){ werrstr("URLs cannot contain Runes > 0xFF"); free(t); return nil; } if((*needesc)(r)){ *u++ = '%'; *u++ = hex[(r>>4)&0xF]; *u++ = hex[r&0xF]; }else *u++ = r; } *u = '\0'; return t;}char*unescapeurl(char *s){ char *r, *w; Rune rune; s = estrdup(s); for(r=w=s; *r; r++){ if(*r=='%'){ r++; if(!isxdigit(r[0]) || !isxdigit(r[1])){ werrstr("bad escape sequence '%.3s' in URL", r); return nil; } if(r[0]=='0' && r[2]=='0'){ werrstr("escaped NUL in URL"); return nil; } rune = (dhex(r[0])<<4)|dhex(r[1]); /* latin1 */ w += runetochar(w, &rune); r += 2; }else *w++ = *r; } *w = '\0'; return s;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -