shithub: vdict

ref: 5fde169e1ca610a2cd56e943e9dd669c63c0c07a
dir: vdict/dictc.c

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <String.h>
#include <draw.h>
#include <thread.h>
#include "a.h"

typedef struct Response Response;

struct Response
{
	int code;
	char *msg;
};

struct Dvec
{
	void **elts;
	usize  len;
	usize  sz;
};

enum
{
	Eok,
	Eeof,
	Eunexpected,
	Ebadformat,
	Enoshow,
};

const char* Errors[] = {
	[Eeof]        = "no response from server",
	[Eunexpected] = "unexpected response from server",
	[Ebadformat]  = "bad response format",
	[Enoshow]     = "show command did not return any result",
};

Dvec*
mkdvec(usize size)
{
	Dvec *v;

	v = emalloc(sizeof *v);
	v->len  = 0;
	v->sz   = size;
	v->elts = emalloc(size * sizeof(void*));
	return v;
}

void
dvadd(Dvec *v, void *p)
{
	if(v->len == v->sz){
		v->sz  *= 1.5;
		v->elts = erealloc(v->elts, v->sz * sizeof(void*));
	}
	v->elts[v->len++] = p;
}

usize
dvlen(Dvec *v)
{
	return v->len;
}

void*
dvref(Dvec *v, usize i)
{
	return v->elts[i];
}

int
readstatus(char *s)
{
	char *e;
	long l;

	l = strtol(s, &e, 10);
	if(l == 0 || e == s)
		return -1;
	return l;
}

int
expectline(Dictc *c, char *l)
{
	char *s;

	s = Brdstr(c->bin, '\n', 1);
	if(s == nil)
		return Eeof;
	if(strncmp(s, l, strlen(l)) == 0)
		return Eok;
	return Ebadformat;
}

int
sendcmd(Dictc *c, const char *cmd, Response *r)
{
	char *s;

	write(c->fd, cmd, strlen(cmd));
	write(c->fd, "\n", 1);
	s = Brdstr(c->bin, '\n', 1);
	if(s == nil)
		return Eeof;
	if(r != nil){
		r->code = readstatus(s);
		if(r->code == -1){
			free(s);
			return Eunexpected;
		}
		r->msg = strdup(s+4);
	}
	free(s);
	return Eok;
}

Dvec*
show(Dictc *c, char *cmd, int ok, int ko, int *err)
{
	Response r;
	Element *e;
	Dvec *v;
	char *s, *p;
	int rc, n, i;

	rc = sendcmd(c, cmd, &r);
	if(rc != Eok){
		*err = rc;
		return nil;
	}
	if(r.code == ko){
		*err = Enoshow;
		return nil;
	}else if(r.code != ok){
		*err = Eunexpected;
		return nil;
	}
	n = readstatus(r.msg);
	free(r.msg);
	v = mkdvec(n);
	for(i = 0; i < n; i++){
		s = Brdstr(c->bin, '\n', 1);
		if(s == nil){
			*err = Eeof;
			free(v); /* FIXME: free elts */
			return nil;
		}
		p = strchr(s, ' ');
		if(p == nil){
			*err = Ebadformat;
			free(v); /* FIXME: free elts */
			return nil;
		}
		e = emalloc(sizeof(Element));
		p += 2; /* skip <space>" */
		p[strlen(p) - 2] = 0; /* remove "\r */
		e->desc = strdup(p);
		p -= 2;
		*p = '\0';
		e->name = strdup(s);
		dvadd(v, e);
		free(s);
	}
	if((n = expectline(c, ".")) != Eok){
		*err = n;
		free(v); /* FIXME: free elts */
		return nil; 
	}
	if((n = expectline(c, "250 ok")) != Eok){
		*err = n;
		free(v); /* FIXME: free elts */
		return nil; 
	}
	*err = Eok;
	return v;
}

void
freedictc(Dictc *c)
{
	Element *e;
	int i;

	Bterm(c->bin);
	close(c->fd);
	if(c->db != nil){
		for(i = 0; i < dvlen(c->db); i++){
			e = dvref(c->db, i);
			free(e->name);
			free(e->desc);
			free(e);
		}
		free(c->db);
	}
	free(c);
}

Dictc*
dictdial(const char *addr, int port)
{
	Dictc *c;
	char *s, buf[255];
	int n;

	if(port == 0)
		port = 2628;
	snprint(buf, sizeof buf, "tcp!%s!%d", addr, port);
	c = malloc(sizeof *c);
	if(c == nil)
		sysfatal("malloc: %r");
	c->fd = dial(buf, nil, nil, nil);
	if(c->fd <= 0)
		sysfatal("dial: %r");
	c->bin = Bfdopen(c->fd, OREAD);
	if(c->bin == nil)
		sysfatal("Bfdopen: %r");
	s = Brdstr(c->bin, '\n', 1);
	if(s == nil){
		werrstr("no status sent by server");
		freedictc(c);
		return nil;
	}
	c->db = show(c, "SHOW DB", 110, 554, &n);
	if(n != Eok){
		werrstr(Errors[n]);
		freedictc(c);
		return nil;
	}
	c->strat = show(c, "SHOW STRAT", 111, 555, &n);
	if(n != Eok){
		werrstr(Errors[n]);
		freedictc(c);
		return nil;
	}
	return c;
}

void
dictquit(Dictc *c)
{
	sendcmd(c, "QUIT", nil);
	freedictc(c);
}

char*
dbdesc(Dictc *c, char *db)
{
	Element *e;
	int i;

	for(i = 0; i < dvlen(c->db); i++){
		e = dvref(c->db, i);
		if(strcmp(e->name, db) == 0)
			return e->desc;
	}
	return nil;
}

Definition*
parsedefinition(Dictc *c)
{
	Definition *d;
	char *s, *p, *q, *src;
	String *sb;
	int n;

	s = Brdstr(c->bin, '\n', 1);
	if(s == nil){
		werrstr(Errors[Eeof]);
		return nil;
	}
	n = readstatus(s);
	if(n != 151){
		werrstr(Errors[Eunexpected]);
		return nil;
	}
	src = nil;
	p = strchr(s+5, '"'); /* skip NNN<space>" */
	if(p != nil){
		p += 2;
		q = strchr(p, ' ');
		*q = '\0';
		src = dbdesc(c, p);
	}
	free(s);
	sb = s_newalloc(255);
	for(;;){
		s = Brdstr(c->bin, '\n', 1);
		if(s == nil){
			s_free(sb);
			werrstr(Errors[Eeof]);
			return nil;
		}
		if(*s == '.'){
			free(s);
			break;
		}
		s[Blinelen(c->bin) - 1] = '\n'; /* replace \r with \n */
		s_append(sb, s);
		free(s);
	}
	s_terminate(sb);
	d = emalloc(sizeof *d);
	d->db   = src;
	d->text = strdup(s_to_c(sb));
	s_free(sb);
	return d;
}

Dvec*
dictdefine(Dictc* c, char *db, char *word)
{
	Dvec *v;
	Response r;
	Definition *d;
	char buf[1024] = {0};
	int rc, n, i;

	snprint(buf, sizeof buf, "DEFINE %s \"%s\"", db, word);
	rc = sendcmd(c, buf, &r);
	if(rc != Eok){
		werrstr(Errors[rc]);
		return nil;
	}
	if(r.code == 552)
		return mkdvec(1);
	if(r.code != 150){
		werrstr(Errors[Eunexpected]);
		return nil;
	}
	n = readstatus(r.msg);
	v = mkdvec(n);
	for(i = 0; i < n; i++){
		d = parsedefinition(c);
		if(d == nil)
			return nil; /* FIXME: cleanup vec */
		dvadd(v, d);
	}
	if((n = expectline(c, "250 ok")) != Eok){
		werrstr(Errors[n]);
		return nil;
	}
	return v;
}