121740Sdist /* 2*26047Sminshall * 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[] = 9*26047Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 1021740Sdist All rights reserved.\n"; 1121740Sdist #endif not lint 1210297Ssam 1321740Sdist #ifndef lint 14*26047Sminshall static char sccsid[] = "@(#)main.c 5.4 (Berkeley) 02/03/86"; 1521740Sdist #endif not lint 1621740Sdist 1710297Ssam /* 1810297Ssam * FTP User Program -- Command Interface. 1910297Ssam */ 20*26047Sminshall #include "ftp_var.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 3410297Ssam int intr(); 3510297Ssam int lostpeer(); 3611352Ssam extern char *home; 3725907Smckusick char *getlogin(); 3810297Ssam 3910297Ssam main(argc, argv) 4010297Ssam char *argv[]; 4110297Ssam { 4210297Ssam register char *cp; 4310297Ssam int top; 4425907Smckusick struct passwd *pw = NULL; 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++; 103*26047Sminshall cpend = 0; /* no pending replies */ 104*26047Sminshall proxy = 0; /* proxy not active */ 105*26047Sminshall crflag = 1; /* strip c.r. on ascii gets */ 10611352Ssam /* 10711352Ssam * Set up the home directory in case we're globbing. 10811352Ssam */ 10925907Smckusick cp = getlogin(); 110*26047Sminshall if (cp != NULL) { 11125907Smckusick pw = getpwnam(cp); 112*26047Sminshall } 11311352Ssam if (pw == NULL) 11411352Ssam pw = getpwuid(getuid()); 11511352Ssam if (pw != NULL) { 11611352Ssam home = homedir; 11711352Ssam strcpy(home, pw->pw_dir); 11811352Ssam } 11910297Ssam if (argc > 0) { 12010297Ssam if (setjmp(toplevel)) 12110297Ssam exit(0); 12212993Ssam signal(SIGINT, intr); 12312993Ssam signal(SIGPIPE, lostpeer); 12410297Ssam setpeer(argc + 1, argv - 1); 12510297Ssam } 12610297Ssam top = setjmp(toplevel) == 0; 12710297Ssam if (top) { 12812993Ssam signal(SIGINT, intr); 12912993Ssam signal(SIGPIPE, lostpeer); 13010297Ssam } 13110297Ssam for (;;) { 13210297Ssam cmdscanner(top); 13310297Ssam top = 1; 13410297Ssam } 13510297Ssam } 13610297Ssam 13710297Ssam intr() 13810297Ssam { 13910297Ssam 14010297Ssam longjmp(toplevel, 1); 14110297Ssam } 14210297Ssam 14310297Ssam lostpeer() 14410297Ssam { 14510297Ssam extern FILE *cout; 14610297Ssam extern int data; 14710297Ssam 14810297Ssam if (connected) { 14910297Ssam if (cout != NULL) { 15011239Ssam shutdown(fileno(cout), 1+1); 15110297Ssam fclose(cout); 15210297Ssam cout = NULL; 15310297Ssam } 15410297Ssam if (data >= 0) { 15511239Ssam shutdown(data, 1+1); 15610297Ssam (void) close(data); 15710297Ssam data = -1; 15810297Ssam } 15910297Ssam connected = 0; 16010297Ssam } 161*26047Sminshall pswitch(1); 162*26047Sminshall if (connected) { 163*26047Sminshall if (cout != NULL) { 164*26047Sminshall shutdown(fileno(cout), 1+1); 165*26047Sminshall fclose(cout); 166*26047Sminshall cout = NULL; 167*26047Sminshall } 168*26047Sminshall connected = 0; 169*26047Sminshall } 170*26047Sminshall proxflag = 0; 171*26047Sminshall pswitch(0); 17210297Ssam } 17310297Ssam 17410297Ssam char * 17510297Ssam tail(filename) 17610297Ssam char *filename; 17710297Ssam { 17810297Ssam register char *s; 17910297Ssam 18010297Ssam while (*filename) { 18110297Ssam s = rindex(filename, '/'); 18210297Ssam if (s == NULL) 18310297Ssam break; 18410297Ssam if (s[1]) 18510297Ssam return (s + 1); 18610297Ssam *s = '\0'; 18710297Ssam } 18810297Ssam return (filename); 18910297Ssam } 19010297Ssam 19110297Ssam /* 19210297Ssam * Command parser. 19310297Ssam */ 19410297Ssam cmdscanner(top) 19510297Ssam int top; 19610297Ssam { 19710297Ssam register struct cmd *c; 19810297Ssam struct cmd *getcmd(); 19910297Ssam extern struct cmd cmdtab[]; 20010297Ssam extern int help(); 20110297Ssam 20210297Ssam if (!top) 20310297Ssam putchar('\n'); 20410297Ssam for (;;) { 20510297Ssam if (fromatty) { 20610297Ssam printf("ftp> "); 20710297Ssam fflush(stdout); 20810297Ssam } 20913968Ssam if (gets(line) == 0) { 210*26047Sminshall if (feof(stdin)) { 211*26047Sminshall if (!fromatty) 212*26047Sminshall quit(); 213*26047Sminshall clearerr(stdin); 214*26047Sminshall putchar('\n'); 215*26047Sminshall } 21610297Ssam break; 21713968Ssam } 21810297Ssam if (line[0] == 0) 21910297Ssam break; 22010297Ssam makeargv(); 221*26047Sminshall if (margc == 0) { 22225907Smckusick continue; 223*26047Sminshall } 22410297Ssam c = getcmd(margv[0]); 22510297Ssam if (c == (struct cmd *)-1) { 22610297Ssam printf("?Ambiguous command\n"); 22710297Ssam continue; 22810297Ssam } 22910297Ssam if (c == 0) { 23010297Ssam printf("?Invalid command\n"); 23110297Ssam continue; 23210297Ssam } 23311654Ssam if (c->c_conn && !connected) { 23411654Ssam printf ("Not connected.\n"); 23511654Ssam continue; 23611654Ssam } 23710297Ssam (*c->c_handler)(margc, margv); 23810297Ssam if (bell && c->c_bell) 23910297Ssam putchar(CTRL(g)); 24010297Ssam if (c->c_handler != help) 24110297Ssam break; 24210297Ssam } 243*26047Sminshall signal(SIGINT, intr); 244*26047Sminshall signal(SIGPIPE, lostpeer); 24510297Ssam } 24610297Ssam 24710297Ssam struct cmd * 24810297Ssam getcmd(name) 24910297Ssam register char *name; 25010297Ssam { 25110297Ssam register char *p, *q; 25210297Ssam register struct cmd *c, *found; 25310297Ssam register int nmatches, longest; 25410297Ssam 25510297Ssam longest = 0; 25610297Ssam nmatches = 0; 25710297Ssam found = 0; 25810297Ssam for (c = cmdtab; p = c->c_name; c++) { 25910297Ssam for (q = name; *q == *p++; q++) 26010297Ssam if (*q == 0) /* exact match? */ 26110297Ssam return (c); 26210297Ssam if (!*q) { /* the name was a prefix */ 26310297Ssam if (q - name > longest) { 26410297Ssam longest = q - name; 26510297Ssam nmatches = 1; 26610297Ssam found = c; 26710297Ssam } else if (q - name == longest) 26810297Ssam nmatches++; 26910297Ssam } 27010297Ssam } 27110297Ssam if (nmatches > 1) 27210297Ssam return ((struct cmd *)-1); 27310297Ssam return (found); 27410297Ssam } 27510297Ssam 27610297Ssam /* 27710297Ssam * Slice a string up into argc/argv. 27810297Ssam */ 279*26047Sminshall 280*26047Sminshall int slrflag; 281*26047Sminshall 28210297Ssam makeargv() 28310297Ssam { 28410297Ssam char **argp; 28510297Ssam char *slurpstring(); 28610297Ssam 28710297Ssam margc = 0; 28810297Ssam argp = margv; 28910297Ssam stringbase = line; /* scan from first of buffer */ 29010297Ssam argbase = argbuf; /* store from first of buffer */ 291*26047Sminshall slrflag = 0; 292*26047Sminshall while (*argp++ = slurpstring()) 29310297Ssam margc++; 29410297Ssam } 29510297Ssam 29610297Ssam /* 29710297Ssam * Parse string into argbuf; 29810297Ssam * implemented with FSM to 29910297Ssam * handle quoting and strings 30010297Ssam */ 30110297Ssam char * 30210297Ssam slurpstring() 30310297Ssam { 30410297Ssam int got_one = 0; 30510297Ssam register char *sb = stringbase; 30610297Ssam register char *ap = argbase; 30710297Ssam char *tmp = argbase; /* will return this if token found */ 30810297Ssam 309*26047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 310*26047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 311*26047Sminshall case 0: 312*26047Sminshall slrflag++; 313*26047Sminshall stringbase++; 314*26047Sminshall return ((*sb == '!') ? "!" : "$"); 315*26047Sminshall break; 316*26047Sminshall case 1: 317*26047Sminshall slrflag++; 318*26047Sminshall altarg = stringbase; 319*26047Sminshall break; 320*26047Sminshall default: 321*26047Sminshall break; 322*26047Sminshall } 323*26047Sminshall } 324*26047Sminshall 32510297Ssam S0: 32610297Ssam switch (*sb) { 32710297Ssam 32810297Ssam case '\0': 32910297Ssam goto OUT; 33010297Ssam 33110297Ssam case ' ': 33210297Ssam case '\t': 33310297Ssam sb++; goto S0; 33410297Ssam 33510297Ssam default: 336*26047Sminshall switch (slrflag) { 337*26047Sminshall case 0: 338*26047Sminshall slrflag++; 339*26047Sminshall break; 340*26047Sminshall case 1: 341*26047Sminshall slrflag++; 342*26047Sminshall altarg = sb; 343*26047Sminshall break; 344*26047Sminshall default: 345*26047Sminshall break; 346*26047Sminshall } 34710297Ssam goto S1; 34810297Ssam } 34910297Ssam 35010297Ssam S1: 35110297Ssam switch (*sb) { 35210297Ssam 35310297Ssam case ' ': 35410297Ssam case '\t': 35510297Ssam case '\0': 35610297Ssam goto OUT; /* end of token */ 35710297Ssam 35810297Ssam case '\\': 35910297Ssam sb++; goto S2; /* slurp next character */ 36010297Ssam 36110297Ssam case '"': 36210297Ssam sb++; goto S3; /* slurp quoted string */ 36310297Ssam 36410297Ssam default: 36510297Ssam *ap++ = *sb++; /* add character to token */ 36610297Ssam got_one = 1; 36710297Ssam goto S1; 36810297Ssam } 36910297Ssam 37010297Ssam S2: 37110297Ssam switch (*sb) { 37210297Ssam 37310297Ssam case '\0': 37410297Ssam goto OUT; 37510297Ssam 37610297Ssam default: 37710297Ssam *ap++ = *sb++; 37810297Ssam got_one = 1; 37910297Ssam goto S1; 38010297Ssam } 38110297Ssam 38210297Ssam S3: 38310297Ssam switch (*sb) { 38410297Ssam 38510297Ssam case '\0': 38610297Ssam goto OUT; 38710297Ssam 38810297Ssam case '"': 38910297Ssam sb++; goto S1; 39010297Ssam 39110297Ssam default: 39210297Ssam *ap++ = *sb++; 39310297Ssam got_one = 1; 39410297Ssam goto S3; 39510297Ssam } 39610297Ssam 39710297Ssam OUT: 39810297Ssam if (got_one) 39910297Ssam *ap++ = '\0'; 40010297Ssam argbase = ap; /* update storage pointer */ 40110297Ssam stringbase = sb; /* update scan pointer */ 402*26047Sminshall if (got_one) { 40310297Ssam return(tmp); 404*26047Sminshall } 405*26047Sminshall switch (slrflag) { 406*26047Sminshall case 0: 407*26047Sminshall slrflag++; 408*26047Sminshall break; 409*26047Sminshall case 1: 410*26047Sminshall slrflag++; 411*26047Sminshall altarg = (char *) 0; 412*26047Sminshall break; 413*26047Sminshall default: 414*26047Sminshall break; 415*26047Sminshall } 41610297Ssam return((char *)0); 41710297Ssam } 41810297Ssam 41910297Ssam #define HELPINDENT (sizeof ("directory")) 42010297Ssam 42110297Ssam /* 42210297Ssam * Help command. 42310297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42410297Ssam */ 42510297Ssam help(argc, argv) 42610297Ssam int argc; 42710297Ssam char *argv[]; 42810297Ssam { 42910297Ssam register struct cmd *c; 43010297Ssam 43110297Ssam if (argc == 1) { 432*26047Sminshall register int i, j, w, k; 43310297Ssam int columns, width = 0, lines; 43410297Ssam extern int NCMDS; 43510297Ssam 43610297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 43710297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 43810297Ssam int len = strlen(c->c_name); 43910297Ssam 44010297Ssam if (len > width) 44110297Ssam width = len; 44210297Ssam } 44310297Ssam width = (width + 8) &~ 7; 44410297Ssam columns = 80 / width; 44510297Ssam if (columns == 0) 44610297Ssam columns = 1; 44710297Ssam lines = (NCMDS + columns - 1) / columns; 44810297Ssam for (i = 0; i < lines; i++) { 44910297Ssam for (j = 0; j < columns; j++) { 45010297Ssam c = cmdtab + j * lines + i; 451*26047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 45225907Smckusick printf("%s", c->c_name); 453*26047Sminshall } 454*26047Sminshall else if (c->c_name) { 455*26047Sminshall for (k=0; k < strlen(c->c_name); k++) { 456*26047Sminshall putchar(' '); 457*26047Sminshall } 458*26047Sminshall } 45910297Ssam if (c + lines >= &cmdtab[NCMDS]) { 46010297Ssam printf("\n"); 46110297Ssam break; 46210297Ssam } 46310297Ssam w = strlen(c->c_name); 46410297Ssam while (w < width) { 46510297Ssam w = (w + 8) &~ 7; 46610297Ssam putchar('\t'); 46710297Ssam } 46810297Ssam } 46910297Ssam } 47010297Ssam return; 47110297Ssam } 47210297Ssam while (--argc > 0) { 47310297Ssam register char *arg; 47410297Ssam arg = *++argv; 47510297Ssam c = getcmd(arg); 47610297Ssam if (c == (struct cmd *)-1) 47710297Ssam printf("?Ambiguous help command %s\n", arg); 47810297Ssam else if (c == (struct cmd *)0) 47910297Ssam printf("?Invalid help command %s\n", arg); 48010297Ssam else 48110297Ssam printf("%-*s\t%s\n", HELPINDENT, 48210297Ssam c->c_name, c->c_help); 48310297Ssam } 48410297Ssam } 48510297Ssam 48610297Ssam /* 48710297Ssam * Call routine with argc, argv set from args (terminated by 0). 48810297Ssam */ 48910297Ssam /* VARARGS2 */ 49010297Ssam call(routine, args) 49110297Ssam int (*routine)(); 49210297Ssam int args; 49310297Ssam { 49410297Ssam register int *argp; 49510297Ssam register int argc; 49610297Ssam 49710297Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 49810297Ssam ; 49910297Ssam (*routine)(argc, &args); 50010297Ssam } 501