/* Cmds.c */ #include "Sys.h" #include #include #include #include #include #include "Util.h" #include "RCmd.h" #include "Cmds.h" #include "Cmdline.h" #include "List.h" #include "MakeArgv.h" #include "Macro.h" #include "Main.h" #include "DateSize.h" #include "Open.h" #include "Glob.h" #include "Getopt.h" #include "FTP.h" #include "Bookmark.h" #include "Cpp.h" #include "Prefs.h" #include "Tips.h" #include "Version.h" /* Full path of the remote current working directory. */ longstring gRemoteCWD = ""; /* The full path of the previous remote working directory. */ longstring gPrevRemoteCWD = ""; /* Full path of the local current working directory. */ longstring gLocalCWD = ""; /* Full path of the previous local working directory. */ longstring gPrevLocalCWD = ""; /* This is the type we use for file transfers. Note that we always use * type ascii for directory listings. */ int gTransferType = 'I'; /* This what type is in use at the moment. */ int gCurType; /* Upon receipt of a signal during paging a local file, we jump here. */ jmp_buf gShellJmp; /* Flag for debugging mode. */ #if (kAlpha > 0) || (kBeta > 0) int gDebug = kDebuggingOff; int gTrace = kTracingOn; #else int gDebug = kDebuggingOff; int gTrace = kTracingOff; #endif extern int gNumCommands; extern Command gCommands[]; extern int gVerbosity, gMode; extern Bookmark gRmtInfo; extern UserInfo gUserInfo; extern CppSymbol gCppSymbols[]; extern int gNumCppSymbols; extern string gVersion; extern longstring gPager; extern int gDoneApplication, gConnected; extern FILE *gTraceLogFile; extern int gStdout; extern MacroNodePtr gFirstMacro; extern int gNumGlobalMacros, gOtherSessionRunning; extern char *gOptArg; extern int gOptInd; extern struct hostent *GetHostEntry(char *host, struct in_addr *ip_address); void GetRemoteCWD(char *cdstub, ResponsePtr cwdrp); /* Runs the "SYST" command, and if the remote host supports it, will return * the system type we're connected to, otherwise an empty string. This is * handy to see if we're connected to a UNIX box, or something icky, like * MS/DOS, or even more icky, VMS. */ int DoSystem(char *systType, size_t siz) { ResponsePtr rp; int result; rp = InitResponse(); result = RCmd(rp, "SYST"); rp->printMode = kDontPrint; Strncpy(systType, rp->msg.first->line, siz); DoneWithResponse(rp); if (result != 2) { systType[0] = '\0'; return (-1); } return (0); } /* DoSystem */ /*ARGSUSED*/ static void SigLocalPage(/* int sigNum */ void) { alarm(0); longjmp(gShellJmp, 1); } /* SigLocalPage */ int LocalPageCmd(int argc0, char **argv0) { FILE *volatile fp; FILE *volatile pager; volatile int i; volatile int errs; int opt; longstring pageCmd; volatile LineList globFiles; volatile LinePtr globFile; volatile int useBuiltIn; volatile Sig_t si, sp; volatile int argc; char **volatile argv; string str; argv = argv0; argc = argc0; GetoptReset(); useBuiltIn = 0; while ((opt = Getopt(argc, argv, "bp")) >= 0) { switch (opt) { case 'b': useBuiltIn = 1; break; case 'p': useBuiltIn = 0; break; default: return (kUsageErr); } } argv += gOptInd; argc -= gOptInd; si = SIGNAL(SIGINT, SigLocalPage); sp = SIGNAL(SIGPIPE, SigLocalPage); errs = 0; if (useBuiltIn == 0) { 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"); errs = -1; goto done; } SaveScreen(); } for (i=0; inext) { fp = fopen(globFile->line, "r"); if (fp == NULL) { Error(kDoPerror, "Can't open %s.\n", globFile->line); --errs; } else if (useBuiltIn == 1) { MultiLineInit(); MultiLinePrintF("*** %s ***\n", globFile->line); if (setjmp(gShellJmp) != 0) { /* Command was interrupted. */ (void) SIGNAL(SIGINT, SIG_IGN); fclose((FILE *) fp); DisposeLineListContents((LineList *) &globFiles); --errs; goto done; } else { while (fgets(str, ((int) sizeof(str)) - 1, (FILE *)fp) != NULL) MultiLinePrintF("%s", str); } (void) SIGNAL(SIGINT, SIG_IGN); fclose((FILE *) fp); } else { STRNCPY(pageCmd, gPager); STRNCAT(pageCmd, " "); STRNCAT(pageCmd, globFile->line); pager = NULL; if (setjmp(gShellJmp) != 0) { /* Command was interrupted. */ (void) SIGNAL(SIGINT, SIG_IGN); (void) SIGNAL(SIGPIPE, SIG_IGN); if (pager != ((volatile FILE *) 0)) PClose((FILE *) pager); fclose((FILE *) fp); DisposeLineListContents((LineList *) &globFiles); --errs; goto done; } else { pager = POpen(pageCmd, "w", 1); while (fgets(str, ((int) sizeof(str)) - 1, (FILE *)fp) != NULL) fputs(str, (FILE *) pager); PClose((FILE *) pager); fclose((FILE *) fp); } } } DisposeLineListContents((LineList *) &globFiles); } done: if (useBuiltIn == 0) RestoreScreen(1); (void) SIGNAL(SIGINT, si); (void) SIGNAL(SIGPIPE, sp); Beep(0); /* User should be aware that it took a while, so no beep. */ return (errs); } /* LocalPageCmd */ /* Returns the full path of the remote working directory. */ void GetRemoteCWD(char *cdstub, ResponsePtr cwdrp) { ResponsePtr rp; char *l, *r; char *cp1; /* Some servers like NcFTPd return the new working * directory in the response. When you call this * function you can optionally pass the result * of a previous CWD or CDUP command, and see * if we can parse out the new directory without * doing a PWD command. */ if (cwdrp != NULL) { /* "xxxx" is new cwd. * Strip out just the xxxx to copy into the remote cwd. */ l = strchr(cwdrp->msg.first->line, '"'); r = strrchr(cwdrp->msg.first->line, '"'); if ((r != NULL) && (l != NULL) && (l != r) && (STRNEQ(r, "\" is ", 5))) { *r = '\0'; ++l; STRNCPY(gRemoteCWD, l); *r = '"'; /* Restore, so response prints correctly. */ SetScreenInfo(); return; } } rp = InitResponse(); if (RCmd(rp, "PWD") == 2) { if ((r = strrchr(rp->msg.first->line, '"')) != NULL) { /* "xxxx" is current directory. * Strip out just the xxxx to copy into the remote cwd. */ l = strchr(rp->msg.first->line, '"'); if ((l != NULL) && (l != r)) { *r = '\0'; ++l; STRNCPY(gRemoteCWD, l); *r = '"'; /* Restore, so response prints correctly. */ } } else { /* xxxx is current directory. * Mostly for VMS. */ if ((r = strchr(rp->msg.first->line, ' ')) != NULL) { *r = '\0'; STRNCPY(gRemoteCWD, (rp->msg.first->line)); *r = ' '; /* Restore, so response prints correctly. */ } } SetScreenInfo(); } else { /* Error. */ if (cdstub != kDidNotChdir) { /* We couldn't PWD. This could happen if we chdir'd to a * directory that looked like d--x--x--x. We could cd there, * but not read the contents. * * What we can do, since we know we just tried cd'ing here from * a previous directory is fake it and just append the path * we tried to cd at after the previous CWD. */ if (*cdstub == '/') { /* Just cd'd using an absolute path. */ STRNCPY(gRemoteCWD, cdstub); } else { /* If "cd .." , remove the lowest directory. * If "cd ." , do nothing. * Don't append the slash if previous directory was * the root. */ cp1 = strrchr(gRemoteCWD, '/'); if (STREQ(cdstub, "..") && !STREQ(gRemoteCWD, "/") && (cp1 != NULL)) *cp1 = '\0'; else if (STREQ(cdstub, ".")); /* do nothing */ else { if (! STREQ(gRemoteCWD, "/")) STRNCAT(gRemoteCWD, "/"); STRNCAT(gRemoteCWD, cdstub); } } SetScreenInfo(); } } DoneWithResponse(rp); } /* GetRemoteCWD */ int LocalPwdCmd(void) { (void) GetCWD(gLocalCWD, sizeof(gLocalCWD)); PrintF("Current local directory is %s.\n", gLocalCWD); return 0; } /* LocalPwdCmd */ int PwdCmd(void) { GetRemoteCWD(kDidNotChdir, NULL); PrintF("Current remote directory: %s\n", gRemoteCWD); return 0; } /* PwdCmd */ /* If the remote host supports the MDTM command, we can find out the exact * modification date of a remote file. */ int DoMdtm(char *fName, time_t *mdtm) { ResponsePtr rp; int result; *mdtm = kModTimeUnknown; result = -1; /* Don't bother if we know the current host doesn't support it. * We must make sure that the gRmtInfo is properly set each * time a host is opened. */ if (gRmtInfo.hasMDTM) { rp = InitResponse(); rp->printMode = kDontPrint; if (RCmd(rp, "MDTM %s", fName) == 2) { /* Reply should look like "213 19930602204445\n" so we will have * "19930602204445" in the first line of the reply string list. */ *mdtm = UnMDTMDate(rp->msg.first->line); result = 0; } else if (UNIMPLEMENTED_CMD(rp->code)) gRmtInfo.hasMDTM = 0; /* Command not supported. */ DoneWithResponse(rp); } return (result); } /* DoMdtm */ /* If the remote host supports the SIZE command, we can find out the exact * size of a remote file, depending on the transfer type in use. SIZE * returns different values for ascii and binary modes! */ int DoSize(char *fName, long *size) { ResponsePtr rp; int result; *size = kSizeUnknown; result = -1; /* Don't bother if we know the current host doesn't support it. * We must make sure that the gRmtInfo is properly set each * time a host is opened. */ if (gRmtInfo.hasSIZE) { rp = InitResponse(); rp->printMode = kDontPrint; if (RCmd(rp, "SIZE %s", fName) == 2) { sscanf(rp->msg.first->line, "%ld", size); result = 0; } else if (UNIMPLEMENTED_CMD(rp->code)) gRmtInfo.hasSIZE = 0; /* Command not supported. */ DoneWithResponse(rp); } return (result); } /* DoSize */ /* See if we can cd to the dir requested, and if not, that's okay. */ int TryQuietChdir(char *dir) { int result; ResponsePtr rp; rp = InitResponse(); rp->printMode = kDontPrint; if (STREQ(dir, "..")) result = RCmd(rp, "CDUP"); else { if (*dir == '\0') dir = "/"; result = RCmd(rp, "CWD %s", dir); } if (result == 2) { GetRemoteCWD(dir, rp); DoneWithResponse(rp); return (0); } DoneWithResponse(rp); return (-1); } /* TryQuietChdir */ /* Attempt to cd to the directory specifed, reporting an error if we * failed (or maybe not, depending on the verbosity level in use). */ int DoChdir(char *dir) { int result; ResponsePtr rp; rp = InitResponse(); if (STREQ(dir, "..")) result = RCmd(rp, "CDUP"); else { if (*dir == '\0') dir = "/"; result = RCmd(rp, "CWD %s", dir); } GetRemoteCWD(dir, rp); DoneWithResponse(rp); return (result != 2 ? -1 : 0); } /* DoChdir */ int ChdirCmd(int argcUNUSED, char **argv) { LineList globFiles; longstring str; char *cddir; int rglobbed; int result; #if 0 /* Can't glob a directory name without a major hassle. * We could do a "NLST -d dir*pattern" but most servers have that * damned NLST/-flags/glob-pattern conflict which prevents that. * * We could just do a "NLST dir*pattern" but then that gives us back * entire directory listings, like "dir/file1 dir/file2..." which * could get large and too wasteful of net bandwidth just because * the user is too lazy to type a directory name. * * We could try a "LIST -d dir*pattern" but then we'd have to parse * a big line of junk. This may be done sometime later. * * For now, I just don't support globbing wth cd. */ cddir = argv[1]; rglobbed = 0; #else InitLineList(&globFiles); if (gRmtInfo.isUnix == 0) { /* Don't try to glob the directory name unless server is UNIX. * This won't work on VMS, for example. */ cddir = argv[1]; rglobbed = 0; } else { RemoteGlob(&globFiles, argv[1], kListNoFlags); if (globFiles.first == NULL) { EPrintF("%s: no match.\n", argv[1]); DisposeLineListContents(&globFiles); return (kCmdErr); } else if (globFiles.first->next != NULL) { EPrintF("%s: wildcard matches more than one remote item.\n", argv[1]); DisposeLineListContents(&globFiles); return (kCmdErr); } else { rglobbed = 1; cddir = globFiles.first->line; } } #endif /* Steal the Korn shell's "cd -" trick, which cd's you to the * directory you were in before. */ STRNCPY(str, gRemoteCWD); if (STREQ(cddir, "-") && (gPrevRemoteCWD[0] != '\0')) { result = DoChdir(gPrevRemoteCWD); /* Sets gRemoteCWD. */ } else result = DoChdir(cddir); if (result == 0) STRNCPY(gPrevRemoteCWD, str); if (rglobbed != 0) DisposeLineListContents(&globFiles); return (result); } /* ChdirCmd */ /* cd to 'dir' on the local host. The dir specified may have glob * characters, or ~stuff in it also. */ int DoLocalChdir(char *dir, int quiet) { int result; LineList globFiles; InitLineList(&globFiles); LocalGlob(&globFiles, dir); if ((globFiles.first == NULL) || ((dir = globFiles.first->line) == NULL)) { Error(kDontPerror, "No match.\n"); result = kCmdErr; } else if (globFiles.first->next != NULL) { Error(kDontPerror, "Ambiguous directory name %s.\n", dir); result = kCmdErr; } else if ((result = chdir(dir)) < 0) { Error(kDoPerror, "Could not change local directory to %s.\n", dir); } else { (void) GetCWD(gLocalCWD, sizeof(gLocalCWD)); if (!quiet) PrintF("Current local directory is %s.\n", gLocalCWD); } DisposeLineListContents(&globFiles); return (result); } /* DoLocalChdir */ /* Stub command that lcd's to the appropriate directory, or if none * was supplied, carry on the tradition and make that the same as * lcd'ing to the home directory. */ int LocalChdirCmd(int argc, char **argv) { int result; char *cp; longstring str; if (argc < 2) cp = gUserInfo.home; else if (STREQ(argv[1], "-") && (gPrevLocalCWD[0] != '\0')) cp = gPrevLocalCWD; else cp = argv[1]; STRNCPY(str, gLocalCWD); result = DoLocalChdir(cp, 0); /* Sets gRemoteCWD. */ if (result == 0) STRNCPY(gPrevLocalCWD, str); return (result); } /* LocalChdirCmd */ /* Changes the debugging status, or prints some extra debugging * info depending on the parameter given. */ int DebugCmd(int argc, char **argv) { char *cp; int i; if (argc == 1) { PrintF("Debug Mode = %d. Trace Mode = %d.\n", gDebug, gTrace); } else { #if (LIBMALLOC == FAST_MALLOC) if (STREQ(argv[1], "memchk")) { struct mallinfo mi; mi = mallinfo(); PrintF("\ total space in arena: %d\n\ number of ordinary blocks: %d\n\ number of small blocks: %d\n\ number of holding blocks: %d\n\ space in holding block headers: %d\n\ space in small blocks in use: %d\n\ space in free small blocks: %d\n\ space in ordinary blocks in use: %d\n\ space in free ordinary blocks: %d\n\ cost of enabling keep option: %d\n", mi.arena, mi.ordblks, mi.smblks, mi.hblks, mi.hblkhd, mi.usmblks, mi.fsmblks, mi.uordblks, mi.fordblks, mi.keepcost ); return 0; } #endif #if (LIBMALLOC == DEBUG_MALLOC) if (STREQ(argv[1], "memchk")) { PrintF("malloc_chain_check: %d\n\n", malloc_chain_check(0)); PrintF("malloc_inuse: %lu\n", malloc_inuse(NULL)); return 0; } if (STREQ(argv[1], "memdump")) { malloc_dump(1); return 0; } #endif for (cp = argv[1]; (*cp != '\0') && isdigit(*cp); ) ++cp; if (*cp == '\0') { gDebug = atoi(argv[1]); return 0; } else if (ISTREQ(argv[1], "macro")) { /* Dump specified macro, or if NULL, all of them. */ DumpMacro(argv[2]); } else if (ISTREQ(argv[1], "segv")) { /* Intentionally bomb the program... */ *((int *) 0) = 99; } else if (ISTREQ(argv[1], "multi")) { MultiLineInit(); for (i=1; i<=60; i++) MultiLinePrintF("This is line %d.\n", i); } else if (ISTREQ(argv[1], "trace")) { if (argc > 2) gTrace = atoi(argv[2]); else gTrace = !gTrace; if (gTrace) { if (gTraceLogFile == NULL) OpenTraceLog(); } else { if (gTraceLogFile != NULL) CloseTraceLog(); } } else if (ISTREQ(argv[1], "tips")) { /* Dump all the tips. */ PrintAllTips(); } } return 0; } /* DebugCmd */ /* Sets the verbosity level of our informational messages. */ int VerboseCmd(int argc, char **argv) { int newVerbose; if (argc == 1) PrintF("Verbosity = %d.\n", gVerbosity); else { newVerbose = atoi(argv[1]); if (newVerbose < kQuiet) newVerbose = kQuiet; else if (newVerbose > kVerbose) newVerbose = kVerbose; (void) SetVerbose(newVerbose); } return 0; } /* VerboseCmd */ /* Sets the data transfer mode to the one specified, if needed, and returns * the mode it used or -1 upon failure. The 'mode' parameter must be * an uppercase letter. */ int SetMode(int mode) { ResponsePtr rp; int result = -1; if (mode == gMode) { /* Already on this mode, so don't waste network bandwidth. */ result = 0; } else if ((mode == 'S') || (mode == 'B')) { rp = InitResponse(); RCmd(rp, "MODE %c", mode); if (rp->codeType == 2) { result = 0; gRmtInfo.xferMode = gMode = mode; } DoneWithResponse(rp); } return (result); } /* SetMode */ /* Sets the data transfer type to the one specified, if needed, and returns * the type it used or -1 upon failure. The 'type' parameter must be * an uppercase letter. */ int SetType(int type) { ResponsePtr rp; int result = -1; if (type == 'B') type = 'I'; if (type == gCurType) { /* Already on this type, so don't waste network bandwidth. */ result = type; } else if ((type == 'A') || (type == 'I') || (type == 'T')) { rp = InitResponse(); RCmd(rp, "TYPE %c", type); if (rp->codeType == 2) { result = 0; gCurType = type; } DoneWithResponse(rp); } return (result); } /* SetType */ void DoType(char *typestr) { int type; type = *typestr; if (islower(type)) type = toupper(type); if (SetType(type) < 0) PrintF("Unknown type '%s'\n", typestr); else { gTransferType = gCurType; /* We only "remember" this type for next time, if the user * explicitly issued a type command. */ gRmtInfo.xferType = gTransferType; } } /* DoType */ int TypeCmd(int argc, char **argv) { if ((argc == 1) && (argv[0][0] != 't')) { /* Check for aliased commands binary and ascii, which come here. */ DoType(argv[0]); } if (argc > 1) { DoType(argv[1]); } else { PrintF("Transfer type is %c.\n", gTransferType); } return 0; } /* TypeCmd */ int ModeCmd(int argc, char **argv) { int c; if (argc > 1) { c = (int) argv[1][0]; switch(c) { case 's': case 'S': SetMode('S'); break; case 'b': case 'B': SetMode('B'); break; default: EPrintF("Only supported FTP transfer modes are \"stream\" and \"block.\"\n"); } } else { PrintF("Transfer mode is %c.\n", gMode); } return 0; } /* ModeCmd */ void DoQuit(int exitStatus) { /* Only do this once, in case we get caught with infinite recursion. */ if (++gDoneApplication <= 1) { if (gConnected) DoClose(1); (void) RunPrefixedMacro("quit.", "ncftp"); (void) RunPrefixedMacro("end.", "ncftp"); if (gOtherSessionRunning == 0) { WriteBookmarkFile(); CloseLogs(); WritePrefs(); SaveHistory(); } } Exit(exitStatus); } /* DoQuit */ int QuitCmd(void) { DoQuit(kExitNoErr); /*NOTREACHED*/ return 0; } /* QuitCmd */ /* Prints the command list, or gives a little more detail about a * specified command. */ int HelpCmd(int argc, char **argv) { CommandPtr c; MacroNodePtr macp; int showall = 0, helpall = 0; char *arg; int i, j, k, n; int nRows, nCols; int nCmds2Print; int screenColumns; int len, widestName; char *cp, **cmdnames, spec[16]; CMNamePtr cm; MultiLineInit(); if (argc == 2) { showall = (STREQ(argv[1], "showall")); helpall = (STREQ(argv[1], "helpall")); } if (argc == 1 || showall) { MultiLinePrintF("\ Commands may be abbreviated. 'help showall' shows aliases, invisible and\n\ unsupported commands. 'help ' gives a brief description of .\n\n"); /* First, see how many commands we will be printing to the screen. * Unless 'showall' was given, we won't be printing the hidden * (i.e. not very useful to the end-user) commands. */ c = gCommands; nCmds2Print = 0; for (n = 0; n < gNumCommands; c++, n++) if ((!iscntrl(c->name[0])) && (!(c->flags & kCmdHidden) || showall)) nCmds2Print++; if ((cmdnames = (char **) malloc(sizeof(char *) * nCmds2Print)) == NULL) OutOfMemory(); /* Now form the list we'll be printing, and noting what the maximum * length of a command name was, so we can use that when determining * how to print in columns. */ c = gCommands; i = 0; widestName = 0; for (n = 0; n < gNumCommands; c++, n++) { if ((!iscntrl(c->name[0])) && (!(c->flags & kCmdHidden) || showall)) { cmdnames[i++] = c->name; len = (int) strlen(c->name); if (len > widestName) widestName = len; } } if ((cp = (char *) getenv("COLUMNS")) == NULL) screenColumns = 80; else screenColumns = atoi(cp); /* Leave an extra bit of whitespace for the margins between columns. */ widestName += 2; nCols = (screenColumns + 0) / widestName; nRows = nCmds2Print / nCols; if ((nCmds2Print % nCols) > 0) nRows++; for (i = 0; i < nRows; i++) { for (j = 0; j < nCols; j++) { k = nRows * j + i; if (k < nCmds2Print) { (void) sprintf(spec, "%%-%ds", (j < nCols - 1) ? widestName : widestName - 2 ); MultiLinePrintF(spec, cmdnames[k]); } } MultiLinePrintF("\n"); } free(cmdnames); if (gNumGlobalMacros > 0) { MultiLinePrintF("\nMacros:\n\n"); /* Now do the same for the macros. */ if ((cmdnames = (char **) malloc(sizeof(char *) * gNumGlobalMacros)) == NULL) OutOfMemory(); /* Form the list we'll be printing, and noting what the maximum * length of a command name was, so we can use that when determining * how to print in columns. */ macp = gFirstMacro; widestName = 0; for (i = 0; macp != NULL; macp = macp->next) { cmdnames[i++] = macp->name; len = (int) strlen(macp->name); if (len > widestName) widestName = len; } nCmds2Print = i; /* Leave an extra bit of whitespace for the margins between columns. */ widestName += 2; nCols = (screenColumns + 0) / widestName; nRows = nCmds2Print / nCols; if ((nCmds2Print % nCols) > 0) nRows++; for (i = 0; i < nRows; i++) { for (j = 0; j < nCols; j++) { k = nRows * j + i; if (k < nCmds2Print) { (void) sprintf(spec, "%%-%ds", (j < nCols - 1) ? widestName : widestName - 2 ); MultiLinePrintF(spec, cmdnames[k]); } } MultiLinePrintF("\n"); } free(cmdnames); } } else if (helpall) { /* Really intended for me, so I can debug the help strings. */ for (c = gCommands, n = 0; n < gNumCommands; c++, n++) { PrintCmdHelp(c); PrintCmdUsage(c); } } else { /* For each command name specified, print its help stuff. */ while (--argc > 0) { arg = *++argv; cm = GetCommandOrMacro(arg, kAbbreviatedMatchAllowed); if (cm == kAmbiguousName) MultiLinePrintF("\"%s:\" Ambiguous command or macro name.\n", arg); else if (cm == kNoName) MultiLinePrintF("\"%s:\" Invalid command or macro name.\n", arg); else if (cm->isCmd) { c = cm->u.cmd; PrintCmdHelp(c); PrintCmdUsage(c); } else { MultiLinePrintF("\"%s\" is a macro, so no help is available.\n", arg); } } } return 0; } /* HelpCmd */ int VersionCmd(void) { int i; longstring line; longstring sym; char num[32]; int symsOnLine; int symLen; int lineLen; MultiLineInit(); MultiLinePrintF("Version: %s\n", gVersion); MultiLinePrintF("Author: Mike Gleason (mgleason@NcFTP.com)\n"); MultiLinePrintF("Archived in: ftp://ftp.NcFTP.com/ncftp/\n"); #ifdef __DATE__ MultiLinePrintF("Compile Date: %s\n", __DATE__); #endif #ifdef MK MultiLinePrintF("MK: %s\n", MK); #endif MultiLinePrintF("\nCompile options:\n\n"); line[0] = '\0'; symsOnLine = 0; lineLen = 0; for (i=0; i 79) { MultiLinePrintF("%s\n", line); line[0] = '\0'; symsOnLine = 0; lineLen = 0; } STRNCAT(line, sym); ++symsOnLine; lineLen += symLen; } if (symsOnLine) { MultiLinePrintF("%s\n", line); } return 0; } /* VersionCmd */ int GenericGlobCmd(int argc, char **argv, char *cmd, int printMode) { ResponsePtr rp; int i; int result, errs; LineList globFiles; LinePtr globFile; rp = InitResponse(); for (i=1, errs=0; inext) { rp->printMode = printMode; result = RCmd(rp, "%s %s", cmd, globFile->line); if (result != 2) { --errs; if (UNIMPLEMENTED_CMD(rp->code)) { DoneWithResponse(rp); DisposeLineListContents(&globFiles); return (errs); } } ReInitResponse(rp); } DisposeLineListContents(&globFiles); } DoneWithResponse(rp); return (errs); } /* GenericGlobCmd */ int GenericCmd(int argc, char **argv, char *cmd, int printMode) { ResponsePtr rp; int i; int result, errs; rp = InitResponse(); for (i=1, errs=0; iprintMode = printMode; result = RCmd(rp, "%s %s", cmd, argv[i]); if (result != 2) { --errs; if (UNIMPLEMENTED_CMD(rp->code)) goto done; } ReInitResponse(rp); } done: DoneWithResponse(rp); return (errs); } /* GenericCmd */ int DeleteCmd(int argc, char **argv) { return GenericGlobCmd(argc, argv, "DELE", kDefaultPrint); } /* DeleteCmd */ int RmdirCmd(int argc, char **argv) { return GenericGlobCmd(argc, argv, "RMD", kDefaultPrint); } /* RmdirCmd */ int MkdirCmd(int argc, char **argv) { return GenericCmd(argc, argv, "MKD", kDefaultPrint); } /* MkdirCmd */ int RenameCmd(int argcUNUSED, char **argv) { if (RCmd(kDefaultResponse, "RNFR %s", argv[1]) == 3) { RCmd(kDefaultResponse, "RNTO %s", argv[2]); } return 0; } /* MkdirCmd */ int QuoteCmd(int argc, char **argv) { longstring str; ResponsePtr rp; int i; str[0] = '\0'; for (i=1; i 1) STRNCAT(str, " "); STRNCAT(str, argv[i]); } rp = InitResponse(); rp->printMode = kDoPrint; (void) RCmd(rp, "%s%s", argv[0][0] == 's' ? "SITE " : "", str ); DoneWithResponse(rp); return 0; } /* QuoteCmd */ int ClearCmd(void) { UpdateScreen(1); return 0; } /* ClearCmd */ int RmtHelpCmd(int argc, char **argv) { ResponsePtr rp; if (argc > 1) GenericCmd(argc, argv, "HELP", kDoPrint); else { rp = InitResponse(); rp->printMode = kDoPrint; (void) RCmd(rp, "HELP"); DoneWithResponse(rp); } return 0; } /* RmtHelpCmd */ int ShellCmd(int argc, char **argv) { int result; char *volatile theShell; char *cmdLine; VSig_t si; si = (VSig_t) 0; if ((theShell = (char *) getenv("SHELL")) == NULL) theShell = gUserInfo.shell; if (theShell == NULL) theShell = "/bin/sh"; SaveScreen(); if (setjmp(gShellJmp) != 0) { /* Command was interrupted. */ (void) SIGNAL(SIGINT, SIG_IGN); result = 1; } else { si = SIGNAL(SIGINT, SigLocalPage); if (argc < 2) result = system(theShell); else { /* We have a hack where we keep a copy of the original * command line before parsing at position argc + 2. */ cmdLine = CMDLINEFROMARGS(argc, argv); /* Skip the ! and whitespace after it. */ while ((*cmdLine == '!') || isspace(*cmdLine)) cmdLine++; result = system(cmdLine); } } RestoreScreen(1); if (si != (VSig_t) 0) (void) SIGNAL(SIGINT, si); return result; } /* ShellCmd */ int EchoCmd(int argc, char **argv) { longstring str; int i; int noNewLine = 0; for (i=1; i 1 ? " " : ""), str); } if (!noNewLine) PrintF("\n"); return 0; } /* EchoCmd */ int LookupCmd(int argc, char **argv) { int i, j; struct hostent *hp; char *host, **cpp; struct in_addr ip_address; int shortMode, opt; char ipStr[16]; shortMode = 1; GetoptReset(); while ((opt = Getopt(argc, argv, "v")) >= 0) { if (opt == 'v') shortMode = 0; else return kUsageErr; } for (i=gOptInd; i gOptInd) && (shortMode == 0)) PrintF("\n"); if (hp == NULL) { PrintF("Unable to get information about site %s.\n", host); } else if (shortMode) { MyInetAddr(ipStr, sizeof(ipStr), hp->h_addr_list, 0); PrintF("%-40s %s\n", hp->h_name, ipStr); } else { PrintF("%s:\n", host); PrintF(" Name: %s\n", hp->h_name); for (cpp = hp->h_aliases; *cpp != NULL; cpp++) PrintF(" Alias: %s\n", *cpp); for (j = 0, cpp = hp->h_addr_list; *cpp != NULL; cpp++, ++j) { MyInetAddr(ipStr, sizeof(ipStr), hp->h_addr_list, j); PrintF(" Address: %s\n", ipStr); } } } return 0; } /* LookupCmd */ int BookmarkCmd(int argcUNUSED, char **argv) { SaveBookmark(argv[1]); return 0; } /* BookmarkCmd */