Sigrid Haflínudóttir9gc: chatrw: set Error flaga0aa2216 years agoHistory
447 lines · 8.0 KB · C
RawDownload
#define _DEFAULT_SOURCE#define _FILE_OFFSET_BITS 64#include <errno.h>#include <netdb.h>#include <netinet/in.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/select.h>#include <sys/socket.h>#include <sys/types.h>#include <termios.h>#include <unistd.h>#include "c9.h"#include "parg.h" #define MIN(a,b) ((a)<=(b)?(a):(b))#define USED(x) (void)(x) enum {	Msize = 8192, 	Rootfid = 0,	Indexfid,	Chatfid, 	Error = 1<<0,	Joined = 1<<1,	Disconnected = 1<<2,}; typedef struct REntry REntry; static C9ctx ctx;static uint8_t rdbuf[Msize], wrbuf[Msize];static uint32_t wroff;static const char *nick;static int f, flags, printjoin, startlen, inflight;static uint64_t chatoff, skipuntil;static char start[256]; static uint8_t *ctxread(C9ctx *c, uint32_t size, int *err){	uint32_t n;	int r; 	USED(c);	*err = 0;	for (n = 0; n < size; n += r) {		if ((r = read(f, rdbuf+n, size-n)) <= 0) {			if (errno == EINTR)				continue;			flags |= Disconnected;			close(f);			f = -1;			return NULL;		}	} 	return rdbuf;} static intwrsend(void){	uint32_t n;	int w; 	for (n = 0; n < wroff; n += w) {		if ((w = write(f, wrbuf+n, wroff-n)) <= 0) {			if (errno == EINTR)				continue;			if (errno != EPIPE){ /* remote end closed */				flags |= Disconnected;				close(f);				f = -1;			}			return -1;		}	}	wroff = 0; 	return 0;} static uint8_t *ctxbegin(C9ctx *c, uint32_t size){	uint8_t *b; 	USED(c);	if (wroff + size > sizeof(wrbuf)) {		if (wrsend() != 0 || wroff+size > sizeof(wrbuf))			return NULL;	}	b = &wrbuf[wroff];	wroff += size; 	return b;} static intctxend(C9ctx *c){	USED(c);	return 0;} static intdial(char *s){	struct addrinfo *r, *a, hint = {.ai_flags = AI_ADDRCONFIG, .ai_family = AF_UNSPEC, 0};	char host[64], *port;	int e, fd; 	if (strncmp(s, "udp!", 4) == 0) {		hint.ai_socktype = SOCK_DGRAM;		hint.ai_protocol = IPPROTO_UDP;	} else if (strncmp(s, "tcp!", 4) == 0) {		hint.ai_socktype = SOCK_STREAM;		hint.ai_protocol = IPPROTO_TCP;	} else {		fprintf(stderr, "invalid dial string: %s\n", s);		return -1;	}	if ((port = strchr(s+4, '!')) == NULL) {		fprintf(stderr, "invalid dial string: %s\n", s);		return -1;	}	if (snprintf(host, sizeof(host), "%.*s", (int)(port-s-4), s+4) >= (int)sizeof(host)) {		fprintf(stderr, "host name too large: %s\n", s);		return -1;	}	port++;	if ((e = getaddrinfo(host, port, &hint, &r)) != 0)		return -1;	fd = -1;	for (a = r; a != NULL; a = a->ai_next) {		if ((fd = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) < 0)			continue;		if (connect(fd, a->ai_addr, a->ai_addrlen) != 0) {			close(fd);			fd = -1;			continue;		}	}	freeaddrinfo(r); 	return fd;} static voidctxchatR(C9ctx *c, C9r *r){	C9tag tag;	const char *path[2];	char buf[64];	uint64_t sz, n;	int i; 	switch (r->type) {	case Rversion:		c9attach(c, &tag, Rootfid, C9nofid, "none", NULL);		path[0] = "chat";		path[1] = NULL;		c9walk(c, &tag, Rootfid, Chatfid, path);		c9open(c, &tag, Chatfid, C9rdwr);		break; 	case Rread:		inflight--;		if (chatoff == 0 && skipuntil > 0 && memcmp(start, r->read.data, MIN((int)r->read.size, startlen)) != 0) {			skipuntil = 0;			startlen = 0;		}		if (startlen < (int)sizeof(start)) {			n = MIN(r->read.size, sizeof(start)-startlen);			memmove(start+startlen, r->read.data, n);			startlen += n;		}		if (chatoff >= skipuntil)			write(1, r->read.data, r->read.size);		chatoff += r->read.size;		/* fallthrough */	case Ropen:		if ((flags & Joined) == 0 && printjoin) {			c9write(c, &tag, Chatfid, 0, buf, snprintf(buf, sizeof(buf), "JOIN %s to chat\n", nick));			flags |= Joined;		}		/* gotta read fast */		n = chatoff;		for (i = 0; i < 4-inflight; i++) {			sz = n < skipuntil ? skipuntil-n : Msize;			c9read(c, &tag, Chatfid, n, sz);			n += sz;		}		inflight = 4;		break; 	case Rerror:		fprintf(stderr, "chat error: %s\n", r->error);		flags |= Error;		break; 	default:		break;	}} static voidctxregistryR(C9ctx *c, C9r *r){	char *s, *b;	C9tag tag;	const char *path[2];	int i; 	switch (r->type) {	case Rversion:		c9attach(c, &tag, Rootfid, C9nofid, "none", NULL);		path[0] = "index";		path[1] = NULL;		c9walk(c, &tag, Rootfid, Indexfid, path);		c9open(c, &tag, Indexfid, C9read);		break; 	case Ropen:		c9read(c, &tag, Indexfid, 0, Msize);		break; 	case Rread:		r->read.data[r->read.size] = 0;		for (s = (char*)r->read.data;;) {			if ((s = strstr(s, "gridchat")) == NULL)				break;			for (b = s; b != (char*)r->read.data && *b != '\n'; b--);			if (*b == '\n')				b++;			if ((s = strchr(s, '\n')) == NULL)				s = (char*)&r->read.data[r->read.size];			*s = 0;			if (strstr(b, "tlssrv") == NULL && (s = strchr(b, ' ')) != NULL) {				*s = 0;				close(f);				for (i = 0; i < 10 && (f = dial(b)) < 0; i++)					sleep(5);				if (f < 0) {					fprintf(stderr, "failed to connect to %s, giving up\n", b);					exit(1);				}				flags = 0;				ctx.r = ctxchatR;				wroff = 0;				if (c9version(c, &tag, Msize) != 0 || wrsend() != 0)					exit(1);				return;			}		}		fprintf(stderr, "chat hasn't been found in the registry\n");		exit(1);		break; 	case Rerror:		fprintf(stderr, "registry error: %s\n", r->error);		flags |= Error;		break; 	default:		break;	}} __attribute__ ((format (printf, 1, 2)))static voidctxerror(const char *fmt, ...){	va_list ap; 	va_start(ap, fmt);	vfprintf(stderr, fmt, ap);	fprintf(stderr, "\n");	va_end(ap);} static intsrv(char *s){	if ((f = dial(s)) < 0)		return -1;	ctx.read = ctxread;	ctx.begin = ctxbegin;	ctx.end = ctxend;	ctx.error = ctxerror; 	return 0;} static intregistry(void){	C9tag tag;	int i; 	for (i = 0; i < 10; i++) {		if (srv("tcp!registry.9gridchan.org!6675") == 0) {			ctx.r = ctxregistryR;			if (c9version(&ctx, &tag, Msize) == 0 && wrsend() == 0) {				while (c9proc(&ctx) == 0 && ctx.r == ctxregistryR) {					if (wrsend() != 0)						return -1;				}			}			return 0;		}		sleep(5);	} 	return -1;} static intchatrw(void){	struct timeval t;	fd_set r, e;	int n, sz, res;	C9tag tag;	char *s; 	if (f < 0) {		flags |= Error;		return -1;	} 	FD_ZERO(&r);	FD_SET(f, &r);	FD_SET(0, &r);	FD_ZERO(&e);	FD_SET(f, &e);	FD_SET(0, &e);	memset(&t, 0, sizeof(t));	t.tv_sec = 10;	for (;;) {		errno = 0;		if ((n = select(f+1, &r, NULL, &e, &t)) < 0 || FD_ISSET(f, &e) || FD_ISSET(0, &e)) {			if (errno == EINTR)				continue;			flags |= Error;			return -1;		}		break;	} 	if (FD_ISSET(f, &r)) {		res = c9proc(&ctx);	} else if (FD_ISSET(0, &r)) {		s = (char*)rdbuf;		sz = sprintf(s, "%s → ", nick);		for (;;) {			if ((n = read(0, s+sz, sizeof(rdbuf)-sz)) > 0)				sz += n;			else				exit(0);			if (s[sz-1] != '\n'){				s[sz-1] = '\n';			}else{				res = c9write(&ctx, &tag, Chatfid, 0, s, sz);				break;			}		}	} else {		const char *path[] = {NULL};		res = c9walk(&ctx, &tag, Rootfid, Rootfid, path);	} 	return res;} intmain(int argc, char **argv){	struct parg_state ps;	struct termios t;	int c, noecho; 	parg_init(&ps);	noecho = 0;	while ((c = parg_getopt(&ps, argc, argv, "hjed")) >= 0) {		switch (c) {		case 1:			if (nick != NULL) {				fprintf(stderr, "only one nickname can be specified\n");				return 1;			}			nick = ps.optarg;			break;		case 'j':			printjoin = 1;			break;		case 'e':			noecho = 1;			break;		case 'h':			fprintf(stderr, "usage: 9gc [-j] [-e] NICKNAME\n");			return 0;			break;		case '?':			fprintf(stderr, "unknown option -%c\n", ps.optopt);			return 1;			break;		default:			fprintf(stderr, "unhandled option -%c\n", c);			return 1;			break;		}	} 	if (nick == NULL) {		fprintf(stderr, "no nickname specified\n");		return 1;	} 	if (noecho && tcgetattr(0, &t) == 0) {		t.c_lflag &= ~ECHO;		tcsetattr(STDIN_FILENO, TCSANOW, &t);	} 	for (;;) {		if (registry() != 0)			return 1;		while (chatrw() == 0 && wrsend() == 0);		if (flags & (Disconnected|Error)) {			flags &= ~(Disconnected|Error);			skipuntil = chatoff;			wroff = 0;			chatoff = 0;			inflight = 0;		} else {			printf("exiting\n");			exit(1);		}	} 	return 0;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t