121740Sdist /* 221740Sdist * Copyright (c) 1983 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[] = 921740Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 1021740Sdist All rights reserved.\n"; 1121740Sdist #endif not lint 1210297Ssam 1321740Sdist #ifndef lint 14*25907Smckusick static char sccsid[] = "@(#)main.c 5.3 (Berkeley) 01/13/86"; 1521740Sdist #endif not lint 1621740Sdist 1710297Ssam /* 1810297Ssam * FTP User Program -- Command Interface. 1910297Ssam */ 2011352Ssam #include <sys/param.h> 2110297Ssam #include <sys/socket.h> 2210297Ssam #include <sys/ioctl.h> 2310297Ssam 2412398Ssam #include <arpa/ftp.h> 2512398Ssam 2610297Ssam #include <signal.h> 2710297Ssam #include <stdio.h> 2810297Ssam #include <errno.h> 2910297Ssam #include <ctype.h> 3010297Ssam #include <netdb.h> 3111352Ssam #include <pwd.h> 3210297Ssam 3310297Ssam #include "ftp_var.h" 3410297Ssam 3510297Ssam int intr(); 3610297Ssam int lostpeer(); 3711352Ssam extern char *home; 38*25907Smckusick char *getlogin(); 3910297Ssam 4010297Ssam main(argc, argv) 4110297Ssam char *argv[]; 4210297Ssam { 4310297Ssam register char *cp; 4410297Ssam int top; 45*25907Smckusick 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: 8710297Ssam fprintf(stderr, 8810297Ssam "ftp: %c: unknown option\n", *cp); 8910297Ssam exit(1); 9010297Ssam } 9110297Ssam argc--, argv++; 9210297Ssam } 9310297Ssam fromatty = isatty(fileno(stdin)); 9410297Ssam /* 9510297Ssam * Set up defaults for FTP. 9610297Ssam */ 9710297Ssam strcpy(typename, "ascii"), type = TYPE_A; 9810297Ssam strcpy(formname, "non-print"), form = FORM_N; 9910297Ssam strcpy(modename, "stream"), mode = MODE_S; 10010297Ssam strcpy(structname, "file"), stru = STRU_F; 10111227Ssam strcpy(bytename, "8"), bytesize = 8; 10210297Ssam if (fromatty) 10310297Ssam verbose++; 10411352Ssam /* 10511352Ssam * Set up the home directory in case we're globbing. 10611352Ssam */ 107*25907Smckusick cp = getlogin(); 108*25907Smckusick if (cp != NULL) 109*25907Smckusick pw = getpwnam(cp); 11011352Ssam if (pw == NULL) 11111352Ssam pw = getpwuid(getuid()); 11211352Ssam if (pw != NULL) { 11311352Ssam home = homedir; 11411352Ssam strcpy(home, pw->pw_dir); 11511352Ssam } 11610297Ssam if (argc > 0) { 11710297Ssam if (setjmp(toplevel)) 11810297Ssam exit(0); 11912993Ssam signal(SIGINT, intr); 12012993Ssam signal(SIGPIPE, lostpeer); 12110297Ssam setpeer(argc + 1, argv - 1); 12210297Ssam } 12310297Ssam top = setjmp(toplevel) == 0; 12410297Ssam if (top) { 12512993Ssam signal(SIGINT, intr); 12612993Ssam signal(SIGPIPE, lostpeer); 12710297Ssam } 12810297Ssam for (;;) { 12910297Ssam cmdscanner(top); 13010297Ssam top = 1; 13110297Ssam } 13210297Ssam } 13310297Ssam 13410297Ssam intr() 13510297Ssam { 13610297Ssam 13710297Ssam longjmp(toplevel, 1); 13810297Ssam } 13910297Ssam 14010297Ssam lostpeer() 14110297Ssam { 14210297Ssam extern FILE *cout; 14310297Ssam extern int data; 14410297Ssam 14510297Ssam if (connected) { 14610297Ssam if (cout != NULL) { 14711239Ssam shutdown(fileno(cout), 1+1); 14810297Ssam fclose(cout); 14910297Ssam cout = NULL; 15010297Ssam } 15110297Ssam if (data >= 0) { 15211239Ssam shutdown(data, 1+1); 15310297Ssam (void) close(data); 15410297Ssam data = -1; 15510297Ssam } 15610297Ssam connected = 0; 15710297Ssam } 15810297Ssam longjmp(toplevel, 1); 15910297Ssam } 16010297Ssam 16110297Ssam char * 16210297Ssam tail(filename) 16310297Ssam char *filename; 16410297Ssam { 16510297Ssam register char *s; 16610297Ssam 16710297Ssam while (*filename) { 16810297Ssam s = rindex(filename, '/'); 16910297Ssam if (s == NULL) 17010297Ssam break; 17110297Ssam if (s[1]) 17210297Ssam return (s + 1); 17310297Ssam *s = '\0'; 17410297Ssam } 17510297Ssam return (filename); 17610297Ssam } 17710297Ssam 17810297Ssam /* 17910297Ssam * Command parser. 18010297Ssam */ 18110297Ssam cmdscanner(top) 18210297Ssam int top; 18310297Ssam { 18410297Ssam register struct cmd *c; 18510297Ssam struct cmd *getcmd(); 18610297Ssam extern struct cmd cmdtab[]; 18710297Ssam extern int help(); 18810297Ssam 18910297Ssam if (!top) 19010297Ssam putchar('\n'); 19110297Ssam for (;;) { 19210297Ssam if (fromatty) { 19310297Ssam printf("ftp> "); 19410297Ssam fflush(stdout); 19510297Ssam } 19613968Ssam if (gets(line) == 0) { 19725802Slepreau if (feof(stdin)) 19825802Slepreau quit(); 19910297Ssam break; 20013968Ssam } 20110297Ssam if (line[0] == 0) 20210297Ssam break; 20310297Ssam makeargv(); 204*25907Smckusick if (margc == 0) 205*25907Smckusick continue; 20610297Ssam c = getcmd(margv[0]); 20710297Ssam if (c == (struct cmd *)-1) { 20810297Ssam printf("?Ambiguous command\n"); 20910297Ssam continue; 21010297Ssam } 21110297Ssam if (c == 0) { 21210297Ssam printf("?Invalid command\n"); 21310297Ssam continue; 21410297Ssam } 21511654Ssam if (c->c_conn && !connected) { 21611654Ssam printf ("Not connected.\n"); 21711654Ssam continue; 21811654Ssam } 21910297Ssam (*c->c_handler)(margc, margv); 22010297Ssam if (bell && c->c_bell) 22110297Ssam putchar(CTRL(g)); 22210297Ssam if (c->c_handler != help) 22310297Ssam break; 22410297Ssam } 22510297Ssam } 22610297Ssam 22710297Ssam struct cmd * 22810297Ssam getcmd(name) 22910297Ssam register char *name; 23010297Ssam { 23110297Ssam register char *p, *q; 23210297Ssam register struct cmd *c, *found; 23310297Ssam register int nmatches, longest; 23410297Ssam 23510297Ssam longest = 0; 23610297Ssam nmatches = 0; 23710297Ssam found = 0; 23810297Ssam for (c = cmdtab; p = c->c_name; c++) { 23910297Ssam for (q = name; *q == *p++; q++) 24010297Ssam if (*q == 0) /* exact match? */ 24110297Ssam return (c); 24210297Ssam if (!*q) { /* the name was a prefix */ 24310297Ssam if (q - name > longest) { 24410297Ssam longest = q - name; 24510297Ssam nmatches = 1; 24610297Ssam found = c; 24710297Ssam } else if (q - name == longest) 24810297Ssam nmatches++; 24910297Ssam } 25010297Ssam } 25110297Ssam if (nmatches > 1) 25210297Ssam return ((struct cmd *)-1); 25310297Ssam return (found); 25410297Ssam } 25510297Ssam 25610297Ssam /* 25710297Ssam * Slice a string up into argc/argv. 25810297Ssam */ 25910297Ssam makeargv() 26010297Ssam { 26110297Ssam char **argp; 26210297Ssam char *slurpstring(); 26310297Ssam 26410297Ssam margc = 0; 26510297Ssam argp = margv; 26610297Ssam stringbase = line; /* scan from first of buffer */ 26710297Ssam argbase = argbuf; /* store from first of buffer */ 268*25907Smckusick while (*stringbase == ' ' || *stringbase == '\t') 269*25907Smckusick stringbase++; /* skip initial white space */ 270*25907Smckusick if (*stringbase == '!') { /* handle shell escapes specially */ 271*25907Smckusick stringbase++; 272*25907Smckusick *argp++ = "!"; /* command name is "!" */ 27310297Ssam margc++; 274*25907Smckusick while (*stringbase == ' ' || *stringbase == '\t') 275*25907Smckusick stringbase++; /* skip white space */ 276*25907Smckusick if (*stringbase != '\0') { 277*25907Smckusick *argp++ = stringbase; /* argument is entire command string */ 278*25907Smckusick margc++; 279*25907Smckusick } 280*25907Smckusick *argp++ = NULL; 281*25907Smckusick } else { 282*25907Smckusick while (*argp++ = slurpstring()) 283*25907Smckusick margc++; 284*25907Smckusick } 28510297Ssam } 28610297Ssam 28710297Ssam /* 28810297Ssam * Parse string into argbuf; 28910297Ssam * implemented with FSM to 29010297Ssam * handle quoting and strings 29110297Ssam */ 29210297Ssam char * 29310297Ssam slurpstring() 29410297Ssam { 29510297Ssam int got_one = 0; 29610297Ssam register char *sb = stringbase; 29710297Ssam register char *ap = argbase; 29810297Ssam char *tmp = argbase; /* will return this if token found */ 29910297Ssam 30010297Ssam S0: 30110297Ssam switch (*sb) { 30210297Ssam 30310297Ssam case '\0': 30410297Ssam goto OUT; 30510297Ssam 30610297Ssam case ' ': 30710297Ssam case '\t': 30810297Ssam sb++; goto S0; 30910297Ssam 31010297Ssam default: 31110297Ssam goto S1; 31210297Ssam } 31310297Ssam 31410297Ssam S1: 31510297Ssam switch (*sb) { 31610297Ssam 31710297Ssam case ' ': 31810297Ssam case '\t': 31910297Ssam case '\0': 32010297Ssam goto OUT; /* end of token */ 32110297Ssam 32210297Ssam case '\\': 32310297Ssam sb++; goto S2; /* slurp next character */ 32410297Ssam 32510297Ssam case '"': 32610297Ssam sb++; goto S3; /* slurp quoted string */ 32710297Ssam 32810297Ssam default: 32910297Ssam *ap++ = *sb++; /* add character to token */ 33010297Ssam got_one = 1; 33110297Ssam goto S1; 33210297Ssam } 33310297Ssam 33410297Ssam S2: 33510297Ssam switch (*sb) { 33610297Ssam 33710297Ssam case '\0': 33810297Ssam goto OUT; 33910297Ssam 34010297Ssam default: 34110297Ssam *ap++ = *sb++; 34210297Ssam got_one = 1; 34310297Ssam goto S1; 34410297Ssam } 34510297Ssam 34610297Ssam S3: 34710297Ssam switch (*sb) { 34810297Ssam 34910297Ssam case '\0': 35010297Ssam goto OUT; 35110297Ssam 35210297Ssam case '"': 35310297Ssam sb++; goto S1; 35410297Ssam 35510297Ssam default: 35610297Ssam *ap++ = *sb++; 35710297Ssam got_one = 1; 35810297Ssam goto S3; 35910297Ssam } 36010297Ssam 36110297Ssam OUT: 36210297Ssam if (got_one) 36310297Ssam *ap++ = '\0'; 36410297Ssam argbase = ap; /* update storage pointer */ 36510297Ssam stringbase = sb; /* update scan pointer */ 36610297Ssam if (got_one) 36710297Ssam return(tmp); 36810297Ssam return((char *)0); 36910297Ssam } 37010297Ssam 37110297Ssam #define HELPINDENT (sizeof ("directory")) 37210297Ssam 37310297Ssam /* 37410297Ssam * Help command. 37510297Ssam * Call each command handler with argc == 0 and argv[0] == name. 37610297Ssam */ 37710297Ssam help(argc, argv) 37810297Ssam int argc; 37910297Ssam char *argv[]; 38010297Ssam { 38110297Ssam register struct cmd *c; 38210297Ssam 38310297Ssam if (argc == 1) { 38410297Ssam register int i, j, w; 38510297Ssam int columns, width = 0, lines; 38610297Ssam extern int NCMDS; 38710297Ssam 38810297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 38910297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 39010297Ssam int len = strlen(c->c_name); 39110297Ssam 39210297Ssam if (len > width) 39310297Ssam width = len; 39410297Ssam } 39510297Ssam width = (width + 8) &~ 7; 39610297Ssam columns = 80 / width; 39710297Ssam if (columns == 0) 39810297Ssam columns = 1; 39910297Ssam lines = (NCMDS + columns - 1) / columns; 40010297Ssam for (i = 0; i < lines; i++) { 40110297Ssam for (j = 0; j < columns; j++) { 40210297Ssam c = cmdtab + j * lines + i; 403*25907Smckusick if (c->c_name) 404*25907Smckusick printf("%s", c->c_name); 40510297Ssam if (c + lines >= &cmdtab[NCMDS]) { 40610297Ssam printf("\n"); 40710297Ssam break; 40810297Ssam } 40910297Ssam w = strlen(c->c_name); 41010297Ssam while (w < width) { 41110297Ssam w = (w + 8) &~ 7; 41210297Ssam putchar('\t'); 41310297Ssam } 41410297Ssam } 41510297Ssam } 41610297Ssam return; 41710297Ssam } 41810297Ssam while (--argc > 0) { 41910297Ssam register char *arg; 42010297Ssam arg = *++argv; 42110297Ssam c = getcmd(arg); 42210297Ssam if (c == (struct cmd *)-1) 42310297Ssam printf("?Ambiguous help command %s\n", arg); 42410297Ssam else if (c == (struct cmd *)0) 42510297Ssam printf("?Invalid help command %s\n", arg); 42610297Ssam else 42710297Ssam printf("%-*s\t%s\n", HELPINDENT, 42810297Ssam c->c_name, c->c_help); 42910297Ssam } 43010297Ssam } 43110297Ssam 43210297Ssam /* 43310297Ssam * Call routine with argc, argv set from args (terminated by 0). 43410297Ssam */ 43510297Ssam /* VARARGS2 */ 43610297Ssam call(routine, args) 43710297Ssam int (*routine)(); 43810297Ssam int args; 43910297Ssam { 44010297Ssam register int *argp; 44110297Ssam register int argc; 44210297Ssam 44310297Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 44410297Ssam ; 44510297Ssam (*routine)(argc, &args); 44610297Ssam } 447