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