1662bbc18Sdist /*
2679d30f6Spendry * Copyright (c) 1985, 1989, 1993, 1994
3306c4fb0Sbostic * The Regents of the University of California. All rights reserved.
4c2a61bcaSbostic *
50cd120c3Sbostic * %sccs.include.redist.c%
6662bbc18Sdist */
7662bbc18Sdist
81b61d977Ssam #ifndef lint
9306c4fb0Sbostic static char copyright[] =
10679d30f6Spendry "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
11306c4fb0Sbostic The Regents of the University of California. All rights reserved.\n";
12c2a61bcaSbostic #endif /* not lint */
13662bbc18Sdist
14662bbc18Sdist #ifndef lint
15*4bbb06eaSmckusick static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/09/94";
16c2a61bcaSbostic #endif /* not lint */
171b61d977Ssam
181b61d977Ssam /*
191b61d977Ssam * FTP User Program -- Command Interface.
201b61d977Ssam */
212bb83062Spendry /*#include <sys/ioctl.h>*/
22a4f214cfSminshall #include <sys/types.h>
232bb83062Spendry #include <sys/socket.h>
241b61d977Ssam
2501aaa315Ssam #include <arpa/ftp.h>
2601aaa315Ssam
271b61d977Ssam #include <ctype.h>
282bb83062Spendry #include <err.h>
291b61d977Ssam #include <netdb.h>
308a4eeb38Ssam #include <pwd.h>
312bb83062Spendry #include <signal.h>
322bb83062Spendry #include <stdio.h>
332bb83062Spendry #include <stdlib.h>
342bb83062Spendry #include <unistd.h>
351b61d977Ssam
362bb83062Spendry #include "ftp_var.h"
371b61d977Ssam
382bb83062Spendry int
main(argc,argv)391b61d977Ssam main(argc, argv)
402bb83062Spendry int argc;
411b61d977Ssam char *argv[];
421b61d977Ssam {
432bb83062Spendry int ch, top;
4469c0d6f7Smckusick struct passwd *pw = NULL;
452bb83062Spendry char *cp, homedir[MAXPATHLEN];
461b61d977Ssam
471b61d977Ssam sp = getservbyname("ftp", "tcp");
482bb83062Spendry if (sp == 0)
492bb83062Spendry errx(1, "ftp/tcp: unknown service");
506f659679Ssam doglob = 1;
518bb385cbSsam interactive = 1;
526f659679Ssam autologin = 1;
531b61d977Ssam
542bb83062Spendry while ((ch = getopt(argc, argv, "dgintv")) != EOF) {
559b2ce23aSbostic switch (ch) {
561b61d977Ssam case 'd':
571b61d977Ssam options |= SO_DEBUG;
581b61d977Ssam debug++;
591b61d977Ssam break;
601b61d977Ssam
612bb83062Spendry case 'g':
622bb83062Spendry doglob = 0;
631b61d977Ssam break;
641b61d977Ssam
651b61d977Ssam case 'i':
668bb385cbSsam interactive = 0;
671b61d977Ssam break;
681b61d977Ssam
691b61d977Ssam case 'n':
701b61d977Ssam autologin = 0;
711b61d977Ssam break;
721b61d977Ssam
732bb83062Spendry case 't':
742bb83062Spendry trace++;
752bb83062Spendry break;
762bb83062Spendry
772bb83062Spendry case 'v':
782bb83062Spendry verbose++;
796f659679Ssam break;
806f659679Ssam
811b61d977Ssam default:
822bb83062Spendry (void)fprintf(stderr,
832bb83062Spendry "usage: ftp [-dgintv] [host [port]]\n");
841b61d977Ssam exit(1);
851b61d977Ssam }
861b61d977Ssam }
872bb83062Spendry argc -= optind;
882bb83062Spendry argv += optind;
892bb83062Spendry
901b61d977Ssam fromatty = isatty(fileno(stdin));
911b61d977Ssam if (fromatty)
921b61d977Ssam verbose++;
934f2e7c0aSminshall cpend = 0; /* no pending replies */
944f2e7c0aSminshall proxy = 0; /* proxy not active */
95*4bbb06eaSmckusick passivemode = 0; /* passive mode not active */
964f2e7c0aSminshall crflag = 1; /* strip c.r. on ascii gets */
979d94ba29Srick sendport = -1; /* not using ports */
988a4eeb38Ssam /*
998a4eeb38Ssam * Set up the home directory in case we're globbing.
1008a4eeb38Ssam */
10169c0d6f7Smckusick cp = getlogin();
1024f2e7c0aSminshall if (cp != NULL) {
10369c0d6f7Smckusick pw = getpwnam(cp);
1044f2e7c0aSminshall }
1058a4eeb38Ssam if (pw == NULL)
1068a4eeb38Ssam pw = getpwuid(getuid());
1078a4eeb38Ssam if (pw != NULL) {
1088a4eeb38Ssam home = homedir;
109a4f214cfSminshall (void) strcpy(home, pw->pw_dir);
1108a4eeb38Ssam }
1111b61d977Ssam if (argc > 0) {
112e8175c4fSpendry char *xargv[5];
1132bb83062Spendry extern char *__progname;
1142bb83062Spendry
1151b61d977Ssam if (setjmp(toplevel))
1161b61d977Ssam exit(0);
117a4f214cfSminshall (void) signal(SIGINT, intr);
118a4f214cfSminshall (void) signal(SIGPIPE, lostpeer);
1192bb83062Spendry xargv[0] = __progname;
1202bb83062Spendry xargv[1] = argv[0];
1212bb83062Spendry xargv[2] = argv[1];
1222bb83062Spendry xargv[3] = argv[2];
123e8175c4fSpendry xargv[4] = NULL;
1242bb83062Spendry setpeer(argc+1, xargv);
1251b61d977Ssam }
1261b61d977Ssam top = setjmp(toplevel) == 0;
1271b61d977Ssam if (top) {
128a4f214cfSminshall (void) signal(SIGINT, intr);
129a4f214cfSminshall (void) signal(SIGPIPE, lostpeer);
1301b61d977Ssam }
1311b61d977Ssam for (;;) {
1321b61d977Ssam cmdscanner(top);
1331b61d977Ssam top = 1;
1341b61d977Ssam }
1351b61d977Ssam }
1361b61d977Ssam
137dd8ce21eSbostic void
intr()1381b61d977Ssam intr()
1391b61d977Ssam {
1401b61d977Ssam
1411b61d977Ssam longjmp(toplevel, 1);
1421b61d977Ssam }
1431b61d977Ssam
144dd8ce21eSbostic void
lostpeer()1451b61d977Ssam lostpeer()
1461b61d977Ssam {
1471b61d977Ssam
1481b61d977Ssam if (connected) {
1491b61d977Ssam if (cout != NULL) {
150a4f214cfSminshall (void) shutdown(fileno(cout), 1+1);
151a4f214cfSminshall (void) fclose(cout);
1521b61d977Ssam cout = NULL;
1531b61d977Ssam }
1541b61d977Ssam if (data >= 0) {
155a4f214cfSminshall (void) shutdown(data, 1+1);
1561b61d977Ssam (void) close(data);
1571b61d977Ssam data = -1;
1581b61d977Ssam }
1591b61d977Ssam connected = 0;
1601b61d977Ssam }
1614f2e7c0aSminshall pswitch(1);
1624f2e7c0aSminshall if (connected) {
1634f2e7c0aSminshall if (cout != NULL) {
164a4f214cfSminshall (void) shutdown(fileno(cout), 1+1);
165a4f214cfSminshall (void) fclose(cout);
1664f2e7c0aSminshall cout = NULL;
1674f2e7c0aSminshall }
1684f2e7c0aSminshall connected = 0;
1694f2e7c0aSminshall }
1704f2e7c0aSminshall proxflag = 0;
1714f2e7c0aSminshall pswitch(0);
1721b61d977Ssam }
1731b61d977Ssam
1742bb83062Spendry /*
1752bb83062Spendry char *
1761b61d977Ssam tail(filename)
1771b61d977Ssam char *filename;
1781b61d977Ssam {
1792bb83062Spendry char *s;
1801b61d977Ssam
1811b61d977Ssam while (*filename) {
1822bb83062Spendry s = strrchr(filename, '/');
1831b61d977Ssam if (s == NULL)
1841b61d977Ssam break;
1851b61d977Ssam if (s[1])
1861b61d977Ssam return (s + 1);
1871b61d977Ssam *s = '\0';
1881b61d977Ssam }
1891b61d977Ssam return (filename);
1901b61d977Ssam }
191a4f214cfSminshall */
1922bb83062Spendry
1931b61d977Ssam /*
1941b61d977Ssam * Command parser.
1951b61d977Ssam */
1962bb83062Spendry void
cmdscanner(top)1971b61d977Ssam cmdscanner(top)
1981b61d977Ssam int top;
1991b61d977Ssam {
2002bb83062Spendry struct cmd *c;
2012bb83062Spendry int l;
2021b61d977Ssam
2031b61d977Ssam if (!top)
204a4f214cfSminshall (void) putchar('\n');
2051b61d977Ssam for (;;) {
2061b61d977Ssam if (fromatty) {
2071b61d977Ssam printf("ftp> ");
208a4f214cfSminshall (void) fflush(stdout);
2091b61d977Ssam }
21066be170bSbostic if (fgets(line, sizeof line, stdin) == NULL)
2112bb83062Spendry quit(0, 0);
21266be170bSbostic l = strlen(line);
21366be170bSbostic if (l == 0)
2141b61d977Ssam break;
21566be170bSbostic if (line[--l] == '\n') {
21666be170bSbostic if (l == 0)
2171b61d977Ssam break;
21866be170bSbostic line[l] = '\0';
21966be170bSbostic } else if (l == sizeof(line) - 2) {
22066be170bSbostic printf("sorry, input line too long\n");
22166be170bSbostic while ((l = getchar()) != '\n' && l != EOF)
22266be170bSbostic /* void */;
22366be170bSbostic break;
22466be170bSbostic } /* else it was a line without a newline */
2251b61d977Ssam makeargv();
2264f2e7c0aSminshall if (margc == 0) {
22769c0d6f7Smckusick continue;
2284f2e7c0aSminshall }
2291b61d977Ssam c = getcmd(margv[0]);
2301b61d977Ssam if (c == (struct cmd *)-1) {
2311b61d977Ssam printf("?Ambiguous command\n");
2321b61d977Ssam continue;
2331b61d977Ssam }
2341b61d977Ssam if (c == 0) {
2351b61d977Ssam printf("?Invalid command\n");
2361b61d977Ssam continue;
2371b61d977Ssam }
2388bb385cbSsam if (c->c_conn && !connected) {
2398bb385cbSsam printf("Not connected.\n");
2408bb385cbSsam continue;
2418bb385cbSsam }
2421b61d977Ssam (*c->c_handler)(margc, margv);
2431b61d977Ssam if (bell && c->c_bell)
244e3d8fb40Skarels (void) putchar('\007');
2451b61d977Ssam if (c->c_handler != help)
2461b61d977Ssam break;
2471b61d977Ssam }
248a4f214cfSminshall (void) signal(SIGINT, intr);
249a4f214cfSminshall (void) signal(SIGPIPE, lostpeer);
2501b61d977Ssam }
2511b61d977Ssam
2521b61d977Ssam struct cmd *
getcmd(name)2531b61d977Ssam getcmd(name)
2542bb83062Spendry char *name;
2551b61d977Ssam {
2562bb83062Spendry char *p, *q;
2572bb83062Spendry struct cmd *c, *found;
2582bb83062Spendry int nmatches, longest;
2591b61d977Ssam
2601b61d977Ssam longest = 0;
2611b61d977Ssam nmatches = 0;
2621b61d977Ssam found = 0;
2631b61d977Ssam for (c = cmdtab; p = c->c_name; c++) {
2641b61d977Ssam for (q = name; *q == *p++; q++)
2651b61d977Ssam if (*q == 0) /* exact match? */
2661b61d977Ssam return (c);
2671b61d977Ssam if (!*q) { /* the name was a prefix */
2681b61d977Ssam if (q - name > longest) {
2691b61d977Ssam longest = q - name;
2701b61d977Ssam nmatches = 1;
2711b61d977Ssam found = c;
2721b61d977Ssam } else if (q - name == longest)
2731b61d977Ssam nmatches++;
2741b61d977Ssam }
2751b61d977Ssam }
2761b61d977Ssam if (nmatches > 1)
2771b61d977Ssam return ((struct cmd *)-1);
2781b61d977Ssam return (found);
2791b61d977Ssam }
2801b61d977Ssam
2811b61d977Ssam /*
2821b61d977Ssam * Slice a string up into argc/argv.
2831b61d977Ssam */
2844f2e7c0aSminshall
2854f2e7c0aSminshall int slrflag;
2864f2e7c0aSminshall
2872bb83062Spendry void
makeargv()2881b61d977Ssam makeargv()
2891b61d977Ssam {
2901b61d977Ssam char **argp;
2911b61d977Ssam
2921b61d977Ssam margc = 0;
2931b61d977Ssam argp = margv;
2941b61d977Ssam stringbase = line; /* scan from first of buffer */
2951b61d977Ssam argbase = argbuf; /* store from first of buffer */
2964f2e7c0aSminshall slrflag = 0;
2971b61d977Ssam while (*argp++ = slurpstring())
2981b61d977Ssam margc++;
2991b61d977Ssam }
3001b61d977Ssam
3011b61d977Ssam /*
3021b61d977Ssam * Parse string into argbuf;
3031b61d977Ssam * implemented with FSM to
3041b61d977Ssam * handle quoting and strings
3051b61d977Ssam */
3061b61d977Ssam char *
slurpstring()3071b61d977Ssam slurpstring()
3081b61d977Ssam {
3091b61d977Ssam int got_one = 0;
3102bb83062Spendry char *sb = stringbase;
3112bb83062Spendry char *ap = argbase;
3121b61d977Ssam char *tmp = argbase; /* will return this if token found */
3131b61d977Ssam
3144f2e7c0aSminshall if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
3154f2e7c0aSminshall switch (slrflag) { /* and $ as token for macro invoke */
3164f2e7c0aSminshall case 0:
3174f2e7c0aSminshall slrflag++;
3184f2e7c0aSminshall stringbase++;
3194f2e7c0aSminshall return ((*sb == '!') ? "!" : "$");
320e3d8fb40Skarels /* NOTREACHED */
3214f2e7c0aSminshall case 1:
3224f2e7c0aSminshall slrflag++;
3234f2e7c0aSminshall altarg = stringbase;
3244f2e7c0aSminshall break;
3254f2e7c0aSminshall default:
3264f2e7c0aSminshall break;
3274f2e7c0aSminshall }
3284f2e7c0aSminshall }
3294f2e7c0aSminshall
3301b61d977Ssam S0:
3311b61d977Ssam switch (*sb) {
3321b61d977Ssam
3331b61d977Ssam case '\0':
3341b61d977Ssam goto OUT;
3351b61d977Ssam
3361b61d977Ssam case ' ':
3371b61d977Ssam case '\t':
3381b61d977Ssam sb++; goto S0;
3391b61d977Ssam
3401b61d977Ssam default:
3414f2e7c0aSminshall switch (slrflag) {
3424f2e7c0aSminshall case 0:
3434f2e7c0aSminshall slrflag++;
3444f2e7c0aSminshall break;
3454f2e7c0aSminshall case 1:
3464f2e7c0aSminshall slrflag++;
3474f2e7c0aSminshall altarg = sb;
3484f2e7c0aSminshall break;
3494f2e7c0aSminshall default:
3504f2e7c0aSminshall break;
3514f2e7c0aSminshall }
3521b61d977Ssam goto S1;
3531b61d977Ssam }
3541b61d977Ssam
3551b61d977Ssam S1:
3561b61d977Ssam switch (*sb) {
3571b61d977Ssam
3581b61d977Ssam case ' ':
3591b61d977Ssam case '\t':
3601b61d977Ssam case '\0':
3611b61d977Ssam goto OUT; /* end of token */
3621b61d977Ssam
3631b61d977Ssam case '\\':
3641b61d977Ssam sb++; goto S2; /* slurp next character */
3651b61d977Ssam
3661b61d977Ssam case '"':
3671b61d977Ssam sb++; goto S3; /* slurp quoted string */
3681b61d977Ssam
3691b61d977Ssam default:
3701b61d977Ssam *ap++ = *sb++; /* add character to token */
3711b61d977Ssam got_one = 1;
3721b61d977Ssam goto S1;
3731b61d977Ssam }
3741b61d977Ssam
3751b61d977Ssam S2:
3761b61d977Ssam switch (*sb) {
3771b61d977Ssam
3781b61d977Ssam case '\0':
3791b61d977Ssam goto OUT;
3801b61d977Ssam
3811b61d977Ssam default:
3821b61d977Ssam *ap++ = *sb++;
3831b61d977Ssam got_one = 1;
3841b61d977Ssam goto S1;
3851b61d977Ssam }
3861b61d977Ssam
3871b61d977Ssam S3:
3881b61d977Ssam switch (*sb) {
3891b61d977Ssam
3901b61d977Ssam case '\0':
3911b61d977Ssam goto OUT;
3921b61d977Ssam
3931b61d977Ssam case '"':
3941b61d977Ssam sb++; goto S1;
3951b61d977Ssam
3961b61d977Ssam default:
3971b61d977Ssam *ap++ = *sb++;
3981b61d977Ssam got_one = 1;
3991b61d977Ssam goto S3;
4001b61d977Ssam }
4011b61d977Ssam
4021b61d977Ssam OUT:
4031b61d977Ssam if (got_one)
4041b61d977Ssam *ap++ = '\0';
4051b61d977Ssam argbase = ap; /* update storage pointer */
4061b61d977Ssam stringbase = sb; /* update scan pointer */
4074f2e7c0aSminshall if (got_one) {
4081b61d977Ssam return (tmp);
4094f2e7c0aSminshall }
4104f2e7c0aSminshall switch (slrflag) {
4114f2e7c0aSminshall case 0:
4124f2e7c0aSminshall slrflag++;
4134f2e7c0aSminshall break;
4144f2e7c0aSminshall case 1:
4154f2e7c0aSminshall slrflag++;
4164f2e7c0aSminshall altarg = (char *) 0;
4174f2e7c0aSminshall break;
4184f2e7c0aSminshall default:
4194f2e7c0aSminshall break;
4204f2e7c0aSminshall }
4211b61d977Ssam return ((char *)0);
4221b61d977Ssam }
4231b61d977Ssam
4242bb83062Spendry #define HELPINDENT ((int) sizeof ("directory"))
4251b61d977Ssam
4261b61d977Ssam /*
4271b61d977Ssam * Help command.
4281b61d977Ssam * Call each command handler with argc == 0 and argv[0] == name.
4291b61d977Ssam */
4302bb83062Spendry void
help(argc,argv)4311b61d977Ssam help(argc, argv)
4321b61d977Ssam int argc;
4331b61d977Ssam char *argv[];
4341b61d977Ssam {
4352bb83062Spendry struct cmd *c;
4361b61d977Ssam
4371b61d977Ssam if (argc == 1) {
4382bb83062Spendry int i, j, w, k;
4391b61d977Ssam int columns, width = 0, lines;
4401b61d977Ssam
4411b61d977Ssam printf("Commands may be abbreviated. Commands are:\n\n");
4421b61d977Ssam for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
4431b61d977Ssam int len = strlen(c->c_name);
4441b61d977Ssam
4451b61d977Ssam if (len > width)
4461b61d977Ssam width = len;
4471b61d977Ssam }
4481b61d977Ssam width = (width + 8) &~ 7;
4491b61d977Ssam columns = 80 / width;
4501b61d977Ssam if (columns == 0)
4511b61d977Ssam columns = 1;
4521b61d977Ssam lines = (NCMDS + columns - 1) / columns;
4531b61d977Ssam for (i = 0; i < lines; i++) {
4541b61d977Ssam for (j = 0; j < columns; j++) {
4551b61d977Ssam c = cmdtab + j * lines + i;
4564f2e7c0aSminshall if (c->c_name && (!proxy || c->c_proxy)) {
4571b61d977Ssam printf("%s", c->c_name);
4584f2e7c0aSminshall }
4594f2e7c0aSminshall else if (c->c_name) {
4604f2e7c0aSminshall for (k=0; k < strlen(c->c_name); k++) {
461a4f214cfSminshall (void) putchar(' ');
4624f2e7c0aSminshall }
4634f2e7c0aSminshall }
4641b61d977Ssam if (c + lines >= &cmdtab[NCMDS]) {
4651b61d977Ssam printf("\n");
4661b61d977Ssam break;
4671b61d977Ssam }
4681b61d977Ssam w = strlen(c->c_name);
4691b61d977Ssam while (w < width) {
4701b61d977Ssam w = (w + 8) &~ 7;
471a4f214cfSminshall (void) putchar('\t');
4721b61d977Ssam }
4731b61d977Ssam }
4741b61d977Ssam }
4751b61d977Ssam return;
4761b61d977Ssam }
4771b61d977Ssam while (--argc > 0) {
4782bb83062Spendry char *arg;
4791b61d977Ssam arg = *++argv;
4801b61d977Ssam c = getcmd(arg);
4811b61d977Ssam if (c == (struct cmd *)-1)
4821b61d977Ssam printf("?Ambiguous help command %s\n", arg);
4831b61d977Ssam else if (c == (struct cmd *)0)
4841b61d977Ssam printf("?Invalid help command %s\n", arg);
4851b61d977Ssam else
4861b61d977Ssam printf("%-*s\t%s\n", HELPINDENT,
4871b61d977Ssam c->c_name, c->c_help);
4881b61d977Ssam }
4891b61d977Ssam }
490