121740Sdist /* 2*37173Skarels * 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[] = 20*37173Skarels "@(#) 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*37173Skarels static char sccsid[] = "@(#)main.c 5.13 (Berkeley) 03/14/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 4510297Ssam 4626495Sminshall uid_t getuid(); 4710297Ssam int intr(); 4810297Ssam int lostpeer(); 4911352Ssam extern char *home; 5025907Smckusick char *getlogin(); 5110297Ssam 5210297Ssam main(argc, argv) 5310297Ssam char *argv[]; 5410297Ssam { 5510297Ssam register char *cp; 5610297Ssam int top; 5725907Smckusick struct passwd *pw = NULL; 5811352Ssam char homedir[MAXPATHLEN]; 5910297Ssam 6010297Ssam sp = getservbyname("ftp", "tcp"); 6110297Ssam if (sp == 0) { 6210297Ssam fprintf(stderr, "ftp: ftp/tcp: unknown service\n"); 6310297Ssam exit(1); 6410297Ssam } 6511351Ssam doglob = 1; 6611654Ssam interactive = 1; 6711351Ssam autologin = 1; 6810297Ssam argc--, argv++; 6910297Ssam while (argc > 0 && **argv == '-') { 7010297Ssam for (cp = *argv + 1; *cp; cp++) 7110297Ssam switch (*cp) { 7210297Ssam 7310297Ssam case 'd': 7410297Ssam options |= SO_DEBUG; 7510297Ssam debug++; 7610297Ssam break; 7710297Ssam 7810297Ssam case 'v': 7910297Ssam verbose++; 8010297Ssam break; 8110297Ssam 8210297Ssam case 't': 8310297Ssam trace++; 8410297Ssam break; 8510297Ssam 8610297Ssam case 'i': 8711654Ssam interactive = 0; 8810297Ssam break; 8910297Ssam 9010297Ssam case 'n': 9110297Ssam autologin = 0; 9210297Ssam break; 9310297Ssam 9411351Ssam case 'g': 9511351Ssam doglob = 0; 9611351Ssam break; 9711351Ssam 9810297Ssam default: 9926495Sminshall fprintf(stdout, 10010297Ssam "ftp: %c: unknown option\n", *cp); 10110297Ssam exit(1); 10210297Ssam } 10310297Ssam argc--, argv++; 10410297Ssam } 10510297Ssam fromatty = isatty(fileno(stdin)); 10610297Ssam /* 10710297Ssam * Set up defaults for FTP. 10810297Ssam */ 10926495Sminshall (void) strcpy(typename, "ascii"), type = TYPE_A; 11026495Sminshall (void) strcpy(formname, "non-print"), form = FORM_N; 11126495Sminshall (void) strcpy(modename, "stream"), mode = MODE_S; 11226495Sminshall (void) strcpy(structname, "file"), stru = STRU_F; 11326495Sminshall (void) strcpy(bytename, "8"), bytesize = 8; 11410297Ssam if (fromatty) 11510297Ssam verbose++; 11626047Sminshall cpend = 0; /* no pending replies */ 11726047Sminshall proxy = 0; /* proxy not active */ 11826047Sminshall crflag = 1; /* strip c.r. on ascii gets */ 11911352Ssam /* 12011352Ssam * Set up the home directory in case we're globbing. 12111352Ssam */ 12225907Smckusick cp = getlogin(); 12326047Sminshall if (cp != NULL) { 12425907Smckusick pw = getpwnam(cp); 12526047Sminshall } 12611352Ssam if (pw == NULL) 12711352Ssam pw = getpwuid(getuid()); 12811352Ssam if (pw != NULL) { 12911352Ssam home = homedir; 13026495Sminshall (void) strcpy(home, pw->pw_dir); 13111352Ssam } 13210297Ssam if (argc > 0) { 13310297Ssam if (setjmp(toplevel)) 13410297Ssam exit(0); 13526495Sminshall (void) signal(SIGINT, intr); 13626495Sminshall (void) signal(SIGPIPE, lostpeer); 13710297Ssam setpeer(argc + 1, argv - 1); 13810297Ssam } 13910297Ssam top = setjmp(toplevel) == 0; 14010297Ssam if (top) { 14126495Sminshall (void) signal(SIGINT, intr); 14226495Sminshall (void) signal(SIGPIPE, lostpeer); 14310297Ssam } 14410297Ssam for (;;) { 14510297Ssam cmdscanner(top); 14610297Ssam top = 1; 14710297Ssam } 14810297Ssam } 14910297Ssam 15010297Ssam intr() 15110297Ssam { 15210297Ssam 15310297Ssam longjmp(toplevel, 1); 15410297Ssam } 15510297Ssam 15610297Ssam lostpeer() 15710297Ssam { 15810297Ssam extern FILE *cout; 15910297Ssam extern int data; 16010297Ssam 16110297Ssam if (connected) { 16210297Ssam if (cout != NULL) { 16326495Sminshall (void) shutdown(fileno(cout), 1+1); 16426495Sminshall (void) fclose(cout); 16510297Ssam cout = NULL; 16610297Ssam } 16710297Ssam if (data >= 0) { 16826495Sminshall (void) shutdown(data, 1+1); 16910297Ssam (void) close(data); 17010297Ssam data = -1; 17110297Ssam } 17210297Ssam connected = 0; 17310297Ssam } 17426047Sminshall pswitch(1); 17526047Sminshall if (connected) { 17626047Sminshall if (cout != NULL) { 17726495Sminshall (void) shutdown(fileno(cout), 1+1); 17826495Sminshall (void) fclose(cout); 17926047Sminshall cout = NULL; 18026047Sminshall } 18126047Sminshall connected = 0; 18226047Sminshall } 18326047Sminshall proxflag = 0; 18426047Sminshall pswitch(0); 18510297Ssam } 18610297Ssam 18726495Sminshall /*char * 18810297Ssam tail(filename) 18910297Ssam char *filename; 19010297Ssam { 19110297Ssam register char *s; 19210297Ssam 19310297Ssam while (*filename) { 19410297Ssam s = rindex(filename, '/'); 19510297Ssam if (s == NULL) 19610297Ssam break; 19710297Ssam if (s[1]) 19810297Ssam return (s + 1); 19910297Ssam *s = '\0'; 20010297Ssam } 20110297Ssam return (filename); 20210297Ssam } 20326495Sminshall */ 20410297Ssam /* 20510297Ssam * Command parser. 20610297Ssam */ 20710297Ssam cmdscanner(top) 20810297Ssam int top; 20910297Ssam { 21010297Ssam register struct cmd *c; 21110297Ssam struct cmd *getcmd(); 21210297Ssam extern int help(); 21310297Ssam 21410297Ssam if (!top) 21526495Sminshall (void) putchar('\n'); 21610297Ssam for (;;) { 21710297Ssam if (fromatty) { 21810297Ssam printf("ftp> "); 21926495Sminshall (void) fflush(stdout); 22010297Ssam } 22113968Ssam if (gets(line) == 0) { 22233374Stef if (feof(stdin) || ferror(stdin)) 22326082Slepreau quit(); 22410297Ssam break; 22513968Ssam } 22610297Ssam if (line[0] == 0) 22710297Ssam break; 22810297Ssam makeargv(); 22926047Sminshall if (margc == 0) { 23025907Smckusick continue; 23126047Sminshall } 23210297Ssam c = getcmd(margv[0]); 23310297Ssam if (c == (struct cmd *)-1) { 23410297Ssam printf("?Ambiguous command\n"); 23510297Ssam continue; 23610297Ssam } 23710297Ssam if (c == 0) { 23810297Ssam printf("?Invalid command\n"); 23910297Ssam continue; 24010297Ssam } 24111654Ssam if (c->c_conn && !connected) { 24211654Ssam printf ("Not connected.\n"); 24311654Ssam continue; 24411654Ssam } 24510297Ssam (*c->c_handler)(margc, margv); 24610297Ssam if (bell && c->c_bell) 24736935Skarels (void) putchar('\007'); 24810297Ssam if (c->c_handler != help) 24910297Ssam break; 25010297Ssam } 25126495Sminshall (void) signal(SIGINT, intr); 25226495Sminshall (void) signal(SIGPIPE, lostpeer); 25310297Ssam } 25410297Ssam 25510297Ssam struct cmd * 25610297Ssam getcmd(name) 25710297Ssam register char *name; 25810297Ssam { 25933224Sbostic extern struct cmd cmdtab[]; 26010297Ssam register char *p, *q; 26110297Ssam register struct cmd *c, *found; 26210297Ssam register int nmatches, longest; 26310297Ssam 26410297Ssam longest = 0; 26510297Ssam nmatches = 0; 26610297Ssam found = 0; 26710297Ssam for (c = cmdtab; p = c->c_name; c++) { 26810297Ssam for (q = name; *q == *p++; q++) 26910297Ssam if (*q == 0) /* exact match? */ 27010297Ssam return (c); 27110297Ssam if (!*q) { /* the name was a prefix */ 27210297Ssam if (q - name > longest) { 27310297Ssam longest = q - name; 27410297Ssam nmatches = 1; 27510297Ssam found = c; 27610297Ssam } else if (q - name == longest) 27710297Ssam nmatches++; 27810297Ssam } 27910297Ssam } 28010297Ssam if (nmatches > 1) 28110297Ssam return ((struct cmd *)-1); 28210297Ssam return (found); 28310297Ssam } 28410297Ssam 28510297Ssam /* 28610297Ssam * Slice a string up into argc/argv. 28710297Ssam */ 28826047Sminshall 28926047Sminshall int slrflag; 29026047Sminshall 29110297Ssam makeargv() 29210297Ssam { 29310297Ssam char **argp; 29410297Ssam char *slurpstring(); 29510297Ssam 29610297Ssam margc = 0; 29710297Ssam argp = margv; 29810297Ssam stringbase = line; /* scan from first of buffer */ 29910297Ssam argbase = argbuf; /* store from first of buffer */ 30026047Sminshall slrflag = 0; 30126047Sminshall while (*argp++ = slurpstring()) 30210297Ssam margc++; 30310297Ssam } 30410297Ssam 30510297Ssam /* 30610297Ssam * Parse string into argbuf; 30710297Ssam * implemented with FSM to 30810297Ssam * handle quoting and strings 30910297Ssam */ 31010297Ssam char * 31110297Ssam slurpstring() 31210297Ssam { 31310297Ssam int got_one = 0; 31410297Ssam register char *sb = stringbase; 31510297Ssam register char *ap = argbase; 31610297Ssam char *tmp = argbase; /* will return this if token found */ 31710297Ssam 31826047Sminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 31926047Sminshall switch (slrflag) { /* and $ as token for macro invoke */ 32026047Sminshall case 0: 32126047Sminshall slrflag++; 32226047Sminshall stringbase++; 32326047Sminshall return ((*sb == '!') ? "!" : "$"); 32436935Skarels /* NOTREACHED */ 32526047Sminshall case 1: 32626047Sminshall slrflag++; 32726047Sminshall altarg = stringbase; 32826047Sminshall break; 32926047Sminshall default: 33026047Sminshall break; 33126047Sminshall } 33226047Sminshall } 33326047Sminshall 33410297Ssam S0: 33510297Ssam switch (*sb) { 33610297Ssam 33710297Ssam case '\0': 33810297Ssam goto OUT; 33910297Ssam 34010297Ssam case ' ': 34110297Ssam case '\t': 34210297Ssam sb++; goto S0; 34310297Ssam 34410297Ssam default: 34526047Sminshall switch (slrflag) { 34626047Sminshall case 0: 34726047Sminshall slrflag++; 34826047Sminshall break; 34926047Sminshall case 1: 35026047Sminshall slrflag++; 35126047Sminshall altarg = sb; 35226047Sminshall break; 35326047Sminshall default: 35426047Sminshall break; 35526047Sminshall } 35610297Ssam goto S1; 35710297Ssam } 35810297Ssam 35910297Ssam S1: 36010297Ssam switch (*sb) { 36110297Ssam 36210297Ssam case ' ': 36310297Ssam case '\t': 36410297Ssam case '\0': 36510297Ssam goto OUT; /* end of token */ 36610297Ssam 36710297Ssam case '\\': 36810297Ssam sb++; goto S2; /* slurp next character */ 36910297Ssam 37010297Ssam case '"': 37110297Ssam sb++; goto S3; /* slurp quoted string */ 37210297Ssam 37310297Ssam default: 37410297Ssam *ap++ = *sb++; /* add character to token */ 37510297Ssam got_one = 1; 37610297Ssam goto S1; 37710297Ssam } 37810297Ssam 37910297Ssam S2: 38010297Ssam switch (*sb) { 38110297Ssam 38210297Ssam case '\0': 38310297Ssam goto OUT; 38410297Ssam 38510297Ssam default: 38610297Ssam *ap++ = *sb++; 38710297Ssam got_one = 1; 38810297Ssam goto S1; 38910297Ssam } 39010297Ssam 39110297Ssam S3: 39210297Ssam switch (*sb) { 39310297Ssam 39410297Ssam case '\0': 39510297Ssam goto OUT; 39610297Ssam 39710297Ssam case '"': 39810297Ssam sb++; goto S1; 39910297Ssam 40010297Ssam default: 40110297Ssam *ap++ = *sb++; 40210297Ssam got_one = 1; 40310297Ssam goto S3; 40410297Ssam } 40510297Ssam 40610297Ssam OUT: 40710297Ssam if (got_one) 40810297Ssam *ap++ = '\0'; 40910297Ssam argbase = ap; /* update storage pointer */ 41010297Ssam stringbase = sb; /* update scan pointer */ 41126047Sminshall if (got_one) { 41210297Ssam return(tmp); 41326047Sminshall } 41426047Sminshall switch (slrflag) { 41526047Sminshall case 0: 41626047Sminshall slrflag++; 41726047Sminshall break; 41826047Sminshall case 1: 41926047Sminshall slrflag++; 42026047Sminshall altarg = (char *) 0; 42126047Sminshall break; 42226047Sminshall default: 42326047Sminshall break; 42426047Sminshall } 42510297Ssam return((char *)0); 42610297Ssam } 42710297Ssam 42810297Ssam #define HELPINDENT (sizeof ("directory")) 42910297Ssam 43010297Ssam /* 43110297Ssam * Help command. 43210297Ssam * Call each command handler with argc == 0 and argv[0] == name. 43310297Ssam */ 43410297Ssam help(argc, argv) 43510297Ssam int argc; 43610297Ssam char *argv[]; 43710297Ssam { 43833224Sbostic extern struct cmd cmdtab[]; 43910297Ssam register struct cmd *c; 44010297Ssam 44110297Ssam if (argc == 1) { 44226047Sminshall register int i, j, w, k; 44310297Ssam int columns, width = 0, lines; 44410297Ssam extern int NCMDS; 44510297Ssam 44610297Ssam printf("Commands may be abbreviated. Commands are:\n\n"); 44710297Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 44810297Ssam int len = strlen(c->c_name); 44910297Ssam 45010297Ssam if (len > width) 45110297Ssam width = len; 45210297Ssam } 45310297Ssam width = (width + 8) &~ 7; 45410297Ssam columns = 80 / width; 45510297Ssam if (columns == 0) 45610297Ssam columns = 1; 45710297Ssam lines = (NCMDS + columns - 1) / columns; 45810297Ssam for (i = 0; i < lines; i++) { 45910297Ssam for (j = 0; j < columns; j++) { 46010297Ssam c = cmdtab + j * lines + i; 46126047Sminshall if (c->c_name && (!proxy || c->c_proxy)) { 46225907Smckusick printf("%s", c->c_name); 46326047Sminshall } 46426047Sminshall else if (c->c_name) { 46526047Sminshall for (k=0; k < strlen(c->c_name); k++) { 46626495Sminshall (void) putchar(' '); 46726047Sminshall } 46826047Sminshall } 46910297Ssam if (c + lines >= &cmdtab[NCMDS]) { 47010297Ssam printf("\n"); 47110297Ssam break; 47210297Ssam } 47310297Ssam w = strlen(c->c_name); 47410297Ssam while (w < width) { 47510297Ssam w = (w + 8) &~ 7; 47626495Sminshall (void) putchar('\t'); 47710297Ssam } 47810297Ssam } 47910297Ssam } 48010297Ssam return; 48110297Ssam } 48210297Ssam while (--argc > 0) { 48310297Ssam register char *arg; 48410297Ssam arg = *++argv; 48510297Ssam c = getcmd(arg); 48610297Ssam if (c == (struct cmd *)-1) 48710297Ssam printf("?Ambiguous help command %s\n", arg); 48810297Ssam else if (c == (struct cmd *)0) 48910297Ssam printf("?Invalid help command %s\n", arg); 49010297Ssam else 49110297Ssam printf("%-*s\t%s\n", HELPINDENT, 49210297Ssam c->c_name, c->c_help); 49310297Ssam } 49410297Ssam } 49510297Ssam 49610297Ssam /* 49710297Ssam * Call routine with argc, argv set from args (terminated by 0). 49810297Ssam */ 49926495Sminshall /*VARARGS1*/ 50010297Ssam call(routine, args) 50110297Ssam int (*routine)(); 50210297Ssam int args; 50310297Ssam { 50410297Ssam register int *argp; 50510297Ssam register int argc; 50610297Ssam 50710297Ssam for (argc = 0, argp = &args; *argp++ != 0; argc++) 50810297Ssam ; 50910297Ssam (*routine)(argc, &args); 51010297Ssam } 511