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*25802Slepreau static char sccsid[] = "@(#)main.c 5.2 (Berkeley) 01/10/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; 3810297Ssam 3910297Ssam main(argc, argv) 4010297Ssam char *argv[]; 4110297Ssam { 4210297Ssam register char *cp; 4310297Ssam int top; 4411352Ssam struct passwd *pw; 4511352Ssam char homedir[MAXPATHLEN]; 4610297Ssam 4710297Ssam sp = getservbyname("ftp", "tcp"); 4810297Ssam if (sp == 0) { 4910297Ssam fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 5010297Ssam exit(1); 5110297Ssam } 5211351Ssam doglob = 1; 5311654Ssam interactive = 1; 5411351Ssam autologin = 1; 5510297Ssam argc--, argv++; 5610297Ssam while (argc > 0 && **argv == '-') { 5710297Ssam for (cp = *argv + 1; *cp; cp++) 5810297Ssam switch (*cp) { 5910297Ssam 6010297Ssam case 'd': 6110297Ssam options |= SO_DEBUG; 6210297Ssam debug++; 6310297Ssam break; 6410297Ssam 6510297Ssam case 'v': 6610297Ssam verbose++; 6710297Ssam break; 6810297Ssam 6910297Ssam case 't': 7010297Ssam trace++; 7110297Ssam break; 7210297Ssam 7310297Ssam case 'i': 7411654Ssam interactive = 0; 7510297Ssam break; 7610297Ssam 7710297Ssam case 'n': 7810297Ssam autologin = 0; 7910297Ssam break; 8010297Ssam 8111351Ssam case 'g': 8211351Ssam doglob = 0; 8311351Ssam break; 8411351Ssam 8510297Ssam default: 8610297Ssam fprintf(stderr, 8710297Ssam "ftp: %c: unknown option\n", *cp); 8810297Ssam exit(1); 8910297Ssam } 9010297Ssam argc--, argv++; 9110297Ssam } 9210297Ssam fromatty = isatty(fileno(stdin)); 9310297Ssam /* 9410297Ssam * Set up defaults for FTP. 9510297Ssam */ 9610297Ssam strcpy(typename, "ascii"), type = TYPE_A; 9710297Ssam strcpy(formname, "non-print"), form = FORM_N; 9810297Ssam strcpy(modename, "stream"), mode = MODE_S; 9910297Ssam strcpy(structname, "file"), stru = STRU_F; 10011227Ssam strcpy(bytename, "8"), bytesize = 8; 10110297Ssam if (fromatty) 10210297Ssam verbose++; 10311352Ssam /* 10411352Ssam * Set up the home directory in case we're globbing. 10511352Ssam */ 10611352Ssam pw = getpwnam(getlogin()); 10711352Ssam if (pw == NULL) 10811352Ssam pw = getpwuid(getuid()); 10911352Ssam if (pw != NULL) { 11011352Ssam home = homedir; 11111352Ssam strcpy(home, pw->pw_dir); 11211352Ssam } 11310297Ssam if (argc > 0) { 11410297Ssam if (setjmp(toplevel)) 11510297Ssam exit(0); 11612993Ssam signal(SIGINT, intr); 11712993Ssam signal(SIGPIPE, lostpeer); 11810297Ssam setpeer(argc + 1, argv - 1); 11910297Ssam } 12010297Ssam top = setjmp(toplevel) == 0; 12110297Ssam if (top) { 12212993Ssam signal(SIGINT, intr); 12312993Ssam signal(SIGPIPE, lostpeer); 12410297Ssam } 12510297Ssam for (;;) { 12610297Ssam cmdscanner(top); 12710297Ssam top = 1; 12810297Ssam } 12910297Ssam } 13010297Ssam 13110297Ssam intr() 13210297Ssam { 13310297Ssam 13410297Ssam longjmp(toplevel, 1); 13510297Ssam } 13610297Ssam 13710297Ssam lostpeer() 13810297Ssam { 13910297Ssam extern FILE *cout; 14010297Ssam extern int data; 14110297Ssam 14210297Ssam if (connected) { 14310297Ssam if (cout != NULL) { 14411239Ssam shutdown(fileno(cout), 1+1); 14510297Ssam fclose(cout); 14610297Ssam cout = NULL; 14710297Ssam } 14810297Ssam if (data >= 0) { 14911239Ssam shutdown(data, 1+1); 15010297Ssam (void) close(data); 15110297Ssam data = -1; 15210297Ssam } 15310297Ssam connected = 0; 15410297Ssam } 15510297Ssam longjmp(toplevel, 1); 15610297Ssam } 15710297Ssam 15810297Ssam char * 15910297Ssam tail(filename) 16010297Ssam char *filename; 16110297Ssam { 16210297Ssam register char *s; 16310297Ssam 16410297Ssam while (*filename) { 16510297Ssam s = rindex(filename, '/'); 16610297Ssam if (s == NULL) 16710297Ssam break; 16810297Ssam if (s[1]) 16910297Ssam return (s + 1); 17010297Ssam *s = '\0'; 17110297Ssam } 17210297Ssam return (filename); 17310297Ssam } 17410297Ssam 17510297Ssam /* 17610297Ssam * Command parser. 17710297Ssam */ 17810297Ssam cmdscanner(top) 17910297Ssam int top; 18010297Ssam { 18110297Ssam register struct cmd *c; 18210297Ssam struct cmd *getcmd(); 18310297Ssam extern struct cmd cmdtab[]; 18410297Ssam extern int help(); 18510297Ssam 18610297Ssam if (!top) 18710297Ssam putchar('\n'); 18810297Ssam for (;;) { 18910297Ssam if (fromatty) { 19010297Ssam printf("ftp> "); 19110297Ssam fflush(stdout); 19210297Ssam } 19313968Ssam if (gets(line) == 0) { 194*25802Slepreau if (feof(stdin)) 195*25802Slepreau quit(); 19610297Ssam break; 19713968Ssam } 19810297Ssam if (line[0] == 0) 19910297Ssam break; 20010297Ssam makeargv(); 20110297Ssam c = getcmd(margv[0]); 20210297Ssam if (c == (struct cmd *)-1) { 20310297Ssam printf("?Ambiguous command\n"); 20410297Ssam continue; 20510297Ssam } 20610297Ssam if (c == 0) { 20710297Ssam printf("?Invalid command\n"); 20810297Ssam continue; 20910297Ssam } 21011654Ssam if (c->c_conn && !connected) { 21111654Ssam printf ("Not connected.\n"); 21211654Ssam continue; 21311654Ssam } 21410297Ssam (*c->c_handler)(margc, margv); 21510297Ssam if (bell && c->c_bell) 21610297Ssam putchar(CTRL(g)); 21710297Ssam if (c->c_handler != help) 21810297Ssam break; 21910297Ssam } 22010297Ssam longjmp(toplevel, 0); 22110297Ssam } 22210297Ssam 22310297Ssam struct cmd * 22410297Ssam getcmd(name) 22510297Ssam register char *name; 22610297Ssam { 22710297Ssam register char *p, *q; 22810297Ssam register struct cmd *c, *found; 22910297Ssam register int nmatches, longest; 23010297Ssam 23110297Ssam longest = 0; 23210297Ssam nmatches = 0; 23310297Ssam found = 0; 23410297Ssam for (c = cmdtab; p = c->c_name; c++) { 23510297Ssam for (q = name; *q == *p++; q++) 23610297Ssam if (*q == 0) /* exact match? */ 23710297Ssam return (c); 23810297Ssam if (!*q) { /* the name was a prefix */ 23910297Ssam if (q - name > longest) { 24010297Ssam longest = q - name; 24110297Ssam nmatches = 1; 24210297Ssam found = c; 24310297Ssam } else if (q - name == longest) 24410297Ssam nmatches++; 24510297Ssam } 24610297Ssam } 24710297Ssam if (nmatches > 1) 24810297Ssam return ((struct cmd *)-1); 24910297Ssam return (found); 25010297Ssam } 25110297Ssam 25210297Ssam /* 25310297Ssam * Slice a string up into argc/argv. 25410297Ssam */ 25510297Ssam makeargv() 25610297Ssam { 25710297Ssam char **argp; 25810297Ssam char *slurpstring(); 25910297Ssam 26010297Ssam margc = 0; 26110297Ssam argp = margv; 26210297Ssam stringbase = line; /* scan from first of buffer */ 26310297Ssam argbase = argbuf; /* store from first of buffer */ 26410297Ssam while (*argp++ = slurpstring()) 26510297Ssam margc++; 26610297Ssam } 26710297Ssam 26810297Ssam /* 26910297Ssam * Parse string into argbuf; 27010297Ssam * implemented with FSM to 27110297Ssam * handle quoting and strings 27210297Ssam */ 27310297Ssam char * 27410297Ssam slurpstring() 27510297Ssam { 27610297Ssam int got_one = 0; 27710297Ssam register char *sb = stringbase; 27810297Ssam register char *ap = argbase; 27910297Ssam char *tmp = argbase; /* will return this if token found */ 28010297Ssam 28111654Ssam if (*sb == '!') { /* recognize ! as a token for shell */ 28211654Ssam stringbase++; 28311654Ssam return ("!"); 28411654Ssam } 28510297Ssam S0: 28610297Ssam switch (*sb) { 28710297Ssam 28810297Ssam case '\0': 28910297Ssam goto OUT; 29010297Ssam 29110297Ssam case ' ': 29210297Ssam case '\t': 29310297Ssam sb++; goto S0; 29410297Ssam 29510297Ssam default: 29610297Ssam goto S1; 29710297Ssam } 29810297Ssam 29910297Ssam S1: 30010297Ssam switch (*sb) { 30110297Ssam 30210297Ssam case ' ': 30310297Ssam case '\t': 30410297Ssam case '\0': 30510297Ssam goto OUT; /* end of token */ 30610297Ssam 30710297Ssam case '\\': 30810297Ssam sb++; goto S2; /* slurp next character */ 30910297Ssam 31010297Ssam case '"': 31110297Ssam sb++; goto S3; /* slurp quoted string */ 31210297Ssam 31310297Ssam default: 31410297Ssam *ap++ = *sb++; /* add character to token */ 31510297Ssam got_one = 1; 31610297Ssam goto S1; 31710297Ssam } 31810297Ssam 31910297Ssam S2: 32010297Ssam switch (*sb) { 32110297Ssam 32210297Ssam case '\0': 32310297Ssam goto OUT; 32410297Ssam 32510297Ssam default: 32610297Ssam *ap++ = *sb++; 32710297Ssam got_one = 1; 32810297Ssam goto S1; 32910297Ssam } 33010297Ssam 33110297Ssam S3: 33210297Ssam switch (*sb) { 33310297Ssam 33410297Ssam case '\0': 33510297Ssam goto OUT; 33610297Ssam 33710297Ssam case '"': 33810297Ssam sb++; goto S1; 33910297Ssam 34010297Ssam default: 34110297Ssam *ap++ = *sb++; 34210297Ssam got_one = 1; 34310297Ssam goto S3; 34410297Ssam } 34510297Ssam 34610297Ssam OUT: 34710297Ssam if (got_one) 34810297Ssam *ap++ = '\0'; 34910297Ssam argbase = ap; /* update storage pointer */ 35010297Ssam stringbase = sb; /* update scan pointer */ 35110297Ssam if (got_one) 35210297Ssam return(tmp); 35310297Ssam return((char *)0); 35410297Ssam } 35510297Ssam 35610297Ssam #define HELPINDENT (sizeof ("directory")) 35710297Ssam 35810297Ssam /* 35910297Ssam * Help command. 36010297Ssam * Call each command handler with argc == 0 and argv[0] == name. 36110297Ssam */ 36210297Ssam help(argc, argv) 36310297Ssam int argc; 36410297Ssam char *argv[]; 36510297Ssam { 36610297Ssam register struct cmd *c; 36710297Ssam 36810297Ssam if (argc == 1) { 36910297Ssam register int i, j, w; 37010297Ssam int columns, width = 0, lines; 37110297Ssam extern int NCMDS; 37210297Ssam 37310297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 37410297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 37510297Ssam int len = strlen(c->c_name); 37610297Ssam 37710297Ssam if (len > width) 37810297Ssam width = len; 37910297Ssam } 38010297Ssam width = (width + 8) &~ 7; 38110297Ssam columns = 80 / width; 38210297Ssam if (columns == 0) 38310297Ssam columns = 1; 38410297Ssam lines = (NCMDS + columns - 1) / columns; 38510297Ssam for (i = 0; i < lines; i++) { 38610297Ssam for (j = 0; j < columns; j++) { 38710297Ssam c = cmdtab + j * lines + i; 38810297Ssam printf("%s", c->c_name); 38910297Ssam if (c + lines >= &cmdtab[NCMDS]) { 39010297Ssam printf("\n"); 39110297Ssam break; 39210297Ssam } 39310297Ssam w = strlen(c->c_name); 39410297Ssam while (w < width) { 39510297Ssam w = (w + 8) &~ 7; 39610297Ssam putchar('\t'); 39710297Ssam } 39810297Ssam } 39910297Ssam } 40010297Ssam return; 40110297Ssam } 40210297Ssam while (--argc > 0) { 40310297Ssam register char *arg; 40410297Ssam arg = *++argv; 40510297Ssam c = getcmd(arg); 40610297Ssam if (c == (struct cmd *)-1) 40710297Ssam printf("?Ambiguous help command %s\n", arg); 40810297Ssam else if (c == (struct cmd *)0) 40910297Ssam printf("?Invalid help command %s\n", arg); 41010297Ssam else 41110297Ssam printf("%-*s\t%s\n", HELPINDENT, 41210297Ssam c->c_name, c->c_help); 41310297Ssam } 41410297Ssam } 41510297Ssam 41610297Ssam /* 41710297Ssam * Call routine with argc, argv set from args (terminated by 0). 41810297Ssam */ 41910297Ssam /* VARARGS2 */ 42010297Ssam call(routine, args) 42110297Ssam int (*routine)(); 42210297Ssam int args; 42310297Ssam { 42410297Ssam register int *argp; 42510297Ssam register int argc; 42610297Ssam 42710297Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 42810297Ssam ; 42910297Ssam (*routine)(argc, &args); 43010297Ssam } 431