121740Sdist /* 266673Spendry * Copyright (c) 1985, 1989, 1993, 1994 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[] = 1066673Spendry "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\ 1162012Sbostic The Regents of the University of California. All rights reserved.\n"; 1233737Sbostic #endif /* not lint */ 1310297Ssam 1421740Sdist #ifndef lint 15*66691Spendry static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 04/03/94"; 1633737Sbostic #endif /* not lint */ 1721740Sdist 1810297Ssam /* 1910297Ssam * FTP User Program -- Command Interface. 2010297Ssam */ 2166670Spendry /*#include <sys/ioctl.h>*/ 2266670Spendry #include <sys/types.h> 2310297Ssam #include <sys/socket.h> 2410297Ssam 2512398Ssam #include <arpa/ftp.h> 2612398Ssam 2710297Ssam #include <ctype.h> 2866670Spendry #include <err.h> 2910297Ssam #include <netdb.h> 3011352Ssam #include <pwd.h> 3166670Spendry #include <signal.h> 3266670Spendry #include <stdio.h> 3366670Spendry #include <stdlib.h> 3466670Spendry #include <unistd.h> 3510297Ssam 3666670Spendry #include "ftp_var.h" 3710297Ssam 3866670Spendry int 3910297Ssam main(argc, argv) 4066670Spendry int argc; 4110297Ssam char *argv[]; 4210297Ssam { 4366670Spendry int ch, top; 4425907Smckusick struct passwd *pw = NULL; 4566670Spendry char *cp, homedir[MAXPATHLEN]; 4610297Ssam 4710297Ssam sp = getservbyname("ftp", "tcp"); 4866670Spendry if (sp == 0) 4966670Spendry errx(1, "ftp/tcp: unknown service"); 5011351Ssam doglob = 1; 5111654Ssam interactive = 1; 5211351Ssam autologin = 1; 5310297Ssam 5466670Spendry while ((ch = getopt(argc, argv, "dgintv")) != EOF) { 5566670Spendry switch (*cp) { 5666670Spendry case 'd': 5766670Spendry options |= SO_DEBUG; 5866670Spendry debug++; 5966670Spendry break; 6010297Ssam 6166670Spendry case 'g': 6266670Spendry doglob = 0; 6366670Spendry break; 6410297Ssam 6566670Spendry case 'i': 6666670Spendry interactive = 0; 6766670Spendry break; 6810297Ssam 6966670Spendry case 'n': 7066670Spendry autologin = 0; 7166670Spendry break; 7210297Ssam 7366670Spendry case 't': 7466670Spendry trace++; 7566670Spendry break; 7610297Ssam 7766670Spendry case 'v': 7866670Spendry verbose++; 7966670Spendry break; 8011351Ssam 8166670Spendry default: 8266670Spendry (void)fprintf(stderr, 8366670Spendry "usage: ftp [-dgintv] [host [port]]\n"); 8466670Spendry exit(1); 8566670Spendry } 8610297Ssam } 8766670Spendry argc -= optind; 8866670Spendry argv += optind; 8966670Spendry 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*66691Spendry char *xargv[5]; 11266670Spendry extern char *__progname; 11366670Spendry 11410297Ssam if (setjmp(toplevel)) 11510297Ssam exit(0); 11626495Sminshall (void) signal(SIGINT, intr); 11726495Sminshall (void) signal(SIGPIPE, lostpeer); 11866670Spendry xargv[0] = __progname; 11966670Spendry xargv[1] = argv[0]; 12066670Spendry xargv[2] = argv[1]; 12166670Spendry xargv[3] = argv[2]; 122*66691Spendry xargv[4] = NULL; 12366670Spendry setpeer(argc+1, xargv); 12410297Ssam } 12510297Ssam top = setjmp(toplevel) == 0; 12610297Ssam if (top) { 12726495Sminshall (void) signal(SIGINT, intr); 12826495Sminshall (void) signal(SIGPIPE, lostpeer); 12910297Ssam } 13010297Ssam for (;;) { 13110297Ssam cmdscanner(top); 13210297Ssam top = 1; 13310297Ssam } 13410297Ssam } 13510297Ssam 13646828Sbostic void 13710297Ssam intr() 13810297Ssam { 13910297Ssam 14010297Ssam longjmp(toplevel, 1); 14110297Ssam } 14210297Ssam 14346828Sbostic void 14410297Ssam lostpeer() 14510297Ssam { 14610297Ssam 14710297Ssam if (connected) { 14810297Ssam if (cout != NULL) { 14926495Sminshall (void) shutdown(fileno(cout), 1+1); 15026495Sminshall (void) fclose(cout); 15110297Ssam cout = NULL; 15210297Ssam } 15310297Ssam if (data >= 0) { 15426495Sminshall (void) shutdown(data, 1+1); 15510297Ssam (void) close(data); 15610297Ssam data = -1; 15710297Ssam } 15810297Ssam connected = 0; 15910297Ssam } 16026047Sminshall pswitch(1); 16126047Sminshall if (connected) { 16226047Sminshall if (cout != NULL) { 16326495Sminshall (void) shutdown(fileno(cout), 1+1); 16426495Sminshall (void) fclose(cout); 16526047Sminshall cout = NULL; 16626047Sminshall } 16726047Sminshall connected = 0; 16826047Sminshall } 16926047Sminshall proxflag = 0; 17026047Sminshall pswitch(0); 17110297Ssam } 17210297Ssam 17366670Spendry /* 17466670Spendry char * 17510297Ssam tail(filename) 17610297Ssam char *filename; 17710297Ssam { 17866670Spendry char *s; 17910297Ssam 18010297Ssam while (*filename) { 18166670Spendry s = strrchr(filename, '/'); 18210297Ssam if (s == NULL) 18310297Ssam break; 18410297Ssam if (s[1]) 18510297Ssam return (s + 1); 18610297Ssam *s = '\0'; 18710297Ssam } 18810297Ssam return (filename); 18910297Ssam } 19026495Sminshall */ 19166670Spendry 19210297Ssam /* 19310297Ssam * Command parser. 19410297Ssam */ 19566670Spendry void 19610297Ssam cmdscanner(top) 19710297Ssam int top; 19810297Ssam { 19966670Spendry struct cmd *c; 20066670Spendry int l; 20110297Ssam 20210297Ssam if (!top) 20326495Sminshall (void) putchar('\n'); 20410297Ssam for (;;) { 20510297Ssam if (fromatty) { 20610297Ssam printf("ftp> "); 20726495Sminshall (void) fflush(stdout); 20810297Ssam } 20945260Sbostic if (fgets(line, sizeof line, stdin) == NULL) 21066670Spendry quit(0, 0); 21145260Sbostic l = strlen(line); 21245260Sbostic if (l == 0) 21310297Ssam break; 21445260Sbostic if (line[--l] == '\n') { 21545260Sbostic if (l == 0) 21645260Sbostic break; 21745260Sbostic line[l] = '\0'; 21845260Sbostic } else if (l == sizeof(line) - 2) { 21945260Sbostic printf("sorry, input line too long\n"); 22045260Sbostic while ((l = getchar()) != '\n' && l != EOF) 22145260Sbostic /* void */; 22210297Ssam break; 22345260Sbostic } /* else it was a line without a newline */ 22410297Ssam makeargv(); 22526047Sminshall if (margc == 0) { 22625907Smckusick continue; 22726047Sminshall } 22810297Ssam c = getcmd(margv[0]); 22910297Ssam if (c == (struct cmd *)-1) { 23010297Ssam printf("?Ambiguous command\n"); 23110297Ssam continue; 23210297Ssam } 23310297Ssam if (c == 0) { 23410297Ssam printf("?Invalid command\n"); 23510297Ssam continue; 23610297Ssam } 23711654Ssam if (c->c_conn && !connected) { 23845260Sbostic printf("Not connected.\n"); 23911654Ssam continue; 24011654Ssam } 24110297Ssam (*c->c_handler)(margc, margv); 24210297Ssam if (bell && c->c_bell) 24336935Skarels (void) putchar('\007'); 24410297Ssam if (c->c_handler != help) 24510297Ssam break; 24610297Ssam } 24726495Sminshall (void) signal(SIGINT, intr); 24826495Sminshall (void) signal(SIGPIPE, lostpeer); 24910297Ssam } 25010297Ssam 25110297Ssam struct cmd * 25210297Ssam getcmd(name) 25366670Spendry char *name; 25410297Ssam { 25566670Spendry char *p, *q; 25666670Spendry struct cmd *c, *found; 25766670Spendry int nmatches, longest; 25810297Ssam 25910297Ssam longest = 0; 26010297Ssam nmatches = 0; 26110297Ssam found = 0; 26210297Ssam for (c = cmdtab; p = c->c_name; c++) { 26310297Ssam for (q = name; *q == *p++; q++) 26410297Ssam if (*q == 0) /* exact match? */ 26510297Ssam return (c); 26610297Ssam if (!*q) { /* the name was a prefix */ 26710297Ssam if (q - name > longest) { 26810297Ssam longest = q - name; 26910297Ssam nmatches = 1; 27010297Ssam found = c; 27110297Ssam } else if (q - name == longest) 27210297Ssam nmatches++; 27310297Ssam } 27410297Ssam } 27510297Ssam if (nmatches > 1) 27610297Ssam return ((struct cmd *)-1); 27710297Ssam return (found); 27810297Ssam } 27910297Ssam 28010297Ssam /* 28110297Ssam * Slice a string up into argc/argv. 28210297Ssam */ 28326047Sminshall 28426047Sminshall int slrflag; 28526047Sminshall 28666670Spendry void 28710297Ssam makeargv() 28810297Ssam { 28910297Ssam char **argp; 29010297Ssam 29110297Ssam margc = 0; 29210297Ssam argp = margv; 29310297Ssam stringbase = line; /* scan from first of buffer */ 29410297Ssam argbase = argbuf; /* store from first of buffer */ 29526047Sminshall slrflag = 0; 29626047Sminshall while (*argp++ = slurpstring()) 29710297Ssam margc++; 29810297Ssam } 29910297Ssam 30010297Ssam /* 30110297Ssam * Parse string into argbuf; 30210297Ssam * implemented with FSM to 30310297Ssam * handle quoting and strings 30410297Ssam */ 30510297Ssam char * 30610297Ssam slurpstring() 30710297Ssam { 30810297Ssam int got_one = 0; 30966670Spendry char *sb = stringbase; 31066670Spendry char *ap = argbase; 31110297Ssam char *tmp = argbase; /* will return this if token found */ 31210297Ssam 31326047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 31426047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 31526047Sminshall case 0: 31626047Sminshall slrflag++; 31726047Sminshall stringbase++; 31826047Sminshall return ((*sb == '!') ? "!" : "$"); 31936935Skarels /* NOTREACHED */ 32026047Sminshall case 1: 32126047Sminshall slrflag++; 32226047Sminshall altarg = stringbase; 32326047Sminshall break; 32426047Sminshall default: 32526047Sminshall break; 32626047Sminshall } 32726047Sminshall } 32826047Sminshall 32910297Ssam S0: 33010297Ssam switch (*sb) { 33110297Ssam 33210297Ssam case '\0': 33310297Ssam goto OUT; 33410297Ssam 33510297Ssam case ' ': 33610297Ssam case '\t': 33710297Ssam sb++; goto S0; 33810297Ssam 33910297Ssam default: 34026047Sminshall switch (slrflag) { 34126047Sminshall case 0: 34226047Sminshall slrflag++; 34326047Sminshall break; 34426047Sminshall case 1: 34526047Sminshall slrflag++; 34626047Sminshall altarg = sb; 34726047Sminshall break; 34826047Sminshall default: 34926047Sminshall break; 35026047Sminshall } 35110297Ssam goto S1; 35210297Ssam } 35310297Ssam 35410297Ssam S1: 35510297Ssam switch (*sb) { 35610297Ssam 35710297Ssam case ' ': 35810297Ssam case '\t': 35910297Ssam case '\0': 36010297Ssam goto OUT; /* end of token */ 36110297Ssam 36210297Ssam case '\\': 36310297Ssam sb++; goto S2; /* slurp next character */ 36410297Ssam 36510297Ssam case '"': 36610297Ssam sb++; goto S3; /* slurp quoted string */ 36710297Ssam 36810297Ssam default: 36910297Ssam *ap++ = *sb++; /* add character to token */ 37010297Ssam got_one = 1; 37110297Ssam goto S1; 37210297Ssam } 37310297Ssam 37410297Ssam S2: 37510297Ssam switch (*sb) { 37610297Ssam 37710297Ssam case '\0': 37810297Ssam goto OUT; 37910297Ssam 38010297Ssam default: 38110297Ssam *ap++ = *sb++; 38210297Ssam got_one = 1; 38310297Ssam goto S1; 38410297Ssam } 38510297Ssam 38610297Ssam S3: 38710297Ssam switch (*sb) { 38810297Ssam 38910297Ssam case '\0': 39010297Ssam goto OUT; 39110297Ssam 39210297Ssam case '"': 39310297Ssam sb++; goto S1; 39410297Ssam 39510297Ssam default: 39610297Ssam *ap++ = *sb++; 39710297Ssam got_one = 1; 39810297Ssam goto S3; 39910297Ssam } 40010297Ssam 40110297Ssam OUT: 40210297Ssam if (got_one) 40310297Ssam *ap++ = '\0'; 40410297Ssam argbase = ap; /* update storage pointer */ 40510297Ssam stringbase = sb; /* update scan pointer */ 40626047Sminshall if (got_one) { 40766670Spendry return (tmp); 40826047Sminshall } 40926047Sminshall switch (slrflag) { 41026047Sminshall case 0: 41126047Sminshall slrflag++; 41226047Sminshall break; 41326047Sminshall case 1: 41426047Sminshall slrflag++; 41526047Sminshall altarg = (char *) 0; 41626047Sminshall break; 41726047Sminshall default: 41826047Sminshall break; 41926047Sminshall } 42066670Spendry return ((char *)0); 42110297Ssam } 42210297Ssam 42366670Spendry #define HELPINDENT ((int) sizeof ("directory")) 42410297Ssam 42510297Ssam /* 42610297Ssam * Help command. 42710297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42810297Ssam */ 42966670Spendry void 43010297Ssam help(argc, argv) 43110297Ssam int argc; 43210297Ssam char *argv[]; 43310297Ssam { 43466670Spendry struct cmd *c; 43510297Ssam 43610297Ssam if (argc == 1) { 43766670Spendry int i, j, w, k; 43810297Ssam int columns, width = 0, lines; 43910297Ssam 44010297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 44110297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 44210297Ssam int len = strlen(c->c_name); 44310297Ssam 44410297Ssam if (len > width) 44510297Ssam width = len; 44610297Ssam } 44710297Ssam width = (width + 8) &~ 7; 44810297Ssam columns = 80 / width; 44910297Ssam if (columns == 0) 45010297Ssam columns = 1; 45110297Ssam lines = (NCMDS + columns - 1) / columns; 45210297Ssam for (i = 0; i < lines; i++) { 45310297Ssam for (j = 0; j < columns; j++) { 45410297Ssam c = cmdtab + j * lines + i; 45526047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 45625907Smckusick printf("%s", c->c_name); 45726047Sminshall } 45826047Sminshall else if (c->c_name) { 45926047Sminshall for (k=0; k < strlen(c->c_name); k++) { 46026495Sminshall (void) putchar(' '); 46126047Sminshall } 46226047Sminshall } 46310297Ssam if (c + lines >= &cmdtab[NCMDS]) { 46410297Ssam printf("\n"); 46510297Ssam break; 46610297Ssam } 46710297Ssam w = strlen(c->c_name); 46810297Ssam while (w < width) { 46910297Ssam w = (w + 8) &~ 7; 47026495Sminshall (void) putchar('\t'); 47110297Ssam } 47210297Ssam } 47310297Ssam } 47410297Ssam return; 47510297Ssam } 47610297Ssam while (--argc > 0) { 47766670Spendry char *arg; 47810297Ssam arg = *++argv; 47910297Ssam c = getcmd(arg); 48010297Ssam if (c == (struct cmd *)-1) 48110297Ssam printf("?Ambiguous help command %s\n", arg); 48210297Ssam else if (c == (struct cmd *)0) 48310297Ssam printf("?Invalid help command %s\n", arg); 48410297Ssam else 48510297Ssam printf("%-*s\t%s\n", HELPINDENT, 48610297Ssam c->c_name, c->c_help); 48710297Ssam } 48810297Ssam } 489