/* main.c */

/*  $RCSfile: main.c,v $
*  $Revision: 14020.15 $
*  $Date: 93/07/09 11:50:12 $
*/

#define _main_c_

#define FTP_VERSION "1.7.3 (April 13, 1994)"

/* #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 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 */

#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 */
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).
                                                                                */

#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

char                            *tcap_normal = "\033[0m";       /* Default ANSI escapes */
char                            *tcap_boldface = "\033[1m";
char                            *tcap_underline = "\033[4m";
char                            *tcap_reverse = "\033[7m";

#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;
extern int                      options, cpend, data, connected, logged_in;
extern int                      curtype, macnum, remote_is_unix;
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;

       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;
       (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: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"  */

       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;
               (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 */



/*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)
                       cp = getenv("mail");
               if (cp == NULL)
                       (void) sprintf(str, "/usr/spool/mail/%s", uinfo.username);
               else
                       (void) Strncpy(str, cp);
               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 != 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.
*/
void cmdscanner(int top)
{
       register struct cmd *c;

       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;
               }
               if ((*c->c_handler)(margc, margv) == USAGE)
                       cmd_usage(c);
               if (c->c_handler != help)
                       break;
       }
       (void) Signal(SIGINT, intr);
       (void) Signal(SIGPIPE, lostpeer);
}       /* 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;

       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 */




void tcap_put(char *cap)
{
       tputs(cap, 0, c_output);
}       /* tcap_put */

#endif /* CURSES */

/* eof main.c */