home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ncftp-2.3.0-src.tgz / tar.out / contrib / ncftp / Main.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  23KB  |  950 lines

  1. /* Main.c */
  2.  
  3. #define _main_c_ 1
  4.  
  5. /* This is copyrighted software.  Read the entire ``COPYRIGHT.h'' file
  6.  * before compiling or using!
  7.  */
  8. #include "COPYRIGHT.h"
  9.  
  10. #include "Sys.h"
  11.  
  12. #if (LOCK_METHOD == 2)
  13. #    ifdef HAVE_SYS_FILE_H
  14. #        include <sys/file.h>
  15. #    endif
  16. #endif
  17.  
  18. #ifdef SYSLOG
  19. #    include <syslog.h>
  20. #endif
  21.  
  22. #include <pwd.h>
  23. #include <errno.h>
  24. #include <ctype.h>
  25. #include <signal.h>
  26. #include <setjmp.h>
  27. #include <stdlib.h>
  28.  
  29. #include "Util.h"
  30. #include "Main.h"
  31. #include "Cmds.h"
  32. #include "Open.h"
  33. #include "Cmdline.h"
  34. #include "DateSize.h"
  35. #include "Bookmark.h"
  36. #include "Prefs.h"
  37. #include "FTP.h"
  38. #include "Glob.h"
  39. #include "Getopt.h"
  40. #include "Xfer.h"
  41. #include "Complete.h"
  42. #include "Tips.h"
  43. #include "Version.h"
  44.  
  45. /* We need to know our fully qualified hostname.  That way we can give
  46.  * a complete email address as a password for anonymous logins.
  47.  */
  48. string gOurHostName;
  49.  
  50. /* The user's full email address, which we use for the password on
  51.  * anonymous logins.
  52.  */
  53. string gEmailAddress;
  54.  
  55. /* This is what we'll actually use for the anonymous password.
  56.  * We keep an unmodified copy of gEmailAddress, and let the user
  57.  * change the variable below if they want to.
  58.  */
  59. string gAnonPassword;
  60.  
  61. /* Since we create a bunch of files, instead of making them all
  62.  * .dot files in the home directory, we have our own .dot directory
  63.  * and keep all our stuff in there.
  64.  */
  65. string gOurDirectoryPath;
  66.  
  67. /* We keep some basic information about the user.  Most of the structure
  68.  * is just a copy of the user's password file entry.  Instead of keeping
  69.  * just that pointer from getpwnam(), we make our own copy so we can
  70.  * use getpwnam() again later.
  71.  */
  72. UserInfo gUserInfo;
  73.  
  74. /* Boolean indicating whether input is coming from a live user or
  75.  * a file.
  76.  */
  77. int gIsFromTTY;
  78.  
  79. /* Boolean indicating whether output is going to the screen or to
  80.  * a file.
  81.  */
  82. int gIsToTTY;
  83.  
  84. /* Boolean indicating whether input is coming from a script file.
  85.  * This is really just a copy of gIsFromTTY.
  86.  */
  87. int gDoingScript;
  88.  
  89. /* A FILE pointer to the user's usage log, or NULL if we're not
  90.  * logging anything.
  91.  */
  92. FILE *gLogFile = NULL;
  93.  
  94. /* We can optionally log all the debugging information to a separate
  95.  * log file.
  96.  */
  97. FILE *gTraceLogFile = NULL;
  98.  
  99. /* Full pathname of the user's usage log file, in our .dot directory. */
  100. longstring gLogFileName;
  101.  
  102. /* Maximum size we will let the log grow to before making it smaller.
  103.  * If this is zero, there is no limit.
  104.  */
  105. int gMaxLogSize = 10240;
  106.  
  107. /* Boolean indicating whether the user wanted to keep a usage log. */
  108. int gLogging = USERLOG;
  109.  
  110. /* Full pathname of the user's pager.  Maybe be just "more" if the user
  111.  * doesn't have a PAGER environment variable, or didn't set it explicitly.
  112.  */
  113. longstring gPager;
  114.  
  115. int gVisualMode;
  116. int gDefaultVisualMode = VISUAL;
  117.  
  118. /* If another ncftp process is running, we don't want to overwrite the
  119.  * the files in our directory.  We will only read them to prevent the
  120.  * user losing changes made in the first process.
  121.  */
  122. int gOtherSessionRunning = 0;
  123.  
  124. /* We track the number of times the user has run the program.  We may
  125.  * use that information for something else later.
  126.  */
  127. int gTotalRuns = 0;
  128.  
  129. /* Types of startup messages user wants us to print when we first run. */
  130. int gStartupMsgs = (kStartupMsg | kTips);
  131.  
  132. string gVersion;
  133.  
  134. /* If this user variable is set, we will change directory to the last
  135.  * local directory the user was in when the user exited.
  136.  */
  137. int gRememberLCWD = 0;
  138.  
  139. /* If this user variable is set, we will always chdir to this value each
  140.  * time we run the program.
  141.  */
  142. longstring gDownloadDir = "";
  143.  
  144. jmp_buf gMainJmp;
  145.  
  146. /* We save previously entered commands between sessions, so the user can
  147.  * use the history.
  148.  */
  149. LineList gCmdHistory;
  150.  
  151. /* Name of the file we use to tell if another ncftp process is running. */
  152. longstring gLockFileName = "";
  153.  
  154. /* Boolean telling whether the splash-screen and interactive startup stuff
  155.  * has been done.
  156.  */
  157. int gStartup = 0;
  158.  
  159. extern int gStdout, gRealStdout;
  160. extern char *getlogin(void);
  161. extern longstring gLocalCWD;
  162. extern time_t gMailBoxTime;
  163. extern int gWinInit, gOptErr, gTrace, gDebug;
  164. extern char *gSprintfBuf;
  165. extern jmp_buf gCmdLoopJmp;
  166. extern LineList gRedir;
  167.  
  168.  
  169.  
  170. /* This looks up the user's password entry, trying to look by the username.
  171.  * We have a couple of extra hacks in place to increase the probability
  172.  * that we can get the username.
  173.  */
  174. static
  175. struct passwd *GetPwByName(void)
  176. {
  177.     char *cp;
  178.     struct passwd *pw;
  179.     
  180.     cp = getlogin();
  181.     if (cp == NULL) {
  182.         cp = (char *) getenv("LOGNAME");
  183.         if (cp == NULL)
  184.             cp = (char *) getenv("USER");
  185.     }
  186.     pw = NULL;
  187.     if (cp != NULL)
  188.         pw = getpwnam(cp);
  189.     return (pw);
  190. }    /* GetPwByName */
  191.  
  192.  
  193.  
  194.  
  195. void GetUserInfo(void)
  196. {
  197.     register char            *cp;
  198.     struct passwd            *pw;
  199.     string                    str;
  200.     struct stat                st;
  201.  
  202.     pw = NULL;
  203.     errno = 0;
  204. #ifdef USE_GETPWUID
  205.     /* Try to use getpwuid(), but if we have to, fall back to getpwnam(). */
  206.     if ((pw = getpwuid(getuid())) == NULL)
  207.         pw = GetPwByName();    /* Oh well, try getpwnam() then. */
  208. #else
  209.     /* Try to use getpwnam(), but if we have to, fall back to getpwuid(). */
  210.     if ((pw = GetPwByName()) == NULL)
  211.         pw = getpwuid(getuid());    /* Try getpwnam() then. */
  212. #endif
  213.     if (pw != NULL) {
  214.         gUserInfo.uid = pw->pw_uid;
  215.         (void) STRNCPY(gUserInfo.userName, pw->pw_name);
  216.         gUserInfo.shell = StrDup(pw->pw_shell);
  217.         if ((cp = (char *) getenv("HOME")) != NULL)
  218.             gUserInfo.home = StrDup(cp);
  219.         else
  220.             gUserInfo.home = StrDup(pw->pw_dir);
  221.     } else {
  222.         /* Couldn't get information about this user.  Since this isn't
  223.          * the end of the world as far as I'm concerned, make up
  224.          * some stuff that might work good enough.
  225.          */
  226.         Error(kDoPerror, "Could not get your passwd entry!");
  227.         sleep(1);
  228.         if ((cp = (char *) getenv("LOGNAME")) == NULL)
  229.             cp = "nobody";
  230.         (void) STRNCPY(gUserInfo.userName, cp);
  231.         gUserInfo.shell = StrDup("/bin/sh");
  232.         if ((cp = (char *) getenv("HOME")) == NULL)
  233.             cp = ".";
  234.         gUserInfo.home = StrDup(cp);
  235.         gUserInfo.uid = 999;
  236.     }
  237.  
  238.     cp = (char *) getenv("MAIL");
  239.     if (cp == NULL)
  240.         cp = (char *) getenv("mail");
  241.     if (cp != NULL) {
  242.         /* We do have a Mail environment variable. */
  243.         (void) STRNCPY(str, cp);
  244.         cp = str;
  245.  
  246.         /* Mail variable may be like MAIL=(28 /usr/mail/me /usr/mail/you),
  247.          * so try to find the first mail path.
  248.          */
  249.         while ((*cp != '/') && (*cp != 0))
  250.             cp++;
  251.         gUserInfo.mail = StrDup(cp);
  252.         if ((cp = strchr(gUserInfo.mail, ' ')) != NULL)
  253.             *cp = '\0';
  254.     } else {
  255.         /* Guess between /usr/mail and /usr/spool/mail as
  256.          * possible directories.  We'll just choose /usr/spool/mail
  257.          * if we have to.
  258.          */
  259.         (void) sprintf(str, "/usr/mail/%s", gUserInfo.userName);
  260.         if (stat(str, &st) < 0)
  261.             (void) sprintf(str, "/usr/spool/mail/%s", gUserInfo.userName);
  262.         gUserInfo.mail = StrDup(str);
  263.     }
  264.  
  265.     DebugMsg("GetOurHostName returned %d, %s.\n",    
  266.         GetOurHostName(gOurHostName, sizeof(gOurHostName)),
  267.         gOurHostName
  268.     );
  269.     STRNCPY(gEmailAddress, gUserInfo.userName);
  270.     STRNCAT(gEmailAddress, "@");
  271.     STRNCAT(gEmailAddress, gOurHostName);
  272.     STRNCPY(gAnonPassword, gEmailAddress);
  273. }    /* GetUserInfo */
  274.  
  275.  
  276.  
  277.  
  278. /* Setup the malloc library, if we're using one. */
  279. static void InitMalloc(void)
  280. {
  281. #ifdef DEBUG
  282. #    if (LIBMALLOC == FAST_MALLOC)
  283.     FILE *malloclog;
  284.  
  285.     mallopt(M_DEBUG, 1);
  286.     mallopt(M_LOG, 1);    /* turn on/off malloc/realloc/free logging */
  287.     /* set the file to use for logging */
  288.     if ((malloclog = fopen("malloc.log", "w")) != NULL)
  289.         mallopt(M_LOGFILE, fileno(malloclog)); 
  290. #    endif
  291. #endif
  292.  
  293. #if (LIBMALLOC == DEBUG_MALLOC)
  294.     /* Thanks to Conor Cahill for making something like this possible! */
  295.     union dbmalloptarg  m;
  296.  
  297. #    if 1
  298.     m.i = 1;
  299.     dbmallopt(MALLOC_CKCHAIN, &m);
  300. #    endif
  301.  
  302. #    if 1
  303.     m.i = M_HANDLE_IGNORE;
  304.     dbmallopt(MALLOC_WARN, &m);
  305. #    endif
  306.  
  307. #    if 0    /* If 0, stderr. */
  308.     m.str = "malloc.log";
  309.     dbmallopt(MALLOC_ERRFILE, &m);
  310. #    endif
  311. #endif
  312. }    /* InitMalloc */
  313.  
  314.  
  315.  
  316.  
  317. void OpenTraceLog(void)
  318. {
  319.     string traceLogPath;
  320.     string traceLog2Path;
  321.     string traceLogTmpPath;
  322.     string line;
  323.     FILE *f1, *f2, *f3;
  324.     int i, lim;
  325.     time_t now;
  326.  
  327.     if (gOtherSessionRunning != 0)
  328.         return;
  329.  
  330.     if (gTraceLogFile != NULL)
  331.         return;    /* Already open. */
  332.  
  333.     if (gOurDirectoryPath[0] == '\0')
  334.         return;        /* Don't create in root directory. */
  335.  
  336.     OurDirectoryPath(traceLogPath, sizeof(traceLogPath), kTraceLogName);    
  337.     if (access(traceLogPath, F_OK) == 0) {
  338.         /* Need to prepend last session's log to the master debug log. */
  339.         OurDirectoryPath(traceLog2Path, sizeof(traceLog2Path), kTraceLog2Name);
  340.         OurDirectoryPath(traceLogTmpPath, sizeof(traceLogTmpPath), kTraceLogTmpName);
  341.  
  342.         f1 = fopen(traceLogTmpPath, "w");
  343.         if (f1 == NULL) {
  344.             Error(kDoPerror, "Can't open %s for writing.\n", traceLogTmpPath);
  345.             return;
  346.         }
  347.         f2 = fopen(traceLogPath, "r");
  348.         if (f2 == NULL) {
  349.             Error(kDoPerror, "Can't open %s for reading.\n", traceLogPath);
  350.             return;
  351.         }
  352.         
  353.         lim = kMaxTraceLogLines - 1;
  354.         for (i=0; i<lim; i++) {
  355.             if (fgets(line, ((int) sizeof(line)) - 1, f2) == NULL)
  356.                 break;
  357.             fputs(line, f1);
  358.         }
  359.         if (i + 2 < lim) {
  360.             fputs("\n\n", f1);
  361.             i += 2;
  362.         }
  363.         (void) fclose(f2);
  364.         f3 = fopen(traceLog2Path, "r");
  365.         if (f3 != NULL) {
  366.             for (; i<lim; i++) {
  367.                 if (fgets(line, ((int) sizeof(line)) - 1, f3) == NULL)
  368.                     break;
  369.                 fputs(line, f1);
  370.             }
  371.             (void) fclose(f3);            
  372.         }
  373.  
  374.         if (i >= lim)
  375.             fputs("...Remaining lines omitted...\n", f1);
  376.  
  377.         (void) fclose(f1);
  378.         (void) UNLINK(traceLogPath);
  379.         (void) UNLINK(traceLog2Path);
  380.         (void) rename(traceLogTmpPath, traceLog2Path);
  381.     }
  382.  
  383.     gTraceLogFile = fopen(traceLogPath, "w");
  384.     if (gTraceLogFile == NULL) {
  385.         Error(kDoPerror, "Can't open %s for writing.\n", traceLogPath);
  386.     } else {
  387.         time(&now);
  388.         fprintf(
  389.             gTraceLogFile,
  390.             "SESSION STARTED at %s%s\n\n",
  391.             ctime(&now),
  392.             "-----------------------------------------------"
  393.         );
  394.     }
  395. }    /* OpenTraceLog */
  396.  
  397.  
  398.  
  399.  
  400. void OpenLogs(void)
  401. {
  402.     /* First try open the user's private log, unless the user
  403.      * has this option turned off.
  404.      */
  405.     OurDirectoryPath(gLogFileName, sizeof(gLogFileName), kLogName);
  406.     gLogFile = NULL;
  407.     if (gOtherSessionRunning == 0) {
  408.         if ((gLogging) && (gMaxLogSize > 0))
  409.             gLogFile = fopen(gLogFileName, "a");
  410.  
  411.         if (gTrace == kTracingOn)
  412.             OpenTraceLog();
  413.     }
  414.  
  415.     /* Open a port to the system log, if you're hellbent on knowing
  416.      * everything users do with the program.
  417.      */
  418. #ifdef SYSLOG
  419. #    ifdef LOG_LOCAL3
  420.     openlog ("NcFTP", LOG_PID, LOG_LOCAL3);
  421. #    else
  422.     openlog ("NcFTP", LOG_PID);
  423. #    endif
  424. #endif                /* SYSLOG */
  425. }    /* OpenLogs */
  426.  
  427.  
  428.  
  429.  
  430. /* Create, if necessary, a directory in the user's home directory to
  431.  * put our incredibly important stuff in.
  432.  */
  433. void InitOurDirectory(void)
  434. {
  435.     struct stat st;
  436.     char *cp;
  437.  
  438.     cp = getenv("NCFTPDIR");
  439.     if (cp != NULL) {
  440.         STRNCPY(gOurDirectoryPath, cp);
  441.     } else if (STREQ(gUserInfo.home, "/")) {
  442.         /* Don't create it if you're root and your home directory
  443.          * is the root directory.
  444.          */
  445.         gOurDirectoryPath[0] = '\0';
  446.         return;
  447.     } else {
  448.         (void) Path(gOurDirectoryPath,
  449.             sizeof(gOurDirectoryPath),
  450.             gUserInfo.home,
  451.             kOurDirectoryName
  452.         );
  453.     }
  454.  
  455.     if (stat(gOurDirectoryPath, &st) < 0) {
  456.         (void) mkdir(gOurDirectoryPath, 00755);
  457.     }
  458. }    /* InitOurDirectory */
  459.  
  460.  
  461.  
  462. /* There would be problems if we had multiple ncftp's going.  They would
  463.  * each try to update the files in ~/.ncftp.  Whichever program exited
  464.  * last would have its stuff written, and the others which may have made
  465.  * changes, would lose the changes they made.
  466.  *
  467.  * This checks to see if another ncftp is running.  To do that we try
  468.  * to lock a special file.  If we could make the lock, then we are the first
  469.  * ncftp running and our changes will be written.  Other ncftps that may
  470.  * start will not be able to save their changes.
  471.  *
  472.  * No matter what locking method in use, whenever we Exit() we try to
  473.  * remove the lock file if we had the lock.  For the first two methods,
  474.  * exiting releases the lock automatically.
  475.  */
  476. static
  477. void CheckForOtherSessions(void)
  478. {
  479.     int fd;
  480.     char pidbuf[64];
  481.     time_t now;
  482.  
  483. #if (LOCK_METHOD == 1)
  484.     struct flock l;
  485.     int err;
  486.  
  487.     l.l_type = F_WRLCK;
  488.     l.l_start = 0;
  489.     l.l_whence = SEEK_SET;
  490.     l.l_len = 1;
  491.  
  492.     OurDirectoryPath(gLockFileName, sizeof(gLockFileName), kLockFileName);
  493.     if ((fd = open(gLockFileName, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) {
  494.         Error(kDoPerror, "Could not open lock file %s.\n", gLockFileName);
  495.         sleep(5);
  496.  
  497.         /* Try to save data anyway. */
  498.         gOtherSessionRunning = 0;
  499.     } else {
  500.         err = fcntl(fd, F_SETLK, &l);
  501.         if (err == 0) {
  502.             /* We now have a lock set on the first byte. */
  503.             gOtherSessionRunning = 0;
  504.             time(&now);
  505.             sprintf(pidbuf, "%5d %s", (int) getpid(), ctime(&now));
  506.             write(fd, pidbuf, strlen(pidbuf) + 1);
  507.         } else {
  508.             /* Could not lock;  maybe another ncftp process has
  509.              * already locked it.
  510.              */
  511.             gOtherSessionRunning = 1;
  512.         }
  513.     }
  514. #endif    /* (LOCK_METHOD == 1) */
  515.  
  516. #if (LOCK_METHOD == 2)    /* BSD's flock */
  517.     int err;
  518.  
  519.     OurDirectoryPath(gLockFileName, sizeof(gLockFileName), kLockFileName);
  520.     if ((fd = open(gLockFileName, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) {
  521.         Error(kDoPerror, "Could not open lock file %s.\n", gLockFileName);
  522.         sleep(5);
  523.  
  524.         /* Try to save data anyway. */
  525.         gOtherSessionRunning = 0;
  526.     } else {
  527.         err = flock(fd, LOCK_EX | LOCK_NB);
  528.         if (err == 0) {
  529.             /* We now have a lock set on the whole file. */
  530.             gOtherSessionRunning = 0;
  531.             time(&now);
  532.             sprintf(pidbuf, "%5d %s", (int) getpid(), ctime(&now));
  533.             write(fd, pidbuf, strlen(pidbuf) + 1);
  534.         } else {
  535.             /* Could not lock;  maybe another ncftp process has
  536.              * already locked it.
  537.              */
  538.             gOtherSessionRunning = 1;
  539.         }
  540.     }
  541. #endif    /* (LOCK_METHOD == 2) */
  542.  
  543. #if (LOCK_METHOD == 3)    /* Cheezy lock */
  544.     /* For this locking mechanism, we consider it locked if another
  545.      * process had already created the lockfile.  Unfortunately we
  546.      * aren't guaranteed to get the lock file removed if the program
  547.      * crashes somewhere without explicitly removing the lockfile.
  548.      */
  549.  
  550.     OurDirectoryPath(gLockFileName, sizeof(gLockFileName), kLockFileName);
  551.     if ((fd = open(gLockFileName, O_CREAT | O_EXCL | O_WRONLY, S_IWUSR)) < 0) {
  552.         gOtherSessionRunning = 1;
  553.     } else {
  554.         gOtherSessionRunning = 0;
  555.         /* Unlike the other two methods, we don't need to keep the file
  556.          * open.
  557.          */
  558.         close(fd);
  559.     }
  560. #endif    /* (LOCK_METHOD == 3) */
  561. }    /* CheckForOtherSessions */
  562.  
  563.  
  564.  
  565.  
  566. void Init(void)
  567. {
  568.     char *cp;
  569.     
  570.     gRealStdout = gStdout = 1;
  571.     InitMalloc();
  572.     InitXferBuffer();
  573.     gSprintfBuf = (char *) malloc((size_t) 4096);
  574.     if (gSprintfBuf == NULL)
  575.         OutOfMemory();
  576.  
  577. #if (kAlpha > 0)
  578.         sprintf(gVersion, "%s Alpha %d (%s)",
  579.             kVersion,
  580.             kAlpha,
  581.             kVersionDate
  582.         );
  583. #else
  584. #    if (kBeta > 0)
  585.         sprintf(gVersion, "%s Beta %d (%s)",
  586.             kVersion,
  587.             kBeta,
  588.             kVersionDate
  589.         );
  590. #    else
  591.         sprintf(gVersion, "%s (%s)",
  592.             kVersion,
  593.             kVersionDate
  594.         );
  595. #    endif
  596. #endif
  597.  
  598.     srand((unsigned int) time(NULL));
  599.  
  600.     GetUserInfo();
  601.     InitDefaultFTPPort();
  602.     if ((cp = (char *) getenv("PAGER")) != NULL)
  603.         STRNCPY(gPager, cp);
  604.     else {
  605. #ifdef MORE
  606.         STRNCPY(gPager, MORE);
  607. #else
  608.         gPager[0] = '\0';
  609. #endif
  610.     }
  611.     gIsFromTTY = gDoingScript = isatty(0);
  612.     gIsToTTY = isatty(1);
  613.     (void) UserLoggedIn();    /* Init parent-death detection. */
  614.  
  615.     /* Init the mailbox checking code. */
  616.     (void) time(&gMailBoxTime);
  617.  
  618.     /* Clear the redir buffer. */
  619.     InitLineList(&gRedir);
  620.  
  621.     (void) GetCWD(gLocalCWD, sizeof(gLocalCWD));
  622.  
  623.     /* Compute our own private directory's name, and create it
  624.      * if we have to.  Stuff that depends on this will need
  625.      * to follow (duh).
  626.      */
  627.     (void) InitOurDirectory();
  628.  
  629.     (void) CheckForOtherSessions();
  630.     ReadPrefs();
  631.  
  632.     OpenLogs();
  633.     ReadBookmarkFile();
  634.     ReadMacroFile();
  635.     InitCommandAndMacroNameList();
  636.     
  637.     ++gTotalRuns;
  638. }    /* Init */
  639.  
  640.  
  641.  
  642.  
  643. void CloseTraceLog(void)
  644. {
  645.     time_t now;
  646.  
  647.     if (gTraceLogFile != NULL) {
  648.         time(&now);
  649.         fprintf(
  650.             gTraceLogFile,
  651.             "\nSESSION ENDED at %s",
  652.             ctime(&now)
  653.         );
  654.         (void) fclose(gTraceLogFile);
  655.         gTraceLogFile = NULL;
  656.     }
  657. }    /* CloseTraceLog */
  658.  
  659.  
  660.  
  661.  
  662. void CloseLogs(void)
  663. {
  664.     FILE *new, *old;
  665.     struct stat st;
  666.     long fat;
  667.     string str;
  668.     longstring tmpLog;
  669.  
  670. #ifdef SYSLOG
  671.     /* Close system log first. */
  672.     closelog();
  673. #endif
  674.  
  675.     /* Close the debugging log next. */
  676.     CloseTraceLog();
  677.  
  678.     /* The rest is for the user's log. */
  679.     if (!gLogging)
  680.         return;
  681.  
  682.     CloseFile(&gLogFile);
  683.  
  684.     if (gOurDirectoryPath[0] == '\0')
  685.         return;        /* Don't create in root directory. */
  686.  
  687.     /* If the user wants to, s/he can specify the maximum size of the log file,
  688.      * so it doesn't waste too much disk space.  If the log is too fat, trim the
  689.      * older lines (at the top) until we're under the limit.
  690.      */
  691.     if ((gMaxLogSize < 0) || (stat(gLogFileName, &st) < 0) ||
  692.         ((old = fopen(gLogFileName, "r")) == NULL))
  693.         return;                           /* Never trim, or no log. */
  694.  
  695.     if (st.st_size < (size_t)gMaxLogSize)
  696.         return;                           /* Log size not over limit yet. */
  697.  
  698.     /* Want to make it so we're about 30% below capacity.
  699.      * That way we won't trim the log each time we run the program.
  700.      */
  701.     fat = st.st_size - gMaxLogSize + (long) (0.30 * gMaxLogSize);
  702.     DebugMsg("%s was over %ld limit; trimming at least %ld...\n",
  703.         gLogFileName,
  704.         gMaxLogSize,
  705.         fat
  706.     );
  707.     while (fat > 0L) {
  708.         if (fgets(str, (int) sizeof(str), old) == NULL)
  709.             return;
  710.         fat -= (long) strlen(str);
  711.     }
  712.     /* skip lines until a new site was opened */
  713.     while (1) {
  714.         if (fgets(str, (int) sizeof(str), old) == NULL) {
  715.             (void) fclose(old);
  716.             (void) UNLINK(gLogFileName);
  717.             return;                       /* Nothing left, start anew next time. */
  718.         }
  719.         if (! isspace(*str))
  720.             break;
  721.     }
  722.  
  723.     /* Copy the remaining lines in "old" to "new" */
  724.     OurDirectoryPath(tmpLog, sizeof(tmpLog), kTmpLogName);
  725.     if ((new = fopen(tmpLog, "w")) == NULL) {
  726.         (void) Error(kDoPerror, "Could not open %s.\n", tmpLog);
  727.         return;
  728.     }
  729.     (void) fputs(str, new);
  730.     while (fgets(str, (int) sizeof(str), old) != NULL)
  731.         (void) fputs(str, new);
  732.     (void) fclose(old);
  733.     (void) fclose(new);
  734.     if (UNLINK(gLogFileName) < 0)
  735.         (void) Error(kDoPerror, "Could not delete %s.\n", gLogFileName);
  736.     if (rename(tmpLog, gLogFileName) < 0)
  737.         (void) Error(kDoPerror, "Could not rename %s to %s.\n",
  738.             tmpLog, gLogFileName);
  739. }                                       /* CloseLogs */
  740.  
  741.  
  742.  
  743.  
  744. void SaveHistory(void)
  745. {
  746.     string histFileName;
  747.     FILE *fp;
  748.     LinePtr lp;
  749.     int i;
  750.  
  751.     if (gWinInit) {
  752.         if (gOurDirectoryPath[0] == '\0')
  753.             return;        /* Don't create in root directory. */
  754.         lp = gCmdHistory.last;
  755.         OurDirectoryPath(histFileName, sizeof(histFileName), kHistoryName);
  756.         fp = fopen(histFileName, "w");
  757.         if ((fp != NULL) && (lp != NULL)) {
  758.             for (i = 1 ; (lp->prev != NULL) && (i < kMaxHistorySaveLines); i++)
  759.                 lp = lp->prev;
  760.             for ( ; lp != NULL; lp = lp->next)
  761.                 fprintf(fp, "%s\n", lp->line);
  762.             fclose(fp);
  763.         }
  764.     }
  765. }    /* SaveHistory */
  766.  
  767.  
  768.  
  769.  
  770. void LoadHistory(void)
  771. {
  772.     string histFileName;
  773.     string str;
  774.     FILE *fp;
  775.  
  776.     InitLineList(&gCmdHistory);
  777.     OurDirectoryPath(histFileName, sizeof(histFileName), kHistoryName);
  778.     fp = fopen(histFileName, "r");
  779.     if (fp != NULL) {
  780.         while (FGets(str, sizeof(str), fp) != NULL)
  781.             AddLine(&gCmdHistory, str);
  782.     }
  783. }    /* LoadHistory */
  784.  
  785.  
  786.  
  787.  
  788. void StartupMsgs(void)
  789. {
  790.     char curLocalCWD[512];
  791.  
  792.     if ((gStartupMsgs & kStartupMsg) != 0) {
  793. #if (kAlpha > 0) || (kBeta > 0)
  794.         if (gWinInit == 0)
  795.             PrintF("NcFTP %s, by Mike Gleason, NCEMRSoft.\n", gVersion);
  796.         BoldPrintF(
  797.         "This pre-release is for testing only.  Please do not archive.\n\n");
  798.         BoldPrintF(
  799.         "Check the CHANGELOG file for changes between betas.\n\n");
  800. #else
  801.         if (gWinInit == 0)
  802.             PrintF("NcFTP %s, by Mike Gleason, NCEMRSoft.\n", gVersion);
  803. #endif
  804.     }
  805.     if (gTotalRuns < 2) {
  806.         PrintF("%s%s%s\n", gCopyright1 + 5, gCopyright2, gCopyright3);
  807.     } else if ((gStartupMsgs & kTips) != 0) {
  808.         PrintRandomTip();
  809.     }
  810.  
  811.     if (gOtherSessionRunning) {
  812.         BoldPrintF("Note: Another session is running. Changes made to the\n");
  813.         BoldPrintF("      prefs, bookmarks, and trace files will not be saved.\n\n");
  814.     }
  815.  
  816.     (void) GetCWD(curLocalCWD, sizeof(curLocalCWD));
  817.     if (gDownloadDir[0] != '\0') {
  818.         /* If set, always try to cd there. */
  819.         ExpandTilde(gDownloadDir, sizeof(gDownloadDir));
  820.         (void) chdir(gDownloadDir);    /* May not succeed. */
  821.     } else if (gRememberLCWD) {
  822.         /* Try restoring the last local directory we were in.
  823.          * gLocalCWD may be set when the prefs are read in.
  824.          */
  825.         if (gLocalCWD[0] != '\0')
  826.             (void) chdir(gLocalCWD);    /* May not succeed. */
  827.     }
  828.  
  829.     /* Print a message if we changed the directory. */
  830.     (void) GetCWD(gLocalCWD, sizeof(gLocalCWD));
  831.     if (strcmp(curLocalCWD, gLocalCWD))
  832.         PrintF("Current local directory is %s.\n", gLocalCWD);
  833. }    /* StartupMsg */
  834.  
  835.  
  836.  
  837.  
  838. void Startup(void)
  839. {
  840.     if (gStartup == 0) {
  841.         gStartup = 1;
  842.         InitWindows();
  843.         StartupMsgs();
  844.         (void) RunPrefixedMacro("start.", "ncftp");
  845.          InitReadline();
  846.     }
  847. }    /* Startup */
  848.  
  849.  
  850.  
  851.  
  852. /*ARGSUSED*/
  853. static void
  854. SigIntMain(int sigNum)
  855. {
  856.     SIGNAL(SIGINT, SigIntMain);
  857.     alarm(0);
  858.     EPrintF("\n*Interrupt*\n");
  859.     longjmp(gMainJmp, 1);
  860. }   /* SigIntMain */
  861.  
  862.  
  863.  
  864. void main(int argc, char **argv)
  865. {
  866.     int opt, result;
  867.     OpenOptions openopt;
  868.  
  869.     Init();
  870.     RunStartupScript();
  871.  
  872.     GetoptReset();
  873.     gOptErr = 0;
  874.     if (GetOpenOptions(argc, argv, &openopt, 1) == kUsageErr)
  875.         goto usage;
  876.  
  877.     gOptErr = 1;
  878.     GetoptReset();
  879.     gVisualMode = gDefaultVisualMode;
  880.  
  881.     /* Note that we getopt on three sets of options, one for open,
  882.      * one for get, and one for the program.  That means that these
  883.      * commands' flags and the program must use mutually exclusive
  884.      * sets of flags.
  885.      */
  886.     while ((opt = Getopt(argc, argv, "aiup:rd:g:cmCfGRn:zDLVH")) >= 0) {
  887.         if (strchr("aiup:rd:g:cmCfGRn:z", opt) == NULL) {
  888.             switch (opt) {
  889.                 case 'D':
  890.                     gDebug = kDebuggingOn;
  891.                     gTrace = kTracingOn;
  892.                     break;
  893.                 case 'L': gVisualMode = 0; break;
  894.                 case 'V': gVisualMode = 1; break;
  895.                 case 'H': VersionCmd(0, NULL); Exit(kExitNoErr); break;
  896.                 default:
  897.                 usage:
  898.                     EPrintF(
  899.         "Usage: ncftp [options] [hostname[:path]]\n");
  900.                     EPrintF("Program options:\n\
  901.   -D   : Turn debug mode and trace mode on.\n\
  902.   -L   : Don't use visual mode (use line mode).\n\
  903.   -V   : Use visual mode.\n\
  904.   -H   : Dump the version information.\n");
  905.                     EPrintF("Command-line open options:\n\
  906.   -a   : Open anonymously.\n\
  907.   -u   : Open with username and password prompt.\n\
  908.   -p X : Use port number X when opening.\n\
  909.   -r   : Redial until connected.\n\
  910.   -d X : Redial, delaying X seconds between tries.\n\
  911.   -g X : Give up after X redials without connection.\n");
  912.                     EPrintF("Command-line retrieve options:\n\
  913.   -C   : Force continuation (reget).\n\
  914.   -f   : Force overwrite.\n\
  915.   -G   : Don't use wildcard matching.\n\
  916.   -R   : Recursive.  Useful for fetching whole directories.\n\
  917.   -n X : Get selected files only if X days old or newer.\n");
  918.                     Exit(kExitUsageErr);
  919.             }
  920.         }
  921.     }
  922.  
  923.     result = kNoErr;
  924.     if (openopt.hostname[0] != '\0') {
  925.         if (setjmp(gMainJmp)) {
  926.             result = kNoErr;
  927.         } else {
  928.             SIGNAL(SIGINT, SigIntMain);
  929.             SIGNAL(SIGPIPE, SIG_IGN);
  930.             if (setjmp(gCmdLoopJmp)) {
  931.                 /* May have lost connection during Open. */
  932.                 result = kCmdErr;
  933.             } else {
  934.                 result = Open(&openopt);
  935.             }
  936.         }
  937.     }
  938.  
  939.     if (result == kNoErr) {
  940.         Startup();        /* Init the interactive shell. */
  941.         CommandShell();
  942.         DoQuit(kExitNoErr);
  943.     }
  944.     DoQuit(kExitColonModeFail);
  945.     /*NOTREACHED*/
  946.     Exit(kExitNoErr);
  947. }    /* main */
  948.  
  949. /* eof Main.c */
  950.