121740Sdist /* 2*62012Sbostic * Copyright (c) 1985, 1989, 1993 3*62012Sbostic * The Regents of the University of California. All rights reserved. 433737Sbostic * 542666Sbostic * %sccs.include.redist.c% 621740Sdist */ 721740Sdist 810297Ssam #ifndef lint 9*62012Sbostic static char copyright[] = 10*62012Sbostic "@(#) Copyright (c) 1985, 1989, 1993\n\ 11*62012Sbostic The Regents of the University of California. All rights reserved.\n"; 1233737Sbostic #endif /* not lint */ 1310297Ssam 1421740Sdist #ifndef lint 15*62012Sbostic static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 06/06/93"; 1633737Sbostic #endif /* not lint */ 1721740Sdist 1810297Ssam /* 1910297Ssam * FTP User Program -- Command Interface. 2010297Ssam */ 2126047Sminshall #include "ftp_var.h" 2210297Ssam #include <sys/socket.h> 2310297Ssam #include <sys/ioctl.h> 2426495Sminshall #include <sys/types.h> 2510297Ssam 2612398Ssam #include <arpa/ftp.h> 2712398Ssam 2810297Ssam #include <signal.h> 2910297Ssam #include <stdio.h> 3010297Ssam #include <errno.h> 3110297Ssam #include <ctype.h> 3210297Ssam #include <netdb.h> 3311352Ssam #include <pwd.h> 3410297Ssam 3526495Sminshall uid_t getuid(); 3646828Sbostic void intr(), lostpeer(); 3711352Ssam extern char *home; 3825907Smckusick char *getlogin(); 3910297Ssam 4010297Ssam main(argc, argv) 4110297Ssam char *argv[]; 4210297Ssam { 4310297Ssam register char *cp; 4410297Ssam int top; 4525907Smckusick struct passwd *pw = NULL; 4611352Ssam char homedir[MAXPATHLEN]; 4710297Ssam 4810297Ssam sp = getservbyname("ftp", "tcp"); 4910297Ssam if (sp == 0) { 5010297Ssam fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 5110297Ssam exit(1); 5210297Ssam } 5311351Ssam doglob = 1; 5411654Ssam interactive = 1; 5511351Ssam autologin = 1; 5610297Ssam argc--, argv++; 5710297Ssam while (argc > 0 && **argv == '-') { 5810297Ssam for (cp = *argv + 1; *cp; cp++) 5910297Ssam switch (*cp) { 6010297Ssam 6110297Ssam case 'd': 6210297Ssam options |= SO_DEBUG; 6310297Ssam debug++; 6410297Ssam break; 6510297Ssam 6610297Ssam case 'v': 6710297Ssam verbose++; 6810297Ssam break; 6910297Ssam 7010297Ssam case 't': 7110297Ssam trace++; 7210297Ssam break; 7310297Ssam 7410297Ssam case 'i': 7511654Ssam interactive = 0; 7610297Ssam break; 7710297Ssam 7810297Ssam case 'n': 7910297Ssam autologin = 0; 8010297Ssam break; 8110297Ssam 8211351Ssam case 'g': 8311351Ssam doglob = 0; 8411351Ssam break; 8511351Ssam 8610297Ssam default: 8726495Sminshall fprintf(stdout, 8810297Ssam "ftp: %c: unknown option\n", *cp); 8910297Ssam exit(1); 9010297Ssam } 9110297Ssam argc--, argv++; 9210297Ssam } 9310297Ssam fromatty = isatty(fileno(stdin)); 9410297Ssam if (fromatty) 9510297Ssam verbose++; 9638131Srick cpend = 0; /* no pending replies */ 9726047Sminshall proxy = 0; /* proxy not active */ 9838131Srick crflag = 1; /* strip c.r. on ascii gets */ 9938131Srick sendport = -1; /* not using ports */ 10011352Ssam /* 10111352Ssam * Set up the home directory in case we're globbing. 10211352Ssam */ 10325907Smckusick cp = getlogin(); 10426047Sminshall if (cp != NULL) { 10525907Smckusick pw = getpwnam(cp); 10626047Sminshall } 10711352Ssam if (pw == NULL) 10811352Ssam pw = getpwuid(getuid()); 10911352Ssam if (pw != NULL) { 11011352Ssam home = homedir; 11126495Sminshall (void) strcpy(home, pw->pw_dir); 11211352Ssam } 11310297Ssam if (argc > 0) { 11410297Ssam if (setjmp(toplevel)) 11510297Ssam exit(0); 11626495Sminshall (void) signal(SIGINT, intr); 11726495Sminshall (void) signal(SIGPIPE, lostpeer); 11810297Ssam setpeer(argc + 1, argv - 1); 11910297Ssam } 12010297Ssam top = setjmp(toplevel) == 0; 12110297Ssam if (top) { 12226495Sminshall (void) signal(SIGINT, intr); 12326495Sminshall (void) signal(SIGPIPE, lostpeer); 12410297Ssam } 12510297Ssam for (;;) { 12610297Ssam cmdscanner(top); 12710297Ssam top = 1; 12810297Ssam } 12910297Ssam } 13010297Ssam 13146828Sbostic void 13210297Ssam intr() 13310297Ssam { 13410297Ssam 13510297Ssam longjmp(toplevel, 1); 13610297Ssam } 13710297Ssam 13846828Sbostic void 13910297Ssam lostpeer() 14010297Ssam { 14110297Ssam extern FILE *cout; 14210297Ssam extern int data; 14310297Ssam 14410297Ssam if (connected) { 14510297Ssam if (cout != NULL) { 14626495Sminshall (void) shutdown(fileno(cout), 1+1); 14726495Sminshall (void) fclose(cout); 14810297Ssam cout = NULL; 14910297Ssam } 15010297Ssam if (data >= 0) { 15126495Sminshall (void) shutdown(data, 1+1); 15210297Ssam (void) close(data); 15310297Ssam data = -1; 15410297Ssam } 15510297Ssam connected = 0; 15610297Ssam } 15726047Sminshall pswitch(1); 15826047Sminshall if (connected) { 15926047Sminshall if (cout != NULL) { 16026495Sminshall (void) shutdown(fileno(cout), 1+1); 16126495Sminshall (void) fclose(cout); 16226047Sminshall cout = NULL; 16326047Sminshall } 16426047Sminshall connected = 0; 16526047Sminshall } 16626047Sminshall proxflag = 0; 16726047Sminshall pswitch(0); 16810297Ssam } 16910297Ssam 17026495Sminshall /*char * 17110297Ssam tail(filename) 17210297Ssam char *filename; 17310297Ssam { 17410297Ssam register char *s; 17510297Ssam 17610297Ssam while (*filename) { 17710297Ssam s = rindex(filename, '/'); 17810297Ssam if (s == NULL) 17910297Ssam break; 18010297Ssam if (s[1]) 18110297Ssam return (s + 1); 18210297Ssam *s = '\0'; 18310297Ssam } 18410297Ssam return (filename); 18510297Ssam } 18626495Sminshall */ 18710297Ssam /* 18810297Ssam * Command parser. 18910297Ssam */ 19010297Ssam cmdscanner(top) 19110297Ssam int top; 19210297Ssam { 19310297Ssam register struct cmd *c; 19445260Sbostic register int l; 19510297Ssam struct cmd *getcmd(); 19610297Ssam extern int help(); 19710297Ssam 19810297Ssam if (!top) 19926495Sminshall (void) putchar('\n'); 20010297Ssam for (;;) { 20110297Ssam if (fromatty) { 20210297Ssam printf("ftp> "); 20326495Sminshall (void) fflush(stdout); 20410297Ssam } 20545260Sbostic if (fgets(line, sizeof line, stdin) == NULL) 20645260Sbostic quit(); 20745260Sbostic l = strlen(line); 20845260Sbostic if (l == 0) 20910297Ssam break; 21045260Sbostic if (line[--l] == '\n') { 21145260Sbostic if (l == 0) 21245260Sbostic break; 21345260Sbostic line[l] = '\0'; 21445260Sbostic } else if (l == sizeof(line) - 2) { 21545260Sbostic printf("sorry, input line too long\n"); 21645260Sbostic while ((l = getchar()) != '\n' && l != EOF) 21745260Sbostic /* void */; 21810297Ssam break; 21945260Sbostic } /* else it was a line without a newline */ 22010297Ssam makeargv(); 22126047Sminshall if (margc == 0) { 22225907Smckusick continue; 22326047Sminshall } 22410297Ssam c = getcmd(margv[0]); 22510297Ssam if (c == (struct cmd *)-1) { 22610297Ssam printf("?Ambiguous command\n"); 22710297Ssam continue; 22810297Ssam } 22910297Ssam if (c == 0) { 23010297Ssam printf("?Invalid command\n"); 23110297Ssam continue; 23210297Ssam } 23311654Ssam if (c->c_conn && !connected) { 23445260Sbostic printf("Not connected.\n"); 23511654Ssam continue; 23611654Ssam } 23710297Ssam (*c->c_handler)(margc, margv); 23810297Ssam if (bell && c->c_bell) 23936935Skarels (void) putchar('\007'); 24010297Ssam if (c->c_handler != help) 24110297Ssam break; 24210297Ssam } 24326495Sminshall (void) signal(SIGINT, intr); 24426495Sminshall (void) signal(SIGPIPE, lostpeer); 24510297Ssam } 24610297Ssam 24710297Ssam struct cmd * 24810297Ssam getcmd(name) 24910297Ssam register char *name; 25010297Ssam { 25133224Sbostic extern struct cmd cmdtab[]; 25210297Ssam register char *p, *q; 25310297Ssam register struct cmd *c, *found; 25410297Ssam register int nmatches, longest; 25510297Ssam 25610297Ssam longest = 0; 25710297Ssam nmatches = 0; 25810297Ssam found = 0; 25910297Ssam for (c = cmdtab; p = c->c_name; c++) { 26010297Ssam for (q = name; *q == *p++; q++) 26110297Ssam if (*q == 0) /* exact match? */ 26210297Ssam return (c); 26310297Ssam if (!*q) { /* the name was a prefix */ 26410297Ssam if (q - name > longest) { 26510297Ssam longest = q - name; 26610297Ssam nmatches = 1; 26710297Ssam found = c; 26810297Ssam } else if (q - name == longest) 26910297Ssam nmatches++; 27010297Ssam } 27110297Ssam } 27210297Ssam if (nmatches > 1) 27310297Ssam return ((struct cmd *)-1); 27410297Ssam return (found); 27510297Ssam } 27610297Ssam 27710297Ssam /* 27810297Ssam * Slice a string up into argc/argv. 27910297Ssam */ 28026047Sminshall 28126047Sminshall int slrflag; 28226047Sminshall 28310297Ssam makeargv() 28410297Ssam { 28510297Ssam char **argp; 28610297Ssam char *slurpstring(); 28710297Ssam 28810297Ssam margc = 0; 28910297Ssam argp = margv; 29010297Ssam stringbase = line; /* scan from first of buffer */ 29110297Ssam argbase = argbuf; /* store from first of buffer */ 29226047Sminshall slrflag = 0; 29326047Sminshall while (*argp++ = slurpstring()) 29410297Ssam margc++; 29510297Ssam } 29610297Ssam 29710297Ssam /* 29810297Ssam * Parse string into argbuf; 29910297Ssam * implemented with FSM to 30010297Ssam * handle quoting and strings 30110297Ssam */ 30210297Ssam char * 30310297Ssam slurpstring() 30410297Ssam { 30510297Ssam int got_one = 0; 30610297Ssam register char *sb = stringbase; 30710297Ssam register char *ap = argbase; 30810297Ssam char *tmp = argbase; /* will return this if token found */ 30910297Ssam 31026047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 31126047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 31226047Sminshall case 0: 31326047Sminshall slrflag++; 31426047Sminshall stringbase++; 31526047Sminshall return ((*sb == '!') ? "!" : "$"); 31636935Skarels /* NOTREACHED */ 31726047Sminshall case 1: 31826047Sminshall slrflag++; 31926047Sminshall altarg = stringbase; 32026047Sminshall break; 32126047Sminshall default: 32226047Sminshall break; 32326047Sminshall } 32426047Sminshall } 32526047Sminshall 32610297Ssam S0: 32710297Ssam switch (*sb) { 32810297Ssam 32910297Ssam case '\0': 33010297Ssam goto OUT; 33110297Ssam 33210297Ssam case ' ': 33310297Ssam case '\t': 33410297Ssam sb++; goto S0; 33510297Ssam 33610297Ssam default: 33726047Sminshall switch (slrflag) { 33826047Sminshall case 0: 33926047Sminshall slrflag++; 34026047Sminshall break; 34126047Sminshall case 1: 34226047Sminshall slrflag++; 34326047Sminshall altarg = sb; 34426047Sminshall break; 34526047Sminshall default: 34626047Sminshall break; 34726047Sminshall } 34810297Ssam goto S1; 34910297Ssam } 35010297Ssam 35110297Ssam S1: 35210297Ssam switch (*sb) { 35310297Ssam 35410297Ssam case ' ': 35510297Ssam case '\t': 35610297Ssam case '\0': 35710297Ssam goto OUT; /* end of token */ 35810297Ssam 35910297Ssam case '\\': 36010297Ssam sb++; goto S2; /* slurp next character */ 36110297Ssam 36210297Ssam case '"': 36310297Ssam sb++; goto S3; /* slurp quoted string */ 36410297Ssam 36510297Ssam default: 36610297Ssam *ap++ = *sb++; /* add character to token */ 36710297Ssam got_one = 1; 36810297Ssam goto S1; 36910297Ssam } 37010297Ssam 37110297Ssam S2: 37210297Ssam switch (*sb) { 37310297Ssam 37410297Ssam case '\0': 37510297Ssam goto OUT; 37610297Ssam 37710297Ssam default: 37810297Ssam *ap++ = *sb++; 37910297Ssam got_one = 1; 38010297Ssam goto S1; 38110297Ssam } 38210297Ssam 38310297Ssam S3: 38410297Ssam switch (*sb) { 38510297Ssam 38610297Ssam case '\0': 38710297Ssam goto OUT; 38810297Ssam 38910297Ssam case '"': 39010297Ssam sb++; goto S1; 39110297Ssam 39210297Ssam default: 39310297Ssam *ap++ = *sb++; 39410297Ssam got_one = 1; 39510297Ssam goto S3; 39610297Ssam } 39710297Ssam 39810297Ssam OUT: 39910297Ssam if (got_one) 40010297Ssam *ap++ = '\0'; 40110297Ssam argbase = ap; /* update storage pointer */ 40210297Ssam stringbase = sb; /* update scan pointer */ 40326047Sminshall if (got_one) { 40410297Ssam return(tmp); 40526047Sminshall } 40626047Sminshall switch (slrflag) { 40726047Sminshall case 0: 40826047Sminshall slrflag++; 40926047Sminshall break; 41026047Sminshall case 1: 41126047Sminshall slrflag++; 41226047Sminshall altarg = (char *) 0; 41326047Sminshall break; 41426047Sminshall default: 41526047Sminshall break; 41626047Sminshall } 41710297Ssam return((char *)0); 41810297Ssam } 41910297Ssam 42010297Ssam #define HELPINDENT (sizeof ("directory")) 42110297Ssam 42210297Ssam /* 42310297Ssam * Help command. 42410297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42510297Ssam */ 42610297Ssam help(argc, argv) 42710297Ssam int argc; 42810297Ssam char *argv[]; 42910297Ssam { 43033224Sbostic extern struct cmd cmdtab[]; 43110297Ssam register struct cmd *c; 43210297Ssam 43310297Ssam if (argc == 1) { 43426047Sminshall register int i, j, w, k; 43510297Ssam int columns, width = 0, lines; 43610297Ssam extern int NCMDS; 43710297Ssam 43810297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 43910297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 44010297Ssam int len = strlen(c->c_name); 44110297Ssam 44210297Ssam if (len > width) 44310297Ssam width = len; 44410297Ssam } 44510297Ssam width = (width + 8) &~ 7; 44610297Ssam columns = 80 / width; 44710297Ssam if (columns == 0) 44810297Ssam columns = 1; 44910297Ssam lines = (NCMDS + columns - 1) / columns; 45010297Ssam for (i = 0; i < lines; i++) { 45110297Ssam for (j = 0; j < columns; j++) { 45210297Ssam c = cmdtab + j * lines + i; 45326047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 45425907Smckusick printf("%s", c->c_name); 45526047Sminshall } 45626047Sminshall else if (c->c_name) { 45726047Sminshall for (k=0; k < strlen(c->c_name); k++) { 45826495Sminshall (void) putchar(' '); 45926047Sminshall } 46026047Sminshall } 46110297Ssam if (c + lines >= &cmdtab[NCMDS]) { 46210297Ssam printf("\n"); 46310297Ssam break; 46410297Ssam } 46510297Ssam w = strlen(c->c_name); 46610297Ssam while (w < width) { 46710297Ssam w = (w + 8) &~ 7; 46826495Sminshall (void) putchar('\t'); 46910297Ssam } 47010297Ssam } 47110297Ssam } 47210297Ssam return; 47310297Ssam } 47410297Ssam while (--argc > 0) { 47510297Ssam register char *arg; 47610297Ssam arg = *++argv; 47710297Ssam c = getcmd(arg); 47810297Ssam if (c == (struct cmd *)-1) 47910297Ssam printf("?Ambiguous help command %s\n", arg); 48010297Ssam else if (c == (struct cmd *)0) 48110297Ssam printf("?Invalid help command %s\n", arg); 48210297Ssam else 48310297Ssam printf("%-*s\t%s\n", HELPINDENT, 48410297Ssam c->c_name, c->c_help); 48510297Ssam } 48610297Ssam } 487