home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume39 / ncftp / part03 / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-25  |  22.7 KB  |  1,029 lines

  1. /* main.c */
  2.  
  3. /*  $RCSfile: main.c,v $
  4.  *  $Revision: 14020.15 $
  5.  *  $Date: 93/07/09 11:50:12 $
  6.  */
  7.  
  8. #define _main_c_
  9.  
  10. #define FTP_VERSION \
  11. "NcFTP 1.5.0 (Aug 22, 1993) by Mike Gleason, NCEMRSoft."
  12.  
  13. /* #define BETA 1 */ /* If defined, it prints a little warning message. */
  14.  
  15. #include "sys.h"
  16. #include <sys/types.h>
  17. #include <sys/param.h>
  18. #include <sys/socket.h>
  19. #include <sys/stat.h>
  20. #include <sys/time.h>
  21. #include <arpa/ftp.h>
  22. #include <setjmp.h>
  23. #include <signal.h>
  24. #include <string.h>
  25. #include <errno.h>
  26. #include <ctype.h>
  27. #include <netdb.h>
  28. #include <pwd.h>
  29.  
  30. #ifdef SYSLOG
  31. #    include <syslog.h>
  32. #endif
  33.  
  34. #ifndef NO_UNISTDH
  35. #    include <unistd.h>
  36. #endif
  37.  
  38. #ifdef CURSES
  39. #    undef HZ        /* Collides with HaZeltine ! */
  40. #    include <curses.h>
  41. #endif    /* CURSES */
  42.  
  43. #include "util.h"
  44. #include "cmds.h"
  45. #include "main.h"
  46. #include "ftp.h"
  47. #include "ftprc.h"
  48. #include "open.h"
  49. #include "set.h"
  50. #include "defaults.h"
  51. #include "copyright.h"
  52.  
  53. /* main.c globals */
  54. int                    slrflag;
  55. int                    fromatty;            /* input is from a terminal */
  56. int                    toatty;                /* output is to a terminal */
  57. int                    doing_script;        /* is a file being <redirected to me? */
  58. char                *altarg;            /* argv[1] with no shell-like preprocessing  */
  59. struct servent        serv;                /* service spec for tcp/ftp */
  60. jmp_buf                toplevel;            /* non-local goto stuff for cmd scanner */
  61. char                *line;                /* input line buffer */
  62. char                *stringbase;        /* current scan point in line buffer */
  63. char                *argbuf;            /* argument storage buffer */
  64. char                *argbase;            /* current storage point in arg buffer */
  65. int                    margc;                /* count of arguments on input line */
  66. char                *margv[20];            /* args parsed from input line */
  67. struct userinfo        uinfo;                /* a copy of their pwent really */
  68. int                    ansi_escapes;        /* for fancy graphics */
  69. int                             startup_msg = 1;        /* TAR: display message on startup? */
  70. int                    ignore_rc;            /* are we supposed to ignore the netrc */
  71. string                progname;            /* simple filename */
  72. string                prompt, prompt2;    /* shell prompt string */
  73. string                anon_password;        /* most likely your email address */
  74. string                pager;                /* program to browse text files */
  75. string                version = FTP_VERSION;
  76. long                eventnumber;        /* number of commands we've done */
  77. FILE                *logf = NULL;        /* log user activity */
  78. longstring            logfname;            /* name of the logfile */
  79. long                logsize = 4096L;    /* max log size. 0 == no limit */
  80. int                    percent_flags;        /* "%" in prompt string? */
  81. int                    at_flags;            /* "@" in prompt string? */
  82. string                 mail_path;            /* your mailbox */
  83. time_t                mbox_time;            /* last modified time of mbox */
  84. size_t                epromptlen;            /* length of the last line of the
  85.                                          * prompt as it will appear on screen,
  86.                                          * (i.e. no invis escape codes).
  87.                                          */
  88. char                *tcap_normal = "\033[0m";    /* Default ANSI escapes */
  89. char                *tcap_boldface = "\033[1m";
  90. char                *tcap_underline = "\033[4m";
  91. char                *tcap_reverse = "\033[7m";
  92. size_t                tcl_normal = 4,        /* lengths of the above strings. */
  93.                     tcl_bold = 4,
  94.                     tcl_uline = 4,
  95.                     tcl_rev = 4;
  96.  
  97. #ifdef CURSES
  98. static char            tcbuf[2048];
  99. #endif
  100.  
  101. /* main.c externs */
  102. extern int            debug, verbose, mprompt;
  103. extern int            options, cpend, data, connected, logged_in;
  104. extern int            curtype, macnum, remote_is_unix;
  105. extern FILE            *cout;
  106. extern struct cmd    cmdtab[];
  107. extern str32        curtypename;
  108. extern char            *macbuf;
  109. extern char            *reply_string;
  110. extern char            *short_verbose_msgs[4];
  111. extern string        vstr;
  112. extern Hostname        hostname;
  113. extern longstring    cwd, lcwd, recent_file;
  114. extern int            Optind;
  115. extern char            *Optarg;
  116. #ifdef GATEWAY
  117. extern string        gate_login;
  118. #endif
  119.  
  120. void main(int argc, char **argv)
  121. {
  122.     register char        *cp;
  123.     int                    top, opt, openopts = 0;
  124.     string                tmp, oline;
  125.     struct servent        *sptr;
  126.  
  127.     if ((cp = rindex(argv[0], '/'))) cp++;
  128.     else cp = argv[0];
  129.     (void) Strncpy(progname, cp);
  130.     
  131.     sptr = getservbyname("ftp", "tcp");
  132.     if (sptr == 0) fatal("ftp/tcp: unknown service");
  133.     serv = *sptr;
  134.  
  135.     if (init_arrays())            /* Reserve large blocks of memory now */
  136.         fatal("could not reserve large amounts of memory.");
  137.  
  138.     /*
  139.      * Set up defaults for FTP.
  140.      */
  141.     mprompt = dMPROMPT;
  142.     debug = dDEBUG;
  143.     verbose = dVERBOSE;
  144.     (void) Strncpy(vstr, short_verbose_msgs[verbose+1]);
  145.  
  146.     (void) Strncpy(curtypename, dTYPESTR);
  147.     curtype = dTYPE;
  148.     (void) Strncpy(prompt, dPROMPT);
  149. #ifdef GATEWAY
  150.     (void) Strncpy(gate_login, dGATEWAY_LOGIN);
  151. #endif
  152.  
  153. #ifdef SOCKS
  154.     SOCKSinit("ncftp");
  155. #endif
  156.     
  157.     /*    Setup our pager variable, before we run through the rc,
  158.         which may change it. */
  159.     set_pager(getenv("PAGER"), 0);
  160. #ifdef CURSES
  161.     ansi_escapes = 1;
  162.     termcap_init();
  163. #else
  164.     ansi_escapes = 0;
  165.     if ((cp = getenv("TERM")) != NULL) {
  166.         if ((*cp == 'v' && cp[1] == 't')        /* vt100, vt102, ... */
  167.             || (strcmp(cp, "xterm") == 0))
  168.             ansi_escapes = 1;
  169.     }
  170. #endif
  171.     (void) getuserinfo();
  172.  
  173.     /* Init the mailbox checking code. */
  174.     (void) time(&mbox_time);
  175.  
  176.     (void) Strncpy(anon_password, uinfo.username);
  177.     if (getlocalhostname(uinfo.hostname, sizeof(uinfo.hostname)) == 0) {
  178.         (void) Strncat(anon_password, "@");
  179.         (void) Strncat(anon_password, uinfo.hostname);
  180.     }
  181. #if dLOGGING
  182.     (void) Strncpy(logfname, dLOGNAME);
  183.     (void) LocalDotPath(logfname);
  184. #else
  185.     *logfname = 0;
  186. #endif
  187.     (void) Strncpy(recent_file, dRECENTF);
  188.     (void) LocalDotPath(recent_file);
  189.  
  190.     (void) get_cwd(lcwd, (int) sizeof(lcwd));
  191.  
  192. #ifdef SYSLOG
  193. #    ifdef LOG_LOCAL3
  194.     openlog ("NcFTP", LOG_PID, LOG_LOCAL3);
  195. #    else
  196.     openlog ("NcFTP", LOG_PID);
  197. #    endif
  198. #endif                /* SYSLOG */
  199.  
  200.  
  201.     ignore_rc = 0;
  202.     (void) strcpy(oline, "open ");
  203.     while ((opt = Getopt(argc, argv, "D:V:INRHaicmup:rd:g:")) >= 0) {
  204.         switch(opt) {
  205.             case 'a':
  206.             case 'c':
  207.             case 'i':
  208.             case 'm':
  209.             case 'u':
  210.             case 'r':
  211.                 (void) sprintf(tmp, "-%c ", opt);
  212.                 goto cattmp;
  213.  
  214.             case 'p':
  215.             case 'd':
  216.             case 'g':
  217.                 (void) sprintf(tmp, "-%c %s ", opt, Optarg);
  218.             cattmp:
  219.                 (void) strcat(oline, tmp);
  220.                 openopts++;
  221.                 break;
  222.  
  223.             case 'D':
  224.                 debug = atoi(Optarg);
  225.                 break;
  226.             
  227.             case 'V':
  228.                 set_verbose(Optarg, 0);
  229.                 break;
  230.  
  231.             case 'I':
  232.                 mprompt = !mprompt;
  233.                 break;
  234.  
  235.             case 'N':
  236.                 ++ignore_rc;
  237.                 break;
  238.  
  239.             case 'H':
  240.                 (void) show_version(0, NULL);
  241.                 exit (0);
  242.  
  243.             default:
  244.             usage:
  245.                 (void) fprintf(stderr, "Usage: %s [program options] [[open options] site.to.open[:path]]\n\
  246. Program Options:\n\
  247.     -D x   : Set debugging level to x (a number).\n\
  248.     -H     : Show version and compilation information.\n\
  249.     -I     : Toggle interactive (mprompt) mode.\n\
  250.     -N     : Toggle reading of the .netrc/.ncftprc.\n\
  251.     -V x   : Set verbosity to level x (-1,0,1,2).\n\
  252. Open Options:\n\
  253.     -a     : Open anonymously (this is the default).\n\
  254.     -u     : Open, specify user/password.\n\
  255.     -i     : Ignore machine entry in your .netrc.\n\
  256.     -p N   : Use port #N for connection.\n\
  257.     -r     : \"Redial\" until connected.\n\
  258.     -d N   : Redial, pausing N seconds between tries.\n\
  259.     -g N   : Redial, giving up after N tries.\n\
  260.     :path  : ``Colon-mode:'' If \"path\" is a file, it opens site, retrieves\n\
  261.              file \"path,\" then exits; if \"path\" is a remote directory,\n\
  262.              it opens site then starts you in that directory..\n\
  263.     -c     : If you're using colon-mode with a file path, this will cat the\n\
  264.              file to stdout instead of storing on disk.\n\
  265.     -m     : Just like -c, only it pipes the file to your $PAGER.\n\
  266. Examples:\n\
  267.     ncftp ftp.unl.edu:/pub/README (just fetches README then quits)\n\
  268.     ncftp  (just enters ncftp command shell)\n\
  269.     ncftp -V -u ftp.unl.edu\n\
  270.     ncftp -c ftp.unl.edu:/pub/README (cats README to stdout then quits)\n\
  271.     ncftp -D -r -d 120 -g 10 ftp.unl.edu\n", progname);
  272.             exit(1);
  273.         }
  274.     }
  275.  
  276.     cp = argv[Optind];  /* the site to open. */
  277.     if (cp == NULL) {
  278.         if (openopts)
  279.             goto usage;
  280.     } else
  281.         (void) strcat(oline, cp);
  282.  
  283.     if (ignore_rc <= 0)
  284.         (void) thrash_rc();
  285.     if (ignore_rc <= 1)
  286.         ReadRecentSitesFile();
  287.  
  288.     (void) fix_options();    /* adjust "options" according to "debug"  */
  289.     
  290.     fromatty = doing_script = isatty(0);
  291.     toatty = isatty(1);
  292.     (void) UserLoggedIn();    /* Init parent-death detection. */
  293.     cpend = 0;  /* no pending replies */
  294.     
  295.     if (*logfname)
  296.         logf = fopen (logfname, "a");
  297.  
  298.  
  299.     /* The user specified a host, maybe in 'colon-mode', on the command
  300.      * line.  Open it now...
  301.      */
  302.     if (argc > 1 && cp) {
  303.         if (setjmp(toplevel))
  304.             exit(0);
  305.         (void) Signal(SIGINT, intr);
  306.         (void) Signal(SIGPIPE, lostpeer);
  307.         (void) strcpy(line, oline);
  308.         makeargv();
  309.         /* setpeer uses this to tell if it was called from the cmd-line. */
  310.         eventnumber = 0L;
  311.         (void) cmdOpen(margc, margv);
  312.     }
  313.     eventnumber = 1L;
  314.  
  315.     (void) init_prompt();
  316.  
  317.     if (startup_msg) {  /* TAR */
  318.         if (ansi_escapes) {
  319. #ifdef BETA
  320. #    define BETA_MSG "\n\
  321. For testing purposes only.  Do not re-distribute or subject to novice users."
  322. #else
  323. #    define BETA_MSG ""
  324. #endif
  325.  
  326. #ifndef CURSES
  327.         (void) printf("%s%s%s%s\n", 
  328.                 tcap_boldface, FTP_VERSION, BETA_MSG, tcap_normal);
  329. #else
  330.         string vis;
  331.         (void) sprintf(vis, "%s%s%s%s\n", 
  332.                 tcap_boldface, FTP_VERSION, BETA_MSG, tcap_normal);
  333.         tcap_put(vis);
  334. #endif /* !CURSES */
  335.         }
  336.         else
  337.         (void) printf("%s%s\n", FTP_VERSION, BETA_MSG);
  338.     }  /* TAR */
  339.     if (NOT_VQUIET)
  340.         PrintTip();
  341.     top = setjmp(toplevel) == 0;
  342.     if (top) {
  343.         (void) Signal(SIGINT, intr);
  344.         (void) Signal(SIGPIPE, lostpeer);
  345.     }
  346.     for (;;) {
  347.         (void) cmdscanner(top);
  348.         top = 1;
  349.     }
  350. }    /* main */
  351.  
  352.  
  353.  
  354. /*ARGSUSED*/
  355. void intr SIG_PARAMS
  356. {
  357.     dbprintf("intr()\n");
  358.     (void) Signal(SIGINT, intr);
  359.     (void) longjmp(toplevel, 1);
  360. }    /* intr */
  361.  
  362.  
  363.  
  364. int getuserinfo(void)
  365. {
  366.     register char            *cp;
  367.     struct passwd            *pw;
  368.     string                    str;
  369.     extern char                *home;    /* for glob.c */
  370.     
  371.     pw = getpwuid(getuid());
  372.     if (pw != NULL) {
  373.         (void) Strncpy(uinfo.username, pw->pw_name);
  374.         (void) Strncpy(uinfo.shell, pw->pw_shell);
  375.         (void) Strncpy(uinfo.homedir, pw->pw_dir);
  376.         uinfo.uid = pw->pw_uid;
  377.         home = uinfo.homedir;    /* for glob.c */
  378.         cp = getenv("MAIL");
  379.         if (cp == NULL)
  380.             cp = getenv("mail");
  381.         if (cp == NULL)
  382.             (void) sprintf(str, "/usr/spool/mail/%s", uinfo.username);
  383.         else
  384.             (void) Strncpy(str, cp);
  385.         cp = str;
  386.  
  387.         /*
  388.          * mbox variable may be like MAIL=(28 /usr/mail/me /usr/mail/you),
  389.          * so try to find the first mail path.
  390.          */
  391.         while ((*cp != '/') && (*cp != 0))
  392.             cp++;
  393.         (void) Strncpy(mail_path, cp);
  394.         if ((cp = index(mail_path, ' ')) != NULL)
  395.             *cp = '\0';
  396.         return (0);
  397.     } else {
  398.         (void) Strncpy(uinfo.username, "unknown");
  399.         (void) Strncpy(uinfo.shell, "/bin/sh");
  400.         (void) Strncpy(uinfo.homedir, ".");    /* current directory */
  401.         uinfo.uid = 999;
  402.         return (-1);
  403.     }
  404. }    /* getuserinfo */
  405.  
  406.  
  407.  
  408.  
  409. int init_arrays(void)
  410. {
  411.     if ((macbuf = (char *) malloc((size_t)(MACBUFLEN))) == NULL)
  412.         goto barf;
  413.     if ((line = (char *) malloc((size_t)(CMDLINELEN))) == NULL)
  414.         goto barf;
  415.     if ((argbuf = (char *) malloc((size_t)(CMDLINELEN))) == NULL)
  416.         goto barf;
  417.     if ((reply_string = (char *) malloc((size_t)(RECEIVEDLINELEN))) == NULL)
  418.         goto barf;
  419.     
  420.     *macbuf = '\0';
  421.     init_transfer_buffer();
  422.     return (0);
  423. barf:
  424.     return (-1);
  425. }    /* init_arrays */
  426.  
  427.  
  428.  
  429. #ifndef BUFSIZ
  430. #define BUFSIZ 512
  431. #endif
  432.  
  433. void init_transfer_buffer(void)
  434. {
  435.     extern char *xferbuf;
  436.     extern size_t xferbufsize;
  437.     
  438.     /* Make sure we use a multiple of BUFSIZ for efficiency. */
  439.     xferbufsize = (MAX_XFER_BUFSIZE / BUFSIZ) * BUFSIZ;
  440.     while (1) {
  441.         xferbuf = (char *) malloc (xferbufsize);
  442.         if (xferbuf != NULL || xferbufsize < 1024)
  443.             break;
  444.         xferbufsize >>= 2;
  445.     }
  446.     
  447.     if (xferbuf != NULL) return;
  448.     fatal("out of memory for transfer buffer.");
  449. }    /* init_transfer_buffer */
  450.  
  451.  
  452.  
  453.  
  454. void init_prompt(void)
  455. {
  456.     register char *cp;
  457.     
  458.     percent_flags = at_flags = 0;
  459.     for (cp = prompt; *cp; cp++) {
  460.         if (*cp == '%') percent_flags = 1;
  461.         else if (*cp == '@') at_flags = 1;
  462.     }
  463. }    /* init_prompt */
  464.  
  465.  
  466.  
  467. /*ARGSUSED*/
  468. void lostpeer SIG_PARAMS
  469. {
  470.     if (connected) {
  471.         close_streams(1);
  472.         if (data >= 0) {
  473.             (void) shutdown(data, 1+1);
  474.             (void) close(data);
  475.             data = -1;
  476.         }
  477.         connected = 0;
  478.     }
  479.     if (connected) {
  480.         close_streams(1);
  481.         connected = 0;
  482.     }
  483.     hostname[0] = cwd[0] = 0;
  484.     logged_in = macnum = 0;
  485. }    /* lostpeer */
  486.  
  487.  
  488. /*
  489.  * Command parser.
  490.  */
  491. void cmdscanner(int top)
  492. {
  493.     register struct cmd *c;
  494. #ifdef CURSES
  495.     string vis, *vp;
  496. #endif
  497.  
  498.     if (!top)
  499.         (void) putchar('\n');
  500.     for (;;) {
  501.         if (!doing_script && !UserLoggedIn())
  502.             (void) quit(0, NULL);
  503.         if (Gets(strprompt(), line, (size_t)CMDLINELEN) == NULL) {
  504.             (void) quit(0, NULL);    /* control-d */
  505.         }
  506.         eventnumber++;
  507.         dbprintf("\"%s\"\n", line);
  508.         (void) makeargv();
  509.         if (margc == 0) {
  510.             continue;    /* blank line... */
  511.         }
  512.         c = getcmd(margv[0]);
  513.         if (c == (struct cmd *) -1) {
  514.             (void) printf("?Ambiguous command\n");
  515.             continue;
  516.         }
  517.         if (c == 0) {
  518.             if (!implicit_cd(margv[0]))
  519.                 (void) printf("?Invalid command\n");
  520.             continue;
  521.         }
  522.         if (c->c_conn && !connected) {
  523.             (void) printf ("Not connected.\n");
  524.             continue;
  525.         }
  526.         if ((*c->c_handler)(margc, margv) == USAGE)
  527.             cmd_usage(c);
  528.         if (c->c_handler != help)
  529.             break;
  530.     }
  531.     (void) Signal(SIGINT, intr);
  532.     (void) Signal(SIGPIPE, lostpeer);
  533. }    /* cmdscanner */
  534.  
  535.  
  536.  
  537.  
  538. char *strprompt(void)
  539. {
  540.     time_t                    tyme;
  541.     char                    eventstr[8];
  542.     char                    *dname, *lastlinestart;
  543.     register char            *p, *q;
  544.     string                    str;
  545.     int                        flag;
  546.  
  547.     if (at_flags == 0 && percent_flags == 0) {
  548.         epromptlen = strlen(prompt);
  549.         return (prompt);    /* But don't overwrite it! */
  550.     }
  551.     epromptlen = 0;
  552.     lastlinestart = prompt2;
  553.     if (at_flags) {
  554.         for (p = prompt, q = prompt2, *q = 0; (*p); p++) {
  555.             if (*p == '@') switch (flag = *++p) {
  556.                 case '\0':
  557.                     --p;
  558.                     break;
  559.                 case 'M':
  560.                     if (CheckNewMail() > 0)
  561.                         q = Strpcpy(q, "(Mail) ");
  562.                     break;
  563.                 case 'N':
  564.                     q = Strpcpy(q, "\n");
  565.                     lastlinestart = q;
  566.                     epromptlen = 0;
  567.                     break;
  568.                 case 'P':    /* reset to no bold, no uline, no inverse, etc. */
  569.                     if (ansi_escapes) {
  570.                         q = Strpcpy(q, tcap_normal);
  571.                         epromptlen += tcl_normal;
  572.                     }
  573.                     break;
  574.                 case 'B':    /* toggle boldface */
  575.                     if (ansi_escapes) {
  576.                         q = Strpcpy(q, tcap_boldface);
  577.                         epromptlen += tcl_bold;
  578.                     }
  579.                     break;
  580.                 case 'U':    /* toggle underline */
  581.                     if (ansi_escapes) {
  582.                         q = Strpcpy(q, tcap_underline);
  583.                         epromptlen += tcl_uline;
  584.                     }
  585.                     break;
  586.                 case 'R':
  587.                 case 'I':    /* toggle inverse (reverse) video */
  588.                     if (ansi_escapes) {
  589.                         q = Strpcpy(q, tcap_reverse);
  590.                         epromptlen += tcl_rev;
  591.                     }
  592.                     break;
  593.                 case 'D':    /* insert current directory */
  594.                 case 'J':
  595.                     if ((flag == 'J') && (remote_is_unix)) {
  596.                         /* Not the whole path, just the dir name. */
  597.                         dname = rindex(cwd, '/');
  598.                         if (dname == NULL)
  599.                             dname = cwd;
  600.                         else if ((dname != cwd) && (dname[1]))
  601.                             ++dname;
  602.                     } else
  603.                         dname = cwd;
  604.                     if (dname[0]) {
  605.                         q = Strpcpy(q, dname);
  606.                         q = Strpcpy(q, " ");
  607.                     }
  608.                     break;
  609.                 case 'H':    /* insert name of connected host */
  610.                     if (logged_in) {
  611.                         (void) sprintf(str, "%s ", hostname);
  612.                         q = Strpcpy(q, str);
  613.                     }
  614.                     break;
  615.                 case 'C':  /* Insert host:path (colon-mode format. */
  616.                     if (logged_in) {
  617.                         (void) sprintf(str, "%s:%s ", hostname, cwd);
  618.                         q = Strpcpy(q, str);
  619.                     } else
  620.                         q = Strpcpy(q, "(not connected)");
  621.                     break;
  622.                 case 'c':
  623.                     if (logged_in) {
  624.                         (void) sprintf(str, "%s:%s\n", hostname, cwd);
  625.                         q = Strpcpy(q, str);
  626.                         lastlinestart = q;    /* there is a \n at the end. */
  627.                         epromptlen = 0;
  628.                     }
  629.                     break;
  630.                 case '!':
  631.                 case 'E':    /* insert event number */
  632.                     (void) sprintf(eventstr, "%ld", eventnumber);
  633.                     q = Strpcpy(q, eventstr);
  634.                     break;
  635.                 default:
  636.                     *q++ = *p;    /* just copy it; unknown switch */
  637.             } else
  638.                 *q++ = *p;
  639.         }
  640.         *q = '\0';
  641.     } else 
  642.         (void) strcpy(prompt2, prompt);
  643.     
  644.     if (percent_flags) {
  645.         /*    only strftime if the user requested it (with a %something),
  646.             otherwise don't waste time doing nothing. */
  647.         (void) time(&tyme);
  648.         (void) Strncpy(str, prompt2);
  649.         (void) strftime(prompt2, sizeof(str), str, localtime(&tyme));
  650.     }
  651.     epromptlen = (size_t) ((long) strlen(lastlinestart) - (long) epromptlen);
  652.     return (prompt2);
  653. }    /* strprompt */
  654.  
  655.  
  656. /*
  657.  * Slice a string up into argc/argv.
  658.  */
  659.  
  660. void makeargv(void)
  661. {
  662.     char **argp;
  663.  
  664.     margc = 0;
  665.     argp = margv;
  666.     stringbase = line;        /* scan from first of buffer */
  667.     argbase = argbuf;        /* store from first of buffer */
  668.     slrflag = 0;
  669.     while (*argp++ = slurpstring())
  670.         margc++;
  671. }    /* makeargv */
  672.  
  673.  
  674.  
  675.  
  676. /*
  677.  * Parse string into argbuf;
  678.  * implemented with FSM to
  679.  * handle quoting and strings
  680.  */
  681. char *slurpstring(void)
  682. {
  683.     int got_one = 0;
  684.     register char *sb = stringbase;
  685.     register char *ap = argbase;
  686.     char *tmp = argbase;        /* will return this if token found */
  687.  
  688.     if (*sb == '!' || *sb == '$') {    /* recognize ! as a token for shell */
  689.         switch (slrflag) {    /* and $ as token for macro invoke */
  690.             case 0:
  691.                 slrflag++;
  692.                 stringbase++;
  693.                 return ((*sb == '!') ? "!" : "$");
  694.                 /* NOTREACHED */
  695.             case 1:
  696.                 slrflag++;
  697.                 altarg = stringbase;
  698.                 break;
  699.             default:
  700.                 break;
  701.         }
  702.     }
  703.  
  704. S0:
  705.     switch (*sb) {
  706.  
  707.     case '\0':
  708.         goto OUT;
  709.  
  710.     case ' ':
  711.     case '\t':
  712.     case '\n':
  713.     case '=':
  714.         sb++; goto S0;
  715.  
  716.     default:
  717.         switch (slrflag) {
  718.             case 0:
  719.                 slrflag++;
  720.                 break;
  721.             case 1:
  722.                 slrflag++;
  723.                 altarg = sb;
  724.                 break;
  725.             default:
  726.                 break;
  727.         }
  728.         goto S1;
  729.     }
  730.  
  731. S1:
  732.     switch (*sb) {
  733.  
  734.     case ' ':
  735.     case '\t':
  736.     case '\n':
  737.     case '=':
  738.     case '\0':
  739.         goto OUT;    /* end of token */
  740.  
  741.     case '\\':
  742.         sb++; goto S2;    /* slurp next character */
  743.  
  744.     case '"':
  745.         sb++; goto S3;    /* slurp quoted string */
  746.  
  747.     default:
  748.         *ap++ = *sb++;    /* add character to token */
  749.         got_one = 1;
  750.         goto S1;
  751.     }
  752.  
  753. S2:
  754.     switch (*sb) {
  755.  
  756.     case '\0':
  757.         goto OUT;
  758.  
  759.     default:
  760.         *ap++ = *sb++;
  761.         got_one = 1;
  762.         goto S1;
  763.     }
  764.  
  765. S3:
  766.     switch (*sb) {
  767.  
  768.     case '\0':
  769.         goto OUT;
  770.  
  771.     case '"':
  772.         sb++; goto S1;
  773.  
  774.     default:
  775.         *ap++ = *sb++;
  776.         got_one = 1;
  777.         goto S3;
  778.     }
  779.  
  780. OUT:
  781.     if (got_one)
  782.         *ap++ = '\0';
  783.     argbase = ap;            /* update storage pointer */
  784.     stringbase = sb;        /* update scan pointer */
  785.     if (got_one) {
  786.         return(tmp);
  787.     }
  788.     switch (slrflag) {
  789.         case 0:
  790.             slrflag++;
  791.             break;
  792.         case 1:
  793.             slrflag++;
  794.             altarg = (char *) 0;
  795.             break;
  796.         default:
  797.             break;
  798.     }
  799.     return((char *)0);
  800. }    /* slurpstring */
  801.  
  802. /*
  803.  * Help command.
  804.  * Call each command handler with argc == 0 and argv[0] == name.
  805.  */
  806. int
  807. help(int argc, char **argv)
  808. {
  809.     register struct cmd        *c;
  810.     int                        showall = 0, helpall = 0;
  811.     char                    *arg;
  812.     int                        i, j, k;
  813.     int                     nRows, nCols;
  814.     int                     nCmds2Print;
  815.     int                     screenColumns;
  816.     int                     len, widestName;
  817.     char                     *cp, **cmdnames, spec[16];
  818.  
  819.     if (argc == 2) {
  820.         showall = (strcmp(argv[1], "showall") == 0);
  821.         helpall = (strcmp(argv[1], "helpall") == 0);
  822.     }
  823.     if (argc == 1 || showall)  {
  824.         (void) printf("\
  825. Commands may be abbreviated.  'help showall' shows aliases, invisible and\n\
  826. unsupported commands.  'help <command>' gives a brief description of <command>.\n\n");
  827.  
  828.         for (c = cmdtab, nCmds2Print=0; c->c_name != NULL; c++) 
  829.             if (!c->c_hidden || showall)
  830.                 nCmds2Print++;
  831.  
  832.         if ((cmdnames = (char **) malloc(sizeof(char *) * nCmds2Print)) == NULL)
  833.             fatal("out of memory!");
  834.  
  835.         for (c = cmdtab, i=0, widestName=0; c->c_name != NULL; c++) {
  836.             if (!c->c_hidden || showall) {
  837.                 cmdnames[i++] = c->c_name;
  838.                 len = (int) strlen(c->c_name);
  839.                 if (len > widestName)
  840.                     widestName = len;
  841.             }
  842.         }
  843.  
  844.         if ((cp = getenv("COLUMNS")) == NULL)
  845.             screenColumns = 80;
  846.         else
  847.             screenColumns = atoi(cp);
  848.  
  849.         widestName += 2;    /* leave room for white-space in between cols. */
  850.         nCols = screenColumns / widestName;
  851.         /* if ((screenColumns % widestName) > 0) nCols++; */
  852.         nRows = nCmds2Print / nCols;
  853.         if ((nCmds2Print % nCols) > 0)
  854.             nRows++;
  855.  
  856.         (void) sprintf(spec, "%%-%ds", widestName);
  857.         for (i=0; i<nRows; i++) {
  858.             for (j=0; j<nCols; j++) {
  859.                 k = nRows*j + i;
  860.                 if (k < nCmds2Print)
  861.                     (void) printf(spec, cmdnames[k]);
  862.             }
  863.             (void) printf("\n");
  864.         }
  865.         free(cmdnames);
  866.     } else if (helpall) {
  867.         /* Really intended to debug the help strings. */
  868.         for (c = cmdtab; c->c_name != NULL; c++) {
  869.             cmd_help(c);
  870.             cmd_usage(c);
  871.         }
  872.     } else while (--argc > 0) {
  873.         arg = *++argv;
  874.         c = getcmd(arg);
  875.         if (c == (struct cmd *)-1)
  876.             (void) printf("?Ambiguous help command %s\n", arg);
  877.         else if (c == (struct cmd *)0)
  878.             (void) printf("?Invalid help command %s\n", arg);
  879.         else {
  880.             cmd_help(c);
  881.             cmd_usage(c);
  882.         }
  883.     }
  884.     return NOERR;
  885. }    /* help */
  886.  
  887.  
  888. /*
  889.  * If the user wants to, s/he can specify the maximum size of the log
  890.  * file, so it doesn't waste too much disk space.  If the log is too
  891.  * fat, trim the older lines (at the top) until we're under the limit.
  892.  */
  893. void trim_log(void)
  894. {
  895.     FILE                *new, *old;
  896.     struct stat            st;
  897.     long                fat;
  898.     string                tmplogname, str;
  899.  
  900.     if (logsize <= 0 || *logfname == 0 || stat(logfname, &st) ||
  901.         (old = fopen(logfname, "r")) == NULL)
  902.         return;    /* never trim, or no log */
  903.     fat = st.st_size - logsize;
  904.     if (fat <= 0L) return;    /* log too small yet */
  905.     while (fat > 0L) {
  906.         if (FGets(str, old) == NULL) return;
  907.         fat -= (long) strlen(str);
  908.     }
  909.     /* skip lines until a new site was opened */
  910.     while (1) {
  911.         if (FGets(str, old) == NULL) {
  912.             (void) fclose(old);
  913.             (void) unlink(logfname);
  914.             return;    /* nothing left, start anew */
  915.         }
  916.         if (*str != '\t') break;
  917.     }
  918.     
  919.     /* copy the remaining lines in "old" to "new" */
  920.     (void) Strncpy(tmplogname, logfname);
  921.     tmplogname[strlen(tmplogname) - 1] = 'T';
  922.     if ((new = fopen(tmplogname, "w")) == NULL) {
  923.         (void) PERROR("trim_log", tmplogname);
  924.         return;
  925.     }
  926.     (void) fputs(str, new);
  927.     while (FGets(str, old))
  928.         (void) fputs(str, new);
  929.     (void) fclose(old); (void) fclose(new);
  930.     if (unlink(logfname) < 0)
  931.         PERROR("trim_log", logfname);
  932.     if (rename(tmplogname, logfname) < 0)
  933.         PERROR("trim_log", tmplogname);
  934. }    /* trim_log */
  935.  
  936.  
  937.  
  938.  
  939. int CheckNewMail(void)
  940. {
  941.     struct stat stbuf;
  942.  
  943.     if (*mail_path == '\0') return 0;
  944.     if (stat(mail_path, &stbuf) < 0) {    /* cant find mail_path so we'll */
  945.         *mail_path = '\0';                /* never check it again */
  946.         return 0;
  947.     }
  948.  
  949.     /*
  950.      * Check if the size is non-zero and the access time is less than
  951.      * the modify time -- this indicates unread mail.
  952.      */
  953.     if ((stbuf.st_size != 0) && (stbuf.st_atime <= stbuf.st_mtime)) {
  954.         if (stbuf.st_mtime > mbox_time) {
  955.             (void) printf("%s\n", NEWMAILMESSAGE);
  956.             mbox_time = stbuf.st_mtime;
  957.         }
  958.         return 1;
  959.     }
  960.  
  961.     return 0;
  962. }    /* CheckNewMail */
  963.  
  964.  
  965.  
  966. #ifdef CURSES
  967. void termcap_get(char *dest, char *attr)
  968. {
  969.     static char area[1024];
  970.     static char *s = area;
  971.     char buf[32];
  972.     int foo;
  973.  
  974.     (void) Strncpy(buf, tgetstr(attr, &s));
  975.     if (buf[0]) {
  976.          for (foo = 0; (buf[foo] <= '9') && (buf[foo] >= '0'); foo++); 
  977.          if ((dest = (char *)malloc(strlen(&(buf[foo])) + 1)) == NULL) 
  978.              *dest = 0;
  979.          else 
  980.              (void) strcpy(dest, &(buf[foo]));
  981.      } else 
  982.          *dest = 0;
  983. }    /* termcap_get */
  984.  
  985.  
  986.   
  987. void termcap_init(void)
  988. {
  989.     static char area[1024];
  990.     static char *s = area;
  991.     char *term;
  992.  
  993.     if ((term = getenv("TERM")) == NULL) {
  994.         term = "dumb";  /* TAR */
  995.         ansi_escapes = 0;
  996.     }
  997.     if (tgetent(tcbuf,term) != 1) {
  998.         (void) fprintf(stderr,"Can't get termcap entry for terminal [%s]\n", term);
  999.     } else {
  1000.         termcap_get(tcap_normal, "me");
  1001.         termcap_get(tcap_boldface, "md");
  1002.         termcap_get(tcap_underline, "us");
  1003.         termcap_get(tcap_reverse, "so");
  1004.         tcl_normal = strlen(tcap_normal);
  1005.         tcl_bold = strlen(tcap_boldface);
  1006.         tcl_uline = strlen(tcap_underline);
  1007.         tcl_rev = strlen(tcap_reverse);
  1008.     }
  1009. }    /* termcap_init */
  1010.  
  1011.  
  1012.  
  1013. static int c_output(int c)
  1014. {
  1015.     putchar(c);
  1016. }    /* c_output */
  1017.  
  1018.  
  1019.  
  1020.  
  1021. void tcap_put(char *cap)
  1022. {
  1023.     tputs(cap, 0, c_output);
  1024. }    /* tcap_put */
  1025.  
  1026. #endif /* CURSES */
  1027.  
  1028. /* eof main.c */
  1029.