9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [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

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

On Thursday, October 3, 2002, at 07:17 PM, Russ Cox wrote:
> this doesn't quite qualify as an irc client.

ircII under APE in dumb mode (without curses) works okay as a
standalone IRC client.  The dumb mode doesn't really cause a loss of
functionality, because the things that ircII normally does with curses
are basically identical to standard rio window insertion point
behavior.  (If I remember right, you can #define out the curses
functions because if you haven't set TERM in your environment it won't
even try to use them.  Otherwise you can use -d.)



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

* Re: [9fans] irc tools
  2002-10-04  2:17 [9fans] irc tools 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:24   ` andrey mirtchovski
  1 sibling, 2 replies; 9+ messages in thread
From: Jack Johnson @ 2002-10-04 16:40 UTC (permalink / raw)
  To: 9fans

Russ Cox wrote:
> i'm more interested in scripting things than
> actually chatting, hence the rough interactive
> interface.

I'm looking forward to trying this out.

I've used 9irc for kicks, and it made me consider what a proper Plan 9
IRC interface might look like, and the closest I can envision (without
more caffeine) is an inverted sam interface, with a command window at
the bottom and multiple client streams in discrete windows above.  It
might provide a clean way to separate the messages to the server from
the data received, segregate private messages from the channels, etc.

Any commands (other than /server) would be directed at the server
associated with the active window.

Some clever plumbing, and you might just be able to use sam....

-Jack



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

* Re: [9fans] irc tools
  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
  1 sibling, 1 reply; 9+ messages in thread
From: Scott Schwartz @ 2002-10-04 16:43 UTC (permalink / raw)
  To: 9fans

| ... and the closest I can envision (without
| more caffeine) is an inverted sam interface,

Not acme?



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

* Re: [9fans] irc tools
  2002-10-04 16:43   ` Scott Schwartz
@ 2002-10-04 17:14     ` Jack Johnson
  0 siblings, 0 replies; 9+ messages in thread
From: Jack Johnson @ 2002-10-04 17:14 UTC (permalink / raw)
  To: 9fans

Scott Schwartz wrote:
> | ... and the closest I can envision (without
> | more caffeine) is an inverted sam interface,
>
> Not acme?

No, not acme.

The problem with acme and IRC (as I see it) is that your input goes
wherever the cursor happens to lie, so if you want to change modes (from
one server to another, or from public to private), you have to not only
move the cursor, but provide a facility in each window to separate the
user input from the server output, or live with the dumb terminal mode
of mixing the two mid-typing (which in traditional Plan 9 parlance can
be confusing, if you're used to editing a command before submitting it).

If you segregate the commands to the server from the server output in
acme, then you have to have a command to switch the server/person/window
to which you would like to direct the current message.

Conversely, sam-style the streams from the various sources are free to
keep doing their thing while you compose your thoughts, and changing
active windows meshes nicely with the modal change in discussions (if
you're prone to having more than one discussion at once).

-Jack



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

* Re: [9fans] irc tools
  2002-10-04 16:40 ` Jack Johnson
  2002-10-04 16:43   ` Scott Schwartz
@ 2002-10-04 17:24   ` andrey mirtchovski
  1 sibling, 0 replies; 9+ messages in thread
From: andrey mirtchovski @ 2002-10-04 17:24 UTC (permalink / raw)
  To: 9fans

see the control library (libcontrol).. it has all you need for implementing
an irc client. acme would be even better, of course.

sometime in april i was thinking of creating a term-like control
with copy/paste functionality, but gave up on grounds of lazyness[1].

i'm trying to see if i can dig out from the university of saskatchewan
backups my 200 or so lines irc client (~ 09/2000), but hopes aren't high.

a year ago there was discussion on how to implement an irc slient the p9 way
(with a user-space file server) but nothing came out of it :)

andrey

[1] in the meantime i've stopped using irc -- efnet is not what it used to
be 7 years ago :)

On Fri, 4 Oct 2002, Jack Johnson wrote:

> I'm looking forward to trying this out.
>
> I've used 9irc for kicks, and it made me consider what a proper Plan 9
> IRC interface might look like, and the closest I can envision (without
> more caffeine) is an inverted sam interface, with a command window at
> the bottom and multiple client streams in discrete windows above.  It
> might provide a clean way to separate the messages to the server from
> the data received, segregate private messages from the channels, etc.
>
> Any commands (other than /server) would be directed at the server
> associated with the active window.
>
> Some clever plumbing, and you might just be able to use sam....



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

* Re: [9fans] irc tools
  2002-10-05  0:41 Russ Cox
@ 2002-10-05 16:34 ` Jack Johnson
  0 siblings, 0 replies; 9+ messages in thread
From: Jack Johnson @ 2002-10-05 16:34 UTC (permalink / raw)
  To: 9fans

On Fri, 2002-10-04 at 17:41, Russ Cox wrote:
> 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 think it would/could be in this case, but you've clarified something
for me.  See below.

> 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

Ah, now (if memory serves) this is exactly what does not happen to me
using either 9irc or a unix IRC client in dumb terminal mode, run as a
host command in Inferno.

You can imagine how annoying it would be to do:

	g% som-----------
	ething

and attempting to hold a conversation in a busy channel.  I mistakenly
extrapolated that behavior to something inherent in acme.  My sincere
apologies.

(for the record, ditching the prompt for an IRC client seems like a good
idea)

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

Traditionally, sam ends up looking something like this:

	+------------------+
	|                  |
	+------------------+
	+-------------+
	|             |
	|    +-------------+
	|    |             |
	+----|             |
	     +-------------+

where the upper window is the sam window and the lower windows are file
windows (though of course the placement is arbitrary).

It would be easy enough to use the same model, maybe invert it something
like:

	+-------------+
	|             |
	|    +-------------+
	|    |             |
	+----|             |
	     +-------------+
	+------------------+
	|                  |
	+------------------+

where the upper windows were all IRC server output (either raw/basic for
development purposes or separated out by channel/person/etc.) and the
lower window was for sending text and/or commands to the active window.
My reasoning for using this mode rather than acme being the ability to
easily link the action with the result.  Active window: active
conversation.  Arbitrary placement of any of the windows wouldn't change
the user's intended result, but I can easily envision a window
arrangement in acme that would be less than intuitive (assuming we're
still talking about multiple windows per server).

I could see myself mixing conversations in an acme-oriented IRC
environment, though if you're already in acme my suggestion would just
be a waste of screen real estate, and the more thought I give it the
more I think a single window client would probably work out the best.

-Jack



^ 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

* 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

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-04  2:17 [9fans] irc tools 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
2002-10-05  0:41 Russ Cox
2002-10-05 16:34 ` Jack Johnson
2002-10-05  2:09 rob pike, esq.

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).