121740Sdist /* 226047Sminshall * Copyright (c) 1985 Regents of the University of California. 321740Sdist * All rights reserved. The Berkeley software License Agreement 421740Sdist * specifies the terms and conditions for redistribution. 521740Sdist */ 621740Sdist 710297Ssam #ifndef lint 821740Sdist char copyright[] = 926047Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 1021740Sdist All rights reserved.\n"; 1121740Sdist #endif not lint 1210297Ssam 1321740Sdist #ifndef lint 14*26495Sminshall static char sccsid[] = "@(#)main.c 5.6 (Berkeley) 03/07/86"; 1521740Sdist #endif not lint 1621740Sdist 1710297Ssam /* 1810297Ssam * FTP User Program -- Command Interface. 1910297Ssam */ 2026047Sminshall #include "ftp_var.h" 2110297Ssam #include <sys/socket.h> 2210297Ssam #include <sys/ioctl.h> 23*26495Sminshall #include <sys/types.h> 2410297Ssam 2512398Ssam #include <arpa/ftp.h> 2612398Ssam 2710297Ssam #include <signal.h> 2810297Ssam #include <stdio.h> 2910297Ssam #include <errno.h> 3010297Ssam #include <ctype.h> 3110297Ssam #include <netdb.h> 3211352Ssam #include <pwd.h> 3310297Ssam 3410297Ssam 35*26495Sminshall uid_t getuid(); 3610297Ssam int intr(); 3710297Ssam int 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: 88*26495Sminshall fprintf(stdout, 8910297Ssam "ftp: %c: unknown option\n", *cp); 9010297Ssam exit(1); 9110297Ssam } 9210297Ssam argc--, argv++; 9310297Ssam } 9410297Ssam fromatty = isatty(fileno(stdin)); 9510297Ssam /* 9610297Ssam * Set up defaults for FTP. 9710297Ssam */ 98*26495Sminshall (void) strcpy(typename, "ascii"), type = TYPE_A; 99*26495Sminshall (void) strcpy(formname, "non-print"), form = FORM_N; 100*26495Sminshall (void) strcpy(modename, "stream"), mode = MODE_S; 101*26495Sminshall (void) strcpy(structname, "file"), stru = STRU_F; 102*26495Sminshall (void) strcpy(bytename, "8"), bytesize = 8; 10310297Ssam if (fromatty) 10410297Ssam verbose++; 10526047Sminshall cpend = 0; /* no pending replies */ 10626047Sminshall proxy = 0; /* proxy not active */ 10726047Sminshall crflag = 1; /* strip c.r. on ascii gets */ 10811352Ssam /* 10911352Ssam * Set up the home directory in case we're globbing. 11011352Ssam */ 11125907Smckusick cp = getlogin(); 11226047Sminshall if (cp != NULL) { 11325907Smckusick pw = getpwnam(cp); 11426047Sminshall } 11511352Ssam if (pw == NULL) 11611352Ssam pw = getpwuid(getuid()); 11711352Ssam if (pw != NULL) { 11811352Ssam home = homedir; 119*26495Sminshall (void) strcpy(home, pw->pw_dir); 12011352Ssam } 12110297Ssam if (argc > 0) { 12210297Ssam if (setjmp(toplevel)) 12310297Ssam exit(0); 124*26495Sminshall (void) signal(SIGINT, intr); 125*26495Sminshall (void) signal(SIGPIPE, lostpeer); 12610297Ssam setpeer(argc + 1, argv - 1); 12710297Ssam } 12810297Ssam top = setjmp(toplevel) == 0; 12910297Ssam if (top) { 130*26495Sminshall (void) signal(SIGINT, intr); 131*26495Sminshall (void) signal(SIGPIPE, lostpeer); 13210297Ssam } 13310297Ssam for (;;) { 13410297Ssam cmdscanner(top); 13510297Ssam top = 1; 13610297Ssam } 13710297Ssam } 13810297Ssam 13910297Ssam intr() 14010297Ssam { 14110297Ssam 14210297Ssam longjmp(toplevel, 1); 14310297Ssam } 14410297Ssam 14510297Ssam lostpeer() 14610297Ssam { 14710297Ssam extern FILE *cout; 14810297Ssam extern int data; 14910297Ssam 15010297Ssam if (connected) { 15110297Ssam if (cout != NULL) { 152*26495Sminshall (void) shutdown(fileno(cout), 1+1); 153*26495Sminshall (void) fclose(cout); 15410297Ssam cout = NULL; 15510297Ssam } 15610297Ssam if (data >= 0) { 157*26495Sminshall (void) shutdown(data, 1+1); 15810297Ssam (void) close(data); 15910297Ssam data = -1; 16010297Ssam } 16110297Ssam connected = 0; 16210297Ssam } 16326047Sminshall pswitch(1); 16426047Sminshall if (connected) { 16526047Sminshall if (cout != NULL) { 166*26495Sminshall (void) shutdown(fileno(cout), 1+1); 167*26495Sminshall (void) fclose(cout); 16826047Sminshall cout = NULL; 16926047Sminshall } 17026047Sminshall connected = 0; 17126047Sminshall } 17226047Sminshall proxflag = 0; 17326047Sminshall pswitch(0); 17410297Ssam } 17510297Ssam 176*26495Sminshall /*char * 17710297Ssam tail(filename) 17810297Ssam char *filename; 17910297Ssam { 18010297Ssam register char *s; 18110297Ssam 18210297Ssam while (*filename) { 18310297Ssam s = rindex(filename, '/'); 18410297Ssam if (s == NULL) 18510297Ssam break; 18610297Ssam if (s[1]) 18710297Ssam return (s + 1); 18810297Ssam *s = '\0'; 18910297Ssam } 19010297Ssam return (filename); 19110297Ssam } 192*26495Sminshall */ 19310297Ssam /* 19410297Ssam * Command parser. 19510297Ssam */ 19610297Ssam cmdscanner(top) 19710297Ssam int top; 19810297Ssam { 19910297Ssam register struct cmd *c; 20010297Ssam struct cmd *getcmd(); 20110297Ssam extern struct cmd cmdtab[]; 20210297Ssam extern int help(); 20310297Ssam 20410297Ssam if (!top) 205*26495Sminshall (void) putchar('\n'); 20610297Ssam for (;;) { 20710297Ssam if (fromatty) { 20810297Ssam printf("ftp> "); 209*26495Sminshall (void) fflush(stdout); 21010297Ssam } 21113968Ssam if (gets(line) == 0) { 21226082Slepreau if (feof(stdin)) 21326082Slepreau quit(); 21410297Ssam break; 21513968Ssam } 21610297Ssam if (line[0] == 0) 21710297Ssam break; 21810297Ssam makeargv(); 21926047Sminshall if (margc == 0) { 22025907Smckusick continue; 22126047Sminshall } 22210297Ssam c = getcmd(margv[0]); 22310297Ssam if (c == (struct cmd *)-1) { 22410297Ssam printf("?Ambiguous command\n"); 22510297Ssam continue; 22610297Ssam } 22710297Ssam if (c == 0) { 22810297Ssam printf("?Invalid command\n"); 22910297Ssam continue; 23010297Ssam } 23111654Ssam if (c->c_conn && !connected) { 23211654Ssam printf ("Not connected.\n"); 23311654Ssam continue; 23411654Ssam } 23510297Ssam (*c->c_handler)(margc, margv); 23610297Ssam if (bell && c->c_bell) 237*26495Sminshall (void) putchar(CTRL(g)); 23810297Ssam if (c->c_handler != help) 23910297Ssam break; 24010297Ssam } 241*26495Sminshall (void) signal(SIGINT, intr); 242*26495Sminshall (void) signal(SIGPIPE, lostpeer); 24310297Ssam } 24410297Ssam 24510297Ssam struct cmd * 24610297Ssam getcmd(name) 24710297Ssam register char *name; 24810297Ssam { 24910297Ssam register char *p, *q; 25010297Ssam register struct cmd *c, *found; 25110297Ssam register int nmatches, longest; 25210297Ssam 25310297Ssam longest = 0; 25410297Ssam nmatches = 0; 25510297Ssam found = 0; 25610297Ssam for (c = cmdtab; p = c->c_name; c++) { 25710297Ssam for (q = name; *q == *p++; q++) 25810297Ssam if (*q == 0) /* exact match? */ 25910297Ssam return (c); 26010297Ssam if (!*q) { /* the name was a prefix */ 26110297Ssam if (q - name > longest) { 26210297Ssam longest = q - name; 26310297Ssam nmatches = 1; 26410297Ssam found = c; 26510297Ssam } else if (q - name == longest) 26610297Ssam nmatches++; 26710297Ssam } 26810297Ssam } 26910297Ssam if (nmatches > 1) 27010297Ssam return ((struct cmd *)-1); 27110297Ssam return (found); 27210297Ssam } 27310297Ssam 27410297Ssam /* 27510297Ssam * Slice a string up into argc/argv. 27610297Ssam */ 27726047Sminshall 27826047Sminshall int slrflag; 27926047Sminshall 28010297Ssam makeargv() 28110297Ssam { 28210297Ssam char **argp; 28310297Ssam char *slurpstring(); 28410297Ssam 28510297Ssam margc = 0; 28610297Ssam argp = margv; 28710297Ssam stringbase = line; /* scan from first of buffer */ 28810297Ssam argbase = argbuf; /* store from first of buffer */ 28926047Sminshall slrflag = 0; 29026047Sminshall while (*argp++ = slurpstring()) 29110297Ssam margc++; 29210297Ssam } 29310297Ssam 29410297Ssam /* 29510297Ssam * Parse string into argbuf; 29610297Ssam * implemented with FSM to 29710297Ssam * handle quoting and strings 29810297Ssam */ 29910297Ssam char * 30010297Ssam slurpstring() 30110297Ssam { 30210297Ssam int got_one = 0; 30310297Ssam register char *sb = stringbase; 30410297Ssam register char *ap = argbase; 30510297Ssam char *tmp = argbase; /* will return this if token found */ 30610297Ssam 30726047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 30826047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 30926047Sminshall case 0: 31026047Sminshall slrflag++; 31126047Sminshall stringbase++; 31226047Sminshall return ((*sb == '!') ? "!" : "$"); 31326047Sminshall break; 31426047Sminshall case 1: 31526047Sminshall slrflag++; 31626047Sminshall altarg = stringbase; 31726047Sminshall break; 31826047Sminshall default: 31926047Sminshall break; 32026047Sminshall } 32126047Sminshall } 32226047Sminshall 32310297Ssam S0: 32410297Ssam switch (*sb) { 32510297Ssam 32610297Ssam case '\0': 32710297Ssam goto OUT; 32810297Ssam 32910297Ssam case ' ': 33010297Ssam case '\t': 33110297Ssam sb++; goto S0; 33210297Ssam 33310297Ssam default: 33426047Sminshall switch (slrflag) { 33526047Sminshall case 0: 33626047Sminshall slrflag++; 33726047Sminshall break; 33826047Sminshall case 1: 33926047Sminshall slrflag++; 34026047Sminshall altarg = sb; 34126047Sminshall break; 34226047Sminshall default: 34326047Sminshall break; 34426047Sminshall } 34510297Ssam goto S1; 34610297Ssam } 34710297Ssam 34810297Ssam S1: 34910297Ssam switch (*sb) { 35010297Ssam 35110297Ssam case ' ': 35210297Ssam case '\t': 35310297Ssam case '\0': 35410297Ssam goto OUT; /* end of token */ 35510297Ssam 35610297Ssam case '\\': 35710297Ssam sb++; goto S2; /* slurp next character */ 35810297Ssam 35910297Ssam case '"': 36010297Ssam sb++; goto S3; /* slurp quoted string */ 36110297Ssam 36210297Ssam default: 36310297Ssam *ap++ = *sb++; /* add character to token */ 36410297Ssam got_one = 1; 36510297Ssam goto S1; 36610297Ssam } 36710297Ssam 36810297Ssam S2: 36910297Ssam switch (*sb) { 37010297Ssam 37110297Ssam case '\0': 37210297Ssam goto OUT; 37310297Ssam 37410297Ssam default: 37510297Ssam *ap++ = *sb++; 37610297Ssam got_one = 1; 37710297Ssam goto S1; 37810297Ssam } 37910297Ssam 38010297Ssam S3: 38110297Ssam switch (*sb) { 38210297Ssam 38310297Ssam case '\0': 38410297Ssam goto OUT; 38510297Ssam 38610297Ssam case '"': 38710297Ssam sb++; goto S1; 38810297Ssam 38910297Ssam default: 39010297Ssam *ap++ = *sb++; 39110297Ssam got_one = 1; 39210297Ssam goto S3; 39310297Ssam } 39410297Ssam 39510297Ssam OUT: 39610297Ssam if (got_one) 39710297Ssam *ap++ = '\0'; 39810297Ssam argbase = ap; /* update storage pointer */ 39910297Ssam stringbase = sb; /* update scan pointer */ 40026047Sminshall if (got_one) { 40110297Ssam return(tmp); 40226047Sminshall } 40326047Sminshall switch (slrflag) { 40426047Sminshall case 0: 40526047Sminshall slrflag++; 40626047Sminshall break; 40726047Sminshall case 1: 40826047Sminshall slrflag++; 40926047Sminshall altarg = (char *) 0; 41026047Sminshall break; 41126047Sminshall default: 41226047Sminshall break; 41326047Sminshall } 41410297Ssam return((char *)0); 41510297Ssam } 41610297Ssam 41710297Ssam #define HELPINDENT (sizeof ("directory")) 41810297Ssam 41910297Ssam /* 42010297Ssam * Help command. 42110297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42210297Ssam */ 42310297Ssam help(argc, argv) 42410297Ssam int argc; 42510297Ssam char *argv[]; 42610297Ssam { 42710297Ssam register struct cmd *c; 42810297Ssam 42910297Ssam if (argc == 1) { 43026047Sminshall register int i, j, w, k; 43110297Ssam int columns, width = 0, lines; 43210297Ssam extern int NCMDS; 43310297Ssam 43410297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 43510297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 43610297Ssam int len = strlen(c->c_name); 43710297Ssam 43810297Ssam if (len > width) 43910297Ssam width = len; 44010297Ssam } 44110297Ssam width = (width + 8) &~ 7; 44210297Ssam columns = 80 / width; 44310297Ssam if (columns == 0) 44410297Ssam columns = 1; 44510297Ssam lines = (NCMDS + columns - 1) / columns; 44610297Ssam for (i = 0; i < lines; i++) { 44710297Ssam for (j = 0; j < columns; j++) { 44810297Ssam c = cmdtab + j * lines + i; 44926047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 45025907Smckusick printf("%s", c->c_name); 45126047Sminshall } 45226047Sminshall else if (c->c_name) { 45326047Sminshall for (k=0; k < strlen(c->c_name); k++) { 454*26495Sminshall (void) putchar(' '); 45526047Sminshall } 45626047Sminshall } 45710297Ssam if (c + lines >= &cmdtab[NCMDS]) { 45810297Ssam printf("\n"); 45910297Ssam break; 46010297Ssam } 46110297Ssam w = strlen(c->c_name); 46210297Ssam while (w < width) { 46310297Ssam w = (w + 8) &~ 7; 464*26495Sminshall (void) putchar('\t'); 46510297Ssam } 46610297Ssam } 46710297Ssam } 46810297Ssam return; 46910297Ssam } 47010297Ssam while (--argc > 0) { 47110297Ssam register char *arg; 47210297Ssam arg = *++argv; 47310297Ssam c = getcmd(arg); 47410297Ssam if (c == (struct cmd *)-1) 47510297Ssam printf("?Ambiguous help command %s\n", arg); 47610297Ssam else if (c == (struct cmd *)0) 47710297Ssam printf("?Invalid help command %s\n", arg); 47810297Ssam else 47910297Ssam printf("%-*s\t%s\n", HELPINDENT, 48010297Ssam c->c_name, c->c_help); 48110297Ssam } 48210297Ssam } 48310297Ssam 48410297Ssam /* 48510297Ssam * Call routine with argc, argv set from args (terminated by 0). 48610297Ssam */ 487*26495Sminshall /*VARARGS1*/ 48810297Ssam call(routine, args) 48910297Ssam int (*routine)(); 49010297Ssam int args; 49110297Ssam { 49210297Ssam register int *argp; 49310297Ssam register int argc; 49410297Ssam 49510297Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 49610297Ssam ; 49710297Ssam (*routine)(argc, &args); 49810297Ssam } 499