shithub: vcardfs

Download patch

ref: 66deaa106179d8362745bbd367a25b06612863ae
author: sirjofri <sirjofri@sirjofri.de>
date: Thu Oct 17 08:47:11 EDT 2024

adds files

diff: cannot open b/libvcard//null: file does not exist: 'b/libvcard//null'
--- /dev/null
+++ b/john.vcf
@@ -1,0 +1,11 @@
+BEGIN:VCARD
+VERSION:4.0
+N;TYPE=ABC;type="A;B;C":Doe;John;;
+FN:John 
+ Doe
+END:VCARD
+BEGIN:VCARD
+VERSION:4.0
+N:D;Jane
+FN:Jane D
+END:VCARD
\ No newline at end of file
--- /dev/null
+++ b/libvcard/mkfile
@@ -1,0 +1,18 @@
+</$objtype/mkfile
+
+LIB=libvcard.a$O
+OFILES=\
+	y.tab.$O\
+	vlex.$O\
+	vcard.$O\
+
+HFILES=vcard.h
+
+YFILES=vcard.y
+
+CFLAGS=-DTEST $CFLAGS
+
+</sys/src/cmd/mklib
+
+y.tab.h y.tab.c: $YFILES
+	yacc -dv -D0 $YFLAGS $prereq
--- /dev/null
+++ b/libvcard/vcard.c
@@ -1,0 +1,83 @@
+#include <u.h>
+#include <libc.h>
+#include "vcard.h"
+
+extern Vcard *vcparsecard;
+extern char *vcparsestr;
+extern int yyparse(void);
+
+static Vcard*
+parse(char *s)
+{
+	memset(&vcstate, sizeof vcstate, 0);
+	vcparsestr = s;
+	vcparsecard = nil;
+	yyparse();
+	return vcparsecard;
+}
+
+static void
+fold(char *str)
+{
+	char *s;
+	char *end;
+	
+	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);
+}
+
+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;
+}
\ No newline at end of file
--- /dev/null
+++ b/libvcard/vcard.h
@@ -1,0 +1,35 @@
+typedef struct Vcard Vcard;
+typedef struct Vline Vline;
+typedef struct Vparam Vparam;
+typedef struct Vstate Vstate;
+
+struct Vcard {
+	Vline *content;
+	Vcard *next;
+};
+
+struct Vline {
+	char *name;
+	char *value;
+	char *group;
+	Vparam *params;
+	Vline *next;
+};
+
+struct Vparam {
+	char *name;
+	char *value;
+	Vparam *next;
+};
+
+struct Vstate {
+	char *str;
+	char *s;
+	int invalue;
+	int inquote;
+};
+
+extern Vstate vcstate;
+
+Vcard* vcparse(char*);
+Vcard* vcparsefile(char*);
--- /dev/null
+++ b/libvcard/vcard.y
@@ -1,0 +1,189 @@
+%{
+#include <u.h>
+#include <libc.h>
+#include "vcard.h"
+
+Vcard *vcparsecard;
+
+extern int yylex(void);
+
+void
+yyerror(char *s)
+{
+	werrstr("%s (%10s)\n", s, vcstate.s);
+}
+
+static Vcard*
+mkvcard(Vline *lines)
+{
+	Vcard *vc;
+	vc = mallocz(sizeof(Vcard), 1);
+	vc->content = lines;
+	return vc;
+}
+
+static void
+enqueue(Vcard *head, Vcard *tail)
+{
+	head->next = tail;
+	vcparsecard = head;
+}
+
+static void
+enqueuel(Vline *head, Vline *tail)
+{
+	head->next = tail;
+}
+
+static void
+enqueuep(Vparam *head, Vparam *tail)
+{
+	head->next = tail;
+}
+
+static Vline*
+mkline(char *name, Vparam *params, char *value, char *group)
+{
+	Vline *vl;
+	vl = mallocz(sizeof(Vline), 1);
+	vl->name = name;
+	vl->value = value;
+	vl->params = params;
+	vl->group = group;
+	/*
+	fprint(2, "new line:\n");
+	fprint(2, "  name: %s\n", name);
+	fprint(2, "  value: %s\n", value);
+	fprint(2, "  params: %p\n", params);
+	fprint(2, "  group: %s\n", group);
+	*/
+	return vl;
+}
+
+static Vparam*
+addparam(char *name, char *value)
+{
+	Vparam *vp;
+	vp = mallocz(sizeof(Vparam), 1);
+	vp->name = name;
+	vp->value = value;
+	return vp;
+}
+
+static char*
+xname(char *s)
+{
+	char *n;
+	n = smprint("x-%s", s);
+	free(s);
+	return n;
+}
+
+%}
+
+%union {
+	int i;
+	Vcard *vc;
+	Vline *vl;
+	Vparam *vp;
+	char *s;
+}
+
+%token	<i> 	BEGIN END CRLF
+
+%token	<s> 	WORD SWORD FWORD
+%token	<s> 	SOURCE KIND FN N NICKNAME PHOTO BDAY ANNIVERSARY GENDER
+%token	<s> 	ADR TEL EMAIL IMPP LANG TZ GEO TITLE ROLE LOGO ORG MEMBER
+%token	<s> 	RELATED CATEGORIES NOTE PRODID REV SOUND UID CLIENTPIDMAP
+%token	<s> 	URL KEY FBURL CALADRURI CALURI XML
+
+%type	<s> 	iana-token x-name
+%type	<s> 	pvalue
+
+%type	<vc>	vclist vcard
+%type	<vl>	cline clinel
+%type	<vp>	params param
+%type	<s> 	group name value
+
+%%
+
+vclist:
+	  vcard { $$ = $1; enqueue($1, nil); }
+	| vcard vclist { $$ = $1; enqueue($1, $2); }
+	;
+
+vcard:
+	  BEGIN clinel END { $$ = mkvcard($2); }
+	| BEGIN clinel END CRLF { $$ = mkvcard($2); }
+	;
+
+clinel:
+	  cline
+	| cline clinel { $$ = $1; enqueuel($1, $2); }
+	;
+
+cline:
+	  group '.' name params ':' value CRLF { $$ = mkline($3, $4, $6, $1); }
+	| name params ':' value CRLF { $$ = mkline($1, $2, $4, nil); }
+	| name ':' value CRLF { $$ = mkline($1, nil, $3, nil); }
+	;
+
+params:
+	  ';' param { $$ = $2; }
+	| ';' param params { $$ = $2; enqueuep($2, $3); }
+	;
+
+param: SWORD '=' pvalue { $$ = addparam($1, $3); };
+
+pvalue:
+	  SWORD
+	| '"' WORD '"' { $$ = $2; }
+	| '"' SWORD '"' { $$ = $2; }
+	| '"' FWORD '"' { $$ = $2; }
+	;
+
+group: SWORD;
+name:
+	  SOURCE
+	| KIND
+	| FN
+	| N
+	| NICKNAME
+	| PHOTO
+	| BDAY
+	| ANNIVERSARY
+	| GENDER
+	| ADR
+	| TEL
+	| EMAIL
+	| IMPP
+	| LANG
+	| TZ
+	| GEO
+	| TITLE
+	| ROLE
+	| LOGO
+	| ORG
+	| MEMBER
+	| RELATED
+	| CATEGORIES
+	| NOTE
+	| PRODID
+	| REV
+	| SOUND
+	| UID
+	| CLIENTPIDMAP
+	| URL
+	| KEY
+	| FBURL
+	| CALADRURI
+	| CALURI
+	| XML
+	| iana-token
+	| x-name
+	;
+
+iana-token: SWORD;
+x-name: 'x' '-' SWORD { $$ = xname($3); };
+
+value: WORD | SWORD | FWORD;
--- /dev/null
+++ b/libvcard/vlex.c
@@ -1,0 +1,178 @@
+#include <u.h>
+#include <libc.h>
+#include "vcard.h"
+#include "y.tab.h"
+
+static void
+iltolower(char *s)
+{
+	while (*s) {
+		if (*s >= 'A' && *s <= 'Z')
+			*s = *s - 'A' + 'a';
+		s++;
+	}
+}
+
+char *vcparsestr;
+Vstate vcstate;
+
+typedef struct Vcname Vcname;
+struct Vcname {
+	char *str;
+	int token;
+};
+
+static char beginstr[] = "begin:vcard\r\nversion:4.0\r\n";
+
+Vcname vcnames[] = {
+	{ "end:vcard", END },
+	/* fields */
+	{ "kind", KIND },
+	{ "fn", FN },
+	{ "n", N },
+	{ "nickname", NICKNAME },
+	{ "photo", PHOTO },
+	{ "bday", BDAY },
+	{ "anniversary", ANNIVERSARY },
+	{ "gender", GENDER },
+	{ "adr", ADR },
+	{ "tel", TEL },
+	{ "email", EMAIL },
+	{ "impp", IMPP },
+	{ "lang", LANG },
+	{ "tz", TZ },
+	{ "geo", GEO },
+	{ "title", TITLE },
+	{ "role", ROLE },
+	{ "logo", LOGO },
+	{ "org", ORG },
+	{ "member", MEMBER },
+	{ "related", RELATED },
+	{ "categories", CATEGORIES },
+	{ "note", NOTE },
+	{ "prodid", PRODID },
+	{ "rev", REV },
+	{ "sound", SOUND },
+	{ "uid", UID },
+	{ "clientpidmap", CLIENTPIDMAP },
+	{ "url", URL },
+	{ "key", KEY },
+	{ "fburl", FBURL },
+	{ "caladruri", CALADRURI },
+	{ "caluri", CALURI },
+	{ "xml", XML },
+	/* params */
+	{ "language", SWORD },
+	{ "value", SWORD },
+	{ "pref", SWORD },
+	{ "altid", SWORD },
+	{ "pid", SWORD },
+	{ "type", SWORD },
+	{ "mediatype", SWORD },
+	{ "calscale", SWORD },
+	{ "sort-as", SWORD },
+	{ "geo", SWORD },
+	{ "tz", SWORD }, /* TODO: it's a duplicate! make state-dependent */
+	{ nil, 0 }
+};
+
+static char verbchars[] = ":;\"=";
+
+int
+yylex(void)
+{
+	Vcname *nm;
+	int n;
+	char *s, *t;
+	
+	if (!vcparsestr)
+		return 0;
+	
+	if (!vcstate.s) {
+		vcstate.s = vcparsestr;
+		vcstate.str = vcparsestr;
+	}
+	
+	/* value string */
+	if (vcstate.invalue) {
+		s = strstr(vcstate.s, "\r\n");
+		if (!s) {
+			yylval.s = strdup(vcstate.s);
+			vcstate.s = strchr(vcstate.s, 0);
+			return FWORD;
+		}
+		yylval.s = mallocz(s - vcstate.s + 1, 1);
+		memcpy(yylval.s, vcstate.s, s - vcstate.s);
+		vcstate.s = s;
+		vcstate.invalue = 0;
+		return FWORD;
+	}
+	
+	/* TODO: quoted string */
+	if (vcstate.inquote == 1) {
+		s = strchr(vcstate.s, '"');
+		if (!s) {
+			fprint(2, "parse error: quotes not closing!\n");
+			return -1;
+		}
+		yylval.s = mallocz(s - vcstate.s + 1, 1);
+		memcpy(yylval.s, vcstate.s, s - vcstate.s);
+		vcstate.s = s;
+		vcstate.inquote = 2;
+		return FWORD;
+	}
+	
+	/* verbatim characters */
+	if (strchr(verbchars, *vcstate.s)) {
+		switch (*vcstate.s) {
+		case ':':
+			vcstate.invalue++;
+			break;
+		case '"':
+			if (vcstate.inquote == 2) {
+				vcstate.inquote = 0;
+			} else {
+				vcstate.inquote++;
+			}
+			break;
+		}
+		n = *vcstate.s;
+		vcstate.s++;
+		return n;
+	}
+	
+	/* newline */
+	if (cistrncmp(vcstate.s, "\r\n", 2) == 0) {
+		vcstate.s += 2;
+		return CRLF;
+	}
+	
+	/* begin block */
+	n = strlen(beginstr);
+	if (cistrncmp(vcstate.s, beginstr, n) == 0) {
+		vcstate.s += n;
+		return BEGIN;
+	}
+	
+	/* reserved keywords */
+	for (nm = vcnames; nm->str; nm++) {
+		n = strlen(nm->str);
+		if (cistrncmp(vcstate.s, nm->str, n) == 0) {
+			yylval.s = mallocz(n+1, 1);
+			memcpy(yylval.s, vcstate.s, n);
+			iltolower(yylval.s);
+			vcstate.s += n;
+			return nm->token;
+		}
+	}
+	
+	/* assume SWORD string (unquoted param value) */
+	s = strchr(vcstate.s, ':');
+	t = strchr(vcstate.s, ';');
+	s = s < t ? s : t;
+	n = s - vcstate.s;
+	yylval.s = mallocz(n + 1, 1);
+	memcpy(yylval.s, vcstate.s, n);
+	vcstate.s += n;
+	return SWORD;
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,12 @@
+</$objtype/mkfile
+
+TARG=pim\
+	vcffs\
+
+LIB=libvcard/libvcard.a$O
+
+</sys/src/cmd/mkmany
+
+$LIB:V:
+	cd libvcard
+	mk
--- /dev/null
+++ b/pim.c
@@ -1,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <String.h>
+#include "libvcard/vcard.h"
+
+extern void _vc_t_fold(char*);
+
+void
+printparams(Vparam *p)
+{
+	for (; p; p = p->next) {
+		fprint(2, "  param: %s → %s\n", p->name, p->value);
+	}
+}
+
+void
+main(int argc, char **argv)
+{
+	String *s;
+	Biobuf *b;
+	Vcard *vc, *card;
+	Vline *vl;
+	
+	ARGBEGIN{
+	case 'h':
+		break;
+	}ARGEND;
+	
+	if (argc < 1)
+		sysfatal("usage");
+	
+	b = Bopen(argv[0], OREAD);
+	if (!b)
+		sysfatal("%r");
+	
+	s = s_new();
+	
+	while (s_read(b, s, 256) > 0)
+		continue;
+	
+	Bterm(b);
+	
+	_vc_t_fold(s_to_c(s));
+	fprint(2, "%s\n\n", s_to_c(s));
+	
+	vc = vcparse(s_to_c(s));
+	if (!vc)
+		sysfatal("err: %r");
+	
+	fprint(2, "printing results:\n\n");
+	
+	for (card = vc; card; card = card->next) {
+		fprint(2, "NEW CARD\n");
+		for (vl = card->content; vl; vl = vl->next) {
+			fprint(2, "%s → %s\n", vl->name, vl->value);
+			if (vl->params)
+				printparams(vl->params);
+		}
+	}
+	
+	exits(nil);
+}
--- /dev/null
+++ b/vcffs.c
@@ -1,0 +1,278 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "libvcard/vcard.h"
+
+/* rough structure
+
+/john-doe/
+	/fn/
+		/data (line value)
+		/group (line group)
+		/param (param value)
+	/tel/
+		/data (line value)
+		/group (line group)
+		/param (param value)
+/ctl
+	- "write" (save file)
+/import
+	- write new vcf file to import
+
+*/
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-s srv] [-m mtpt] file\n", argv0);
+	exits("usage");
+}
+
+static void*
+emalloc(long n)
+{
+	void *p;
+	p = mallocz(n, 1);
+	if (!p)
+		sysfatal("emalloc: %r");
+	return p;
+}
+
+static char*
+estrdup(char *s)
+{
+	void *t;
+	t = strdup(s);
+	if (!t)
+		sysfatal("estrdup: %r");
+	return t;
+}
+
+enum {
+	Qroot,
+		Qctl,
+		Qimport,
+		Qcard,
+			Qline,
+				Qdata,
+				Qgroup,
+				Qparams,
+					Qparamdata,
+};
+
+typedef struct Vfile Vfile;
+struct Vfile {
+	int level;
+	Vcard *card;
+	Vline *line;
+	Vparam *param;
+};
+
+char *user = nil;
+char *mtpt = "/mnt/vcf";
+char *service = nil;
+
+char *file = nil;
+Vcard *cards = nil;
+
+static Vfile*
+emkvfile(int level, Vcard *c, Vline *l, Vparam *p)
+{
+	Vfile *f;
+	f = mallocz(sizeof(Vfile), 1);
+	if (!f)
+		sysfatal("%r");
+	f->level = level;
+	f->card = c;
+	f->line = l;
+	f->param = p;
+	return f;
+}
+
+static void
+fsread(Req *r)
+{
+	Vfile *f;
+	f = r->fid->file->aux;
+	switch (f->level) {
+	case Qctl:
+		respond(r, nil);
+		return;
+	case Qimport:
+		respond(r, nil);
+		return;
+	case Qdata:
+		readstr(r, f->line->value);
+		respond(r, nil);
+		return;
+	case Qgroup:
+		if (!f->line->group) {
+			respond(r, "file not found");
+			return;
+		}
+		readstr(r, f->line->group);
+		respond(r, nil);
+		return;
+	case Qparamdata:
+		readstr(r, f->param->value);
+		respond(r, nil);
+		return;
+	}
+	respond(r, "not implemented");
+}
+
+static void
+fswrite(Req *r)
+{
+	Vfile *f;
+	f = r->fid->file->aux;
+	switch (f->level) {
+	case Qctl:
+		break;
+	}
+	respond(r, "not implemented");
+}
+
+Srv fs = {
+	.read = fsread,
+	.write = fswrite,
+};
+
+/* TODO: LOOKAT:
+	/sys/src/cmd/webcookies.c:/createfile
+	/sys/src/cmd/aux/gps/gpsfs.c:/createfile
+*/
+static char*
+safename(char *n)
+{
+	char *s;
+	
+	s = estrdup(n);
+	n = s;
+	while (*n) {
+		switch (*n) {
+		case ' ':
+			*n = '_';
+			break;
+		case '\t':
+			*n = '_';
+			break;
+		}
+		n++;
+	}
+	return s;
+}
+
+static char*
+getcardname(Vcard *c)
+{
+	Vline *l;
+	Vline *fn = nil, *n = nil;
+	
+	for (l = c->content; l; l = l->next) {
+		if (cistrcmp(l->name, "fn") == 0)
+			fn = l;
+		else if (cistrcmp(l->name, "n") == 0)
+			n = l;
+		if (fn && n)
+			break;
+	}
+	
+	if (fn)
+		return safename(fn->value);
+	if (n)
+		return safename(n->value);
+	return nil;
+}
+
+static void
+initcardfiles(Vcard *chain)
+{
+	File *fc, *fl, *fp, *f;
+	Vcard *c;
+	Vline *l;
+	Vparam *p;
+	Vfile *vf;
+	char *s;
+	
+	// TODO: fill with Vfile structure, which points to card data
+	
+	for (c = chain; c; c = c->next) {
+		s = getcardname(c);
+		if (!s)
+			continue;
+		vf = emkvfile(Qcard, c, nil, nil);
+		fc = createfile(fs.tree->root, s, user, DMDIR|0555, vf);
+		free(s);
+		if (!fc)
+			sysfatal("%r");
+		
+		for (l = c->content; l; l = l->next) {
+			vf = emkvfile(Qline, c, l, nil);
+			fl = createfile(fc, l->name, user, DMDIR|0555, vf);
+			vf = emkvfile(Qdata, c, l, nil);
+			f = createfile(fl, "data", user, 0666, vf);
+	//		closefile(f);
+			if (l->group) {
+				vf = emkvfile(Qgroup, c, l, nil);
+				f = createfile(fl, "group", user, 0666, vf);
+	//			closefile(f);
+			}
+			vf = emkvfile(Qparams, c, l, nil);
+			f = createfile(fl, "params", user, DMDIR|0555, vf);
+	//		closefile(f);
+			
+			for (p = l->params; p; p = p->next) {
+				vf = emkvfile(Qparamdata, c, l, p);
+				fp = createfile(f, p->name, user, 0666, vf);
+	//			closefile(fp);
+			}
+	//		closefile(fl);
+		}
+	//	closefile(fc);
+	}
+}
+
+void
+main(int argc, char **argv)
+{
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 's':
+		service = EARGF(usage());
+		break;
+	default:
+		usage();
+		break;
+	}ARGEND;
+	
+	rfork(RFNOTEG);
+	
+	if (argc != 1)
+		usage();
+	
+	file = argv[0];
+	cards = vcparsefile(file);
+	
+	if (!cards)
+		sysfatal("%r");
+	
+	user = getuser();
+	
+	fs.tree = alloctree("vcf", "vcf", DMDIR|0555, nil);
+	createfile(fs.tree->root, "ctl", user, 0666,
+		emkvfile(Qctl, nil, nil, nil));
+	createfile(fs.tree->root, "import", user, 0666,
+		emkvfile(Qimport, nil, nil, nil));
+	initcardfiles(cards);
+	
+	postmountsrv(&fs, service, mtpt, MREPL);
+	exits(nil);
+}