121740Sdist /* 262012Sbostic * Copyright (c) 1985, 1989, 1993 362012Sbostic * The Regents of the University of California. All rights reserved. 433737Sbostic * 542666Sbostic * %sccs.include.redist.c% 621740Sdist */ 721740Sdist 810297Ssam #ifndef lint 962012Sbostic static char copyright[] = 1062012Sbostic "@(#) Copyright (c) 1985, 1989, 1993\n\ 1162012Sbostic The Regents of the University of California. All rights reserved.\n"; 1233737Sbostic #endif /* not lint */ 1310297Ssam 1421740Sdist #ifndef lint 15*66670Spendry static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 04/02/94"; 1633737Sbostic #endif /* not lint */ 1721740Sdist 1810297Ssam /* 1910297Ssam * FTP User Program -- Command Interface. 2010297Ssam */ 21*66670Spendry /*#include <sys/ioctl.h>*/ 22*66670Spendry #include <sys/types.h> 2310297Ssam #include <sys/socket.h> 2410297Ssam 2512398Ssam #include <arpa/ftp.h> 2612398Ssam 2710297Ssam #include <ctype.h> 28*66670Spendry #include <err.h> 2910297Ssam #include <netdb.h> 3011352Ssam #include <pwd.h> 31*66670Spendry #include <signal.h> 32*66670Spendry #include <stdio.h> 33*66670Spendry #include <stdlib.h> 34*66670Spendry #include <unistd.h> 3510297Ssam 36*66670Spendry #include "ftp_var.h" 3710297Ssam 38*66670Spendry int 3910297Ssam main(argc, argv) 40*66670Spendry int argc; 4110297Ssam char *argv[]; 4210297Ssam { 43*66670Spendry int ch, top; 4425907Smckusick struct passwd *pw = NULL; 45*66670Spendry char *cp, homedir[MAXPATHLEN]; 4610297Ssam 4710297Ssam sp = getservbyname("ftp", "tcp"); 48*66670Spendry if (sp == 0) 49*66670Spendry errx(1, "ftp/tcp: unknown service"); 5011351Ssam doglob = 1; 5111654Ssam interactive = 1; 5211351Ssam autologin = 1; 5310297Ssam 54*66670Spendry while ((ch = getopt(argc, argv, "dgintv")) != EOF) { 55*66670Spendry switch (*cp) { 56*66670Spendry case 'd': 57*66670Spendry options |= SO_DEBUG; 58*66670Spendry debug++; 59*66670Spendry break; 6010297Ssam 61*66670Spendry case 'g': 62*66670Spendry doglob = 0; 63*66670Spendry break; 6410297Ssam 65*66670Spendry case 'i': 66*66670Spendry interactive = 0; 67*66670Spendry break; 6810297Ssam 69*66670Spendry case 'n': 70*66670Spendry autologin = 0; 71*66670Spendry break; 7210297Ssam 73*66670Spendry case 't': 74*66670Spendry trace++; 75*66670Spendry break; 7610297Ssam 77*66670Spendry case 'v': 78*66670Spendry verbose++; 79*66670Spendry break; 8011351Ssam 81*66670Spendry default: 82*66670Spendry (void)fprintf(stderr, 83*66670Spendry "usage: ftp [-dgintv] [host [port]]\n"); 84*66670Spendry exit(1); 85*66670Spendry } 8610297Ssam } 87*66670Spendry argc -= optind; 88*66670Spendry argv += optind; 89*66670Spendry 9010297Ssam fromatty = isatty(fileno(stdin)); 9110297Ssam if (fromatty) 9210297Ssam verbose++; 9338131Srick cpend = 0; /* no pending replies */ 9426047Sminshall proxy = 0; /* proxy not active */ 9538131Srick crflag = 1; /* strip c.r. on ascii gets */ 9638131Srick sendport = -1; /* not using ports */ 9711352Ssam /* 9811352Ssam * Set up the home directory in case we're globbing. 9911352Ssam */ 10025907Smckusick cp = getlogin(); 10126047Sminshall if (cp != NULL) { 10225907Smckusick pw = getpwnam(cp); 10326047Sminshall } 10411352Ssam if (pw == NULL) 10511352Ssam pw = getpwuid(getuid()); 10611352Ssam if (pw != NULL) { 10711352Ssam home = homedir; 10826495Sminshall (void) strcpy(home, pw->pw_dir); 10911352Ssam } 11010297Ssam if (argc > 0) { 111*66670Spendry char *xargv[3]; 112*66670Spendry extern char *__progname; 113*66670Spendry 11410297Ssam if (setjmp(toplevel)) 11510297Ssam exit(0); 11626495Sminshall (void) signal(SIGINT, intr); 11726495Sminshall (void) signal(SIGPIPE, lostpeer); 118*66670Spendry xargv[0] = __progname; 119*66670Spendry xargv[1] = argv[0]; 120*66670Spendry xargv[2] = argv[1]; 121*66670Spendry xargv[3] = argv[2]; 122*66670Spendry setpeer(argc+1, xargv); 12310297Ssam } 12410297Ssam top = setjmp(toplevel) == 0; 12510297Ssam if (top) { 12626495Sminshall (void) signal(SIGINT, intr); 12726495Sminshall (void) signal(SIGPIPE, lostpeer); 12810297Ssam } 12910297Ssam for (;;) { 13010297Ssam cmdscanner(top); 13110297Ssam top = 1; 13210297Ssam } 13310297Ssam } 13410297Ssam 13546828Sbostic void 13610297Ssam intr() 13710297Ssam { 13810297Ssam 13910297Ssam longjmp(toplevel, 1); 14010297Ssam } 14110297Ssam 14246828Sbostic void 14310297Ssam lostpeer() 14410297Ssam { 14510297Ssam 14610297Ssam if (connected) { 14710297Ssam if (cout != NULL) { 14826495Sminshall (void) shutdown(fileno(cout), 1+1); 14926495Sminshall (void) fclose(cout); 15010297Ssam cout = NULL; 15110297Ssam } 15210297Ssam if (data >= 0) { 15326495Sminshall (void) shutdown(data, 1+1); 15410297Ssam (void) close(data); 15510297Ssam data = -1; 15610297Ssam } 15710297Ssam connected = 0; 15810297Ssam } 15926047Sminshall pswitch(1); 16026047Sminshall if (connected) { 16126047Sminshall if (cout != NULL) { 16226495Sminshall (void) shutdown(fileno(cout), 1+1); 16326495Sminshall (void) fclose(cout); 16426047Sminshall cout = NULL; 16526047Sminshall } 16626047Sminshall connected = 0; 16726047Sminshall } 16826047Sminshall proxflag = 0; 16926047Sminshall pswitch(0); 17010297Ssam } 17110297Ssam 172*66670Spendry /* 173*66670Spendry char * 17410297Ssam tail(filename) 17510297Ssam char *filename; 17610297Ssam { 177*66670Spendry char *s; 17810297Ssam 17910297Ssam while (*filename) { 180*66670Spendry s = strrchr(filename, '/'); 18110297Ssam if (s == NULL) 18210297Ssam break; 18310297Ssam if (s[1]) 18410297Ssam return (s + 1); 18510297Ssam *s = '\0'; 18610297Ssam } 18710297Ssam return (filename); 18810297Ssam } 18926495Sminshall */ 190*66670Spendry 19110297Ssam /* 19210297Ssam * Command parser. 19310297Ssam */ 194*66670Spendry void 19510297Ssam cmdscanner(top) 19610297Ssam int top; 19710297Ssam { 198*66670Spendry struct cmd *c; 199*66670Spendry int l; 20010297Ssam 20110297Ssam if (!top) 20226495Sminshall (void) putchar('\n'); 20310297Ssam for (;;) { 20410297Ssam if (fromatty) { 20510297Ssam printf("ftp> "); 20626495Sminshall (void) fflush(stdout); 20710297Ssam } 20845260Sbostic if (fgets(line, sizeof line, stdin) == NULL) 209*66670Spendry quit(0, 0); 21045260Sbostic l = strlen(line); 21145260Sbostic if (l == 0) 21210297Ssam break; 21345260Sbostic if (line[--l] == '\n') { 21445260Sbostic if (l == 0) 21545260Sbostic break; 21645260Sbostic line[l] = '\0'; 21745260Sbostic } else if (l == sizeof(line) - 2) { 21845260Sbostic printf("sorry, input line too long\n"); 21945260Sbostic while ((l = getchar()) != '\n' && l != EOF) 22045260Sbostic /* void */; 22110297Ssam break; 22245260Sbostic } /* else it was a line without a newline */ 22310297Ssam makeargv(); 22426047Sminshall if (margc == 0) { 22525907Smckusick continue; 22626047Sminshall } 22710297Ssam c = getcmd(margv[0]); 22810297Ssam if (c == (struct cmd *)-1) { 22910297Ssam printf("?Ambiguous command\n"); 23010297Ssam continue; 23110297Ssam } 23210297Ssam if (c == 0) { 23310297Ssam printf("?Invalid command\n"); 23410297Ssam continue; 23510297Ssam } 23611654Ssam if (c->c_conn && !connected) { 23745260Sbostic printf("Not connected.\n"); 23811654Ssam continue; 23911654Ssam } 24010297Ssam (*c->c_handler)(margc, margv); 24110297Ssam if (bell && c->c_bell) 24236935Skarels (void) putchar('\007'); 24310297Ssam if (c->c_handler != help) 24410297Ssam break; 24510297Ssam } 24626495Sminshall (void) signal(SIGINT, intr); 24726495Sminshall (void) signal(SIGPIPE, lostpeer); 24810297Ssam } 24910297Ssam 25010297Ssam struct cmd * 25110297Ssam getcmd(name) 252*66670Spendry char *name; 25310297Ssam { 254*66670Spendry char *p, *q; 255*66670Spendry struct cmd *c, *found; 256*66670Spendry int nmatches, longest; 25710297Ssam 25810297Ssam longest = 0; 25910297Ssam nmatches = 0; 26010297Ssam found = 0; 26110297Ssam for (c = cmdtab; p = c->c_name; c++) { 26210297Ssam for (q = name; *q == *p++; q++) 26310297Ssam if (*q == 0) /* exact match? */ 26410297Ssam return (c); 26510297Ssam if (!*q) { /* the name was a prefix */ 26610297Ssam if (q - name > longest) { 26710297Ssam longest = q - name; 26810297Ssam nmatches = 1; 26910297Ssam found = c; 27010297Ssam } else if (q - name == longest) 27110297Ssam nmatches++; 27210297Ssam } 27310297Ssam } 27410297Ssam if (nmatches > 1) 27510297Ssam return ((struct cmd *)-1); 27610297Ssam return (found); 27710297Ssam } 27810297Ssam 27910297Ssam /* 28010297Ssam * Slice a string up into argc/argv. 28110297Ssam */ 28226047Sminshall 28326047Sminshall int slrflag; 28426047Sminshall 285*66670Spendry void 28610297Ssam makeargv() 28710297Ssam { 28810297Ssam char **argp; 28910297Ssam 29010297Ssam margc = 0; 29110297Ssam argp = margv; 29210297Ssam stringbase = line; /* scan from first of buffer */ 29310297Ssam argbase = argbuf; /* store from first of buffer */ 29426047Sminshall slrflag = 0; 29526047Sminshall while (*argp++ = slurpstring()) 29610297Ssam margc++; 29710297Ssam } 29810297Ssam 29910297Ssam /* 30010297Ssam * Parse string into argbuf; 30110297Ssam * implemented with FSM to 30210297Ssam * handle quoting and strings 30310297Ssam */ 30410297Ssam char * 30510297Ssam slurpstring() 30610297Ssam { 30710297Ssam int got_one = 0; 308*66670Spendry char *sb = stringbase; 309*66670Spendry char *ap = argbase; 31010297Ssam char *tmp = argbase; /* will return this if token found */ 31110297Ssam 31226047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 31326047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 31426047Sminshall case 0: 31526047Sminshall slrflag++; 31626047Sminshall stringbase++; 31726047Sminshall return ((*sb == '!') ? "!" : "$"); 31836935Skarels /* NOTREACHED */ 31926047Sminshall case 1: 32026047Sminshall slrflag++; 32126047Sminshall altarg = stringbase; 32226047Sminshall break; 32326047Sminshall default: 32426047Sminshall break; 32526047Sminshall } 32626047Sminshall } 32726047Sminshall 32810297Ssam S0: 32910297Ssam switch (*sb) { 33010297Ssam 33110297Ssam case '\0': 33210297Ssam goto OUT; 33310297Ssam 33410297Ssam case ' ': 33510297Ssam case '\t': 33610297Ssam sb++; goto S0; 33710297Ssam 33810297Ssam default: 33926047Sminshall switch (slrflag) { 34026047Sminshall case 0: 34126047Sminshall slrflag++; 34226047Sminshall break; 34326047Sminshall case 1: 34426047Sminshall slrflag++; 34526047Sminshall altarg = sb; 34626047Sminshall break; 34726047Sminshall default: 34826047Sminshall break; 34926047Sminshall } 35010297Ssam goto S1; 35110297Ssam } 35210297Ssam 35310297Ssam S1: 35410297Ssam switch (*sb) { 35510297Ssam 35610297Ssam case ' ': 35710297Ssam case '\t': 35810297Ssam case '\0': 35910297Ssam goto OUT; /* end of token */ 36010297Ssam 36110297Ssam case '\\': 36210297Ssam sb++; goto S2; /* slurp next character */ 36310297Ssam 36410297Ssam case '"': 36510297Ssam sb++; goto S3; /* slurp quoted string */ 36610297Ssam 36710297Ssam default: 36810297Ssam *ap++ = *sb++; /* add character to token */ 36910297Ssam got_one = 1; 37010297Ssam goto S1; 37110297Ssam } 37210297Ssam 37310297Ssam S2: 37410297Ssam switch (*sb) { 37510297Ssam 37610297Ssam case '\0': 37710297Ssam goto OUT; 37810297Ssam 37910297Ssam default: 38010297Ssam *ap++ = *sb++; 38110297Ssam got_one = 1; 38210297Ssam goto S1; 38310297Ssam } 38410297Ssam 38510297Ssam S3: 38610297Ssam switch (*sb) { 38710297Ssam 38810297Ssam case '\0': 38910297Ssam goto OUT; 39010297Ssam 39110297Ssam case '"': 39210297Ssam sb++; goto S1; 39310297Ssam 39410297Ssam default: 39510297Ssam *ap++ = *sb++; 39610297Ssam got_one = 1; 39710297Ssam goto S3; 39810297Ssam } 39910297Ssam 40010297Ssam OUT: 40110297Ssam if (got_one) 40210297Ssam *ap++ = '\0'; 40310297Ssam argbase = ap; /* update storage pointer */ 40410297Ssam stringbase = sb; /* update scan pointer */ 40526047Sminshall if (got_one) { 406*66670Spendry return (tmp); 40726047Sminshall } 40826047Sminshall switch (slrflag) { 40926047Sminshall case 0: 41026047Sminshall slrflag++; 41126047Sminshall break; 41226047Sminshall case 1: 41326047Sminshall slrflag++; 41426047Sminshall altarg = (char *) 0; 41526047Sminshall break; 41626047Sminshall default: 41726047Sminshall break; 41826047Sminshall } 419*66670Spendry return ((char *)0); 42010297Ssam } 42110297Ssam 422*66670Spendry #define HELPINDENT ((int) sizeof ("directory")) 42310297Ssam 42410297Ssam /* 42510297Ssam * Help command. 42610297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42710297Ssam */ 428*66670Spendry void 42910297Ssam help(argc, argv) 43010297Ssam int argc; 43110297Ssam char *argv[]; 43210297Ssam { 433*66670Spendry struct cmd *c; 43410297Ssam 43510297Ssam if (argc == 1) { 436*66670Spendry int i, j, w, k; 43710297Ssam int columns, width = 0, lines; 43810297Ssam 43910297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 44010297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 44110297Ssam int len = strlen(c->c_name); 44210297Ssam 44310297Ssam if (len > width) 44410297Ssam width = len; 44510297Ssam } 44610297Ssam width = (width + 8) &~ 7; 44710297Ssam columns = 80 / width; 44810297Ssam if (columns == 0) 44910297Ssam columns = 1; 45010297Ssam lines = (NCMDS + columns - 1) / columns; 45110297Ssam for (i = 0; i < lines; i++) { 45210297Ssam for (j = 0; j < columns; j++) { 45310297Ssam c = cmdtab + j * lines + i; 45426047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 45525907Smckusick printf("%s", c->c_name); 45626047Sminshall } 45726047Sminshall else if (c->c_name) { 45826047Sminshall for (k=0; k < strlen(c->c_name); k++) { 45926495Sminshall (void) putchar(' '); 46026047Sminshall } 46126047Sminshall } 46210297Ssam if (c + lines >= &cmdtab[NCMDS]) { 46310297Ssam printf("\n"); 46410297Ssam break; 46510297Ssam } 46610297Ssam w = strlen(c->c_name); 46710297Ssam while (w < width) { 46810297Ssam w = (w + 8) &~ 7; 46926495Sminshall (void) putchar('\t'); 47010297Ssam } 47110297Ssam } 47210297Ssam } 47310297Ssam return; 47410297Ssam } 47510297Ssam while (--argc > 0) { 476*66670Spendry char *arg; 47710297Ssam arg = *++argv; 47810297Ssam c = getcmd(arg); 47910297Ssam if (c == (struct cmd *)-1) 48010297Ssam printf("?Ambiguous help command %s\n", arg); 48110297Ssam else if (c == (struct cmd *)0) 48210297Ssam printf("?Invalid help command %s\n", arg); 48310297Ssam else 48410297Ssam printf("%-*s\t%s\n", HELPINDENT, 48510297Ssam c->c_name, c->c_help); 48610297Ssam } 48710297Ssam } 488