home *** CD-ROM | disk | FTP | other *** search
/ ftp.ncftp.com / ftp.ncftp.com.zip / ftp.ncftp.com / ncftp / ncftp-1.9.5.tar.gz / ncftp-1.9.5.tar / ncftp-1.9.5 / main.c < prev    next >
C/C++ Source or Header  |  1995-10-29  |  27KB  |  1,161 lines

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