121740Sdist /* 237173Skarels * Copyright (c) 1985, 1989 Regents of the University of California. 333737Sbostic * All rights reserved. 433737Sbostic * 533737Sbostic * Redistribution and use in source and binary forms are permitted 634901Sbostic * provided that the above copyright notice and this paragraph are 734901Sbostic * duplicated in all such forms and that any documentation, 834901Sbostic * advertising materials, and other materials related to such 934901Sbostic * distribution and use acknowledge that the software was developed 1034901Sbostic * by the University of California, Berkeley. The name of the 1134901Sbostic * University may not be used to endorse or promote products derived 1234901Sbostic * from this software without specific prior written permission. 1334901Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434901Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1536935Skarels * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621740Sdist */ 1721740Sdist 1810297Ssam #ifndef lint 1921740Sdist char copyright[] = 2037173Skarels "@(#) Copyright (c) 1985, 1989 Regents of the University of California.\n\ 2121740Sdist All rights reserved.\n"; 2233737Sbostic #endif /* not lint */ 2310297Ssam 2421740Sdist #ifndef lint 25*38131Srick static char sccsid[] = "@(#)main.c 5.15 (Berkeley) 05/26/89"; 2633737Sbostic #endif /* not lint */ 2721740Sdist 2810297Ssam /* 2910297Ssam * FTP User Program -- Command Interface. 3010297Ssam */ 3126047Sminshall #include "ftp_var.h" 3210297Ssam #include <sys/socket.h> 3310297Ssam #include <sys/ioctl.h> 3426495Sminshall #include <sys/types.h> 3510297Ssam 3612398Ssam #include <arpa/ftp.h> 3712398Ssam 3810297Ssam #include <signal.h> 3910297Ssam #include <stdio.h> 4010297Ssam #include <errno.h> 4110297Ssam #include <ctype.h> 4210297Ssam #include <netdb.h> 4311352Ssam #include <pwd.h> 4410297Ssam 4526495Sminshall uid_t getuid(); 46*38131Srick sig_t intr(); 47*38131Srick sig_t lostpeer(); 4811352Ssam extern char *home; 4925907Smckusick char *getlogin(); 5010297Ssam 5110297Ssam main(argc, argv) 5210297Ssam char *argv[]; 5310297Ssam { 5410297Ssam register char *cp; 5510297Ssam int top; 5625907Smckusick struct passwd *pw = NULL; 5711352Ssam char homedir[MAXPATHLEN]; 5810297Ssam 5910297Ssam sp = getservbyname("ftp", "tcp"); 6010297Ssam if (sp == 0) { 6110297Ssam fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 6210297Ssam exit(1); 6310297Ssam } 6411351Ssam doglob = 1; 6511654Ssam interactive = 1; 6611351Ssam autologin = 1; 6710297Ssam argc--, argv++; 6810297Ssam while (argc > 0 && **argv == '-') { 6910297Ssam for (cp = *argv + 1; *cp; cp++) 7010297Ssam switch (*cp) { 7110297Ssam 7210297Ssam case 'd': 7310297Ssam options |= SO_DEBUG; 7410297Ssam debug++; 7510297Ssam break; 7610297Ssam 7710297Ssam case 'v': 7810297Ssam verbose++; 7910297Ssam break; 8010297Ssam 8110297Ssam case 't': 8210297Ssam trace++; 8310297Ssam break; 8410297Ssam 8510297Ssam case 'i': 8611654Ssam interactive = 0; 8710297Ssam break; 8810297Ssam 8910297Ssam case 'n': 9010297Ssam autologin = 0; 9110297Ssam break; 9210297Ssam 9311351Ssam case 'g': 9411351Ssam doglob = 0; 9511351Ssam break; 9611351Ssam 9710297Ssam default: 9826495Sminshall fprintf(stdout, 9910297Ssam "ftp: %c: unknown option\n", *cp); 10010297Ssam exit(1); 10110297Ssam } 10210297Ssam argc--, argv++; 10310297Ssam } 10410297Ssam fromatty = isatty(fileno(stdin)); 10510297Ssam if (fromatty) 10610297Ssam verbose++; 107*38131Srick cpend = 0; /* no pending replies */ 10826047Sminshall proxy = 0; /* proxy not active */ 109*38131Srick crflag = 1; /* strip c.r. on ascii gets */ 110*38131Srick sendport = -1; /* not using ports */ 11111352Ssam /* 11211352Ssam * Set up the home directory in case we're globbing. 11311352Ssam */ 11425907Smckusick cp = getlogin(); 11526047Sminshall if (cp != NULL) { 11625907Smckusick pw = getpwnam(cp); 11726047Sminshall } 11811352Ssam if (pw == NULL) 11911352Ssam pw = getpwuid(getuid()); 12011352Ssam if (pw != NULL) { 12111352Ssam home = homedir; 12226495Sminshall (void) strcpy(home, pw->pw_dir); 12311352Ssam } 12410297Ssam if (argc > 0) { 12510297Ssam if (setjmp(toplevel)) 12610297Ssam exit(0); 12726495Sminshall (void) signal(SIGINT, intr); 12826495Sminshall (void) signal(SIGPIPE, lostpeer); 12910297Ssam setpeer(argc + 1, argv - 1); 13010297Ssam } 13110297Ssam top = setjmp(toplevel) == 0; 13210297Ssam if (top) { 13326495Sminshall (void) signal(SIGINT, intr); 13426495Sminshall (void) signal(SIGPIPE, lostpeer); 13510297Ssam } 13610297Ssam for (;;) { 13710297Ssam cmdscanner(top); 13810297Ssam top = 1; 13910297Ssam } 14010297Ssam } 14110297Ssam 142*38131Srick sig_t 14310297Ssam intr() 14410297Ssam { 14510297Ssam 14610297Ssam longjmp(toplevel, 1); 14710297Ssam } 14810297Ssam 149*38131Srick sig_t 15010297Ssam lostpeer() 15110297Ssam { 15210297Ssam extern FILE *cout; 15310297Ssam extern int data; 15410297Ssam 15510297Ssam if (connected) { 15610297Ssam if (cout != NULL) { 15726495Sminshall (void) shutdown(fileno(cout), 1+1); 15826495Sminshall (void) fclose(cout); 15910297Ssam cout = NULL; 16010297Ssam } 16110297Ssam if (data >= 0) { 16226495Sminshall (void) shutdown(data, 1+1); 16310297Ssam (void) close(data); 16410297Ssam data = -1; 16510297Ssam } 16610297Ssam connected = 0; 16710297Ssam } 16826047Sminshall pswitch(1); 16926047Sminshall if (connected) { 17026047Sminshall if (cout != NULL) { 17126495Sminshall (void) shutdown(fileno(cout), 1+1); 17226495Sminshall (void) fclose(cout); 17326047Sminshall cout = NULL; 17426047Sminshall } 17526047Sminshall connected = 0; 17626047Sminshall } 17726047Sminshall proxflag = 0; 17826047Sminshall pswitch(0); 17910297Ssam } 18010297Ssam 18126495Sminshall /*char * 18210297Ssam tail(filename) 18310297Ssam char *filename; 18410297Ssam { 18510297Ssam register char *s; 18610297Ssam 18710297Ssam while (*filename) { 18810297Ssam s = rindex(filename, '/'); 18910297Ssam if (s == NULL) 19010297Ssam break; 19110297Ssam if (s[1]) 19210297Ssam return (s + 1); 19310297Ssam *s = '\0'; 19410297Ssam } 19510297Ssam return (filename); 19610297Ssam } 19726495Sminshall */ 19810297Ssam /* 19910297Ssam * Command parser. 20010297Ssam */ 20110297Ssam cmdscanner(top) 20210297Ssam int top; 20310297Ssam { 20410297Ssam register struct cmd *c; 20510297Ssam struct cmd *getcmd(); 20610297Ssam extern int help(); 20710297Ssam 20810297Ssam if (!top) 20926495Sminshall (void) putchar('\n'); 21010297Ssam for (;;) { 21110297Ssam if (fromatty) { 21210297Ssam printf("ftp> "); 21326495Sminshall (void) fflush(stdout); 21410297Ssam } 21513968Ssam if (gets(line) == 0) { 21633374Stef if (feof(stdin) || ferror(stdin)) 21726082Slepreau quit(); 21810297Ssam break; 21913968Ssam } 22010297Ssam if (line[0] == 0) 22110297Ssam break; 22210297Ssam makeargv(); 22326047Sminshall if (margc == 0) { 22425907Smckusick continue; 22526047Sminshall } 22610297Ssam c = getcmd(margv[0]); 22710297Ssam if (c == (struct cmd *)-1) { 22810297Ssam printf("?Ambiguous command\n"); 22910297Ssam continue; 23010297Ssam } 23110297Ssam if (c == 0) { 23210297Ssam printf("?Invalid command\n"); 23310297Ssam continue; 23410297Ssam } 23511654Ssam if (c->c_conn && !connected) { 23611654Ssam printf ("Not connected.\n"); 23711654Ssam continue; 23811654Ssam } 23910297Ssam (*c->c_handler)(margc, margv); 24010297Ssam if (bell && c->c_bell) 24136935Skarels (void) putchar('\007'); 24210297Ssam if (c->c_handler != help) 24310297Ssam break; 24410297Ssam } 24526495Sminshall (void) signal(SIGINT, intr); 24626495Sminshall (void) signal(SIGPIPE, lostpeer); 24710297Ssam } 24810297Ssam 24910297Ssam struct cmd * 25010297Ssam getcmd(name) 25110297Ssam register char *name; 25210297Ssam { 25333224Sbostic extern struct cmd cmdtab[]; 25410297Ssam register char *p, *q; 25510297Ssam register struct cmd *c, *found; 25610297Ssam register int nmatches, longest; 25710297Ssam 25810297Ssam longest = 0; 25910297Ssam nmatches = 0; 26010297Ssam found = 0; 26110297Ssam for (c = cmdtab; p = c->c_name; c++) { 26210297Ssam for (q = name; *q == *p++; q++) 26310297Ssam if (*q == 0) /* exact match? */ 26410297Ssam return (c); 26510297Ssam if (!*q) { /* the name was a prefix */ 26610297Ssam if (q - name > longest) { 26710297Ssam longest = q - name; 26810297Ssam nmatches = 1; 26910297Ssam found = c; 27010297Ssam } else if (q - name == longest) 27110297Ssam nmatches++; 27210297Ssam } 27310297Ssam } 27410297Ssam if (nmatches > 1) 27510297Ssam return ((struct cmd *)-1); 27610297Ssam return (found); 27710297Ssam } 27810297Ssam 27910297Ssam /* 28010297Ssam * Slice a string up into argc/argv. 28110297Ssam */ 28226047Sminshall 28326047Sminshall int slrflag; 28426047Sminshall 28510297Ssam makeargv() 28610297Ssam { 28710297Ssam char **argp; 28810297Ssam char *slurpstring(); 28910297Ssam 29010297Ssam margc = 0; 29110297Ssam argp = margv; 29210297Ssam stringbase = line; /* scan from first of buffer */ 29310297Ssam argbase = argbuf; /* store from first of buffer */ 29426047Sminshall slrflag = 0; 29526047Sminshall while (*argp++ = slurpstring()) 29610297Ssam margc++; 29710297Ssam } 29810297Ssam 29910297Ssam /* 30010297Ssam * Parse string into argbuf; 30110297Ssam * implemented with FSM to 30210297Ssam * handle quoting and strings 30310297Ssam */ 30410297Ssam char * 30510297Ssam slurpstring() 30610297Ssam { 30710297Ssam int got_one = 0; 30810297Ssam register char *sb = stringbase; 30910297Ssam register char *ap = argbase; 31010297Ssam char *tmp = argbase; /* will return this if token found */ 31110297Ssam 31226047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 31326047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 31426047Sminshall case 0: 31526047Sminshall slrflag++; 31626047Sminshall stringbase++; 31726047Sminshall return ((*sb == '!') ? "!" : "$"); 31836935Skarels /* NOTREACHED */ 31926047Sminshall case 1: 32026047Sminshall slrflag++; 32126047Sminshall altarg = stringbase; 32226047Sminshall break; 32326047Sminshall default: 32426047Sminshall break; 32526047Sminshall } 32626047Sminshall } 32726047Sminshall 32810297Ssam S0: 32910297Ssam switch (*sb) { 33010297Ssam 33110297Ssam case '\0': 33210297Ssam goto OUT; 33310297Ssam 33410297Ssam case ' ': 33510297Ssam case '\t': 33610297Ssam sb++; goto S0; 33710297Ssam 33810297Ssam default: 33926047Sminshall switch (slrflag) { 34026047Sminshall case 0: 34126047Sminshall slrflag++; 34226047Sminshall break; 34326047Sminshall case 1: 34426047Sminshall slrflag++; 34526047Sminshall altarg = sb; 34626047Sminshall break; 34726047Sminshall default: 34826047Sminshall break; 34926047Sminshall } 35010297Ssam goto S1; 35110297Ssam } 35210297Ssam 35310297Ssam S1: 35410297Ssam switch (*sb) { 35510297Ssam 35610297Ssam case ' ': 35710297Ssam case '\t': 35810297Ssam case '\0': 35910297Ssam goto OUT; /* end of token */ 36010297Ssam 36110297Ssam case '\\': 36210297Ssam sb++; goto S2; /* slurp next character */ 36310297Ssam 36410297Ssam case '"': 36510297Ssam sb++; goto S3; /* slurp quoted string */ 36610297Ssam 36710297Ssam default: 36810297Ssam *ap++ = *sb++; /* add character to token */ 36910297Ssam got_one = 1; 37010297Ssam goto S1; 37110297Ssam } 37210297Ssam 37310297Ssam S2: 37410297Ssam switch (*sb) { 37510297Ssam 37610297Ssam case '\0': 37710297Ssam goto OUT; 37810297Ssam 37910297Ssam default: 38010297Ssam *ap++ = *sb++; 38110297Ssam got_one = 1; 38210297Ssam goto S1; 38310297Ssam } 38410297Ssam 38510297Ssam S3: 38610297Ssam switch (*sb) { 38710297Ssam 38810297Ssam case '\0': 38910297Ssam goto OUT; 39010297Ssam 39110297Ssam case '"': 39210297Ssam sb++; goto S1; 39310297Ssam 39410297Ssam default: 39510297Ssam *ap++ = *sb++; 39610297Ssam got_one = 1; 39710297Ssam goto S3; 39810297Ssam } 39910297Ssam 40010297Ssam OUT: 40110297Ssam if (got_one) 40210297Ssam *ap++ = '\0'; 40310297Ssam argbase = ap; /* update storage pointer */ 40410297Ssam stringbase = sb; /* update scan pointer */ 40526047Sminshall if (got_one) { 40610297Ssam return(tmp); 40726047Sminshall } 40826047Sminshall switch (slrflag) { 40926047Sminshall case 0: 41026047Sminshall slrflag++; 41126047Sminshall break; 41226047Sminshall case 1: 41326047Sminshall slrflag++; 41426047Sminshall altarg = (char *) 0; 41526047Sminshall break; 41626047Sminshall default: 41726047Sminshall break; 41826047Sminshall } 41910297Ssam return((char *)0); 42010297Ssam } 42110297Ssam 42210297Ssam #define HELPINDENT (sizeof ("directory")) 42310297Ssam 42410297Ssam /* 42510297Ssam * Help command. 42610297Ssam * Call each command handler with argc == 0 and argv[0] == name. 42710297Ssam */ 42810297Ssam help(argc, argv) 42910297Ssam int argc; 43010297Ssam char *argv[]; 43110297Ssam { 43233224Sbostic extern struct cmd cmdtab[]; 43310297Ssam register struct cmd *c; 43410297Ssam 43510297Ssam if (argc == 1) { 43626047Sminshall register int i, j, w, k; 43710297Ssam int columns, width = 0, lines; 43810297Ssam extern int NCMDS; 43910297Ssam 44010297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 44110297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 44210297Ssam int len = strlen(c->c_name); 44310297Ssam 44410297Ssam if (len > width) 44510297Ssam width = len; 44610297Ssam } 44710297Ssam width = (width + 8) &~ 7; 44810297Ssam columns = 80 / width; 44910297Ssam if (columns == 0) 45010297Ssam columns = 1; 45110297Ssam lines = (NCMDS + columns - 1) / columns; 45210297Ssam for (i = 0; i < lines; i++) { 45310297Ssam for (j = 0; j < columns; j++) { 45410297Ssam c = cmdtab + j * lines + i; 45526047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 45625907Smckusick printf("%s", c->c_name); 45726047Sminshall } 45826047Sminshall else if (c->c_name) { 45926047Sminshall for (k=0; k < strlen(c->c_name); k++) { 46026495Sminshall (void) putchar(' '); 46126047Sminshall } 46226047Sminshall } 46310297Ssam if (c + lines >= &cmdtab[NCMDS]) { 46410297Ssam printf("\n"); 46510297Ssam break; 46610297Ssam } 46710297Ssam w = strlen(c->c_name); 46810297Ssam while (w < width) { 46910297Ssam w = (w + 8) &~ 7; 47026495Sminshall (void) putchar('\t'); 47110297Ssam } 47210297Ssam } 47310297Ssam } 47410297Ssam return; 47510297Ssam } 47610297Ssam while (--argc > 0) { 47710297Ssam register char *arg; 47810297Ssam arg = *++argv; 47910297Ssam c = getcmd(arg); 48010297Ssam if (c == (struct cmd *)-1) 48110297Ssam printf("?Ambiguous help command %s\n", arg); 48210297Ssam else if (c == (struct cmd *)0) 48310297Ssam printf("?Invalid help command %s\n", arg); 48410297Ssam else 48510297Ssam printf("%-*s\t%s\n", HELPINDENT, 48610297Ssam c->c_name, c->c_help); 48710297Ssam } 48810297Ssam } 489