freebsd-skq/usr.bin/ncftp/main.c
1995-12-10 19:54:49 +00:00

1175 lines
26 KiB
C

/* main.c */
#define _main_c_
#define FTP_VERSION "1.9.5 (October 29, 1995)"
/* #define BETA 1 */ /* If defined, it prints a little warning message. */
#include "sys.h"
#include <sys/stat.h>
#include <arpa/ftp.h>
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <netdb.h>
#include <pwd.h>
#ifdef __FreeBSD__
#include <locale.h>
#endif
#ifdef SYSLOG
# include <syslog.h>
#endif
#if defined(CURSES) && !defined(NO_CURSES_H)
# undef HZ /* Collides with HaZeltine ! */
# include <curses.h>
# ifdef TERMH
# include <term.h>
# endif
#endif /* CURSES */
#if defined(CURSES) && defined(SGTTYB)
# include <sgtty.h>
#endif
#include "util.h"
#include "cmds.h"
#include "main.h"
#include "ftp.h"
#include "ftprc.h"
#include "open.h"
#include "set.h"
#include "defaults.h"
#include "copyright.h"
/* 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 */
static char pad2a[8] = "Pad 2a"; /* SunOS overwrites jmp_bufs... */
jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
static char pad2b[8] = "Pad 2b";
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).
*/
#ifdef HPUX
char *tcap_normal = "\033&d@"; /* Default ANSI escapes */
char *tcap_boldface = "\033&dH"; /* Half Bright */
char *tcap_underline = "\033&dD";
char *tcap_reverse = "\033&dB";
#else
#ifdef NO_FORMATTING
char *tcap_normal = "";
char *tcap_boldface = "";
char *tcap_underline = "";
char *tcap_reverse = "";
#else
char *tcap_normal = "\033[0m"; /* Default ANSI escapes */
char *tcap_boldface = "\033[1m";
char *tcap_underline = "\033[4m";
char *tcap_reverse = "\033[7m";
#endif
#endif
size_t tcl_normal = 4, /* lengths of the above strings. */
tcl_bold = 4,
tcl_uline = 4,
tcl_rev = 4;
#ifdef CURSES
static char tcbuf[2048];
#endif
/* main.c externs */
extern int debug, verbose, mprompt, passivemode;
extern int options, cpend, data, connected, logged_in;
extern int curtype, macnum, remote_is_unix;
extern int restricted_data_ports;
extern FILE *cout;
extern struct cmd cmdtab[];
extern str32 curtypename;
extern char *macbuf;
extern char *reply_string;
extern char *short_verbose_msgs[4];
extern string vstr;
extern Hostname hostname;
extern longstring cwd, lcwd, recent_file;
extern int Optind;
extern char *Optarg;
#ifdef GATEWAY
extern string gate_login;
#endif
void main(int argc, char **argv)
{
register char *cp;
int top, opt, openopts = 0;
string tmp, oline;
struct servent *sptr;
#ifdef __FreeBSD__
(void) setlocale(LC_TIME, "");
#endif
if ((cp = rindex(argv[0], '/'))) cp++;
else cp = argv[0];
(void) Strncpy(progname, cp);
sptr = getservbyname("ftp", "tcp");
if (sptr == 0) fatal("ftp/tcp: unknown service");
serv = *sptr;
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;
passivemode = dPASSIVE;
restricted_data_ports = dRESTRICT;
(void) Strncpy(vstr, short_verbose_msgs[verbose+1]);
(void) Strncpy(curtypename, dTYPESTR);
curtype = dTYPE;
(void) Strncpy(prompt, dPROMPT);
#ifdef GATEWAY
(void) Strncpy(gate_login, dGATEWAY_LOGIN);
#endif
#ifdef SOCKS
SOCKSinit("ncftp");
#endif
/* Setup our pager variable, before we run through the rc,
which may change it. */
set_pager(getenv("PAGER"), 0);
#ifdef CURSES
ansi_escapes = 1;
termcap_init();
#else
ansi_escapes = 0;
if ((cp = getenv("TERM")) != NULL) {
if ((*cp == 'v' && cp[1] == 't') /* vt100, vt102, ... */
|| (strcmp(cp, "xterm") == 0))
ansi_escapes = 1;
}
#endif
(void) getuserinfo();
/* Init the mailbox checking code. */
(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) Strncpy(logfname, dLOGNAME);
(void) LocalDotPath(logfname);
#else
*logfname = 0;
#endif
(void) Strncpy(recent_file, dRECENTF);
(void) LocalDotPath(recent_file);
(void) get_cwd(lcwd, (int) sizeof(lcwd));
#ifdef SYSLOG
# ifdef LOG_LOCAL3
openlog ("NcFTP", LOG_PID, LOG_LOCAL3);
# else
openlog ("NcFTP", LOG_PID);
# endif
#endif /* SYSLOG */
ignore_rc = 0;
(void) strcpy(oline, "open ");
while ((opt = Getopt(argc, argv, "D:V:INPURHaicmup: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 'P':
passivemode = !passivemode;
break;
case 'U':
restricted_data_ports = !restricted_data_ports;
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\
-P : Toggle passive mode ftp (for use behind firewalls).\n\
-U : Toggle restricted data ports (for use behind firewalls).\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" */
fromatty = doing_script = isatty(0);
toatty = isatty(1);
(void) UserLoggedIn(); /* Init parent-death detection. */
cpend = 0; /* no pending replies */
if (*logfname)
logf = fopen (logfname, "a");
/* 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;
if (cmdOpen(margc, margv) != NOERR) {
exit(1);
}
}
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 (;;) {
if (cmdscanner(top) && !fromatty)
exit(1);
top = 1;
}
} /* main */
/*ARGSUSED*/
void intr SIG_PARAMS
{
dbprintf("intr()\n");
(void) Signal(SIGINT, intr);
(void) longjmp(toplevel, 1);
} /* intr */
int getuserinfo(void)
{
register char *cp;
struct passwd *pw;
string str;
extern char *home; /* for glob.c */
home = uinfo.homedir; /* for glob.c */
pw = NULL;
#ifdef USE_GETPWUID
/* Try to use getpwuid(), but if we have to, fall back to getpwnam(). */
pw = getpwuid(getuid());
if (pw == NULL) {
/* Oh well, try getpwnam() then. */
cp = getlogin();
if (cp == NULL) {
cp = getenv("LOGNAME");
if (cp == NULL)
cp = getenv("USER");
}
if (cp != NULL)
pw = getpwnam(cp);
}
#else
/* Try to use getpwnam(), but if we have to, fall back to getpwuid(). */
cp = getlogin();
if (cp == NULL) {
cp = getenv("LOGNAME");
if (cp == NULL)
cp = getenv("USER");
}
if (cp != NULL)
pw = getpwnam(cp);
if (pw == NULL) {
/* Oh well, try getpwuid() then. */
pw = getpwuid(getuid());
}
#endif
if (pw != NULL) {
uinfo.uid = pw->pw_uid;
(void) Strncpy(uinfo.username, pw->pw_name);
(void) Strncpy(uinfo.shell, pw->pw_shell);
if ((cp = getenv("HOME")) != NULL)
(void) Strncpy(uinfo.homedir, cp);
else
(void) Strncpy(uinfo.homedir, pw->pw_dir);
cp = getenv("MAIL");
if (cp != NULL) {
(void) Strncpy(str, cp);
} else {
/* Check a few typical mail directories.
* If we don't find it, too bad. Checking for new mail
* isn't very important anyway.
*/
(void) sprintf(str, "/usr/spool/mail/%s", uinfo.username);
if (access(str, 0) < 0) {
(void) sprintf(str, "/var/mail/%s", uinfo.username);
}
}
cp = str;
if (access(cp, 0) < 0) {
mail_path[0] = 0;
} else {
/*
* mbox variable may be like MAIL=(28 /usr/mail/me /usr/mail/you),
* so try to find the first mail path.
*/
while ((*cp != '/') && (*cp != 0))
cp++;
(void) Strncpy(mail_path, cp);
if ((cp = index(mail_path, ' ')) != NULL)
*cp = '\0';
}
return (0);
} else {
PERROR("getuserinfo", "Could not get your passwd entry!");
(void) Strncpy(uinfo.shell, "/bin/sh");
(void) Strncpy(uinfo.homedir, "."); /* current directory */
uinfo.uid = 999;
if ((cp = getenv("HOME")) != NULL)
(void) Strncpy(uinfo.homedir, cp);
mail_path[0] = 0;
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 */
#ifndef BUFSIZ
#define BUFSIZ 512
#endif
void init_transfer_buffer(void)
{
extern char *xferbuf;
extern size_t xferbufsize;
/* Make sure we use a multiple of BUFSIZ for efficiency. */
xferbufsize = (MAX_XFER_BUFSIZE / BUFSIZ) * BUFSIZ;
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 SIG_PARAMS
{
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;
logged_in = macnum = 0;
} /* lostpeer */
/*
* Command parser.
*/
int cmdscanner(int top)
{
register struct cmd *c;
int cmd_status, rcode = 0;
if (!top)
(void) putchar('\n');
for (;;) {
if (!doing_script && !UserLoggedIn())
(void) quit(0, NULL);
if (Gets(strprompt(), line, (size_t)CMDLINELEN) == NULL) {
(void) quit(0, NULL); /* control-d */
}
eventnumber++;
dbprintf("\"%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;
}
cmd_status = (*c->c_handler)(margc, margv);
if (cmd_status == USAGE)
cmd_usage(c);
else if (cmd_status == CMDERR)
rcode = 1;
if (c->c_handler != help)
break;
}
(void) Signal(SIGINT, intr);
(void) Signal(SIGPIPE, lostpeer);
return rcode;
} /* cmdscanner */
char *strprompt(void)
{
time_t tyme;
char eventstr[8];
char *dname, *lastlinestart;
register char *p, *q;
string str;
int flag;
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;
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 */
/*
* 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;
}
}
if ((cp = getenv("COLUMNS")) == NULL)
screenColumns = 80;
else
screenColumns = atoi(cp);
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;
#ifdef SGTTYB
struct sgttyb ttyb;
extern short ospeed;
#endif
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_boldface, "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);
#ifdef SGTTYB
if (ioctl(fileno(stdout), TIOCGETP, &ttyb) == 0)
ospeed = ttyb.sg_ospeed;
#endif
}
} /* termcap_init */
static int c_output(int c)
{
return (putchar(c));
} /* c_output */
void tcap_put(char *cap)
{
tputs(cap, 0, c_output);
} /* tcap_put */
#endif /* CURSES */
/* eof main.c */