xref: /plan9/sys/src/cmd/ip/ftpfs/proto.c (revision 05a04ac52a2b23ad037b995c87589df531465009)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <ip.h>
59a747e4fSDavid du Colombier #include <auth.h>
69a747e4fSDavid du Colombier #include <fcall.h>
79a747e4fSDavid du Colombier #include <ctype.h>
89a747e4fSDavid du Colombier #include <String.h>
99a747e4fSDavid du Colombier #include "ftpfs.h"
109a747e4fSDavid du Colombier 
119a747e4fSDavid du Colombier enum
129a747e4fSDavid du Colombier {
139a747e4fSDavid du Colombier 	/* return codes */
149a747e4fSDavid du Colombier 	Extra=		1,
159a747e4fSDavid du Colombier 	Success=	2,
169a747e4fSDavid du Colombier 	Incomplete=	3,
179a747e4fSDavid du Colombier 	TempFail=	4,
189a747e4fSDavid du Colombier 	PermFail=	5,
199a747e4fSDavid du Colombier 	Impossible=	6,
209a747e4fSDavid du Colombier };
219a747e4fSDavid du Colombier 
229a747e4fSDavid du Colombier Node	*remdir;		/* current directory on remote machine */
239a747e4fSDavid du Colombier Node	*remroot;		/* root directory on remote machine */
249a747e4fSDavid du Colombier 
259a747e4fSDavid du Colombier int	ctlfd;			/* fd for control connection */
269a747e4fSDavid du Colombier Biobuf	ctlin;			/* input buffer for control connection */
279a747e4fSDavid du Colombier Biobuf	stdin;			/* input buffer for standard input */
289a747e4fSDavid du Colombier Biobuf	dbuf;			/* buffer for data connection */
299a747e4fSDavid du Colombier char	msg[512];		/* buffer for replies */
309a747e4fSDavid du Colombier char	net[Maxpath];		/* network for connections */
319a747e4fSDavid du Colombier int	listenfd;		/* fd to listen on for connections */
329a747e4fSDavid du Colombier char	netdir[Maxpath];
339a747e4fSDavid du Colombier int	os, defos;
349a747e4fSDavid du Colombier char	topsdir[64];		/* name of listed directory for TOPS */
359a747e4fSDavid du Colombier String	*remrootpath;	/* path on remote side to remote root */
369a747e4fSDavid du Colombier char	*user;
379a747e4fSDavid du Colombier int	nopassive;
389a747e4fSDavid du Colombier long	lastsend;
399a747e4fSDavid du Colombier 
409a747e4fSDavid du Colombier static void	sendrequest(char*, char*);
419a747e4fSDavid du Colombier static int	getreply(Biobuf*, char*, int, int);
429a747e4fSDavid du Colombier static int	active(int, Biobuf**, char*, char*);
439a747e4fSDavid du Colombier static int	passive(int, Biobuf**, char*, char*);
449a747e4fSDavid du Colombier static int	data(int, Biobuf**, char*, char*);
459a747e4fSDavid du Colombier static int	port(void);
469a747e4fSDavid du Colombier static void	ascii(void);
479a747e4fSDavid du Colombier static void	image(void);
489a747e4fSDavid du Colombier static void	unixpath(Node*, String*);
499a747e4fSDavid du Colombier static void	vmspath(Node*, String*);
509a747e4fSDavid du Colombier static void	mvspath(Node*, String*);
519a747e4fSDavid du Colombier static Node*	vmsdir(char*);
529a747e4fSDavid du Colombier static int	getpassword(char*, char*);
53ecc2a59cSDavid du Colombier static int	nw_mode(char dirlet, char *s);
549a747e4fSDavid du Colombier 
559a747e4fSDavid du Colombier /*
569a747e4fSDavid du Colombier  *  connect to remote server, default network is "tcp/ip"
579a747e4fSDavid du Colombier  */
589a747e4fSDavid du Colombier void
599a747e4fSDavid du Colombier hello(char *dest)
609a747e4fSDavid du Colombier {
619a747e4fSDavid du Colombier 	char *p;
629a747e4fSDavid du Colombier 	char dir[Maxpath];
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier 	Binit(&stdin, 0, OREAD);	/* init for later use */
659a747e4fSDavid du Colombier 
669a747e4fSDavid du Colombier 	ctlfd = dial(netmkaddr(dest, "tcp", "ftp"), 0, dir, 0);
679a747e4fSDavid du Colombier 	if(ctlfd < 0){
689a747e4fSDavid du Colombier 		fprint(2, "can't dial %s: %r\n", dest);
699a747e4fSDavid du Colombier 		exits("dialing");
709a747e4fSDavid du Colombier 	}
719a747e4fSDavid du Colombier 	Binit(&ctlin, ctlfd, OREAD);
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier 	/* remember network for the data connections */
749a747e4fSDavid du Colombier 	p = strrchr(dir+1, '/');
759a747e4fSDavid du Colombier 	if(p == 0)
769a747e4fSDavid du Colombier 		fatal("wrong dial(2) linked with ftp");
779a747e4fSDavid du Colombier 	*p = 0;
789a747e4fSDavid du Colombier 	safecpy(net, dir, sizeof(net));
799a747e4fSDavid du Colombier 
809a747e4fSDavid du Colombier 	/* wait for hello from other side */
819a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
829a747e4fSDavid du Colombier 		fatal("bad hello");
83d9306527SDavid du Colombier 	if(strstr(msg, "Plan 9"))
84d9306527SDavid du Colombier 		os = Plan9;
859a747e4fSDavid du Colombier }
869a747e4fSDavid du Colombier 
879a747e4fSDavid du Colombier /*
889a747e4fSDavid du Colombier  *  login to remote system
899a747e4fSDavid du Colombier  */
909a747e4fSDavid du Colombier void
91eaa278a2SDavid du Colombier rlogin(char *rsys, char *keyspec)
929a747e4fSDavid du Colombier {
939a747e4fSDavid du Colombier 	char *line;
949a747e4fSDavid du Colombier 	char pass[128];
95d9306527SDavid du Colombier 	UserPasswd *up;
969a747e4fSDavid du Colombier 
97d9306527SDavid du Colombier 	up = nil;
989a747e4fSDavid du Colombier 	for(;;){
99d9306527SDavid du Colombier 		if(up == nil && os != Plan9)
100eaa278a2SDavid du Colombier 			up = auth_getuserpasswd(auth_getkey, "proto=pass server=%s service=ftp %s", rsys, keyspec);
101d9306527SDavid du Colombier 		if(up != nil){
102d9306527SDavid du Colombier 			sendrequest("USER", up->user);
103d9306527SDavid du Colombier 		} else {
1049a747e4fSDavid du Colombier 			print("User[default = %s]: ", user);
1059a747e4fSDavid du Colombier 			line = Brdline(&stdin, '\n');
1069a747e4fSDavid du Colombier 			if(line == 0)
1079a747e4fSDavid du Colombier 				exits(0);
1089a747e4fSDavid du Colombier 			line[Blinelen(&stdin)-1] = 0;
1099a747e4fSDavid du Colombier 			if(*line){
1109a747e4fSDavid du Colombier 				free(user);
1119a747e4fSDavid du Colombier 				user = strdup(line);
1129a747e4fSDavid du Colombier 			}
1139a747e4fSDavid du Colombier 			sendrequest("USER", user);
114d9306527SDavid du Colombier 		}
1159a747e4fSDavid du Colombier 		switch(getreply(&ctlin, msg, sizeof(msg), 1)){
1169a747e4fSDavid du Colombier 		case Success:
117d9306527SDavid du Colombier 			goto out;
1189a747e4fSDavid du Colombier 		case Incomplete:
1199a747e4fSDavid du Colombier 			break;
1209a747e4fSDavid du Colombier 		case TempFail:
1219a747e4fSDavid du Colombier 		case PermFail:
1229a747e4fSDavid du Colombier 			continue;
1239a747e4fSDavid du Colombier 		}
1249a747e4fSDavid du Colombier 
125d9306527SDavid du Colombier 		if(up != nil){
126d9306527SDavid du Colombier 			sendrequest("PASS", up->passwd);
127d9306527SDavid du Colombier 		} else {
1289a747e4fSDavid du Colombier 			if(getpassword(pass, pass+sizeof(pass)) < 0)
1299a747e4fSDavid du Colombier 				exits(0);
1309a747e4fSDavid du Colombier 			sendrequest("PASS", pass);
131d9306527SDavid du Colombier 		}
1329a747e4fSDavid du Colombier 		if(getreply(&ctlin, msg, sizeof(msg), 1) == Success){
1339a747e4fSDavid du Colombier 			if(strstr(msg, "Sess#"))
1349a747e4fSDavid du Colombier 				defos = MVS;
135d9306527SDavid du Colombier 			break;
1369a747e4fSDavid du Colombier 		}
1379a747e4fSDavid du Colombier 	}
138d9306527SDavid du Colombier out:
139d9306527SDavid du Colombier 	if(up != nil){
140d9306527SDavid du Colombier 		memset(up, 0, sizeof(*up));
141d9306527SDavid du Colombier 		free(up);
142d9306527SDavid du Colombier 	}
1439a747e4fSDavid du Colombier }
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier /*
1469a747e4fSDavid du Colombier  *  login to remote system with given user name and password.
1479a747e4fSDavid du Colombier  */
1489a747e4fSDavid du Colombier void
1499a747e4fSDavid du Colombier clogin(char *cuser, char *cpassword)
1509a747e4fSDavid du Colombier {
1519a747e4fSDavid du Colombier 	free(user);
1529a747e4fSDavid du Colombier 	user = strdup(cuser);
1539a747e4fSDavid du Colombier 	if (strcmp(user, "anonymous") != 0 &&
1549a747e4fSDavid du Colombier 	    strcmp(user, "ftp") != 0)
1559a747e4fSDavid du Colombier 		fatal("User must be 'anonymous' or 'ftp'");
1569a747e4fSDavid du Colombier 	sendrequest("USER", user);
1579a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 1)){
1589a747e4fSDavid du Colombier 	case Success:
1599a747e4fSDavid du Colombier 		return;
1609a747e4fSDavid du Colombier 	case Incomplete:
1619a747e4fSDavid du Colombier 		break;
1629a747e4fSDavid du Colombier 	case TempFail:
1639a747e4fSDavid du Colombier 	case PermFail:
1649a747e4fSDavid du Colombier 		fatal("login failed");
1659a747e4fSDavid du Colombier 	}
1669a747e4fSDavid du Colombier 	if (cpassword == 0)
1679a747e4fSDavid du Colombier 		fatal("password needed");
1689a747e4fSDavid du Colombier 	sendrequest("PASS", cpassword);
1699a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 1) != Success)
1709a747e4fSDavid du Colombier 		fatal("password failed");
1719a747e4fSDavid du Colombier 	if(strstr(msg, "Sess#"))
1729a747e4fSDavid du Colombier 		defos = MVS;
1739a747e4fSDavid du Colombier 	return;
1749a747e4fSDavid du Colombier }
1759a747e4fSDavid du Colombier 
1769a747e4fSDavid du Colombier /*
1779a747e4fSDavid du Colombier  *  find out about the other side.  go to it's root if requested.  set
1789a747e4fSDavid du Colombier  *  image mode if a Plan9 system.
1799a747e4fSDavid du Colombier  */
1809a747e4fSDavid du Colombier void
1819a747e4fSDavid du Colombier preamble(char *mountroot)
1829a747e4fSDavid du Colombier {
1839a747e4fSDavid du Colombier 	char *p, *ep;
1849a747e4fSDavid du Colombier 	int rv;
1859a747e4fSDavid du Colombier 	OS *o;
1869a747e4fSDavid du Colombier 
1879a747e4fSDavid du Colombier 	/*
1889a747e4fSDavid du Colombier 	 *  create a root directory mirror
1899a747e4fSDavid du Colombier 	 */
1909a747e4fSDavid du Colombier 	remroot = newnode(0, s_copy("/"));
1919a747e4fSDavid du Colombier 	remroot->d->qid.type = QTDIR;
1929a747e4fSDavid du Colombier 	remroot->d->mode = DMDIR|0777;
1939a747e4fSDavid du Colombier 	remdir = remroot;
1949a747e4fSDavid du Colombier 
1959a747e4fSDavid du Colombier 	/*
1969a747e4fSDavid du Colombier 	 *  get system type
1979a747e4fSDavid du Colombier 	 */
1989a747e4fSDavid du Colombier 	sendrequest("SYST", nil);
1999a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 1)){
2009a747e4fSDavid du Colombier 	case Success:
2019a747e4fSDavid du Colombier 		for(o = oslist; o->os != Unknown; o++)
2029a747e4fSDavid du Colombier 			if(strncmp(msg+4, o->name, strlen(o->name)) == 0)
2039a747e4fSDavid du Colombier 				break;
2049a747e4fSDavid du Colombier 		os = o->os;
2059a747e4fSDavid du Colombier 		if(os == NT)
2069a747e4fSDavid du Colombier 			os = Unix;
2079a747e4fSDavid du Colombier 		break;
2089a747e4fSDavid du Colombier 	default:
2099a747e4fSDavid du Colombier 		os = defos;
2109a747e4fSDavid du Colombier 		break;
2119a747e4fSDavid du Colombier 	}
2129a747e4fSDavid du Colombier 	if(os == Unknown)
2139a747e4fSDavid du Colombier 		os = defos;
2149a747e4fSDavid du Colombier 
2159a747e4fSDavid du Colombier 	remrootpath = s_reset(remrootpath);
2169a747e4fSDavid du Colombier 	switch(os){
2176b6b9ac8SDavid du Colombier 	case NetWare:
2186b6b9ac8SDavid du Colombier               /*
2196b6b9ac8SDavid du Colombier                * Request long, rather than 8.3 filenames,
2206b6b9ac8SDavid du Colombier                * where the Servers & Volume support them.
2216b6b9ac8SDavid du Colombier                */
2226b6b9ac8SDavid du Colombier               sendrequest("SITE LONG", nil);
2236b6b9ac8SDavid du Colombier               getreply(&ctlin, msg, sizeof(msg), 0);
2246b6b9ac8SDavid du Colombier               /* FALL THRU */
2259a747e4fSDavid du Colombier 	case Unix:
2269a747e4fSDavid du Colombier 	case Plan9:
2279a747e4fSDavid du Colombier 		/*
2289a747e4fSDavid du Colombier 		 *  go to the remote root, if asked
2299a747e4fSDavid du Colombier 		 */
2309a747e4fSDavid du Colombier 		if(mountroot){
2319a747e4fSDavid du Colombier 			sendrequest("CWD", mountroot);
2329a747e4fSDavid du Colombier 			getreply(&ctlin, msg, sizeof(msg), 0);
2339a747e4fSDavid du Colombier 		} else {
2349a747e4fSDavid du Colombier 			s_append(remrootpath, "/usr/");
2359a747e4fSDavid du Colombier 			s_append(remrootpath, user);
2369a747e4fSDavid du Colombier 		}
2379a747e4fSDavid du Colombier 
2389a747e4fSDavid du Colombier 		/*
2399a747e4fSDavid du Colombier 		 *  get the root directory
2409a747e4fSDavid du Colombier 		 */
2419a747e4fSDavid du Colombier 		sendrequest("PWD", nil);
2429a747e4fSDavid du Colombier 		rv = getreply(&ctlin, msg, sizeof(msg), 1);
2439a747e4fSDavid du Colombier 		if(rv == PermFail){
2449a747e4fSDavid du Colombier 			sendrequest("XPWD", nil);
2459a747e4fSDavid du Colombier 			rv = getreply(&ctlin, msg, sizeof(msg), 1);
2469a747e4fSDavid du Colombier 		}
2479a747e4fSDavid du Colombier 		if(rv == Success){
2489a747e4fSDavid du Colombier 			p = strchr(msg, '"');
2499a747e4fSDavid du Colombier 			if(p){
2509a747e4fSDavid du Colombier 				p++;
2519a747e4fSDavid du Colombier 				ep = strchr(p, '"');
2529a747e4fSDavid du Colombier 				if(ep){
2539a747e4fSDavid du Colombier 					*ep = 0;
2549a747e4fSDavid du Colombier 					s_append(s_reset(remrootpath), p);
2559a747e4fSDavid du Colombier 				}
2569a747e4fSDavid du Colombier 			}
2579a747e4fSDavid du Colombier 		}
2589a747e4fSDavid du Colombier 
2599a747e4fSDavid du Colombier 		break;
2609a747e4fSDavid du Colombier 	case Tops:
2619a747e4fSDavid du Colombier 	case VM:
2629a747e4fSDavid du Colombier 		/*
2639a747e4fSDavid du Colombier 		 *  top directory is a figment of our imagination.
2649a747e4fSDavid du Colombier 		 *  make it permanently cached & valid.
2659a747e4fSDavid du Colombier 		 */
2669a747e4fSDavid du Colombier 		CACHED(remroot);
2679a747e4fSDavid du Colombier 		VALID(remroot);
2689a747e4fSDavid du Colombier 		remroot->d->atime = time(0) + 100000;
2699a747e4fSDavid du Colombier 
2709a747e4fSDavid du Colombier 		/*
2719a747e4fSDavid du Colombier 		 *  no initial directory.  We are in the
2729a747e4fSDavid du Colombier 		 *  imaginary root.
2739a747e4fSDavid du Colombier 		 */
2749a747e4fSDavid du Colombier 		remdir = newtopsdir("???");
2759a747e4fSDavid du Colombier 		topsdir[0] = 0;
2769a747e4fSDavid du Colombier 		if(os == Tops && readdir(remdir) >= 0){
2779a747e4fSDavid du Colombier 			CACHED(remdir);
2789a747e4fSDavid du Colombier 			if(*topsdir)
2799a747e4fSDavid du Colombier 				remdir->remname = s_copy(topsdir);
2809a747e4fSDavid du Colombier 			VALID(remdir);
2819a747e4fSDavid du Colombier 		}
2829a747e4fSDavid du Colombier 		break;
2839a747e4fSDavid du Colombier 	case VMS:
2849a747e4fSDavid du Colombier 		/*
2859a747e4fSDavid du Colombier 		 *  top directory is a figment of our imagination.
2869a747e4fSDavid du Colombier 		 *  make it permanently cached & valid.
2879a747e4fSDavid du Colombier 		 */
2889a747e4fSDavid du Colombier 		CACHED(remroot);
2899a747e4fSDavid du Colombier 		VALID(remroot);
2909a747e4fSDavid du Colombier 		remroot->d->atime = time(0) + 100000;
2919a747e4fSDavid du Colombier 
2929a747e4fSDavid du Colombier 		/*
2939a747e4fSDavid du Colombier 		 *  get current directory
2949a747e4fSDavid du Colombier 		 */
2959a747e4fSDavid du Colombier 		sendrequest("PWD", nil);
2969a747e4fSDavid du Colombier 		rv = getreply(&ctlin, msg, sizeof(msg), 1);
2979a747e4fSDavid du Colombier 		if(rv == PermFail){
2989a747e4fSDavid du Colombier 			sendrequest("XPWD", nil);
2999a747e4fSDavid du Colombier 			rv = getreply(&ctlin, msg, sizeof(msg), 1);
3009a747e4fSDavid du Colombier 		}
3019a747e4fSDavid du Colombier 		if(rv == Success){
3029a747e4fSDavid du Colombier 			p = strchr(msg, '"');
3039a747e4fSDavid du Colombier 			if(p){
3049a747e4fSDavid du Colombier 				p++;
3059a747e4fSDavid du Colombier 				ep = strchr(p, '"');
3069a747e4fSDavid du Colombier 				if(ep){
3079a747e4fSDavid du Colombier 					*ep = 0;
3089a747e4fSDavid du Colombier 					remroot = remdir = vmsdir(p);
3099a747e4fSDavid du Colombier 				}
3109a747e4fSDavid du Colombier 			}
3119a747e4fSDavid du Colombier 		}
3129a747e4fSDavid du Colombier 		break;
3139a747e4fSDavid du Colombier 	case MVS:
3149a747e4fSDavid du Colombier 		usenlst = 1;
3159a747e4fSDavid du Colombier 		break;
3169a747e4fSDavid du Colombier 	}
3179a747e4fSDavid du Colombier 
3189a747e4fSDavid du Colombier 	if(os == Plan9)
3199a747e4fSDavid du Colombier 		image();
3209a747e4fSDavid du Colombier }
3219a747e4fSDavid du Colombier 
3229a747e4fSDavid du Colombier static void
3239a747e4fSDavid du Colombier ascii(void)
3249a747e4fSDavid du Colombier {
3259a747e4fSDavid du Colombier 	sendrequest("TYPE A", nil);
3269a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
3279a747e4fSDavid du Colombier 	case Success:
3289a747e4fSDavid du Colombier 		break;
3299a747e4fSDavid du Colombier 	default:
3309a747e4fSDavid du Colombier 		fatal("can't set type to ascii");
3319a747e4fSDavid du Colombier 	}
3329a747e4fSDavid du Colombier }
3339a747e4fSDavid du Colombier 
3349a747e4fSDavid du Colombier static void
3359a747e4fSDavid du Colombier image(void)
3369a747e4fSDavid du Colombier {
3379a747e4fSDavid du Colombier 	sendrequest("TYPE I", nil);
3389a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
3399a747e4fSDavid du Colombier 	case Success:
3409a747e4fSDavid du Colombier 		break;
3419a747e4fSDavid du Colombier 	default:
3429a747e4fSDavid du Colombier 		fatal("can't set type to image/binary");
3439a747e4fSDavid du Colombier 	}
3449a747e4fSDavid du Colombier }
3459a747e4fSDavid du Colombier 
3469a747e4fSDavid du Colombier /*
3479a747e4fSDavid du Colombier  *  decode the time fields, return seconds since epoch began
3489a747e4fSDavid du Colombier  */
3499a747e4fSDavid du Colombier char *monthchars = "janfebmaraprmayjunjulaugsepoctnovdec";
3509a747e4fSDavid du Colombier static Tm now;
3519a747e4fSDavid du Colombier 
3529a747e4fSDavid du Colombier static ulong
3539a747e4fSDavid du Colombier cracktime(char *month, char *day, char *yr, char *hms)
3549a747e4fSDavid du Colombier {
3559a747e4fSDavid du Colombier 	Tm tm;
3569a747e4fSDavid du Colombier 	int i;
3579a747e4fSDavid du Colombier 	char *p;
3589a747e4fSDavid du Colombier 
3596b6b9ac8SDavid du Colombier 
3609a747e4fSDavid du Colombier 	/* default time */
3619a747e4fSDavid du Colombier 	if(now.year == 0)
3629a747e4fSDavid du Colombier 		now = *localtime(time(0));
3639a747e4fSDavid du Colombier 	tm = now;
3646b6b9ac8SDavid du Colombier 	tm.yday = 0;
3659a747e4fSDavid du Colombier 
3669a747e4fSDavid du Colombier 	/* convert ascii month to a number twixt 1 and 12 */
3679a747e4fSDavid du Colombier 	if(*month >= '0' && *month <= '9'){
3689a747e4fSDavid du Colombier 		tm.mon = atoi(month) - 1;
3699a747e4fSDavid du Colombier 		if(tm.mon < 0 || tm.mon > 11)
3709a747e4fSDavid du Colombier 			tm.mon = 5;
3719a747e4fSDavid du Colombier 	} else {
3729a747e4fSDavid du Colombier 		for(p = month; *p; p++)
3739a747e4fSDavid du Colombier 			*p = tolower(*p);
3749a747e4fSDavid du Colombier 		for(i = 0; i < 12; i++)
3759a747e4fSDavid du Colombier 			if(strncmp(&monthchars[i*3], month, 3) == 0){
3769a747e4fSDavid du Colombier 				tm.mon = i;
3779a747e4fSDavid du Colombier 				break;
3789a747e4fSDavid du Colombier 			}
3799a747e4fSDavid du Colombier 	}
3809a747e4fSDavid du Colombier 
3819a747e4fSDavid du Colombier 	tm.mday = atoi(day);
3829a747e4fSDavid du Colombier 
3839a747e4fSDavid du Colombier 	if(hms){
3849a747e4fSDavid du Colombier 		tm.hour = strtol(hms, &p, 0);
3859a747e4fSDavid du Colombier 		if(*p == ':'){
3869a747e4fSDavid du Colombier 			tm.min = strtol(p+1, &p, 0);
3879a747e4fSDavid du Colombier 			if(*p == ':')
3889a747e4fSDavid du Colombier 				tm.sec = strtol(p+1, &p, 0);
3899a747e4fSDavid du Colombier 		}
3909a747e4fSDavid du Colombier 		if(tolower(*p) == 'p')
3919a747e4fSDavid du Colombier 			tm.hour += 12;
3929a747e4fSDavid du Colombier 	}
3939a747e4fSDavid du Colombier 
3949a747e4fSDavid du Colombier 	if(yr){
3959a747e4fSDavid du Colombier 		tm.year = atoi(yr);
3969a747e4fSDavid du Colombier 		if(tm.year >= 1900)
3979a747e4fSDavid du Colombier 			tm.year -= 1900;
3989a747e4fSDavid du Colombier 	} else {
3999a747e4fSDavid du Colombier 		if(tm.mon > now.mon || (tm.mon == now.mon && tm.mday > now.mday+1))
4009a747e4fSDavid du Colombier 			tm.year--;
4019a747e4fSDavid du Colombier 	}
4029a747e4fSDavid du Colombier 
4039a747e4fSDavid du Colombier 	/* convert to epoch seconds */
4049a747e4fSDavid du Colombier 	return tm2sec(&tm);
4059a747e4fSDavid du Colombier }
4069a747e4fSDavid du Colombier 
4079a747e4fSDavid du Colombier /*
4089a747e4fSDavid du Colombier  *  decode a Unix or Plan 9 file mode
4099a747e4fSDavid du Colombier  */
4109a747e4fSDavid du Colombier static ulong
4119a747e4fSDavid du Colombier crackmode(char *p)
4129a747e4fSDavid du Colombier {
4139a747e4fSDavid du Colombier 	ulong flags;
4149a747e4fSDavid du Colombier 	ulong mode;
4159a747e4fSDavid du Colombier 	int i;
4169a747e4fSDavid du Colombier 
4179a747e4fSDavid du Colombier 	flags = 0;
4189a747e4fSDavid du Colombier 	switch(strlen(p)){
4199a747e4fSDavid du Colombier 	case 10:	/* unix and new style plan 9 */
4209a747e4fSDavid du Colombier 		switch(*p){
4219a747e4fSDavid du Colombier 		case 'l':
4229a747e4fSDavid du Colombier 			return DMSYML|0777;
4239a747e4fSDavid du Colombier 		case 'd':
4249a747e4fSDavid du Colombier 			flags |= DMDIR;
4259a747e4fSDavid du Colombier 		case 'a':
4269a747e4fSDavid du Colombier 			flags |= DMAPPEND;
4279a747e4fSDavid du Colombier 		}
4289a747e4fSDavid du Colombier 		p++;
4299a747e4fSDavid du Colombier 		if(p[2] == 'l')
4309a747e4fSDavid du Colombier 			flags |= DMEXCL;
4319a747e4fSDavid du Colombier 		break;
4329a747e4fSDavid du Colombier 	case 11:	/* old style plan 9 */
4339a747e4fSDavid du Colombier 		switch(*p++){
4349a747e4fSDavid du Colombier 		case 'd':
4359a747e4fSDavid du Colombier 			flags |= DMDIR;
4369a747e4fSDavid du Colombier 			break;
4379a747e4fSDavid du Colombier 		case 'a':
4389a747e4fSDavid du Colombier 			flags |= DMAPPEND;
4399a747e4fSDavid du Colombier 			break;
4409a747e4fSDavid du Colombier 		}
4419a747e4fSDavid du Colombier 		if(*p++ == 'l')
4429a747e4fSDavid du Colombier 			flags |= DMEXCL;
4439a747e4fSDavid du Colombier 		break;
4449a747e4fSDavid du Colombier 	default:
4459a747e4fSDavid du Colombier 		return DMDIR|0777;
4469a747e4fSDavid du Colombier 	}
4479a747e4fSDavid du Colombier 	mode = 0;
4489a747e4fSDavid du Colombier 	for(i = 0; i < 3; i++){
4499a747e4fSDavid du Colombier 		mode <<= 3;
4509a747e4fSDavid du Colombier 		if(*p++ == 'r')
4519a747e4fSDavid du Colombier 			mode |= DMREAD;
4529a747e4fSDavid du Colombier 		if(*p++ == 'w')
4539a747e4fSDavid du Colombier 			mode |= DMWRITE;
4549a747e4fSDavid du Colombier 		if(*p == 'x' || *p == 's' || *p == 'S')
4559a747e4fSDavid du Colombier 			mode |= DMEXEC;
4569a747e4fSDavid du Colombier 		p++;
4579a747e4fSDavid du Colombier 	}
4589a747e4fSDavid du Colombier 	return mode | flags;
4599a747e4fSDavid du Colombier }
4609a747e4fSDavid du Colombier 
4619a747e4fSDavid du Colombier /*
4629a747e4fSDavid du Colombier  *  find first punctuation
4639a747e4fSDavid du Colombier  */
4649a747e4fSDavid du Colombier char*
4659a747e4fSDavid du Colombier strpunct(char *p)
4669a747e4fSDavid du Colombier {
4679a747e4fSDavid du Colombier 	int c;
4689a747e4fSDavid du Colombier 
4699a747e4fSDavid du Colombier 	for(;c = *p; p++){
4709a747e4fSDavid du Colombier 		if(ispunct(c))
4719a747e4fSDavid du Colombier 			return p;
4729a747e4fSDavid du Colombier 	}
4739a747e4fSDavid du Colombier 	return 0;
4749a747e4fSDavid du Colombier }
4759a747e4fSDavid du Colombier 
4769a747e4fSDavid du Colombier /*
4779a747e4fSDavid du Colombier  *  decode a Unix or Plan 9 directory listing
4789a747e4fSDavid du Colombier  */
4799a747e4fSDavid du Colombier static Dir*
4809a747e4fSDavid du Colombier crackdir(char *p, String **remname)
4819a747e4fSDavid du Colombier {
4829a747e4fSDavid du Colombier 	char *field[15];
4839a747e4fSDavid du Colombier 	char *dfield[4];
4849a747e4fSDavid du Colombier 	char *cp;
4859a747e4fSDavid du Colombier 	String *s;
4869a747e4fSDavid du Colombier 	int dn, n;
4879a747e4fSDavid du Colombier 	Dir d, *dp;
4889a747e4fSDavid du Colombier 
4899a747e4fSDavid du Colombier 	memset(&d, 0, sizeof(d));
4909a747e4fSDavid du Colombier 
4919a747e4fSDavid du Colombier 	n = getfields(p, field, 15, 1, " \t");
4929a747e4fSDavid du Colombier 	if(n > 2 && strcmp(field[n-2], "->") == 0)
4939a747e4fSDavid du Colombier 		n -= 2;
4949a747e4fSDavid du Colombier 	switch(os){
4959a747e4fSDavid du Colombier 	case TSO:
4969a747e4fSDavid du Colombier 		cp = strchr(field[0], '.');
4979a747e4fSDavid du Colombier 		if(cp){
4989a747e4fSDavid du Colombier 			*cp++ = 0;
4999a747e4fSDavid du Colombier 			s = s_copy(cp);
5009a747e4fSDavid du Colombier 			d.uid = field[0];
5019a747e4fSDavid du Colombier 		} else {
5029a747e4fSDavid du Colombier 			s = s_copy(field[0]);
5039a747e4fSDavid du Colombier 			d.uid = "TSO";
5049a747e4fSDavid du Colombier 		}
5059a747e4fSDavid du Colombier 		d.gid = "TSO";
5069a747e4fSDavid du Colombier 		d.mode = 0666;
5079a747e4fSDavid du Colombier 		d.length = 0;
5089a747e4fSDavid du Colombier 		d.atime = 0;
5099a747e4fSDavid du Colombier 		break;
5109a747e4fSDavid du Colombier 	case OS½:
5119a747e4fSDavid du Colombier 		s = s_copy(field[n-1]);
5129a747e4fSDavid du Colombier 		d.uid = "OS½";
5139a747e4fSDavid du Colombier 		d.gid = d.uid;
5149a747e4fSDavid du Colombier 		d.mode = 0666;
5159a747e4fSDavid du Colombier 		switch(n){
5169a747e4fSDavid du Colombier 		case 5:
5179a747e4fSDavid du Colombier 			if(strcmp(field[1], "DIR") == 0)
5189a747e4fSDavid du Colombier 				d.mode |= DMDIR;
5199a747e4fSDavid du Colombier 			d.length = atoi(field[0]);
5209a747e4fSDavid du Colombier 			dn = getfields(field[2], dfield, 4, 1, "-");
5219a747e4fSDavid du Colombier 			if(dn == 3)
5229a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[3]);
5239a747e4fSDavid du Colombier 			break;
5249a747e4fSDavid du Colombier 		}
5259a747e4fSDavid du Colombier 		break;
5269a747e4fSDavid du Colombier 	case Tops:
5279a747e4fSDavid du Colombier 		if(n != 4){ /* tops directory name */
5289a747e4fSDavid du Colombier 			safecpy(topsdir, field[0], sizeof(topsdir));
5299a747e4fSDavid du Colombier 			return 0;
5309a747e4fSDavid du Colombier 		}
5319a747e4fSDavid du Colombier 		s = s_copy(field[3]);
5329a747e4fSDavid du Colombier 		d.length = atoi(field[0]);
5339a747e4fSDavid du Colombier 		d.mode = 0666;
5349a747e4fSDavid du Colombier 		d.uid = "Tops";
5359a747e4fSDavid du Colombier 		d.gid = d.uid;
5369a747e4fSDavid du Colombier 		dn = getfields(field[1], dfield, 4, 1, "-");
5379a747e4fSDavid du Colombier 		if(dn == 3)
5389a747e4fSDavid du Colombier 			d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[2]);
5399a747e4fSDavid du Colombier 		else
5409a747e4fSDavid du Colombier 			d.atime = time(0);
5419a747e4fSDavid du Colombier 		break;
5429a747e4fSDavid du Colombier 	case VM:
5439a747e4fSDavid du Colombier 		switch(n){
5449a747e4fSDavid du Colombier 		case 9:
5459a747e4fSDavid du Colombier 			s = s_copy(field[0]);
5469a747e4fSDavid du Colombier 			s_append(s, ".");
5479a747e4fSDavid du Colombier 			s_append(s, field[1]);
5489a747e4fSDavid du Colombier 			d.length = atoi(field[3])*atoi(field[4]);
5499a747e4fSDavid du Colombier 			if(*field[2] == 'F')
5509a747e4fSDavid du Colombier 				d.mode = 0666;
5519a747e4fSDavid du Colombier 			else
5529a747e4fSDavid du Colombier 				d.mode = 0777;
5539a747e4fSDavid du Colombier 			d.uid = "VM";
5549a747e4fSDavid du Colombier 			d.gid = d.uid;
5559a747e4fSDavid du Colombier 			dn = getfields(field[6], dfield, 4, 1, "/-");
5569a747e4fSDavid du Colombier 			if(dn == 3)
5579a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[7]);
5589a747e4fSDavid du Colombier 			else
5599a747e4fSDavid du Colombier 				d.atime = time(0);
5609a747e4fSDavid du Colombier 			break;
5619a747e4fSDavid du Colombier 		case 1:
5629a747e4fSDavid du Colombier 			s = s_copy(field[0]);
5639a747e4fSDavid du Colombier 			d.uid = "VM";
5649a747e4fSDavid du Colombier 			d.gid = d.uid;
5659a747e4fSDavid du Colombier 			d.mode = 0777;
5669a747e4fSDavid du Colombier 			d.atime = 0;
5679a747e4fSDavid du Colombier 			break;
5689a747e4fSDavid du Colombier 		default:
5699a747e4fSDavid du Colombier 			return nil;
5709a747e4fSDavid du Colombier 		}
5719a747e4fSDavid du Colombier 		break;
5729a747e4fSDavid du Colombier 	case VMS:
5739a747e4fSDavid du Colombier 		switch(n){
5749a747e4fSDavid du Colombier 		case 6:
5759a747e4fSDavid du Colombier 			for(cp = field[0]; *cp; cp++)
5769a747e4fSDavid du Colombier 				*cp = tolower(*cp);
5779a747e4fSDavid du Colombier 			cp = strchr(field[0], ';');
5789a747e4fSDavid du Colombier 			if(cp)
5799a747e4fSDavid du Colombier 				*cp = 0;
5809a747e4fSDavid du Colombier 			d.mode = 0666;
5819a747e4fSDavid du Colombier 			cp = field[0] + strlen(field[0]) - 4;
5829a747e4fSDavid du Colombier 			if(strcmp(cp, ".dir") == 0){
5839a747e4fSDavid du Colombier 				d.mode |= DMDIR;
5849a747e4fSDavid du Colombier 				*cp = 0;
5859a747e4fSDavid du Colombier 			}
5869a747e4fSDavid du Colombier 			s = s_copy(field[0]);
5879a747e4fSDavid du Colombier 			d.length = atoi(field[1])*512;
5889a747e4fSDavid du Colombier 			field[4][strlen(field[4])-1] = 0;
5899a747e4fSDavid du Colombier 			d.uid = field[4]+1;
5909a747e4fSDavid du Colombier 			d.gid = d.uid;
5919a747e4fSDavid du Colombier 			dn = getfields(field[2], dfield, 4, 1, "/-");
5929a747e4fSDavid du Colombier 			if(dn == 3)
5939a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[1], dfield[0], dfield[2], field[3]);
5949a747e4fSDavid du Colombier 			else
5959a747e4fSDavid du Colombier 				d.atime = time(0);
5969a747e4fSDavid du Colombier 			break;
5979a747e4fSDavid du Colombier 		default:
5989a747e4fSDavid du Colombier 			return nil;
5999a747e4fSDavid du Colombier 		}
6009a747e4fSDavid du Colombier 		break;
6019a747e4fSDavid du Colombier 	case NetWare:
6029a747e4fSDavid du Colombier 		switch(n){
603ecc2a59cSDavid du Colombier 		case 8:		/* New style */
604ecc2a59cSDavid du Colombier 			s = s_copy(field[7]);
605ecc2a59cSDavid du Colombier 			d.uid = field[2];
606ecc2a59cSDavid du Colombier 			d.gid = d.uid;
607ecc2a59cSDavid du Colombier 			d.mode = nw_mode(field[0][0], field[1]);
608ecc2a59cSDavid du Colombier 			d.length = atoi(field[3]);
609ecc2a59cSDavid du Colombier 			if(strchr(field[6], ':'))
610ecc2a59cSDavid du Colombier 				d.atime = cracktime(field[4], field[5], nil, field[6]);
611ecc2a59cSDavid du Colombier 			else
612ecc2a59cSDavid du Colombier 				d.atime = cracktime(field[4], field[5], field[6], nil);
613ecc2a59cSDavid du Colombier 			break;
6149a747e4fSDavid du Colombier 		case 9:
6159a747e4fSDavid du Colombier 			s = s_copy(field[8]);
6169a747e4fSDavid du Colombier 			d.uid = field[2];
6179a747e4fSDavid du Colombier 			d.gid = d.uid;
6189a747e4fSDavid du Colombier 			d.mode = 0666;
6199a747e4fSDavid du Colombier 			if(*field[0] == 'd')
6209a747e4fSDavid du Colombier 				d.mode |= DMDIR;
6219a747e4fSDavid du Colombier 			d.length = atoi(field[3]);
6229a747e4fSDavid du Colombier 			d.atime = cracktime(field[4], field[5], field[6], field[7]);
6239a747e4fSDavid du Colombier 			break;
6249a747e4fSDavid du Colombier 		case 1:
6259a747e4fSDavid du Colombier 			s = s_copy(field[0]);
6269a747e4fSDavid du Colombier 			d.uid = "none";
6279a747e4fSDavid du Colombier 			d.gid = d.uid;
6289a747e4fSDavid du Colombier 			d.mode = 0777;
6299a747e4fSDavid du Colombier 			d.atime = 0;
6309a747e4fSDavid du Colombier 			break;
6319a747e4fSDavid du Colombier 		default:
6329a747e4fSDavid du Colombier 			return nil;
6339a747e4fSDavid du Colombier 		}
6349a747e4fSDavid du Colombier 		break;
6359a747e4fSDavid du Colombier 	case Unix:
6369a747e4fSDavid du Colombier 	case Plan9:
6379a747e4fSDavid du Colombier 	default:
6389a747e4fSDavid du Colombier 		switch(n){
6399a747e4fSDavid du Colombier 		case 8:		/* ls -l */
6409a747e4fSDavid du Colombier 			s = s_copy(field[7]);
6419a747e4fSDavid du Colombier 			d.uid = field[2];
6429a747e4fSDavid du Colombier 			d.gid = d.uid;
6439a747e4fSDavid du Colombier 			d.mode = crackmode(field[0]);
6449a747e4fSDavid du Colombier 			d.length = atoi(field[3]);
6459a747e4fSDavid du Colombier 			if(strchr(field[6], ':'))
6469a747e4fSDavid du Colombier 				d.atime = cracktime(field[4], field[5], 0, field[6]);
6479a747e4fSDavid du Colombier 			else
6489a747e4fSDavid du Colombier 				d.atime = cracktime(field[4], field[5], field[6], 0);
6499a747e4fSDavid du Colombier 			break;
6509a747e4fSDavid du Colombier 		case 9:		/* ls -lg */
6519a747e4fSDavid du Colombier 			s = s_copy(field[8]);
6529a747e4fSDavid du Colombier 			d.uid = field[2];
6539a747e4fSDavid du Colombier 			d.gid = field[3];
6549a747e4fSDavid du Colombier 			d.mode = crackmode(field[0]);
6559a747e4fSDavid du Colombier 			d.length = atoi(field[4]);
6569a747e4fSDavid du Colombier 			if(strchr(field[7], ':'))
6579a747e4fSDavid du Colombier 				d.atime = cracktime(field[5], field[6], 0, field[7]);
6589a747e4fSDavid du Colombier 			else
6599a747e4fSDavid du Colombier 				d.atime = cracktime(field[5], field[6], field[7], 0);
6609a747e4fSDavid du Colombier 			break;
6619a747e4fSDavid du Colombier 		case 10:	/* plan 9 */
6629a747e4fSDavid du Colombier 			s = s_copy(field[9]);
6639a747e4fSDavid du Colombier 			d.uid = field[3];
6649a747e4fSDavid du Colombier 			d.gid = field[4];
6659a747e4fSDavid du Colombier 			d.mode = crackmode(field[0]);
6669a747e4fSDavid du Colombier 			d.length = atoi(field[5]);
6679a747e4fSDavid du Colombier 			if(strchr(field[8], ':'))
6689a747e4fSDavid du Colombier 				d.atime = cracktime(field[6], field[7], 0, field[8]);
6699a747e4fSDavid du Colombier 			else
6709a747e4fSDavid du Colombier 				d.atime = cracktime(field[6], field[7], field[8], 0);
6719a747e4fSDavid du Colombier 			break;
6729a747e4fSDavid du Colombier 		case 4:		/* a Windows_NT version */
6739a747e4fSDavid du Colombier 			s = s_copy(field[3]);
6749a747e4fSDavid du Colombier 			d.uid = "NT";
6759a747e4fSDavid du Colombier 			d.gid = d.uid;
6769a747e4fSDavid du Colombier 			if(strcmp("<DIR>", field[2]) == 0){
6779a747e4fSDavid du Colombier 				d.length = 0;
6789a747e4fSDavid du Colombier 				d.mode = DMDIR|0777;
6799a747e4fSDavid du Colombier 			} else {
6809a747e4fSDavid du Colombier 				d.mode = 0666;
6819a747e4fSDavid du Colombier 				d.length = atoi(field[2]);
6829a747e4fSDavid du Colombier 			}
6839a747e4fSDavid du Colombier 			dn = getfields(field[0], dfield, 4, 1, "/-");
6849a747e4fSDavid du Colombier 			if(dn == 3)
6859a747e4fSDavid du Colombier 				d.atime = cracktime(dfield[0], dfield[1], dfield[2], field[1]);
6869a747e4fSDavid du Colombier 			break;
6879a747e4fSDavid du Colombier 		case 1:
6889a747e4fSDavid du Colombier 			s = s_copy(field[0]);
6899a747e4fSDavid du Colombier 			d.uid = "none";
6909a747e4fSDavid du Colombier 			d.gid = d.uid;
6919a747e4fSDavid du Colombier 			d.mode = 0777;
6929a747e4fSDavid du Colombier 			d.atime = 0;
6939a747e4fSDavid du Colombier 			break;
6949a747e4fSDavid du Colombier 		default:
6959a747e4fSDavid du Colombier 			return nil;
6969a747e4fSDavid du Colombier 		}
6979a747e4fSDavid du Colombier 	}
6989a747e4fSDavid du Colombier 	d.muid = d.uid;
6999a747e4fSDavid du Colombier 	d.qid.type = (d.mode & DMDIR) ? QTDIR : QTFILE;
7009a747e4fSDavid du Colombier 	d.mtime = d.atime;
7019a747e4fSDavid du Colombier 	if(ext && (d.qid.type & QTDIR) == 0)
7029a747e4fSDavid du Colombier 		s_append(s, ext);
7039a747e4fSDavid du Colombier 	d.name = s_to_c(s);
7049a747e4fSDavid du Colombier 
7059a747e4fSDavid du Colombier 	/* allocate a freeable dir structure */
7069a747e4fSDavid du Colombier 	dp = reallocdir(&d, 0);
7079a747e4fSDavid du Colombier 	*remname = s;
7089a747e4fSDavid du Colombier 
7099a747e4fSDavid du Colombier 	return dp;
7109a747e4fSDavid du Colombier }
7119a747e4fSDavid du Colombier 
7129a747e4fSDavid du Colombier /*
7139a747e4fSDavid du Colombier  *  probe files in a directory to see if they are directories
7149a747e4fSDavid du Colombier  */
7159a747e4fSDavid du Colombier /*
7169a747e4fSDavid du Colombier  *  read a remote directory
7179a747e4fSDavid du Colombier  */
7189a747e4fSDavid du Colombier int
7199a747e4fSDavid du Colombier readdir(Node *node)
7209a747e4fSDavid du Colombier {
7219a747e4fSDavid du Colombier 	Biobuf *bp;
7229a747e4fSDavid du Colombier 	char *line;
7239a747e4fSDavid du Colombier 	Node *np;
7249a747e4fSDavid du Colombier 	Dir *d;
7259a747e4fSDavid du Colombier 	long n;
7269a747e4fSDavid du Colombier 	int tries, x, files;
7279a747e4fSDavid du Colombier 	static int uselist;
7289a747e4fSDavid du Colombier 	int usenlist;
7299a747e4fSDavid du Colombier 	String *remname;
7309a747e4fSDavid du Colombier 
7319a747e4fSDavid du Colombier 	if(changedir(node) < 0)
7329a747e4fSDavid du Colombier 		return -1;
7339a747e4fSDavid du Colombier 
7349a747e4fSDavid du Colombier 	usenlist = 0;
7359a747e4fSDavid du Colombier 	for(tries = 0; tries < 3; tries++){
7369a747e4fSDavid du Colombier 		if(usenlist || usenlst)
7379a747e4fSDavid du Colombier 			x = data(OREAD, &bp, "NLST", nil);
7389a747e4fSDavid du Colombier 		else if(os == Unix && !uselist)
7399a747e4fSDavid du Colombier 			x = data(OREAD, &bp, "LIST -l", nil);
7409a747e4fSDavid du Colombier 		else
7419a747e4fSDavid du Colombier 			x = data(OREAD, &bp, "LIST", nil);
7429a747e4fSDavid du Colombier 		switch(x){
7439a747e4fSDavid du Colombier 		case Extra:
7449a747e4fSDavid du Colombier 			break;
7459a747e4fSDavid du Colombier /*		case TempFail:
7469a747e4fSDavid du Colombier 			continue;
7479a747e4fSDavid du Colombier */
7489a747e4fSDavid du Colombier 		default:
7499a747e4fSDavid du Colombier 			if(os == Unix && uselist == 0){
7509a747e4fSDavid du Colombier 				uselist = 1;
7519a747e4fSDavid du Colombier 				continue;
7529a747e4fSDavid du Colombier 			}
7539a747e4fSDavid du Colombier 			return seterr(nosuchfile);
7549a747e4fSDavid du Colombier 		}
7559a747e4fSDavid du Colombier 		files = 0;
7569a747e4fSDavid du Colombier 		while(line = Brdline(bp, '\n')){
7579a747e4fSDavid du Colombier 			n = Blinelen(bp);
7589a747e4fSDavid du Colombier 			if(debug)
7599a747e4fSDavid du Colombier 				write(2, line, n);
7609a747e4fSDavid du Colombier 			if(n > 1 && line[n-2] == '\r')
7619a747e4fSDavid du Colombier 				n--;
7629a747e4fSDavid du Colombier 			line[n - 1] = 0;
7639a747e4fSDavid du Colombier 
7649a747e4fSDavid du Colombier 			d = crackdir(line, &remname);
7659a747e4fSDavid du Colombier 			if(d == nil)
7669a747e4fSDavid du Colombier 				continue;
7679a747e4fSDavid du Colombier 			files++;
7689a747e4fSDavid du Colombier 			np = extendpath(node, remname);
7699a747e4fSDavid du Colombier 			d->qid.path = np->d->qid.path;
7709a747e4fSDavid du Colombier 			d->qid.vers = np->d->qid.vers;
7719a747e4fSDavid du Colombier 			d->type = np->d->type;
7729a747e4fSDavid du Colombier 			d->dev = 1;			/* mark node as valid */
7739a747e4fSDavid du Colombier 			if(os == MVS && node == remroot){
7749a747e4fSDavid du Colombier 				d->qid.type = QTDIR;
7759a747e4fSDavid du Colombier 				d->mode |= DMDIR;
7769a747e4fSDavid du Colombier 			}
7779a747e4fSDavid du Colombier 			free(np->d);
7789a747e4fSDavid du Colombier 			np->d = d;
7799a747e4fSDavid du Colombier 		}
7809a747e4fSDavid du Colombier 		close(Bfildes(bp));
7819a747e4fSDavid du Colombier 
7829a747e4fSDavid du Colombier 		switch(getreply(&ctlin, msg, sizeof(msg), 0)){
7839a747e4fSDavid du Colombier 		case Success:
7849a747e4fSDavid du Colombier 			if(files == 0 && !usenlst && !usenlist){
7859a747e4fSDavid du Colombier 				usenlist = 1;
7869a747e4fSDavid du Colombier 				continue;
7879a747e4fSDavid du Colombier 			}
7889a747e4fSDavid du Colombier 			if(files && usenlist)
7899a747e4fSDavid du Colombier 				usenlst = 1;
7909a747e4fSDavid du Colombier 			if(usenlst)
7919a747e4fSDavid du Colombier 				node->chdirunknown = 1;
7929a747e4fSDavid du Colombier 			return 0;
7939a747e4fSDavid du Colombier 		case TempFail:
7949a747e4fSDavid du Colombier 			break;
7959a747e4fSDavid du Colombier 		default:
7969a747e4fSDavid du Colombier 			return seterr(nosuchfile);
7979a747e4fSDavid du Colombier 		}
7989a747e4fSDavid du Colombier 	}
7999a747e4fSDavid du Colombier 	return seterr(nosuchfile);
8009a747e4fSDavid du Colombier }
8019a747e4fSDavid du Colombier 
8029a747e4fSDavid du Colombier /*
8039a747e4fSDavid du Colombier  *  create a remote directory
8049a747e4fSDavid du Colombier  */
8059a747e4fSDavid du Colombier int
8069a747e4fSDavid du Colombier createdir(Node *node)
8079a747e4fSDavid du Colombier {
8089a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
8099a747e4fSDavid du Colombier 		return -1;
8109a747e4fSDavid du Colombier 
8119a747e4fSDavid du Colombier 	sendrequest("MKD", node->d->name);
8129a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
8139a747e4fSDavid du Colombier 		return -1;
8149a747e4fSDavid du Colombier 	return 0;
8159a747e4fSDavid du Colombier }
8169a747e4fSDavid du Colombier 
8179a747e4fSDavid du Colombier /*
8189a747e4fSDavid du Colombier  *  change to a remote directory.
8199a747e4fSDavid du Colombier  */
8209a747e4fSDavid du Colombier int
8219a747e4fSDavid du Colombier changedir(Node *node)
8229a747e4fSDavid du Colombier {
8239a747e4fSDavid du Colombier 	Node *to;
8249a747e4fSDavid du Colombier 	String *cdpath;
8259a747e4fSDavid du Colombier 
8269a747e4fSDavid du Colombier 	to = node;
8279a747e4fSDavid du Colombier 	if(to == remdir)
8289a747e4fSDavid du Colombier 		return 0;
8299a747e4fSDavid du Colombier 
8309a747e4fSDavid du Colombier 	/* build an absolute path */
8319a747e4fSDavid du Colombier 	switch(os){
8329a747e4fSDavid du Colombier 	case Tops:
8339a747e4fSDavid du Colombier 	case VM:
8349a747e4fSDavid du Colombier 		switch(node->depth){
8359a747e4fSDavid du Colombier 		case 0:
8369a747e4fSDavid du Colombier 			remdir = node;
8379a747e4fSDavid du Colombier 			return 0;
8389a747e4fSDavid du Colombier 		case 1:
8399a747e4fSDavid du Colombier 			cdpath = s_clone(node->remname);
8409a747e4fSDavid du Colombier 			break;
8419a747e4fSDavid du Colombier 		default:
8429a747e4fSDavid du Colombier 			return seterr(nosuchfile);
8439a747e4fSDavid du Colombier 		}
8449a747e4fSDavid du Colombier 		break;
8459a747e4fSDavid du Colombier 	case VMS:
8469a747e4fSDavid du Colombier 		switch(node->depth){
8479a747e4fSDavid du Colombier 		case 0:
8489a747e4fSDavid du Colombier 			remdir = node;
8499a747e4fSDavid du Colombier 			return 0;
8509a747e4fSDavid du Colombier 		default:
8519a747e4fSDavid du Colombier 			cdpath = s_new();
8529a747e4fSDavid du Colombier 			vmspath(node, cdpath);
8539a747e4fSDavid du Colombier 		}
8549a747e4fSDavid du Colombier 		break;
8559a747e4fSDavid du Colombier 	case MVS:
8569a747e4fSDavid du Colombier 		if(node->depth == 0)
8579a747e4fSDavid du Colombier 			cdpath = s_clone(remrootpath);
8589a747e4fSDavid du Colombier 		else{
8599a747e4fSDavid du Colombier 			cdpath = s_new();
8609a747e4fSDavid du Colombier 			mvspath(node, cdpath);
8619a747e4fSDavid du Colombier 		}
8629a747e4fSDavid du Colombier 		break;
8639a747e4fSDavid du Colombier 	default:
8649a747e4fSDavid du Colombier 		if(node->depth == 0)
8659a747e4fSDavid du Colombier 			cdpath = s_clone(remrootpath);
8669a747e4fSDavid du Colombier 		else{
8679a747e4fSDavid du Colombier 			cdpath = s_new();
8689a747e4fSDavid du Colombier 			unixpath(node, cdpath);
8699a747e4fSDavid du Colombier 		}
8709a747e4fSDavid du Colombier 		break;
8719a747e4fSDavid du Colombier 	}
8729a747e4fSDavid du Colombier 
8739a747e4fSDavid du Colombier 	uncachedir(remdir, 0);
8749a747e4fSDavid du Colombier 
8759a747e4fSDavid du Colombier 	/*
8769a747e4fSDavid du Colombier 	 *  connect, if we need a password (Incomplete)
8779a747e4fSDavid du Colombier 	 *  act like it worked (best we can do).
8789a747e4fSDavid du Colombier 	 */
8799a747e4fSDavid du Colombier 	sendrequest("CWD", s_to_c(cdpath));
8809a747e4fSDavid du Colombier 	s_free(cdpath);
8819a747e4fSDavid du Colombier 	switch(getreply(&ctlin, msg, sizeof(msg), 0)){
8829a747e4fSDavid du Colombier 	case Success:
8839a747e4fSDavid du Colombier 	case Incomplete:
8849a747e4fSDavid du Colombier 		remdir = node;
8859a747e4fSDavid du Colombier 		return 0;
8869a747e4fSDavid du Colombier 	default:
8879a747e4fSDavid du Colombier 		return seterr(nosuchfile);
8889a747e4fSDavid du Colombier 	}
8899a747e4fSDavid du Colombier }
8909a747e4fSDavid du Colombier 
8919a747e4fSDavid du Colombier /*
8929a747e4fSDavid du Colombier  *  read a remote file
8939a747e4fSDavid du Colombier  */
8949a747e4fSDavid du Colombier int
8959a747e4fSDavid du Colombier readfile1(Node *node)
8969a747e4fSDavid du Colombier {
8979a747e4fSDavid du Colombier 	Biobuf *bp;
8989a747e4fSDavid du Colombier 	char buf[4*1024];
8999a747e4fSDavid du Colombier 	long off;
9009a747e4fSDavid du Colombier 	int n;
9019a747e4fSDavid du Colombier 	int tries;
9029a747e4fSDavid du Colombier 
9039a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
9049a747e4fSDavid du Colombier 		return -1;
9059a747e4fSDavid du Colombier 
9069a747e4fSDavid du Colombier 	for(tries = 0; tries < 4; tries++){
9079a747e4fSDavid du Colombier 		switch(data(OREAD, &bp, "RETR", s_to_c(node->remname))){
9089a747e4fSDavid du Colombier 		case Extra:
9099a747e4fSDavid du Colombier 			break;
9109a747e4fSDavid du Colombier 		case TempFail:
9119a747e4fSDavid du Colombier 			continue;
9129a747e4fSDavid du Colombier 		default:
9139a747e4fSDavid du Colombier 			return seterr(nosuchfile);
9149a747e4fSDavid du Colombier 		}
9159a747e4fSDavid du Colombier 		off = 0;
9169a747e4fSDavid du Colombier 		while((n = read(Bfildes(bp), buf, sizeof buf)) > 0){
9179a747e4fSDavid du Colombier 			if(filewrite(node, buf, off, n) != n){
9189a747e4fSDavid du Colombier 				off = -1;
9199a747e4fSDavid du Colombier 				break;
9209a747e4fSDavid du Colombier 			}
9219a747e4fSDavid du Colombier 			off += n;
9229a747e4fSDavid du Colombier 		}
9239a747e4fSDavid du Colombier 		if(off < 0)
9249a747e4fSDavid du Colombier 			return -1;
9259a747e4fSDavid du Colombier 
9269a747e4fSDavid du Colombier 		/* make sure a file gets created even for a zero length file */
9279a747e4fSDavid du Colombier 		if(off == 0)
9289a747e4fSDavid du Colombier 			filewrite(node, buf, 0, 0);
9299a747e4fSDavid du Colombier 
9309a747e4fSDavid du Colombier 		close(Bfildes(bp));
9319a747e4fSDavid du Colombier 		switch(getreply(&ctlin, msg, sizeof(msg), 0)){
9329a747e4fSDavid du Colombier 		case Success:
9339a747e4fSDavid du Colombier 			return off;
9349a747e4fSDavid du Colombier 		case TempFail:
9359a747e4fSDavid du Colombier 			continue;
9369a747e4fSDavid du Colombier 		default:
9379a747e4fSDavid du Colombier 			return seterr(nosuchfile);
9389a747e4fSDavid du Colombier 		}
9399a747e4fSDavid du Colombier 	}
9409a747e4fSDavid du Colombier 	return seterr(nosuchfile);
9419a747e4fSDavid du Colombier }
9429a747e4fSDavid du Colombier 
9439a747e4fSDavid du Colombier int
9449a747e4fSDavid du Colombier readfile(Node *node)
9459a747e4fSDavid du Colombier {
9469a747e4fSDavid du Colombier 	int rv, inimage;
9479a747e4fSDavid du Colombier 
9489a747e4fSDavid du Colombier 	switch(os){
9499a747e4fSDavid du Colombier 	case MVS:
9509a747e4fSDavid du Colombier 	case Plan9:
9519a747e4fSDavid du Colombier 	case Tops:
9529a747e4fSDavid du Colombier 	case TSO:
9539a747e4fSDavid du Colombier 		inimage = 0;
9549a747e4fSDavid du Colombier 		break;
9559a747e4fSDavid du Colombier 	default:
9569a747e4fSDavid du Colombier 		inimage = 1;
9579a747e4fSDavid du Colombier 		image();
9589a747e4fSDavid du Colombier 		break;
9599a747e4fSDavid du Colombier 	}
9609a747e4fSDavid du Colombier 
9619a747e4fSDavid du Colombier 	rv = readfile1(node);
9629a747e4fSDavid du Colombier 
9639a747e4fSDavid du Colombier 	if(inimage)
9649a747e4fSDavid du Colombier 		ascii();
9659a747e4fSDavid du Colombier 	return rv;
9669a747e4fSDavid du Colombier }
9679a747e4fSDavid du Colombier 
9689a747e4fSDavid du Colombier /*
9699a747e4fSDavid du Colombier  *  write back a file
9709a747e4fSDavid du Colombier  */
9719a747e4fSDavid du Colombier int
9729a747e4fSDavid du Colombier createfile1(Node *node)
9739a747e4fSDavid du Colombier {
9749a747e4fSDavid du Colombier 	Biobuf *bp;
9759a747e4fSDavid du Colombier 	char buf[4*1024];
9769a747e4fSDavid du Colombier 	long off;
9779a747e4fSDavid du Colombier 	int n;
9789a747e4fSDavid du Colombier 
9799a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
9809a747e4fSDavid du Colombier 		return -1;
9819a747e4fSDavid du Colombier 
9829a747e4fSDavid du Colombier 	if(data(OWRITE, &bp, "STOR", s_to_c(node->remname)) != Extra)
9839a747e4fSDavid du Colombier 		return -1;
9849a747e4fSDavid du Colombier 	for(off = 0; ; off += n){
9859a747e4fSDavid du Colombier 		n = fileread(node, buf, off, sizeof(buf));
9869a747e4fSDavid du Colombier 		if(n <= 0)
9879a747e4fSDavid du Colombier 			break;
9889a747e4fSDavid du Colombier 		write(Bfildes(bp), buf, n);
9899a747e4fSDavid du Colombier 	}
9909a747e4fSDavid du Colombier 	close(Bfildes(bp));
991*05a04ac5SDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
992*05a04ac5SDavid du Colombier 		return -1;
9939a747e4fSDavid du Colombier 	return off;
9949a747e4fSDavid du Colombier }
9959a747e4fSDavid du Colombier 
9969a747e4fSDavid du Colombier int
9979a747e4fSDavid du Colombier createfile(Node *node)
9989a747e4fSDavid du Colombier {
9999a747e4fSDavid du Colombier 	int rv;
10009a747e4fSDavid du Colombier 
10019a747e4fSDavid du Colombier 	switch(os){
10029a747e4fSDavid du Colombier 	case Plan9:
10039a747e4fSDavid du Colombier 	case Tops:
10049a747e4fSDavid du Colombier 		break;
10059a747e4fSDavid du Colombier 	default:
10069a747e4fSDavid du Colombier 		image();
10079a747e4fSDavid du Colombier 		break;
10089a747e4fSDavid du Colombier 	}
10099a747e4fSDavid du Colombier 	rv = createfile1(node);
10109a747e4fSDavid du Colombier 	switch(os){
10119a747e4fSDavid du Colombier 	case Plan9:
10129a747e4fSDavid du Colombier 	case Tops:
10139a747e4fSDavid du Colombier 		break;
10149a747e4fSDavid du Colombier 	default:
10159a747e4fSDavid du Colombier 		ascii();
10169a747e4fSDavid du Colombier 		break;
10179a747e4fSDavid du Colombier 	}
10189a747e4fSDavid du Colombier 	return rv;
10199a747e4fSDavid du Colombier }
10209a747e4fSDavid du Colombier 
10219a747e4fSDavid du Colombier /*
10229a747e4fSDavid du Colombier  *  remove a remote file
10239a747e4fSDavid du Colombier  */
10249a747e4fSDavid du Colombier int
10259a747e4fSDavid du Colombier removefile(Node *node)
10269a747e4fSDavid du Colombier {
10279a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
10289a747e4fSDavid du Colombier 		return -1;
10299a747e4fSDavid du Colombier 
10309a747e4fSDavid du Colombier 	sendrequest("DELE", s_to_c(node->remname));
10319a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
10329a747e4fSDavid du Colombier 		return -1;
10339a747e4fSDavid du Colombier 	return 0;
10349a747e4fSDavid du Colombier }
10359a747e4fSDavid du Colombier 
10369a747e4fSDavid du Colombier /*
10379a747e4fSDavid du Colombier  *  remove a remote directory
10389a747e4fSDavid du Colombier  */
10399a747e4fSDavid du Colombier int
10409a747e4fSDavid du Colombier removedir(Node *node)
10419a747e4fSDavid du Colombier {
10429a747e4fSDavid du Colombier 	if(changedir(node->parent) < 0)
10439a747e4fSDavid du Colombier 		return -1;
10449a747e4fSDavid du Colombier 
10459a747e4fSDavid du Colombier 	sendrequest("RMD", s_to_c(node->remname));
10469a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
10479a747e4fSDavid du Colombier 		return -1;
10489a747e4fSDavid du Colombier 	return 0;
10499a747e4fSDavid du Colombier }
10509a747e4fSDavid du Colombier 
10519a747e4fSDavid du Colombier /*
10529a747e4fSDavid du Colombier  *  tell remote that we're exiting and then do it
10539a747e4fSDavid du Colombier  */
10549a747e4fSDavid du Colombier void
10559a747e4fSDavid du Colombier quit(void)
10569a747e4fSDavid du Colombier {
10579a747e4fSDavid du Colombier 	sendrequest("QUIT", nil);
10589a747e4fSDavid du Colombier 	getreply(&ctlin, msg, sizeof(msg), 0);
10599a747e4fSDavid du Colombier 	exits(0);
10609a747e4fSDavid du Colombier }
10619a747e4fSDavid du Colombier 
10629a747e4fSDavid du Colombier /*
10639a747e4fSDavid du Colombier  *  send a request
10649a747e4fSDavid du Colombier  */
10659a747e4fSDavid du Colombier static void
10669a747e4fSDavid du Colombier sendrequest(char *a, char *b)
10679a747e4fSDavid du Colombier {
10689a747e4fSDavid du Colombier 	char buf[2*1024];
10699a747e4fSDavid du Colombier 	int n;
10709a747e4fSDavid du Colombier 
10719a747e4fSDavid du Colombier 	n = strlen(a)+2+1;
10729a747e4fSDavid du Colombier 	if(b != nil)
10739a747e4fSDavid du Colombier 		n += strlen(b)+1;
10749a747e4fSDavid du Colombier 	if(n >= sizeof(buf))
10759a747e4fSDavid du Colombier 		fatal("proto request too long");
10769a747e4fSDavid du Colombier 	strcpy(buf, a);
10779a747e4fSDavid du Colombier 	if(b != nil){
10789a747e4fSDavid du Colombier 		strcat(buf, " ");
10799a747e4fSDavid du Colombier 		strcat(buf, b);
10809a747e4fSDavid du Colombier 	}
10819a747e4fSDavid du Colombier 	strcat(buf, "\r\n");
10829a747e4fSDavid du Colombier 	n = strlen(buf);
10839a747e4fSDavid du Colombier 	if(write(ctlfd, buf, n) != n)
10849a747e4fSDavid du Colombier 		fatal("remote side hung up");
10859a747e4fSDavid du Colombier 	if(debug)
10869a747e4fSDavid du Colombier 		write(2, buf, n);
10879a747e4fSDavid du Colombier 	lastsend = time(0);
10889a747e4fSDavid du Colombier }
10899a747e4fSDavid du Colombier 
10909a747e4fSDavid du Colombier /*
10919a747e4fSDavid du Colombier  *  replies codes are in the range [100, 999] and may contain multiple lines of
10929a747e4fSDavid du Colombier  *  continuation.
10939a747e4fSDavid du Colombier  */
10949a747e4fSDavid du Colombier static int
10959a747e4fSDavid du Colombier getreply(Biobuf *bp, char *msg, int len, int printreply)
10969a747e4fSDavid du Colombier {
10979a747e4fSDavid du Colombier 	char *line;
1098d9306527SDavid du Colombier 	char *p;
10999a747e4fSDavid du Colombier 	int rv;
11009a747e4fSDavid du Colombier 	int i, n;
11019a747e4fSDavid du Colombier 
11029a747e4fSDavid du Colombier 	while(line = Brdline(bp, '\n')){
11039a747e4fSDavid du Colombier 		/* add line to message buffer, strip off \r */
11049a747e4fSDavid du Colombier 		n = Blinelen(bp);
11059a747e4fSDavid du Colombier 		if(n > 1 && line[n-2] == '\r'){
11069a747e4fSDavid du Colombier 			n--;
11079a747e4fSDavid du Colombier 			line[n-1] = '\n';
11089a747e4fSDavid du Colombier 		}
11099a747e4fSDavid du Colombier 		if(printreply && !quiet)
11109a747e4fSDavid du Colombier 			write(1, line, n);
11119a747e4fSDavid du Colombier 		else if(debug)
11129a747e4fSDavid du Colombier 			write(2, line, n);
11139a747e4fSDavid du Colombier 		if(n > len - 1)
11149a747e4fSDavid du Colombier 			i = len - 1;
11159a747e4fSDavid du Colombier 		else
11169a747e4fSDavid du Colombier 			i = n;
11179a747e4fSDavid du Colombier 		if(i > 0){
11189a747e4fSDavid du Colombier 			memmove(msg, line, i);
11199a747e4fSDavid du Colombier 			msg += i;
11209a747e4fSDavid du Colombier 			len -= i;
11219a747e4fSDavid du Colombier 			*msg = 0;
11229a747e4fSDavid du Colombier 		}
11239a747e4fSDavid du Colombier 
11249a747e4fSDavid du Colombier 		/* stop if not a continuation */
1125d9306527SDavid du Colombier 		rv = strtol(line, &p, 10);
1126d9306527SDavid du Colombier 		if(rv >= 100 && rv < 600 && p==line+3 && *p != '-')
11279a747e4fSDavid du Colombier 			return rv/100;
11289a747e4fSDavid du Colombier 
11299a747e4fSDavid du Colombier 		/* tell user about continuations */
11309a747e4fSDavid du Colombier 		if(!debug && !quiet && !printreply)
11319a747e4fSDavid du Colombier 			write(2, line, n);
11329a747e4fSDavid du Colombier 	}
11339a747e4fSDavid du Colombier 
11349a747e4fSDavid du Colombier 	fatal("remote side closed connection");
11359a747e4fSDavid du Colombier 	return 0;
11369a747e4fSDavid du Colombier }
11379a747e4fSDavid du Colombier 
11389a747e4fSDavid du Colombier /*
11399a747e4fSDavid du Colombier  *  Announce on a local port and tell its address to the the remote side
11409a747e4fSDavid du Colombier  */
11419a747e4fSDavid du Colombier static int
11429a747e4fSDavid du Colombier port(void)
11439a747e4fSDavid du Colombier {
11449a747e4fSDavid du Colombier 	char buf[256];
11459a747e4fSDavid du Colombier 	int n, fd;
11469a747e4fSDavid du Colombier 	char *ptr;
11479a747e4fSDavid du Colombier 	uchar ipaddr[IPaddrlen];
11489a747e4fSDavid du Colombier 	int port;
11499a747e4fSDavid du Colombier 
11509a747e4fSDavid du Colombier 	/* get a channel to listen on, let kernel pick the port number */
11519a747e4fSDavid du Colombier 	sprint(buf, "%s!*!0", net);
11529a747e4fSDavid du Colombier 	listenfd = announce(buf, netdir);
11539a747e4fSDavid du Colombier 	if(listenfd < 0)
11549a747e4fSDavid du Colombier 		return seterr("can't announce");
11559a747e4fSDavid du Colombier 
11569a747e4fSDavid du Colombier 	/* get the local address and port number */
11579a747e4fSDavid du Colombier 	sprint(buf, "%s/local", netdir);
11589a747e4fSDavid du Colombier 	fd = open(buf, OREAD);
11599a747e4fSDavid du Colombier 	if(fd < 0)
11609a747e4fSDavid du Colombier 		return seterr("opening %s: %r", buf);
11619a747e4fSDavid du Colombier 	n = read(fd, buf, sizeof(buf)-1);
11629a747e4fSDavid du Colombier 	close(fd);
11639a747e4fSDavid du Colombier 	if(n <= 0)
11649a747e4fSDavid du Colombier 		return seterr("opening %s/local: %r", netdir);
11659a747e4fSDavid du Colombier 	buf[n] = 0;
11669a747e4fSDavid du Colombier 	ptr = strchr(buf, ' ');
11679a747e4fSDavid du Colombier 	if(ptr)
11689a747e4fSDavid du Colombier 		*ptr = 0;
11699a747e4fSDavid du Colombier 	ptr = strchr(buf, '!')+1;
11709a747e4fSDavid du Colombier 	parseip(ipaddr, buf);
11719a747e4fSDavid du Colombier 	port = atoi(ptr);
11729a747e4fSDavid du Colombier 
11739a747e4fSDavid du Colombier 	/* tell remote side */
11749a747e4fSDavid du Colombier 	sprint(buf, "PORT %d,%d,%d,%d,%d,%d", ipaddr[IPv4off+0], ipaddr[IPv4off+1],
11759a747e4fSDavid du Colombier 		ipaddr[IPv4off+2], ipaddr[IPv4off+3], port>>8, port&0xff);
11769a747e4fSDavid du Colombier 	sendrequest(buf, nil);
11779a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success)
11789a747e4fSDavid du Colombier 		return seterr(msg);
11799a747e4fSDavid du Colombier 	return 0;
11809a747e4fSDavid du Colombier }
11819a747e4fSDavid du Colombier 
11829a747e4fSDavid du Colombier /*
11839a747e4fSDavid du Colombier  *  have server call back for a data connection
11849a747e4fSDavid du Colombier  */
11859a747e4fSDavid du Colombier static int
11869a747e4fSDavid du Colombier active(int mode, Biobuf **bpp, char *cmda, char *cmdb)
11879a747e4fSDavid du Colombier {
11889a747e4fSDavid du Colombier 	int cfd, dfd, rv;
11899a747e4fSDavid du Colombier 	char newdir[Maxpath];
11909a747e4fSDavid du Colombier 	char datafile[Maxpath + 6];
11919a747e4fSDavid du Colombier 
11929a747e4fSDavid du Colombier 	if(port() < 0)
11939a747e4fSDavid du Colombier 		return TempFail;
11949a747e4fSDavid du Colombier 
11959a747e4fSDavid du Colombier 	sendrequest(cmda, cmdb);
11969a747e4fSDavid du Colombier 
11979a747e4fSDavid du Colombier 	rv = getreply(&ctlin, msg, sizeof(msg), 0);
11989a747e4fSDavid du Colombier 	if(rv != Extra){
11999a747e4fSDavid du Colombier 		close(listenfd);
12009a747e4fSDavid du Colombier 		return rv;
12019a747e4fSDavid du Colombier 	}
12029a747e4fSDavid du Colombier 
12039a747e4fSDavid du Colombier 	/* wait for a new call */
12049a747e4fSDavid du Colombier 	cfd = listen(netdir, newdir);
12059a747e4fSDavid du Colombier 	if(cfd < 0)
12069a747e4fSDavid du Colombier 		fatal("waiting for data connection");
12079a747e4fSDavid du Colombier 	close(listenfd);
12089a747e4fSDavid du Colombier 
12099a747e4fSDavid du Colombier 	/* open it's data connection and close the control connection */
12109a747e4fSDavid du Colombier 	sprint(datafile, "%s/data", newdir);
12119a747e4fSDavid du Colombier 	dfd = open(datafile, ORDWR);
12129a747e4fSDavid du Colombier 	close(cfd);
12139a747e4fSDavid du Colombier 	if(dfd < 0)
12149a747e4fSDavid du Colombier 		fatal("opening data connection");
12159a747e4fSDavid du Colombier 	Binit(&dbuf, dfd, mode);
12169a747e4fSDavid du Colombier 	*bpp = &dbuf;
12179a747e4fSDavid du Colombier 	return Extra;
12189a747e4fSDavid du Colombier }
12199a747e4fSDavid du Colombier 
12209a747e4fSDavid du Colombier /*
12219a747e4fSDavid du Colombier  *  call out for a data connection
12229a747e4fSDavid du Colombier  */
12239a747e4fSDavid du Colombier static int
12249a747e4fSDavid du Colombier passive(int mode, Biobuf **bpp, char *cmda, char *cmdb)
12259a747e4fSDavid du Colombier {
12269a747e4fSDavid du Colombier 	char msg[1024];
12279a747e4fSDavid du Colombier 	char ds[1024];
12289a747e4fSDavid du Colombier 	char *f[6];
12299a747e4fSDavid du Colombier 	char *p;
12309a747e4fSDavid du Colombier 	int x, fd;
12319a747e4fSDavid du Colombier 
12329a747e4fSDavid du Colombier 	if(nopassive)
12339a747e4fSDavid du Colombier 		return Impossible;
12349a747e4fSDavid du Colombier 
12359a747e4fSDavid du Colombier 	sendrequest("PASV", nil);
12369a747e4fSDavid du Colombier 	if(getreply(&ctlin, msg, sizeof(msg), 0) != Success){
12379a747e4fSDavid du Colombier 		nopassive = 1;
12389a747e4fSDavid du Colombier 		return Impossible;
12399a747e4fSDavid du Colombier 	}
12409a747e4fSDavid du Colombier 
12419a747e4fSDavid du Colombier 	/* get address and port number from reply, this is AI */
12429a747e4fSDavid du Colombier 	p = strchr(msg, '(');
12439a747e4fSDavid du Colombier 	if(p == 0){
12449a747e4fSDavid du Colombier 		for(p = msg+3; *p; p++)
12459a747e4fSDavid du Colombier 			if(isdigit(*p))
12469a747e4fSDavid du Colombier 				break;
12479a747e4fSDavid du Colombier 	} else
12489a747e4fSDavid du Colombier 		p++;
12499a747e4fSDavid du Colombier 	if(getfields(p, f, 6, 0, ",") < 6){
12509a747e4fSDavid du Colombier 		if(debug)
12519a747e4fSDavid du Colombier 			fprint(2, "passive mode protocol botch: %s\n", msg);
12529a747e4fSDavid du Colombier 		werrstr("ftp protocol botch");
12539a747e4fSDavid du Colombier 		nopassive = 1;
12549a747e4fSDavid du Colombier 		return Impossible;
12559a747e4fSDavid du Colombier 	}
12569a747e4fSDavid du Colombier 	snprint(ds, sizeof(ds), "%s!%s.%s.%s.%s!%d", net,
12579a747e4fSDavid du Colombier 		f[0], f[1], f[2], f[3],
12589a747e4fSDavid du Colombier 		((atoi(f[4])&0xff)<<8) + (atoi(f[5])&0xff));
12599a747e4fSDavid du Colombier 
12609a747e4fSDavid du Colombier 	/* open data connection */
12619a747e4fSDavid du Colombier 	fd = dial(ds, 0, 0, 0);
12629a747e4fSDavid du Colombier 	if(fd < 0){
12639a747e4fSDavid du Colombier 		if(debug)
12649a747e4fSDavid du Colombier 			fprint(2, "passive mode connect to %s failed: %r\n", ds);
12659a747e4fSDavid du Colombier 		nopassive = 1;
12669a747e4fSDavid du Colombier 		return TempFail;
12679a747e4fSDavid du Colombier 	}
12689a747e4fSDavid du Colombier 
12699a747e4fSDavid du Colombier 	/* tell remote to send a file */
12709a747e4fSDavid du Colombier 	sendrequest(cmda, cmdb);
12719a747e4fSDavid du Colombier 	x = getreply(&ctlin, msg, sizeof(msg), 0);
12729a747e4fSDavid du Colombier 	if(x != Extra){
12739a747e4fSDavid du Colombier 		close(fd);
12749a747e4fSDavid du Colombier 		if(debug)
12759a747e4fSDavid du Colombier 			fprint(2, "passive mode retrieve failed: %s\n", msg);
12769a747e4fSDavid du Colombier 		werrstr(msg);
12779a747e4fSDavid du Colombier 		return x;
12789a747e4fSDavid du Colombier 	}
12799a747e4fSDavid du Colombier 
12809a747e4fSDavid du Colombier 	Binit(&dbuf, fd, mode);
12819a747e4fSDavid du Colombier 	*bpp = &dbuf;
12829a747e4fSDavid du Colombier 	return Extra;
12839a747e4fSDavid du Colombier }
12849a747e4fSDavid du Colombier 
12859a747e4fSDavid du Colombier static int
12869a747e4fSDavid du Colombier data(int mode, Biobuf **bpp, char* cmda, char *cmdb)
12879a747e4fSDavid du Colombier {
12889a747e4fSDavid du Colombier 	int x;
12899a747e4fSDavid du Colombier 
12909a747e4fSDavid du Colombier 	x = passive(mode, bpp, cmda, cmdb);
12919a747e4fSDavid du Colombier 	if(x != Impossible)
12929a747e4fSDavid du Colombier 		return x;
12939a747e4fSDavid du Colombier 	return active(mode, bpp, cmda, cmdb);
12949a747e4fSDavid du Colombier }
12959a747e4fSDavid du Colombier 
12969a747e4fSDavid du Colombier /*
12979a747e4fSDavid du Colombier  *  used for keep alives
12989a747e4fSDavid du Colombier  */
12999a747e4fSDavid du Colombier void
13009a747e4fSDavid du Colombier nop(void)
13019a747e4fSDavid du Colombier {
13029a747e4fSDavid du Colombier 	if(lastsend - time(0) < 15)
13039a747e4fSDavid du Colombier 		return;
13049a747e4fSDavid du Colombier 	sendrequest("PWD", nil);
13059a747e4fSDavid du Colombier 	getreply(&ctlin, msg, sizeof(msg), 0);
13069a747e4fSDavid du Colombier }
13079a747e4fSDavid du Colombier 
13089a747e4fSDavid du Colombier /*
13099a747e4fSDavid du Colombier  *  turn a vms spec into a path
13109a747e4fSDavid du Colombier  */
13119a747e4fSDavid du Colombier static Node*
13129a747e4fSDavid du Colombier vmsextendpath(Node *np, char *name)
13139a747e4fSDavid du Colombier {
13149a747e4fSDavid du Colombier 	np = extendpath(np, s_copy(name));
13159a747e4fSDavid du Colombier 	if(!ISVALID(np)){
13169a747e4fSDavid du Colombier 		np->d->qid.type = QTDIR;
13179a747e4fSDavid du Colombier 		np->d->atime = time(0);
13189a747e4fSDavid du Colombier 		np->d->mtime = np->d->atime;
13199a747e4fSDavid du Colombier 		strcpy(np->d->uid, "who");
13209a747e4fSDavid du Colombier 		strcpy(np->d->gid, "cares");
13219a747e4fSDavid du Colombier 		np->d->mode = DMDIR|0777;
13229a747e4fSDavid du Colombier 		np->d->length = 0;
13239a747e4fSDavid du Colombier 		if(changedir(np) >= 0)
13249a747e4fSDavid du Colombier 			VALID(np);
13259a747e4fSDavid du Colombier 	}
13269a747e4fSDavid du Colombier 	return np;
13279a747e4fSDavid du Colombier }
13289a747e4fSDavid du Colombier static Node*
13299a747e4fSDavid du Colombier vmsdir(char *name)
13309a747e4fSDavid du Colombier {
13319a747e4fSDavid du Colombier 	char *cp;
13329a747e4fSDavid du Colombier 	Node *np;
13339a747e4fSDavid du Colombier 	char *oname;
13349a747e4fSDavid du Colombier 
13359a747e4fSDavid du Colombier 	np = remroot;
13369a747e4fSDavid du Colombier 	cp = strchr(name, '[');
13379a747e4fSDavid du Colombier 	if(cp)
13389a747e4fSDavid du Colombier 		strcpy(cp, cp+1);
13399a747e4fSDavid du Colombier 	cp = strchr(name, ']');
13409a747e4fSDavid du Colombier 	if(cp)
13419a747e4fSDavid du Colombier 		*cp = 0;
13429a747e4fSDavid du Colombier 	oname = name = strdup(name);
13439a747e4fSDavid du Colombier 	if(name == 0)
13449a747e4fSDavid du Colombier 		return 0;
13459a747e4fSDavid du Colombier 
13469a747e4fSDavid du Colombier 	while(cp = strchr(name, '.')){
13479a747e4fSDavid du Colombier 		*cp = 0;
13489a747e4fSDavid du Colombier 		np = vmsextendpath(np, name);
13499a747e4fSDavid du Colombier 		name = cp+1;
13509a747e4fSDavid du Colombier 	}
13519a747e4fSDavid du Colombier 	np = vmsextendpath(np, name);
13529a747e4fSDavid du Colombier 
13539a747e4fSDavid du Colombier 	/*
13549a747e4fSDavid du Colombier 	 *  walk back to first accessible directory
13559a747e4fSDavid du Colombier 	 */
13569a747e4fSDavid du Colombier 	for(; np->parent != np; np = np->parent)
13579a747e4fSDavid du Colombier 		if(ISVALID(np)){
13589a747e4fSDavid du Colombier 			CACHED(np->parent);
13599a747e4fSDavid du Colombier 			break;
13609a747e4fSDavid du Colombier 		}
13619a747e4fSDavid du Colombier 
13629a747e4fSDavid du Colombier 	free(oname);
13639a747e4fSDavid du Colombier 	return np;
13649a747e4fSDavid du Colombier }
13659a747e4fSDavid du Colombier 
13669a747e4fSDavid du Colombier /*
13679a747e4fSDavid du Colombier  *  walk up the tree building a VMS style path
13689a747e4fSDavid du Colombier  */
13699a747e4fSDavid du Colombier static void
13709a747e4fSDavid du Colombier vmspath(Node *node, String *path)
13719a747e4fSDavid du Colombier {
13729a747e4fSDavid du Colombier 	char *p;
13739a747e4fSDavid du Colombier 	int n;
13749a747e4fSDavid du Colombier 
13759a747e4fSDavid du Colombier 	if(node->depth == 1){
13769a747e4fSDavid du Colombier 		p = strchr(s_to_c(node->remname), ':');
13779a747e4fSDavid du Colombier 		if(p){
13789a747e4fSDavid du Colombier 			n = p - s_to_c(node->remname) + 1;
13799a747e4fSDavid du Colombier 			s_nappend(path, s_to_c(node->remname), n);
13809a747e4fSDavid du Colombier 			s_append(path, "[");
13819a747e4fSDavid du Colombier 			s_append(path, p+1);
13829a747e4fSDavid du Colombier 		} else {
13839a747e4fSDavid du Colombier 			s_append(path, "[");
13849a747e4fSDavid du Colombier 			s_append(path, s_to_c(node->remname));
13859a747e4fSDavid du Colombier 		}
13869a747e4fSDavid du Colombier 		s_append(path, "]");
13879a747e4fSDavid du Colombier 		return;
13889a747e4fSDavid du Colombier 	}
13899a747e4fSDavid du Colombier 	vmspath(node->parent, path);
13909a747e4fSDavid du Colombier 	s_append(path, ".");
13919a747e4fSDavid du Colombier 	s_append(path, s_to_c(node->remname));
13929a747e4fSDavid du Colombier }
13939a747e4fSDavid du Colombier 
13949a747e4fSDavid du Colombier /*
13959a747e4fSDavid du Colombier  *  walk up the tree building a Unix style path
13969a747e4fSDavid du Colombier  */
13979a747e4fSDavid du Colombier static void
13989a747e4fSDavid du Colombier unixpath(Node *node, String *path)
13999a747e4fSDavid du Colombier {
14009a747e4fSDavid du Colombier 	if(node == node->parent){
14019a747e4fSDavid du Colombier 		s_append(path, s_to_c(remrootpath));
14029a747e4fSDavid du Colombier 		return;
14039a747e4fSDavid du Colombier 	}
14049a747e4fSDavid du Colombier 	unixpath(node->parent, path);
1405ecc2a59cSDavid du Colombier 	if(s_len(path) > 0 && strcmp(s_to_c(path), "/") != 0)
14069a747e4fSDavid du Colombier 		s_append(path, "/");
14079a747e4fSDavid du Colombier 	s_append(path, s_to_c(node->remname));
14089a747e4fSDavid du Colombier }
14099a747e4fSDavid du Colombier 
14109a747e4fSDavid du Colombier /*
14119a747e4fSDavid du Colombier  *  walk up the tree building a MVS style path
14129a747e4fSDavid du Colombier  */
14139a747e4fSDavid du Colombier static void
14149a747e4fSDavid du Colombier mvspath(Node *node, String *path)
14159a747e4fSDavid du Colombier {
14169a747e4fSDavid du Colombier 	if(node == node->parent){
14179a747e4fSDavid du Colombier 		s_append(path, s_to_c(remrootpath));
14189a747e4fSDavid du Colombier 		return;
14199a747e4fSDavid du Colombier 	}
14209a747e4fSDavid du Colombier 	mvspath(node->parent, path);
14219a747e4fSDavid du Colombier 	if(s_len(path) > 0)
14229a747e4fSDavid du Colombier 		s_append(path, ".");
14239a747e4fSDavid du Colombier 	s_append(path, s_to_c(node->remname));
14249a747e4fSDavid du Colombier }
14259a747e4fSDavid du Colombier 
14269a747e4fSDavid du Colombier static int
14279a747e4fSDavid du Colombier getpassword(char *buf, char *e)
14289a747e4fSDavid du Colombier {
14299a747e4fSDavid du Colombier 	char *p;
14309a747e4fSDavid du Colombier 	int c;
14319a747e4fSDavid du Colombier 	int consctl, rv = 0;
14329a747e4fSDavid du Colombier 
14339a747e4fSDavid du Colombier 	consctl = open("/dev/consctl", OWRITE);
14349a747e4fSDavid du Colombier 	if(consctl >= 0)
14359a747e4fSDavid du Colombier 		write(consctl, "rawon", 5);
14369a747e4fSDavid du Colombier 	print("Password: ");
14379a747e4fSDavid du Colombier 	e--;
14389a747e4fSDavid du Colombier 	for(p = buf; p <= e; p++){
14399a747e4fSDavid du Colombier 		c = Bgetc(&stdin);
14409a747e4fSDavid du Colombier 		if(c < 0){
14419a747e4fSDavid du Colombier 			rv = -1;
14429a747e4fSDavid du Colombier 			goto out;
14439a747e4fSDavid du Colombier 		}
14449a747e4fSDavid du Colombier 		if(c == '\n' || c == '\r')
14459a747e4fSDavid du Colombier 			break;
14469a747e4fSDavid du Colombier 		*p = c;
14479a747e4fSDavid du Colombier 	}
14489a747e4fSDavid du Colombier 	*p = 0;
14499a747e4fSDavid du Colombier 	print("\n");
14509a747e4fSDavid du Colombier 
14519a747e4fSDavid du Colombier out:
14529a747e4fSDavid du Colombier 	if(consctl >= 0)
14539a747e4fSDavid du Colombier 		close(consctl);
14549a747e4fSDavid du Colombier 	return rv;
14559a747e4fSDavid du Colombier }
14569a747e4fSDavid du Colombier 
14579a747e4fSDavid du Colombier /*
14589a747e4fSDavid du Colombier  *  convert from latin1 to utf
14599a747e4fSDavid du Colombier  */
14609a747e4fSDavid du Colombier static char*
14619a747e4fSDavid du Colombier fromlatin1(char *from)
14629a747e4fSDavid du Colombier {
14639a747e4fSDavid du Colombier 	char *p, *to;
14649a747e4fSDavid du Colombier 	Rune r;
14659a747e4fSDavid du Colombier 
14669a747e4fSDavid du Colombier 	if(os == Plan9)
14679a747e4fSDavid du Colombier 		return nil;
14689a747e4fSDavid du Colombier 
14699a747e4fSDavid du Colombier 	/* don't convert if we don't have to */
14709a747e4fSDavid du Colombier 	for(p = from; *p; p++)
14719a747e4fSDavid du Colombier 		if(*p & 0x80)
14729a747e4fSDavid du Colombier 			break;
14739a747e4fSDavid du Colombier 	if(*p == 0)
14749a747e4fSDavid du Colombier 		return nil;
14759a747e4fSDavid du Colombier 
14769a747e4fSDavid du Colombier 	to = malloc(3*strlen(from)+2);
14779a747e4fSDavid du Colombier 	if(to == nil)
14789a747e4fSDavid du Colombier 		return nil;
14799a747e4fSDavid du Colombier 	for(p = to; *from; from++){
14809a747e4fSDavid du Colombier 		r = (*from) & 0xff;
14819a747e4fSDavid du Colombier 		p += runetochar(p, &r);
14829a747e4fSDavid du Colombier 	}
14839a747e4fSDavid du Colombier 	*p = 0;
14849a747e4fSDavid du Colombier 	return to;
14859a747e4fSDavid du Colombier }
14869a747e4fSDavid du Colombier 
14879a747e4fSDavid du Colombier Dir*
14889a747e4fSDavid du Colombier reallocdir(Dir *d, int dofree)
14899a747e4fSDavid du Colombier {
14909a747e4fSDavid du Colombier 	Dir *dp;
14919a747e4fSDavid du Colombier 	char *p;
14929a747e4fSDavid du Colombier 	int nn, ng, nu, nm;
14939a747e4fSDavid du Colombier 	char *utf;
14949a747e4fSDavid du Colombier 
14959a747e4fSDavid du Colombier 	if(d->name == nil)
14969a747e4fSDavid du Colombier 		d->name = "?name?";
14979a747e4fSDavid du Colombier 	if(d->uid == nil)
14989a747e4fSDavid du Colombier 		d->uid = "?uid?";
14999a747e4fSDavid du Colombier 	if(d->gid == nil)
15009a747e4fSDavid du Colombier 		d->gid = d->uid;
15019a747e4fSDavid du Colombier 	if(d->muid == nil)
15029a747e4fSDavid du Colombier 		d->muid = d->uid;
15039a747e4fSDavid du Colombier 
15049a747e4fSDavid du Colombier 	utf = fromlatin1(d->name);
15059a747e4fSDavid du Colombier 	if(utf != nil)
15069a747e4fSDavid du Colombier 		d->name = utf;
15079a747e4fSDavid du Colombier 
15089a747e4fSDavid du Colombier 	nn = strlen(d->name)+1;
15099a747e4fSDavid du Colombier 	nu = strlen(d->uid)+1;
15109a747e4fSDavid du Colombier 	ng = strlen(d->gid)+1;
15119a747e4fSDavid du Colombier 	nm = strlen(d->muid)+1;
15129a747e4fSDavid du Colombier 	dp = malloc(sizeof(Dir)+nn+nu+ng+nm);
15139a747e4fSDavid du Colombier 	if(dp == nil){
15149a747e4fSDavid du Colombier 		if(dofree)
15159a747e4fSDavid du Colombier 			free(d);
15169a747e4fSDavid du Colombier 		if(utf != nil)
15179a747e4fSDavid du Colombier 			free(utf);
15189a747e4fSDavid du Colombier 		return nil;
15199a747e4fSDavid du Colombier 	}
15209a747e4fSDavid du Colombier 	*dp = *d;
15219a747e4fSDavid du Colombier 	p = (char*)&dp[1];
15229a747e4fSDavid du Colombier 	strcpy(p, d->name);
15239a747e4fSDavid du Colombier 	dp->name = p;
15249a747e4fSDavid du Colombier 	p += nn;
15259a747e4fSDavid du Colombier 	strcpy(p, d->uid);
15269a747e4fSDavid du Colombier 	dp->uid = p;
15279a747e4fSDavid du Colombier 	p += nu;
15289a747e4fSDavid du Colombier 	strcpy(p, d->gid);
15299a747e4fSDavid du Colombier 	dp->gid = p;
15309a747e4fSDavid du Colombier 	p += ng;
15319a747e4fSDavid du Colombier 	strcpy(p, d->muid);
15329a747e4fSDavid du Colombier 	dp->muid = p;
15339a747e4fSDavid du Colombier 	if(dofree)
15349a747e4fSDavid du Colombier 		free(d);
15359a747e4fSDavid du Colombier 	if(utf != nil)
15369a747e4fSDavid du Colombier 		free(utf);
15379a747e4fSDavid du Colombier 	return dp;
15389a747e4fSDavid du Colombier }
15399a747e4fSDavid du Colombier 
15409a747e4fSDavid du Colombier Dir*
15419a747e4fSDavid du Colombier dir_change_name(Dir *d, char *name)
15429a747e4fSDavid du Colombier {
15439a747e4fSDavid du Colombier 	if(d->name && strlen(d->name) >= strlen(name)){
15449a747e4fSDavid du Colombier 		strcpy(d->name, name);
15459a747e4fSDavid du Colombier 		return d;
15469a747e4fSDavid du Colombier 	}
15479a747e4fSDavid du Colombier 	d->name = name;
15489a747e4fSDavid du Colombier 	return reallocdir(d, 1);
15499a747e4fSDavid du Colombier }
15509a747e4fSDavid du Colombier 
15519a747e4fSDavid du Colombier Dir*
15529a747e4fSDavid du Colombier dir_change_uid(Dir *d, char *name)
15539a747e4fSDavid du Colombier {
15549a747e4fSDavid du Colombier 	if(d->uid && strlen(d->uid) >= strlen(name)){
15559a747e4fSDavid du Colombier 		strcpy(d->name, name);
15569a747e4fSDavid du Colombier 		return d;
15579a747e4fSDavid du Colombier 	}
15589a747e4fSDavid du Colombier 	d->uid = name;
15599a747e4fSDavid du Colombier 	return reallocdir(d, 1);
15609a747e4fSDavid du Colombier }
15619a747e4fSDavid du Colombier 
15629a747e4fSDavid du Colombier Dir*
15639a747e4fSDavid du Colombier dir_change_gid(Dir *d, char *name)
15649a747e4fSDavid du Colombier {
15659a747e4fSDavid du Colombier 	if(d->gid && strlen(d->gid) >= strlen(name)){
15669a747e4fSDavid du Colombier 		strcpy(d->name, name);
15679a747e4fSDavid du Colombier 		return d;
15689a747e4fSDavid du Colombier 	}
15699a747e4fSDavid du Colombier 	d->gid = name;
15709a747e4fSDavid du Colombier 	return reallocdir(d, 1);
15719a747e4fSDavid du Colombier }
15729a747e4fSDavid du Colombier 
15739a747e4fSDavid du Colombier Dir*
15749a747e4fSDavid du Colombier dir_change_muid(Dir *d, char *name)
15759a747e4fSDavid du Colombier {
15769a747e4fSDavid du Colombier 	if(d->muid && strlen(d->muid) >= strlen(name)){
15779a747e4fSDavid du Colombier 		strcpy(d->name, name);
15789a747e4fSDavid du Colombier 		return d;
15799a747e4fSDavid du Colombier 	}
15809a747e4fSDavid du Colombier 	d->muid = name;
15819a747e4fSDavid du Colombier 	return reallocdir(d, 1);
15829a747e4fSDavid du Colombier }
1583ecc2a59cSDavid du Colombier 
1584ecc2a59cSDavid du Colombier static int
1585ecc2a59cSDavid du Colombier nw_mode(char dirlet, char *s)		/* NetWare file mode mapping */
1586ecc2a59cSDavid du Colombier {
1587ecc2a59cSDavid du Colombier 	int mode = 0777;
1588ecc2a59cSDavid du Colombier 
1589ecc2a59cSDavid du Colombier 	if(dirlet == 'd')
1590ecc2a59cSDavid du Colombier 		mode |= DMDIR;
1591ecc2a59cSDavid du Colombier 
1592ecc2a59cSDavid du Colombier 	if (strlen(s) >= 10 && s[0] != '[' || s[9] != ']')
1593ecc2a59cSDavid du Colombier 		return(mode);
1594ecc2a59cSDavid du Colombier 
1595ecc2a59cSDavid du Colombier 	if (s[1] == '-')					/* can't read file */
1596ecc2a59cSDavid du Colombier 		mode &= ~0444;
1597ecc2a59cSDavid du Colombier 	if (dirlet == 'd' && s[6] == '-')			/* cannot scan dir */
1598ecc2a59cSDavid du Colombier 		mode &= ~0444;
1599ecc2a59cSDavid du Colombier 	if (s[2] == '-')					/* can't write file */
1600ecc2a59cSDavid du Colombier 		mode &= ~0222;
1601ecc2a59cSDavid du Colombier 	if (dirlet == 'd' && s[7] == '-' && s[3] == '-')	/* cannot create in, or modify dir */
1602ecc2a59cSDavid du Colombier 		mode &= ~0222;
1603ecc2a59cSDavid du Colombier 
1604ecc2a59cSDavid du Colombier 	return(mode);
1605ecc2a59cSDavid du Colombier }
1606