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 >
Wrap
C/C++ Source or Header
|
1996-09-28
|
23KB
|
1,024 lines
/* Get.c */
#include "Sys.h"
#include <signal.h>
#ifdef HAVE_UTIME_H
# include <utime.h>
#else
struct utimbuf {time_t actime; time_t modtime;};
#endif
#include "Util.h"
#include "RCmd.h"
#include "Xfer.h"
#include "Cmds.h"
#include "Glob.h"
#include "Get.h"
#include "DateSize.h"
#include "List.h"
#include "Getopt.h"
int gMayUTime = kDoUTime; /* User variable. */
extern longstring gPager;
extern longstring gRemoteCWD;
extern longstring gLocalCWD;
extern int gTransferType, gXferAbortFlag;
extern int gStdout, gWinInit;
extern char *gOptArg;
extern int gOptInd;
long BinaryGetProc(char *buf, size_t bufsize, XferSpecPtr xp)
{
long len;
len = (long) ReadOrTimeout(xp->inStream, buf, bufsize);
return (len);
} /* BinaryGetProc */
long BinaryPutProc(char *buf, size_t bufsize, XferSpecPtr xp)
{
long len;
len = (long) WriteOrTimeout(xp->outStream, buf, bufsize);
if (len != (long)bufsize) {
if (len >= 0L) /* Could already be kTimeoutErr. */
len = -1L;
}
return (len);
} /* BinaryPutProc */
long AsciiGetRemoteProc(char *buf, size_t bufsize, XferSpecPtr xp)
{
long len;
len = (long) ReadOrTimeout(xp->inStream, buf, bufsize);
return (len);
} /* AsciiGetRemoteProc */
long AsciiPutLocalProc(char *buf, size_t bufsize, XferSpecPtr xp)
{
long len, len2;
char *i, *o;
long ct;
/* In ASCII mode, all end-of-lines are denoted by CR/LF. For
* UNIX, we don't want that, we want just LFs, so skip all the
* CR's.
*/
for (len = bufsize, i = o = buf, ct = 0; ct < len; ct++, ++i) {
if (*i != '\r')
*o++ = *i;
}
len = o - buf;
len2 = (long) WriteOrTimeout(xp->outStream, buf, len);
if (len2 != len) {
if (len2 >= 0L) /* Could already be kTimeoutErr. */
len2 = -1L;
}
return (len2);
} /* AsciiPutLocalProc */
int BinaryGet(XferSpecPtr xp)
{
int result;
/* This is supposed to be done previously, if you wanted accurate
* file sizes from GetDateAndSize. We don't do a SETBINARY here.
* Instead, we set it to gTransferType, which we know is not
* ascii. Most often this will mean binary mode, but perhaps we're
* dealing with a tenex machine.
*/
SetType(gTransferType);
xp->getBlock = BinaryGetProc;
xp->putBlock = BinaryPutProc;
/* xp->inStream = gDataSocket; RDataCmd fills this in when it gets it. */
/* Send the request and do the transfer. */
result = RDataCmd(xp, "RETR %s", xp->remoteFileName);
return (result);
} /* BinaryGet */
int AsciiGet(XferSpecPtr xp)
{
int result;
/* This is supposed to be done previously, if you wanted accurate
* file sizes from GetDateAndSize.
*/
SETASCII;
/* Setup the parameter block to give to RDataCmd. */
xp->getBlock = AsciiGetRemoteProc;
xp->putBlock = AsciiPutLocalProc;
/* xp->inStream = gDataSocket; RDataCmd fills this in when it gets it. */
/* Send the request and do the transfer. */
result = RDataCmd(xp, "RETR %s", xp->remoteFileName);
return (result);
} /* AsciiGet */
/* From the pathname given in remoteName, get a local filename for
* the current local directory. Then open the actual file for writing.
*/
static
void GetLocalName(GetOptionsPtr gopt, string localName)
{
char *cp;
if ((cp = gopt->lName) == NULL) {
/* We're supposed to pick it. */
cp = strrchr(gopt->rName, '/');
if (cp == NULL)
cp = gopt->rName;
else
cp++;
}
gopt->lName = Strncpy(localName, cp, sizeof(string));
} /* GetLocalName */
void SetLocalFileTimes(int doUTime, time_t remoteModTime, char *lname)
{
struct utimbuf ut;
/* Restore the modifcation date of the new file to
* what it was on the remote host, if possible.
*/
if ((doUTime == kDoUTime) && (remoteModTime != kModTimeUnknown)) {
time(&ut.actime);
ut.modtime = remoteModTime;
(void) utime(lname, &ut);
}
} /* SetLocalFileTimes */
int TruncReOpenReceiveFile(XferSpecPtr xp)
{
int fd;
close(xp->outStream);
fd = open(xp->localFileName, O_WRONLY | O_TRUNC | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
/* Should never get here, since we were able to open this
* very file for appending earlier.
*/
Error(kDoPerror, "Can't re-open local file %s.\n", xp->localFileName);
/* Try to give it something to write to anyway. */
fd = open("/dev/null", O_WRONLY);
xp->outStream = fd;
if (fd < 0) {
/* Don't core, please. */
Exit(kExitPanic);
}
return (-1);
}
xp->outStream = fd;
return (0);
} /* TruncReOpenReceiveFile */
int DoGet(GetOptionsPtr gopt)
{
int fd;
int result;
string local;
long fileSize;
time_t modifTime;
int doReports;
struct stat st;
size_t restartPt;
char *mode;
time_t now;
XferSpecPtr xp;
if (gTransferType == 'A') {
/* Have to set the type here, because GetDateAndSize() may
* use the SIZE command, and the result of that depends
* on the current transfer type setting.
*/
SETASCII;
} else {
SetType(gTransferType);
}
/* See if we can get some info about the file first. */
fileSize = GetDateAndSize(gopt->rName, &modifTime);
restartPt = SZ(0);
doReports = 0;
if (gopt->outputMode == kDumpToStdout) {
fd = gStdout;
STRNCPY(local, kLocalFileIsStdout);
/* Don't have progress reports going if we're piping or
* dumping to the screen.
*/
} else {
GetLocalName(gopt, local);
mode = "w";
if (stat(local, &st) == 0) {
/* File exists on the local host. We must decide whether
* we really want to fetch this file, since we might have
* it here already. But when in doubt, we will go ahead
* and fetch the file.
*/
if (gopt->forceReget) {
/* If the local file is smaller, then we
* should attempt to restart the transfer
* from where we left off.
*/
if ((st.st_size < fileSize) || (fileSize == kSizeUnknown)) {
restartPt = SZ(st.st_size);
mode = "a";
DebugMsg("Manually continuing local file %s.", local);
} else {
PrintF("Already have %s with size %lu.\n", gopt->rName, fileSize);
return (0);
}
} else if (!gopt->overwrite) {
if (modifTime != kModTimeUnknown) {
/* We know the date of the remote file. */
DebugMsg("Local file %s has size %lu and is dated %s",
local,
(unsigned long) st.st_size,
ctime(&st.st_mtime)
);
if (modifTime < st.st_mtime) {
/* Remote file is older than existing local file. */
PrintF("Already have %s.\n", gopt->rName);
return (0);
} else if (modifTime == st.st_mtime) {
/* Remote file is same age. */
if (fileSize != kSizeUnknown) {
/* If the local file is smaller, then we
* should attempt to restart the transfer
* from where we left off, since we the remote
* file has the same date.
*/
if (st.st_size < fileSize) {
restartPt = SZ(st.st_size);
mode = "a";
} else if (st.st_size == fileSize) {
PrintF("Already have %s.\n", gopt->rName);
return (0);
} else {
DebugMsg("Overwriting %s; local file has same date,\n",
gopt->lName);
DebugMsg("but local file is larger, so fetching remote version anyway.\n");
}
} else {
DebugMsg("Overwriting %s; local file has same date,\n",
gopt->lName);
DebugMsg("but can't determine remote size, so fetching remote version anyway.\n");
}
} else {
/* Remote file is more recent. Fetch the
* whole file.
*/
DebugMsg("Overwriting %s; remote was newer.\n",
gopt->lName);
}
} else {
/* We don't know the date of the file.
* We won't be able to safely assume anything about
* the remote file. It is legal to have a more
* recent remote file (which we don't know), with a
* smaller (or greater, or equal even) size. We
* will just have to fetch it no matter what.
*/
DebugMsg("Overwriting %s; couldn't determine remote file date.\n",
gopt->lName);
}
} else {
DebugMsg("Explicitly overwriting %s.\n", gopt->lName);
}
} else {
/* We don't have a local file with the same name as the remote,
* but we may also want to avoid doing the transfer of this
* file. For example, this is where we check the remote
* file's date if we were told to only get files which are
* less than X days old.
*/
if (gopt->newer > 0) {
time(&now);
if ((unsigned long) (now - (gopt->newer * 86400)) > modifTime) {
DebugMsg("Skipping %s, older than %d days.\n",
gopt->rName, gopt->newer);
return (0);
}
}
}
if (*mode == 'w')
fd = open(local, O_WRONLY | O_TRUNC | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
else
fd = open(local, O_WRONLY | O_APPEND | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
Error(kDoPerror, "Can't open local file %s.\n", local);
return (-1);
}
doReports = gopt->doReports;
}
xp = InitXferSpec();
xp->netMode = kNetReading;
xp->outStream = fd;
/* This group is needed for the progress reporting and logging stuff.
* Otherwise, it isn't that important.
*/
xp->doReports = doReports;
xp->localFileName = local;
xp->remoteFileName = gopt->rName;
xp->expectedSize = fileSize;
xp->startPoint = restartPt;
xp->doUTime = gopt->doUTime;
xp->remoteModTime = modifTime;
if (gTransferType == 'A') {
result = AsciiGet(xp);
} else {
result = BinaryGet(xp);
}
if (fd != gStdout) {
(void) close(fd);
if ((result < 0) && (xp->bytesTransferred < 1L)) {
/* An error occurred, and we didn't transfer anything,
* so remove empty file we just made.
*/
(void) UNLINK(local);
} else {
/* Restore the modifcation date of the new file to
* what it was on the remote host, if possible.
*/
SetLocalFileTimes(gopt->doUTime, modifTime, local);
}
}
DoneWithXferSpec(xp);
return (result);
} /* DoGet */
void InitGetOutputMode(GetOptionsPtr gopt, int outputMode)
{
gopt->outputMode = outputMode;
if (outputMode == kSaveToDisk) {
gopt->doUTime = gMayUTime;
gopt->doReports = 1;
} else {
gopt->doUTime = kDontUTime;
gopt->doReports = 0;
}
} /* InitGetOutputMode */
void InitGetOptions(GetOptionsPtr gopt)
{
PTRZERO(gopt, sizeof(GetOptions));
} /* InitGetOptions */
int SetGetOption(GetOptionsPtr gopt, int opt, char *optArg)
{
int i;
switch (opt) {
case 'C': /* Force continuation */
gopt->forceReget = 1;
break;
case 'f': /* Force overwrite (no reget, no newer) */
gopt->overwrite = 1;
break;
case 'G': /* No glob */
gopt->noGlob = 1;
break;
case 'R': /* Recursive */
gopt->recursive = 1;
break;
case 'n': /* Get files if not X days or newer. */
i = atoi(optArg);
if (i <= 0) {
EPrintF("Option to -n must be greater than zero.\n");
return (kUsageErr);
}
gopt->newer = i;
break;
case 'z': /* Get one file x, and save as y. */
gopt->saveAs = 1;
break;
default:
return (kUsageErr);
}
return (kNoErr);
} /* SetGetOption */
int GetGetOptions(int argc, char **argv, GetOptionsPtr gopt)
{
int opt;
/* When this is called, we are always writing to disk.
* In other words, we have no colon-mode to worry about.
*/
InitGetOptions(gopt);
InitGetOutputMode(gopt, kSaveToDisk);
/* Tell Getopt() that we want to start over with a new command. */
GetoptReset();
while ((opt = Getopt(argc, argv, "CfGRn:z")) >= 0) {
if (SetGetOption(gopt, opt, gOptArg) == kUsageErr)
return (kUsageErr);
}
return (kNoErr);
} /* GetGetOptions */
#ifdef HAVE_SYMLINK
static
int GetSymLinkInfo(char *dst, size_t siz, char *rLink)
{
LineList fileList;
char *cp;
int result;
result = -1;
*dst = '\0';
InitLineList(&fileList);
ListToMemory(&fileList, "LIST", kListDirNamesOnlyMode, rLink);
if (fileList.first != NULL) {
cp = fileList.first->line;
*cp++ = '\0';
for (cp += strlen(cp) - 1; ; cp--) {
if (*cp == '\0')
goto done;
if ((cp[0] == '>') && (cp[-1] == '-'))
break;
}
(void) Strncpy(dst, cp + 2, siz);
result = 0;
}
done:
DisposeLineListContents(&fileList);
return (result);
} /* GetSymLinkInfo */
#endif /* HAVE_SYMLINK */
int GetDir(GetOptionsPtr gopt, char *dName, char *rRoot, char *lRoot)
{
LineList dirFiles;
LinePtr dirFile;
char *rd; /* Remote directory path. */
char *ld; /* Local directory path. */
char *rf; /* Complete remote pathname for an item. */
char *lf; /* Complete local pathname for an item. */
char *sl; /* What a symlink points to. */
char *iName;
int fType;
rd = NULL;
ld = NULL;
rf = NULL;
lf = NULL;
if ((rd = StrDup(rRoot)) == NULL)
goto fail;
if ((rd = PtrCatSlash(rd, dName)) == NULL)
goto fail;
if ((ld = StrDup(lRoot)) == NULL)
goto fail;
if ((ld = PtrCatSlash(ld, dName)) == NULL)
goto fail;
/* Create this directory on the local host first. */
if (MkDirs(ld)) {
EPrintF("Could not create directory '%s.'\n", ld);
goto fail;
}
/* Get the names of all files and subdirs. */
InitLineList(&dirFiles);
GetFileList(&dirFiles, rd);
/* Get all the files first. */
for (dirFile = dirFiles.first; dirFile != NULL; dirFile = dirFile->next) {
fType = (int) dirFile->line[0];
if ((fType == '-') || (fType == 'l')) {
iName = dirFile->line + 1;
if ((rf = StrDup(rd)) == NULL)
goto fail;
if ((rf = PtrCatSlash(rf, iName)) == NULL)
goto fail;
if ((lf = StrDup(ld)) == NULL)
goto fail;
if ((lf = PtrCatSlash(lf, iName)) == NULL)
goto fail;
if (fType == '-') {
gopt->rName = rf;
gopt->lName = lf;
DoGet(gopt);
} else {
#ifdef HAVE_SYMLINK
sl = (char *) malloc(SZ(512));
if (sl != NULL) {
if (GetSymLinkInfo(sl, SZ(512), rf) == 0)
(void) symlink(sl, lf);
free(sl);
}
#endif /* HAVE_SYMLINK */
}
free(rf);
free(lf);
rf = NULL;
lf = NULL;
}
if (gXferAbortFlag == SIGINT)
break; /* Don't get rest of files if you interrupted. */
}
/* Now get subdirectories. */
for (dirFile = dirFiles.first; dirFile != NULL; dirFile = dirFile->next) {
if (gXferAbortFlag == SIGINT)
break; /* Don't get rest of files if you interrupted. */
fType = (int) dirFile->line[0];
if (fType == 'd') {
iName = dirFile->line + 1;
if (GetDir(gopt, iName, rd, ld) < 0)
break;
}
}
free(ld);
free(rd);
DisposeLineListContents(&dirFiles);
return (0);
fail:
if (rd != NULL)
free(rd);
if (ld != NULL)
free(ld);
if (rf != NULL)
free(rf);
if (lf != NULL)
free(lf);
return (-1);
} /* GetDir */
int RemoteFileType(char *fName)
{
LineList fileList;
char *cp;
int result;
int i;
result = 0;
InitLineList(&fileList);
ListToMemory(&fileList, "LIST", kListDirNamesOnlyMode, fName);
if (fileList.first != NULL) {
cp = fileList.first->line;
/* Do a quick check and see if it looks like a unix ls line. */
for (i=1; i<=3; i++)
if ((cp[i] != 'r') && (cp[i] != 'w') && (cp[i] != 'x') && (cp[i] != '-'))
goto done;
result = (int) cp[0];
}
done:
DisposeLineListContents(&fileList);
return (result);
} /* RemoteFileType */
int DoGetWithGlobbingAndRecursion(GetOptionsPtr gopt)
{
int err;
LineList globFiles;
LinePtr globFile;
char *cp;
int fType;
int result;
longstring rcwd;
err = 0;
InitLineList(&globFiles);
RemoteGlob(&globFiles, gopt->rName, kListNoFlags);
for (globFile = globFiles.first; globFile != NULL;
globFile = globFile->next)
{
if (gXferAbortFlag == SIGINT)
break; /* Don't get rest of files if you interrupted. */
if (gopt->recursive) {
fType = RemoteFileType(globFile->line);
if (fType == 'd') {
if ((cp = strrchr(globFile->line, '/')) != NULL) {
/* If the user said something like
* "get -R /pub/a/b/c/d" we want to just write the
* contents of the 'd' as a subdirectory of the local
* directory, and not create ./pub, ./pub/a, etc.
*/
STRNCPY(rcwd, gRemoteCWD);
*cp++ = '\0';
if (DoChdir(globFile->line) == 0) {
GetDir(gopt, cp, gRemoteCWD, gLocalCWD);
}
/* Restore the directory we were in before. */
(void) DoChdir(rcwd);
} else {
/* Otherwise, the user gave a simple path, so it was
* something like "get -R pub"
*/
GetDir(gopt, globFile->line, gRemoteCWD, gLocalCWD);
}
} else if (fType == 'l') {
EPrintF("Ignoring symbolic link '%s'\n",
globFile->line);
} else if (fType == '-') {
goto regFile;
}
} else {
regFile:
gopt->rName = globFile->line;
gopt->lName = NULL; /* Make it later. */
result = DoGet(gopt);
if (result < 0)
err = -1;
}
}
DisposeLineListContents(&globFiles);
return (err);
} /* DoGetWithGlobbingAndRecursion */
/* Fetch one or more remote files. */
int GetCmd(int argc, char **argv)
{
int i, result, errs;
GetOptions gopt;
if (GetGetOptions(argc, argv, &gopt) == kUsageErr)
return (kUsageErr);
argv += gOptInd;
argc -= gOptInd;
errs = 0;
if (gopt.noGlob || gopt.saveAs) {
for (i=0; i<argc; i++) {
gopt.rName = argv[i];
if (gopt.saveAs) {
if (++i < argc)
gopt.lName = argv[i]; /* Use this name. */
else
return (kUsageErr);
} else {
gopt.lName = NULL; /* Make it later. */
}
result = DoGet(&gopt);
if (gXferAbortFlag == SIGINT)
break; /* Don't get rest of files if you interrupted. */
if (result < 0)
--errs;
}
} else {
for (i=0; i<argc; i++) {
gopt.rName = argv[i];
errs += DoGetWithGlobbingAndRecursion(&gopt);
if (gXferAbortFlag == SIGINT)
break; /* Don't get rest of files if you interrupted. */
}
}
return (errs);
} /* GetCmd */
long CatGetProc(char *buf, size_t bufsize, XferSpecPtr xp)
{
long len;
len = (long) BufferGets(buf, bufsize, xp);
return (len);
} /* CatGetProc */
long CatPutProc(char *buf, size_t bufsize, XferSpecPtr xp)
{
long result;
longstring buf2;
if (isatty(xp->outStream)) {
MakeStringPrintable(buf2, (unsigned char *) buf, sizeof(buf2));
MultiLinePrintF("%s", buf2);
result = (long) bufsize;
} else {
result = (long) write(xp->outStream, buf, bufsize);
if (result != bufsize) {
return (-1L);
}
}
return (result);
} /* CatPutProc */
/* View a remote file through your pager. */
int DoCat(char *remoteName)
{
int result;
XferSpecPtr xp;
MultiLineInit();
xp = InitXferSpec();
xp->netMode = kNetReading;
xp->outStream = gStdout;
/* This group is needed for the progress reporting and logging stuff.
* Otherwise, it isn't that important.
*/
xp->doReports = kNoReports;
xp->localFileName = kLocalFileIsStdout;
xp->remoteFileName = remoteName;
/* This is supposed to be done previously, if you wanted accurate
* file sizes from GetDateAndSize.
*/
SETASCII;
/* Setup the parameter block to give to RDataCmd. */
xp->getBlock = CatGetProc;
xp->putBlock = CatPutProc;
/* xp->inStream = gDataSocket; RDataCmd fills this in when it gets it. */
/* Send the request and do the transfer. */
result = RDataCmd(xp, "RETR %s", xp->remoteFileName);
DoneWithXferSpec(xp);
return (result);
} /* DoCat */
/* We need to make something we can give to popen. This is simple
* if it is a plain file, but if they wanted to page a compressed
* file we have to prepend the correct filter before the pager name.
*/
int MakePageCmdLine(char *cmd, size_t siz, char *remote_file)
{
int useZCat;
int useGZCat;
int binaryPage;
int len;
useZCat = 0;
useGZCat = 0;
binaryPage = 0;
len = (int) strlen(remote_file);
if (len > 2) {
if (remote_file[len - 2] == '.') {
/* Check for .Z files. */
if (remote_file[len-1] == 'Z')
useZCat = 1;
/* Check for .z (gzip) files. */
if (remote_file[len - 1] == 'z')
useGZCat = 1;
}
}
if (len > 3) {
/* Check for ".gz" (gzip) files. */
if (STREQ(remote_file + len - 3, ".gz"))
useGZCat = 1;
}
/* Run compressed remote files through zcat, then the pager.
* If GZCAT was defined, we also try paging gzipped files.
*/
if (useGZCat) {
#ifdef GZCAT
(void) Strncpy(cmd, GZCAT, siz);
(void) Strncat(cmd, " | ", siz);
(void) Strncat(cmd, gPager, siz);
#else
PrintF("NcFTP wasn't configured to page gzipped files.\n");
#endif
} else if (useZCat) {
#ifdef ZCAT
(void) Strncpy(cmd, ZCAT, siz);
(void) Strncat(cmd, " | ", siz);
(void) Strncat(cmd, gPager, siz);
#else
# ifdef GZCAT
/* gzcat can do .Z's also. */
(void) Strncpy(cmd, GZCAT, siz);
(void) Strncat(cmd, " | ", siz);
(void) Strncat(cmd, gPager, siz);
# else
PrintF("NcFTP wasn't configured to page compressed files.\n");
# endif
#endif
} else {
(void) Strncpy(cmd, gPager, siz);
}
binaryPage = (useZCat || useGZCat);
return (binaryPage);
} /* MakePageCmdLine */
/* View a remote file through your pager. */
int DoPage(char *remoteName)
{
FILE *fp;
int result;
longstring pageCmd;
int binaryPage;
XferSpecPtr xp;
binaryPage = MakePageCmdLine(pageCmd, sizeof(pageCmd), remoteName);
DebugMsg("%s page: %s\n",
binaryPage ? "Binary" : "Ascii",
pageCmd
);
fp = POpen(pageCmd, "w", 1);
if (fp == NULL) {
Error(kDoPerror, "Could not run %s.\n", pageCmd);
return -1;
}
xp = InitXferSpec();
xp->netMode = kNetReading;
xp->outStream = fileno(fp);
/* This group is needed for the progress reporting and logging stuff.
* Otherwise, it isn't that important.
*/
xp->doReports = kNoReports;
xp->localFileName = kLocalFileIsStdout;
xp->remoteFileName = remoteName;
if (!binaryPage) {
/* Try to use text mode for paging, so newlines get converted. */
result = AsciiGet(xp);
} else {
/* Must use binary, or else zcat will complain about corrupted
* input files, since we'd be converting carriage-returns.
*/
result = BinaryGet(xp);
}
DoneWithXferSpec(xp);
(void) PClose(fp);
RestoreScreen(1);
return (result);
} /* DoPage */
/* View one or more remote files through your pager. */
int PageCmd(int argc, char **argv)
{
int i, result, errs;
LineList globFiles;
LinePtr globFile;
char *pagerProg;
if (STREQ(argv[1], "-b") && (gWinInit > 0) && (argc > 2)) {
/* A hack to let you use the built-in pager like you
* can with the lpage command.
*/
pagerProg = NULL; /* Use built-in */
i = 2;
} else {
if (gPager[0] == '\0') {
EPrintF("You haven't specified a program to use as a pager.\n");
EPrintF("You can set this from the preferences screen (prefs command).\n");
return -1;
}
pagerProg = gPager;
i = 1;
}
for (errs=0; i<argc; i++) {
InitLineList(&globFiles);
RemoteGlob(&globFiles, argv[i], kListNoFlags);
for (globFile = globFiles.first; globFile != NULL;
globFile = globFile->next)
{
if (pagerProg == NULL)
result = DoCat(globFile->line);
else
result = DoPage(globFile->line);
if (result < 0)
--errs;
if (gXferAbortFlag == SIGINT)
break; /* Don't get rest of files if you interrupted. */
}
DisposeLineListContents(&globFiles);
}
return (errs);
} /* PageCmd */
/* View one or more remote files through your pager. */
int CatCmd(int argc, char **argv)
{
int i, result, errs;
LineList globFiles;
LinePtr globFile;
MultiLineInit();
for (i=1, errs=0; i<argc; i++) {
InitLineList(&globFiles);
RemoteGlob(&globFiles, argv[i], kListNoFlags);
for (globFile = globFiles.first; globFile != NULL;
globFile = globFile->next)
{
result = DoCat(globFile->line);
if (result < 0)
--errs;
if (gXferAbortFlag == SIGINT)
break; /* Don't get rest of files if you interrupted. */
if (argc > 2)
MultiLinePrintF("### End of file %s ###\n", globFile->line);
}
DisposeLineListContents(&globFiles);
}
return (errs);
} /* CatCmd */