121740Sdist /* 226047Sminshall * Copyright (c) 1985 Regents of the University of California. 3*33737Sbostic * All rights reserved. 4*33737Sbostic * 5*33737Sbostic * Redistribution and use in source and binary forms are permitted 6*33737Sbostic * provided that this notice is preserved and that due credit is given 7*33737Sbostic * to the University of California at Berkeley. The name of the University 8*33737Sbostic * may not be used to endorse or promote products derived from this 9*33737Sbostic * software without specific prior written permission. This software 10*33737Sbostic * is provided ``as is'' without express or implied warranty. 1121740Sdist */ 1221740Sdist 1310297Ssam #ifndef lint 1421740Sdist char copyright[] = 1526047Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 1621740Sdist All rights reserved.\n"; 17*33737Sbostic #endif /* not lint */ 1810297Ssam 1921740Sdist #ifndef lint 20*33737Sbostic static char sccsid[] = "@(#)main.c 5.10 (Berkeley) 03/14/88"; 21*33737Sbostic #endif /* not lint */ 2221740Sdist 2310297Ssam /* 2410297Ssam * FTP User Program -- Command Interface. 2510297Ssam */ 2626047Sminshall #include "ftp_var.h" 2710297Ssam #include <sys/socket.h> 2810297Ssam #include <sys/ioctl.h> 2926495Sminshall #include <sys/types.h> 3010297Ssam 3112398Ssam #include <arpa/ftp.h> 3212398Ssam 3310297Ssam #include <signal.h> 3410297Ssam #include <stdio.h> 3510297Ssam #include <errno.h> 3610297Ssam #include <ctype.h> 3710297Ssam #include <netdb.h> 3811352Ssam #include <pwd.h> 3910297Ssam 4010297Ssam 4126495Sminshall uid_t getuid(); 4210297Ssam int intr(); 4310297Ssam int lostpeer(); 4411352Ssam extern char *home; 4525907Smckusick char *getlogin(); 4610297Ssam 4710297Ssam main(argc, argv) 4810297Ssam char *argv[]; 4910297Ssam { 5010297Ssam register char *cp; 5110297Ssam int top; 5225907Smckusick struct passwd *pw = NULL; 5311352Ssam char homedir[MAXPATHLEN]; 5410297Ssam 5510297Ssam sp = getservbyname("ftp", "tcp"); 5610297Ssam if (sp == 0) { 5710297Ssam fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 5810297Ssam exit(1); 5910297Ssam } 6011351Ssam doglob = 1; 6111654Ssam interactive = 1; 6211351Ssam autologin = 1; 6310297Ssam argc--, argv++; 6410297Ssam while (argc > 0 && **argv == '-') { 6510297Ssam for (cp = *argv + 1; *cp; cp++) 6610297Ssam switch (*cp) { 6710297Ssam 6810297Ssam case 'd': 6910297Ssam options |= SO_DEBUG; 7010297Ssam debug++; 7110297Ssam break; 7210297Ssam 7310297Ssam case 'v': 7410297Ssam verbose++; 7510297Ssam break; 7610297Ssam 7710297Ssam case 't': 7810297Ssam trace++; 7910297Ssam break; 8010297Ssam 8110297Ssam case 'i': 8211654Ssam interactive = 0; 8310297Ssam break; 8410297Ssam 8510297Ssam case 'n': 8610297Ssam autologin = 0; 8710297Ssam break; 8810297Ssam 8911351Ssam case 'g': 9011351Ssam doglob = 0; 9111351Ssam break; 9211351Ssam 9310297Ssam default: 9426495Sminshall fprintf(stdout, 9510297Ssam "ftp: %c: unknown option\n", *cp); 9610297Ssam exit(1); 9710297Ssam } 9810297Ssam argc--, argv++; 9910297Ssam } 10010297Ssam fromatty = isatty(fileno(stdin)); 10110297Ssam /* 10210297Ssam * Set up defaults for FTP. 10310297Ssam */ 10426495Sminshall (void) strcpy(typename, "ascii"), type = TYPE_A; 10526495Sminshall (void) strcpy(formname, "non-print"), form = FORM_N; 10626495Sminshall (void) strcpy(modename, "stream"), mode = MODE_S; 10726495Sminshall (void) strcpy(structname, "file"), stru = STRU_F; 10826495Sminshall (void) strcpy(bytename, "8"), bytesize = 8; 10910297Ssam if (fromatty) 11010297Ssam verbose++; 11126047Sminshall cpend = 0; /* no pending replies */ 11226047Sminshall proxy = 0; /* proxy not active */ 11326047Sminshall crflag = 1; /* strip c.r. on ascii gets */ 11411352Ssam /* 11511352Ssam * Set up the home directory in case we're globbing. 11611352Ssam */ 11725907Smckusick cp = getlogin(); 11826047Sminshall if (cp != NULL) { 11925907Smckusick pw = getpwnam(cp); 12026047Sminshall } 12111352Ssam if (pw == NULL) 12211352Ssam pw = getpwuid(getuid()); 12311352Ssam if (pw != NULL) { 12411352Ssam home = homedir; 12526495Sminshall (void) strcpy(home, pw->pw_dir); 12611352Ssam } 12710297Ssam if (argc > 0) { 12810297Ssam if (setjmp(toplevel)) 12910297Ssam exit(0); 13026495Sminshall (void) signal(SIGINT, intr); 13126495Sminshall (void) signal(SIGPIPE, lostpeer); 13210297Ssam setpeer(argc + 1, argv - 1); 13310297Ssam } 13410297Ssam top = setjmp(toplevel) == 0; 13510297Ssam if (top) { 13626495Sminshall (void) signal(SIGINT, intr); 13726495Sminshall (void) signal(SIGPIPE, lostpeer); 13810297Ssam } 13910297Ssam for (;;) { 14010297Ssam cmdscanner(top); 14110297Ssam top = 1; 14210297Ssam } 14310297Ssam } 14410297Ssam 14510297Ssam intr() 14610297Ssam { 14710297Ssam 14810297Ssam longjmp(toplevel, 1); 14910297Ssam } 15010297Ssam 15110297Ssam lostpeer() 15210297Ssam { 15310297Ssam extern FILE *cout; 15410297Ssam extern int data; 15510297Ssam 15610297Ssam if (connected) { 15710297Ssam if (cout != NULL) { 15826495Sminshall (void) shutdown(fileno(cout), 1+1); 15926495Sminshall (void) fclose(cout); 16010297Ssam cout = NULL; 16110297Ssam } 16210297Ssam if (data >= 0) { 16326495Sminshall (void) shutdown(data, 1+1); 16410297Ssam (void) close(data); 16510297Ssam data = -1; 16610297Ssam } 16710297Ssam connected = 0; 16810297Ssam } 16926047Sminshall pswitch(1); 17026047Sminshall if (connected) { 17126047Sminshall if (cout != NULL) { 17226495Sminshall (void) shutdown(fileno(cout), 1+1); 17326495Sminshall (void) fclose(cout); 17426047Sminshall cout = NULL; 17526047Sminshall } 17626047Sminshall connected = 0; 17726047Sminshall } 17826047Sminshall proxflag = 0; 17926047Sminshall pswitch(0); 18010297Ssam } 18110297Ssam 18226495Sminshall /*char * 18310297Ssam tail(filename) 18410297Ssam char *filename; 18510297Ssam { 18610297Ssam register char *s; 18710297Ssam 18810297Ssam while (*filename) { 18910297Ssam s = rindex(filename, '/'); 19010297Ssam if (s == NULL) 19110297Ssam break; 19210297Ssam if (s[1]) 19310297Ssam return (s + 1); 19410297Ssam *s = '\0'; 19510297Ssam } 19610297Ssam return (filename); 19710297Ssam } 19826495Sminshall */ 19910297Ssam /* 20010297Ssam * Command parser. 20110297Ssam */ 20210297Ssam cmdscanner(top) 20310297Ssam int top; 20410297Ssam { 20510297Ssam register struct cmd *c; 20610297Ssam struct cmd *getcmd(); 20710297Ssam extern int help(); 20810297Ssam 20910297Ssam if (!top) 21026495Sminshall (void) putchar('\n'); 21110297Ssam for (;;) { 21210297Ssam if (fromatty) { 21310297Ssam printf("ftp> "); 21426495Sminshall (void) fflush(stdout); 21510297Ssam } 21613968Ssam if (gets(line) == 0) { 21733374Stef if (feof(stdin) || ferror(stdin)) 21826082Slepreau quit(); 21910297Ssam break; 22013968Ssam } 22110297Ssam if (line[0] == 0) 22210297Ssam break; 22310297Ssam makeargv(); 22426047Sminshall if (margc == 0) { 22525907Smckusick continue; 22626047Sminshall } 22710297Ssam c = getcmd(margv[0]); 22810297Ssam if (c == (struct cmd *)-1) { 22910297Ssam printf("?Ambiguous command\n"); 23010297Ssam continue; 23110297Ssam } 23210297Ssam if (c == 0) { 23310297Ssam printf("?Invalid command\n"); 23410297Ssam continue; 23510297Ssam } 23611654Ssam if (c->c_conn && !connected) { 23711654Ssam printf ("Not connected.\n"); 23811654Ssam continue; 23911654Ssam } 24010297Ssam (*c->c_handler)(margc, margv); 24110297Ssam if (bell && c->c_bell) 24233116Sbostic (void) putchar(CTRL('g')); 24310297Ssam if (c->c_handler != help) 24410297Ssam break; 24510297Ssam } 24626495Sminshall (void) signal(SIGINT, intr); 24726495Sminshall (void) signal(SIGPIPE, lostpeer); 24810297Ssam } 24910297Ssam 25010297Ssam struct cmd * 25110297Ssam getcmd(name) 25210297Ssam register char *name; 25310297Ssam { 25433224Sbostic extern struct cmd cmdtab[]; 25510297Ssam register char *p, *q; 25610297Ssam register struct cmd *c, *found; 25710297Ssam register int nmatches, longest; 25810297Ssam 25910297Ssam longest = 0; 26010297Ssam nmatches = 0; 26110297Ssam found = 0; 26210297Ssam for (c = cmdtab; p = c->c_name; c++) { 26310297Ssam for (q = name; *q == *p++; q++) 26410297Ssam if (*q == 0) /* exact match? */ 26510297Ssam return (c); 26610297Ssam if (!*q) { /* the name was a prefix */ 26710297Ssam if (q - name > longest) { 26810297Ssam longest = q - name; 26910297Ssam nmatches = 1; 27010297Ssam found = c; 27110297Ssam } else if (q - name == longest) 27210297Ssam nmatches++; 27310297Ssam } 27410297Ssam } 27510297Ssam if (nmatches > 1) 27610297Ssam return ((struct cmd *)-1); 27710297Ssam return (found); 27810297Ssam } 27910297Ssam 28010297Ssam /* 28110297Ssam * Slice a string up into argc/argv. 28210297Ssam */ 28326047Sminshall 28426047Sminshall int slrflag; 28526047Sminshall 28610297Ssam makeargv() 28710297Ssam { 28810297Ssam char **argp; 28910297Ssam char *slurpstring(); 29010297Ssam 29110297Ssam margc = 0; 29210297Ssam argp = margv; 29310297Ssam stringbase = line; /* scan from first of buffer */ 29410297Ssam argbase = argbuf; /* store from first of buffer */ 29526047Sminshall slrflag = 0; 29626047Sminshall while (*argp++ = slurpstring()) 29710297Ssam margc++; 29810297Ssam } 29910297Ssam 30010297Ssam /* 30110297Ssam * Parse string into argbuf; 30210297Ssam * implemented with FSM to 30310297Ssam * handle quoting and strings 30410297Ssam */ 30510297Ssam char * 30610297Ssam slurpstring() 30710297Ssam { 30810297Ssam int got_one = 0; 30910297Ssam register char *sb = stringbase; 31010297Ssam register char *ap = argbase; 31110297Ssam char *tmp = argbase; /* will return this if token found */ 31210297Ssam 31326047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 31426047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 31526047Sminshall case 0: 31626047Sminshall slrflag++; 31726047Sminshall stringbase++; 31826047Sminshall return ((*sb == '!') ? "!" : "$"); 31926047Sminshall break; 32026047Sminshall case 1: 32126047Sminshall slrflag++; 32226047Sminshall altarg = stringbase; 32326047Sminshall break; 32426047Sminshall default: 32526047Sminshall break; 32626047Sminshall } 32726047Sminshall } 32826047Sminshall 32910297Ssam S0: 33010297Ssam switch (*sb) { 33110297Ssam 33210297Ssam case '\0': 33310297Ssam goto OUT; 33410297Ssam 33510297Ssam case ' ': 33610297Ssam case '\t': 33710297Ssam sb++; goto S0; 33810297Ssam 33910297Ssam default: 34026047Sminshall switch (slrflag) { 34126047Sminshall case 0: 34226047Sminshall slrflag++; 34326047Sminshall break; 34426047Sminshall case 1: 34526047Sminshall slrflag++; 34626047Sminshall altarg = sb; 34726047Sminshall break; 34826047Sminshall default: 34926047Sminshall break; 35026047Sminshall } 35110297Ssam goto S1; 35210297Ssam } 35310297Ssam 35410297Ssam S1: 35510297Ssam switch (*sb) { 35610297Ssam 35710297Ssam case ' ': 35810297Ssam case '\t': 35910297Ssam case '\0': 36010297Ssam goto OUT; /* end of token */ 36110297Ssam 36210297Ssam case '\\': 36310297Ssam sb++; goto S2; /* slurp next character */ 36410297Ssam 36510297Ssam case '"': 36610297Ssam sb++; goto S3; /* slurp quoted string */ 36710297Ssam 36810297Ssam default: 36910297Ssam *ap++ = *sb++; /* add character to token */ 37010297Ssam got_one = 1; 37110297Ssam goto S1; 37210297Ssam } 37310297Ssam 37410297Ssam S2: 37510297Ssam switch (*sb) { 37610297Ssam 37710297Ssam case '\0': 37810297Ssam goto OUT; 37910297Ssam 38010297Ssam default: 38110297Ssam *ap++ = *sb++; 38210297Ssam got_one = 1; 38310297Ssam goto S1; 38410297Ssam } 38510297Ssam 38610297Ssam S3: 38710297Ssam switch (*sb) { 38810297Ssam 38910297Ssam case '\0': 39010297Ssam goto OUT; 39110297Ssam 39210297Ssam case '"': 39310297Ssam sb++; goto S1; 39410297Ssam 39510297Ssam default: 39610297Ssam *ap++ = *sb++; 39710297Ssam got_one = 1; 39810297Ssam goto S3; 39910297Ssam } 40010297Ssam 40110297Ssam OUT: 40210297Ssam if (got_one) 40310297Ssam *ap++ = '\0'; 40410297Ssam argbase = ap; /* update storage pointer */ 40510297Ssam stringbase = sb; /* update scan pointer */ 40626047Sminshall if (got_one) { 40710297Ssam return(tmp); 40826047Sminshall } 40926047Sminshall switch (slrflag) { 41026047Sminshall case 0: 41126047Sminshall slrflag++; 41226047Sminshall break; 41326047Sminshall case 1: 41426047Sminshall slrflag++; 41526047Sminshall altarg = (char *) 0; 41626047Sminshall break; 41726047Sminshall default: 41826047Sminshall break; 41926047Sminshall } 42010297Ssam return((char *)0); 42110297Ssam } 42210297Ssam 42310297Ssam #define HELPINDENT (sizeof ("directory")) 42410297Ssam 42510297Ssam /* 42610297Ssam * Help command. 42710297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42810297Ssam */ 42910297Ssam help(argc, argv) 43010297Ssam int argc; 43110297Ssam char *argv[]; 43210297Ssam { 43333224Sbostic extern struct cmd cmdtab[]; 43410297Ssam register struct cmd *c; 43510297Ssam 43610297Ssam if (argc == 1) { 43726047Sminshall register int i, j, w, k; 43810297Ssam int columns, width = 0, lines; 43910297Ssam extern int NCMDS; 44010297Ssam 44110297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 44210297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 44310297Ssam int len = strlen(c->c_name); 44410297Ssam 44510297Ssam if (len > width) 44610297Ssam width = len; 44710297Ssam } 44810297Ssam width = (width + 8) &~ 7; 44910297Ssam columns = 80 / width; 45010297Ssam if (columns == 0) 45110297Ssam columns = 1; 45210297Ssam lines = (NCMDS + columns - 1) / columns; 45310297Ssam for (i = 0; i < lines; i++) { 45410297Ssam for (j = 0; j < columns; j++) { 45510297Ssam c = cmdtab + j * lines + i; 45626047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 45725907Smckusick printf("%s", c->c_name); 45826047Sminshall } 45926047Sminshall else if (c->c_name) { 46026047Sminshall for (k=0; k < strlen(c->c_name); k++) { 46126495Sminshall (void) putchar(' '); 46226047Sminshall } 46326047Sminshall } 46410297Ssam if (c + lines >= &cmdtab[NCMDS]) { 46510297Ssam printf("\n"); 46610297Ssam break; 46710297Ssam } 46810297Ssam w = strlen(c->c_name); 46910297Ssam while (w < width) { 47010297Ssam w = (w + 8) &~ 7; 47126495Sminshall (void) putchar('\t'); 47210297Ssam } 47310297Ssam } 47410297Ssam } 47510297Ssam return; 47610297Ssam } 47710297Ssam while (--argc > 0) { 47810297Ssam register char *arg; 47910297Ssam arg = *++argv; 48010297Ssam c = getcmd(arg); 48110297Ssam if (c == (struct cmd *)-1) 48210297Ssam printf("?Ambiguous help command %s\n", arg); 48310297Ssam else if (c == (struct cmd *)0) 48410297Ssam printf("?Invalid help command %s\n", arg); 48510297Ssam else 48610297Ssam printf("%-*s\t%s\n", HELPINDENT, 48710297Ssam c->c_name, c->c_help); 48810297Ssam } 48910297Ssam } 49010297Ssam 49110297Ssam /* 49210297Ssam * Call routine with argc, argv set from args (terminated by 0). 49310297Ssam */ 49426495Sminshall /*VARARGS1*/ 49510297Ssam call(routine, args) 49610297Ssam int (*routine)(); 49710297Ssam int args; 49810297Ssam { 49910297Ssam register int *argp; 50010297Ssam register int argc; 50110297Ssam 50210297Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 50310297Ssam ; 50410297Ssam (*routine)(argc, &args); 50510297Ssam } 506