ref: 66deaa106179d8362745bbd367a25b06612863ae
author: sirjofri <sirjofri@sirjofri.de>
date: Thu Oct 17 08:47:11 EDT 2024
adds files
--- /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
+ | 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);
+}