9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* Re: [9fans] irc tools
@ 2002-10-05  0:41 Russ Cox
  2002-10-05 16:34 ` Jack Johnson
  0 siblings, 1 reply; 9+ messages in thread
From: Russ Cox @ 2002-10-05  0:41 UTC (permalink / raw)
  To: 9fans

I'm curious what you mean about the difference between
acme and sam as a model for a chat client.  The only difference
between acme and sam windows (for the purposes of this discussion)
is that sam windows are click-to-type and acme windows aren't.
Are you claiming that moving the mouse but not clicking is
somehow worse than moving the mouse and clicking?

I'm particularly confused by your claim that "dumb terminal mode"
would mix server output and user input mid-typing, which simply
isn't true at all in Plan 9.  (It's true in Unix.)  Run:

	{sleep 5; echo -----------} &

and then type something at the next prompt but don't hit enter.

	g% {sleep 5; echo -----------} &
	g% something

becomes

	g% {sleep 5; echo -----------} &
	g% -----------
	something

the lines stay unmixed.  Sadly, the prompt doesn't move,
but you could get around that by not having a prompt, or by
telling win/rio how big the prompt is.

If I were going to build a real interactive IRC (or IM, ...) client
for Plan 9, I'd have two acme windows for each open channel,
one displaying chatter and the other taking your input a la
(probably via) win.  Almost all the pieces are there now, in fact.
I'm not sure about one-on-one chats -- I can see benefits to having
them in a single window and benefits to having them split across
two windows.

What did you have in mind in as the "sam-style" interface?

Russ


^ permalink raw reply	[flat|nested] 9+ messages in thread
* Re: [9fans] irc tools
@ 2002-10-05  2:09 rob pike, esq.
  0 siblings, 0 replies; 9+ messages in thread
From: rob pike, esq. @ 2002-10-05  2:09 UTC (permalink / raw)
  To: 9fans

> I'm curious what you mean about the difference between
> acme and sam as a model for a chat client.  The only difference
> between acme and sam windows (for the purposes of this discussion)
> is that sam windows are click-to-type and acme windows aren't.

... unless you run
	acme -b

-rob



^ permalink raw reply	[flat|nested] 9+ messages in thread
* [9fans] irc tools
@ 2002-10-04  2:17 Russ Cox
  2002-10-04  4:48 ` Jonathan Sergent
  2002-10-04 16:40 ` Jack Johnson
  0 siblings, 2 replies; 9+ messages in thread
From: Russ Cox @ 2002-10-04  2:17 UTC (permalink / raw)
  To: 9fans

this doesn't quite qualify as an irc client.
the interface is terrible, but all the functionality
is there.  it implements dcc chat and file sending too.

i'm more interested in scripting things than
actually chatting, hence the rough interactive
interface.

there are a few helper programs and a main script.
the helpers are:

	netmux - consolefs, but for a network connection
	irccat - rewrite irc traffic from
			:from cmd to args...
		to
			cmd :from to args...
		quoting as necessary to make it safe shell input
	ircctcp - format a ctcp command into a PRIVMSG
		command.  used by the ctcp shell function.
	ircget - perform a dcc file receive

the main script logs into the server and then
runs a background proc that watches the irc
traffic for PING and PRIVMSG commands and
acts accordingly.  PING gets PONGed, and PRIVMSG
looks for dcc chat or file transfers and starts them.
(this isn't quite that safe, no.)  my expectation is
you'd want to change the behavior of PRIVMSG,
though the PING behavior is safe.

as an example of how everything works,
the background proc runs by doing

	irccat | grep '^(PRIVMSG|PING) ' | rc

in a shell with appropriate PRIVMSG and PING
functions.

irc under plan 9 seems to be a regular though not
particularly frequent question.  this is my
contribution to the cause.

enjoy.
russ


# To unbundle, run this file
echo irc
sed 's/.//' >irc <<'//GO.SYSIN DD irc'
-#!/bin/rc
-
-rfork en
-
-fn cmd {
-	echo $* >/dev/irc
-}
-fn nick {
-	switch($#*){
-	case 1
-		cmd NICK $1
-	case *
-		echo 'usage: nick name' >[1=2]
-	}
-}
-
-fn user {
-	cmd USER $*
-}
-
-fn msg {
-	switch($#*){
-	case 0 1
-		echo 'usage: msg name message...' >[1=2]
-	case *
-		addr = $1
-		shift
-		cmd PRIVMSG $addr $"*
-	}
-}
-
-fn ctcp {
-	switch($#*){
-	case 0 1
-		echo 'usage: msg name message...' >[1=2]
-	case *
-		addr = $1
-		shift
-		# can't use normal processing because we need
-		# to send an 001 byte, and rc cuts them out of variables.
-		# (they are word separators.)
-		x=`{echo $* | tr a-z A-Z}
-		ircctcp $addr $"x >/dev/irc
-	}
-}
-
-fn _join {
-	rfork e
-	fn sigexit {
-		cmd PART $channel
-	}
-	irccat /dev/irc | grep -i '^(PRIVMSG|NOTICE) '''$channel''' ' &
-	prompt=($channel^'% ' '	')
-	rc -i
-}
-fn join {
-	switch($#*){
-	case 1
-		cmd JOIN $1
-		channel=$1
-		window -m rc -c _join $1
-	case *
-		echo 'usage: join channel' >[1=2]
-	}
-}
-
-fn decimalip {
-	echo $1 | awk '{a=$1; printf("%d.%d.%d.%d\n", a/256/256/256, (a/256/256)%256, (a/256)%256, a%256);}'
-}
-
-fn PING {	# ping handler
-	echo Ping-pong.
-	cmd PONG $*
-}
-
-# PRIVMSG :from to '\x01DCC CHAT chat ip-decimal port\x01'
-# PRIVMSG :from to '\x01DCC SEND file-name ip-decimal port size\x01'
-
-fn _chat {
-	rfork e
-	con -Cl $1
-}
-
-fn PRIVMSG {	# privmsg handler
-	*=(`{echo $3})
-	if(~ $1 DCC && ~ $2 CHAT && ~ $3 chat){
-		ip=`{decimalip $4}
-		port=$5
-		window -m rc -c '_chat 'tcp!$ip!$port
-	}
-	if not if(~ $1 DCC && ~ $2 SEND){
-		file=$3
-		ip=`{decimalip $4}
-		port=$5
-		size=$6
-		out=/tmp/irc/in.$pid.^`{date -n}
-		whatis file ip port size >$out.info
-		ircget tcp!$ip!$port >$out &
-	}
-}
-
-fn sigexit {
-	cmd QUIT
-}
-
-email=(irc irc.irc)
-fullname='IRC User'
-
-switch($#*){
-case 2
-	;
-case 3
-	email=`{echo $3 | tr @ ' '}
-case 4
-	email=`{echo $3 | tr @ ' '}
-	fullname=$4
-case *
-	echo 'usage: irc server nick [email [fullname]]' >[1=2]
-	exit usage
-}
-
-server=$1
-nick=$2
-
-if(~ $#email 0 1)
-	email=(irc irc.irc)
-if(! ~ $#email 2)
-	email=($email(1) $email(2))
-
-netmux -m /dev -n irc $1
-
-# handlers
-irccat /dev/irc | grep '^(PING|PRIVMSG) ' | rc &
-irccat /dev/irc &
-prompt=('IRC% ' '	')
-nick $2
-user $email $server ':'^$"fullname
-rc -i
//GO.SYSIN DD irc
echo irccat.c
sed 's/.//' >irccat.c <<'//GO.SYSIN DD irccat.c'
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-char *file;
-
-int
-lsquote(int c)
-{
-	if(c <= ' ')
-		return 1;
-	if(strchr("`^#*[]=|\?${}()';", c))
-		return 1;
-	return 0;
-}
-
-void
-usage(void)
-{
-	fprint(2, "usage: irccat [/dev/irc]\n");
-	exits("usage");
-}
-
-int
-irctokenize(char *s, char **args, int maxargs)
-{
-	int nargs;
-	char *os;
-
-	os = s;
-	for(nargs=0; nargs<maxargs; nargs++){
-		while(*s==' ')
-			*s++ = '\0';
-		if(*s == '\0')
-			break;
-		args[nargs] = s;
-		if(s[0] == ':' && s != os){
-			args[nargs++]++;
-			break;
-		}
-		while(*s != ' ' && *s != '\0')
-			s++;
-	}
-
-	return nargs;
-}
-
-void
-main(int argc, char **argv)
-{
-	char *f[64];
-	char *s;
-	Biobuf *b;
-	Biobuf bout;
-	int nf;
-	int i, cmd;
-	char *src;
-
-	doquote = lsquote;
-	quotefmtinstall();
-	ARGBEGIN{
-	default:
-		usage();
-	}ARGEND
-
-	file = "/fd/0";
-	switch(argc){
-	default:
-		usage();
-	case 0:
-		break;
-	case 1:
-		file = argv[0];
-		break;
-	}
-
-	b = Bopen(file, OREAD);
-	if(b == nil)
-		sysfatal("open: %r");
-
-	Binit(&bout, 1, OWRITE);
-	for(; (s = Brdstr(b, '\n', 1)) != nil; free(s)){
-		if(strlen(s) == 0)
-			continue;
-		if(s[strlen(s)-1] == '\r')
-			s[strlen(s)-1] = '\0';
-		nf = irctokenize(s, f, nelem(f));
-		if(nf == 0)
-			continue;
-		cmd = 0;
-		src = "";
-		if(f[0][0] == ':'){
-			src = f[0];
-			cmd++;
-		}
-		if(nf <= cmd)
-			continue;
-		Bprint(&bout, "%q %q", f[cmd], src);
-		for(i=cmd+1; i<nf; i++)
-			Bprint(&bout, " %q", f[i]);
-		Bprint(&bout, "\n");
-		Bflush(&bout);
-	}
-	exits(nil);
-}
-
//GO.SYSIN DD irccat.c
echo ircctcp.c
sed 's/.//' >ircctcp.c <<'//GO.SYSIN DD ircctcp.c'
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-void
-usage(void)
-{
-	fprint(2, "usage: ircctcp dest msg>/dev/irc\n");
-	exits("usage");
-}
-
-char*
-q(char *m)
-{
-	Fmt fmt;
-	char *p, s[2];
-
-	fmtstrinit(&fmt);
-	for(p=m; *p; p++)
-		if(*p == 1)
-			fmtstrcpy(&fmt, "\\a");
-		else if(*p == '\\')
-			fmtstrcpy(&fmt, "\\\\");
-		else{
-			s[0] = *p;
-			s[1] = '\0';
-			fmtstrcpy(&fmt, s);
-		}
-	return fmtstrflush(&fmt);
-}
-
-void
-main(int argc, char **argv)
-{
-	char *dst, *msg, *s;
-
-	argv0 = argv[0];
-	if(argc != 3)
-		usage();
-
-	dst = argv[1];
-	msg = argv[2];
-
-	s = smprint("PRIVMSG %s :%c%s%c\n", dst, 1, q(msg), 1);
-	write(1, s, strlen(s));
-	exits(nil);
-}
-
//GO.SYSIN DD ircctcp.c
echo ircget.c
sed 's/.//' >ircget.c <<'//GO.SYSIN DD ircget.c'
-#include <u.h>
-#include <libc.h>
-
-int alarmed;
-
-void
-usage(void)
-{
-	fprint(2, "usage: ircget address\n");
-	exits("usage");
-}
-
-int
-bell(void*, char *msg)
-{
-	if(strcmp(msg, "alarm") == 0){
-		alarmed = 1;
-		return 1;
-	}
-	return 0;
-}
-
-void
-send(int fd, int tot)
-{
-	uchar buf[4];
-
-if(fd == 0) fd = 1;
-	buf[0] = tot>>24;
-	buf[1] = tot>>16;
-	buf[2] = tot>>8;
-	buf[3] = tot;
-	write(fd, buf, 4);
-}
-
-void
-main(int argc, char **argv)
-{
-	char *addr;
-	int fd, tot;
-	char buf[8192];
-	int n, first, cur;
-
-	argv0 = argv[0];
-	if(argc > 2)
-		usage();
-
-	if(argc == 2){
-		addr = argv[1];
-		fd = dial(addr, nil, nil, nil);
-		if(fd < 0)
-			sysfatal("dial: %r");
-	}else
-		fd = 0;
-
-	atnotify(bell, 1);
-
-	first = 0;
-	tot = 0;
-	cur = 0;
-	for(;;){
-		alarm(1000);
-		alarmed = 0;
-		if((n = read(fd, buf, sizeof buf)) <= 0){
-			alarm(0);
-			if(alarmed){
-				if(first == 0)
-					first = tot;
-				cur = tot;
-				send(fd, tot);
-				continue;
-			}
-			break;
-		}
-		tot += n;
-		if(first && cur+first < tot){
-			send(fd, tot);
-			cur = tot;
-		}
-		alarm(0);
-		if(write(1, buf, n) != n)
-			sysfatal("write: %r");
-	}
-	send(fd, tot);
-	exits(nil);
-}
//GO.SYSIN DD ircget.c
echo netmux.c
sed 's/.//' >netmux.c <<'//GO.SYSIN DD netmux.c'
-/*
- * Mux a given dialed network address among many clients.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <fcall.h>
-#include <thread.h>
-#include <9p.h>
-
-uint time0;
-
-enum
-{
-	Qroot = 0,
-	Qkid,
-
-	Bufsize = 32*1024,
-};
-
-typedef struct Buffer Buffer;
-struct Buffer
-{
-	Buffer *next;
-	char buf[Bufsize];
-	uint nbuf;
-	Req *wait;
-	Req *endwait;
-};
-
-int dialnet(void);
-int nfd = -1;
-char *addr;
-char *emailname;
-char *emailserver;
-char *server;
-char *nick;
-char *email;
-char *fullname;
-int didnet;
-int redial;
-Buffer *allbuf;
-QLock buflock;
-int debug;
-char *kidname = "mux";
-
-void
-servebuf(Buffer *b)
-{
-	int n;
-	Req *r;
-
-	while(b->wait && b->nbuf){
-		n = b->nbuf;
-		r = b->wait;
-		b->wait = r->aux;
-		r->aux = nil;
-		if(n > r->ifcall.count)
-			n = r->ifcall.count;
-		r->ofcall.data = b->buf;
-		r->ofcall.count = n;
-		respond(r, nil);
-		b->nbuf -= n;
-		memmove(b->buf, b->buf+n, b->nbuf);
-	}
-}
-
-void
-addbuf(char *a, uint n)
-{
-	Buffer *b;
-
-	if(n > Bufsize){
-		a += n - Bufsize;
-		n = Bufsize;
-	}
-
-	qlock(&buflock);
-	for(b=allbuf; b; b=b->next){
-		if(b->nbuf + n > Bufsize)
-			b->nbuf = 0;
-		memmove(b->buf+b->nbuf, a, n);
-		b->nbuf += n;
-		servebuf(b);
-	}
-	qunlock(&buflock);
-}
-
-void
-cancelbuf(void)
-{
-	Buffer *b;
-	Req *r, *next;
-
-	qlock(&buflock);
-	for(b=allbuf; b; b=b->next){
-		for(r=b->wait; r; r=next){
-			next = r->aux;
-			r->aux = nil;
-			respond(r, "hangup");
-		}
-		b->wait = nil;
-	}
-	qunlock(&buflock);
-}
-
-void
-readbuffer(Req *r, Fid *fid)
-{
-	Buffer *b;
-
-	qlock(&buflock);
-	if(time0 == 0){
-		respond(r, "hangup");
-		qunlock(&buflock);
-		return;
-	}
-
-	b = fid->aux;
-	r->aux = nil;
-	if(b->wait == nil)
-		b->wait = r;
-	else
-		b->endwait->aux = r;
-	b->endwait = r;
-	servebuf(b);
-	qunlock(&buflock);
-}
-
-void
-flushbuf(Buffer *b, Req *r)
-{
-	Req **l;
-
-	qlock(&buflock);
-	for(l=&b->wait; *l; l=&(*l)->aux){
-		if(*l == r){
-			*l = r->aux;
-			qunlock(&buflock);
-			respond(r, nil);
-			return;
-		}
-	}
-	qunlock(&buflock);
-}
-
-Buffer*
-mkbuffer(void)
-{
-	Buffer *b;
-
-	qlock(&buflock);
-	b = emalloc9p(sizeof *b);
-	memset(b, 0, sizeof *b);
-	b->next = allbuf;
-	allbuf = b;
-	qunlock(&buflock);
-	return b;
-}
-
-void
-closebuffer(Buffer *b)
-{
-	Buffer **l;
-
-	if(b == nil)
-		return;
-
-	qlock(&buflock);
-	for(l=&allbuf; *l; l=&(*l)->next)
-		if(*l == b)
-			break;
-	if(*l == nil)
-		sysfatal("buffer not found");
-	*l = b->next;
-	qunlock(&buflock);
-
-	if(b->wait != nil)
-		sysfatal("buffer has waiting requests");
-
-	free(b);
-}
-
-void
-netreader(void*)
-{
-	char buf[512];
-	int n;
-
-	for(;;){
-		while((n = read(nfd, buf, sizeof buf)) > 0){
-			if(debug)
-				write(2, buf, n);
-			addbuf(buf, n);
-		}
-		if(debug)
-			fprint(2, "hangup\n");
-		if(!redial)
-			sysfatal("hangup on network connection");
-		close(nfd);
-		nfd = -1;
-		time0 = 0;
-		cancelbuf();
-
-		for(;;){
-			if(dialnet() < 0)
-				sleep(1000);
-		}
-		time0 = time(0);
-	}
-}
-
-void
-fsattach(Req *r)
-{
-	char *spec;
-
-	spec = r->ifcall.aname;
-	if(spec && spec[0]){
-		respond(r, "invalid attach specifier");
-		return;
-	}
-	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
-	r->fid->qid = r->ofcall.qid;
-
-	if(!didnet){
-		didnet = 1;
-		proccreate(netreader, nil, 8192);
-	}
-	respond(r, nil);
-}
-
-char*
-fswalk1(Fid *fid, char *name, Qid *qid)
-{
-	switch((int)fid->qid.path){
-	default:
-		return "path not found";
-	case Qroot:
-		if(strcmp(name, "..") == 0)
-			break;
-		if(strcmp(name, kidname) == 0){
-			fid->qid = (Qid){Qkid, 0, 0};
-			break;
-		}
-		return "path not found";
-	case Qkid:
-		if(strcmp(name, "..") == 0){
-			fid->qid = (Qid){Qroot, 0, QTDIR};
-			break;
-		}
-		return "path not found";
-	}
-	*qid = fid->qid;
-	return nil;
-}
-
-void
-fsstat(Req *r)
-{
-	int q;
-	Dir *d;
-
-	d = &r->d;
-	memset(d, 0, sizeof *d);
-	q = r->fid->qid.path;
-	d->qid = r->fid->qid;
-	switch(q){
-	case Qroot:
-		d->name = estrdup9p("/");
-		d->mode = DMDIR|0777;
-		break;
-
-	case Qkid:
-		d->name = estrdup9p(kidname);
-		d->mode = 0666;
-		break;
-	}
-
-	d->atime = d->mtime = time0;
-	d->uid = estrdup9p("netmux");
-	d->gid = estrdup9p("netmux");
-	d->muid = estrdup9p("");
-	respond(r, nil);
-}
-
-int
-dirgen(int off, Dir *d, void*)
-{
-	if(off != 0)
-		return -1;
-
-	memset(d, 0, sizeof *d);
-	d->atime = d->mtime = time0;
-	d->name = estrdup9p(kidname);
-	d->mode = 0666;
-	d->qid = (Qid){Qkid, 0, 0};
-	d->qid.type = d->mode>>24;
-	d->uid = estrdup9p("stub");
-	d->gid = estrdup9p("stub");
-	d->muid = estrdup9p("");
-	return 0;
-}
-
-void
-fsread(Req *r)
-{
-	int q;
-
-	q = r->fid->qid.path;
-	switch(q){
-	default:
-		respond(r, "bug");
-		return;
-
-	case Qkid:
-		if(r->fid->qid.vers != time0 || time0 == 0){
-			respond(r, "hangup");
-			return;
-		}
-		readbuffer(r, r->fid);
-		return;
-
-	case Qroot:
-		dirread9p(r, dirgen, nil);
-		respond(r, nil);
-		return;
-	}
-}
-
-void
-fswrite(Req *r)
-{
-	int q;
-
-	q = r->fid->qid.path;
-	switch(q){
-	default:
-		respond(r, "no writing");
-		return;
-
-	case Qkid:
-		if(r->fid->qid.vers != time0 || time0 == 0){
-			respond(r, "hangup");
-			return;
-		}
-		if(debug)
-			write(2, r->ifcall.data, r->ifcall.count);
-		if(write(nfd, r->ifcall.data, r->ifcall.count) != r->ifcall.count){
-			respond(r, "hangup");
-			return;
-		}
-		r->ofcall.count = r->ifcall.count;
-		respond(r, nil);
-		return;
-	}
-}
-
-void
-fsopen(Req *r)
-{
-	if(r->fid->qid.path == Qroot){
-		if(r->ifcall.mode != OREAD)
-			respond(r, "permission denied");
-		else
-			respond(r, nil);
-		return;
-	}
-	if(r->fid->qid.path == Qkid){
-		r->fid->aux = mkbuffer();
-		r->fid->qid.vers = time0;
-		r->ofcall.qid.vers = time0;
-		respond(r, nil);
-		return;
-	}
-	respond(r, "permission denied");
-}
-
-void
-fsflush(Req *r)
-{
-	flushbuf(r->oldreq->fid->aux, r->oldreq);
-	respond(r, nil);
-}
-
-int
-dialnet(void)
-{
-	if(nfd >= 0){
-		close(nfd);
-		nfd = -1;
-		USED(nfd);
-	}
-	if((nfd = dial(addr, nil, nil, nil)) < 0)
-		return -1;
-
-	time0 = time(0);
-	return 0;
-}
-
-void
-fsclose(Fid *fid)
-{
-	closebuffer(fid->aux);
-}
-
-void
-fsend(Srv*)
-{
-	threadexitsall(nil);
-}
-
-Srv fs = {
-	.attach=	fsattach,
-	.open=	fsopen,
-	.read=	fsread,
-	.write=	fswrite,
-	.stat=	fsstat,
-	.walk1=	fswalk1,
-	.destroyfid=	fsclose,
-	.end=	fsend,
-	.flush=	fsflush,
-};
-
-void
-usage(void)
-{
-	fprint(2, "usage: netmux [-m mtpt] [-n name] addr\n");
-	exits("usage");
-}
-
-void
-threadmain(int argc, char **argv)
-{
-	char *mtpt;
-
-	quotefmtinstall();
-
-	mtpt = "/mnt";
-	time0 = time(0);
-	ARGBEGIN{
-	case 'D':
-		chatty9p++;
-		break;
-	case 'd':
-		debug = 1;
-		break;
-	case 'm':
-		mtpt = EARGF(usage());
-		break;
-	case 'n':
-		kidname = EARGF(usage());
-		break;
-	case 'r':
-		redial = 1;
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if(argc != 1)
-		usage();
-
-	addr = argv[0];
-
-	if(dialnet() < 0)
-		sysfatal("dial: %r");
-
-	threadpostmountsrv(&fs, nil, mtpt, MBEFORE);
-}
-
//GO.SYSIN DD netmux.c



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2002-10-05 16:34 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-10-05  0:41 [9fans] irc tools Russ Cox
2002-10-05 16:34 ` Jack Johnson
  -- strict thread matches above, loose matches on Subject: below --
2002-10-05  2:09 rob pike, esq.
2002-10-04  2:17 Russ Cox
2002-10-04  4:48 ` Jonathan Sergent
2002-10-04 16:40 ` Jack Johnson
2002-10-04 16:43   ` Scott Schwartz
2002-10-04 17:14     ` Jack Johnson
2002-10-04 17:24   ` andrey mirtchovski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).