Subject: 9wm/9term cmdpipes

From: [email-masked] (Steve_Kilbane)
Date: Fri, 28 Apr 1995 14:49:57 -0400

A while ago, I mentioned some hacks I'd done to 9term to add a
command pipe, like sam's. Rich $alz commented that 9wm could do
with something similar, and I couldn't resist. This is the
result, including patches for both 9term and 9wm. There are
probably bugs, but they work ok for me. :-) A README file
is included.

Steve

PS Rich: I've made some improvements since I sent you the
sample patches, so this lot is different.

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./9term.patch`
then
echo "writing ./9term.patch"
cat > ./9term.patch << '\Rogue\Monster\'
*** orig/9term/9term.c	Fri Jun  3 09:35:39 1994
--- hacked/9term/9term.c	Fri Apr 21 08:14:28 1995
***************
*** 16,36 ****
  
  #include "9term.h"
  
  int		flushing;
  int		suspended;
  Event		e;
  ulong		Erc;
  int		highwater = 50000;
  int		lowwater = 40000;
  int		waterquantum;
  int		beepmask;
  int		ninewm;
  
! static char	*items[] = { "cut", "paste", "snarf", "send", "scroll", 0 };
  static Menu	edit = {items};
  enum {	mCUT,
  	mPASTE,
  	mSNARF,
  	mSEND,
  	mSCROLL
  };
--- 16,48 ----
  
  #include "9term.h"
  
+ #define PIPEDIRENV		"NINETERMPIPE"
+ #define PIPEDIRDEF		"/tmp/.9terms."
+ 
  int		flushing;
  int		suspended;
  Event		e;
  ulong		Erc;
+ ulong		Epipe;
+ static char	*exname;
+ static int	exlen;
  int		highwater = 50000;
  int		lowwater = 40000;
  int		waterquantum;
  int		beepmask;
  int		ninewm;
+ static long	mark0, mark1;
+ static int	markset;
  
! void removeextern(void);
! static void pipeinput(Event *e);
! 
! static char	*items[] = { "cut", "paste", "snarf", "mark", "send", "scroll", 0 };
  static Menu	edit = {items};
  enum {	mCUT,
  	mPASTE,
  	mSNARF,
+ 	mMARK,
  	mSEND,
  	mSCROLL
  };
***************
*** 167,173 ****
--- 179,273 ----
  	run();
  }
  
+ 
  /*
+  * create a named pipe for this 9term to accept commands on.
+  * This function is mainly copied from samterm...
+  */
+ 
+ static
+ void init_ninepipe(void)
+ {
+ #ifndef	NOFIFO
+ 	char	*ninepipedir;
+ 	char	*disp;
+ 	char	*user;
+ 	int	fd;
+ 	int	flags;
+ 	extern ulong windowid;
+ 
+ 	if ((ninepipedir = getenv(PIPEDIRENV)) == 0) {
+ 		/* user hasn't specified a directory - make up a name */
+ 		/* exname is PIPEDIR.user.display/wid or PIPEDIR.user/wid */
+ 		user = getuser();
+ 		disp = getenv("DISPLAY");
+ 	
+ 		if (disp) {
+ 			exlen = strlen(PIPEDIRDEF) + strlen(user) + 1 + strlen(disp);
+ 			if ((exname = (char *)malloc(exlen + 20)) == 0)
+ 				return;
+ 			sprintf(exname, "%s%s.%s", PIPEDIRDEF, user, disp);
+ 		} else {
+ 			exlen = strlen(PIPEDIRDEF) + strlen(user);
+ 			if ((exname = (char *)malloc(exlen + 20)) == 0)
+ 				return;
+ 			sprintf(exname, "%s%s", PIPEDIRDEF, user);
+ 		}
+ 	} else {
+ 		/* use user's specified directory */
+ 		exlen = strlen(ninepipedir);
+ 		if ((exname = (char *)malloc(exlen + 20)) == 0)
+ 			return;
+ 		strcpy(exname,ninepipedir);
+ 	}
+ 
+ 	/* Create a directory for the pipes to exist in, if necessary. */
+ 	(void)mkdir(exname,0700);
+ 
+ 	/* add the next level - the pid */
+ 	sprintf(exname+exlen,"/%d",windowid);
+ 
+ 	/* Make the named pipe */
+ 	if (mkfifo(exname, 0600) == -1) {
+ 		removeextern();
+ 		return;
+ 	}
+ 
+ 	fd = open(exname, O_RDONLY | O_NONBLOCK);
+ 	if (fd == -1) {
+ 		removeextern();
+ 		return;
+ 	}
+ 
+ 	/*
+ 	 * Turn off no-delay and provide ourselves as a lingering
+ 	 * writer so as not to get end of file on read.
+          */ 
+ 	flags = fcntl(fd, F_GETFL, 0);
+ 	if (flags == -1 || fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1
+ 			|| open(exname, O_WRONLY) == -1) {
+ 		(void)close(fd);
+ 		removeextern();
+ 		return;
+ 	}
+ 
+ 	Epipe = estart(0, fd, 8192);
+ 	atexit(removeextern);
+ 	return;
+ #endif
+ }
+ 
+ void
+ removeextern(void)
+ {
+ 	if (exname) {
+ 		(void)unlink(exname);
+ 		exname[exlen] = 0;
+ 		(void)rmdir(exname);
+ 	}
+ }
+ 
+ /*
   *	Register command output stream and handle events
   */
  #define MAXMSG		128
***************
*** 179,184 ****
--- 279,285 ----
  	ulong type;
  
  	Erc = estart(0, comm_fd, MAXMSG + MAXFDATA);
+ 	init_ninepipe();
  	for (;;) {
  		/* disable shell output when more than one buffer ahead */
  /*
***************
*** 204,210 ****
  			}
  #endif
  			shelloutput(Erc, &e);
! 		}
  	}
  }
  
--- 305,312 ----
  			}
  #endif
  			shelloutput(Erc, &e);
! 		} else if (type == Epipe)
! 			pipeinput(&e);
  	}
  }
  
***************
*** 234,239 ****
--- 336,371 ----
  }
  
  /*
+  *	Handle command pipe event
+  */
+ 
+ static void
+ pipeinput(Event *e)
+ {
+ 	Rune r;
+ 	char *c;
+ 	Text *t = text;
+ 
+ 	/* XXX - should do something like:
+ 		if (e->n)
+ 			t->p0 = t->p1 = t->end;
+ 	or whatever, to make sure we're inserting at the end. */
+ 
+ 	if (e->n)
+ 		t->p0 = t->p1 = t->length;
+ 	for (c = (char *)e->data; e->n--; c++) {
+ 		chartorune(&r, c);
+ 		/* ignore eof or eol */
+ 		if (r == eofchar || r == eolchar)
+ 			continue;
+ 		textinsert(t, &r, (&r)+1, t->p0);
+ 		textshow(t, t->p0, 1);
+ 		sendrunes(&r,1);
+ 	}
+ 	texthighlight(text,t->p0,t->p1, F & ~D);
+ }
+ 
+ /*
   *	Handle command output event
   */
  static void
***************
*** 398,403 ****
--- 530,536 ----
  	static Rune nl[] = { '\n', 0 };
  
  	specialchars(slave_fd);
+ 	edit.item[mMARK] = markset?"select":"mark";
  	edit.item[mSCROLL] = text->scrolling?"noscroll":"scroll";
  	switch (menuhit(2, m, &edit))
  	{
***************
*** 428,433 ****
--- 561,582 ----
  			inputrune(text, text->snarfed, text->snarflen);
  			if (text->snarfed[text->snarflen-1] != '\n')
  				inputrune(text, nl, 1);
+ 		}
+ 		break;
+ 	case mMARK:			/* save point, or select to point */
+ 		if (markset) {
+ 			ulong m0, m1;
+ 
+ 			m0 = (mark0 < text->p0)? mark0 : text->p0;
+ 			m1 = (mark1 > text->p1)? mark1 : text->p1;
+ 			markset = 0;
+ 			/* if (m0 < text->base || text->end < m0)
+ 				textset(text, _backnl(text, m0, 3)); */
+ 			texthighlight(text,m0,m1, F & ~D);
+ 		} else {
+ 			mark0 = text->p0;
+ 			mark1 = text->p1;
+ 			markset = 1;
  		}
  		break;
  	case mSCROLL:			/* toggle scroll state */
*** orig/9term/display.c	Thu Dec 15 14:48:28 1994
--- hacked/9term/display.c	Wed Mar 29 12:42:36 1995
***************
*** 52,57 ****
--- 52,58 ----
  static void	delwin(Widget, XEvent*, String*, Cardinal*);
  
  Text		*text;		/* the main and only text buffer */
+ ulong		windowid;
  
  	/* too many X options */
  static XrmOptionDescRec optable[] = {
***************
*** 229,235 ****
  	XSetErrorHandler(abort);
  #endif
  		/* export window id to environment */
! 	sprintf(id, "%d", XtWindow(_toplevel));
  	setenv("WINDOWID", id, 1);
  
  		/* register mouse and keyboard events */
--- 230,237 ----
  	XSetErrorHandler(abort);
  #endif
  		/* export window id to environment */
! 	windowid = XtWindow(_toplevel);
! 	sprintf(id, "%d", windowid);
  	setenv("WINDOWID", id, 1);
  
  		/* register mouse and keyboard events */
\Rogue\Monster\
else
  echo "will not over write ./9term.patch"
fi
if `test ! -s ./9wm.patch`
then
echo "writing ./9wm.patch"
cat > ./9wm.patch << '\Rogue\Monster\'
*** orig/9wm/9wm.c	Tue Mar 28 16:37:51 1995
--- hacked/9wm/9wm.c	Tue Apr 18 14:45:52 1995
***************
*** 14,20 ****
  
  char    *version[] = 
  {
!     "9wm version 1.1, Copyright (c) 1994 David Hogan", 0,
  };
  
  Display         *dpy;
--- 14,22 ----
  
  char    *version[] = 
  {
!     "9wm version 1.1, Copyright (c) 1994 David Hogan",
!     "cmdpipe extensions by Steve Kilbane",
!     0,
  };
  
  Display         *dpy;
***************
*** 51,57 ****
--- 53,61 ----
  Atom        _9wm_hold_mode;
  
  void    usage(), sighandler(), getevent();
+ static	int do_select();
  
+ 
  char    *fontlist[] = {
      "lucm.latin1.9",
      "blit",
***************
*** 221,226 ****
--- 225,232 ----
      nofocus();
      scanwins();
  
+     init_ninepipe();		/* smk */
+ 
      for (;;) {
          getevent(&ev);
  
***************
*** 443,448 ****
--- 449,455 ----
      e->value_mask |= CWBorderWidth;
  
      XConfigureWindow(dpy, e->window, e->value_mask, &wc);
+     wins_changed |= wm_configurereq;
  }
  
  void
***************
*** 479,484 ****
--- 486,492 ----
          unhidec(c, 1);
          break;
      }
+     wins_changed |= wm_mapreq;
  }
  
  void
***************
*** 506,511 ****
--- 514,520 ----
          }
          c->reparenting = 0;
      }
+     wins_changed |= wm_unmap;
  }
  
  void
***************
*** 532,537 ****
--- 541,547 ----
          c->dy = e->height;
          c->border = e->border_width;
      }
+     wins_changed |= wm_newwindow;
  }
  
  void
***************
*** 551,556 ****
--- 561,567 ----
      ignore_badwindow = 1;
      XSync(dpy, False);
      ignore_badwindow = 0;
+     wins_changed |= wm_destroy;
  }
  
  void
***************
*** 657,662 ****
--- 668,674 ----
          if (c == current)
              cmapfocus(c);
      }
+     wins_changed |= wm_property;
  }
  
  void
***************
*** 684,689 ****
--- 696,702 ----
          if (c != 0 && (c->parent == root || withdrawn(c))) 
              rmclient(c);
      }
+     wins_changed |= wm_reparent;
  }
  
  #ifdef  SHAPE
***************
*** 698,703 ****
--- 711,717 ----
          return;
  
      setshape(c);
+     wins_changed |= wm_shape;
  }
  #endif
  
***************
*** 728,755 ****
  XEvent *e;
  {
      int fd;
-     fd_set rfds;
-     struct timeval t;
  
      if (!signalled) {
!         if (QLength(dpy) > 0) {
!             XNextEvent(dpy, e);
              return;
          }
          fd = ConnectionNumber(dpy);
!         FD_ZERO(&rfds);
!         FD_SET(fd, &rfds);
!         t.tv_sec = t.tv_usec = 0;
!         if (select(fd+1, &rfds, NULL, NULL, &t) == 1) {
!             XNextEvent(dpy, e);
              return;
-         }
          XFlush(dpy);
!         FD_SET(fd, &rfds);
!         if (select(fd+1, &rfds, NULL, NULL, NULL) == 1) {
!             XNextEvent(dpy, e);
              return;
-         }
          if (errno != EINTR || !signalled) {
              perror("9wm: select failed");
              exit(1);
--- 742,761 ----
  XEvent *e;
  {
      int fd;
  
      if (!signalled) {
!         if (QLength(dpy) > 0) {		/* if there are X events, handle them */
!             XNextEvent(dpy, e);		/* may mean pipe never gets a look in */
              return;
          }
+         if (wins_changed)
+ 		update_windows_list();
          fd = ConnectionNumber(dpy);
!         if (do_select(fd,e,0))
              return;
          XFlush(dpy);
!         if (do_select(fd,e,1))
              return;
          if (errno != EINTR || !signalled) {
              perror("9wm: select failed");
              exit(1);
***************
*** 758,761 ****
--- 764,801 ----
      cleanup();
      fprintf(stderr, "9wm: exiting on signal\n");
      exit(1);
+ }
+ 
+ static int
+ do_select(fd, e, i)
+ int fd;
+ XEvent *e;
+ int i;
+ {
+     int nfds, r;
+     fd_set rfds;
+     struct timeval t;
+ 
+     for (;;) {			/* stay here until we get an X event */
+         FD_ZERO(&rfds);
+         FD_SET(fd, &rfds);
+         if (ninepipefd != -1) {
+             FD_SET(ninepipefd, &rfds);
+             nfds = ninepipefd > fd? ninepipefd : fd;
+         } else {
+             nfds = fd;
+         }
+         t.tv_sec = t.tv_usec = 0;
+         if ((r = select(nfds+1, &rfds, NULL, NULL, i? NULL : &t)) < 1) {
+             return 0;
+         }
+         if (ninepipefd != -1 && FD_ISSET(ninepipefd,&rfds)) {
+             pipecmd();
+             continue;
+         }
+         if (FD_ISSET(fd,&rfds)) {
+             XNextEvent(dpy, e);
+             return 1;
+         }
+     }
  }
*** orig/9wm/client.c	Tue Mar 28 16:37:53 1995
--- hacked/9wm/client.c	Tue Apr 18 14:45:52 1995
***************
*** 68,73 ****
--- 68,74 ----
  }
  #endif
  
+ 
  void
  active(c)
  Client *c;
***************
*** 94,99 ****
--- 95,101 ----
      if (debug)
          dump_revert();
  #endif
+     wins_changed |= wm_active;
  }
  
  void
***************
*** 123,128 ****
--- 125,131 ----
      }
      XSetInputFocus(dpy, w, RevertToPointerRoot, timestamp());
      cmapfocus(0);
+     wins_changed |= wm_nofocus;
  }
  
  Client *
*** orig/9wm/dat.h	Tue Mar 28 16:37:55 1995
--- hacked/9wm/dat.h	Tue Apr 18 15:00:54 1995
***************
*** 115,117 ****
--- 115,137 ----
  
  /* error.c */
  extern int          ignore_badwindow;
+ 
+ /* pipe.c - smk */
+ extern int          ninepipefd;
+ extern int          wins_changed;
+ 
+ #define wm_configurereq 0x1
+ #define wm_mapreq 0x2
+ #define wm_unmap 0x4
+ #define wm_newwindow 0x8
+ #define wm_destroy 0x10
+ #define wm_property 0x20
+ #define wm_reparent 0x40
+ #define wm_shape 0x80
+ #define wm_active 0x100
+ #define wm_nofocus 0x200
+ #define wm_hide 0x400
+ #define wm_unhide 0x800
+ #define wm_renamec 0x1000
+ #define wm_creshape 0x2000
+ #define wm_cmove 0x4000
*** orig/9wm/fns.h	Tue Mar 28 16:37:55 1995
--- hacked/9wm/fns.h	Tue Apr 18 14:15:42 1995
***************
*** 73,75 ****
--- 73,81 ----
  
  /* cursor.c */
  void    initcurs();
+ 
+ /* pipe.c - smk */
+ void	pipecmd();
+ void	init_ninepipe();
+ void	remove9pipe();
+ void    update_windows_list();
*** orig/9wm/manage.c	Tue Mar 28 16:37:52 1995
--- hacked/9wm/manage.c	Tue Apr 18 13:59:40 1995
***************
*** 88,94 ****
  
      /* Now do it!!! */
  
!     if (doreshape) {
          cmapfocus(0);
          if (!(fixsize ? drag(c) : sweep(c)) && c->is9term) {
              XDestroyWindow(dpy, c->window);
--- 88,94 ----
  
      /* Now do it!!! */
  
!     if (doreshape && (! dohide || ! fixsize)) {
          cmapfocus(0);
          if (!(fixsize ? drag(c) : sweep(c)) && c->is9term) {
              XDestroyWindow(dpy, c->window);
*** orig/9wm/menu.c	Tue Mar 28 16:37:52 1995
--- hacked/9wm/menu.c	Tue Apr 18 15:10:18 1995
***************
*** 108,114 ****
                  fprintf(stderr, "9wm: exec %s", shell);
                  perror(" failed");
              }
!             execlp("9term", "9term", "-9wm", 0);
              execlp("xterm", "xterm", "-ut", 0);
              perror("9wm: exec 9term/xterm failed");
              exit(1);
--- 108,114 ----
                  fprintf(stderr, "9wm: exec %s", shell);
                  perror(" failed");
              }
!             execlp("9term", "9term", "-unix", "-9wm", 0);
              execlp("xterm", "xterm", "-ut", 0);
              perror("9wm: exec 9term/xterm failed");
              exit(1);
***************
*** 186,191 ****
--- 186,192 ----
      b3items[B3FIXED+numhidden] = c->label;
      numhidden++;
      b3items[B3FIXED+numhidden] = 0;
+     wins_changed |= wm_hide;
  }
  
  void
***************
*** 220,225 ****
--- 221,227 ----
          b3items[B3FIXED+i] = b3items[B3FIXED+i+1];
      }
      b3items[B3FIXED+numhidden] = 0;
+     wins_changed |= wm_unhide;
  }
  
  void
***************
*** 248,253 ****
--- 250,256 ----
      if (name == 0)
          name = "???";
      c->label = name;
+     wins_changed |= wm_renamec;
      if (!hidden(c))
          return;
      for (i = 0; i < numhidden; i++)
\Rogue\Monster\
else
  echo "will not over write ./9wm.patch"
fi
if `test ! -s ./README`
then
echo "writing ./README"
cat > ./README << '\Rogue\Monster\'
This sharfile contains four files:
	README			(this file)
	9term.patch		(cmdpipe support for 9term)
	9wm.patch		(first part of cmdpipe support for 9wm)
	pipe.c			(second part)

The 9term patch is against 1.6.3, while the 9wm patch is against 1.1.

I've used these changes on Solaris 2.3 for quite a while now, and
they seem stable as far as daily use goes. Mind you, they don't
*much* use daily... incidentally, there's a bug in 9term 1.6.3
under solaris 2.3 - occasionally the cursor starts up in the
wrong place (between two shell prompts), and input results in
complaints about null characters. This is in the standard 9term
release, and I haven't been able to track it down.

9term changes
=============
Each 9term creates a pipe which can be used for input, in the
manner of sam's pipe. The name of the pipe is given by
$NINETERMPIPE, if set. If not, the pipes go into the directory
/tmp/.9terms.$USER.$DISPLAY, if $DISPLAY is set, or
/tmp/.9terms.$USER, if not. Within the directory, the pipe's
name is $WINDOWID. The directory is created if required, and
when 9term exits, the pipe is removed, and so is the directory,
if empty.

Text written to the pipe is inserted to 9term's window as though
it had been typed by the user - it is sent to the shell, too.
The cursor is moved to the end of the window first, if necessary.

This patch also includes the mark/select toggle on the second
menu: mark saves the current selection, and select selects all
text between the saved and current selections, inclusive.

Caveat:
	I don't bother to set $NINETERMPIPE, using the default
	pipe name. Therefore, I haven't actually got around to
	testing this bit of code. :-)
Bug:
	Text set to the pipe is always sent straight to the shell,
	too. This means that if you've, say, typed "echo hello "
	so far, and you squirt "world\n" into the pipe, you'll get
	"world: no such file or directory", and then when you press
	return, you get "hello world" appear. This doesn't bother me
	too much, since I normally only send text to the pipe while
	I'm between commands anyway.

9wm changes
===========
9wm creates a file which contains the current state of the windows
9wm knows about. The name of the file is $NINEWINDOWS, or
/tmp/.9wm-windows by default. Each line of the file contains
wid s x y dx dy l

where:
	wid	is $WINDOWID
	s	is the state of the window (n = normal, h = hidden)
	x y	are the coords of the window's position
	dx dy	are the window's dimensions
	l	is the window's label

The windows' details are added in the same order as the current
window stack. Thus, the current window will always be the first
line in the file, and so on. If a window's never been current,
it'll be one of the last lines in the file.

This file is updated whenever the details in it have changed. I've
tried to catch all the cases, and have probably been a little eager - 
it can get updated an awful lot when some X events are going on,
even though there's no visible change on the screen. However, 9wm
is still a lot faster than olwm...

9wm also creates a pipe, name from $NINEPIPE, or /tmp/.9wm-pipe
by default. This pipe is used for sending commands to 9wm itself.
Commands are:
	none
		Just used for testing.
	reshape wid x y dx dy
		change position and size of window wid
	resize wid dx dy
		change size of window wid
	move wid x d
		change position of window wid
	front wid
		move window wid to the front, and make current
	hide wid
		hides window wid
	delete wid
		deletes window wid
	label wid str
		sets label of window wid to str

This all seems to work on my system. I haven't tested the
label command *too* much, and I suspect that there may be
problems in the interaction between X's memory allocations and
mine. I haven't seen any evidence of this, it's just that I've
probably got it wrong somewhere. :-)

Caveats:
	Again, I use the default names, so I haven't tested the
	$NINEWINDOWS/$NINEPIPE code.
	The update code for the windows file is a little eager.
	Label memory handling could be dodgy.

What's the point?
=================
I've used these changes, along with 9menu, for adding a
few interesting things. Menu options for doing "front" and
"back" commands are trivial, and since I use the es shell,
other 9menus can repeat the last command or list my recent
command history for occasional selection.

The most extreme application is 9vwm, a virtual window
manager for 9wm written in es. It mostly works, but since
I don't have much of a tendency to have many windows anyway,
there's no inclination to finish it off. But it was fun.

Future directions
=================
Sooner or later David Hogan's going to release 9wm 1.2, which
has got some fairly extensive changes in it, so I'll have to
upgrade to that. :-(

I'm considering adding kill/unkill commands to 9wm, so that
9wm can be told of a list of daemons that are interested in
being told when the windows file has changed (via a kill()).
This might make it easier to write programs that follow manual
changes to the window structure.

It might be nice to have the windows file contain the arguments
to the window, too. That way saving workspaces would be a doddle.

It's probably a good idea for 9wm to have both "exit" and "exit!",
or something similar - the latter is the current behaviour, while
the former just complains to stderr if there are any windows currently
hidden. It would probably have to ignore 9menu, though....

Steve Kilbane
<[email-masked]>
\Rogue\Monster\
else
  echo "will not over write ./README"
fi
if `test ! -s ./pipe.c`
then
echo "writing ./pipe.c"
cat > ./pipe.c << '\Rogue\Monster\'
/* Copyright (c) 1995 Steve Kilbane. See README for details */

#include <stdio.h>
#include <ctype.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string.h>
#include <fcntl.h>
#include "dat.h"
#include "fns.h"
#include <malloc.h>
#include <stdlib.h>

#define		NINEWINDOWSDEF	"/tmp/.9wm-windows"
#define		NINEWINDOWSENV	"NINEWINDOWS"

#define PCMDMAX	80
#define PIPENAMEENV	"NINEPIPE"
#define PIPENAMEDEF	"/tmp/.9wm-pipe"

typedef enum {
	pNone, pReshape, pResize, pMove, pHide, pFront, pDelete, pLabel, pError
} PipeCmd;

#define XY	1
#define DXY	2
#define STR	4

static struct cmdinfo {
	PipeCmd cmd;
	char *name;
	char *fmt;
	int nconvs;
	unsigned short flags;
} names[] = {
	{ pNone, "none", "", 0, 0 },
	{ pReshape, "reshape", "%lu%d%d%d%d", 5, (XY|DXY) },
	{ pResize, "resize", "%lu%d%d", 3, DXY },
	{ pMove, "move", "%lu%d%d", 3, XY },
	{ pFront, "front", "%lu", 1, 0 },
	{ pHide, "hide", "%lu", 1, 0 },
	{ pDelete, "delete", "%lu", 1, 0 },
	{ pLabel, "label", "%lu%*[ \t]%n", 1, STR },
	{ pNone, 0, 0, 0, 0 }
};

static struct LabelStr {
	char *s;
	struct LabelStr *n;
	int f;
} *labelstrs = NULL;

static Client *findc();
static Client *setsize();
static PipeCmd parsepipecmd();
static PipeCmd parse();
static Client *freelabelstr();
static char *getlabelstr();
static void creshape();
static void cmove();

static char *pipename;
static pid_t serverpid;
int ninepipefd = -1;
int wins_changed = 0;

static struct {
	int i;
	char *s;
} reasons[] = {
	{ wm_configurereq, "configurereq" },
	{ wm_mapreq, "mapreq" },
	{ wm_unmap, "unmap" },
	{ wm_newwindow, "newwindow" },
	{ wm_destroy, "destroy" },
	{ wm_property, "property" },
	{ wm_reparent, "reparent" },
	{ wm_shape, "shape" },
	{ wm_active, "active" },
	{ wm_nofocus, "nofocus" },
	{ wm_hide, "hide" },
	{ wm_unhide, "unhide" },
	{ wm_renamec, "renamec" },
	{ wm_cmove, "cmove" },
	{ wm_creshape, "creshape" },
	{ 0, 0 }
};

static void
print_reason()
{
	int x;

	fprintf(stderr,"9wm: reason");
	for (x = 0; reasons[x].s ; x++)
		if (wins_changed & reasons[x].i)
			fprintf(stderr," %s",reasons[x].s);
	fprintf(stderr,"\n");
}

static int
write_window_line(c,fd)
Client *c;
int fd;
{
	static char *buffer;
	static int maxlen;
	int len;
	char state;

	if (c->label == 0)
		return 0;
	len = 45 + strlen(c->label);
	if (len >= maxlen) {
		maxlen = len + 1;
		buffer = buffer? realloc(buffer,maxlen) : malloc(maxlen);
		if (buffer == 0) {
			maxlen = 0;
			return 1;
		}
	}
	state = hidden(c)? 'h' : 'n';		/* ignore withdrawn */
	sprintf(buffer,"%lu %c %d %d %d %d %s\n",c->window,state,c->x,c->y,c->dx,c->dy,c->label);
	(void)write(fd,buffer,strlen(buffer));
	return 0;
}

void
update_windows_list(void)
{
	static char *ninewindows, *ninewindowstmp;
	Client *c, *cc;
	int fd;

/* print_reason(); */
	wins_changed = 0;
	if (ninewindowstmp == 0) {
		if ((ninewindows = getenv(NINEWINDOWSENV)) == 0)
			ninewindows = NINEWINDOWSDEF;
		if ((ninewindowstmp = malloc(strlen(ninewindows)+5)) == 0) {
			fprintf(stderr,"9wm: cannot alloc tmpfile name for %s\n",ninewindows);
			return;
		}
		sprintf(ninewindowstmp,"%s.tmp",ninewindows);
	}
	if ((fd = creat(ninewindowstmp, 0744)) < 0) {
		fprintf(stderr,"9wm: cannot create '%s'\n",ninewindowstmp);
		return;
	}
	/* write clients that have been selected first, in
	   active order */
	for (c = current; c; c = c->revert)
		if (write_window_line(c,fd)) {
			close(fd);
			(void)unlink(ninewindowstmp);
			return;
		}
	/* now do those that have never been selected, in any order */
	for (c = clients; c; c = c->next) {
		for (cc = current; cc; cc = cc->revert)
			if (cc == c)
				break;
		if (cc == 0)
			if (write_window_line(c,fd)) {
				close(fd);
				(void)unlink(ninewindowstmp);
				return;
			}
	}
	close(fd);
	(void)rename(ninewindowstmp,ninewindows);
	return;
}

void
pipecmd()
{
	static char data[PCMDMAX];
	int x, y, dx, dy;
	int len;
	Window winid;
	char *text;
	Client *c;
	char *ptr;

	if ((len = read(ninepipefd,data,PCMDMAX)) < 1)
		return;
	ptr = data;
	for (;;) {
		switch (parsepipecmd(&ptr,&len,&winid,&x,&y,&dx,&dy,&text)) {
			case pNone:
				return;
			case pReshape:
				creshape(setsize(findc(winid),x,y,dx,dy,1,1));
				break;
			case pResize:
				creshape(setsize(findc(winid),0,0,x,y,1,0));
				break;
			case pMove:
				cmove(setsize(findc(winid),x,y,0,0,0,1));
				break;
			case pHide:
				hide(findc(winid));
				break;
			case pFront:
				c = findc(winid);
				if (c == 0)
					break;
				if (hidden(c))
					unhidec(c,1);
				else {
					XMapRaised(dpy, c->parent);
					active(c);
				}
				break;
			case pDelete:
				delete(findc(winid),1);
				break;
			case pLabel:
				c = findc(winid);
				if (c == 0)
					break;
				renamec(freelabelstr(c),getlabelstr(text));
				break;
			default:
				fprintf(stderr,"9wm: unparsed command: %s\n",text);
				return;
		}
	XFlush(dpy);
	}
}

static Client *
findc(w)
Window w;
{
	Client *c;

	for (c = clients; c; c = c->next)
		if (c->window == w)
			return c;
	fprintf(stderr,"9wm: invalid window id %lu\n",w);
	return 0;
}

static Client *
setsize(c,x,y,dx,dy,resizing,moving)
Client *c;
int x, y, dx, dy, resizing, moving;
{
	if (c == 0)
		return 0;
	if (resizing) {
		if (dx < 0)
			dx = -dx;
		if (dy < 0)
			dy = -dy;
		/* following nicked from grab.c:sweepcalc() */
		dx -= 2*BORDER;
		dy -= 2*BORDER;
		if (!c->is9term) {
			if (dx < c->min_dx)
				dx = c->min_dx;
			if (dy < c->min_dy)
				dy = c->min_dy;
		}
		if (dx < 4)
			dx = 4;
		if (dy < 4)
			dy = 4;
	
		if (c->size.flags & PResizeInc) {
			dx = c->min_dx + (dx-c->min_dx)/c->size.width_inc*c->size.width_inc;
			dy = c->min_dy + (dy-c->min_dy)/c->size.height_inc*c->size.height_inc;
		}
	
		if (c->size.flags & PMaxSize) {
			if (dx > c->size.max_width)
				dx = c->size.max_width;
			if (dy > c->size.max_height)
				dy = c->size.max_height;
		}
		/* end of nicked code */
	c->dx = dx + 2*BORDER;
	c->dy = dy + 2*BORDER;
	}

	if (moving) {
		if (x < 0)
			x = 0;
		if (y < 0)
			y = 0;
		c->x = x;
		c->y = y;
	}
	return c;
}

/*
 * this function's fun. the event is pretty much going to be a character
 * at a time, but we don't know this. Therefore, we're called each time
 * with the new additions to the buffer, and we read until we get a
 * newline. At this point, we check the line length is ok, and then
 * start parsing. If we haven't got a newline yet, we return pNone once
 * we've saved the characters. If we find an error, we set *text to point
 * to the buffer, so that we can display it in the error message.
 */

static PipeCmd
parsepipecmd(data, n, w, x, y, dx, dy, text)
char **data;
int *n;
Window *w;
int *x, *y, *dx, *dy;
char **text;
{
	static char buff[PCMDMAX];
	static int len;
	char c;

	if (*n == 0)
		return pNone;
	*text = buff;
	while ((*n)--) {
		c = *(*data)++;
		if (c == '\n' || c == '\r') {
			if (len < PCMDMAX) {
				buff[len] = 0;
				len = 0;
				return parse(buff,w,x,y,dx,dy,text);
			} else {
				len = 0;
				return pError;
			}
		}
		buff[len++] = c;
	}
	return pNone;
}

static PipeCmd
parse(buff, w, x, y, dx, dy, text)
char *buff;
Window *w;
int *x, *y, *dx, *dy;
char **text;
{
	struct cmdinfo *p;
	char *str;
	unsigned int len;
	int r;

	for (p = names; p->name; p++) {
		len = strlen(p->name);
		if (strncmp(buff,p->name,len) == 0 && isspace(buff[len])) {
			break;
		} else {
		}
	}
	if (p->name == 0) {
		return pError;
	}
	str = buff + len + 1;
	if (p->flags & STR)
		r = sscanf(str,p->fmt,w,&len);
	else
		r = sscanf(str,p->fmt,w,x,y,dx,dy);
	if (r < p->nconvs)
		return pError;
	if (p->flags & STR)
		*text = str + len;
	return p->cmd;
}

/*
 * following is largely nicked from my hacks to 9term.c, which
 * in turn were swiped from samterm.
 */

void
init_ninepipe()
{
#ifndef NOFIFO
	int fd;
	int flags;
	extern char *getenv();

	serverpid = getpid();
	if ((pipename = getenv(PIPENAMEENV)) == 0)
		pipename = PIPENAMEDEF;
	if (mkfifo(pipename, 0600) == -1)
		return;
	if ((fd = open(pipename, O_RDONLY | O_NONBLOCK)) < 0) {
		remove9pipe();
		return;
	}
	flags = fcntl(fd, F_GETFL, 0);
	if (flags == -1 || fcntl(fd,F_SETFL, flags & ~O_NONBLOCK) == -1
		|| open(pipename, O_WRONLY) == -1) {
		(void)close(fd);
		remove9pipe();
		return;
	}
	ninepipefd = fd;
	atexit(remove9pipe);
	return;
#endif
}

void
remove9pipe()
{
	if (pipename && serverpid == getpid()) {
		(void)unlink(pipename);
		pipename = 0;
	}
	return;
}

static char *
getlabelstr(s)
char *s;
{
	struct LabelStr *l;

	if (s == 0)
		return s;
	if ((l = (struct LabelStr *)malloc(sizeof(*l))) == 0)
		return s;
	l->n = labelstrs;
	labelstrs = l;
	if ((l->s = strdup(s)) == 0)
		l->s = s;
	l->f = (l->s == s);
	return l->s;
}

static Client *
freelabelstr(c)
Client *c;
{
	char *s = c->label;
	struct LabelStr *l, **p = &labelstrs;

	while (*p && (*p)->s != s)
		p = &((*p)->n);
	if (*p) {
		l = *p;
		*p = l->n;
		if (l->f)
			(void)free(l->s);
		(void)free(l);
	}
	return c;
}

static void
cmove(c)
Client *c;
{
    if (c == 0)
        return;
    active(c);
    XRaiseWindow(dpy, c->parent);
    XMoveWindow(dpy, c->parent, c->x-BORDER, c->y-BORDER);
    sendconfig(c);
    wins_changed |= wm_cmove;
}

static void
creshape(c)
Client *c;
{
    if (c == 0)
        return;
    active(c);
    XRaiseWindow(dpy, c->parent);
    XMoveResizeWindow(dpy, c->parent, c->x-BORDER, c->y-BORDER,
                    c->dx+2*(BORDER-1), c->dy+2*(BORDER-1));
    XMoveResizeWindow(dpy, c->window, BORDER-1, BORDER-1, c->dx, c->dy);
    wins_changed |= wm_creshape;
}
\Rogue\Monster\
else
  echo "will not over write ./pipe.c"
fi
echo "Finished archive 1 of 1"
exit