Sigrid Solveig Haflínudóttirupdate987fe3112 years agoHistory
576 lines · 11.3 KB · C
RawDownload
#include "spotifs.h"#include "xml.h" /* * Yet another [retarded XML] parser. */ enum {	Tcmplx,	Tlist, 	/*	 * The next type deserves a separate comment.	 * One expects to get <tracks><track></track><track></track></tracks>	 * structure in XML. This is what you get on "search" query.	 * One gets <discs><disc></disc>...</discs>, which is correct as well.	 * But then THIS:	 * <discs>	 *   <disc>	 *     <disc-number></disc-number>	 *     <name></name>	 *     <track></track>	 *     <track></track>	 *     <track></track>	 *     ....	 */	Tlist2, 	Tstr,	Tfloat,	Tint,	Tbool,	Tfid, /* 20 bytes */	Tid,  /* 16 bytes */ 	Stflinl= 1,	Stflerr= 2,}; typedef struct Xml Xml;struct Xml {	char	*name;	int		type;	Xml		*chain;	ulong	offset;	int		size;}; typedef struct {	int  flags;	char *t;     /* current tag */	int  indent; /* to debug stuff */}State; #define end {nil, 0, nil} static char *escmap[] = {	"\x06\"&quot;",	"\x06\'&apos;",	"\x04<&lt;",	"\x04>&gt;",	"\x05&&amp;",}; static Xml track;static Xml artist;static Xml album; static Xml img[] = {	{"id",     Tfid, nil, offsetof(Img, id[0]),    0},	{"width",  Tint, nil, offsetof(Img, width),    0},	{"height", Tint, nil, offsetof(Img, height),   0},	{"small",  Tfid, nil, offsetof(Img, small[0]), 0},	{"large",  Tfid, nil, offsetof(Img, large[0]), 0},	end}; static Xml biofields[] = {	{"text", Tstr, nil, offsetof(Bio, text), 0},	end}; static Xml bio = {"bio", Tcmplx, biofields, 0, sizeof(Bio)}; static Xml copy[] = {	{"c", Tstr, nil, offsetof(Copy, c), 0},	{"p", Tstr, nil, offsetof(Copy, p), 0},	end}; static Xml artistfields[] = {	{"popularity",      Tfloat, nil,     offsetof(Artist, pop),     0},	{"name",            Tstr,   nil,     offsetof(Artist, name),    0},	{"portrait",        Tcmplx, img,     offsetof(Artist, img),     sizeof(Img)},	{"bios",            Tlist,  &bio,    offsetof(Artist, bios),    0},	{"genres",          Tstr,   nil,     offsetof(Artist, genres),  0},	{"years-active",    Tstr,   nil,     offsetof(Artist, years),   0},	{"tophits",         Tlist,  &track,  offsetof(Artist, top),     0},	{"similar-artists", Tlist,  &artist, offsetof(Artist, similar), 0},	{"albums",          Tlist,  &album,  offsetof(Artist, albums),  0},	{"id",              Tid,    nil,     offsetof(Artist, id[0]),   0},	end}; static Xml artist = {"artist", Tcmplx, artistfields, 0, sizeof(Artist)}; static Xml discfields[] = {	{"disc-number", Tint,   nil,    offsetof(Disc, n),      0},	{"name",        Tstr,   nil,    offsetof(Disc, name),   0},	{"track",       Tlist2, &track, offsetof(Disc, tracks), 0},	end}; static Xml disc = {"disc", Tcmplx, discfields, 0, sizeof(Disc)}; static Xml albumfields[] = {	{"popularity",  Tfloat, nil,   offsetof(Album, pop),       0},	{"name",        Tstr,   nil,   offsetof(Album, name),      0},	{"artist-name", Tstr,   nil,   offsetof(Album, aname),     0},	{"album-type",  Tstr,   nil,   offsetof(Album, atype),     0},	{"review",      Tstr,   nil,   offsetof(Album, review),    0},	{"copyright",   Tcmplx, copy,  offsetof(Album, copy),      sizeof(Copy)},	{"discs",       Tlist,  &disc, offsetof(Album, discs),     0},	{"cover",       Tfid,   nil,   offsetof(Album, c[0]),      0},	{"cover-small", Tfid,   nil,   offsetof(Album, csmall[0]), 0},	{"cover-large", Tfid,   nil,   offsetof(Album, clarge[0]), 0},	{"artist-id",   Tid,    nil,   offsetof(Album, aid[0]),    0},	{"id",          Tid,    nil,   offsetof(Album, id[0]),     0},	end}; static Xml album = {"album", Tcmplx, albumfields, 0, sizeof(Album)}; static Xml filefields[] = {	{"id",     Tfid, nil, offsetof(Sfile, id[0]), 0},	{"format", Tstr, nil, offsetof(Sfile, fmt),   0},	end}; static Xml file = {"file", Tcmplx, filefields, 0, sizeof(Sfile)}; static Xml trackfields[] = {	{"track-number",    Tint,   nil,    offsetof(Track, n),         0},	{"year",            Tint,   nil,    offsetof(Track, year),      0},	{"length",          Tint,   nil,    offsetof(Track, len),       0},	{"title",           Tstr,   nil,    offsetof(Track, name),      0},	{"artist",          Tstr,   nil,    offsetof(Track, aname),     0},	{"album",           Tstr,   nil,    offsetof(Track, alname),    0},	{"album-artist",    Tstr,   nil,    offsetof(Track, aaname),    0},	{"files",           Tlist,  &file,  offsetof(Track, files),     0},	{"popularity",      Tfloat, nil,    offsetof(Track, pop),       0},	{"explicit",        Tbool,  nil,    offsetof(Track, explicit),  0},	{"id",              Tid,    nil,    offsetof(Track, id[0]),     0},	{"artist-id",       Tid,    nil,    offsetof(Track, aid[0]),    0},	{"album-id",        Tid,    nil,    offsetof(Track, alid[0]),   0},	{"album-artist-id", Tid,    nil,    offsetof(Track, aaid[0]),   0},	{"cover",           Tfid,   nil,    offsetof(Track, c[0]),      0},	{"cover-small",     Tfid,   nil,    offsetof(Track, csmall[0]), 0},	{"cover-large",     Tfid,   nil,    offsetof(Track, clarge[0]), 0},	end}; static Xml track = {"track", Tcmplx, trackfields, 0, sizeof(Track)}; static Xml searchfields[] = {	{"artists", Tlist, &artist, offsetof(Search, artists), 0},	{"albums",  Tlist, &album,  offsetof(Search, albums),  0},	{"tracks",  Tlist, &track,  offsetof(Search, tracks),  0},	end}; static Xml search = {"result", Tcmplx, searchfields, 0, sizeof(Search)}; static Xml *type2xml[] = {	[Xtsearch]	&search,	[Xtartist]	&artist,	[Xtalbum]	&album,}; static voidfreex(Xml *x, void *p){	void *fp;	void **lp;	int i; 	if(p == nil)		return; 	switch(x->type){	case Tcmplx:		for(x = x->chain; x->name != nil; x++){			fp = *((void**)((uchar*)p + x->offset));			freex(x, fp);		}		free(p);		break; 	case Tlist:	case Tlist2:		lp = p;		for(i = 0; lp[i] != nil; i++)			freex(x->chain, lp[i]);		free(lp);		break; 	case Tstr:		free(p);		break; 	case Tfloat:	case Tint:	case Tbool:	case Tfid:	case Tid:		break;	}} voidxmlfree(int type, void *p){	assert(type < nelem(type2xml) && type >= 0);	freex(type2xml[type], p);} static char*skiptag(char **s, State state){	char *p;	int len; 	p = *s;	if((state.flags&Stflinl) != 0){		p = utfutf(p, "/>");		if(p != nil)			p += 2;	}else{		len = strlen(state.t);		for(; p != nil;){			p = utfutf(p, "</");			if(p != nil){				if(strncmp(p+2, state.t, len) == 0 && p[2+len] == '>'){					p += 2+len;					break;				}				p += 2;			}		}	} 	*s = p;	return p;} static char*gettag(char **s, State *state){	char *p, *tag;	int inl; 	inl = state->flags & Stflinl; 	for(p = *s; *p != 0; p++){		if(!inl){			if(*p != '<')				continue;			p++;		}else		if(*p == ' ')			continue; 		if(*p >= 'a' && *p <= 'z')			break;		if(*p == '/' && (!inl || p[1] == '>')){			*s = inl ? p+2 : p+1;			state->flags &= ~Stflinl;			return nil;		} 		werrstr("unexpected '%c%c'", *p, *(p+1));		goto error;	} 	if(*p == 0){		werrstr("unexpected EOF");		goto error;	} 	for(tag = p, p++; *p != 0; p++){		if((*p >= 'a' && *p <= 'z') || *p == '-')			continue; 		if(inl){			if(*p == '='){				*p = 0;				break;			}		}else{			if(*p == ' '){				*p = 0;				state->flags |= Stflinl;				break;			}			if(*p == '>'){				*p = 0;				break;			}			if(*p == '/' && p[1] == '>'){				*s = p+2;				return gettag(s, state);			}		} 		werrstr("unexpected '%c' as tag name end", *p);		goto error;	} 	*s = p+1;	return tag; error:	werrstr("gettag: %r");	state->flags |= Stflerr;	return nil;} static char*getvalue(char **s, State *state){	char *p, *v;	int len; 	p = *s;	if((state->flags&Stflinl) != 0){		if(*p != '"'){			werrstr("expected '\"'");			goto error;		}		p++;		v = p;		p = utfrune(p, '"');		if(p != nil){			*p = 0;			*s = p+1;			return v;		}		werrstr("unclosed double quote");		goto error;	} 	v = p;repeat:	p = utfrune(p, '<');	if(p == nil){		werrstr("unexpected EOF");		goto error;	} 	p++;	len = strlen(state->t);	if(strncmp(p, "![CDATA[", 8) == 0){		v = p+8;		p = utfutf(v, "]]");		if(p == nil){			werrstr("CDATA not closed");			goto error;		}		*p = 0;		p += 3;		goto repeat;	} 	if(*p != '/' || strncmp(p+1, state->t, len) != 0){		werrstr("tag not closed");		goto error;	}	*(p-1) = 0;	*s = p+1+len+1;	return v; error:	state->flags |= Stflerr;	werrstr("getvalue: %r");	return nil;} static voidhex2hash(char *hex, uchar *hash, int size){	static uchar h2d[] = {		['0'] 0,		['1'] 1,		['2'] 2,		['3'] 3,		['4'] 4,		['5'] 5,		['6'] 6,		['7'] 7,		['8'] 8,		['9'] 9,		['a'] 10,		['b'] 11,		['c'] 12,		['d'] 13,		['e'] 14,		['f'] 15,	};	int i; 	for(i = 0; i < size*2; i += 2)		hash[i>>1] = h2d[hex[i]]<<4 | h2d[hex[i+1]];} static char*unxml(char *orig){	char *s;	int i, n; 	n = 0;	for(s = orig; *s; s++, n++){		orig[n] = *s;		for(i = 0; i < nelem(escmap); i++){			if(strncmp(s, &escmap[i][2], escmap[i][0]) == 0){				orig[n] = escmap[i][1];				s += escmap[i][0] - 1;				break;			}		}	} 	orig[n] = 0;	return orig;} static void*parse(Xml *x, char **s, State state, void **r){	State st;	Xml *subx;	char *t, *v;	void **pa;	int num; 	assert(r != nil);	if(!**s)		return nil; 	st = state;	switch(x->type){	case Tcmplx:		if(debug > 1)			fprint(2, "%*c%s\n", state.indent*4, 0, state.t);		*r = mallocz(x->size, 1);		assert(x->chain != nil);		for(;;){			st = state;			t = gettag(s, &st);			if((state.flags&Stflerr) != 0)				goto error;			if(t == nil)				break;			st.t = t;			st.indent++;			for(subx = x->chain; subx->name != nil; subx++){				if(strcmp(subx->name, t) == 0){					parse(subx, s, st, (void**)((uchar*)*r + subx->offset));					break;				}			}			if(subx->name == nil && skiptag(s, st) == nil)					goto error;		}		break; 	case Tlist2:		subx = x->chain;		assert(subx != nil);		pa = *r;		for(num = 1; pa != nil && pa[num-1] != nil; num++);		pa = realloc(pa, sizeof(void*)*(num+1));		*r = pa;		pa[num] = pa[num-1] = nil;		parse(subx, s, st, &pa[num-1]);		break; 	case Tlist:		if(debug > 1)			fprint(2, "%*c%s\n", state.indent*4, 0, state.t);		subx = x->chain;		assert(subx != nil);		for(num = 1;;){			st = state;			t = gettag(s, &st);			if((state.flags&Stflerr) != 0)				goto error;			if(t == nil)				break;			st.t = t;

Lines 1–500 of 576.