121740Sdist /* 237173Skarels * Copyright (c) 1985, 1989 Regents of the University of California. 333737Sbostic * All rights reserved. 433737Sbostic * 542666Sbostic * %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*45260Sbostic static char sccsid[] = "@(#)main.c 5.17 (Berkeley) 09/28/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; 195*45260Sbostic register int l; 19610297Ssam struct cmd *getcmd(); 19710297Ssam extern int help(); 19810297Ssam 19910297Ssam if (!top) 20026495Sminshall (void) putchar('\n'); 20110297Ssam for (;;) { 20210297Ssam if (fromatty) { 20310297Ssam printf("ftp> "); 20426495Sminshall (void) fflush(stdout); 20510297Ssam } 206*45260Sbostic if (fgets(line, sizeof line, stdin) == NULL) 207*45260Sbostic quit(); 208*45260Sbostic l = strlen(line); 209*45260Sbostic if (l == 0) 21010297Ssam break; 211*45260Sbostic if (line[--l] == '\n') { 212*45260Sbostic if (l == 0) 213*45260Sbostic break; 214*45260Sbostic line[l] = '\0'; 215*45260Sbostic } else if (l == sizeof(line) - 2) { 216*45260Sbostic printf("sorry, input line too long\n"); 217*45260Sbostic while ((l = getchar()) != '\n' && l != EOF) 218*45260Sbostic /* void */; 21910297Ssam break; 220*45260Sbostic } /* else it was a line without a newline */ 22110297Ssam makeargv(); 22226047Sminshall if (margc == 0) { 22325907Smckusick continue; 22426047Sminshall } 22510297Ssam c = getcmd(margv[0]); 22610297Ssam if (c == (struct cmd *)-1) { 22710297Ssam printf("?Ambiguous command\n"); 22810297Ssam continue; 22910297Ssam } 23010297Ssam if (c == 0) { 23110297Ssam printf("?Invalid command\n"); 23210297Ssam continue; 23310297Ssam } 23411654Ssam if (c->c_conn && !connected) { 235*45260Sbostic printf("Not connected.\n"); 23611654Ssam continue; 23711654Ssam } 23810297Ssam (*c->c_handler)(margc, margv); 23910297Ssam if (bell && c->c_bell) 24036935Skarels (void) putchar('\007'); 24110297Ssam if (c->c_handler != help) 24210297Ssam break; 24310297Ssam } 24426495Sminshall (void) signal(SIGINT, intr); 24526495Sminshall (void) signal(SIGPIPE, lostpeer); 24610297Ssam } 24710297Ssam 24810297Ssam struct cmd * 24910297Ssam getcmd(name) 25010297Ssam register char *name; 25110297Ssam { 25233224Sbostic extern struct cmd cmdtab[]; 25310297Ssam register char *p, *q; 25410297Ssam register struct cmd *c, *found; 25510297Ssam register int nmatches, longest; 25610297Ssam 25710297Ssam longest = 0; 25810297Ssam nmatches = 0; 25910297Ssam found = 0; 26010297Ssam for (c = cmdtab; p = c->c_name; c++) { 26110297Ssam for (q = name; *q == *p++; q++) 26210297Ssam if (*q == 0) /* exact match? */ 26310297Ssam return (c); 26410297Ssam if (!*q) { /* the name was a prefix */ 26510297Ssam if (q - name > longest) { 26610297Ssam longest = q - name; 26710297Ssam nmatches = 1; 26810297Ssam found = c; 26910297Ssam } else if (q - name == longest) 27010297Ssam nmatches++; 27110297Ssam } 27210297Ssam } 27310297Ssam if (nmatches > 1) 27410297Ssam return ((struct cmd *)-1); 27510297Ssam return (found); 27610297Ssam } 27710297Ssam 27810297Ssam /* 27910297Ssam * Slice a string up into argc/argv. 28010297Ssam */ 28126047Sminshall 28226047Sminshall int slrflag; 28326047Sminshall 28410297Ssam makeargv() 28510297Ssam { 28610297Ssam char **argp; 28710297Ssam char *slurpstring(); 28810297Ssam 28910297Ssam margc = 0; 29010297Ssam argp = margv; 29110297Ssam stringbase = line; /* scan from first of buffer */ 29210297Ssam argbase = argbuf; /* store from first of buffer */ 29326047Sminshall slrflag = 0; 29426047Sminshall while (*argp++ = slurpstring()) 29510297Ssam margc++; 29610297Ssam } 29710297Ssam 29810297Ssam /* 29910297Ssam * Parse string into argbuf; 30010297Ssam * implemented with FSM to 30110297Ssam * handle quoting and strings 30210297Ssam */ 30310297Ssam char * 30410297Ssam slurpstring() 30510297Ssam { 30610297Ssam int got_one = 0; 30710297Ssam register char *sb = stringbase; 30810297Ssam register char *ap = argbase; 30910297Ssam char *tmp = argbase; /* will return this if token found */ 31010297Ssam 31126047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 31226047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 31326047Sminshall case 0: 31426047Sminshall slrflag++; 31526047Sminshall stringbase++; 31626047Sminshall return ((*sb == '!') ? "!" : "$"); 31736935Skarels /* NOTREACHED */ 31826047Sminshall case 1: 31926047Sminshall slrflag++; 32026047Sminshall altarg = stringbase; 32126047Sminshall break; 32226047Sminshall default: 32326047Sminshall break; 32426047Sminshall } 32526047Sminshall } 32626047Sminshall 32710297Ssam S0: 32810297Ssam switch (*sb) { 32910297Ssam 33010297Ssam case '\0': 33110297Ssam goto OUT; 33210297Ssam 33310297Ssam case ' ': 33410297Ssam case '\t': 33510297Ssam sb++; goto S0; 33610297Ssam 33710297Ssam default: 33826047Sminshall switch (slrflag) { 33926047Sminshall case 0: 34026047Sminshall slrflag++; 34126047Sminshall break; 34226047Sminshall case 1: 34326047Sminshall slrflag++; 34426047Sminshall altarg = sb; 34526047Sminshall break; 34626047Sminshall default: 34726047Sminshall break; 34826047Sminshall } 34910297Ssam goto S1; 35010297Ssam } 35110297Ssam 35210297Ssam S1: 35310297Ssam switch (*sb) { 35410297Ssam 35510297Ssam case ' ': 35610297Ssam case '\t': 35710297Ssam case '\0': 35810297Ssam goto OUT; /* end of token */ 35910297Ssam 36010297Ssam case '\\': 36110297Ssam sb++; goto S2; /* slurp next character */ 36210297Ssam 36310297Ssam case '"': 36410297Ssam sb++; goto S3; /* slurp quoted string */ 36510297Ssam 36610297Ssam default: 36710297Ssam *ap++ = *sb++; /* add character to token */ 36810297Ssam got_one = 1; 36910297Ssam goto S1; 37010297Ssam } 37110297Ssam 37210297Ssam S2: 37310297Ssam switch (*sb) { 37410297Ssam 37510297Ssam case '\0': 37610297Ssam goto OUT; 37710297Ssam 37810297Ssam default: 37910297Ssam *ap++ = *sb++; 38010297Ssam got_one = 1; 38110297Ssam goto S1; 38210297Ssam } 38310297Ssam 38410297Ssam S3: 38510297Ssam switch (*sb) { 38610297Ssam 38710297Ssam case '\0': 38810297Ssam goto OUT; 38910297Ssam 39010297Ssam case '"': 39110297Ssam sb++; goto S1; 39210297Ssam 39310297Ssam default: 39410297Ssam *ap++ = *sb++; 39510297Ssam got_one = 1; 39610297Ssam goto S3; 39710297Ssam } 39810297Ssam 39910297Ssam OUT: 40010297Ssam if (got_one) 40110297Ssam *ap++ = '\0'; 40210297Ssam argbase = ap; /* update storage pointer */ 40310297Ssam stringbase = sb; /* update scan pointer */ 40426047Sminshall if (got_one) { 40510297Ssam return(tmp); 40626047Sminshall } 40726047Sminshall switch (slrflag) { 40826047Sminshall case 0: 40926047Sminshall slrflag++; 41026047Sminshall break; 41126047Sminshall case 1: 41226047Sminshall slrflag++; 41326047Sminshall altarg = (char *) 0; 41426047Sminshall break; 41526047Sminshall default: 41626047Sminshall break; 41726047Sminshall } 41810297Ssam return((char *)0); 41910297Ssam } 42010297Ssam 42110297Ssam #define HELPINDENT (sizeof ("directory")) 42210297Ssam 42310297Ssam /* 42410297Ssam * Help command. 42510297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42610297Ssam */ 42710297Ssam help(argc, argv) 42810297Ssam int argc; 42910297Ssam char *argv[]; 43010297Ssam { 43133224Sbostic extern struct cmd cmdtab[]; 43210297Ssam register struct cmd *c; 43310297Ssam 43410297Ssam if (argc == 1) { 43526047Sminshall register int i, j, w, k; 43610297Ssam int columns, width = 0, lines; 43710297Ssam extern int NCMDS; 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) { 47610297Ssam register 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