mirror of
https://kernel.googlesource.com/pub/scm/network/tftp/tftp-hpa
synced 2025-05-03 13:29:53 +03:00

I have encountered a situation where I would like to use tftp-hpa to retrieve a file that resides within an absolute path containing a ':' character. Ala, "tftp foobar -c get C:2/tftpdir/myfile". Since the tftp client automatically converts the host:file syntax, I get an error "tftp: C: Unknown host". I made a chage to the tftp client code to add a literal mode (-l), that prevents the special treatment of the ':' character. I've attached a patch set for main.c and tftp.1.in for your consideration. I'm not sure how many folks run into this, but it may be somewhat common for VxWorks and perhaps MSDOS users. Thanks, Craig Johnston
916 lines
19 KiB
C
916 lines
19 KiB
C
/* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $ */
|
|
/* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1983, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "common/tftpsubs.h"
|
|
|
|
#ifndef lint
|
|
static const char *copyright UNUSED =
|
|
"@(#) Copyright (c) 1983, 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n";
|
|
/* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */
|
|
/* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */
|
|
static const char *rcsid UNUSED =
|
|
"tftp-hpa $Id$";
|
|
#endif /* not lint */
|
|
|
|
/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
|
|
|
|
/*
|
|
* TFTP User Program -- Command Interface.
|
|
*/
|
|
#include <sys/file.h>
|
|
#include <ctype.h>
|
|
#include <netdb.h>
|
|
#ifdef WITH_READLINE
|
|
#include <readline/readline.h>
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
#include <readline/history.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "extern.h"
|
|
|
|
#define TIMEOUT 5 /* secs between rexmt's */
|
|
#define LBUFLEN 200 /* size of input buffer */
|
|
|
|
struct modes {
|
|
const char *m_name;
|
|
const char *m_mode;
|
|
int m_openflags;
|
|
};
|
|
|
|
static const struct modes modes[] = {
|
|
{ "netascii", "netascii", O_TEXT },
|
|
{ "ascii", "netascii", O_TEXT },
|
|
{ "octet", "octet", O_BINARY },
|
|
{ "binary", "octet", O_BINARY },
|
|
{ "image", "octet", O_BINARY },
|
|
{ 0, 0, 0 }
|
|
};
|
|
#define MODE_OCTET (&modes[2])
|
|
#define MODE_NETASCII (&modes[0])
|
|
#define MODE_DEFAULT MODE_NETASCII
|
|
|
|
struct sockaddr_in peeraddr;
|
|
int f;
|
|
u_short port;
|
|
int trace;
|
|
int verbose;
|
|
int literal;
|
|
int connected;
|
|
const struct modes *mode;
|
|
#ifdef WITH_READLINE
|
|
char *line = NULL;
|
|
#else
|
|
char line[LBUFLEN];
|
|
#endif
|
|
int margc;
|
|
char *margv[20];
|
|
const char *prompt = "tftp> ";
|
|
sigjmp_buf toplevel;
|
|
void intr(int);
|
|
struct servent *sp;
|
|
int portrange = 0;
|
|
unsigned int portrange_from = 0;
|
|
unsigned int portrange_to = 0;
|
|
|
|
void get (int, char **);
|
|
void help (int, char **);
|
|
void modecmd (int, char **);
|
|
void put (int, char **);
|
|
void quit (int, char **);
|
|
void setascii (int, char **);
|
|
void setbinary (int, char **);
|
|
void setpeer (int, char **);
|
|
void setrexmt (int, char **);
|
|
void settimeout (int, char **);
|
|
void settrace (int, char **);
|
|
void setverbose (int, char **);
|
|
void status (int, char **);
|
|
void setliteral (int, char **);
|
|
|
|
static void command (void);
|
|
|
|
static void getusage (char *);
|
|
static void makeargv (void);
|
|
static void putusage (char *);
|
|
static void settftpmode (const struct modes *);
|
|
|
|
#define HELPINDENT (sizeof("connect"))
|
|
|
|
struct cmd {
|
|
const char *name;
|
|
const char *help;
|
|
void (*handler) (int, char **);
|
|
};
|
|
|
|
struct cmd cmdtab[] = {
|
|
{ "connect",
|
|
"connect to remote tftp",
|
|
setpeer },
|
|
{ "mode",
|
|
"set file transfer mode",
|
|
modecmd },
|
|
{ "put",
|
|
"send file",
|
|
put },
|
|
{ "get",
|
|
"receive file",
|
|
get },
|
|
{ "quit",
|
|
"exit tftp",
|
|
quit },
|
|
{ "verbose",
|
|
"toggle verbose mode",
|
|
setverbose },
|
|
{ "trace",
|
|
"toggle packet tracing",
|
|
settrace },
|
|
{ "literal",
|
|
"toggle literal mode, ignore ':' in file name",
|
|
setliteral },
|
|
{ "status",
|
|
"show current status",
|
|
status },
|
|
{ "binary",
|
|
"set mode to octet",
|
|
setbinary },
|
|
{ "ascii",
|
|
"set mode to netascii",
|
|
setascii },
|
|
{ "rexmt",
|
|
"set per-packet transmission timeout",
|
|
setrexmt },
|
|
{ "timeout",
|
|
"set total retransmission timeout",
|
|
settimeout },
|
|
{ "?",
|
|
"print help information",
|
|
help },
|
|
{ "help",
|
|
"print help information",
|
|
help },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
struct cmd *getcmd(char *);
|
|
char *tail(char *);
|
|
|
|
char *xstrdup(const char *);
|
|
|
|
const char *program;
|
|
|
|
static inline void usage(int errcode)
|
|
{
|
|
fprintf(stderr, "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n", program);
|
|
exit(errcode);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct sockaddr_in s_in;
|
|
int arg;
|
|
static int pargc, peerargc;
|
|
static int iscmd = 0;
|
|
char **pargv;
|
|
const char *optx;
|
|
char *peerargv[3];
|
|
|
|
program = argv[0];
|
|
|
|
mode = MODE_DEFAULT;
|
|
|
|
peerargv[0] = argv[0];
|
|
peerargc = 1;
|
|
|
|
for ( arg = 1 ; !iscmd && arg < argc ; arg++ ) {
|
|
if ( argv[arg][0] == '-' ) {
|
|
for ( optx = &argv[arg][1] ; *optx ; optx++ ) {
|
|
switch ( *optx ) {
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'V':
|
|
/* Print version and configuration to stdout and exit */
|
|
printf("%s\n", TFTP_CONFIG_STR);
|
|
exit(0);
|
|
case 'l':
|
|
literal = 1;
|
|
break;
|
|
case 'm':
|
|
if ( ++arg >= argc )
|
|
usage(EX_USAGE);
|
|
{
|
|
const struct modes *p;
|
|
|
|
for ( p = modes ; p->m_name ; p++ ) {
|
|
if (!strcmp(argv[arg], p->m_name))
|
|
break;
|
|
}
|
|
if (p->m_name) {
|
|
settftpmode(p);
|
|
} else {
|
|
fprintf(stderr, "%s: invalid mode: %s\n", argv[0], argv[arg]);
|
|
exit(EX_USAGE);
|
|
}
|
|
}
|
|
break;
|
|
case 'c':
|
|
iscmd = 1;
|
|
break;
|
|
case 'R':
|
|
if ( ++arg >= argc )
|
|
usage(EX_USAGE);
|
|
if ( sscanf(argv[arg], "%u:%u", &portrange_from, &portrange_to) != 2 ||
|
|
portrange_from > portrange_to || portrange_to > 65535 ) {
|
|
fprintf(stderr, "Bad port range: %s\n", argv[arg]);
|
|
exit(EX_USAGE);
|
|
}
|
|
portrange = 1;
|
|
break;
|
|
case 'h':
|
|
default:
|
|
usage(*optx == 'h' ? 0 : EX_USAGE);
|
|
}
|
|
}
|
|
} else {
|
|
if ( peerargc >= 3 )
|
|
usage(EX_USAGE);
|
|
|
|
peerargv[peerargc++] = argv[arg];
|
|
}
|
|
}
|
|
|
|
pargv = argv + arg;
|
|
pargc = argc - arg;
|
|
|
|
sp = getservbyname("tftp", "udp");
|
|
if (sp == 0) {
|
|
/* Use canned values */
|
|
if (verbose)
|
|
fprintf(stderr, "tftp: tftp/udp: unknown service, faking it...\n");
|
|
sp = xmalloc(sizeof(struct servent));
|
|
sp->s_name = (char *)"tftp";
|
|
sp->s_aliases = NULL;
|
|
sp->s_port = htons(IPPORT_TFTP);
|
|
sp->s_proto = (char *)"udp";
|
|
}
|
|
port = sp->s_port; /* Default port */
|
|
f = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (f < 0) {
|
|
perror("tftp: socket");
|
|
exit(EX_OSERR);
|
|
}
|
|
bzero((char *)&s_in, sizeof (s_in));
|
|
s_in.sin_family = AF_INET;
|
|
if (pick_port_bind(f, &s_in, portrange_from, portrange_to)) {
|
|
perror("tftp: bind");
|
|
exit(EX_OSERR);
|
|
}
|
|
bsd_signal(SIGINT, intr);
|
|
|
|
if ( peerargc ) {
|
|
/* Set peer */
|
|
if (sigsetjmp(toplevel,1) != 0)
|
|
exit(EX_NOHOST);
|
|
setpeer(peerargc, peerargv);
|
|
}
|
|
|
|
if ( iscmd && pargc ) {
|
|
/* -c specified; execute command and exit */
|
|
struct cmd *c;
|
|
|
|
if (sigsetjmp(toplevel,1) != 0)
|
|
exit(EX_UNAVAILABLE);
|
|
|
|
c = getcmd(pargv[0]);
|
|
if ( c == (struct cmd *)-1 || c == (struct cmd *)0 ) {
|
|
fprintf(stderr, "%s: invalid command: %s\n", argv[0], pargv[1]);
|
|
exit(EX_USAGE);
|
|
}
|
|
(*c->handler)(pargc, pargv);
|
|
exit(0);
|
|
}
|
|
|
|
#ifdef WITH_READLINE
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
using_history();
|
|
#endif
|
|
#endif
|
|
|
|
if (sigsetjmp(toplevel,1) != 0)
|
|
(void)putchar('\n');
|
|
command();
|
|
|
|
return 0; /* Never reached */
|
|
}
|
|
|
|
char *hostname;
|
|
|
|
/* Called when a command is incomplete; modifies
|
|
the global variable "line" */
|
|
static void
|
|
getmoreargs(const char *partial, const char *mprompt)
|
|
{
|
|
#ifdef WITH_READLINE
|
|
char *eline;
|
|
int len, elen;
|
|
|
|
len = strlen(partial);
|
|
eline = readline(mprompt);
|
|
if (!eline)
|
|
exit(0); /* EOF */
|
|
|
|
elen = strlen(eline);
|
|
|
|
if (line) {
|
|
free(line);
|
|
line = NULL;
|
|
}
|
|
line = xmalloc(len+elen+1);
|
|
strcpy(line, partial);
|
|
strcpy(line+len, eline);
|
|
free(eline);
|
|
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
add_history(line);
|
|
#endif
|
|
#else
|
|
int len = strlen(partial);
|
|
|
|
strcpy(line, partial);
|
|
fputs(mprompt, stdout);
|
|
if ( fgets(line+len, LBUFLEN-len, stdin) == 0 )
|
|
if ( feof(stdin) )
|
|
exit(0); /* EOF */
|
|
#endif
|
|
}
|
|
|
|
void
|
|
setpeer(int argc, char *argv[])
|
|
{
|
|
struct hostent *host;
|
|
|
|
if (argc < 2) {
|
|
getmoreargs("connect ", "(to) ");
|
|
makeargv();
|
|
argc = margc;
|
|
argv = margv;
|
|
}
|
|
if ((argc < 2) || (argc > 3)) {
|
|
printf("usage: %s host-name [port]\n", argv[0]);
|
|
return;
|
|
}
|
|
|
|
host = gethostbyname(argv[1]);
|
|
if (host == 0) {
|
|
connected = 0;
|
|
printf("%s: unknown host\n", argv[1]);
|
|
return;
|
|
}
|
|
peeraddr.sin_family = host->h_addrtype;
|
|
bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
|
|
hostname = xstrdup(host->h_name);
|
|
|
|
port = sp->s_port;
|
|
if (argc == 3) {
|
|
struct servent *usp;
|
|
usp = getservbyname(argv[2], "udp");
|
|
if ( usp ) {
|
|
port = usp->s_port;
|
|
} else {
|
|
unsigned long myport;
|
|
char *ep;
|
|
myport = strtoul(argv[2], &ep, 10);
|
|
if (*ep || myport > 65535UL) {
|
|
printf("%s: bad port number\n", argv[2]);
|
|
connected = 0;
|
|
return;
|
|
}
|
|
port = htons((u_short)myport);
|
|
}
|
|
}
|
|
|
|
if (verbose) {
|
|
printf("Connected to %s (%s), port %u\n",
|
|
hostname, inet_ntoa(peeraddr.sin_addr),
|
|
(unsigned int)ntohs(port));
|
|
}
|
|
connected = 1;
|
|
}
|
|
|
|
void
|
|
modecmd(int argc, char *argv[])
|
|
{
|
|
const struct modes *p;
|
|
const char *sep;
|
|
|
|
if (argc < 2) {
|
|
printf("Using %s mode to transfer files.\n", mode->m_mode);
|
|
return;
|
|
}
|
|
if (argc == 2) {
|
|
for (p = modes; p->m_name; p++)
|
|
if (strcmp(argv[1], p->m_name) == 0)
|
|
break;
|
|
if (p->m_name) {
|
|
settftpmode(p);
|
|
return;
|
|
}
|
|
printf("%s: unknown mode\n", argv[1]);
|
|
/* drop through and print usage message */
|
|
}
|
|
|
|
printf("usage: %s [", argv[0]);
|
|
sep = " ";
|
|
for (p = modes; p->m_name; p++) {
|
|
printf("%s%s", sep, p->m_name);
|
|
if (*sep == ' ')
|
|
sep = " | ";
|
|
}
|
|
printf(" ]\n");
|
|
return;
|
|
}
|
|
|
|
void
|
|
setbinary(int argc, char *argv[])
|
|
{
|
|
(void)argc; (void)argv; /* Quiet unused warning */
|
|
settftpmode(MODE_OCTET);
|
|
}
|
|
|
|
void
|
|
setascii(int argc, char *argv[])
|
|
{
|
|
(void)argc; (void)argv; /* Quiet unused warning */
|
|
settftpmode(MODE_NETASCII);
|
|
}
|
|
|
|
static void
|
|
settftpmode(const struct modes *newmode)
|
|
{
|
|
mode = newmode;
|
|
if (verbose)
|
|
printf("mode set to %s\n", mode->m_mode);
|
|
}
|
|
|
|
|
|
/*
|
|
* Send file(s).
|
|
*/
|
|
void
|
|
put(int argc, char *argv[])
|
|
{
|
|
int fd;
|
|
int n;
|
|
char *cp, *targ;
|
|
|
|
if (argc < 2) {
|
|
getmoreargs("send ", "(file) ");
|
|
makeargv();
|
|
argc = margc;
|
|
argv = margv;
|
|
}
|
|
if (argc < 2) {
|
|
putusage(argv[0]);
|
|
return;
|
|
}
|
|
targ = argv[argc - 1];
|
|
if (!literal && strchr(argv[argc - 1], ':')) {
|
|
struct hostent *hp;
|
|
|
|
for (n = 1; n < argc - 1; n++)
|
|
if (strchr(argv[n], ':')) {
|
|
putusage(argv[0]);
|
|
return;
|
|
}
|
|
cp = argv[argc - 1];
|
|
targ = strchr(cp, ':');
|
|
*targ++ = 0;
|
|
hp = gethostbyname(cp);
|
|
if (hp == NULL) {
|
|
fprintf(stderr, "tftp: %s: ", cp);
|
|
herror((char *)NULL);
|
|
return;
|
|
}
|
|
bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length);
|
|
peeraddr.sin_family = hp->h_addrtype;
|
|
connected = 1;
|
|
hostname = xstrdup(hp->h_name);
|
|
}
|
|
if (!connected) {
|
|
printf("No target machine specified.\n");
|
|
return;
|
|
}
|
|
if (argc < 4) {
|
|
cp = argc == 2 ? tail(targ) : argv[1];
|
|
fd = open(cp, O_RDONLY|mode->m_openflags);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "tftp: "); perror(cp);
|
|
return;
|
|
}
|
|
if (verbose)
|
|
printf("putting %s to %s:%s [%s]\n",
|
|
cp, hostname, targ, mode->m_mode);
|
|
peeraddr.sin_port = port;
|
|
tftp_sendfile(fd, targ, mode->m_mode);
|
|
return;
|
|
}
|
|
/* this assumes the target is a directory */
|
|
/* on a remote unix system. hmmmm. */
|
|
cp = strchr(targ, '\0');
|
|
*cp++ = '/';
|
|
for (n = 1; n < argc - 1; n++) {
|
|
strcpy(cp, tail(argv[n]));
|
|
fd = open(argv[n], O_RDONLY|mode->m_openflags);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "tftp: "); perror(argv[n]);
|
|
continue;
|
|
}
|
|
if (verbose)
|
|
printf("putting %s to %s:%s [%s]\n",
|
|
argv[n], hostname, targ, mode->m_mode);
|
|
peeraddr.sin_port = port;
|
|
tftp_sendfile(fd, targ, mode->m_mode);
|
|
}
|
|
}
|
|
|
|
static void
|
|
putusage(char *s)
|
|
{
|
|
printf("usage: %s file ... host:target, or\n", s);
|
|
printf(" %s file ... target (when already connected)\n", s);
|
|
}
|
|
|
|
/*
|
|
* Receive file(s).
|
|
*/
|
|
void
|
|
get(int argc, char *argv[])
|
|
{
|
|
int fd;
|
|
int n;
|
|
char *cp;
|
|
char *src;
|
|
|
|
if (argc < 2) {
|
|
getmoreargs("get ", "(files) ");
|
|
makeargv();
|
|
argc = margc;
|
|
argv = margv;
|
|
}
|
|
if (argc < 2) {
|
|
getusage(argv[0]);
|
|
return;
|
|
}
|
|
if (!connected) {
|
|
for (n = 1; n < argc ; n++)
|
|
if (literal || strchr(argv[n], ':') == 0) {
|
|
getusage(argv[0]);
|
|
return;
|
|
}
|
|
}
|
|
for (n = 1; n < argc ; n++) {
|
|
src = strchr(argv[n], ':');
|
|
if (literal || src == NULL)
|
|
src = argv[n];
|
|
else {
|
|
struct hostent *hp;
|
|
|
|
*src++ = 0;
|
|
hp = gethostbyname(argv[n]);
|
|
if (hp == NULL) {
|
|
fprintf(stderr, "tftp: %s: ", argv[n]);
|
|
herror((char *)NULL);
|
|
continue;
|
|
}
|
|
bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
|
|
hp->h_length);
|
|
peeraddr.sin_family = hp->h_addrtype;
|
|
connected = 1;
|
|
hostname = xstrdup(hp->h_name);
|
|
}
|
|
if (argc < 4) {
|
|
cp = argc == 3 ? argv[2] : tail(src);
|
|
fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "tftp: "); perror(cp);
|
|
return;
|
|
}
|
|
if (verbose)
|
|
printf("getting from %s:%s to %s [%s]\n",
|
|
hostname, src, cp, mode->m_mode);
|
|
peeraddr.sin_port = port;
|
|
tftp_recvfile(fd, src, mode->m_mode);
|
|
break;
|
|
}
|
|
cp = tail(src); /* new .. jdg */
|
|
fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "tftp: "); perror(cp);
|
|
continue;
|
|
}
|
|
if (verbose)
|
|
printf("getting from %s:%s to %s [%s]\n",
|
|
hostname, src, cp, mode->m_mode);
|
|
peeraddr.sin_port = port;
|
|
tftp_recvfile(fd, src, mode->m_mode);
|
|
}
|
|
}
|
|
|
|
static void
|
|
getusage(char *s)
|
|
{
|
|
printf("usage: %s host:file host:file ... file, or\n", s);
|
|
printf(" %s file file ... file if connected\n", s);
|
|
}
|
|
|
|
int rexmtval = TIMEOUT;
|
|
|
|
void
|
|
setrexmt(int argc, char *argv[])
|
|
{
|
|
int t;
|
|
|
|
if (argc < 2) {
|
|
getmoreargs("rexmt-timeout ", "(value) ");
|
|
makeargv();
|
|
argc = margc;
|
|
argv = margv;
|
|
}
|
|
if (argc != 2) {
|
|
printf("usage: %s value\n", argv[0]);
|
|
return;
|
|
}
|
|
t = atoi(argv[1]);
|
|
if (t < 0)
|
|
printf("%s: bad value\n", argv[1]);
|
|
else
|
|
rexmtval = t;
|
|
}
|
|
|
|
int maxtimeout = 5 * TIMEOUT;
|
|
|
|
void
|
|
settimeout(int argc, char *argv[])
|
|
{
|
|
int t;
|
|
|
|
if (argc < 2) {
|
|
getmoreargs("maximum-timeout ", "(value) ");
|
|
makeargv();
|
|
argc = margc;
|
|
argv = margv;
|
|
}
|
|
if (argc != 2) {
|
|
printf("usage: %s value\n", argv[0]);
|
|
return;
|
|
}
|
|
t = atoi(argv[1]);
|
|
if (t < 0)
|
|
printf("%s: bad value\n", argv[1]);
|
|
else
|
|
maxtimeout = t;
|
|
}
|
|
|
|
void
|
|
setliteral(int argc, char *argv[])
|
|
{
|
|
(void)argc; (void)argv; /* Quiet unused warning */
|
|
literal = !literal;
|
|
printf("Literal mode %s.\n", literal ? "on" : "off");
|
|
}
|
|
|
|
void
|
|
status(int argc, char *argv[])
|
|
{
|
|
(void)argc; (void)argv; /* Quiet unused warning */
|
|
if (connected)
|
|
printf("Connected to %s.\n", hostname);
|
|
else
|
|
printf("Not connected.\n");
|
|
printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode,
|
|
verbose ? "on" : "off", trace ? "on" : "off", literal ? "on" : "off");
|
|
printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
|
|
rexmtval, maxtimeout);
|
|
}
|
|
|
|
void
|
|
intr(int sig)
|
|
{
|
|
(void)sig; /* Quiet unused warning */
|
|
|
|
bsd_signal(SIGALRM, SIG_IGN);
|
|
alarm(0);
|
|
siglongjmp(toplevel, -1);
|
|
}
|
|
|
|
char *
|
|
tail(char *filename)
|
|
{
|
|
char *s;
|
|
|
|
while (*filename) {
|
|
s = strrchr(filename, '/');
|
|
if (s == NULL)
|
|
break;
|
|
if (s[1])
|
|
return (s + 1);
|
|
*s = '\0';
|
|
}
|
|
return (filename);
|
|
}
|
|
|
|
/*
|
|
* Command parser.
|
|
*/
|
|
static void
|
|
command(void)
|
|
{
|
|
struct cmd *c;
|
|
|
|
for (;;) {
|
|
#ifdef WITH_READLINE
|
|
if ( line ) {
|
|
free(line);
|
|
line = NULL;
|
|
}
|
|
line = readline(prompt);
|
|
if ( !line )
|
|
exit(0); /* EOF */
|
|
#else
|
|
fputs(prompt, stdout);
|
|
if (fgets(line, LBUFLEN, stdin) == 0) {
|
|
if (feof(stdin)) {
|
|
exit(0);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
if ((line[0] == 0) || (line[0] == '\n'))
|
|
continue;
|
|
#ifdef WITH_READLINE
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
add_history(line);
|
|
#endif
|
|
#endif
|
|
makeargv();
|
|
if (margc == 0)
|
|
continue;
|
|
|
|
c = getcmd(margv[0]);
|
|
if (c == (struct cmd *)-1) {
|
|
printf("?Ambiguous command\n");
|
|
continue;
|
|
}
|
|
if (c == 0) {
|
|
printf("?Invalid command\n");
|
|
continue;
|
|
}
|
|
(*c->handler)(margc, margv);
|
|
}
|
|
}
|
|
|
|
struct cmd *
|
|
getcmd(char *name)
|
|
{
|
|
const char *p;
|
|
char *q;
|
|
struct cmd *c, *found;
|
|
int nmatches, longest;
|
|
|
|
longest = 0;
|
|
nmatches = 0;
|
|
found = 0;
|
|
for (c = cmdtab; (p = c->name) != NULL; c++) {
|
|
for (q = name; *q == *p++; q++)
|
|
if (*q == 0) /* exact match? */
|
|
return (c);
|
|
if (!*q) { /* the name was a prefix */
|
|
if (q - name > longest) {
|
|
longest = q - name;
|
|
nmatches = 1;
|
|
found = c;
|
|
} else if (q - name == longest)
|
|
nmatches++;
|
|
}
|
|
}
|
|
if (nmatches > 1)
|
|
return ((struct cmd *)-1);
|
|
return (found);
|
|
}
|
|
|
|
/*
|
|
* Slice a string up into argc/argv.
|
|
*/
|
|
static void
|
|
makeargv(void)
|
|
{
|
|
char *cp;
|
|
char **argp = margv;
|
|
|
|
margc = 0;
|
|
for (cp = line; *cp;) {
|
|
while (isspace(*cp))
|
|
cp++;
|
|
if (*cp == '\0')
|
|
break;
|
|
*argp++ = cp;
|
|
margc += 1;
|
|
while (*cp != '\0' && !isspace(*cp))
|
|
cp++;
|
|
if (*cp == '\0')
|
|
break;
|
|
*cp++ = '\0';
|
|
}
|
|
*argp++ = 0;
|
|
}
|
|
|
|
void
|
|
quit(int argc, char *argv[])
|
|
{
|
|
(void)argc; (void)argv; /* Quiet unused warning */
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
* Help command.
|
|
*/
|
|
void
|
|
help(int argc, char *argv[])
|
|
{
|
|
struct cmd *c;
|
|
|
|
printf("%s\n", VERSION);
|
|
|
|
if (argc == 1) {
|
|
printf("Commands may be abbreviated. Commands are:\n\n");
|
|
for (c = cmdtab; c->name; c++)
|
|
printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
|
|
return;
|
|
}
|
|
while (--argc > 0) {
|
|
char *arg;
|
|
arg = *++argv;
|
|
c = getcmd(arg);
|
|
if (c == (struct cmd *)-1)
|
|
printf("?Ambiguous help command %s\n", arg);
|
|
else if (c == (struct cmd *)0)
|
|
printf("?Invalid help command %s\n", arg);
|
|
else
|
|
printf("%s\n", c->help);
|
|
}
|
|
}
|
|
|
|
void
|
|
settrace(int argc, char *argv[])
|
|
{
|
|
(void)argc; (void)argv; /* Quiet unused warning */
|
|
|
|
trace = !trace;
|
|
printf("Packet tracing %s.\n", trace ? "on" : "off");
|
|
}
|
|
|
|
void
|
|
setverbose(int argc, char *argv[])
|
|
{
|
|
(void)argc; (void)argv; /* Quiet unused warning */
|
|
|
|
verbose = !verbose;
|
|
printf("Verbose mode %s.\n", verbose ? "on" : "off");
|
|
}
|