Commits

Anonymous committed 9f65898

initial commit

  • Participants

Comments (0)

Files changed (4)

+</$objtype/mkfile
+
+TARG=xplor
+
+HFILES=win.h
+
+OFILES=xplor.$O\
+		win.$O\
+
+BIN= /acme/bin/$objtype
+</sys/src/cmd/mkone
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include "win.h"
+
+void*
+erealloc(void *p, uint n)
+{
+	p = realloc(p, n);
+	if(p == nil)
+		fprint(2, "realloc failed: %r");
+	return p;
+}
+
+void
+wnew(Win *w)
+{
+	char buf[12];
+
+	w->ctl = open("/mnt/acme/new/ctl", ORDWR);
+	if(w->ctl<0 || read(w->ctl, buf, 12)!=12)
+		 fprint (2, "can't open window ctl file: %r");
+	ctlwrite(w, "noscroll\n");
+	w->winid = atoi(buf);
+	w->event = openfile(w, "event");
+	w->addr = -1;	/* will be opened when needed */
+	w->body = nil;
+	w->data = -1;
+}
+
+int
+openfile(Win *w, char *f)
+{
+	char buf[64];
+	int fd;
+
+	sprint(buf, "/mnt/acme/%d/%s", w->winid, f);
+	fd = open(buf, ORDWR|OCEXEC);
+	if(fd < 0)
+		 fprint (2,"can't open window %s file: %r", f);
+	return fd;
+}
+
+void
+opendata(Win *w, int mode)
+{
+	char buf[64];
+
+	sprint(buf, "/mnt/acme/%d/data", w->winid);
+	w->body = Bopen(buf, mode|OCEXEC);
+	if(w->body == nil)
+		 fprint(2,"can't open window data file: %r");
+}
+
+void
+wwritedata(Win *w, char *s, int n)
+{
+	if(w->body == nil)
+		opendata(w, OWRITE);
+	if(Bwrite(w->body, s, n) != n)
+		  fprint(2,"write error to window: %r");
+	Bflush(w->body);
+}
+
+void
+wtagwrite(Win *w, char *s, int n)
+{
+	int fd;
+
+	fd = openfile(w, "tag");
+	if(write(fd, s, n) != n)
+		  fprint(2,"tag write: %r");
+	close(fd);
+}
+
+void
+ctlwrite(Win *w, char *s)
+{
+	int n;
+
+	n = strlen(s);
+	if(write(w->ctl, s, n) != n)
+		 fprint(2,"write error to ctl file: %r");
+}
+
+void
+wname(Win *w, char *s)
+{
+	char buf[128];
+
+	sprint(buf, "name %s\n", s);
+	ctlwrite(w, buf);
+}
+
+void
+wclean(Win *w)
+{
+	if(w->body)
+		Bflush(w->body);
+	ctlwrite(w, "clean\n");
+}
+
+void
+wdormant(Win *w)
+{
+	if(w->addr >= 0){
+		close(w->addr);
+		w->addr = -1;
+	}
+	if(w->body != nil){
+		Bterm(w->body);
+		w->body = nil;
+	}
+	if(w->data >= 0){
+		close(w->data);
+		w->data = -1;
+	}
+}
+
+int
+getec(Win *w)
+{
+	if(w->nbuf == 0){
+		w->nbuf = read(w->event, w->buf, sizeof w->buf);
+		if(w->nbuf <= 0)
+			  fprint(2,"event read error: %r");
+		w->bufp = w->buf;
+	}
+	w->nbuf--;
+	return *w->bufp++;
+}
+
+int
+geten(Win *w)
+{
+	int n, c;
+
+	n = 0;
+	while('0'<=(c=getec(w)) && c<='9')
+		n = n*10+(c-'0');
+	if(c != ' ')
+		 fprint(2, "event number syntax");
+	return n;
+}
+
+int
+geter(Win *w, char *buf, int *nb)
+{
+	Rune r;
+	int n;
+
+	r = getec(w);
+	buf[0] = r;
+	n = 1;
+	if(r < Runeself)
+		goto Return;
+	while(!fullrune(buf, n))
+		buf[n++] = getec(w);
+	chartorune(&r, buf);
+    Return:
+	*nb = n;
+	return r;
+}
+
+
+void
+wevent(Win *w, Event *e)
+{
+	int i, nb;
+
+	e->c1 = getec(w);
+	e->c2 = getec(w);
+	e->q0 = geten(w);
+	e->q1 = geten(w);
+	e->flag = geten(w);
+	e->nr = geten(w);
+	if(e->nr > EVENTSIZE)
+		  fprint(2, "wevent: event string too long");
+	e->nb = 0;
+	for(i=0; i<e->nr; i++){
+		e->r[i] = geter(w, e->b+e->nb, &nb);
+		e->nb += nb;
+	}
+	e->r[e->nr] = 0;
+	e->b[e->nb] = 0;
+	if(getec(w) != '\n')
+		 fprint(2, "wevent: event syntax 2");
+}
+
+void
+wwriteevent(Win *w, Event *e)
+{
+	fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1);
+}
+
+enum
+{
+	False,
+	True,
+	EVENTSIZE=256,
+};
+
+
+typedef struct Event Event;
+struct Event
+{
+	int	c1;
+	int	c2;
+	int	q0;
+	int	q1;
+	int	flag;
+	int	nb;
+	int	nr;
+	char	b[EVENTSIZE*UTFmax+1];
+	Rune	r[EVENTSIZE+1];
+};
+
+
+typedef struct Win Win;
+struct Win
+{
+	int	winid;
+	int	addr;
+	Biobuf *body;
+	int	ctl;
+	int	data;
+	int	event;
+	char	buf[512];
+	char	*bufp;
+	int	nbuf;
+};
+
+int     dead(Win*);
+void	wnew(Win*);
+void	wwritedata(Win*, char *s, int n);
+void	wclean(Win*);
+void	wname(Win*, char*);
+void	wdormant(Win*);
+void	wevent(Win*, Event*);
+void	wtagwrite(Win*, char*, int);
+void	wwriteevent(Win*, Event*);
+void * erealloc(void *p, uint n);
+
+void	ctlwrite(Win*, char*);
+int	getec(Win*);
+int	geten(Win*);
+int	geter(Win*, char*, int*);
+int	openfile(Win*, char*);
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include <plumb.h>
+#include "win.h"
+
+#define ROOTSIZE 256
+#define NBUF 256
+
+enum
+{
+	STACK = 8192,
+};
+
+enum {
+	wait1 = (1<<0),
+	wait2 = (1<<1),
+	winwait = (1<<2),
+	xplorwait = (1<<3),
+};
+
+char *progname = "xplor";
+char *prog = "/bin/xplor";
+char *winprog = "/bin/win";
+char *root;
+char INDENT = '	';
+char *dirflag = "+ ";
+char *args[3];
+Win Mwin;
+
+void
+setAddr(char *addr) {
+	Win *w = &Mwin;
+
+	if(w->addr < 0)
+		w->addr = openfile(w, "addr");
+	if(write(w->addr, addr, strlen(addr)) < 0){
+		fprint(2, "readLine: warning: bad address %s:%r\n", addr);
+		return;
+	}
+}
+
+char *
+readLine(char *addr) {
+	Win *w = &Mwin;
+	char b[NBUF];
+	char *data, *end;
+	int n, len;
+
+	if(w->addr < 0)
+		w->addr = openfile(w, "addr");
+	if(w->data < 0)
+		w->data = openfile(w, "xdata");
+	if(write(w->addr, addr, strlen(addr)) < 0){
+		fprint(2, "readLine: warning: bad address %s:%r\n", addr);
+		return nil;
+	}
+	n = read(w->data, b, sizeof b);
+	if(n <= 0)
+		fprint(2,"reading data: %r");
+	end = strchr(&b[0], '\n');
+	len = end - &b[0];
+	if (len <= 3) {
+		// happens when reading blank lines at the end
+		return nil;
+	}
+	b[len] = 0;
+	// remove dirflag, if any
+	data = malloc(len - 1);
+	strncpy(data, &b[2], len - 2);
+	data[len - 2] = 0;
+	return data;
+}
+
+int
+getDepth(char *line) {
+	int pos = 0;
+
+	while (line[pos] == INDENT) {
+		pos++;
+	}
+	strcpy(line, &line[pos]);
+	return pos;
+}
+
+char *
+getParents(char *charaddr, int depth, int prevline) {
+	int len, len2, newdepth;
+	char *addr, *b, *fullpath;
+
+// what's the diff between that and "" ?
+// do it with a {} construct if possible.
+	if (depth == 0) {
+		fullpath = malloc(1);
+		fullpath[0] = 0;
+		return fullpath;
+	}
+	len = strlen(charaddr);
+//TODO: this will fail if prevline - 1 goes over 999, do better.
+	addr = malloc(6 + len);
+	addr[0] = '#';
+	strcpy(&addr[1], charaddr);
+	if (prevline == 1) {
+		strcpy(&addr[1+len], "-+");
+		addr[len+3] = 0;
+	} else {
+		addr[1+len] = '-';
+		sprint(&addr[2+len], "%d", prevline-1);
+		if (prevline - 1 > 99) {			
+			addr[len+5] = 0;
+		} else {
+			if (prevline - 1 > 9) {
+				addr[len+4] = 0;
+			} else {
+				addr[len+3] = 0;
+			}
+		}		
+	}
+	while(1) {
+		b = readLine(addr);
+		newdepth = getDepth(b);
+		if (newdepth < depth) {
+			fullpath = getParents(charaddr, newdepth, prevline);
+			len2 = strlen(fullpath);
+			fullpath = realloc(fullpath, len2 + 2 + strlen(b));
+			fullpath[len2] = '/';
+			fullpath[len2 + 1] = 0;
+			fullpath = strcat(fullpath, b);
+			free(b);
+			free(addr);
+			return fullpath;
+		}
+		prevline++;
+		sprint(&addr[2+len], "%d", prevline-1);
+		if (prevline - 1 > 99) {		
+			addr[len+5] = 0;
+		} else {
+			if (prevline - 1 > 9) {
+				addr[len+4] = 0;
+			} else {
+				addr[len+3] = 0;
+			}
+		}		
+	}
+	return "";
+}
+
+char *
+mkAddr(char *charaddr, char *terminator) {
+	char *b;
+	int len, len2;
+
+	len = strlen(charaddr);
+	len2 = strlen(terminator);
+	b = malloc(2 + len + len2);
+	b[0] = '#';
+	strcpy(&b[1], charaddr);
+	strcpy(&b[1 + len], terminator);
+	b[1 + len + len2] = 0;
+	return b;
+}
+
+
+int 
+isFolded(char *charaddr) {
+	char *b, *addr;
+	int depth, nextdepth;
+
+	addr = mkAddr(charaddr, "+1-");
+	b = readLine(addr);
+	depth = getDepth(b);
+	free(addr);
+	free(b);
+
+	addr = mkAddr(charaddr, "+-");
+	b = readLine(addr);
+	if (nil == b) {
+		// probably because it was the very bottom line
+		// hence it is indeed folded
+		free(addr);
+		return 1;
+	}
+	nextdepth = getDepth(b);
+	free(addr);
+	free(b);
+
+	if (nextdepth <= depth) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+char *
+getFullPath(char *charaddr, int *depth, char **parent) {
+	char *addr, *terminator, *b, *line;
+	char *fullpath = malloc(NBUF);
+	int dpth, len, pos;
+	Dir *dir;
+
+	// reconstruct full path
+	terminator = smprint("+1-");
+	addr = mkAddr(charaddr, terminator);
+	b = readLine(addr);
+	dpth = getDepth(b);
+	line = b;
+	strcpy(fullpath, root);
+	len = strlen(root);
+	fullpath[len] = '/';
+	b = getParents(charaddr, dpth, 1);
+	pos = len + 1;
+	strcpy(&fullpath[pos], b);
+	pos += strlen(b);
+	fullpath[pos] = '/';
+	pos++;
+	strcpy(&fullpath[pos], line);
+	pos += strlen(line);
+	fullpath[pos] = 0;
+	fullpath = cleanname(fullpath);
+	
+	if (parent != nil && *parent == nil) {
+		dir = dirstat(fullpath);
+		if (dir == nil) {
+			fprint(2,"dir nil \n");
+//TODO: better error handling
+			return;
+		}
+		if ((dir->mode & DMDIR) == 0) {
+			// not a dir -> strip last element of fullpath and provide that as a parent
+			len = strlen(fullpath) - strlen(dir->name);
+			*parent = malloc(len + 1);
+			strncpy(*parent, fullpath, len);
+			(*parent)[len] = 0;
+		} else {
+			*parent = smprint("%s", fullpath);
+		}
+	}
+	if (depth != nil) {
+		*depth = dpth;
+	}
+	free(terminator);
+	free(addr);
+	free(b);
+	free(line);	
+	return fullpath;
+}
+
+static int
+compar(const void *first, const void *second)
+{
+	Dir *dir1 = (Dir *) first;
+	Dir *dir2 = (Dir *) second;
+
+	return strcmp(dir1->name, dir2->name);
+}
+
+void
+printDirContents(char *dirpath, int depth) {
+	int n, len, currentDir, pos;
+	Dir *dirs;
+	char *indents, *name, *line;
+	int dirflaglen = strlen(dirflag);
+
+	dirpath = cleanname(dirpath);
+	currentDir = open(dirpath, OREAD);
+	if (currentDir < 0) {
+		return;
+	}
+
+	n = dirreadall(currentDir, &dirs);
+	if (n <= 0) {
+		return;
+	}
+	close(currentDir);
+
+	qsort(dirs, n, sizeof(Dir), compar);
+	indents = malloc(depth * sizeof(char) + 1);
+	for (int i = 0; i < depth; i++) {
+		indents[i] = '	';
+	}
+	indents[depth] = 0;
+	line = malloc(1);
+	for (int i = 0; i< n; i++) {
+		name = dirs[i].name;
+		len = strlen(name);
+		line = erealloc(line, dirflaglen + depth + len + 1 + 1);
+		if ((dirs[i].mode & DMDIR) != 0) {
+			// it's a dir
+			for (int i=0; i<dirflaglen; i++) {
+				line[i] = dirflag[i];
+			}
+		} else {
+			for (int i=0; i<dirflaglen; i++) {
+				line[i] = ' ';
+			}
+		}
+		pos = dirflaglen;
+		strcpy(&line[pos], indents);
+		pos += depth;
+		strcpy(&line[pos], name);
+		pos += len;
+		line[pos] = '\n';
+		pos++;
+		line[pos] = 0;
+		wwritedata(&Mwin, line, strlen(line));
+	}
+
+	free(line);	
+	free(indents);
+	free(dirs);
+
+	if (depth == 0) {
+	//lame trick for now to dodge the out of range issue, until my address-foo gets better
+		for (int i=0; i<3; i++) {
+			wwritedata(&Mwin, "\n", 2);
+		}
+	} 
+	return;
+}
+
+void 
+onLook(char *charaddr) {
+	char *addr, *terminator, *b, *line, *fullpath;
+	int depth, port, nextdepth, nextline;
+	Dir *dir;
+
+	// reconstruct full path and check if file or dir
+	fullpath = getFullPath(charaddr, &depth, nil);
+
+	dir = dirstat(fullpath);
+	if (dir == nil) {
+		fprint(2,"dir nil \n");
+		return;
+	}
+	if ((dir->mode & DMDIR) == 0) {
+		// not a dir -> send that file to the plumber
+		port = plumbopen("send", OWRITE);
+		if (port <= 0) {
+			fprint(2,"plumbopen failed \n");
+			return;
+		}
+		if (plumbsendtext(port, "xplor", "", "/", fullpath) <=0) {
+			fprint(2,"plumbopen failed \n");
+		} 
+		return;
+	}
+
+	if (isFolded(charaddr) == 1) {
+		// print dir contents
+		addr = mkAddr(charaddr, "+2-1-#0");
+		setAddr(addr);
+		printDirContents(fullpath, depth+1);
+		free(addr);
+		free(fullpath);
+	} else {
+		free(fullpath);
+		// fold, ie delete lines below dir until we hit a dir of the same depth
+		addr = mkAddr(charaddr, "+-");
+		nextdepth = depth + 1;
+		nextline = 1;
+		terminator = malloc(8);
+		terminator[0] = '+';
+//TODO(mpl): too many frees and mallocs in the loop (readLine), use buffs instead to fix that.
+		while (nextdepth > depth) {
+			setAddr(addr);
+			b = readLine(addr);
+			if (b != nil) {
+				// b == nil when we reach the very bottom line
+				nextdepth = getDepth(b);
+				free(b);
+			}
+			nextline++;
+			line = smprint("%d", nextline - 1);
+			strcpy(&terminator[1], line);
+			terminator[1+strlen(line)] = 0;
+			free(line);
+			free(addr);
+			addr = mkAddr(charaddr, terminator);
+			if (b == nil) {
+				break;
+			}
+		}
+		nextline--;
+		line = malloc(strlen(charaddr) * 2 + 8);
+		strcpy(line, charaddr);
+		strcpy(&line[strlen(charaddr)], "+-#0,#");
+		strcpy(&line[strlen(charaddr) + 6], charaddr);
+		line[2 * strlen(charaddr) + 6] = '+';
+		line[2 * strlen(charaddr) + 7] = 0;
+		free(terminator);
+		terminator = smprint("%d", nextline - 2);
+		addr = mkAddr(line, terminator);
+		free(terminator);
+		setAddr(addr);
+		free(addr);
+		wwritedata(&Mwin, "", 1);
+	}
+	return;
+}
+
+char *
+getCharAddr(char *msgBody) {
+	char *temp, *prefix, *out;
+	char *tokens[2];
+	int pos, len;
+
+	len = strlen(progname);
+	prefix = malloc(len + 2);
+	pos = 0;
+	strcpy(prefix, progname);
+	pos = len;
+	prefix[pos] = '-';
+	pos++;
+	prefix[pos] = 0;
+	if (strncmp(msgBody, prefix, len + 1) != 0) {
+		//this message is not from xplor's body, so ignore it
+		free(prefix);
+		return nil;
+	}
+	free(prefix);
+	if (gettokens(msgBody, tokens, 2, ":#") < 2) {
+		return nil;
+	}
+	temp = smprint("%s", tokens[1]);
+	if (gettokens(temp, tokens, 2, ",#") < 2) {
+		free(temp);
+		return nil;
+	}
+	out = smprint("%s", tokens[0]);
+	free(temp);
+	return out;
+}
+
+void
+doDotDot(void) {
+	Win *w = &Mwin;
+	char title[256];
+	
+	// blank the window
+	setAddr("0,$");
+	wwritedata(w, "", 1);
+	// restart from ..
+	root = realloc(root, strlen(root) + 5);
+	root = strcat(root, "/../");
+	root = cleanname(root);
+	sprint(title,"%s-%s", progname, root);
+	wname(w, title);
+	setAddr("#0");
+	printDirContents(root, 0);
+}
+
+void
+pexec(void *v)
+{
+	void **arg;
+	char *prog;
+	char **args;
+	Channel *c;
+
+	arg = v;
+	prog = arg[0];
+	args = arg[1];
+	c = arg[2];
+
+	if (args != nil) {
+		if (args[1] != nil) {
+			chdir(args[1]);
+			free(args[1]);
+			args[1] = nil;
+		}
+	}
+	procexec(c, prog, args);
+	fprint(2, "Remote execution failed: %s %r\n", prog);
+	abort();
+	threadexits(nil);
+}
+
+void
+procpexec(char *prog, char **args)
+{
+	void *rexarg[4];
+	Channel *c;
+
+	c = chancreate(sizeof(ulong), 0);
+	rexarg[0] = prog;
+	rexarg[1] = args;
+	rexarg[2] = c;
+
+	proccreate(pexec, rexarg, STACK);
+	recvul(c);
+	chanfree(c);
+}
+
+void
+handle(void)
+{
+	Event e;
+	Win *w = &Mwin;
+	int waitforit = 0;
+	char *tmp, *tmp2;
+
+	while (True) {
+		wevent(w, &e);
+		switch(e.c2){
+		default:
+			/* fprint(2,"unknown message %c%c\n", e.c1, e.c2); */
+			break;
+		case 'i':
+			/* fprint(2,"'%s' inserted in tag at %d\n", e.b, e.q0);*/
+			break;
+		case 'I':
+			/* fprint(2,"'%s' inserted in body at %d\n", e.b, e.q0);*/
+			break;
+		case 'd':
+			/* fprint(2, "'%s' deleted in tag at %d\n", e.b, e.q0);*/
+			break;
+		case 'D':
+			/* fprint(2, "'%s' deleted in body at %d\n", e.b, e.q0);*/
+			break;
+		case 'x':
+//			fprint(2, "%c, %s \n", e.c2, e.b);
+			if (e.flag & 8) {
+				//chorded command, let's see the rest incoming in next messages
+				waitforit = wait1;
+				break;
+			}
+//TODO: factorize Xplor and Win cases in functions
+			if ( (strncmp(e.b, "Win", 3) == 0) || 
+			((waitforit & winwait) && ((waitforit & wait1) || (waitforit & wait2)))) {
+				if (waitforit & wait2) {
+					goto win;
+				}
+				if (waitforit & winwait) {
+					waitforit |= wait2;
+					break;
+				} 								
+				if (waitforit & wait1) {
+					waitforit |= winwait;
+					break;
+				}
+				win:
+				args[1] = nil;
+				if (waitforit & winwait) {
+					waitforit = 0;
+					tmp = getCharAddr(e.b);
+					if (tmp != nil) {
+						tmp2 = getFullPath(tmp, nil, &args[1]);
+						free(tmp);
+						free(tmp2);
+					}
+				}
+				args[0] = winprog;
+				args[2] = nil;
+				procpexec(winprog, args);
+				break;
+			}
+			if ( (strncmp(e.b, "Xplor", 4) == 0) || 
+			((waitforit & xplorwait) && ((waitforit & wait1) || (waitforit & wait2)))) {
+				if (waitforit & wait2) {
+					goto xplor;
+				}
+				if (waitforit & xplorwait) {
+					waitforit |= wait2;
+					break;
+				} 								
+				if (waitforit & wait1) {
+					waitforit |= xplorwait;
+					break;
+				}
+				xplor:
+				args[1] = nil;
+				if (waitforit & xplorwait) {
+					waitforit = 0;
+					tmp = getCharAddr(e.b);
+					if (tmp != nil) {
+						tmp2 = getFullPath(tmp, nil, &args[1]);
+						free(tmp);
+						free(tmp2);
+					}
+				}				
+				args[0] = prog;
+				args[2] = nil;
+				procpexec(prog, args);
+				break;
+			}
+			if (strncmp(e.b, "DotDot", 6) == 0) {
+				doDotDot();
+				break;				
+			}
+			if (strncmp(e.b, "Del", 3) == 0) {
+//TODO: better clean up when closing
+				ctlwrite(w, "delete");
+				return;
+			}
+			if (strncmp(e.b, "Look", 4) == 0) {
+				// just resend the event to be caught above
+				wwriteevent(w, &e);
+			}	
+//			fprint(2, "%s \n", e.b);
+			break;	
+//		case 'X':				/* Execute command. */
+// this is for when the message is too long. see if we want to use it later
+//			if (e.flag & 2)
+//				wevent(w, &e2);
+//			break;
+		case 'l':
+			// just resend the event so that acme catches it
+			wwriteevent(w, &e);
+			break;
+		case 'L':				/* Look for something. */
+//			if (e.flag & 2)
+//				wevent(w, &e);
+//			wclean(w);		/* Set clean bit. */
+			//ignore expansions
+			if (e.q0 != e.q1) {
+				continue;
+			}
+//			fprint(2, "%d, %d \n", e.q0, e.q1);
+			onLook(smprint("%d",e.q0));
+			break;
+		}
+	}
+}
+
+
+void
+openwin(char *buttons)
+{
+	Win *w = &Mwin;
+	char buf[80];
+
+	wnew(w);
+	sprint(buf,"%s-%s", progname, root);
+	wname(w, buf);
+	wtagwrite(w, buttons, strlen(buttons));
+	wclean(w);
+	wdormant(w);
+	setAddr("#0");
+	printDirContents(root, 0);
+	handle();
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [path]\n", argv0);
+	threadexitsall(nil);
+}
+
+void
+threadmain(int argc, char** argv)
+{
+	char *temp;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+
+	root = malloc(ROOTSIZE);
+	switch(argc){
+	case 0:
+		root = getwd(root, ROOTSIZE);
+		break;
+	case 1:
+		if (argv[0][0] != '/') {
+			// it's a relative path
+			root = getwd(root, ROOTSIZE);
+			temp = &root[strlen(root)];
+			strcpy(temp, argv[0]);
+		} else {
+			strcpy(root, argv[0]);
+		}
+		break;
+	default:
+		usage();
+	}
+	root = cleanname(root);
+	
+	openwin("DotDot Win Xplor");
+}
+