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 / Get.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  23KB  |  1,024 lines

  1. /* Get.c */
  2.  
  3. #include "Sys.h"
  4.  
  5. #include <signal.h>
  6.  
  7. #ifdef HAVE_UTIME_H
  8. #    include <utime.h>
  9. #else
  10. struct    utimbuf {time_t actime; time_t modtime;};
  11. #endif 
  12.  
  13. #include "Util.h"
  14. #include "RCmd.h"
  15. #include "Xfer.h"
  16. #include "Cmds.h"
  17. #include "Glob.h"
  18. #include "Get.h"
  19. #include "DateSize.h"
  20. #include "List.h"
  21. #include "Getopt.h"
  22.  
  23. int gMayUTime = kDoUTime;    /* User variable. */
  24.  
  25. extern longstring gPager;
  26. extern longstring gRemoteCWD;
  27. extern longstring gLocalCWD;
  28. extern int gTransferType, gXferAbortFlag;
  29. extern int gStdout, gWinInit;
  30. extern char *gOptArg;
  31. extern int gOptInd;
  32.  
  33. long BinaryGetProc(char *buf, size_t bufsize, XferSpecPtr xp)
  34. {
  35.     long len;
  36.  
  37.     len = (long) ReadOrTimeout(xp->inStream, buf, bufsize);
  38.     return (len);
  39. }    /* BinaryGetProc */
  40.  
  41.  
  42.  
  43.  
  44. long BinaryPutProc(char *buf, size_t bufsize, XferSpecPtr xp)
  45. {
  46.     long len;
  47.  
  48.     len = (long) WriteOrTimeout(xp->outStream, buf, bufsize);
  49.     if (len != (long)bufsize) {
  50.         if (len >= 0L)        /* Could already be kTimeoutErr. */
  51.             len = -1L;
  52.     }
  53.     return (len);
  54. }    /* BinaryPutProc */
  55.  
  56.  
  57.  
  58.  
  59. long AsciiGetRemoteProc(char *buf, size_t bufsize, XferSpecPtr xp)
  60. {
  61.     long len;
  62.  
  63.     len = (long) ReadOrTimeout(xp->inStream, buf, bufsize);
  64.     return (len);
  65. }    /* AsciiGetRemoteProc */
  66.  
  67.  
  68.  
  69.  
  70. long AsciiPutLocalProc(char *buf, size_t bufsize, XferSpecPtr xp)
  71. {
  72.     long len, len2;
  73.     char *i, *o;
  74.     long ct;
  75.  
  76.     /* In ASCII mode, all end-of-lines are denoted by CR/LF.  For
  77.      * UNIX, we don't want that, we want just LFs, so skip all the
  78.      * CR's.
  79.      */
  80.     for (len = bufsize, i = o = buf, ct = 0; ct < len; ct++, ++i) {
  81.         if (*i != '\r')
  82.             *o++ = *i;
  83.     }
  84.     len = o - buf;
  85.  
  86.     len2 = (long) WriteOrTimeout(xp->outStream, buf, len);
  87.     if (len2 != len) {
  88.         if (len2 >= 0L)        /* Could already be kTimeoutErr. */
  89.             len2 = -1L;
  90.     }
  91.     return (len2);
  92. }    /* AsciiPutLocalProc */
  93.  
  94.  
  95.  
  96.  
  97. int BinaryGet(XferSpecPtr xp)
  98. {
  99.     int result;
  100.  
  101.     /* This is supposed to be done previously, if you wanted accurate
  102.      * file sizes from GetDateAndSize.  We don't do a SETBINARY here.
  103.      * Instead, we set it to gTransferType, which we know is not
  104.      * ascii.  Most often this will mean binary mode, but perhaps we're
  105.      * dealing with a tenex machine.
  106.      */
  107.     SetType(gTransferType);
  108.  
  109.     xp->getBlock = BinaryGetProc;
  110.     xp->putBlock = BinaryPutProc;
  111.     /* xp->inStream = gDataSocket;  RDataCmd fills this in when it gets it. */
  112.  
  113.     /* Send the request and do the transfer. */
  114.     result = RDataCmd(xp, "RETR %s", xp->remoteFileName);
  115.  
  116.     return (result);
  117. }    /* BinaryGet */
  118.  
  119.  
  120.  
  121.  
  122.  
  123. int AsciiGet(XferSpecPtr xp)
  124. {
  125.     int result;
  126.  
  127.     /* This is supposed to be done previously, if you wanted accurate
  128.      * file sizes from GetDateAndSize.
  129.      */
  130.     SETASCII;
  131.     
  132.     /* Setup the parameter block to give to RDataCmd. */
  133.     xp->getBlock = AsciiGetRemoteProc;
  134.     xp->putBlock = AsciiPutLocalProc;
  135.     /* xp->inStream = gDataSocket;  RDataCmd fills this in when it gets it. */
  136.  
  137.     /* Send the request and do the transfer. */
  138.     result = RDataCmd(xp, "RETR %s", xp->remoteFileName);
  139.  
  140.     return (result);
  141. }    /* AsciiGet */
  142.  
  143.  
  144.  
  145. /* From the pathname given in remoteName, get a local filename for
  146.  * the current local directory.  Then open the actual file for writing.
  147.  */
  148. static
  149. void GetLocalName(GetOptionsPtr gopt, string localName)
  150. {
  151.     char *cp;
  152.  
  153.     if ((cp = gopt->lName) == NULL) {
  154.         /* We're supposed to pick it. */
  155.         cp = strrchr(gopt->rName, '/');
  156.         if (cp == NULL)
  157.             cp = gopt->rName;
  158.         else
  159.             cp++;
  160.     }
  161.     gopt->lName = Strncpy(localName, cp, sizeof(string));
  162. }    /* GetLocalName */
  163.  
  164.  
  165.  
  166.  
  167. void SetLocalFileTimes(int doUTime, time_t remoteModTime, char *lname)
  168. {
  169.     struct utimbuf ut;
  170.  
  171.     /* Restore the modifcation date of the new file to
  172.      * what it was on the remote host, if possible.
  173.      */
  174.     if ((doUTime == kDoUTime) && (remoteModTime != kModTimeUnknown)) {
  175.         time(&ut.actime);
  176.         ut.modtime = remoteModTime;
  177.         (void) utime(lname, &ut);
  178.     }
  179. }    /* SetLocalFileTimes */
  180.  
  181.  
  182.  
  183.  
  184. int TruncReOpenReceiveFile(XferSpecPtr xp)
  185. {
  186.     int fd;
  187.     
  188.     close(xp->outStream);
  189.     fd = open(xp->localFileName, O_WRONLY | O_TRUNC | O_CREAT,
  190.                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  191.     if (fd < 0) {
  192.         /* Should never get here, since we were able to open this
  193.          * very file for appending earlier.
  194.          */
  195.         Error(kDoPerror, "Can't re-open local file %s.\n", xp->localFileName);
  196.         
  197.         /* Try to give it something to write to anyway. */
  198.         fd = open("/dev/null", O_WRONLY);
  199.         xp->outStream = fd;
  200.         
  201.         if (fd < 0) {
  202.             /* Don't core, please. */
  203.             Exit(kExitPanic);
  204.         }
  205.         return (-1);
  206.     }
  207.     xp->outStream = fd;
  208.     return (0);
  209. }    /* TruncReOpenReceiveFile */
  210.  
  211.  
  212.  
  213.  
  214. int DoGet(GetOptionsPtr gopt)
  215. {
  216.     int fd;
  217.     int result;
  218.     string local;
  219.     long fileSize;
  220.     time_t modifTime;
  221.     int doReports;
  222.     struct stat st;
  223.     size_t restartPt;
  224.     char *mode;
  225.     time_t now;
  226.     XferSpecPtr xp;
  227.  
  228.     if (gTransferType == 'A') {
  229.         /* Have to set the type here, because GetDateAndSize() may
  230.          * use the SIZE command, and the result of that depends
  231.          * on the current transfer type setting.
  232.          */
  233.         SETASCII;
  234.     } else {
  235.         SetType(gTransferType);
  236.     }
  237.     
  238.     /* See if we can get some info about the file first. */
  239.     fileSize = GetDateAndSize(gopt->rName, &modifTime);
  240.     restartPt = SZ(0);
  241.     doReports = 0;
  242.  
  243.     if (gopt->outputMode == kDumpToStdout) {
  244.         fd = gStdout;
  245.         STRNCPY(local, kLocalFileIsStdout);
  246.         /* Don't have progress reports going if we're piping or
  247.          * dumping to the screen.
  248.          */
  249.     } else {
  250.         GetLocalName(gopt, local);
  251.         mode = "w";
  252.         if (stat(local, &st) == 0) {
  253.             /* File exists on the local host.  We must decide whether
  254.              * we really want to fetch this file, since we might have
  255.              * it here already.  But when in doubt, we will go ahead
  256.              * and fetch the file.
  257.              */
  258.             if (gopt->forceReget) {
  259.                 /* If the local file is smaller, then we
  260.                  * should attempt to restart the transfer
  261.                  * from where we left off.
  262.                  */
  263.                 if ((st.st_size < fileSize) || (fileSize == kSizeUnknown)) {
  264.                     restartPt = SZ(st.st_size);
  265.                     mode = "a";
  266.                     DebugMsg("Manually continuing local file %s.", local);
  267.                 } else {
  268.                     PrintF("Already have %s with size %lu.\n", gopt->rName, fileSize);
  269.                     return (0);
  270.                 }
  271.             } else if (!gopt->overwrite) {
  272.                 if (modifTime != kModTimeUnknown) {
  273.                     /* We know the date of the remote file. */
  274.                     DebugMsg("Local file %s has size %lu and is dated %s",
  275.                         local,
  276.                         (unsigned long) st.st_size,
  277.                         ctime(&st.st_mtime)
  278.                     );
  279.                     if (modifTime < st.st_mtime) {
  280.                         /* Remote file is older than existing local file. */
  281.                         PrintF("Already have %s.\n", gopt->rName);
  282.                         return (0);
  283.                     } else if (modifTime == st.st_mtime) {
  284.                         /* Remote file is same age. */
  285.                         if (fileSize != kSizeUnknown) {
  286.                             /* If the local file is smaller, then we
  287.                              * should attempt to restart the transfer
  288.                              * from where we left off, since we the remote
  289.                              * file has the same date.
  290.                              */
  291.                             if (st.st_size < fileSize) {
  292.                                 restartPt = SZ(st.st_size);
  293.                                 mode = "a";
  294.                             } else if (st.st_size == fileSize) {
  295.                                 PrintF("Already have %s.\n", gopt->rName);
  296.                                 return (0);
  297.                             } else {
  298.                                 DebugMsg("Overwriting %s; local file has same date,\n",
  299.                                     gopt->lName);
  300.                                 DebugMsg("but local file is larger, so fetching remote version anyway.\n");
  301.                             }
  302.                         } else {
  303.                             DebugMsg("Overwriting %s; local file has same date,\n",
  304.                                     gopt->lName);
  305.                             DebugMsg("but can't determine remote size, so fetching remote version anyway.\n");
  306.                         }
  307.                     } else {
  308.                         /* Remote file is more recent.  Fetch the
  309.                          * whole file.
  310.                          */
  311.                         DebugMsg("Overwriting %s; remote was newer.\n",
  312.                             gopt->lName);
  313.                     }
  314.                 } else {
  315.                     /* We don't know the date of the file.
  316.                      * We won't be able to safely assume anything about
  317.                      * the remote file.  It is legal to have a more
  318.                      * recent remote file (which we don't know), with a
  319.                      * smaller (or greater, or equal even) size.  We
  320.                      * will just have to fetch it no matter what.
  321.                      */
  322.                     DebugMsg("Overwriting %s; couldn't determine remote file date.\n",
  323.                         gopt->lName);
  324.                 }
  325.             } else {
  326.                 DebugMsg("Explicitly overwriting %s.\n", gopt->lName);
  327.             }
  328.         } else {
  329.             /* We don't have a local file with the same name as the remote,
  330.              * but we may also want to avoid doing the transfer of this
  331.              * file.  For example, this is where we check the remote
  332.              * file's date if we were told to only get files which are
  333.              * less than X days old.
  334.              */
  335.             if (gopt->newer > 0) {
  336.                 time(&now);
  337.                 if ((unsigned long) (now - (gopt->newer * 86400)) > modifTime) {
  338.                     DebugMsg("Skipping %s, older than %d days.\n",
  339.                         gopt->rName, gopt->newer);
  340.                     return (0);
  341.                 }
  342.             }
  343.         }
  344.         if (*mode == 'w')
  345.             fd = open(local, O_WRONLY | O_TRUNC | O_CREAT,
  346.                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  347.         else
  348.             fd = open(local, O_WRONLY | O_APPEND | O_CREAT,
  349.                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  350.         if (fd < 0) {
  351.             Error(kDoPerror, "Can't open local file %s.\n", local);
  352.             return (-1);
  353.         }
  354.         doReports = gopt->doReports;
  355.     }
  356.  
  357.     xp = InitXferSpec();
  358.     xp->netMode = kNetReading;
  359.     xp->outStream = fd;
  360.     
  361.     /* This group is needed for the progress reporting and logging stuff.
  362.      * Otherwise, it isn't that important.
  363.      */
  364.     xp->doReports = doReports;
  365.     xp->localFileName = local;
  366.     xp->remoteFileName = gopt->rName;
  367.     xp->expectedSize = fileSize;
  368.     xp->startPoint = restartPt;
  369.     xp->doUTime = gopt->doUTime;
  370.     xp->remoteModTime = modifTime;
  371.     
  372.     if (gTransferType == 'A') {
  373.         result = AsciiGet(xp);
  374.     } else {        
  375.         result = BinaryGet(xp);
  376.     }
  377.  
  378.     if (fd != gStdout) {
  379.         (void) close(fd);
  380.         if ((result < 0) && (xp->bytesTransferred < 1L)) {
  381.             /* An error occurred, and we didn't transfer anything,
  382.              * so remove empty file we just made.
  383.              */
  384.             (void) UNLINK(local);
  385.         } else {
  386.             /* Restore the modifcation date of the new file to
  387.              * what it was on the remote host, if possible.
  388.              */
  389.             SetLocalFileTimes(gopt->doUTime, modifTime, local);
  390.         }
  391.     }
  392.  
  393.     DoneWithXferSpec(xp);
  394.     return (result);
  395. }    /* DoGet */
  396.  
  397.  
  398.  
  399.  
  400. void InitGetOutputMode(GetOptionsPtr gopt, int outputMode)
  401. {
  402.     gopt->outputMode = outputMode;
  403.     if (outputMode == kSaveToDisk) {
  404.         gopt->doUTime = gMayUTime;
  405.         gopt->doReports = 1;
  406.     } else {
  407.         gopt->doUTime = kDontUTime;    
  408.         gopt->doReports = 0;
  409.     }
  410. }    /* InitGetOutputMode */
  411.  
  412.  
  413.  
  414.  
  415. void InitGetOptions(GetOptionsPtr gopt)
  416. {
  417.     PTRZERO(gopt, sizeof(GetOptions));
  418. }    /* InitGetOptions */
  419.  
  420.  
  421.  
  422.  
  423. int SetGetOption(GetOptionsPtr gopt, int opt, char *optArg)
  424. {
  425.     int i;
  426.  
  427.     switch (opt) {        
  428.         case 'C':    /* Force continuation */
  429.             gopt->forceReget = 1;
  430.             break;
  431.         case 'f':    /* Force overwrite (no reget, no newer) */
  432.             gopt->overwrite = 1;
  433.             break;
  434.         case 'G':    /* No glob */
  435.             gopt->noGlob = 1;
  436.             break;
  437.         case 'R':    /* Recursive */
  438.             gopt->recursive = 1;
  439.             break;
  440.         case 'n':    /* Get files if not X days or newer.  */
  441.             i = atoi(optArg);
  442.             if (i <= 0) {
  443.                 EPrintF("Option to -n must be greater than zero.\n");
  444.                 return (kUsageErr);
  445.             }
  446.             gopt->newer = i;
  447.             break;
  448.         case 'z':    /* Get one file x, and save as y. */
  449.             gopt->saveAs = 1;
  450.             break;
  451.         default:
  452.             return (kUsageErr);
  453.     }
  454.     return (kNoErr);
  455. }    /* SetGetOption */
  456.  
  457.  
  458.  
  459.  
  460. int GetGetOptions(int argc, char **argv, GetOptionsPtr gopt)
  461. {
  462.     int opt;
  463.  
  464.     /* When this is called, we are always writing to disk.
  465.      * In other words, we have no colon-mode to worry about.
  466.      */
  467.     InitGetOptions(gopt);
  468.     InitGetOutputMode(gopt, kSaveToDisk);
  469.     
  470.     /* Tell Getopt() that we want to start over with a new command. */
  471.     GetoptReset();
  472.     while ((opt = Getopt(argc, argv, "CfGRn:z")) >= 0) {
  473.         if (SetGetOption(gopt, opt, gOptArg) == kUsageErr)
  474.             return (kUsageErr);
  475.     }
  476.     return (kNoErr);
  477. }    /* GetGetOptions */
  478.  
  479.  
  480.  
  481.  
  482. #ifdef HAVE_SYMLINK
  483. static
  484. int GetSymLinkInfo(char *dst, size_t siz, char *rLink)
  485. {
  486.     LineList fileList;
  487.     char *cp;
  488.     int result;
  489.  
  490.     result = -1;
  491.     *dst = '\0';
  492.     InitLineList(&fileList);
  493.     ListToMemory(&fileList, "LIST", kListDirNamesOnlyMode, rLink);
  494.     if (fileList.first != NULL) {
  495.         cp = fileList.first->line;
  496.         *cp++ = '\0';
  497.         for (cp += strlen(cp) - 1; ; cp--) {
  498.             if (*cp == '\0')
  499.                 goto done;
  500.             if ((cp[0] == '>') && (cp[-1] == '-'))
  501.                 break;
  502.         }
  503.         (void) Strncpy(dst, cp + 2, siz);
  504.         result = 0;
  505.     }
  506. done:
  507.     DisposeLineListContents(&fileList);
  508.     return (result);
  509. }    /* GetSymLinkInfo */
  510. #endif    /* HAVE_SYMLINK */
  511.  
  512.  
  513.  
  514. int GetDir(GetOptionsPtr gopt, char *dName, char *rRoot, char *lRoot)
  515. {
  516.     LineList dirFiles;
  517.     LinePtr dirFile;
  518.     char *rd;    /* Remote directory path. */
  519.     char *ld;    /* Local directory path. */
  520.     char *rf;    /* Complete remote pathname for an item. */
  521.     char *lf;    /* Complete local pathname for an item. */ 
  522.     char *sl;    /* What a symlink points to. */
  523.     char *iName;
  524.     int fType;
  525.  
  526.     rd = NULL;
  527.     ld = NULL;
  528.     rf = NULL;
  529.     lf = NULL;
  530.     
  531.     if ((rd = StrDup(rRoot)) == NULL)
  532.         goto fail;
  533.     if ((rd = PtrCatSlash(rd, dName)) == NULL)
  534.         goto fail;
  535.  
  536.     if ((ld = StrDup(lRoot)) == NULL)
  537.         goto fail;
  538.     if ((ld = PtrCatSlash(ld, dName)) == NULL)
  539.         goto fail;
  540.     
  541.     /* Create this directory on the local host first. */
  542.     if (MkDirs(ld)) {
  543.         EPrintF("Could not create directory '%s.'\n", ld);
  544.         goto fail;
  545.     }
  546.     
  547.     /* Get the names of all files and subdirs. */
  548.     InitLineList(&dirFiles);
  549.     GetFileList(&dirFiles, rd);
  550.  
  551.     /* Get all the files first. */
  552.     for (dirFile = dirFiles.first; dirFile != NULL; dirFile = dirFile->next) {
  553.         fType = (int) dirFile->line[0];
  554.         if ((fType == '-') || (fType == 'l')) {
  555.             iName = dirFile->line + 1;
  556.             if ((rf = StrDup(rd)) == NULL)
  557.                 goto fail;
  558.             if ((rf = PtrCatSlash(rf, iName)) == NULL)
  559.                 goto fail;
  560.             if ((lf = StrDup(ld)) == NULL)
  561.                 goto fail;
  562.             if ((lf = PtrCatSlash(lf, iName)) == NULL)
  563.                 goto fail;
  564.             if (fType == '-') {
  565.                 gopt->rName = rf;
  566.                 gopt->lName = lf;
  567.                 DoGet(gopt);
  568.             } else {
  569. #ifdef HAVE_SYMLINK
  570.                 sl = (char *) malloc(SZ(512));
  571.                 if (sl != NULL) {
  572.                     if (GetSymLinkInfo(sl, SZ(512), rf) == 0)
  573.                         (void) symlink(sl, lf);
  574.                     free(sl);
  575.                 }
  576. #endif    /* HAVE_SYMLINK */
  577.             }
  578.             free(rf);
  579.             free(lf);
  580.             rf = NULL;
  581.             lf = NULL;
  582.         }
  583.         if (gXferAbortFlag == SIGINT)
  584.             break;    /* Don't get rest of files if you interrupted. */
  585.     }
  586.     
  587.     /* Now get subdirectories. */
  588.     for (dirFile = dirFiles.first; dirFile != NULL; dirFile = dirFile->next) {
  589.         if (gXferAbortFlag == SIGINT)
  590.             break;    /* Don't get rest of files if you interrupted. */
  591.         fType = (int) dirFile->line[0];
  592.         if (fType == 'd') {
  593.             iName = dirFile->line + 1;
  594.             if (GetDir(gopt, iName, rd, ld) < 0)
  595.                 break;
  596.         }
  597.     }
  598.  
  599.     free(ld);
  600.     free(rd);
  601.     DisposeLineListContents(&dirFiles);
  602.     return (0);
  603.     
  604. fail:
  605.     if (rd != NULL)
  606.         free(rd);
  607.     if (ld != NULL)
  608.         free(ld);
  609.     if (rf != NULL)
  610.         free(rf);
  611.     if (lf != NULL)
  612.         free(lf);
  613.     return (-1);
  614. }    /* GetDir */
  615.  
  616.  
  617.  
  618.  
  619. int RemoteFileType(char *fName)
  620. {
  621.     LineList fileList;
  622.     char *cp;
  623.     int result;
  624.     int i;
  625.  
  626.     result = 0;
  627.     InitLineList(&fileList);
  628.     ListToMemory(&fileList, "LIST", kListDirNamesOnlyMode, fName);
  629.     if (fileList.first != NULL) {
  630.         cp = fileList.first->line;
  631.         /* Do a quick check and see if it looks like a unix ls line. */
  632.         for (i=1; i<=3; i++)
  633.             if ((cp[i] != 'r') && (cp[i] != 'w') && (cp[i] != 'x') && (cp[i] != '-'))
  634.                 goto done;
  635.         result = (int) cp[0];
  636.     }
  637. done:
  638.     DisposeLineListContents(&fileList);
  639.     return (result);
  640. }    /* RemoteFileType */
  641.  
  642.  
  643.  
  644.  
  645.  
  646. int DoGetWithGlobbingAndRecursion(GetOptionsPtr gopt)
  647. {
  648.     int err;
  649.     LineList globFiles;
  650.     LinePtr globFile;
  651.     char *cp;
  652.     int fType;
  653.     int result;
  654.     longstring rcwd;
  655.         
  656.     err = 0;
  657.     InitLineList(&globFiles);
  658.     RemoteGlob(&globFiles, gopt->rName, kListNoFlags);
  659.     
  660.     for (globFile = globFiles.first; globFile != NULL;
  661.         globFile = globFile->next)
  662.     {
  663.         if (gXferAbortFlag == SIGINT)
  664.             break;    /* Don't get rest of files if you interrupted. */
  665.         if (gopt->recursive) {
  666.             fType = RemoteFileType(globFile->line);
  667.             if (fType == 'd') {
  668.                 if ((cp = strrchr(globFile->line, '/')) != NULL) {
  669.                     /* If the user said something like
  670.                      * "get -R /pub/a/b/c/d" we want to just write the
  671.                      * contents of the 'd' as a subdirectory of the local
  672.                      * directory, and not create ./pub, ./pub/a, etc.
  673.                      */
  674.                     STRNCPY(rcwd, gRemoteCWD);
  675.                     *cp++ = '\0';
  676.                     if (DoChdir(globFile->line) == 0) {
  677.                         GetDir(gopt, cp, gRemoteCWD, gLocalCWD);
  678.                     }
  679.                     /* Restore the directory we were in before. */
  680.                     (void) DoChdir(rcwd);
  681.                 } else {
  682.                     /* Otherwise, the user gave a simple path, so it was
  683.                      * something like "get -R pub"
  684.                      */
  685.                     GetDir(gopt, globFile->line, gRemoteCWD, gLocalCWD);
  686.                 }
  687.             } else if (fType == 'l') {
  688.                 EPrintF("Ignoring symbolic link '%s'\n",
  689.                     globFile->line);
  690.             } else if (fType == '-') {
  691.                 goto regFile;
  692.             }
  693.         } else {
  694. regFile:
  695.             gopt->rName = globFile->line;
  696.             gopt->lName = NULL;    /* Make it later. */
  697.             result = DoGet(gopt);
  698.             if (result < 0)
  699.                 err = -1;
  700.         }
  701.     }
  702.     DisposeLineListContents(&globFiles);
  703.     
  704.     return (err);
  705. }    /* DoGetWithGlobbingAndRecursion */
  706.  
  707.  
  708.  
  709.  
  710. /* Fetch one or more remote files. */
  711. int GetCmd(int argc, char **argv)
  712. {
  713.     int i, result, errs;
  714.     GetOptions gopt;
  715.     
  716.     
  717.     if (GetGetOptions(argc, argv, &gopt) == kUsageErr)
  718.         return (kUsageErr);
  719.         
  720.     argv += gOptInd;
  721.     argc -= gOptInd;
  722.     errs = 0;
  723.  
  724.     if (gopt.noGlob || gopt.saveAs) {
  725.         for (i=0; i<argc; i++) {
  726.             gopt.rName = argv[i];
  727.             if (gopt.saveAs) {
  728.                 if (++i < argc)
  729.                     gopt.lName = argv[i];    /* Use this name. */
  730.                 else
  731.                     return (kUsageErr);
  732.             } else {
  733.                 gopt.lName = NULL;    /* Make it later. */
  734.             }
  735.             result = DoGet(&gopt);
  736.             if (gXferAbortFlag == SIGINT)
  737.                 break;    /* Don't get rest of files if you interrupted. */
  738.             if (result < 0)
  739.                 --errs;
  740.         }
  741.     } else {
  742.         for (i=0; i<argc; i++) {
  743.             gopt.rName = argv[i];
  744.             errs += DoGetWithGlobbingAndRecursion(&gopt);
  745.             if (gXferAbortFlag == SIGINT)
  746.                 break;    /* Don't get rest of files if you interrupted. */
  747.         }
  748.     }
  749.     
  750.     return (errs);
  751. }    /* GetCmd */
  752.  
  753.  
  754.  
  755.  
  756. long CatGetProc(char *buf, size_t bufsize, XferSpecPtr xp)
  757. {
  758.     long len;
  759.  
  760.     len = (long) BufferGets(buf, bufsize, xp);
  761.     return (len);
  762. }    /* CatGetProc */
  763.  
  764.  
  765.  
  766.  
  767. long CatPutProc(char *buf, size_t bufsize, XferSpecPtr xp)
  768. {
  769.     long result;
  770.     longstring buf2;
  771.  
  772.     if (isatty(xp->outStream)) {
  773.         MakeStringPrintable(buf2, (unsigned char *) buf, sizeof(buf2));
  774.         MultiLinePrintF("%s", buf2);
  775.         result = (long) bufsize;
  776.     } else {
  777.         result = (long) write(xp->outStream, buf, bufsize);
  778.         if (result != bufsize) {
  779.             return (-1L);
  780.         }
  781.     }
  782.  
  783.     return (result);
  784. }    /* CatPutProc */
  785.  
  786.  
  787.  
  788.  
  789. /* View a remote file through your pager. */
  790. int DoCat(char *remoteName)
  791. {
  792.     int result;
  793.     XferSpecPtr xp;
  794.  
  795.     MultiLineInit();
  796.  
  797.     xp = InitXferSpec();
  798.     xp->netMode = kNetReading;
  799.     xp->outStream = gStdout;
  800.     
  801.     /* This group is needed for the progress reporting and logging stuff.
  802.      * Otherwise, it isn't that important.
  803.      */
  804.     xp->doReports = kNoReports;
  805.     xp->localFileName = kLocalFileIsStdout;
  806.     xp->remoteFileName = remoteName;
  807.  
  808.     /* This is supposed to be done previously, if you wanted accurate
  809.      * file sizes from GetDateAndSize.
  810.      */
  811.     SETASCII;
  812.     
  813.     /* Setup the parameter block to give to RDataCmd. */
  814.     xp->getBlock = CatGetProc;
  815.     xp->putBlock = CatPutProc;
  816.     /* xp->inStream = gDataSocket;  RDataCmd fills this in when it gets it. */
  817.  
  818.     /* Send the request and do the transfer. */
  819.     result = RDataCmd(xp, "RETR %s", xp->remoteFileName);
  820.     DoneWithXferSpec(xp);
  821.     return (result);
  822. }    /* DoCat */
  823.  
  824.  
  825.  
  826.  
  827. /* We need to make something we can give to popen.  This is simple
  828.  * if it is a plain file, but if they wanted to page a compressed
  829.  * file we have to prepend the correct filter before the pager name.
  830.  */
  831. int MakePageCmdLine(char *cmd, size_t siz, char *remote_file)
  832. {
  833.     int useZCat;
  834.     int useGZCat;
  835.     int binaryPage;
  836.     int len;
  837.     
  838.     useZCat = 0;
  839.     useGZCat = 0;
  840.     binaryPage = 0;
  841.     
  842.     len = (int) strlen(remote_file);
  843.  
  844.     if (len > 2) {
  845.              if (remote_file[len - 2] == '.') {
  846.             /* Check for .Z files. */
  847.             if (remote_file[len-1] == 'Z')
  848.                 useZCat = 1;
  849.  
  850.             /* Check for .z (gzip) files. */
  851.             if (remote_file[len - 1] == 'z')
  852.                 useGZCat = 1;
  853.         }
  854.     }
  855.  
  856.     if (len > 3) {
  857.         /* Check for ".gz" (gzip) files. */
  858.         if (STREQ(remote_file + len - 3, ".gz"))
  859.             useGZCat = 1;
  860.     }
  861.  
  862.     /* Run compressed remote files through zcat, then the pager.
  863.      * If GZCAT was defined, we also try paging gzipped files.
  864.      */    
  865.     if (useGZCat) {
  866. #ifdef GZCAT
  867.         (void) Strncpy(cmd, GZCAT, siz);
  868.         (void) Strncat(cmd, " | ", siz);
  869.         (void) Strncat(cmd, gPager, siz);
  870. #else
  871.         PrintF("NcFTP wasn't configured to page gzipped files.\n");
  872. #endif
  873.     } else if (useZCat) {
  874. #ifdef ZCAT
  875.         (void) Strncpy(cmd, ZCAT, siz);
  876.         (void) Strncat(cmd, " | ", siz);
  877.         (void) Strncat(cmd, gPager, siz);
  878. #else
  879. #    ifdef GZCAT
  880.         /* gzcat can do .Z's also. */
  881.         (void) Strncpy(cmd, GZCAT, siz);
  882.         (void) Strncat(cmd, " | ", siz);
  883.         (void) Strncat(cmd, gPager, siz);
  884. #    else
  885.         PrintF("NcFTP wasn't configured to page compressed files.\n");
  886. #    endif
  887. #endif
  888.     } else {
  889.         (void) Strncpy(cmd, gPager, siz);
  890.     }
  891.  
  892.     binaryPage = (useZCat || useGZCat);
  893.     return (binaryPage);
  894. }    /* MakePageCmdLine */
  895.  
  896.  
  897.  
  898.  
  899. /* View a remote file through your pager. */
  900. int DoPage(char *remoteName)
  901. {
  902.     FILE *fp;
  903.     int result;
  904.     longstring pageCmd;
  905.     int binaryPage;
  906.     XferSpecPtr xp;
  907.  
  908.     binaryPage = MakePageCmdLine(pageCmd, sizeof(pageCmd), remoteName);
  909.     DebugMsg("%s page: %s\n",
  910.         binaryPage ? "Binary" : "Ascii",
  911.         pageCmd
  912.     );
  913.  
  914.     fp = POpen(pageCmd, "w", 1);
  915.     if (fp == NULL) {
  916.         Error(kDoPerror, "Could not run %s.\n", pageCmd);
  917.         return -1;
  918.     }
  919.  
  920.     xp = InitXferSpec();
  921.     xp->netMode = kNetReading;
  922.     xp->outStream = fileno(fp);
  923.     
  924.     /* This group is needed for the progress reporting and logging stuff.
  925.      * Otherwise, it isn't that important.
  926.      */
  927.     xp->doReports = kNoReports;
  928.     xp->localFileName = kLocalFileIsStdout;
  929.     xp->remoteFileName = remoteName;
  930.  
  931.     if (!binaryPage) {
  932.         /* Try to use text mode for paging, so newlines get converted. */
  933.         result = AsciiGet(xp);
  934.     } else {
  935.         /* Must use binary, or else zcat will complain about corrupted
  936.          * input files, since we'd be converting carriage-returns.
  937.          */
  938.         result = BinaryGet(xp);
  939.     }
  940.     DoneWithXferSpec(xp);
  941.     (void) PClose(fp);
  942.     RestoreScreen(1);
  943.     return (result);
  944. }    /* DoPage */
  945.  
  946.  
  947.  
  948.  
  949. /* View one or more remote files through your pager. */
  950. int PageCmd(int argc, char **argv)
  951. {
  952.     int i, result, errs;
  953.     LineList globFiles;
  954.     LinePtr globFile;
  955.     char *pagerProg;
  956.  
  957.     if (STREQ(argv[1], "-b") && (gWinInit > 0) && (argc > 2)) {
  958.         /* A hack to let you use the built-in pager like you
  959.          * can with the lpage command.
  960.          */
  961.         pagerProg = NULL;    /* Use built-in */
  962.         i = 2;
  963.     } else {
  964.         if (gPager[0] == '\0') {
  965.             EPrintF("You haven't specified a program to use as a pager.\n");
  966.             EPrintF("You can set this from the preferences screen (prefs command).\n");
  967.             return -1;
  968.         }
  969.         pagerProg = gPager;
  970.         i = 1;
  971.     }
  972.  
  973.     for (errs=0; i<argc; i++) {
  974.         InitLineList(&globFiles);
  975.         RemoteGlob(&globFiles, argv[i], kListNoFlags);
  976.         for (globFile = globFiles.first; globFile != NULL;
  977.             globFile = globFile->next)
  978.         {
  979.             if (pagerProg == NULL)
  980.                 result = DoCat(globFile->line);
  981.             else
  982.                 result = DoPage(globFile->line);
  983.             if (result < 0)
  984.                 --errs;
  985.             if (gXferAbortFlag == SIGINT)
  986.                 break;    /* Don't get rest of files if you interrupted. */
  987.         }
  988.         DisposeLineListContents(&globFiles);
  989.     }
  990.  
  991.     return (errs);
  992. }    /* PageCmd */
  993.  
  994.  
  995.  
  996.  
  997. /* View one or more remote files through your pager. */
  998. int CatCmd(int argc, char **argv)
  999. {
  1000.     int i, result, errs;
  1001.     LineList globFiles;
  1002.     LinePtr globFile;
  1003.  
  1004.     MultiLineInit();
  1005.     for (i=1, errs=0; i<argc; i++) {
  1006.         InitLineList(&globFiles);
  1007.         RemoteGlob(&globFiles, argv[i], kListNoFlags);
  1008.         for (globFile = globFiles.first; globFile != NULL;
  1009.             globFile = globFile->next)
  1010.         {
  1011.             result = DoCat(globFile->line);
  1012.             if (result < 0)
  1013.                 --errs;
  1014.             if (gXferAbortFlag == SIGINT)
  1015.                 break;    /* Don't get rest of files if you interrupted. */
  1016.             if (argc > 2)
  1017.                 MultiLinePrintF("### End of file %s ###\n", globFile->line);
  1018.         }
  1019.         DisposeLineListContents(&globFiles);
  1020.     }
  1021.  
  1022.     return (errs);
  1023. }    /* CatCmd */
  1024.