/* cmds.c */

#include "sys.h"
#include <sys/types.h>
#include <sys/param.h>

#include <sys/wait.h>

#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/ftp.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/time.h>
#include <time.h>

#ifdef SYSLOG
#	include <syslog.h>
#endif

#ifndef NO_UNISTDH
#	include <unistd.h>
#endif

#include "ftpdefs.h"
#include "defaults.h"
#include "cmds.h"
#include "main.h"
#include "ftp.h"
#include "ftprc.h"
#include "getpass.h"
#include "glob.h"
#include "copyright.h"

/* cmds.c globals */
int					curtype;			/* file transfer type */
str32				curtypename;		/* name of file transfer type */
int					verbose;			/* print messages coming back from server */
int					mprompt;			/* interactively prompt on m* cmds */
int					debug;				/* debugging level */
int					options;			/* used during socket creation */
int					macnum;				/* number of defined macros */
int					paging = 0;
int					creating = 0;
struct macel		macros[16];
char				*macbuf;			/* holds ALL macros */
jmp_buf				jabort;
char				*mname;				/* name of current m* command */
int					activemcmd;			/* flag: if != 0, then active multi command */
string				cwd;				/* current remote directory */
string				lcwd;				/* current local directory */
char				lasthostname[64];	/* name of last host w/ lookup(). */
int					remote_is_unix;		/* TRUE if remote host is unix. */
int					auto_binary = dAUTOBINARY;	/* If TRUE, set binary each connection. */
int					Opterr = 1;			/* if error message should be printed */
int					Optind = 1;			/* index into parent argv vector */
int					Optopt;				/* character checked for validity */
char				*Optarg;			/* argument associated with option */
char				*Optplace = EMSG;	/* saved position in an arg */

#ifdef REDIR
int							is_ls = 0;		/* are we doing an ls?  if so, then buffer */
struct lslist       		*lshead = NULL;	/* hold last output from host */
struct lslist       		*lstail = NULL;
#endif

/* cmds.c externs */
extern char					*globerr, *home, *reply_string;
extern int					code, margc, connected, ansi_escapes;
extern int					connected, fromatty, data, progress_meter;
extern int					parsing_rc;
extern char					*altarg, *line, *margv[];
extern char					*globchars;
extern string				hostname, progname, pager, anon_password;
extern string				prompt, logfname, version;
extern long					logsize, eventnumber;
extern size_t				xferbufsize;
extern struct servent		*sp;
extern struct cmd			cmdtab[];
extern struct userinfo		uinfo;
extern FILE					*cin, *cout, *logf;
extern int					Optind;
extern char					*Optarg;
/* extern int gethostname (char *, int); */
extern int ioctl (int, int, ...);

struct var vars[] = {
	{ "anon-password",		STR,	0,	anon_password,	(setvarproc) 0	},
	{ "ansi-escapes",		INT,	0,	&ansi_escapes,	(setvarproc) 0	},
	{ "auto-binary",		INT,	0,	&auto_binary,	(setvarproc) 0	},
	{ "debug",				INT,	0,	&debug,			(setvarproc) 0	},
	{ "local-dir",			STR,	0,	lcwd,			set_ldir 		},
	{ "logfile",			STR,	0,	logfname,		set_log			},
	{ "logsize",			LONG,	0,	&logsize,		(setvarproc) 0	},
	{ "mprompt",			INT,	0,	&mprompt,		(setvarproc) 0	},
	{ "pager",				STR,	0,	pager + 1,		set_pager		},
	{ "prompt",				STR,	0,	prompt,			set_prompt		},
	{ "progress-reports",	INT,	0,	&progress_meter,(setvarproc) 0	},
	{ "remote-is-unix",		INT,	1,	&remote_is_unix,(setvarproc) 0	},
	{ "type",				STR,	1,	curtypename,	set_type		},
	{ "verbose",			INT,	0,	&verbose,		set_verbose		}
};

struct types types[] = {
    { "ascii",  "A",    TYPE_A, 0 },
    { "binary", "I",    TYPE_I, 0 },
    { "image",  "I",    TYPE_I, 0 },
    { "ebcdic", "E",    TYPE_E, 0 },
    { "tenex",  "L",    TYPE_L, "8" },
    0
};



void Getopt_Reset(void)
{
	Optind = 1;
	Optplace = "";
}	/* Getopt_Reset */



int Getopt(int nargc, char **nargv, char *ostr)
{
	register char *oli;				   /* Option letter list index */

	if (!*Optplace) {					   /* update scanning pointer */
		if (Optind >= nargc || *(Optplace = nargv[Optind]) != '-')
			return (EOF);
		if (Optplace[1] && *++Optplace == '-') {	/* found "--" */
			++Optind;
			return (EOF);
		}
	}								   /* Option letter okay? */
	if ((Optopt = (int) *Optplace++) == (int) ':' ||
		!(oli = index(ostr, Optopt))) {
		if (!*Optplace)
			++Optind;
		if (Opterr) {
			fprintf(stderr, "%s%s%c\n", *nargv, ": illegal option -- ", Optopt);
			return(BADCH);
		}
	}
	if (*++oli != ':') {			   /* don't need argument */
		Optarg = NULL;
		if (!*Optplace)
			++Optind;
	} else {						   /* need an argument */
		if (*Optplace)					   /* no white space */
			Optarg = Optplace;
		else if (nargc <= ++Optind) {  /* no arg */
			Optplace = EMSG;
			if (Opterr) {
				fprintf(stderr, "%s%s%c\n", *nargv, ": option requires an argument -- ", Optopt);
				return(BADCH);
			}
		} else						   /* white space */
			Optarg = nargv[Optind];
		Optplace = EMSG;
		++Optind;
	}
	return (Optopt);				   /* dump back Option letter */
}									   /* Getopt */




char *Gets(char *sline, size_t size)
{
	char *cp = fgets(sline, (int) (size - 1), stdin);
	if (cp != NULL) {
		cp += strlen(cp) - 1;
		if (*cp == '\n')
			*cp = 0;	/* get rid of the newline. */
	}
	return cp;
}	/* Gets */




char **re_makeargv(char *promptstr, int *argc)
{
	size_t sz;

	(void) strcat(line, " ");
	(void) printf(promptstr);
	sz = strlen(line);
	(void) Gets(&line[sz], (size_t) (CMDLINELEN - sz)) ;
	(void) makeargv();
	*argc = margc;
	return (margv);
}	/* re_makeargv */




char *get_cwd(char *buf, int size)
{
#ifdef SYSV
#	ifdef NO_UNISTDH
#		ifdef GETCWDSIZET
	extern char *getcwd(char *, size_t);
#		else
	extern char *getcwd(char *, int);
#		endif
#	endif
	return (getcwd(buf, size - 1));
#else
	extern char *getwd(char *);
	return (getwd(buf));
#endif
}   /* get_cwd */

/*
 * Connect to peer server and
 * auto-login, if possible.
 */

int setpeer(int argc, char **argv)
{
	char				*path;
	unsigned int		port;
	time_t				now;
	int					openmode = 1, tmpverbose, opt, hErr;
	int					redial_delay = dREDIALDELAY;
	int					ignore_rc = 0, dials, max_dials = 1;
	string				pathname, newhost;
	char				*cmdname = argv[0];

	port = sp->s_port;
	Getopt_Reset();
	while ((opt = Getopt(argc, argv, "aiup:rd:g:")) >= 0) {
		switch (opt) {
			case 'a': openmode = OPEN_A; break;
			case 'u': openmode = OPEN_U; break;
			case 'i': ignore_rc = 1; break;
			case 'p':
				port = atoi(Optarg);
				if (port <= 0) {
					(void) printf("%s: bad port number (%s).\n", cmdname, Optarg);
					goto usage;
				}
				port = htons(port);
				break;
			case 'd': redial_delay = atoi(Optarg); break;
			case 'g': max_dials = atoi(Optarg);  break;
			case 'r': max_dials = -1; break;
			default:
			usage:
				(void) printf("usage: %s [-a | -u] [-i] [-p N] [-r [-d N] [-g N]] hostname[:pathname]\n\
    -a     : Open anonymously (this is the default).\n\
    -u     : Open, specify user/password.\n\
    -i     : Ignore machine entry in your .netrc.\n\
    -p N   : Use port #N for connection.\n\
    -r     : \"Redial\" until connected.\n\
    -d N   : Redial, pausing N seconds between tries.\n\
    -g N   : Redial, giving up after N tries.\n\
    :path  : Open site, then retrieve file \"path.\"\n", cmdname);
				code = -1;
				return;
		}
	}

	if (argv[Optind] == NULL)
		goto usage;
	(void) Strncpy(newhost, argv[Optind]);
	if (connected) {
		if (NOT_VQUIET && hostname != NULL)
			(void) printf("Closing %s...\n", hostname);
		(void) disconnect(0, NULL);
	}

	/*
	 *	If we are given something like wuarchive.wustl.edu:/pub/foo,
	 *	try to connect to the site then first try to cd to /pub/foo,
	 *	or if that fails, assume it is a file then try to fetch
	 *	/pub/foo and write foo in the current local directory.
	 */
	if ((path = index(newhost, ':')) != NULL) {
		*path++ = 0;
		(void) Strncpy(pathname, path);
	}

	GetFullSiteName(newhost);
	for (dials = 0; max_dials < 0 || dials < max_dials; dials++) {
		if (dials > 0) {
			(void) sleep(redial_delay);
			(void) fprintf(stderr, "Retry Number: %d\n", dials + 1);
		}
		hErr = hookup(newhost, port);
		if (hErr == -2)		/* Recoverable, so we can try again. */
			continue;
		else if (hErr == 0) {
			connected = 1;
			tmpverbose = verbose;
			if (debug == 0)
				verbose = V_QUIET;
			remote_is_unix = 1;
			if (command("SYST") == COMPLETE) {
				if (tmpverbose == V_VERBOSE) {
					register char *cp, c;
					cp = index(reply_string+4, ' ');
					if (cp == NULL)
						cp = index(reply_string+4, '\r');
					if (cp) {
						if (cp[-1] == '.')
							cp--;
						c = *cp;
						*cp = '\0';
					}
	
					(void) printf("Remote system type is %s.\n",
						reply_string+4);
					if (cp)
						*cp = c;
				}
				remote_is_unix = !strncmp(reply_string + 4, "UNIX", (size_t) 4);
			}
	
			if (auto_binary || path || !strncmp(reply_string, "215 UNIX Type: L8", (size_t) 17)) {
				_settype("binary");
				if (tmpverbose > V_TERSE)
				    (void) printf("Using %s mode to transfer files.\n", curtypename);
			}
			if (tmpverbose >= V_ERRS && 
			    !strncmp(reply_string, "215 TOPS20", (size_t) 10)) {
				(void) printf(
	"Remember to set tenex mode when transfering _binary_ files from this machine.\n");
			}
			verbose = tmpverbose;

			if (!login(newhost, openmode, ignore_rc) || cout == NULL)
				goto nextdial;		/* error! */
			if (logf != NULL) {
				(void) time(&now);
				(void) fprintf(logf, "%s opened at %s",
					hostname,
					ctime(&now));
			}

			/* Freshen 'cwd' variable for the prompt. */
			(void) _cd(NULL);
			if (path != NULL) {
				if (! _cd(pathname)) {
					/* Couldn't cd to this path, must be a file then. */
					(void) sprintf(line, "get %s", pathname);
					makeargv();
					(void) get(margc, margv);
					/* If we were invoked from the command line, quit
					 * after we got this file.
					 */
					if (eventnumber == 0L) {
						(void) quit(0, NULL);
					}
				}
			}
			break;
			/* end if we are connected */
		} else {
			/* Irrecoverable error, so don't bother redialing. */
			break;
		}
nextdial: continue;
	}
}	/* setpeer */


/*
 * Set transfer type.
 */
int settype(int argc, char **argv)
{
	if (argc > 2) {
		(void) printf("usage: %s [ ascii | binary | ebcdic | image | tenex ]\n", argv[0]);
		code = -1;
		return;
	}
	code = 0;
	if (argc < 2) {
xx:		(void) printf("Using %s mode to transfer files.\n", curtypename);
		return;
	}
	_settype(argv[1]);
	if (IS_VVERBOSE)
		goto xx;
}	/* settype */




void _settype(char *typename)
{
	register struct types	*p;
	int						comret, i;
	string					cmd;
 
	*cmd = 0;
	switch (isupper(*typename) ? tolower(*typename) : (*typename)) {
		case 'a': i = 0; break;
		case 'b': i = 1; break;
		case 'i': i = 2; break;
		case 'e': i = 3; break;
		case 't': i = 4;
			(void) strcpy(cmd, "TYPE L 8");
			break;
		default:
			(void) printf("%s: unknown type\n", typename);
			code = -1;
			return;
	}
	p = &types[i];
	if (*cmd == 0)
		(void) sprintf(cmd, "TYPE %s", p->t_mode);
	comret = command(cmd);
	if (comret == COMPLETE) {
		(void) Strncpy(curtypename, p->t_name);
		curtype = p->t_type;
	}
}	/* _settype */




/*ARGSUSED*/
int setbinary(int argc, char **argv) {	_settype("binary");	}
/*ARGSUSED*/
int setascii(int argc, char **argv) {	_settype("ascii");	}



char *verbose_msgs[] = {
	"Only printing necessary error messages.\n",
	"Printing error messages and announcements from the remote host.\n",
	"Printing all messages, errors, acknowledgments, and announcements.\n"
};

void set_verbose(char *new, int unset)
{
	int i;

	if (unset == -1) verbose = !verbose;
	else if (unset || !new || !*new) verbose = V_ERRS;
	else {
		i = StrToBool(new);
		if (i < V_QUIET) i = V_QUIET;
		else if (i > V_VERBOSE) i = V_VERBOSE;
		verbose = i;
	}
	if (!parsing_rc && NOT_VQUIET) 
		fputs(verbose_msgs[verbose], stdout);
}	/* set_verbose */




void set_prompt(char *new, int unset)
{
	(void) Strncpy(prompt, (unset || !new || !*new) ? dPROMPT : new);
	init_prompt();
}	/* set_prompt */




void set_log(char *fname, int unset)
{
	if (logf) {
		(void) fclose(logf);
		logf = NULL;
	}
	if (!unset && fname && *fname) {
		(void) Strncpy(logfname, fname);
		logf = fopen (logfname, "a");
	}
}	/* set_log */




void set_pager(char *new, int unset)
{
	if (unset)
		(void) strcpy(pager, "-");
	else {
		if (!new)
			new = dPAGER;
		if (!*new)
			(void) strcpy(pager, "-");
		else if (*new != '-')
			(void) sprintf(pager, "|%s", (*new == '|' ? new + 1 : new));
	}
}	/* set_pager */




void set_type(char *newtype, int unset)
{
	int t = verbose;
	verbose = V_QUIET;
	if (!connected && t > V_QUIET)
		(void) printf("Not connected.\n");
	else
		_settype(newtype && !unset ? newtype : "image");
	verbose = t;
}	/* set_type */




void set_ldir(char *ldir, int unset)
{
	int t = verbose;
	char *argv[2];

	if (ldir && !unset) {
		verbose = V_QUIET;
		argv[1] = ldir;
		lcd(2, argv);
		verbose = t;
	}
}	/* set_ldir */




int set(int argc, char **argv)
{
	int unset, i, c;
	struct var *v;
	char *var, *val = NULL;

	if (argc < 2 || strncmp(argv[1], "all", (size_t)3) == 0) {
		/* Show all variables. */
		for (i=0; i<sizeof(vars)/sizeof(struct var); i++) {
			(void) printf("%-20s= ", vars[i].name);
			c = vars[i].type;
			if (c < 0) c = -c;
			if (vars[i].conn_required && !connected)
				(void) printf("(not connected)\n");
			else switch (c) {
				case INT:
					(void) printf("%d\n", *(int *)vars[i].var); break;
				case LONG:
					(void) printf("%ld\n", *(long *)vars[i].var); break;
				case STR:
					(void) printf("\"%s\"\n", (char *) vars[i].var); break;
			}
		}
	} else {
		unset = argv[0][0] == 'u';
		var = argv[1];
		if (argc == 2)
			val = NULL;
		else if (argc > 2) {
			/* could be =, value or just value. */
			if (*argv[2] == '=') {
				if (argc > 3)
					val = argv[3];
				else return;	/* can't do 'set var =' */
			} else
				val = argv[2];
		}
		for (i=0, v=NULL; i<sizeof(vars)/sizeof(struct var); i++) {
			if (strncmp(vars[i].name, var, sizeof(vars[i].name)) == 0) {
				v = &vars[i];
				break;
			}
		}
		if (v == NULL) 
			(void) fprintf(stderr, "%s: unknown variable.\n", var);
		else {
			if (v->conn_required && !connected)
				(void) fprintf(stderr, "%s: must be connected.\n", var);
			else if (v->type < 0)	
				(void) fprintf(stderr, "%s: read-only variable.\n", var);
			else if (v->proc != (setvarproc) 0)
				(*v->proc)(val, unset);		/* a custom set proc. */
			else if (unset) switch(v->type) {
				case INT:
					*(int *) v->var = 0; break;
				case LONG:
					*(long *) v->var = 0; break;
				case STR:
					*(char *) v->var = 0; break;
			} else {
				if (val == NULL) switch(v->type) {
					/* User just said "set varname" */
					case INT:
						*(int *) v->var = 1; break;
					case LONG:
						*(long *) v->var = 1; break;
					case STR:
						*(char *) v->var = 0; break;
				} else {
					/* User said "set varname = value" */
					switch (v->type) {
						case INT:
							(void) sscanf(val, "%d", (int *) v->var); break;
						case LONG:
							(void) sscanf(val, "%ld", (long *) v->var); break;
						case STR:
							(void) strcpy(v->var, val); break;
					}
				}
			}
		}
	}
}	/* set */




/*
 * Send a single file.
 */
int put(int argc, char **argv)
{
	char *cmd;
	char *oldargv1;

	if (argc == 2) {
		argc++;
		argv[2] = argv[1];
	}
	if (argc < 2)
		argv = re_makeargv("(local-file) ", &argc);
	if (argc < 2) {
usage:
		(void) printf("usage:%s local-file remote-file\n", argv[0]);
		code = -1;
		return;
	}
	if (argc < 3)
		argv = re_makeargv("(remote-file) ", &argc);
	if (argc < 3) 
		goto usage;
	oldargv1 = argv[1];
	if (!globulize(&argv[1])) {
		code = -1;
		return;
	}
	/*
	 * If "globulize" modifies argv[1], and argv[2] is a copy of
	 * the old argv[1], make it a copy of the new argv[1].
	 */
	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
		argv[2] = argv[1];
	}
	cmd = (argv[0][0] == 'a') ? "APPE" : "STOR";
	sendrequest(cmd, argv[1], argv[2]);
}	/* put */




/*
 * Send multiple files.
 */
int mput(int argc, char **argv)
{
	register int i;
	void (*oldintr)(int);
	char *tp;

	if (argc < 2)
		argv = re_makeargv("(local-files) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s local-files\n", argv[0]);
		code = -1;
		return;
	}
	mname = argv[0];
	activemcmd = 1;
	oldintr = signal(SIGINT, (void (*)(int)) mabort);
	(void) setjmp(jabort);
	for (i = 1; i < argc; i++) {
		register char **cpp, **gargs;

		gargs = glob(argv[i]);
		if (globerr != NULL) {
			(void) printf("%s\n", globerr);
			if (gargs) {
				blkfree(gargs);
				free(gargs);
			}
			continue;
		}
		for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
			if (activemcmd && confirm(argv[0], *cpp)) {
				tp = *cpp;
				sendrequest("STOR", *cpp, tp);
				if (!activemcmd && fromatty) {
					if (confirm("Continue with","mput")) {
						activemcmd++;
					}
				}
			}
		}
		if (gargs != NULL) {
			blkfree(gargs);
			free(gargs);
		}
	}
	(void) signal(SIGINT, oldintr);
	activemcmd = 0;
}	/* mput */



int tmp_name(char *str)
{
	(void) strcpy(str, "/tmp/ncftpXXXXXX");
	return (mktemp(str) == NULL);
}	/* tmp_name */




int rem_glob_one(char *pattern)
{
	int			oldverbose, result = 0;
	char		*cp;
	string		str, tname;
	FILE		*ftemp;

	/* Check for wildcard characters. */
	if (*pattern == '|' || strpbrk(pattern, globchars) == NULL)
		return 0;

	(void) tmp_name(tname);
	oldverbose = verbose;
	verbose = V_QUIET;
	recvrequest ("NLST", tname, pattern, "w");
	verbose = oldverbose;
	ftemp = fopen(tname, "r");
	if (ftemp == NULL || FGets(str, ftemp) == NULL) {
		if (NOT_VQUIET)
			(void) printf("%s: no match.\n", pattern);
		result = -1;
		goto done;
	}
	if ((cp = index(str, '\n')) != NULL)
		*cp = '\0';
	(void) strcpy(pattern, str);
	cp = FGets(str, ftemp);
	/* It is an error if the pattern matched more than one file. */
	if (cp != NULL) {
		if (NOT_VQUIET)
			(void) printf("?Ambiguous remote file name.\n");
		result = -2;
	}
done:
	if (ftemp != NULL)
		(void) fclose(ftemp);
	(void) unlink(tname);
	return (result);
}	/* rem_glob_one */




/*
 * Receive (and maybe page) one file.
 */
int get(int argc, char **argv)
{
	string local_file;
	string remote_file;

	/* paging mode is set if the command name is 'page' or 'more.' */
	paging = (**argv != 'g');

	if (argc < 2)
		argv = re_makeargv("(remote-file) ", &argc);

	if (argc < 2) {
		if (paging)
			(void) printf("usage: %s remote-file\n", argv[0]);
		else {
getusage:
				(void) printf("usage: %s remote-file [local-file]\n", argv[0]);
		}
		return;
	}
	argv[1] = Strncpy(remote_file, argv[1]);
	if (rem_glob_one(argv[1]) < 0)
		return;

	if (paging) {
		size_t len = strlen(remote_file);

		/* Run compressed remote files through zcat, then the pager. */
		if (strlen(remote_file) > 2 && remote_file[len - 1] == 'Z' && remote_file[len - 2] == '.') {
			_settype("b");
			(void) sprintf(local_file, "|%s", ZCAT);
			argv[2] = Strncat(local_file, pager);
		} else
			argv[2] = pager;
	} else {
		/* normal get */
		if (argc == 2) {
			(void) Strncpy(local_file, argv[1]);
			argv[2] = local_file;
		} else {
			if (argc < 3)
				argv = re_makeargv("(local-file) ", &argc);
			if (argc < 3) 
				goto getusage;
			if (!globulize(&argv[2])) {
				code = -1;
				return;
			}
		}
	}
	recvrequest("RETR", argv[2], argv[1], "w");
	paging = 0;
}	/* get */



/*ARGSUSED*/
void mabort(int unused)
{
	(void) printf("\n");
	(void) fflush(stdout);
	if (activemcmd && fromatty) {
		if (confirm("Continue with", mname)) {
			longjmp(jabort,0);
		}
	}
	activemcmd = 0;
	longjmp(jabort,0);
}	/* mabort */




/*
 * Get multiple files.
 */
int mget(int argc, char **argv)
{
	char *cp, *tp;
	void (*oldintr)(int);

	if (argc < 2)
		argv = re_makeargv("(remote-files) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s remote-files\n", argv[0]);
		code = -1;
		return;
	}
	mname = argv[0];
	activemcmd = 1;
	oldintr = signal(SIGINT,mabort);
	(void) setjmp(jabort);
	while ((cp = remglob(argv)) != NULL) {
		if (*cp == '\0') {
			activemcmd = 0;
			continue;
		}
		if (activemcmd && confirm(argv[0], cp)) {
			tp = cp;
			recvrequest("RETR", tp, cp, "w");
			if (!activemcmd && fromatty) {
				if (confirm("Continue with","mget")) {
					activemcmd++;
				}
			}
		}
	}
	(void) signal(SIGINT,oldintr);
	activemcmd = 0;
}	/* mget */




char *remglob(char *argv[])
{
	static FILE			*ftemp = NULL;
	int					oldverbose;
	char				*cp, *mode;
	static string		tmpname, str;

	if (!activemcmd) {
xx:
		if (ftemp) {
			(void) fclose(ftemp);
			ftemp = NULL;
			(void) unlink(tmpname);
		}
		return(NULL);
	}
	if (ftemp == NULL) {
		(void) tmp_name(tmpname);
		oldverbose = verbose, verbose = V_QUIET;
		for (mode = "w"; *++argv != NULL; mode = "a")
			recvrequest ("NLST", tmpname, *argv, mode);
		verbose = oldverbose;
		ftemp = fopen(tmpname, "r");
		if (ftemp == NULL) {
			(void) printf("Can't find list of remote files (%s), oops.\n", tmpname);
			return (NULL);
		}
	}
	if (FGets(str, ftemp) == NULL) 
		goto xx;
	if ((cp = index(str, '\n')) != NULL)
		*cp = '\0';
	return (str);
}	/* remglob */




char *
onoff(int boolf)
{
	return (boolf ? "on" : "off");
}   /* onoff */




int
StrToBool(char *s)
{
    register char c;

    c = *s | 32;            /* lcase(*value) */
    switch (c) {
        case 'f':           /* false */
            return (0);
        case 'o':           /* test for "off" and "on" */
            c = s[1] | 32;
            return(c == 'f' ? 0 : 1);
        case 't':           /* true */
            return (1);
        default:            /* 1, 0, -1, other number? */
            return (atoi(s));
    }
}   /* StrToBool */


/*
 * Turn on/off printing of server echo's, messages, and statistics.
 */
int setverbose(int argc, char **argv)
{
	if (argc > 1)
		set_verbose(argv[1], 0);
	else set_verbose(argv[1], -1);
	code = verbose;
}	/* setverbose */



/*
 * Toggle interactive prompting
 * during mget, mput, and mdelete.
 */
int setprompt(int argc, char **argv)
{
	if (argc > 1)
		mprompt = StrToBool(argv[1]);
	else mprompt = !mprompt;
	if (IS_VVERBOSE)
		(void) printf("Interactive prompting for m* commmands %s.\n", onoff(mprompt));
	code = mprompt;
}	/* setprompt */


/*
 * Set debugging mode on/off and/or
 * set level of debugging.
 */
int setdebug(int argc, char **argv)
{
	int val;

	if (argc > 1) {
		val = StrToBool(argv[1]);
		if (val < 0) {
			(void) printf("%s: bad debugging value.\n", argv[1]);
			code = -1;
			return;
		}
	} else
		val = !debug;
	debug = val;
	fix_options();
	if (IS_VVERBOSE)
		(void) printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
	code = debug > 0;
}	/* debug */



void fix_options(void)
{
	if (debug)
		options |= SO_DEBUG;
	else
		options &= ~SO_DEBUG;
}	/* fix_options */



/*
 * Set current working directory
 * on remote machine.
 */
int cd(int argc, char **argv)
{
	if (argc < 2)
		argv = re_makeargv("(remote-directory) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s remote-directory\n", argv[0]);
		code = -1;
		return;
	}
	(void) _cd(argv[1]);
}	/* cd */




int implicit_cd(char *dir)
{
	int i, j = 0;
	
	if (connected) {
		i = verbose;
		/* Special verbosity level that ignores errors and prints other stuff,
		 * so you will get just the unknown command message and not an error
		 * message from cd.
		 */
		verbose = V_IMPLICITCD;
		j = _cd(dir);
		verbose = i;
	}
	return j;
}	/* implicit_cd */




int _cd(char *dir)
{
	register char *cp;
	int result = 0, tmpverbose;
	string str;

	if (dir == NULL)
		goto getrwd;
	/* Won't work because glob really is a ls, so 'cd pu*' will match
	 * pub/README, pub/file2, etc.
	 *	if (result = rem_glob_one(dir) < 0)
	 *	return result;
	 */
	if (strncmp(dir, "CDUP", (size_t) 4) == 0)
		(void) Strncpy(str, dir);
	else
		(void) sprintf(str, "CWD %s", dir);
	if (command(str) != 5) {
getrwd:
		/* (void) Strncpy(cwd, dir); */
		tmpverbose = verbose; verbose = V_QUIET;
		(void) command("PWD");
		verbose = tmpverbose;
		cp = rindex(reply_string, '\"');
		if (cp != NULL) {
			result = 1;
			*cp = '\0';
			cp = index(reply_string, '\"');
			if (cp != NULL)
				(void) Strncpy(cwd, ++cp);
		}
	}
	if (debug > 0)
		(void) printf("---> Current remote directory is \"%s\"\n", cwd);
	return (result);
}	/* _cd */




/*
 * Set current working directory
 * on local machine.
 */
int lcd(int argc, char **argv)
{
	if (argc < 2)
		argc++, argv[1] = home;
	if (argc != 2) {
		(void) printf("usage:%s local-directory\n", argv[0]);
		code = -1;
		return;
	}
	if (!globulize(&argv[1])) {
		code = -1;
		return;
	}
	if (chdir(argv[1]) < 0) {
		Perror(argv[1]);
		code = -1;
		return;
	}
	(void) get_cwd(lcwd, (int) sizeof(lcwd));
	if (IS_VVERBOSE) 
		(void) printf("Local directory now %s\n", lcwd);
	code = 0;
}	/* lcd */




/*
 * Delete a single file.
 */
int do_delete(int argc, char **argv)
{
	string str;

	if (argc < 2)
		argv = re_makeargv("(remote file to delete) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s remote-file\n", argv[0]);
		code = -1;
		return;
	}
	if (rem_glob_one(argv[1]) == 0) {
		(void) sprintf(str, "DELE %s", argv[1]);
		(void) command(str);
	}
}	/* do_delete */




/*
 * Delete multiple files.
 */
int mdelete(int argc, char **argv)
{
	char *cp;
	void (*oldintr)(int);
	string str;

	if (argc < 2)
		argv = re_makeargv("(remote-files) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s remote-files\n", argv[0]);
		code = -1;
		return;
	}
	mname = argv[0];
	activemcmd = 1;
	oldintr = signal(SIGINT, mabort);
	(void) setjmp(jabort);
	while ((cp = remglob(argv)) != NULL) {
		if (*cp == '\0') {
			activemcmd = 0;
			continue;
		}
		if (activemcmd && confirm(argv[0], cp)) {
			(void) sprintf(str, "DELE %s", cp);
			(void) command(str);
			if (!activemcmd && fromatty) {
				if (confirm("Continue with", "mdelete")) {
					activemcmd++;
				}
			}
		}
	}
	(void) signal(SIGINT, oldintr);
	activemcmd = 0;
}	/* mdelete */




/*
 * Rename a remote file.
 */
int renamefile(int argc, char **argv)
{
	string str;

	if (argc < 2)
		argv = re_makeargv("(from-name) ", &argc);
	if (argc < 2) {
usage:
		(void) printf("%s from-name to-name\n", argv[0]);
		code = -1;
		return;
	}
	if (argc < 3)
		argv = re_makeargv("(to-name) ", &argc);
	if (argc < 3)
		goto usage;
	if (rem_glob_one(argv[1]) < 0)
		return;
	(void) sprintf(str, "RNFR %s", argv[1]);
	if (command(str) == CONTINUE) {
		(void) sprintf(str, "RNTO %s", argv[1]);
		(void) command(str);
	}
}	/* renamefile */



/*
 * Get a directory listing
 * of remote files.
 */
int ls(int argc, char **argv)
{
	char		*whichcmd, *cp;
	str32		lsflags;
	string		remote, local, str;
	int			listmode, pagemode, i;

#ifdef REDIR
	register struct lslist *a, *b;
	for (a = lshead; a != NULL; ) {
		b = a->next;
    	if (a->string)
    		free(a->string);	/* free string */
    	free(a);          		/* free node */
    	a = b;
    }
    lshead = lstail = NULL;
#endif

	pagemode = 0;
	switch (**argv) {
		case 'p':							/* pls, pdir, pnlist */
			pagemode = 1;
			listmode = argv[0][1] == 'd';
			break;
		case 'd':							/* dir */
			listmode = 1;
			break;
		default:							/* ls, nlist */
			listmode = 0;
	}
	whichcmd = listmode ? "LIST" : "NLST";

	(void) strncpy(local, (pagemode ? pager : "-"), sizeof(local));
	remote[0] = lsflags[0] = 0;
	
	/* Possible scenarios:
	 *  1.	ls
	 *  2.	ls -flags
	 *  3.	ls directory
	 *  4.  ls -flags >outfile
	 *  5.  ls directory >outfile
     *  6.  ls -flags directory
	 *  7.  ls -flags directory >outfile
	 */

	for (i=1; i<argc; i++) {
		switch (argv[i][0]) {
			case '-': 
				/*
				 * If you give more than one set of flags, concat the each
				 * additional set to the first one (without the dash).
				 */
				(void) strncat(lsflags, (argv[i] + (lsflags[0] == '-')), sizeof(lsflags));
				break;
			case '|':
			case '>': (void) Strncpy(local, argv[i] + (argv[i][0] == '>')); break;
			default:  
				if (remote[0] != 0) {
					printf("ls: only one directory at a time please (or don't forget the '>' in >outfile).\n");
lsusage:
					(void) printf("usage: %s [-flags] [remote-directory] [>local-file]\n", argv[0]);
					code = -1;
					return;
				}
				/*
				 * In case you want to get a remote file called '--README--'
				 * or '>README,' you can use '\--README--' and '\>README.'
				 */
				(void) strncpy(remote, ((argv[i][0] == '\\') && (argv[i][1] != 0)) ? argv[i] + 1 : argv[i], sizeof(remote));
		}	/* end switch */	
	}		/* end loop */

	/*
	 *	If we are given an ls with some flags, make sure we use 
	 *	columnized output (-C) unless one column output (-1) is
	 *	specified.
	 */
	if (!listmode) {
		if (lsflags[0] != 0) {
			(void) Strncpy(str, lsflags);
			for (cp = str + 1; *cp; cp++)
				if (*cp == '1')
					goto aa;
			(void) sprintf(lsflags, "-FC%s", str + 1);
		} else {
			if (remote_is_unix)
				(void) strcpy(lsflags, "-FC");
		}
	}
aa:	if (strcmp(local, "-") != 0) {
		char *lp = local;

		if ((!globulize(&lp)) || ((local[0] != '|') && (!confirm("Output to local-file: ", local)))) {
			code = -1;
			return;
		} 
		if (lp != local) {
			(void) Strncpy(str, lp);
			(void) Strncpy(local, str);
		}
	}
#ifdef REDIR
	is_ls=1; /* tells getreply() to buffer output on a lshead list */
#endif
	(void) Strncpy(str, remote);
	if (lsflags[0] && remote[0])
		(void) sprintf(remote, "%s%c%s", lsflags, LS_FLAGS_AND_FILE, str);
	else
		(void) strncpy(remote, lsflags[0] ? lsflags : str, sizeof(remote));
	recvrequest(whichcmd, local, (remote[0] == 0 ? NULL : remote), "w");
#ifdef REDIR
	is_ls=0;
#endif
}	/* ls */




/*
 * Get a directory listing
 * of multiple remote files.
 */
int mls(int argc, char **argv)
{
	char *cmd, mode[1], *dest;
	int i;
	void (*oldintr)(int);

	if (argc < 2)
		argv = re_makeargv("(remote-files) ", &argc);
	if (argc < 3)
		argv = re_makeargv("(local-file) ", &argc);
	if (argc < 3) {
		(void) printf("usage:%s remote-files local-file\n", argv[0]);
		code = -1;
		return;
	}
	dest = argv[argc - 1];
	argv[argc - 1] = NULL;
	if (strcmp(dest, "-") && *dest != '|')
		if (!globulize(&dest) || !confirm("output to local-file:", dest)) {
			code = -1;
			return;
	}
	cmd = argv[0][1] != 'd' ? "NLST" : "LIST";
	mname = argv[0];
	activemcmd = 1;
	oldintr = signal(SIGINT, mabort);
	(void) setjmp(jabort);
	for (i = 1; activemcmd && i < argc-1; ++i) {
		*mode = (i == 1) ? 'w' : 'a';
		recvrequest(cmd, dest, argv[i], mode);
		if (!activemcmd && fromatty) {
			if (confirm("Continue with", argv[0])) {
				activemcmd++;
			}
		}
	}
	(void) signal(SIGINT, oldintr);
	activemcmd = 0;
}	/* mls */




/*
 * Do a shell escape
 */
/*ARGSUSED*/
int shell(int argc, char **argv)
{
	int				pid;
	void			(*old1)(int), (*old2)(int);
	char			*theShell, *namep;
#ifndef U_WAIT
	int				Status;
#else
	union wait		Status;
#endif
	string			str;

	old1 = signal (SIGINT, SIG_IGN);
	old2 = signal (SIGQUIT, SIG_IGN);
	if ((pid = fork()) == 0) {
		for (pid = 3; pid < 20; pid++)
			(void) close(pid);
		(void) signal(SIGINT, SIG_DFL);
		(void) signal(SIGQUIT, SIG_DFL);
		if ((theShell = getenv("SHELL")) == NULL)
			theShell = uinfo.shell;
		if (theShell == NULL)
			theShell = "/bin/sh";
		namep = rindex(theShell, '/');
		if (namep == NULL)
			namep = theShell;
		(void) strcpy(str, "-");
		(void) strcat(str, ++namep);
		if (strcmp(namep, "sh") != 0)
			str[0] = '+';
		if (debug) {
			(void) printf ("%s\n", theShell);
			(void) fflush (stdout);
		}
		if (argc > 1)
			(void) execl(theShell, str, "-c", altarg, (char *)0);
		else
			(void) execl(theShell, str, (char *)0);
		Perror(theShell);
		code = -1;
		exit(1);
		}
	if (pid > 0)
		while (wait((void *) &Status) != pid)
			;
	(void) signal(SIGINT, old1);
	(void) signal(SIGQUIT, old2);
	if (pid == -1) {
		Perror("Try again later");
		code = -1;
	} else code = 0;
	return (0);
}	/* shell */




/*
 * Send new user information (re-login)
 */
int do_user(int argc, char **argv)
{
	char			acct[80];
	int				n, aflag = 0;
	string			str;

	if (argc < 2)
		argv = re_makeargv("(username) ", &argc);
	if (argc > 4) {
		(void) printf("usage: %s username [password] [account]\n", argv[0]);
		code = -1;
		return (0);
	}
	(void) sprintf(str, "USER %s", argv[1]);
	n = command(str);
	if (n == CONTINUE) {
		if (argc < 3 )
			argv[2] = Getpass("Password: "), argc++;
		(void) sprintf(str, "PASS %s", argv[2]);
		n = command(str);
	}
	if (n == CONTINUE) {
		if (argc < 4) {
			(void) printf("Account: "); (void) fflush(stdout);
			(void) FGets(acct, stdin);
			acct[strlen(acct) - 1] = '\0';
			argv[3] = acct; argc++;
		}
		(void) sprintf(str, "ACCT %s", argv[3]);
		n = command(str);
		aflag++;
	}
	if (n != COMPLETE) {
		(void) fprintf(stdout, "Login failed.\n");
		return (0);
	}
	if (!aflag && argc == 4) {
		(void) sprintf(str, "ACCT %s", argv[3]);
		(void) command(str);
	}
	return (1);
}	/* user */




/*
 * Print working directory.
 */
/*ARGSUSED*/
int pwd(int argc, char **argv)
{
	int tmpverbose = verbose;
	verbose = V_VERBOSE;
	(void) command("PWD");
	verbose = tmpverbose;
}	/* pwd */




/*
 * Make a directory.
 */
int makedir(int argc, char **argv)
{
	string str;

	if (argc < 2)
		argv = re_makeargv("(directory-name) ", &argc);
	if (argc < 2) {
		(void) printf("usage: %s directory-name\n", argv[0]);
		code = -1;
		return;
	}
	(void) sprintf(str, "MKD %s", argv[1]);
	(void) command(str);
}	/* makedir */




/*
 * Remove a directory.
 */
int removedir(int argc, char **argv)
{
	string str;
	if (argc < 2)
		argv = re_makeargv("(directory-name) ", &argc);
	if (argc < 2) {
		(void) printf("usage: %s directory-name\n", argv[0]);
		code = -1;
		return;
	}
	if (rem_glob_one(argv[1]) == 0) {
		(void) sprintf(str, "RMD %s", argv[1]);
		(void) command(str);
	}
}	/* removedir */




/*
 * Send a line, verbatim, to the remote machine.
 */
int quote(int argc, char **argv)
{
	int i, tmpverbose;
	string str;

	if (argc < 2)
		argv = re_makeargv("(command line to send) ", &argc);
	if (argc < 2) {
		(void) printf("usage: %s line-to-send\n", argv[0]);
		code = -1;
		return;
	}
	(void) Strncpy(str, argv[1]);
	for (i = 2; i < argc; i++) {
		(void) Strncat(str, " ");
		(void) Strncat(str, argv[i]);
	}
	tmpverbose = verbose;
	verbose = V_VERBOSE;
	if (command(str) == PRELIM) {
		while (getreply(0) == PRELIM);
	}
	verbose = tmpverbose;
}	/* quote */




/*
 * Ask the other side for help.
 */
int rmthelp(int argc, char **argv)
{
	int oldverbose = verbose;
	string str;

	verbose = V_VERBOSE;
	if (argc == 1) (void) command("HELP");
	else {
		(void) sprintf(str, "HELP %s", argv[1]);
		(void) command(str);
	}
	verbose = oldverbose;
}	/* rmthelp */




/*
 * Terminate session and exit.
 */
/*ARGSUSED*/
int quit(int argc, char **argv)
{
	close_up_shop();
	trim_log();
	exit(0);
}	/* quit */



void close_streams(int wantShutDown)
{
	if (cout != NULL) {
		if (wantShutDown)
			(void) shutdown(fileno(cout), 1+1);
		(void) fclose(cout);
		cout = NULL;
	}
	if (cin != NULL) {
		if (wantShutDown)
			(void) shutdown(fileno(cin), 1+1);
		(void) fclose(cin);
		cin = NULL;
	}
}	/* close_streams */




/*
 * Terminate session, but don't exit.
 */
/*ARGSUSED*/
int disconnect(int argc, char **argv)
{
#ifdef SYSLOG
	syslog (LOG_INFO, "%s disconnected from %s.", uinfo.username, hostname);
#endif

	(void) command("QUIT");
	close_streams(0);
	hostname[0] = cwd[0] = 0;
	connected = 0;
	data = -1;
	macnum = 0;
}	/* disconnect */




int confirm(char *cmd, char *file)
{
	str32 str;

	if (!fromatty || (activemcmd && !mprompt))
		return 1;
	(void) printf("%s %s? ", cmd, file);
	(void) fflush(stdout);
	(void) Gets(str, sizeof(str));
	return (*str != 'n' && *str != 'N');
}	/* confirm */



void
fatal(char *msg)
{
	(void) fprintf(stderr, "%s: %s\n", progname, msg);
	close_up_shop();
	exit(1);
}	/* fatal */



void
close_up_shop(void)
{
	static int only_once = 0;
	if (only_once++ > 0)
		return;
	if (connected)
		disconnect(0, NULL);
	if (logf != NULL) {
		(void) fclose(logf);
		logf = NULL;
	}
}	/* close_up_shop */




/*
 * Glob a local file name specification with
 * the expectation of a single return value.
 * Can't control multiple values being expanded
 * from the expression, we return only the first.
 */
int globulize(char **cpp)
{
	char **globbed;

	globbed = glob(*cpp);
	if (globerr != NULL) {
		(void) printf("%s: %s\n", *cpp, globerr);
		if (globbed) {
			blkfree(globbed);
			free(globbed);
		}
		return (0);
	}
	if (globbed) {
		*cpp = *globbed++;
		/* don't waste too much memory */
		if (*globbed) {
			blkfree(globbed);
			free(globbed);
		}
	}
	return (1);
}	/* globulize */



/* change directory to perent directory */
/*ARGSUSED*/
int cdup(int argc, char **argv)
{
	(void) _cd("CDUP");
}


/* show remote system type */
/*ARGSUSED*/
int syst(int argc, char **argv)
{
	(void) command("SYST");
}




int make_macro(char *name, FILE *fp)
{
	char			*tmp;
	char			*cp;
	string			str;
	size_t			len;

	if (macnum == MAXMACROS) {
		(void) fprintf(stderr, "Limit of %d macros have already been defined.\n", MAXMACROS);
		return -1;
	}
	(void) strncpy(macros[macnum].mac_name, name, (size_t)8);
	if (macnum == 0)
		macros[macnum].mac_start = macbuf;
	else
		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
	tmp = macros[macnum].mac_start;
	while (1) {
		cp = fgets(str, sizeof(str) - 1, fp);
		if (cp == NULL) {
			/*
			 * If we had started a macro, we will say it is
			 * okay to skip the blank line delimiter if we
			 * are at the EOF.
			 */
			if (tmp > macros[macnum].mac_start)
				goto endmac;
			(void) fprintf(stderr, "No text supplied for macro \"%s.\"\n", name);
		}
		/* see if we have a 'blank' line: just whitespace. */
		while (*cp && isspace(*cp)) ++cp;
		if (*cp == '\0') {
			/* Blank line; end this macro. */
endmac:
			macros[macnum++].mac_end = tmp;
			return 0;
		}
		len = strlen(cp) + 1;	/* we need the \0 too. */
		if (tmp + len >= macbuf + MACBUFLEN) {
			(void) fprintf(stderr, "Macro \"%s\" not defined -- %d byte buffer exceeded.\n", name, MACBUFLEN);
			return -1;
		}
		(void) strcpy(tmp, cp);
		tmp += len;
	}
}	/* make_macro */




int macdef(int argc, char **argv)
{
	if (argc < 2)
		argv = re_makeargv("(macro name) ", &argc);
	if (argc != 2) {
		(void) printf("Usage: %s macro_name\n", argv[0]);
		domacro(0, NULL);
		return;
	}
	(void) printf("Enter macro line by line, terminating it with a blank line\n");
	code = make_macro(argv[1], stdin);
}	/* macdef */




int domacro(int argc, char **argv)
{
	register int			i, j;
	register char			*cp1, *cp2;
	int						count = 2, loopflg = 0;
	string					str;
	struct cmd				*c;

	if (argc < 2) {
		/* print macros. */
		if (macnum == 0)
			(void) printf("No macros defined.\n");
		else {
			printf("Current macro definitions:\n");
			for (i = 0; i < macnum; ++i) {
				(void) printf("%s:\n", macros[i].mac_name);
				cp1 = macros[i].mac_start;
				cp2 = macros[i].mac_end;
				while (cp1 < cp2) {
					(void) printf("   > ");
					while (cp1 < cp2 && *cp1)
						putchar(*cp1++);
					++cp1;
				}
			}
		}
		if (argc == 0) return;	/* called from macdef(), above. */
		argv = re_makeargv("(macro to run) ", &argc);
	}			
	if (argc < 2) {
		(void) printf("Usage: %s macro_name.\n", argv[0]);
		code = -1;
		return;
	}
	for (i = 0; i < macnum; ++i) {
		if (!strncmp(argv[1], macros[i].mac_name, (size_t) 9)) {
			break;
		}
	}
	if (i == macnum) {
		(void) printf("'%s' macro not found.\n", argv[1]);
		code = -1;
		return;
	}
	(void) Strncpy(str, line);
TOP:
	cp1 = macros[i].mac_start;
	while (cp1 != macros[i].mac_end) {
		while (isspace(*cp1)) {
			cp1++;
		}
		cp2 = line;
		while (*cp1 != '\0') {
		      switch(*cp1) {
		   	    case '\\':
				 *cp2++ = *++cp1;
				 break;
			    case '$':
				 if (isdigit(*(cp1+1))) {
				    j = 0;
				    while (isdigit(*++cp1)) {
					  j = 10*j +  *cp1 - '0';
				    }
				    cp1--;
				    if (argc - 2 >= j) {
					(void) strcpy(cp2, argv[j+1]);
					cp2 += strlen(argv[j+1]);
				    }
				    break;
				 }
				 if (*(cp1+1) == 'i') {
					loopflg = 1;
					cp1++;
					if (count < argc) {
					   (void) strcpy(cp2, argv[count]);
					   cp2 += strlen(argv[count]);
					}
					break;
				}
				/* intentional drop through */
			    default:
				*cp2++ = *cp1;
				break;
		      }
		      if (*cp1 != '\0') {
					cp1++;
		      }
		}
		*cp2 = '\0';
		makeargv();
		c = getcmd(margv[0]);
		if ((c == (struct cmd *)-1) && !parsing_rc) {
			(void) printf("?Ambiguous command\n");
			code = -1;
		}
		else if (c == NULL && !parsing_rc) {
			(void) printf("?Invalid command\n");
			code = -1;
		} else if (c->c_conn && !connected) {
			(void) printf("Not connected.\n");
			code = -1;
		} else {
			if (IS_VVERBOSE)
				(void) printf("%s\n",line);
			(*c->c_handler)(margc, margv);
			(void) strcpy(line, str);
			makeargv();
			argc = margc;
			argv = margv;
		}
		if (cp1 != macros[i].mac_end) {
			cp1++;
		}
	}
	if (loopflg && ++count < argc) {
		goto TOP;
	}
}	/* domacro */



/*
 * get size of file on remote machine
 */
int sizecmd(int argc, char **argv)
{
	string str;
	int oldv;

	if (argc < 2)
		argv = re_makeargv("(remote-file) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s filename\n", argv[0]);
		code = -1;
		return;
	}
	if (rem_glob_one(argv[1]) == 0) {
		(void) sprintf(str, "SIZE %s", argv[1]);
		oldv = verbose;
		verbose = V_VERBOSE;
		(void) command(str);
		verbose = oldv;
	}
}	/* sizecmd */




/*
 * get last modification time of file on remote machine
 */
int modtime(int argc, char **argv)
{
	int overbose;
	string str;

	if (argc < 2)
		argv = re_makeargv("(remote-file) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s filename\n", argv[0]);
		code = -1;
		return;
	}
	if (rem_glob_one(argv[1]) == 0) {
		overbose = verbose;
		if (debug == 0)
			verbose = V_QUIET;
		(void) sprintf(str, "MDTM %s", argv[1]);
		if (command(str) == COMPLETE) {
			int yy, mo, day, hour, min, sec;
			(void) sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
				&day, &hour, &min, &sec);
			/* might want to print this in local time */
			(void) printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
				mo, day, yy, hour, min, sec);
		} else
			(void) fputs(reply_string, stdout);
		verbose = overbose;
	}
}	/* modtime */



int lookup(int argc, char **argv)
{
	int i, j, by_name, result = 0;
	unsigned long addr;			/* address in host order */
	struct hostent *host;		/* structure returned by gethostbyaddr() */
	extern int h_errno;

	if (argc < 2)
		argv = re_makeargv("(sitename) ", &argc);
	if (argc < 2) {
		(void) printf("usages:\n\t%s <sitenames>\n\t%s <ip numbers>\n",
			argv[0], argv[0]);
	}

 	lasthostname[0] = 0;
	for (i=1; i<argc; i++) {
		/* does the argument look like an address? */
		if (4 == sscanf (argv[i], "%d.%d.%d.%d", &j, &j, &j, &j)) {
			/* ip */
  			if ((addr = inet_addr (argv[i])) == 0xffffffff) {
     			(void) fprintf(stderr, "## could not convert \"%s\" into a valid IP address.\n", argv[i]);
     			continue;
     		}
			host = gethostbyaddr ((char *) &addr, 4, AF_INET);
			by_name = 0;
		} else {
			/* name */
			host = gethostbyname (argv[i]);
			by_name = 1;
		}
		if (host == NULL) {
			if (NOT_VQUIET) {
				/* gethostxxx error */				
				result = h_errno;
				if (h_errno == HOST_NOT_FOUND) {
	     			(void) printf("%s: lookup error (%d).\n",
	     				argv[i], h_errno);
	     			result = h_errno;
	 			} else {
	     			(void) printf("%s \"%s\"\n",
	     				(by_name==0 ? "unknown address" : "unknown host"),
	     				argv[i]);
	     			result = 
	     				h_errno != 0 ? h_errno :
	     				-1;
				}
			}
 		} else {
 			if (*host->h_name)
 				Strncpy(lasthostname, host->h_name);
			if (NOT_VQUIET) {
				(void) printf("%-32s  ", *host->h_name ? host->h_name : "???");
				if (*host->h_addr_list) {
					unsigned long horder;
	
					horder = ntohl (*(unsigned long *) *(char **)host->h_addr_list);
					(void) printf ("%lu.%lu.%lu.%lu\n",
						(horder >> 24),
						(horder >> 16) & 0xff,
						(horder >> 8) & 0xff,
						horder & 0xff);
				}
				else (void) printf("???\n");
			}
		}
    }	/* loop thru all sites */
    return result;
}	/* lookup */




int getlocalhostname(char *host, size_t size)
{
	int oldv, r;
	char *argv[2];

#ifdef HOSTNAME
	(void) strncpy(host, HOSTNAME, size);
	return 0;
#else
	*host = 0;
	if ((r = gethostname(host, size)) == 0) {
		oldv = verbose;
		verbose = V_QUIET;
		argv[0] = "lookup";
		(void) sprintf(line, "lookup %s", host);
		(void) makeargv();
		if (lookup(margc, margv) == 0 && lasthostname[0])
			(void) strncpy(host, lasthostname, size);
		verbose = oldv;
	}
	return r;
#endif
}	/* getlocalhostname */




/*
 * show status on remote machine
 */
int rmtstatus(int argc, char **argv)
{
	string str;
	if (argc > 1) {
		(void) sprintf(str, "STAT %s" , argv[1]);
		(void) command(str);
	} else (void) command("STAT");
}	/* rmtstatus */




/*
 * create an empty file on remote machine.
 */
int create(int argc, char **argv)
{
	string			str;
	FILE			*ftemp;

	if (argc < 2)
		argv = re_makeargv("(remote-file) ", &argc);
	if (argc < 2) {
		(void) printf("usage:%s filename\n", argv[0]);
		code = -1;
		return;
	}
	(void) tmp_name(str);
	ftemp = fopen(str, "w");
	/* (void) fputc('x', ftemp); */
	(void) fclose(ftemp);
	creating = 1;
	sendrequest("STOR", str, argv[1]);
	creating = 0;
	(void) unlink(str);
}	/* create */




/* show version info */
/*ARGSUSED*/
int show_version(int argc, char **argv)
{
	char	*DStrs[20];
	int		nDStrs = 0, i, j;

	(void) printf("%s\n%-30s %s\n", version, "Author:",
		"Mike Gleason, NCEMRSoft (mgleason@cse.unl.edu).");

/* Now entering CPP hell... */
#ifdef __DATE__
	(void) printf("%-30s %s\n", "Compile Date:", __DATE__);
#endif
	(void) printf("%-30s %s (%s)\n", "Operating System:",
#ifdef System
	System,
#else
#	ifdef unix
	"UNIX",
#	else
	"??",
#	endif
#endif
#ifdef SYSV
		"SYSV");
#else
#	ifdef BSD
			"BSD");
#	else
			"neither BSD nor SYSV?");
#	endif
#endif

	/* Show which CPP symbols were used in compilation. */
#ifdef __GNUC__
	DStrs[nDStrs++] = "__GNUC__";
#endif
#ifdef RINDEX
	DStrs[nDStrs++] = "RINDEX";
#endif
#ifdef CURSES
	DStrs[nDStrs++] = "CURSES";
#endif
#ifdef HERROR
	DStrs[nDStrs++] = "HERROR";
#endif
#ifdef U_WAIT
	DStrs[nDStrs++] = "U_WAIT";
#endif
#if defined(CONST) || defined(const)
	DStrs[nDStrs++] = "CONST";
#endif
#ifdef GETPASS
	DStrs[nDStrs++] = "GETPASS";
#endif
#ifdef GETCWDSIZET
	DStrs[nDStrs++] = "GETCWDSIZET";
#endif
#ifdef HOSTNAME
	DStrs[nDStrs++] = "HOSTNAME";
#endif
#ifdef SYSDIRH
	DStrs[nDStrs++] = "SYSDIRH";
#endif
#ifdef SYSSELECTH
	DStrs[nDStrs++] = "SYSSELECTH";
#endif
#ifdef NO_UNISTDH 
	DStrs[nDStrs++] = "NO_UNISTDH";
#endif
#ifdef NO_STDLIBH
	DStrs[nDStrs++] = "NO_STDLIBH";
#endif
#ifdef SYSLOG 
	DStrs[nDStrs++] = "SYSLOG";
#endif
#ifdef REDIR
	DStrs[nDStrs++] = "REDIR";
#endif
#ifdef BAD_INETADDR
	DStrs[nDStrs++] = "BAD_INETADDR";
#endif
#ifdef SGTTYB
	DStrs[nDStrs++] = "SGTTYB";
#endif
#ifdef TERMIOS
	DStrs[nDStrs++] = "TERMIOS";
#endif
#ifdef _POSIX_SOURCE
	DStrs[nDStrs++] = "_POSIX_SOURCE";
#endif


	(void) printf ("\nCompile Options:\n");
	for (i=j=0; i<nDStrs; i++) {
		if (j == 0)
			(void) printf("    ");
		(void) printf("%-15s", DStrs[i]);
		if (++j == 4) {
			j = 0;
			(void) putchar('\n');
		}
	}
	if (j != 0)
		(void) putchar('\n');
	
	(void) printf("\nDefaults:\n");
	(void) printf("\
    Xfer Buf Size: %8d   Debug: %d   MPrompt: %d   Verbosity: %d\n\
    Prompt: %s   Pager: %s  ZCat: %s\n\
    Logname: %s   Logging: %d   Type: %s   Cmd Len: %d\n\
    Recv Line Len: %d   #Macros: %d   Macbuf: %d  Auto-Binary: %d\n\
    Redial Delay: %d  New Mail Message: \"%s\"\n",
		MAX_XFER_BUFSIZE, dDEBUG, dMPROMPT, dVERBOSE,
		dPROMPT, dPAGER, ZCAT,
		dLOGNAME, dLOGGING, dTYPESTR, CMDLINELEN,
		RECEIVEDLINELEN, MAXMACROS, MACBUFLEN, dAUTOBINARY,
		dREDIALDELAY, NEWMAILMESSAGE
	);
}	/* show_version */



void Perror(char *s)
{
	extern int errno;

	if (NOT_VQUIET) {
		if (s != NULL)
			(void) fprintf(stderr, "NcFTP: %s (%d): ", s, errno);
		perror(NULL);
	}
}	/* Perror */



#ifdef REDIR
/*ARGSUSED*/
int showlsbuffer(int argc, char **argv)
{
	register struct lslist *a = lshead;
	int pagemode;
	FILE *fp;
	void (*oldintp)(int);

	if (a == NULL)
		return;
	pagemode= (**argv) == 'p' && pager[0] == '|';
	if (pagemode) {
		fp = popen(pager + 1, "w");
		if (!fp) {
			Perror(pager + 1);
			return;
		}
	} else
		fp = stdout;
	oldintp = signal(SIGPIPE, SIG_IGN);
	while (a) {
		if (a->string)
			(void) fprintf(fp, "%s\n", a->string);
		a = a->next;
	}
	if (pagemode)
		(void) pclose(fp);
	if (oldintp)
		signal(SIGPIPE, oldintp);
}	/* showlsbuffer */
#endif

/* eof cmds.c */
