ref: d50835ed8e9c630099318326135a7202465eb708
parent: dfc5c880042c1c61b88a2f3f2eaf344e8fe88684
author: Julien Blanchard <julien@typed-hole.org>
date: Thu Dec 10 06:08:38 EST 2020
Extract url and util stuff
--- a/castor.c
+++ b/castor.c
@@ -14,6 +14,36 @@
#include "castor.h"
+typedef struct Ctx Ctx;
+typedef struct Hist Hist;
+typedef struct Response Response;
+
+struct Response
+{
+ Url *url;
+ char *meta;
+ int status;
+ int fd;
+};
+
+struct Ctx
+{
+ Url *url;
+ Rtext *text;
+};
+
+struct Hist
+{
+ Hist *p;
+ Hist *n;
+ Ctx *c;
+};
+
+int request(Url *u);
+void gemini_get(Url *u);
+void texthit(Panel *p, int b, Rtext *t);
+void message(char *s, ...);
+
Panel *root;
Panel *backp;
Panel *fwdp;
@@ -44,9 +74,10 @@
};
-char* replace_char(char* str, char find, char replace){
+char*
+replace_char(char* str, char find, char replace){
char *current_pos = strchr(str,find);
- while (current_pos){
+ while(current_pos){
*current_pos = replace;
current_pos = strchr(current_pos,find);
}
@@ -53,7 +84,7 @@
return str;
}
-char *
+char*
cleanup(char *line)
{
if(line=="" || line==NULL)
@@ -60,9 +91,10 @@
return line;
char *src, *dst;
- for (src=dst=line; *src != '\0'; src++) {
+ for(src=dst=line; *src != '\0'; src++){
*dst = *src;
- if (*dst != '\r' && *dst != '\n') dst++;
+ if(*dst != '\r' && *dst != '\n')
+ dst++;
}
*dst = '\0';
@@ -125,34 +157,34 @@
}
}
-char *
+char*
protocol(char *link)
{
- if(strstr(link, "http://") != nil) {
+ if(strstr(link, "http://") != nil){
return " [WWW]";
- } else if(strstr(link, "https://") != nil) {
+ }else if(strstr(link, "https://") != nil){
return " [WWW]";
- } else if(strstr(link, "gopher://") != nil) {
+ }else if(strstr(link, "gopher://") != nil){
return " [GOPHER]";
- } else if(strstr(link, "finger://") != nil) {
+ }else if(strstr(link, "finger://") != nil){
return " [FINGER]";
- } else {
+ }else{
return "";
}
}
-char *
+char*
symbol(char *link)
{
- if(strstr(link, "http://") != nil) {
+ if(strstr(link, "http://") != nil){
return "⇄";
- } else if(strstr(link, "https://") != nil) {
+ }else if(strstr(link, "https://") != nil){
return "⇄";
- } else if(strstr(link, "gopher://") != nil) {
+ }else if(strstr(link, "gopher://") != nil){
return "⇒";
- } else if(strstr(link, "finger://") != nil) {
+ }else if(strstr(link, "finger://") != nil){
return "⇒";
- } else {
+ }else{
return "→";
}
}
@@ -181,7 +213,7 @@
width = 80;
char *preformatted_marker = "```";
- if(strncmp(line, preformatted_marker, strlen(preformatted_marker)) == 0){
+ if(strbeg(line, preformatted_marker) == 0){
if(preformatted==0){
preformatted=1;
}else{
@@ -190,34 +222,29 @@
return;
}
- while(*base)
- {
- if(preformatted==1)
- {
+ while(*base){
+ /* Preformatted text */
+ if(preformatted==1){
plrtstr(&c->text, 1000000, 8, 0, font, strdup(cleanup(base)), PL_HEAD, 0);
break;
}
- if(strncmp(line, "#", strlen("#")) == 0)
- {
+ /* Headers */
+ if(strbeg(line, "#") == 0){
plrtstr(&c->text, 1000000, 8, 0, font, strdup(cleanup(base)), PL_SEL, 0);
break;
- }
- if((length <= width))
- {
+ } /* Small lines */
+ if((length <= width)){
plrtstr(&c->text, 1000000, 8, 0, font, strdup(cleanup(base)), 0, 0);
break;
}
-
+ /* Wrapping the rest */
right_margin = base + width;
- while(!isspace(*right_margin))
- {
+ while(!isspace(*right_margin)){
right_margin--;
- if(right_margin == base)
- {
+ if(right_margin == base){
right_margin += width;
- while(!isspace(*right_margin))
- {
+ while(!isspace(*right_margin)){
if(*right_margin == '\0')
break;
right_margin++;
@@ -239,8 +266,7 @@
char *rest = strtok(NULL, "\0");
char *label;
- if(rest != NULL)
- {
+ if(rest != NULL){
while(isspace(*rest))
rest++;
@@ -252,7 +278,7 @@
plrtstr(&c->text, 1000000, 8, 0, font, strdup(label), PL_HOT, estrdup(link));
}
-Url *
+Url*
base_url(Url *url)
{
char *base_url, *path, *ptr;
@@ -344,8 +370,7 @@
c->url = url;
set_current_base_url(base_url(url));
- if(r->meta != NULL && strncmp(r->meta, "text/", strlen("text/")) != 0)
- {
+ if(r->meta != NULL && strbeg(r->meta, "text/") != 0){
Bflush(&body);
close(fd);
@@ -352,10 +377,8 @@
page(url);
message("Castor9");
}else{
- while((line = Brdstr(&body, '\n', 0)) != nil)
- {
- if(strncmp(line, "=>", strlen("=>")) == 0)
- {
+ while((line = Brdstr(&body, '\n', 0)) != nil){
+ if(strbeg(line, "=>") == 0){
render_link(c, line);
}else{
render_text(c, line);
@@ -373,7 +396,7 @@
show(c);
}
- } else if(r->status == 31) {
+ }else if(r->status == 31){
Url *redirect = urlparse(nil, r->meta);
gemini_get(redirect);
}
@@ -431,31 +454,14 @@
entryhit(Panel *p, char *t)
{
USED(p);
- switch(strlen(t)){
- case 0:
+ if(strlen(t) == 0)
return;
- case 1:
- switch(*t){
- case 'b':
- //backhit(backp, 1);
- break;
- case 'n':
- //nexthit(fwdp, 1);
- break;
- case 'q':
- exits(nil);
- break;
- default:
- message("unknown command %s", t);
- break;
- }
- break;
- default:
- if(strstr(t, "gemini://") == NULL)
- t = smprint("gemini://%s", t);
+
+ if(strstr(t, "gemini://") == NULL)
+ t = smprint("gemini://%s", t);
- gemini_get(urlparse(nil, t));
- }
+ gemini_get(urlparse(nil, t));
+
plinitentry(entryp, PACKN|FILLX, 0, "", entryhit);
pldraw(root, screen);
}
@@ -473,9 +479,9 @@
if(link==nil)
return;
- if (strstr(link, "gemini://") != nil || strstr(link, "://") != nil){
+ if(strstr(link, "gemini://") != nil || strstr(link, "://") != nil){
next_url = urlparse(nil, link);
- } else {
+ }else{
/* assuming relative URL */
if(*link == '/'){
n = smprint("%s%s", urlparse(current_base_url, link)->raw, estrdup(link)+1);
@@ -487,7 +493,7 @@
if(strcmp(next_url->scheme, "gemini") == 0){
gemini_get(next_url);
- } else {
+ }else{
plumburl(next_url);
}
}
@@ -638,447 +644,3 @@
}
}
-// //////////////////////////
-
-enum {
- Domlen = 256,
-};
-
-static char reserved[] = "%:/?#[]@!$&'()*+,;=";
-
-static int
-dhex(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;
-}
-
-static char*
-unescape(char *s, char *spec)
-{
- char *r, *w;
- uchar x;
-
- if(s == nil)
- return s;
- for(r=w=s; x = *r; r++){
- if(x == '%' && isxdigit(r[1]) && isxdigit(r[2])){
- x = (dhex(r[1])<<4)|dhex(r[2]);
- if(spec && strchr(spec, x)){
- *w++ = '%';
- *w++ = toupper(r[1]);
- *w++ = toupper(r[2]);
- }
- else
- *w++ = x;
- r += 2;
- continue;
- }
- *w++ = x;
- }
- *w = 0;
- return s;
-}
-
-int
-Efmt(Fmt *f)
-{
- char *s, *spec;
- Str2 s2;
-
- s2 = va_arg(f->args, Str2);
- s = s2.s1;
- spec = s2.s2;
- for(; *s; s++)
- if(*s == '%' && isxdigit(s[1]) && isxdigit(s[2])){
- fmtprint(f, "%%%c%c", toupper(s[1]), toupper(s[2]));
- s += 2;
- }
- else if(isalnum(*s) || strchr(".-_~!$&'()*,;=", *s) || strchr(spec, *s))
- fmtprint(f, "%c", *s);
- else
- fmtprint(f, "%%%.2X", *s & 0xff);
- return 0;
-}
-
-int
-Nfmt(Fmt *f)
-{
- char d[Domlen], *s;
-
- s = va_arg(f->args, char*);
- if(utf2idn(s, d, sizeof(d)) >= 0)
- s = d;
- fmtprint(f, "%s", s);
- return 0;
-}
-
-int
-Mfmt(Fmt *f)
-{
- char *s = va_arg(f->args, char*);
- fmtprint(f, (*s != '[' && strchr(s, ':') != nil)? "[%s]" : "%s", s);
- return 0;
-}
-
-int
-Ufmt(Fmt *f)
-{
- char *s;
- Url *u;
-
- if((u = va_arg(f->args, Url*)) == nil)
- return fmtprint(f, "nil");
- if(u->scheme)
- fmtprint(f, "%s:", u->scheme);
- if(u->user || u->host)
- fmtprint(f, "//");
- if(u->user){
- fmtprint(f, "%E", (Str2){u->user, ""});
- if(u->pass)
- fmtprint(f, ":%E", (Str2){u->pass, ""});
- fmtprint(f, "@");
- }
- if(u->host){
- fmtprint(f, "%]", u->host);
- if(u->port)
- fmtprint(f, ":%s", u->port);
- }
- if(s = Upath(u))
- fmtprint(f, "%E", (Str2){s, "/:@+"});
- if(u->query)
- fmtprint(f, "?%E", (Str2){u->query, "/:@"});
- if(u->fragment)
- fmtprint(f, "#%E", (Str2){u->fragment, "/:@?+"});
- return 0;
-}
-
-char*
-Upath(Url *u)
-{
- if(u){
- if(u->path)
- return u->path;
- if(u->user || u->host)
- return "/";
- }
- return nil;
-}
-
-static char*
-remdot(char *s)
-{
- char *b, *d, *p;
- int dir, n;
-
- dir = 1;
- b = d = s;
- if(*s == '/')
- s++;
- for(; s; s = p){
- if(p = strchr(s, '/'))
- *p++ = 0;
- if(*s == '.' && ((s[1] == 0) || (s[1] == '.' && s[2] == 0))){
- if(s[1] == '.')
- while(d > b)
- if(*--d == '/')
- break;
- dir = 1;
- continue;
- } else
- dir = (p != nil);
- if((n = strlen(s)) > 0)
- memmove(d+1, s, n);
- *d++ = '/';
- d += n;
- }
- if(dir)
- *d++ = '/';
- *d = 0;
- return b;
-}
-
-static char*
-abspath(char *s, char *b)
-{
- char *x, *a;
-
- if(b && *b){
- if(s == nil || *s == 0)
- return estrdup(b);
- if(*s != '/' && (x = strrchr(b, '/'))){
- a = emalloc((x - b) + strlen(s) + 4);
- sprint(a, "%.*s/%s", utfnlen(b, x - b), b, s);
- return remdot(a);
- }
- }
- if(s && *s){
- if(*s != '/')
- return estrdup(s);
- a = emalloc(strlen(s) + 4);
- sprint(a, "%s", s);
- return remdot(a);
- }
- return nil;
-}
-
-static void
-pstrdup(char **p)
-{
- if(p == nil || *p == nil)
- return;
- if(**p == 0){
- *p = nil;
- return;
- }
- *p = estrdup(*p);
-}
-
-static char*
-mklowcase(char *s)
-{
- char *cp;
- Rune r;
-
- if(s == nil)
- return s;
- cp = s;
- while(*cp != 0){
- chartorune(&r, cp);
- r = tolowerrune(r);
- cp += runetochar(cp, &r);
- }
- return s;
-}
-
-static Url *
-saneurl(Url *u)
-{
- if(u == nil || u->scheme == nil || u->host == nil || Upath(u) == nil){
- freeurl(u);
- return nil;
- }
- if(u->port){
- /* remove default ports */
- switch(atoi(u->port)){
- case 21: if(!strcmp(u->scheme, "ftp")) goto Defport; break;
- case 70: if(!strcmp(u->scheme, "gopher")) goto Defport; break;
- case 80: if(!strcmp(u->scheme, "http")) goto Defport; break;
- case 443: if(!strcmp(u->scheme, "https")) goto Defport; break;
- case 1965: if(!strcmp(u->scheme, "gemini")) goto Defport; break;
- default: if(!strcmp(u->scheme, u->port)) goto Defport; break;
- Defport:
- free(u->port);
- u->port = nil;
- }
- }
- return u;
-}
-
-Url*
-urlparse(Url *b, char *s)
-{
- char *t, *p, *x, *y;
- Url *u;
-
- if(s == nil)
- s = "";
- t = nil;
- s = p = estrdup(s);
- u = emalloc(sizeof(*u));
-
- u->raw = estrdup(s);
-
- for(; *p; p++){
- if(*p == ':'){
- if(p == s)
- break;
- *p++ = 0;
- u->scheme = s;
- b = nil;
- goto Abs;
- }
- if(!isalpha(*p))
- if((p == s) || ((!isdigit(*p) && strchr("+-.", *p) == nil)))
- break;
- }
- p = s;
- if(b){
- switch(*p){
- case 0:
- memmove(u, b, sizeof(*u));
- goto Out;
- case '#':
- memmove(u, b, sizeof(*u));
- u->fragment = p+1;
- goto Out;
- case '?':
- memmove(u, b, sizeof(*u));
- u->fragment = u->query = nil;
- break;
- case '/':
- if(p[1] == '/'){
- u->scheme = b->scheme;
- b = nil;
- break;
- }
- default:
- memmove(u, b, sizeof(*u));
- u->fragment = u->query = u->path = nil;
- break;
- }
- }
-Abs:
- if(x = strchr(p, '#')){
- *x = 0;
- u->fragment = x+1;
- }
- if(x = strchr(p, '?')){
- *x = 0;
- u->query = x+1;
- }
- if(p[0] == '/' && p[1] == '/'){
- p += 2;
- if(x = strchr(p, '/')){
- u->path = t = abspath(x, Upath(b));
- *x = 0;
- }
- if(x = strchr(p, '@')){
- *x = 0;
- if(y = strchr(p, ':')){
- *y = 0;
- u->pass = y+1;
- }
- u->user = p;
- p = x+1;
- }
- if((x = strrchr(p, ']')) == nil)
- x = p;
- if(x = strrchr(x, ':')){
- *x = 0;
- u->port = x+1;
- }
- if(x = strchr(p, '[')){
- p = x+1;
- if(y = strchr(p, ']'))
- *y = 0;
- }
- u->host = p;
- } else {
- u->path = t = abspath(p, Upath(b));
- }
-Out:
- pstrdup(&u->scheme);
- pstrdup(&u->user);
- pstrdup(&u->pass);
- pstrdup(&u->host);
- pstrdup(&u->port);
- pstrdup(&u->path);
- pstrdup(&u->query);
- pstrdup(&u->fragment);
- free(s);
- free(t);
-
- /* the + character encodes space only in query part */
- if(s = u->query)
- while(s = strchr(s, '+'))
- *s++ = ' ';
-
- if(s = u->host){
- t = emalloc(Domlen);
- if(idn2utf(s, t, Domlen) >= 0){
- u->host = estrdup(t);
- free(s);
- }
- free(t);
- }
-
- unescape(u->user, nil);
- unescape(u->pass, nil);
- unescape(u->path, reserved);
- unescape(u->query, reserved);
- unescape(u->fragment, reserved);
- mklowcase(u->scheme);
- mklowcase(u->host);
- mklowcase(u->port);
-
- if((u = saneurl(u)) != nil)
- u->full = smprint("%U", u);
-
- return u;
-}
-
-int
-matchurl(Url *u, Url *s)
-{
- if(u){
- char *a, *b;
-
- if(s == nil)
- return 0;
- if(u->scheme && (s->scheme == nil || strcmp(u->scheme, s->scheme)))
- return 0;
- if(u->user && (s->user == nil || strcmp(u->user, s->user)))
- return 0;
- if(u->host && (s->host == nil || strcmp(u->host, s->host)))
- return 0;
- if(u->port && (s->port == nil || strcmp(u->port, s->port)))
- return 0;
- if(a = Upath(u)){
- b = Upath(s);
- if(b == nil || strncmp(a, b, strlen(a)))
- return 0;
- }
- }
- return 1;
-}
-
-void
-freeurl(Url *u)
-{
- if(u == nil)
- return;
- free(u->full);
- free(u->scheme);
- free(u->user);
- free(u->pass);
- free(u->host);
- free(u->port);
- free(u->path);
- free(u->query);
- free(u->fragment);
- free(u->raw);
- free(u);
-}
-
-// ///////////////
-
-void *
-emalloc(int n)
-{
- void *v;
- if((v = malloc(n)) == nil) {
- fprint(2, "out of memory allocating %d\n", n);
- sysfatal("mem");
- }
- setmalloctag(v, getcallerpc(&n));
- memset(v, 0, n);
- return v;
-}
-
-char *
-estrdup(char *s)
-{
- char *t;
- if((t = strdup(s)) == nil) {
- fprint(2, "out of memory in strdup(%.10s)\n", s);
- sysfatal("mem");
- }
- setmalloctag(t, getcallerpc(&t));
- return t;
-}
\ No newline at end of file
--- a/castor.h
+++ b/castor.h
@@ -1,9 +1,7 @@
-typedef struct Ctx Ctx;
-typedef struct Hist Hist;
typedef struct Url Url;
-typedef struct Response Response;
-struct Url {
+struct Url
+{
char *raw;
char *full;
char *scheme;
@@ -20,28 +18,8 @@
typedef struct {
char *s1;
char *s2;
-}Str2;
+} Str2;
-struct Response {
- Url *url;
- char *meta;
- int status;
- int fd;
-};
-
-struct Ctx
-{
- Url *url;
- Rtext *text;
-};
-
-struct Hist
-{
- Hist *p;
- Hist *n;
- Ctx *c;
-};
-
#pragma varargck type "U" Url*
#pragma varargck type "E" Str2
#pragma varargck type "N" char*
@@ -50,6 +28,7 @@
/* util.c */
void *emalloc(int n);
char *estrdup(char *s);
+int strbeg(char* str, char *prefix);
/* url.c */
int Efmt(Fmt*);
@@ -59,9 +38,4 @@
Url *urlparse(Url *from, char *s);
int matchurl(Url *u, Url *s);
void freeurl(Url *u);
-char *Upath(Url *u);
-
-int request(Url *u);
-void gemini_get(Url *u);
-void texthit(Panel *p, int b, Rtext *t);
-void message(char *s, ...);
\ No newline at end of file
+char *Upath(Url *u);
\ No newline at end of file
--- a/mkfile
+++ b/mkfile
@@ -2,8 +2,8 @@
TARG=castor
LIB=libpanel/libpanel.$O.a
-OFILES=castor.$O
-HFILES=libpanel/panel.h libpanel/rtext.h
+OFILES=url.$O util.$O castor.$O
+HFILES=castor.h libpanel/panel.h libpanel/rtext.h
BIN=/$objtype/bin/
</sys/src/cmd/mkone
--- /dev/null
+++ b/url.c
@@ -1,0 +1,421 @@
+/* this is a copy from webfs */
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include "castor.h"
+
+enum {
+ Domlen = 256,
+};
+
+static char reserved[] = "%:/?#[]@!$&'()*+,;=";
+
+static int
+dhex(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;
+}
+
+static char*
+unescape(char *s, char *spec)
+{
+ char *r, *w;
+ uchar x;
+
+ if(s == nil)
+ return s;
+ for(r=w=s; x = *r; r++){
+ if(x == '%' && isxdigit(r[1]) && isxdigit(r[2])){
+ x = (dhex(r[1])<<4)|dhex(r[2]);
+ if(spec && strchr(spec, x)){
+ *w++ = '%';
+ *w++ = toupper(r[1]);
+ *w++ = toupper(r[2]);
+ }
+ else
+ *w++ = x;
+ r += 2;
+ continue;
+ }
+ *w++ = x;
+ }
+ *w = 0;
+ return s;
+}
+
+int
+Efmt(Fmt *f)
+{
+ char *s, *spec;
+ Str2 s2;
+
+ s2 = va_arg(f->args, Str2);
+ s = s2.s1;
+ spec = s2.s2;
+ for(; *s; s++)
+ if(*s == '%' && isxdigit(s[1]) && isxdigit(s[2])){
+ fmtprint(f, "%%%c%c", toupper(s[1]), toupper(s[2]));
+ s += 2;
+ }
+ else if(isalnum(*s) || strchr(".-_~!$&'()*,;=", *s) || strchr(spec, *s))
+ fmtprint(f, "%c", *s);
+ else
+ fmtprint(f, "%%%.2X", *s & 0xff);
+ return 0;
+}
+
+int
+Nfmt(Fmt *f)
+{
+ char d[Domlen], *s;
+
+ s = va_arg(f->args, char*);
+ if(utf2idn(s, d, sizeof(d)) >= 0)
+ s = d;
+ fmtprint(f, "%s", s);
+ return 0;
+}
+
+int
+Mfmt(Fmt *f)
+{
+ char *s = va_arg(f->args, char*);
+ fmtprint(f, (*s != '[' && strchr(s, ':') != nil)? "[%s]" : "%s", s);
+ return 0;
+}
+
+int
+Ufmt(Fmt *f)
+{
+ char *s;
+ Url *u;
+
+ if((u = va_arg(f->args, Url*)) == nil)
+ return fmtprint(f, "nil");
+ if(u->scheme)
+ fmtprint(f, "%s:", u->scheme);
+ if(u->user || u->host)
+ fmtprint(f, "//");
+ if(u->user){
+ fmtprint(f, "%E", (Str2){u->user, ""});
+ if(u->pass)
+ fmtprint(f, ":%E", (Str2){u->pass, ""});
+ fmtprint(f, "@");
+ }
+ if(u->host){
+ fmtprint(f, "%]", u->host);
+ if(u->port)
+ fmtprint(f, ":%s", u->port);
+ }
+ if(s = Upath(u))
+ fmtprint(f, "%E", (Str2){s, "/:@+"});
+ if(u->query)
+ fmtprint(f, "?%E", (Str2){u->query, "/:@"});
+ if(u->fragment)
+ fmtprint(f, "#%E", (Str2){u->fragment, "/:@?+"});
+ return 0;
+}
+
+char*
+Upath(Url *u)
+{
+ if(u){
+ if(u->path)
+ return u->path;
+ if(u->user || u->host)
+ return "/";
+ }
+ return nil;
+}
+
+static char*
+remdot(char *s)
+{
+ char *b, *d, *p;
+ int dir, n;
+
+ dir = 1;
+ b = d = s;
+ if(*s == '/')
+ s++;
+ for(; s; s = p){
+ if(p = strchr(s, '/'))
+ *p++ = 0;
+ if(*s == '.' && ((s[1] == 0) || (s[1] == '.' && s[2] == 0))){
+ if(s[1] == '.')
+ while(d > b)
+ if(*--d == '/')
+ break;
+ dir = 1;
+ continue;
+ } else
+ dir = (p != nil);
+ if((n = strlen(s)) > 0)
+ memmove(d+1, s, n);
+ *d++ = '/';
+ d += n;
+ }
+ if(dir)
+ *d++ = '/';
+ *d = 0;
+ return b;
+}
+
+static char*
+abspath(char *s, char *b)
+{
+ char *x, *a;
+
+ if(b && *b){
+ if(s == nil || *s == 0)
+ return estrdup(b);
+ if(*s != '/' && (x = strrchr(b, '/'))){
+ a = emalloc((x - b) + strlen(s) + 4);
+ sprint(a, "%.*s/%s", utfnlen(b, x - b), b, s);
+ return remdot(a);
+ }
+ }
+ if(s && *s){
+ if(*s != '/')
+ return estrdup(s);
+ a = emalloc(strlen(s) + 4);
+ sprint(a, "%s", s);
+ return remdot(a);
+ }
+ return nil;
+}
+
+static void
+pstrdup(char **p)
+{
+ if(p == nil || *p == nil)
+ return;
+ if(**p == 0){
+ *p = nil;
+ return;
+ }
+ *p = estrdup(*p);
+}
+
+static char*
+mklowcase(char *s)
+{
+ char *cp;
+ Rune r;
+
+ if(s == nil)
+ return s;
+ cp = s;
+ while(*cp != 0){
+ chartorune(&r, cp);
+ r = tolowerrune(r);
+ cp += runetochar(cp, &r);
+ }
+ return s;
+}
+
+static Url *
+saneurl(Url *u)
+{
+ if(u == nil || u->scheme == nil || u->host == nil || Upath(u) == nil){
+ freeurl(u);
+ return nil;
+ }
+ if(u->port){
+ /* remove default ports */
+ switch(atoi(u->port)){
+ case 21: if(!strcmp(u->scheme, "ftp")) goto Defport; break;
+ case 70: if(!strcmp(u->scheme, "gopher")) goto Defport; break;
+ case 80: if(!strcmp(u->scheme, "http")) goto Defport; break;
+ case 443: if(!strcmp(u->scheme, "https")) goto Defport; break;
+ case 1965: if(!strcmp(u->scheme, "gemini")) goto Defport; break;
+ default: if(!strcmp(u->scheme, u->port)) goto Defport; break;
+ Defport:
+ free(u->port);
+ u->port = nil;
+ }
+ }
+ return u;
+}
+
+Url*
+urlparse(Url *b, char *s)
+{
+ char *t, *p, *x, *y;
+ Url *u;
+
+ if(s == nil)
+ s = "";
+ t = nil;
+ s = p = estrdup(s);
+ u = emalloc(sizeof(*u));
+
+ u->raw = estrdup(s);
+
+ for(; *p; p++){
+ if(*p == ':'){
+ if(p == s)
+ break;
+ *p++ = 0;
+ u->scheme = s;
+ b = nil;
+ goto Abs;
+ }
+ if(!isalpha(*p))
+ if((p == s) || ((!isdigit(*p) && strchr("+-.", *p) == nil)))
+ break;
+ }
+ p = s;
+ if(b){
+ switch(*p){
+ case 0:
+ memmove(u, b, sizeof(*u));
+ goto Out;
+ case '#':
+ memmove(u, b, sizeof(*u));
+ u->fragment = p+1;
+ goto Out;
+ case '?':
+ memmove(u, b, sizeof(*u));
+ u->fragment = u->query = nil;
+ break;
+ case '/':
+ if(p[1] == '/'){
+ u->scheme = b->scheme;
+ b = nil;
+ break;
+ }
+ default:
+ memmove(u, b, sizeof(*u));
+ u->fragment = u->query = u->path = nil;
+ break;
+ }
+ }
+Abs:
+ if(x = strchr(p, '#')){
+ *x = 0;
+ u->fragment = x+1;
+ }
+ if(x = strchr(p, '?')){
+ *x = 0;
+ u->query = x+1;
+ }
+ if(p[0] == '/' && p[1] == '/'){
+ p += 2;
+ if(x = strchr(p, '/')){
+ u->path = t = abspath(x, Upath(b));
+ *x = 0;
+ }
+ if(x = strchr(p, '@')){
+ *x = 0;
+ if(y = strchr(p, ':')){
+ *y = 0;
+ u->pass = y+1;
+ }
+ u->user = p;
+ p = x+1;
+ }
+ if((x = strrchr(p, ']')) == nil)
+ x = p;
+ if(x = strrchr(x, ':')){
+ *x = 0;
+ u->port = x+1;
+ }
+ if(x = strchr(p, '[')){
+ p = x+1;
+ if(y = strchr(p, ']'))
+ *y = 0;
+ }
+ u->host = p;
+ }else{
+ u->path = t = abspath(p, Upath(b));
+ }
+Out:
+ pstrdup(&u->scheme);
+ pstrdup(&u->user);
+ pstrdup(&u->pass);
+ pstrdup(&u->host);
+ pstrdup(&u->port);
+ pstrdup(&u->path);
+ pstrdup(&u->query);
+ pstrdup(&u->fragment);
+ free(s);
+ free(t);
+
+ /* the + character encodes space only in query part */
+ if(s = u->query)
+ while(s = strchr(s, '+'))
+ *s++ = ' ';
+
+ if(s = u->host){
+ t = emalloc(Domlen);
+ if(idn2utf(s, t, Domlen) >= 0){
+ u->host = estrdup(t);
+ free(s);
+ }
+ free(t);
+ }
+
+ unescape(u->user, nil);
+ unescape(u->pass, nil);
+ unescape(u->path, reserved);
+ unescape(u->query, reserved);
+ unescape(u->fragment, reserved);
+ mklowcase(u->scheme);
+ mklowcase(u->host);
+ mklowcase(u->port);
+
+ if((u = saneurl(u)) != nil)
+ u->full = smprint("%U", u);
+
+ return u;
+}
+
+int
+matchurl(Url *u, Url *s)
+{
+ if(u){
+ char *a, *b;
+
+ if(s == nil)
+ return 0;
+ if(u->scheme && (s->scheme == nil || strcmp(u->scheme, s->scheme)))
+ return 0;
+ if(u->user && (s->user == nil || strcmp(u->user, s->user)))
+ return 0;
+ if(u->host && (s->host == nil || strcmp(u->host, s->host)))
+ return 0;
+ if(u->port && (s->port == nil || strcmp(u->port, s->port)))
+ return 0;
+ if(a = Upath(u)){
+ b = Upath(s);
+ if(b == nil || strbeg(a, b))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+freeurl(Url *u)
+{
+ if(u == nil)
+ return;
+ free(u->full);
+ free(u->scheme);
+ free(u->user);
+ free(u->pass);
+ free(u->host);
+ free(u->port);
+ free(u->path);
+ free(u->query);
+ free(u->fragment);
+ free(u->raw);
+ free(u);
+}
--- /dev/null
+++ b/util.c
@@ -1,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include "castor.h"
+
+void *
+emalloc(int n)
+{
+ void *v;
+ if((v = malloc(n)) == nil) {
+ fprint(2, "out of memory allocating %d\n", n);
+ sysfatal("mem");
+ }
+ setmalloctag(v, getcallerpc(&n));
+ memset(v, 0, n);
+ return v;
+}
+
+char *
+estrdup(char *s)
+{
+ char *t;
+ if((t = strdup(s)) == nil) {
+ fprint(2, "out of memory in strdup(%.10s)\n", s);
+ sysfatal("mem");
+ }
+ setmalloctag(t, getcallerpc(&t));
+ return t;
+}
+
+int
+strbeg(char *str, char *prefix)
+{
+ return strncmp(str, prefix, strlen(prefix)) == 0 ? 0 : -1;
+}
\ No newline at end of file