121740Sdist /* 237173Skarels * Copyright (c) 1985, 1989 Regents of the University of California. 333737Sbostic * All rights reserved. 433737Sbostic * 5*42666Sbostic * %sccs.include.redist.c% 621740Sdist */ 721740Sdist 810297Ssam #ifndef lint 921740Sdist char copyright[] = 1037173Skarels "@(#) Copyright (c) 1985, 1989 Regents of the University of California.\n\ 1121740Sdist All rights reserved.\n"; 1233737Sbostic #endif /* not lint */ 1310297Ssam 1421740Sdist #ifndef lint 15*42666Sbostic static char sccsid[] = "@(#)main.c 5.16 (Berkeley) 06/01/90"; 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(); 3638131Srick sig_t intr(); 3738131Srick sig_t lostpeer(); 3811352Ssam extern char *home; 3925907Smckusick char *getlogin(); 4010297Ssam 4110297Ssam main(argc, argv) 4210297Ssam char *argv[]; 4310297Ssam { 4410297Ssam register char *cp; 4510297Ssam int top; 4625907Smckusick struct passwd *pw = NULL; 4711352Ssam char homedir[MAXPATHLEN]; 4810297Ssam 4910297Ssam sp = getservbyname("ftp", "tcp"); 5010297Ssam if (sp == 0) { 5110297Ssam fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 5210297Ssam exit(1); 5310297Ssam } 5411351Ssam doglob = 1; 5511654Ssam interactive = 1; 5611351Ssam autologin = 1; 5710297Ssam argc--, argv++; 5810297Ssam while (argc > 0 && **argv == '-') { 5910297Ssam for (cp = *argv + 1; *cp; cp++) 6010297Ssam switch (*cp) { 6110297Ssam 6210297Ssam case 'd': 6310297Ssam options |= SO_DEBUG; 6410297Ssam debug++; 6510297Ssam break; 6610297Ssam 6710297Ssam case 'v': 6810297Ssam verbose++; 6910297Ssam break; 7010297Ssam 7110297Ssam case 't': 7210297Ssam trace++; 7310297Ssam break; 7410297Ssam 7510297Ssam case 'i': 7611654Ssam interactive = 0; 7710297Ssam break; 7810297Ssam 7910297Ssam case 'n': 8010297Ssam autologin = 0; 8110297Ssam break; 8210297Ssam 8311351Ssam case 'g': 8411351Ssam doglob = 0; 8511351Ssam break; 8611351Ssam 8710297Ssam default: 8826495Sminshall fprintf(stdout, 8910297Ssam "ftp: %c: unknown option\n", *cp); 9010297Ssam exit(1); 9110297Ssam } 9210297Ssam argc--, argv++; 9310297Ssam } 9410297Ssam fromatty = isatty(fileno(stdin)); 9510297Ssam if (fromatty) 9610297Ssam verbose++; 9738131Srick cpend = 0; /* no pending replies */ 9826047Sminshall proxy = 0; /* proxy not active */ 9938131Srick crflag = 1; /* strip c.r. on ascii gets */ 10038131Srick sendport = -1; /* not using ports */ 10111352Ssam /* 10211352Ssam * Set up the home directory in case we're globbing. 10311352Ssam */ 10425907Smckusick cp = getlogin(); 10526047Sminshall if (cp != NULL) { 10625907Smckusick pw = getpwnam(cp); 10726047Sminshall } 10811352Ssam if (pw == NULL) 10911352Ssam pw = getpwuid(getuid()); 11011352Ssam if (pw != NULL) { 11111352Ssam home = homedir; 11226495Sminshall (void) strcpy(home, pw->pw_dir); 11311352Ssam } 11410297Ssam if (argc > 0) { 11510297Ssam if (setjmp(toplevel)) 11610297Ssam exit(0); 11726495Sminshall (void) signal(SIGINT, intr); 11826495Sminshall (void) signal(SIGPIPE, lostpeer); 11910297Ssam setpeer(argc + 1, argv - 1); 12010297Ssam } 12110297Ssam top = setjmp(toplevel) == 0; 12210297Ssam if (top) { 12326495Sminshall (void) signal(SIGINT, intr); 12426495Sminshall (void) signal(SIGPIPE, lostpeer); 12510297Ssam } 12610297Ssam for (;;) { 12710297Ssam cmdscanner(top); 12810297Ssam top = 1; 12910297Ssam } 13010297Ssam } 13110297Ssam 13238131Srick sig_t 13310297Ssam intr() 13410297Ssam { 13510297Ssam 13610297Ssam longjmp(toplevel, 1); 13710297Ssam } 13810297Ssam 13938131Srick sig_t 14010297Ssam lostpeer() 14110297Ssam { 14210297Ssam extern FILE *cout; 14310297Ssam extern int data; 14410297Ssam 14510297Ssam if (connected) { 14610297Ssam if (cout != NULL) { 14726495Sminshall (void) shutdown(fileno(cout), 1+1); 14826495Sminshall (void) fclose(cout); 14910297Ssam cout = NULL; 15010297Ssam } 15110297Ssam if (data >= 0) { 15226495Sminshall (void) shutdown(data, 1+1); 15310297Ssam (void) close(data); 15410297Ssam data = -1; 15510297Ssam } 15610297Ssam connected = 0; 15710297Ssam } 15826047Sminshall pswitch(1); 15926047Sminshall if (connected) { 16026047Sminshall if (cout != NULL) { 16126495Sminshall (void) shutdown(fileno(cout), 1+1); 16226495Sminshall (void) fclose(cout); 16326047Sminshall cout = NULL; 16426047Sminshall } 16526047Sminshall connected = 0; 16626047Sminshall } 16726047Sminshall proxflag = 0; 16826047Sminshall pswitch(0); 16910297Ssam } 17010297Ssam 17126495Sminshall /*char * 17210297Ssam tail(filename) 17310297Ssam char *filename; 17410297Ssam { 17510297Ssam register char *s; 17610297Ssam 17710297Ssam while (*filename) { 17810297Ssam s = rindex(filename, '/'); 17910297Ssam if (s == NULL) 18010297Ssam break; 18110297Ssam if (s[1]) 18210297Ssam return (s + 1); 18310297Ssam *s = '\0'; 18410297Ssam } 18510297Ssam return (filename); 18610297Ssam } 18726495Sminshall */ 18810297Ssam /* 18910297Ssam * Command parser. 19010297Ssam */ 19110297Ssam cmdscanner(top) 19210297Ssam int top; 19310297Ssam { 19410297Ssam register struct cmd *c; 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 } 20513968Ssam if (gets(line) == 0) { 20633374Stef if (feof(stdin) || ferror(stdin)) 20726082Slepreau quit(); 20810297Ssam break; 20913968Ssam } 21010297Ssam if (line[0] == 0) 21110297Ssam break; 21210297Ssam makeargv(); 21326047Sminshall if (margc == 0) { 21425907Smckusick continue; 21526047Sminshall } 21610297Ssam c = getcmd(margv[0]); 21710297Ssam if (c == (struct cmd *)-1) { 21810297Ssam printf("?Ambiguous command\n"); 21910297Ssam continue; 22010297Ssam } 22110297Ssam if (c == 0) { 22210297Ssam printf("?Invalid command\n"); 22310297Ssam continue; 22410297Ssam } 22511654Ssam if (c->c_conn && !connected) { 22611654Ssam printf ("Not connected.\n"); 22711654Ssam continue; 22811654Ssam } 22910297Ssam (*c->c_handler)(margc, margv); 23010297Ssam if (bell && c->c_bell) 23136935Skarels (void) putchar('\007'); 23210297Ssam if (c->c_handler != help) 23310297Ssam break; 23410297Ssam } 23526495Sminshall (void) signal(SIGINT, intr); 23626495Sminshall (void) signal(SIGPIPE, lostpeer); 23710297Ssam } 23810297Ssam 23910297Ssam struct cmd * 24010297Ssam getcmd(name) 24110297Ssam register char *name; 24210297Ssam { 24333224Sbostic extern struct cmd cmdtab[]; 24410297Ssam register char *p, *q; 24510297Ssam register struct cmd *c, *found; 24610297Ssam register int nmatches, longest; 24710297Ssam 24810297Ssam longest = 0; 24910297Ssam nmatches = 0; 25010297Ssam found = 0; 25110297Ssam for (c = cmdtab; p = c->c_name; c++) { 25210297Ssam for (q = name; *q == *p++; q++) 25310297Ssam if (*q == 0) /* exact match? */ 25410297Ssam return (c); 25510297Ssam if (!*q) { /* the name was a prefix */ 25610297Ssam if (q - name > longest) { 25710297Ssam longest = q - name; 25810297Ssam nmatches = 1; 25910297Ssam found = c; 26010297Ssam } else if (q - name == longest) 26110297Ssam nmatches++; 26210297Ssam } 26310297Ssam } 26410297Ssam if (nmatches > 1) 26510297Ssam return ((struct cmd *)-1); 26610297Ssam return (found); 26710297Ssam } 26810297Ssam 26910297Ssam /* 27010297Ssam * Slice a string up into argc/argv. 27110297Ssam */ 27226047Sminshall 27326047Sminshall int slrflag; 27426047Sminshall 27510297Ssam makeargv() 27610297Ssam { 27710297Ssam char **argp; 27810297Ssam char *slurpstring(); 27910297Ssam 28010297Ssam margc = 0; 28110297Ssam argp = margv; 28210297Ssam stringbase = line; /* scan from first of buffer */ 28310297Ssam argbase = argbuf; /* store from first of buffer */ 28426047Sminshall slrflag = 0; 28526047Sminshall while (*argp++ = slurpstring()) 28610297Ssam margc++; 28710297Ssam } 28810297Ssam 28910297Ssam /* 29010297Ssam * Parse string into argbuf; 29110297Ssam * implemented with FSM to 29210297Ssam * handle quoting and strings 29310297Ssam */ 29410297Ssam char * 29510297Ssam slurpstring() 29610297Ssam { 29710297Ssam int got_one = 0; 29810297Ssam register char *sb = stringbase; 29910297Ssam register char *ap = argbase; 30010297Ssam char *tmp = argbase; /* will return this if token found */ 30110297Ssam 30226047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 30326047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 30426047Sminshall case 0: 30526047Sminshall slrflag++; 30626047Sminshall stringbase++; 30726047Sminshall return ((*sb == '!') ? "!" : "$"); 30836935Skarels /* NOTREACHED */ 30926047Sminshall case 1: 31026047Sminshall slrflag++; 31126047Sminshall altarg = stringbase; 31226047Sminshall break; 31326047Sminshall default: 31426047Sminshall break; 31526047Sminshall } 31626047Sminshall } 31726047Sminshall 31810297Ssam S0: 31910297Ssam switch (*sb) { 32010297Ssam 32110297Ssam case '\0': 32210297Ssam goto OUT; 32310297Ssam 32410297Ssam case ' ': 32510297Ssam case '\t': 32610297Ssam sb++; goto S0; 32710297Ssam 32810297Ssam default: 32926047Sminshall switch (slrflag) { 33026047Sminshall case 0: 33126047Sminshall slrflag++; 33226047Sminshall break; 33326047Sminshall case 1: 33426047Sminshall slrflag++; 33526047Sminshall altarg = sb; 33626047Sminshall break; 33726047Sminshall default: 33826047Sminshall break; 33926047Sminshall } 34010297Ssam goto S1; 34110297Ssam } 34210297Ssam 34310297Ssam S1: 34410297Ssam switch (*sb) { 34510297Ssam 34610297Ssam case ' ': 34710297Ssam case '\t': 34810297Ssam case '\0': 34910297Ssam goto OUT; /* end of token */ 35010297Ssam 35110297Ssam case '\\': 35210297Ssam sb++; goto S2; /* slurp next character */ 35310297Ssam 35410297Ssam case '"': 35510297Ssam sb++; goto S3; /* slurp quoted string */ 35610297Ssam 35710297Ssam default: 35810297Ssam *ap++ = *sb++; /* add character to token */ 35910297Ssam got_one = 1; 36010297Ssam goto S1; 36110297Ssam } 36210297Ssam 36310297Ssam S2: 36410297Ssam switch (*sb) { 36510297Ssam 36610297Ssam case '\0': 36710297Ssam goto OUT; 36810297Ssam 36910297Ssam default: 37010297Ssam *ap++ = *sb++; 37110297Ssam got_one = 1; 37210297Ssam goto S1; 37310297Ssam } 37410297Ssam 37510297Ssam S3: 37610297Ssam switch (*sb) { 37710297Ssam 37810297Ssam case '\0': 37910297Ssam goto OUT; 38010297Ssam 38110297Ssam case '"': 38210297Ssam sb++; goto S1; 38310297Ssam 38410297Ssam default: 38510297Ssam *ap++ = *sb++; 38610297Ssam got_one = 1; 38710297Ssam goto S3; 38810297Ssam } 38910297Ssam 39010297Ssam OUT: 39110297Ssam if (got_one) 39210297Ssam *ap++ = '\0'; 39310297Ssam argbase = ap; /* update storage pointer */ 39410297Ssam stringbase = sb; /* update scan pointer */ 39526047Sminshall if (got_one) { 39610297Ssam return(tmp); 39726047Sminshall } 39826047Sminshall switch (slrflag) { 39926047Sminshall case 0: 40026047Sminshall slrflag++; 40126047Sminshall break; 40226047Sminshall case 1: 40326047Sminshall slrflag++; 40426047Sminshall altarg = (char *) 0; 40526047Sminshall break; 40626047Sminshall default: 40726047Sminshall break; 40826047Sminshall } 40910297Ssam return((char *)0); 41010297Ssam } 41110297Ssam 41210297Ssam #define HELPINDENT (sizeof ("directory")) 41310297Ssam 41410297Ssam /* 41510297Ssam * Help command. 41610297Ssam * Call each command handler with argc == 0 and argv[0] == name. 41710297Ssam */ 41810297Ssam help(argc, argv) 41910297Ssam int argc; 42010297Ssam char *argv[]; 42110297Ssam { 42233224Sbostic extern struct cmd cmdtab[]; 42310297Ssam register struct cmd *c; 42410297Ssam 42510297Ssam if (argc == 1) { 42626047Sminshall register int i, j, w, k; 42710297Ssam int columns, width = 0, lines; 42810297Ssam extern int NCMDS; 42910297Ssam 43010297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 43110297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 43210297Ssam int len = strlen(c->c_name); 43310297Ssam 43410297Ssam if (len > width) 43510297Ssam width = len; 43610297Ssam } 43710297Ssam width = (width + 8) &~ 7; 43810297Ssam columns = 80 / width; 43910297Ssam if (columns == 0) 44010297Ssam columns = 1; 44110297Ssam lines = (NCMDS + columns - 1) / columns; 44210297Ssam for (i = 0; i < lines; i++) { 44310297Ssam for (j = 0; j < columns; j++) { 44410297Ssam c = cmdtab + j * lines + i; 44526047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 44625907Smckusick printf("%s", c->c_name); 44726047Sminshall } 44826047Sminshall else if (c->c_name) { 44926047Sminshall for (k=0; k < strlen(c->c_name); k++) { 45026495Sminshall (void) putchar(' '); 45126047Sminshall } 45226047Sminshall } 45310297Ssam if (c + lines >= &cmdtab[NCMDS]) { 45410297Ssam printf("\n"); 45510297Ssam break; 45610297Ssam } 45710297Ssam w = strlen(c->c_name); 45810297Ssam while (w < width) { 45910297Ssam w = (w + 8) &~ 7; 46026495Sminshall (void) putchar('\t'); 46110297Ssam } 46210297Ssam } 46310297Ssam } 46410297Ssam return; 46510297Ssam } 46610297Ssam while (--argc > 0) { 46710297Ssam register char *arg; 46810297Ssam arg = *++argv; 46910297Ssam c = getcmd(arg); 47010297Ssam if (c == (struct cmd *)-1) 47110297Ssam printf("?Ambiguous help command %s\n", arg); 47210297Ssam else if (c == (struct cmd *)0) 47310297Ssam printf("?Invalid help command %s\n", arg); 47410297Ssam else 47510297Ssam printf("%-*s\t%s\n", HELPINDENT, 47610297Ssam c->c_name, c->c_help); 47710297Ssam } 47810297Ssam } 479