/* main.c globals */
int slrflag;
int fromatty; /* input is from a terminal */
int toatty; /* output is to a terminal */
int doing_script; /* is a file being <redirected to me? */
char *altarg; /* argv[1] with no shell-like preprocessing */
struct servent serv; /* 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 startup_msg = 1; /* TAR: display message on startup? */
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 */
longstring 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 */
time_t mbox_time; /* last modified time of mbox */
size_t epromptlen; /* length of the last line of the
* prompt as it will appear on screen,
* (i.e. no invis escape codes).
*/
if (init_arrays()) /* Reserve large blocks of memory now */
fatal("could not reserve large amounts of memory.");
#ifdef GZCAT
if ((GZCAT == (char *)1) || (GZCAT == (char *)0)) {
(void) fprintf(stderr,
"You compiled the program with -DGZCAT, but you must specify the path with it!\n\
Re-compile, this time with -DGZCAT=\\\"/path/to/gzcat\\\".\n");
exit(1);
}
#endif
#ifdef ZCAT
if ((ZCAT == (char *)1) || (ZCAT == (char *)0)) {
(void) fprintf(stderr,
"You compiled the program with -DZCAT, but you must specify the path with it!\n\
Re-compile, this time with -DZCAT=\\\"/path/to/zcat\\\".\n");
exit(1);
}
#endif
/*
* Set up defaults for FTP.
*/
mprompt = dMPROMPT;
debug = dDEBUG;
verbose = dVERBOSE;
(void) Strncpy(vstr, short_verbose_msgs[verbose+1]);
ignore_rc = 0;
(void) strcpy(oline, "open ");
while ((opt = Getopt(argc, argv, "D:V:INRHaicmup:rd:g:")) >= 0) {
switch(opt) {
case 'a':
case 'c':
case 'i':
case 'm':
case 'u':
case 'r':
(void) sprintf(tmp, "-%c ", opt);
goto cattmp;
case 'p':
case 'd':
case 'g':
(void) sprintf(tmp, "-%c %s ", opt, Optarg);
cattmp:
(void) strcat(oline, tmp);
openopts++;
break;
case 'D':
debug = atoi(Optarg);
break;
case 'V':
set_verbose(Optarg, 0);
break;
case 'I':
mprompt = !mprompt;
break;
case 'N':
++ignore_rc;
break;
case 'H':
(void) show_version(0, NULL);
exit (0);
default:
usage:
(void) fprintf(stderr, "Usage: %s [program options] [[open options] site.to.open[:path]]\n\
Program Options:\n\
-D x : Set debugging level to x (a number).\n\
-H : Show version and compilation information.\n\
-I : Toggle interactive (mprompt) mode.\n\
-N : Toggle reading of the .netrc/.ncftprc.\n\
-V x : Set verbosity to level x (-1,0,1,2).\n\
Open Options:\n\
-a : Open anonymously (this is the default).\n\
-u : Open, specify user/password.\n\
-i : Ignore machine entry in your .netrc.\n\
-p N : Use port #N for connection.\n\
-r : \"Redial\" until connected.\n\
-d N : Redial, pausing N seconds between tries.\n\
-g N : Redial, giving up after N tries.\n\
:path : ``Colon-mode:'' If \"path\" is a file, it opens site, retrieves\n\
file \"path,\" then exits; if \"path\" is a remote directory,\n\
it opens site then starts you in that directory..\n\
-c : If you're using colon-mode with a file path, this will cat the\n\
file to stdout instead of storing on disk.\n\
-m : Just like -c, only it pipes the file to your $PAGER.\n\
Examples:\n\
ncftp ftp.unl.edu:/pub/README (just fetches README then quits)\n\
ncftp (just enters ncftp command shell)\n\
ncftp -V -u ftp.unl.edu\n\
ncftp -c ftp.unl.edu:/pub/README (cats README to stdout then quits)\n\
ncftp -D -r -d 120 -g 10 ftp.unl.edu\n", progname);
exit(1);
}
}
cp = argv[Optind]; /* the site to open. */
if (cp == NULL) {
if (openopts)
goto usage;
} else
(void) strcat(oline, cp);
if (ignore_rc <= 0)
(void) thrash_rc();
if (ignore_rc <= 1)
ReadRecentSitesFile();
(void) fix_options(); /* adjust "options" according to "debug" */
/* The user specified a host, maybe in 'colon-mode', 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, oline);
makeargv();
/* setpeer uses this to tell if it was called from the cmd-line. */
eventnumber = 0L;
(void) cmdOpen(margc, margv);
}
eventnumber = 1L;
(void) init_prompt();
if (startup_msg) { /* TAR */
if (ansi_escapes) {
#ifdef BETA
# define BETA_MSG "\n\
For testing purposes only. Do not re-distribute or subject to novice users."
#else
# define BETA_MSG ""
#endif
#ifndef CURSES
(void) printf("%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n",
tcap_boldface,
FTP_VERSION,
tcap_normal,
tcap_reverse,
BETA_MSG,
tcap_normal
);
#else
char vis[256];
(void) sprintf(vis, "%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n",
tcap_boldface,
FTP_VERSION,
tcap_normal,
tcap_reverse,
BETA_MSG,
tcap_normal
);
tcap_put(vis);
#endif /* !CURSES */
}
else
(void) printf("%s%s\n", FTP_VERSION, BETA_MSG);
} /* TAR */
if (NOT_VQUIET)
PrintTip();
top = setjmp(toplevel) == 0;
if (top) {
(void) Signal(SIGINT, intr);
(void) Signal(SIGPIPE, lostpeer);
}
for (;;) {
(void) cmdscanner(top);
top = 1;
}
} /* main */
if (at_flags == 0 && percent_flags == 0) {
epromptlen = strlen(prompt);
return (prompt); /* But don't overwrite it! */
}
epromptlen = 0;
lastlinestart = prompt2;
if (at_flags) {
for (p = prompt, q = prompt2, *q = 0; (*p); p++) {
if (*p == '@') switch (flag = *++p) {
case '\0':
--p;
break;
case 'M':
if (CheckNewMail() > 0)
q = Strpcpy(q, "(Mail) ");
break;
case 'N':
q = Strpcpy(q, "\n");
lastlinestart = q;
epromptlen = 0;
break;
case 'P': /* reset to no bold, no uline, no inverse, etc. */
if (ansi_escapes) {
q = Strpcpy(q, tcap_normal);
epromptlen += tcl_normal;
}
break;
case 'B': /* toggle boldface */
if (ansi_escapes) {
q = Strpcpy(q, tcap_boldface);
epromptlen += tcl_bold;
}
break;
case 'U': /* toggle underline */
if (ansi_escapes) {
q = Strpcpy(q, tcap_underline);
epromptlen += tcl_uline;
}
break;
case 'R':
case 'I': /* toggle inverse (reverse) video */
if (ansi_escapes) {
q = Strpcpy(q, tcap_reverse);
epromptlen += tcl_rev;
}
break;
case 'D': /* insert current directory */
case 'J':
if ((flag == 'J') && (remote_is_unix)) {
/* Not the whole path, just the dir name. */
dname = rindex(cwd, '/');
if (dname == NULL)
dname = cwd;
else if ((dname != cwd) && (dname[1]))
++dname;
} else
dname = cwd;
if (dname[0]) {
q = Strpcpy(q, dname);
q = Strpcpy(q, " ");
}
break;
case 'H': /* insert name of connected host */
if (logged_in) {
(void) sprintf(str, "%s ", hostname);
q = Strpcpy(q, str);
}
break;
case 'C': /* Insert host:path (colon-mode format. */
if (logged_in) {
(void) sprintf(str, "%s:%s ", hostname, cwd);
q = Strpcpy(q, str);
} else
q = Strpcpy(q, "(not connected)");
break;
case 'c':
if (logged_in) {
(void) sprintf(str, "%s:%s\n", hostname, cwd);
q = Strpcpy(q, str);
lastlinestart = q; /* there is a \n at the end. */
epromptlen = 0;
}
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);
#ifndef NO_STRFTIME
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));
}
#endif
epromptlen = (size_t) ((long) strlen(lastlinestart) - (long) epromptlen);
return (prompt2);
} /* strprompt */
/*
* 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()) != 0)
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;
/*
* Help command.
* Call each command handler with argc == 0 and argv[0] == name.
*/
int
help(int argc, char **argv)
{
register struct cmd *c;
int showall = 0, helpall = 0;
char *arg;
int i, j, k;
int nRows, nCols;
int nCmds2Print;
int screenColumns;
int len, widestName;
char *cp, **cmdnames, spec[16];
if (argc == 2) {
showall = (strcmp(argv[1], "showall") == 0);
helpall = (strcmp(argv[1], "helpall") == 0);
}
if (argc == 1 || showall) {
(void) printf("\
Commands may be abbreviated. 'help showall' shows aliases, invisible and\n\
unsupported commands. 'help <command>' gives a brief description of <command>.\n\n");
for (c = cmdtab, nCmds2Print=0; c->c_name != NULL; c++)
if (!c->c_hidden || showall)
nCmds2Print++;
if ((cmdnames = (char **) malloc(sizeof(char *) * nCmds2Print)) == NULL)
fatal("out of memory!");
for (c = cmdtab, i=0, widestName=0; c->c_name != NULL; c++) {
if (!c->c_hidden || showall) {
cmdnames[i++] = c->c_name;
len = (int) strlen(c->c_name);
if (len > widestName)
widestName = len;
}
}
widestName += 2; /* leave room for white-space in between cols. */
nCols = screenColumns / widestName;
/* if ((screenColumns % widestName) > 0) nCols++; */
nRows = nCmds2Print / nCols;
if ((nCmds2Print % nCols) > 0)
nRows++;
(void) sprintf(spec, "%%-%ds", widestName);
for (i=0; i<nRows; i++) {
for (j=0; j<nCols; j++) {
k = nRows*j + i;
if (k < nCmds2Print)
(void) printf(spec, cmdnames[k]);
}
(void) printf("\n");
}
Free(cmdnames);
} else if (helpall) {
/* Really intended to debug the help strings. */
for (c = cmdtab; c->c_name != NULL; c++) {
cmd_help(c);
cmd_usage(c);
}
} 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 {
cmd_help(c);
cmd_usage(c);
}
}
return NOERR;
} /* 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("trim_log", 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("trim_log", logfname);
if (rename(tmplogname, logfname) < 0)
PERROR("trim_log", 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;
}
/*
* Check if the size is non-zero and the access time is less than
* the modify time -- this indicates unread mail.
*/
if ((stbuf.st_size != 0) && (stbuf.st_atime <= stbuf.st_mtime)) {
if (stbuf.st_mtime > mbox_time) {
(void) printf("%s\n", NEWMAILMESSAGE);
mbox_time = stbuf.st_mtime;
}
return 1;
}
return 0;
} /* CheckNewMail */
#ifdef CURSES
int termcap_get(char **dest, char *attr)
{
static char area[1024];
static char *s = area;
char *buf, *cp;
int i, result = -1;
int len = 0;
*dest = NULL;
while (*attr != '\0') {
buf = tgetstr(attr, &s);
if (buf != NULL && buf[0] != '\0') {
for (i = 0; (buf[i] <= '9') && (buf[i] >= '0'); )
i++;
/* Get rid of the terminal delays, like "$<2>". */
if ((cp = strstr(&(buf[i]), "$<")) != NULL)
*cp = 0;
if (*dest == NULL)
*dest = (char *)malloc(strlen(&(buf[i])) + 1);
else
*dest = (char *)realloc(*dest, len + strlen(&(buf[i])) + 1);
if (*dest == NULL)
break;
(void) strcpy(*dest + len, &(buf[i]));
len += strlen (&(buf[i]));
}
attr += 2;
}
if (*dest == NULL)
*dest = "";
else
result = 0;
return (result);
} /* termcap_get */
void termcap_init(void)
{
char *term;
if ((term = getenv("TERM")) == NULL) {
term = "dumb"; /* TAR */
ansi_escapes = 0;
}
if (tgetent(tcbuf,term) != 1) {
(void) fprintf(stderr,"Can't get termcap entry for terminal [%s]\n", term);
} else {
(void) termcap_get(&tcap_normal, "meuese");
if (termcap_get(&tcap_boldface, "md") < 0) {
/* Dim-mode is better than nothing... */
(void) termcap_get(&tcap_normal, "mh");
}
(void) termcap_get(&tcap_underline, "us");
(void) termcap_get(&tcap_reverse, "so");
tcl_normal = strlen(tcap_normal);
tcl_bold = strlen(tcap_boldface);
tcl_uline = strlen(tcap_underline);
tcl_rev = strlen(tcap_reverse);
}
} /* termcap_init */
static int c_output(int c)
{
return (putchar(c));
} /* c_output */