1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 #include "plumb.h"
6 
7 typedef struct EQueue EQueue;
8 
9 struct EQueue
10 {
11 	int		id;
12 	char		*buf;
13 	int		nbuf;
14 	EQueue	*next;
15 };
16 
17 static	EQueue	*equeue;
18 static	Lock		eqlock;
19 
20 static
21 int
partial(int id,Event * e,uchar * b,int n)22 partial(int id, Event *e, uchar *b, int n)
23 {
24 	EQueue *eq, *p;
25 	int nmore;
26 
27 	lock(&eqlock);
28 	for(eq = equeue; eq != nil; eq = eq->next)
29 		if(eq->id == id)
30 			break;
31 	unlock(&eqlock);
32 	if(eq == nil)
33 		return 0;
34 	/* partial message exists for this id */
35 	eq->buf = realloc(eq->buf, eq->nbuf+n);
36 	if(eq->buf == nil)
37 		drawerror(display, "eplumb: cannot allocate buffer");
38 	memmove(eq->buf+eq->nbuf, b, n);
39 	eq->nbuf += n;
40 	e->v = plumbunpackpartial((char*)eq->buf, eq->nbuf, &nmore);
41 	if(nmore == 0){	/* no more to read in this message */
42 		lock(&eqlock);
43 		if(eq == equeue)
44 			equeue = eq->next;
45 		else{
46 			for(p = equeue; p!=nil && p->next!=eq; p = p->next)
47 				;
48 			if(p == nil)
49 				drawerror(display, "eplumb: bad event queue");
50 			p->next = eq->next;
51 		}
52 		unlock(&eqlock);
53 		free(eq->buf);
54 		free(eq);
55 	}
56 	return 1;
57 }
58 
59 static
60 void
addpartial(int id,char * b,int n)61 addpartial(int id, char *b, int n)
62 {
63 	EQueue *eq;
64 
65 	eq = malloc(sizeof(EQueue));
66 	if(eq == nil)
67 		return;
68 	eq->id = id;
69 	eq->nbuf = n;
70 	eq->buf = malloc(n);
71 	if(eq->buf == nil){
72 		free(eq);
73 		return;
74 	}
75 	memmove(eq->buf, b, n);
76 	lock(&eqlock);
77 	eq->next = equeue;
78 	equeue = eq;
79 	unlock(&eqlock);
80 }
81 
82 static
83 int
plumbevent(int id,Event * e,uchar * b,int n)84 plumbevent(int id, Event *e, uchar *b, int n)
85 {
86 	int nmore;
87 
88 	if(partial(id, e, b, n) == 0){
89 		/* no partial message already waiting for this id */
90 		e->v = plumbunpackpartial((char*)b, n, &nmore);
91 		if(nmore > 0)	/* incomplete message */
92 			addpartial(id, (char*)b, n);
93 	}
94 	if(e->v == nil)
95 		return 0;
96 	return id;
97 }
98 
99 int
eplumb(int key,char * port)100 eplumb(int key, char *port)
101 {
102 	int fd;
103 
104 	fd = plumbopen(port, OREAD|OCEXEC);
105 	if(fd < 0)
106 		return -1;
107 	return estartfn(key, fd, 8192, plumbevent);
108 }
109