home *** CD-ROM | disk | FTP | other *** search
Wrap
/* main.c */ #define _main_c_ #include "sys.h" #include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/time.h> #include <arpa/ftp.h> #include <setjmp.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <ctype.h> #include <netdb.h> #include <unistd.h> #include <pwd.h> #ifdef TCAP # undef HZ /* Collides with HaZeltine ! */ # include <tcap.h> #endif /* TCAP */ #include "ftpdefs.h" #include "defaults.h" #include "cmds.h" #include "main.h" #include "ftp.h" #include "ftprc.h" #include "copyright.h" /* main.c globals */ int slrflag; int fromatty; /* input is from a terminal */ char *altarg; /* argv[1] with no shell-like preprocessing */ struct servent *sp; /* service spec for tcp/ftp */ jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ char *line; /* input line buffer */ char *stringbase; /* current scan point in line buffer */ char *argbuf; /* argument storage buffer */ char *argbase; /* current storage point in arg buffer */ int margc; /* count of arguments on input line */ char *margv[20]; /* args parsed from input line */ struct userinfo uinfo; /* a copy of their pwent really */ int ansi_escapes; /* for fancy graphics */ int ignore_rc; /* are we supposed to ignore the netrc */ string progname; /* simple filename */ string prompt, prompt2; /* shell prompt string */ string anon_password; /* most likely your email address */ string pager; /* program to browse text files */ string version = FTP_VERSION; long eventnumber; /* number of commands we've done */ FILE *logf = NULL; /* log user activity */ string logfname; /* name of the logfile */ long logsize = 4096L; /* max log size. 0 == no limit */ int percent_flags; /* "%" in prompt string? */ int at_flags; /* "@" in prompt string? */ string mail_path; /* your mailbox */ int newmail; /* how many new letters you have */ time_t mbox_time; /* last modified time of mbox */ static char *tcap_normal = "\033[0m"; /* Default ANSI escapes */ static char *tcap_boldface = "\033[1m"; static char *tcap_underline = "\033[4m"; static char *tcap_reverse = "\033[7m"; #ifdef TCAP static char tcbuf[2048]; #endif /* main.c externs */ extern int debug, verbose, mprompt; extern int options, cpend, data, connected; extern int curtype, macnum; extern FILE *cout; extern struct cmd cmdtab[]; extern str32 curtypename; extern char *macbuf; extern char *reply_string; extern string hostname, cwd, lcwd; extern int optind; extern char *optarg; main(int argc, char **argv) { register char *cp; int top, opt; if ((cp = rindex(argv[0], '/'))) cp++; else cp = argv[0]; (void) Strncpy(progname, cp); sp = getservbyname("ftp", "tcp"); if (sp == 0) fatal("ftp/tcp: unknown service"); if (init_arrays()) /* Reserve large blocks of memory now */ fatal("could not reserve large amounts of memory."); /* * Set up defaults for FTP. */ mprompt = dMPROMPT; verbose = dVERBOSE; debug = dDEBUG; (void) Strncpy(curtypename, dTYPESTR); curtype = dTYPE; (void) Strncpy(prompt, dPROMPT); /* Setup our pager variable, before we run through the rc, which may change it. */ set_pager(getenv("PAGER"), 0); ansi_escapes = 0; if ((cp = getenv("TERM")) != NULL && *cp++ == 'v' && *cp++ == 't') ansi_escapes = 1; /* only if a vt100ish terminal */ (void) getuserinfo(); newmail = 0; (void) time(&mbox_time); (void) Strncpy(anon_password, uinfo.username); if (getlocalhostname(uinfo.hostname, sizeof(uinfo.hostname)) == 0) { (void) Strncat(anon_password, "@"); (void) Strncat(anon_password, uinfo.hostname); } #if dLOGGING (void) sprintf(logfname, "%s/%s", uinfo.homedir, dLOGNAME); #else *logfname = 0; #endif (void) get_cwd(lcwd, (int) sizeof(lcwd)); ignore_rc = 0; while ((opt = getopt(argc, argv, "dvinr0hH")) >= 0) { switch(opt) { case 'd': /* options |= SO_DEBUG; done below... */ debug++; break; case 'v': verbose++; break; case 'i': mprompt = !mprompt; break; case 'n': case 'r': case '0': ignore_rc = !ignore_rc; break; case 'H': case 'h': show_version(0, NULL); exit (0); default: (void) fprintf(stderr, "%s: unknown option \"%c\"\n", progname, (int) *cp); } } if (ignore_rc == 0) (void) thrash_rc(); (void) fix_options(); /* adjust "options" according to "debug" */ fromatty = isatty(fileno(stdin)); cpend = 0; /* no pending replies */ if (*logfname) logf = fopen (logfname, "a"); eventnumber = 0L; cp = argv[optind]; /* The user specified a host on the command line. Open it now... */ if (argc > 1 && cp) { if (setjmp(toplevel)) exit(0); (void) signal(SIGINT, intr); (void) signal(SIGPIPE, lostpeer); (void) strcpy(line, "open "); (void) strcat(line, cp); makeargv(); (void) setpeer(margc, margv); } (void) init_prompt(); eventnumber = 1L; if (ansi_escapes) { #ifndef TCAP (void) printf("%s%s Ready.%s\n", tcap_boldface, FTP_VERSION, tcap_normal); #else char vis[BUFSIZ]; (void) sprintf(vis, "%s%s Ready.%s\n", tcap_boldface, FTP_VERSION, tcap_normal); tcap_put(vis); #endif /* !TCAP */ } else (void) printf("%s Ready.\n", FTP_VERSION); top = setjmp(toplevel) == 0; if (top) { (void) signal(SIGINT, intr); (void) signal(SIGPIPE, lostpeer); } for (;;) { (void) cmdscanner(top); top = 1; } } /* main */ /*ARGSUSED*/ void intr(int unused) { (void) longjmp(toplevel, 1); } /* intr */ int getuserinfo(void) { register char *cp; struct passwd *pw = NULL; string str; extern char *home; /* for glob.c */ cp = getlogin(); if (cp != NULL) pw = getpwnam(cp); if (pw == NULL) pw = getpwuid(getuid()); if (pw != NULL) { (void) Strncpy(uinfo.username, pw->pw_name); (void) Strncpy(uinfo.shell, pw->pw_shell); (void) Strncpy(uinfo.homedir, pw->pw_dir); uinfo.uid = pw->pw_uid; home = uinfo.homedir; /* for glob.c */ if (((cp = getenv("MAIL")) == NULL) && ((cp = getenv("mail")) == NULL)) { (void) sprintf(str, "/usr/spool/mail/%s", uinfo.username); cp = str; } /* mbox variable may be like MAIL=(28 /usr/mail/me /usr/mail/you), so try to find the first mail path. */ while (*cp != '/') cp++; (void) Strncpy(mail_path, cp); if ((cp = index(mail_path, ' ')) != NULL) *cp = '\0'; return (0); } else { (void) Strncpy(uinfo.username, "unknown"); (void) Strncpy(uinfo.shell, "/bin/sh"); (void) Strncpy(uinfo.homedir, "."); /* current directory */ uinfo.uid = 999; return (-1); } } /* getuserinfo */ int init_arrays(void) { if ((macbuf = (char *) malloc((size_t)(MACBUFLEN))) == NULL) goto barf; if ((line = (char *) malloc((size_t)(CMDLINELEN))) == NULL) goto barf; if ((argbuf = (char *) malloc((size_t)(CMDLINELEN))) == NULL) goto barf; if ((reply_string = (char *) malloc((size_t)(RECEIVEDLINELEN))) == NULL) goto barf; *macbuf = '\0'; init_transfer_buffer(); return (0); barf: return (-1); } /* init_arrays */ void init_transfer_buffer(void) { extern char *xferbuf; extern size_t xferbufsize; xferbufsize = MAX_XFER_BUFSIZE; while (1) { xferbuf = (char *) malloc (xferbufsize); if (xferbuf != NULL || xferbufsize < 1024) break; xferbufsize >>= 2; } if (xferbuf != NULL) return; fatal("out of memory for transfer buffer."); } /* init_transfer_buffer */ void init_prompt(void) { register char *cp; percent_flags = at_flags = 0; for (cp = prompt; *cp; cp++) { if (*cp == '%') percent_flags = 1; else if (*cp == '@') at_flags = 1; } } /* init_prompt */ /*ARGSUSED*/ void lostpeer(int unused) { if (connected) { close_streams(1); if (data >= 0) { (void) shutdown(data, 1+1); (void) close(data); data = -1; } connected = 0; } if (connected) { close_streams(1); connected = 0; } hostname[0] = cwd[0] = 0; macnum = 0; } /* lostpeer */ /* * Command parser. */ void cmdscanner(int top) { register struct cmd *c; #ifdef TCAP char vis[BUFSIZ], *vp; #endif if (!top) (void) putchar('\n'); for (;;) { if (fromatty) { #ifndef TCAP (void) printf(strprompt()); #else (void) sprintf(vis, "%s", strprompt()); tcap_put(vis); #endif /* !TCAP */ (void) fflush(stdout); } if (gets(line) == 0) { if (feof(stdin) || ferror(stdin)) (void) quit(0, NULL); /* control-d */ break; } if (line[0] == 0) /* blank line */ break; eventnumber++; if (debug > 1) (void) printf("---> \"%s\"\n", line); (void) makeargv(); if (margc == 0) { continue; /* blank line... */ } c = getcmd(margv[0]); if (c == (struct cmd *) -1) { (void) printf("?Ambiguous command\n"); continue; } if (c == 0) { if (!implicit_cd(margv[0])) (void) printf("?Invalid command\n"); continue; } if (c->c_conn && !connected) { (void) printf ("Not connected.\n"); continue; } (*c->c_handler)(margc, margv); if (c->c_handler != help) break; } (void) signal(SIGINT, intr); (void) signal(SIGPIPE, lostpeer); } /* cmdscanner */ char *strprompt(void) { time_t tyme; char eventstr[8]; register char *p, *q; string str; extern int percent_flags, at_flags; #ifdef TCAP static int virgin = 0; if (!virgin++ && ansi_escapes) termcap_init(); #endif /* TCAP */ if (at_flags == 0 && percent_flags == 0) return (prompt); /* But don't overwrite it! */ if (at_flags) { for (p = prompt, q = prompt2, *q = 0; (*p); p++) if (*p == '@') switch ((toupper(*++p))) { case '\0': --p; break; case 'M': if (CheckNewMail() > 0) q = Strpcpy(q, "(Mail) "); break; case 'P': /* reset to no bold, no uline, no inverse, etc. */ if (ansi_escapes) q = Strpcpy(q, tcap_normal); break; case 'B': /* toggle boldface */ if (ansi_escapes) q = Strpcpy(q, tcap_boldface); break; case 'U': /* toggle underline */ if (ansi_escapes) q = Strpcpy(q, tcap_underline); break; case 'R': case 'I': /* toggle inverse (reverse) video */ if (ansi_escapes) q = Strpcpy(q, tcap_reverse); break; case 'D': /* insert current directory */ if (cwd != NULL) q = Strpcpy(q, cwd); break; case 'H': /* insert name of connected host */ if (hostname != NULL) q = Strpcpy(q, hostname); break; case '!': case 'E': /* insert event number */ (void) sprintf(eventstr, "%ld", eventnumber); q = Strpcpy(q, eventstr); break; default: *q++ = *p; /* just copy it; unknown switch */ } else *q++ = *p; *q = '\0'; } else (void) strcpy(prompt2, prompt); if (percent_flags) { /* only strftime if the user requested it (with a %something), otherwise don't waste time doing nothing. */ (void) time(&tyme); (void) Strncpy(str, prompt2); (void) strftime(prompt2, sizeof(str), str, localtime(&tyme)); } return (prompt2); } /* strprompt */ char *Strpcpy(char *dst, char *src) { while (*dst++ = *src++) ; return (--dst); /* return current value of dst, NOT original value! */ } /* Strpcpy */ struct cmd *getcmd(char *name) { register char *p, *q; register struct cmd *c, *found; register int nmatches, longest; if (name == NULL) return (NULL); longest = 0; nmatches = 0; found = 0; for (c = cmdtab; p = c->c_name; c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return (c); if (!*q) { /* the name was a prefix */ if (q - name > longest) { longest = q - name; nmatches = 1; found = c; } else if (q - name == longest) nmatches++; } } if (nmatches > 1) return ((struct cmd *)-1); return (found); } /* getcmd */ /* * Slice a string up into argc/argv. */ void makeargv(void) { char **argp; margc = 0; argp = margv; stringbase = line; /* scan from first of buffer */ argbase = argbuf; /* store from first of buffer */ slrflag = 0; while (*argp++ = slurpstring()) margc++; } /* makeargv */ /* * Parse string into argbuf; * implemented with FSM to * handle quoting and strings */ char *slurpstring(void) { int got_one = 0; register char *sb = stringbase; register char *ap = argbase; char *tmp = argbase; /* will return this if token found */ if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ switch (slrflag) { /* and $ as token for macro invoke */ case 0: slrflag++; stringbase++; return ((*sb == '!') ? "!" : "$"); /* NOTREACHED */ case 1: slrflag++; altarg = stringbase; break; default: break; } } S0: switch (*sb) { case '\0': goto OUT; case ' ': case '\t': case '\n': case '=': sb++; goto S0; default: switch (slrflag) { case 0: slrflag++; break; case 1: slrflag++; altarg = sb; break; default: break; } goto S1; } S1: switch (*sb) { case ' ': case '\t': case '\n': case '=': case '\0': goto OUT; /* end of token */ case '\\': sb++; goto S2; /* slurp next character */ case '"': sb++; goto S3; /* slurp quoted string */ default: *ap++ = *sb++; /* add character to token */ got_one = 1; goto S1; } S2: switch (*sb) { case '\0': goto OUT; default: *ap++ = *sb++; got_one = 1; goto S1; } S3: switch (*sb) { case '\0': goto OUT; case '"': sb++; goto S1; default: *ap++ = *sb++; got_one = 1; goto S3; } OUT: if (got_one) *ap++ = '\0'; argbase = ap; /* update storage pointer */ stringbase = sb; /* update scan pointer */ if (got_one) { return(tmp); } switch (slrflag) { case 0: slrflag++; break; case 1: slrflag++; altarg = (char *) 0; break; default: break; } return((char *)0); } /* slurpstring */ #define HELPINDENT (sizeof ("directory")) /* * Help command. * Call each command handler with argc == 0 and argv[0] == name. */ help(int argc, char **argv) { register struct cmd *c; int i, showall = 0; char *arg; if (argc == 2) showall = strcmp(argv[1], "all") == 0; if (argc == 1 || showall) { (void) printf("Commands may be abbreviated. 'help all' shows aliases,\ninvisible and unsupported commands. 'help <command>' \ngives a brief description of <command>. Commands are:\n"); for (c = cmdtab, i=0; c->c_name != NULL; c++) { if (c->c_hidden && !showall) continue; (void) printf("%-13s", c->c_name); if (++i == 6) { i = 0; putchar('\n'); } } if (i < 6) putchar('\n'); } else while (--argc > 0) { arg = *++argv; c = getcmd(arg); if (c == (struct cmd *)-1) (void) printf("?Ambiguous help command %s\n", arg); else if (c == (struct cmd *)0) (void) printf("?Invalid help command %s\n", arg); else (void) printf("%-*s\t%s\n", HELPINDENT, c->c_name, c->c_help); } } /* help */ /* * If the user wants to, s/he can specify the maximum size of the log * file, so it doesn't waste too much disk space. If the log is too * fat, trim the older lines (at the top) until we're under the limit. */ void trim_log(void) { FILE *new, *old; struct stat st; long fat; string tmplogname, str; if (logsize <= 0 || *logfname == 0 || stat(logfname, &st) || (old = fopen(logfname, "r")) == NULL) return; /* never trim, or no log */ fat = st.st_size - logsize; if (fat <= 0L) return; /* log too small yet */ while (fat > 0L) { if (FGets(str, old) == NULL) return; fat -= (long) strlen(str); } /* skip lines until a new site was opened */ while (1) { if (FGets(str, old) == NULL) { (void) fclose(old); (void) unlink(logfname); return; /* nothing left, start anew */ } if (*str != '\t') break; } /* copy the remaining lines in "old" to "new" */ (void) Strncpy(tmplogname, logfname); tmplogname[strlen(tmplogname) - 1] = 'T'; if ((new = fopen(tmplogname, "w")) == NULL) { (void) Perror(tmplogname); return; } (void) fputs(str, new); while (FGets(str, old)) (void) fputs(str, new); (void) fclose(old); (void) fclose(new); if (unlink(logfname) < 0) Perror(logfname); if (rename(tmplogname, logfname) < 0) Perror(tmplogname); } /* trim_log */ int CheckNewMail(void) { struct stat stbuf; if (*mail_path == '\0') return 0; if (stat(mail_path, &stbuf) < 0) { /* cant find mail_path so we'll */ *mail_path = '\0'; /* never check it again */ return 0; } if (stbuf.st_mtime > mbox_time) { newmail++; (void) printf("%s\n", NEWMAILMESSAGE); (void) time(&mbox_time); /* only notify once. */ } return newmail; } /* CheckNewMail */ #ifdef TCAP termcap_init() { static char area[1024]; static char *s = area; char *getenv(), *tgetstr(); char *term; if (tgetent(tcbuf,(term = getenv("TERM"))) != 1) { (void) fprintf(stderr,"Can't get termcap entry for terminal [%s]\n", term); return (0); } if (!(tcap_normal = tgetstr("se", &s))) tcap_normal = ""; if (!(tcap_boldface = tgetstr("md", &s))) tcap_boldface = ""; if (!(tcap_underline = tgetstr("us", &s))) tcap_underline = ""; if (!(tcap_reverse = tgetstr("so", &s))) tcap_reverse = ""; } static int c_output(c) int c; { putchar(c); } tcap_put(cap) char *cap; { tputs(cap, 0, c_output); } #endif /* TCAP */ /* eof main.c */