shithub: vcardfs

ref: 732192cde475a07a558f581eecdd21f05cd1607a
dir: /libvcard/vcard.c/

View raw version
#include <u.h>
#include <libc.h>
#include "vcard.h"

extern Vcard *vcparsecard;
extern char *vcparsestr;
extern int yyparse(void);

static char*
estrdup(char *s)
{
	char *t = strdup(s);
	if (!t)
		sysfatal("%r");
	return t;
}

static Vcard*
parse(char *s)
{
	if (!s) {
		werrstr("parse string is nil");
		return nil;
	}
	/* todo: somewhat weird, but this works */
	memset(&vcstate, sizeof vcstate, 0);
	vcparsestr = s;
	vcstate.str = s;
	vcstate.s = nil;
	vcparsecard = nil;
	yyparse();
	return vcparsecard;
}

static void
fold(char *str)
{
	char *s;
	char *end;
	
	if (!str)
		return;
	
	end = strchr(str, 0);
	
	while (str < end) {
		s = strchr(str, '\r');
		if (!s)
			break;
		if (s[1] == 0)
			break;
		if (s[1] == '\n' && (s[2] == ' ' || s[2] == '\t')) {
			memmove(s, s+3, end-s-3);
			end -= 3;
		}
		str = s+1;
	}
	*end = 0;
}

#ifdef TEST
void
_vc_t_fold(char *s)
{
	fold(s);
}
#endif

Vcard*
vcparse(char *s)
{
	fold(s);
	return parse(s);
}

static void
iltoupper(char *s)
{
	while (*s) {
		if (*s >= 'a' && *s <= 'z')
			*s = *s - 'a' + 'A';
		s++;
	}
}

Vcard*
vcparsefile(char *file)
{
	int fd, step;
	char *s, *t;
	long n;
	Vcard *vc;
	
	fd = open(file, OREAD);
	if (fd < 0)
		return nil;
	
	step = 1;
	s = mallocz(8192, 1);
	t = s;
	while ((n = read(fd, t, 8191)) > 0) {
		t += n;
		step++;
		s = realloc(s, 8192 * step);
		memset(s + 8192*(step-1), 0, 8192);
	}
	close(fd);
	
	vc = vcparse(s);
	free(s);
	return vc;
}

static char*
serializeparams(Vparam *param)
{
	Vparam *p;
	char *s, *ns, *ps;
	char *upper;
	
	if (!param)
		return nil;
	
	s = nil;
	for (p = param; p; p = p->next) {
		// TODO: only quote if needed
		upper = estrdup(p->name);
		iltoupper(upper);
		ps = smprint(";%s=\"%s\"", upper, p->value);
		free(upper);
		if (!ps)
			return nil;
		
		if (!s)
			s = ps;
		else {
			ns = smprint("%s%s", s, ps);
			free(s);
			free(ps);
			s = ns;
		}
	}
	return s;
}

static char*
serializeline(Vline *l)
{
	char *ps, *ls;
	char *upper;

	ls = nil;
	ps = serializeparams(l->params);
	upper = estrdup(l->name);
	iltoupper(upper);
	ls = smprint(
		"%s%s"            /* group string */
		"%s%s:%s\r\n",    /* name, param, value */
		(l->group && *l->group ? l->group : ""),  /* group string */
		(l->group && *l->group ? "." : ""),       /* group dot */
		upper,
		ps ? ps : "",
		l->value);
	free(upper);
	if (ps)
		free(ps);
	if (!ls)
		goto Err;
	return ls;

Err:
	if (ls) free(ls);
	if (ps) free(ps);
	return nil;
}

static char*
foldline(char *line)
{
	Rune r;
	char *nline, *s, *lstart, *lend;
	char *cut;
	int l, n;
	int rlen;
	
	l = strlen(line);
	if (l <= 75)
		return line;

	n = l/75*3 + l;
	nline = realloc(line, n+1);
	if (!nline)
		return line;
	memset(nline + l, 0, l/75*3 + 1);
	lend = strchr(nline, 0);
	lstart = nline;

	s = nline;
	while (*s) {
		rlen = chartorune(&r, s);
		s += rlen;
		if (s - lstart < 73)
			continue;
		memmove(s+3, s, lend-s);
		s[0] = '\r';
		s[1] = '\n';
		s[2] = '\t';
		s += 3;
		lend += 3;
		lstart = s;
	}
	
	return nline;
}

static char*
serializelines(Vline *line)
{
	Vline *l;
	char *s, *ns, *ls;
	
	s = nil;
	for (l = line; l; l = l->next) {
		if (cistrcmp(l->name, "version") == 0) {
			s = serializeline(l);
			break;
		}
	}
	if (!s) {
		werrstr("Vcard has no version property");
		return nil;
	}
	for (l = line; l; l = l->next) {
		if (cistrcmp(l->name, "version") == 0)
			continue;
		ls = serializeline(l);
		if (!ls) {
			if (s) free(s);
			return nil;
		}
		ls = foldline(ls);
		if (!s) {
			s = ls;
		} else {
			ns = smprint("%s%s", s, ls);
			free(s);
			free(ls);
			s = ns;
			if (!s)
				return nil;
		}
	}
	return s;
}

char*
vcmserialize(Vcard *card)
{
	Vcard *c;
	char *s, *ns, *cs, *ls;
	
	s = nil;
	for (c = card; c; c = c->next) {
		if (!c->content)
			continue;
		ls = serializelines(c->content);
		if (!ls) {
			if (s) free(s);
			return nil;
		}
		cs = smprint(
			"BEGIN:VCARD\r\n"
			"%s"
			"END:VCARD\r\n",
			ls);
		if (!cs) {
			free(ls);
			if (s) free(s);
			return nil;
		}
		
		if (!s) {
			s = cs;
			free(ls);
		} else {
			ns = smprint("%s%s", s, cs);
			free(s);
			free(ls);
			free(cs);
			s = ns;
			if (!s)
				return nil;
		}
	}
	return s;
}

void
vcfreeparams(Vparam *params)
{
	Vparam *p, *np;
	
	for (p = params; p; p = np) {
		np = p->next;
		if (p->name)
			free(p->name);
		if (p->value)
			free(p->value);
		free(p);
	}
}

void
vcfreelines(Vline *lines)
{
	Vline *l, *nl;
	
	for (l = lines; l; l = nl) {
		nl = l->next;
		if (l->name)
			free(l->name);
		if (l->value)
			free(l->value);
		if (l->group)
			free(l->group);
		if (l->params)
			vcfreeparams(l->params);
		free(l);
	}
}

void
vcfreecards(Vcard *cards)
{
	Vcard *c, *nc;
	
	for (c = cards; c; c = nc) {
		nc = c->next;
		if (c->content)
			vcfreelines(c->content);
		free(c);
	}
}