home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Networking / ncftp-2.4.2-MIHS / src / Cmds.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-18  |  29.2 KB  |  1,326 lines

  1. /* Cmds.c */
  2.  
  3. #include "Sys.h"
  4.  
  5. #include <ctype.h>
  6. #include <netinet/in.h>
  7. #include <netdb.h>
  8. #include <signal.h>
  9. #include <setjmp.h>
  10.  
  11. #include "Util.h"
  12. #include "RCmd.h"
  13. #include "Cmds.h"
  14. #include "Cmdline.h"
  15. #include "List.h"
  16. #include "MakeArgv.h"
  17. #include "Macro.h"
  18. #include "Main.h"
  19. #include "DateSize.h"
  20. #include "Open.h"
  21. #include "Glob.h"
  22. #include "Getopt.h"
  23. #include "FTP.h"
  24. #include "Bookmark.h"
  25. #include "Cpp.h"
  26. #include "Prefs.h"
  27. #include "Tips.h"
  28. #include "Version.h"
  29.  
  30. /* Full path of the remote current working directory. */
  31. longstring gRemoteCWD = "";
  32.  
  33. /* The full path of the previous remote working directory. */
  34. longstring gPrevRemoteCWD = "";
  35.  
  36. /* Full path of the local current working directory. */
  37. longstring gLocalCWD = "";
  38.  
  39. /* Full path of the previous local working directory. */
  40. longstring gPrevLocalCWD = "";
  41.  
  42. /* This is the type we use for file transfers.  Note that we always use
  43.  * type ascii for directory listings.
  44.  */
  45. int gTransferType = 'I';
  46.  
  47. /* This what type is in use at the moment. */
  48. int gCurType;
  49.  
  50. /* Upon receipt of a signal during paging a local file, we jump here. */
  51. jmp_buf gShellJmp;
  52.  
  53. /* Flag for debugging mode. */
  54. #if (kAlpha > 0) || (kBeta > 0)
  55. int gDebug = kDebuggingOff;
  56. int gTrace = kTracingOn;
  57. #else
  58. int gDebug = kDebuggingOff;
  59. int gTrace = kTracingOff;
  60. #endif
  61.  
  62. extern int gNumCommands;
  63. extern Command gCommands[];
  64. extern int gVerbosity, gMode;
  65. extern Bookmark gRmtInfo;
  66. extern UserInfo gUserInfo;
  67. extern CppSymbol gCppSymbols[];
  68. extern int gNumCppSymbols;
  69. extern string gVersion;
  70. extern longstring gPager;
  71. extern int gDoneApplication, gConnected;
  72. extern FILE *gTraceLogFile;
  73. extern int gStdout;
  74. extern MacroNodePtr gFirstMacro;
  75. extern int gNumGlobalMacros, gOtherSessionRunning;
  76. extern char *gOptArg;
  77. extern int gOptInd;
  78. extern struct hostent *GetHostEntry(char *host, struct in_addr *ip_address);
  79. void GetRemoteCWD(char *cdstub, ResponsePtr cwdrp);
  80.  
  81. /* Runs the "SYST" command, and if the remote host supports it, will return
  82.  * the system type we're connected to, otherwise an empty string.  This is
  83.  * handy to see if we're connected to a UNIX box, or something icky, like
  84.  * MS/DOS, or even more icky, VMS.
  85.  */
  86. int DoSystem(char *systType, size_t siz)
  87. {
  88.     ResponsePtr rp;
  89.     int result;
  90.  
  91.     rp = InitResponse();
  92.     result = RCmd(rp, "SYST");
  93.     rp->printMode = kDontPrint;
  94.     Strncpy(systType, rp->msg.first->line, siz);
  95.     DoneWithResponse(rp);
  96.     if (result != 2) {
  97.         systType[0] = '\0';
  98.         return (-1);
  99.     }
  100.     return (0);
  101. }    /* DoSystem */
  102.  
  103.  
  104.  
  105.  
  106. /*ARGSUSED*/
  107. static void SigLocalPage(/* int sigNum */ void)
  108. {
  109.     alarm(0);
  110.     longjmp(gShellJmp, 1);
  111. }    /* SigLocalPage */
  112.  
  113.  
  114.  
  115.  
  116. int LocalPageCmd(int argc0, char **argv0)
  117. {
  118.     FILE *volatile fp;
  119.     FILE *volatile pager;
  120.     volatile int i;
  121.     volatile int errs;
  122.     int opt;
  123.     longstring pageCmd;
  124.     volatile LineList globFiles;
  125.     volatile LinePtr globFile;
  126.     volatile int useBuiltIn;
  127.     volatile Sig_t si, sp;
  128.     volatile int argc;
  129.     char **volatile argv;
  130.     string str;
  131.  
  132.     argv = argv0;
  133.     argc = argc0;
  134.     GetoptReset();
  135.     useBuiltIn = 0;
  136.     while ((opt = Getopt(argc, argv, "bp")) >= 0) {
  137.         switch (opt) {
  138.             case 'b':
  139.                 useBuiltIn = 1;
  140.                 break;
  141.             case 'p':
  142.                 useBuiltIn = 0;
  143.                 break;
  144.             default:
  145.                 return (kUsageErr);
  146.         }
  147.     }
  148.     argv += gOptInd;
  149.     argc -= gOptInd;
  150.  
  151.     si = SIGNAL(SIGINT, SigLocalPage);
  152.     sp = SIGNAL(SIGPIPE, SigLocalPage);
  153.     errs = 0;
  154.     
  155.     if (useBuiltIn == 0) {
  156.         if (gPager[0] == '\0') {
  157.             EPrintF("You haven't specified a program to use as a pager.\n");
  158.             EPrintF("You can set this from the preferences screen (prefs command).\n");
  159.             errs = -1;
  160.             goto done;
  161.         }
  162.         SaveScreen();
  163.     }
  164.     
  165.     for (i=0; i<argc; i++) {
  166.         InitLineList((LineList *) &globFiles);
  167.         LocalGlob((LineList *) &globFiles, argv[i]);
  168.         for (globFile = globFiles.first; globFile != NULL;
  169.             globFile = globFile->next)
  170.         {
  171.             fp = fopen(globFile->line, "r");
  172.             if (fp == NULL) {
  173.                 Error(kDoPerror, "Can't open %s.\n", globFile->line);
  174.                 --errs;
  175.             } else if (useBuiltIn == 1) {
  176.                 MultiLineInit();
  177.                 MultiLinePrintF("*** %s ***\n", globFile->line);
  178.                 if (setjmp(gShellJmp) != 0) {
  179.                     /* Command was interrupted. */
  180.                     (void) SIGNAL(SIGINT, SIG_IGN);
  181.                     fclose((FILE *) fp);
  182.                     DisposeLineListContents((LineList *) &globFiles);
  183.                     --errs;
  184.                     goto done;
  185.                 } else {
  186.                     while (fgets(str, ((int) sizeof(str)) - 1, (FILE *)fp) != NULL)
  187.                         MultiLinePrintF("%s", str);
  188.                 }
  189.                 (void) SIGNAL(SIGINT, SIG_IGN);
  190.                 fclose((FILE *) fp);
  191.             } else {
  192.                 STRNCPY(pageCmd, gPager);
  193.                 STRNCAT(pageCmd, " ");
  194.                 STRNCAT(pageCmd, globFile->line);
  195.                 pager = NULL;
  196.                 if (setjmp(gShellJmp) != 0) {
  197.                     /* Command was interrupted. */
  198.                     (void) SIGNAL(SIGINT, SIG_IGN);
  199.                     (void) SIGNAL(SIGPIPE, SIG_IGN);
  200.                     if (pager != ((volatile FILE *) 0))
  201.                         PClose((FILE *) pager);
  202.                     fclose((FILE *) fp);
  203.                     DisposeLineListContents((LineList *) &globFiles);
  204.                     --errs;
  205.                     goto done;
  206.                 } else {
  207.                     pager = POpen(pageCmd, "w", 1);
  208.                     while (fgets(str, ((int) sizeof(str)) - 1, (FILE *)fp) != NULL)
  209.                         fputs(str, (FILE *) pager);
  210.                     PClose((FILE *) pager);
  211.                     fclose((FILE *) fp);
  212.                 }
  213.             }
  214.         }
  215.         DisposeLineListContents((LineList *) &globFiles);
  216.     }
  217.  
  218. done:
  219.     if (useBuiltIn == 0)
  220.         RestoreScreen(1);
  221.     (void) SIGNAL(SIGINT, si);
  222.     (void) SIGNAL(SIGPIPE, sp);
  223.     Beep(0);    /* User should be aware that it took a while, so no beep. */
  224.     return (errs);
  225. }    /* LocalPageCmd */
  226.  
  227.  
  228.  
  229.  
  230. /* Returns the full path of the remote working directory. */
  231. void GetRemoteCWD(char *cdstub, ResponsePtr cwdrp)
  232. {
  233.     ResponsePtr rp;
  234.     char *l, *r;
  235.     char *cp1;
  236.  
  237.     /* Some servers like NcFTPd return the new working
  238.      * directory in the response.  When you call this
  239.      * function you can optionally pass the result
  240.      * of a previous CWD or CDUP command, and see
  241.      * if we can parse out the new directory without
  242.      * doing a PWD command.
  243.      */
  244.     if (cwdrp != NULL) {
  245.         /* "xxxx" is new cwd.
  246.          * Strip out just the xxxx to copy into the remote cwd.
  247.          */
  248.         l = strchr(cwdrp->msg.first->line, '"');
  249.         r = strrchr(cwdrp->msg.first->line, '"');
  250.         if ((r != NULL) && (l != NULL) && (l != r) && (STRNEQ(r, "\" is ", 5))) {
  251.             *r = '\0';
  252.             ++l;
  253.             STRNCPY(gRemoteCWD, l);
  254.             *r = '"';    /* Restore, so response prints correctly. */
  255.             SetScreenInfo();
  256.             return;
  257.         }
  258.     }
  259.  
  260.     rp = InitResponse();
  261.     if (RCmd(rp, "PWD") == 2) {
  262.         if ((r = strrchr(rp->msg.first->line, '"')) != NULL) {
  263.             /* "xxxx" is current directory.
  264.              * Strip out just the xxxx to copy into the remote cwd.
  265.              */
  266.             l = strchr(rp->msg.first->line, '"');
  267.             if ((l != NULL) && (l != r)) {
  268.                 *r = '\0';
  269.                 ++l;
  270.                 STRNCPY(gRemoteCWD, l);
  271.                 *r = '"';    /* Restore, so response prints correctly. */
  272.             }
  273.         } else {
  274.             /* xxxx is current directory.
  275.              * Mostly for VMS.
  276.              */
  277.             if ((r = strchr(rp->msg.first->line, ' ')) != NULL) {
  278.                 *r = '\0';
  279.                 STRNCPY(gRemoteCWD, (rp->msg.first->line));
  280.                 *r = ' ';    /* Restore, so response prints correctly. */
  281.             }
  282.         }
  283.         SetScreenInfo();
  284.     } else {
  285.         /* Error. */
  286.         if (cdstub != kDidNotChdir) {
  287.             /* We couldn't PWD.  This could happen if we chdir'd to a
  288.              * directory that looked like d--x--x--x.  We could cd there,
  289.              * but not read the contents.
  290.              *
  291.              * What we can do, since we know we just tried cd'ing here from
  292.              * a previous directory is fake it and just append the path
  293.              * we tried to cd at after the previous CWD.
  294.              */
  295.             if (*cdstub == '/') {
  296.                 /* Just cd'd using an absolute path. */
  297.                 STRNCPY(gRemoteCWD, cdstub);
  298.             } else {
  299.                 /* If "cd .." , remove the lowest directory.
  300.                  * If "cd ." , do nothing.
  301.                  * Don't append the slash if previous directory was
  302.                  * the root.
  303.                  */
  304.                 cp1 = strrchr(gRemoteCWD, '/');
  305.                 if (STREQ(cdstub, "..") && !STREQ(gRemoteCWD, "/")
  306.                     && (cp1 != NULL))
  307.                     *cp1 = '\0';
  308.                 else if (STREQ(cdstub, ".")); /* do nothing */
  309.                 else {
  310.                     if (! STREQ(gRemoteCWD, "/"))
  311.                         STRNCAT(gRemoteCWD, "/");
  312.                     STRNCAT(gRemoteCWD, cdstub);
  313.                 }
  314.             }
  315.             SetScreenInfo();
  316.         }
  317.     }
  318.     DoneWithResponse(rp);
  319. }    /* GetRemoteCWD */
  320.  
  321.  
  322.  
  323.  
  324. int LocalPwdCmd(void)
  325. {
  326.     (void) GetCWD(gLocalCWD, sizeof(gLocalCWD));
  327.     PrintF("Current local directory is %s.\n", gLocalCWD);
  328.     return 0;
  329. }    /* LocalPwdCmd */
  330.  
  331.  
  332.  
  333.  
  334. int PwdCmd(void)
  335. {
  336.     GetRemoteCWD(kDidNotChdir, NULL);
  337.     PrintF("Current remote directory: %s\n", gRemoteCWD);
  338.     return 0;
  339. }    /* PwdCmd */
  340.  
  341.  
  342.  
  343.  
  344. /* If the remote host supports the MDTM command, we can find out the exact
  345.  * modification date of a remote file.
  346.  */
  347. int DoMdtm(char *fName, time_t *mdtm)
  348. {
  349.     ResponsePtr rp;
  350.     int result;
  351.  
  352.     *mdtm = kModTimeUnknown;
  353.     result = -1;
  354.     /* Don't bother if we know the current host doesn't support it.
  355.      * We must make sure that the gRmtInfo is properly set each
  356.      * time a host is opened.
  357.      */
  358.     if (gRmtInfo.hasMDTM) {
  359.         rp = InitResponse();
  360.         rp->printMode = kDontPrint;
  361.         if (RCmd(rp, "MDTM %s", fName) == 2) {
  362.             /* Reply should look like "213 19930602204445\n" so we will have
  363.              * "19930602204445" in the first line of the reply string list.
  364.              */
  365.             *mdtm = UnMDTMDate(rp->msg.first->line);
  366.             result = 0;
  367.         } else if (UNIMPLEMENTED_CMD(rp->code))
  368.             gRmtInfo.hasMDTM = 0;    /* Command not supported. */
  369.         DoneWithResponse(rp);
  370.     }
  371.     return (result);
  372. }    /* DoMdtm */
  373.  
  374.  
  375.  
  376.  
  377. /* If the remote host supports the SIZE command, we can find out the exact
  378.  * size of a remote file, depending on the transfer type in use.  SIZE
  379.  * returns different values for ascii and binary modes!
  380.  */
  381. int DoSize(char *fName, long *size)
  382. {
  383.     ResponsePtr rp;
  384.     int result;
  385.     
  386.     *size = kSizeUnknown;
  387.     result = -1;
  388.     /* Don't bother if we know the current host doesn't support it.
  389.      * We must make sure that the gRmtInfo is properly set each
  390.      * time a host is opened.
  391.      */
  392.     if (gRmtInfo.hasSIZE) {
  393.         rp = InitResponse();
  394.         rp->printMode = kDontPrint;
  395.         if (RCmd(rp, "SIZE %s", fName) == 2) {
  396.             sscanf(rp->msg.first->line, "%ld", size);
  397.             result = 0;
  398.         } else if (UNIMPLEMENTED_CMD(rp->code))
  399.             gRmtInfo.hasSIZE = 0;    /* Command not supported. */
  400.         DoneWithResponse(rp);
  401.     }
  402.     return (result);
  403. }    /* DoSize */
  404.  
  405.  
  406.  
  407.  
  408. /* See if we can cd to the dir requested, and if not, that's okay. */
  409. int TryQuietChdir(char *dir)
  410. {
  411.     int result;
  412.     ResponsePtr rp;
  413.  
  414.     rp = InitResponse();
  415.     rp->printMode = kDontPrint;
  416.     if (STREQ(dir, ".."))
  417.         result = RCmd(rp, "CDUP");
  418.     else {
  419.         if (*dir == '\0')
  420.             dir = "/";
  421.         result = RCmd(rp, "CWD %s", dir);
  422.     }
  423.     if (result == 2) {
  424.         GetRemoteCWD(dir, rp);
  425.         DoneWithResponse(rp);
  426.         return (0);
  427.     }
  428.     DoneWithResponse(rp);
  429.     return (-1);
  430. }    /* TryQuietChdir */
  431.  
  432.  
  433.  
  434.  
  435. /* Attempt to cd to the directory specifed, reporting an error if we
  436.  * failed (or maybe not, depending on the verbosity level in use).
  437.  */
  438. int DoChdir(char *dir)
  439. {
  440.     int result;
  441.     ResponsePtr rp;
  442.  
  443.     rp = InitResponse();
  444.     if (STREQ(dir, ".."))
  445.         result = RCmd(rp, "CDUP");
  446.     else {
  447.         if (*dir == '\0')
  448.             dir = "/";
  449.         result = RCmd(rp, "CWD %s", dir);
  450.     }
  451.     GetRemoteCWD(dir, rp);
  452.     DoneWithResponse(rp);
  453.     return (result != 2 ? -1 : 0);
  454. }    /* DoChdir */
  455.  
  456.  
  457.  
  458.  
  459. int ChdirCmd(int argcUNUSED, char **argv)
  460. {
  461.     LineList globFiles;
  462.     longstring str;
  463.     char *cddir;
  464.     int rglobbed;
  465.     int result;
  466.  
  467. #if 0
  468.     /* Can't glob a directory name without a major hassle.
  469.      * We could do a "NLST -d dir*pattern" but most servers have that
  470.      * damned NLST/-flags/glob-pattern conflict which prevents that.
  471.      *
  472.      * We could just do a "NLST dir*pattern" but then that gives us back
  473.      * entire directory listings, like "dir/file1 dir/file2..." which 
  474.      * could get large and too wasteful of net bandwidth just because
  475.      * the user is too lazy to type a directory name.
  476.      *
  477.      * We could try a "LIST -d dir*pattern" but then we'd have to parse
  478.      * a big line of junk.  This may be done sometime later.
  479.      *
  480.      * For now, I just don't support globbing wth cd.
  481.      */
  482.     cddir = argv[1];
  483.     rglobbed = 0;
  484. #else
  485.     InitLineList(&globFiles);
  486.     if (gRmtInfo.isUnix == 0) {
  487.         /* Don't try to glob the directory name unless server is UNIX.
  488.          * This won't work on VMS, for example.
  489.          */
  490.         cddir = argv[1];
  491.         rglobbed = 0;
  492.     } else {
  493.         RemoteGlob(&globFiles, argv[1], kListNoFlags);
  494.         if (globFiles.first == NULL) {
  495.             EPrintF("%s: no match.\n", argv[1]);
  496.             DisposeLineListContents(&globFiles);
  497.             return (kCmdErr);
  498.         } else if (globFiles.first->next != NULL) {
  499.             EPrintF("%s: wildcard matches more than one remote item.\n", argv[1]);
  500.             DisposeLineListContents(&globFiles);
  501.             return (kCmdErr);
  502.         } else {
  503.             rglobbed = 1;
  504.             cddir = globFiles.first->line;
  505.         }
  506.     }
  507. #endif
  508.  
  509.     /* Steal the Korn shell's "cd -" trick, which cd's you to the
  510.      * directory you were in before.
  511.      */
  512.     STRNCPY(str, gRemoteCWD);
  513.     if (STREQ(cddir, "-") && (gPrevRemoteCWD[0] != '\0')) {
  514.         result = DoChdir(gPrevRemoteCWD);    /* Sets gRemoteCWD. */
  515.     } else
  516.         result = DoChdir(cddir);
  517.     if (result == 0)
  518.         STRNCPY(gPrevRemoteCWD, str);
  519.  
  520.     if (rglobbed != 0)
  521.         DisposeLineListContents(&globFiles);
  522.     return (result);
  523. }    /* ChdirCmd */
  524.  
  525.  
  526.  
  527.  
  528. /* cd to 'dir' on the local host.  The dir specified may have glob
  529.  * characters, or ~stuff in it also.
  530.  */
  531. int DoLocalChdir(char *dir, int quiet)
  532. {
  533.     int result;
  534.     LineList globFiles;
  535.  
  536.     InitLineList(&globFiles);
  537.     LocalGlob(&globFiles, dir);
  538.     if ((globFiles.first == NULL) || ((dir = globFiles.first->line) == NULL)) {
  539.         Error(kDontPerror, "No match.\n");
  540.         result = kCmdErr;
  541.     } else if (globFiles.first->next != NULL) {
  542.         Error(kDontPerror, "Ambiguous directory name %s.\n", dir);
  543.         result = kCmdErr;
  544.     } else if ((result = chdir(dir)) < 0) {
  545.         Error(kDoPerror, "Could not change local directory to %s.\n", dir);
  546.     } else {
  547.         (void) GetCWD(gLocalCWD, sizeof(gLocalCWD));
  548.         if (!quiet)
  549.             PrintF("Current local directory is %s.\n", gLocalCWD);
  550.     }
  551.     DisposeLineListContents(&globFiles);
  552.     return (result);
  553. }    /* DoLocalChdir */
  554.  
  555.  
  556.  
  557. /* Stub command that lcd's to the appropriate directory, or if none
  558.  * was supplied, carry on the tradition and make that the same as
  559.  * lcd'ing to the home directory.
  560.  */
  561. int LocalChdirCmd(int argc, char **argv)
  562. {
  563.     int result;
  564.     char *cp;
  565.     longstring str;
  566.  
  567.     if (argc < 2)
  568.         cp = gUserInfo.home;
  569.     else if (STREQ(argv[1], "-") && (gPrevLocalCWD[0] != '\0'))
  570.         cp = gPrevLocalCWD;
  571.     else
  572.         cp = argv[1];
  573.     STRNCPY(str, gLocalCWD);
  574.     result = DoLocalChdir(cp, 0);    /* Sets gRemoteCWD. */
  575.     if (result == 0)
  576.         STRNCPY(gPrevLocalCWD, str);
  577.     return (result);
  578. }    /* LocalChdirCmd */
  579.  
  580.  
  581.  
  582.  
  583. /* Changes the debugging status, or prints some extra debugging
  584.  * info depending on the parameter given.
  585.  */
  586. int DebugCmd(int argc, char **argv)
  587. {
  588.     char *cp;
  589.     int i;
  590.  
  591.     if (argc == 1) {
  592.         PrintF("Debug Mode = %d.  Trace Mode = %d.\n", gDebug, gTrace);
  593.     } else {
  594. #if (LIBMALLOC == FAST_MALLOC)
  595.         if (STREQ(argv[1], "memchk")) {
  596.             struct mallinfo mi;
  597.         
  598.             mi = mallinfo();
  599.             PrintF("\
  600. total space in arena:               %d\n\
  601. number of ordinary blocks:          %d\n\
  602. number of small blocks:             %d\n\
  603. number of holding blocks:           %d\n\
  604. space in holding block headers:     %d\n\
  605. space in small blocks in use:       %d\n\
  606. space in free small blocks:         %d\n\
  607. space in ordinary blocks in use:    %d\n\
  608. space in free ordinary blocks:      %d\n\
  609. cost of enabling keep option:       %d\n",
  610.                 mi.arena,
  611.                 mi.ordblks,
  612.                 mi.smblks,
  613.                 mi.hblks,
  614.                 mi.hblkhd,
  615.                 mi.usmblks,
  616.                 mi.fsmblks,
  617.                 mi.uordblks,
  618.                 mi.fordblks,
  619.                 mi.keepcost
  620.             );
  621.             return 0;
  622.         }
  623. #endif
  624. #if (LIBMALLOC == DEBUG_MALLOC)
  625.         if (STREQ(argv[1], "memchk")) {
  626.             PrintF("malloc_chain_check: %d\n\n", malloc_chain_check(0));
  627.             PrintF("malloc_inuse: %lu\n", malloc_inuse(NULL));
  628.             return 0;
  629.         }
  630.         if (STREQ(argv[1], "memdump")) {
  631.             malloc_dump(1);
  632.             return 0;
  633.         }
  634. #endif
  635.         for (cp = argv[1]; (*cp != '\0') && isdigit(*cp); )
  636.             ++cp;
  637.         if (*cp == '\0') {
  638.             gDebug = atoi(argv[1]);
  639.             return 0;
  640.         } else if (ISTREQ(argv[1], "macro")) {
  641.             /* Dump specified macro, or if NULL, all of them. */
  642.             DumpMacro(argv[2]);
  643.         } else if (ISTREQ(argv[1], "segv")) {
  644.             /* Intentionally bomb the program... */
  645.             *((int *) 0) = 99;
  646.         } else if (ISTREQ(argv[1], "multi")) {
  647.             MultiLineInit();
  648.             for (i=1; i<=60; i++)
  649.                 MultiLinePrintF("This is line %d.\n", i);
  650.         } else if (ISTREQ(argv[1], "trace")) {
  651.             if (argc > 2)
  652.                 gTrace = atoi(argv[2]);
  653.             else
  654.                 gTrace = !gTrace;
  655.             if (gTrace) {
  656.                 if (gTraceLogFile == NULL)
  657.                     OpenTraceLog();
  658.             } else {
  659.                 if (gTraceLogFile != NULL)
  660.                     CloseTraceLog();
  661.             }
  662.         } else if (ISTREQ(argv[1], "tips")) {
  663.             /* Dump all the tips. */
  664.             PrintAllTips();
  665.         }
  666.     }
  667.     return 0;
  668. }    /* DebugCmd */
  669.  
  670.  
  671.  
  672.  
  673. /* Sets the verbosity level of our informational messages. */
  674. int VerboseCmd(int argc, char **argv)
  675. {
  676.     int newVerbose;
  677.  
  678.     if (argc == 1)
  679.         PrintF("Verbosity = %d.\n", gVerbosity);
  680.     else {
  681.         newVerbose = atoi(argv[1]);
  682.         if (newVerbose < kQuiet)
  683.             newVerbose = kQuiet;
  684.         else if (newVerbose > kVerbose)
  685.             newVerbose = kVerbose;
  686.         (void) SetVerbose(newVerbose);
  687.     }
  688.     return 0;
  689. }    /* VerboseCmd */
  690.  
  691.  
  692.  
  693. /* Sets the data transfer mode to the one specified, if needed, and returns
  694.  * the mode it used or -1 upon failure.  The 'mode' parameter must be
  695.  * an uppercase letter.
  696.  */
  697. int SetMode(int mode)
  698. {
  699.     ResponsePtr rp;
  700.     int result = -1;
  701.  
  702.     if (mode == gMode) {
  703.         /* Already on this mode, so don't waste network bandwidth. */
  704.         result = 0;
  705.     } else if ((mode == 'S') || (mode == 'B')) {
  706.         rp = InitResponse();
  707.         RCmd(rp, "MODE %c", mode);
  708.         if (rp->codeType == 2) {
  709.             result = 0;
  710.             gRmtInfo.xferMode = gMode = mode;
  711.         }
  712.         DoneWithResponse(rp);
  713.     }
  714.     return (result);
  715. }    /* SetMode */
  716.  
  717.  
  718.  
  719. /* Sets the data transfer type to the one specified, if needed, and returns
  720.  * the type it used or -1 upon failure.  The 'type' parameter must be
  721.  * an uppercase letter.
  722.  */
  723. int SetType(int type)
  724. {
  725.     ResponsePtr rp;
  726.     
  727.     int result = -1;
  728.  
  729.     if (type == 'B')
  730.         type = 'I';
  731.  
  732.     if (type == gCurType) {
  733.         /* Already on this type, so don't waste network bandwidth. */
  734.         result = type;
  735.     } else if ((type == 'A') || (type == 'I') || (type == 'T')) {
  736.         rp = InitResponse();
  737.         RCmd(rp, "TYPE %c", type);
  738.         if (rp->codeType == 2) {
  739.             result = 0;
  740.             gCurType = type;
  741.         }
  742.         DoneWithResponse(rp);
  743.     }
  744.     return (result);
  745. }    /* SetType */
  746.  
  747.  
  748.  
  749.  
  750. void DoType(char *typestr)
  751. {
  752.     int type;
  753.  
  754.     type = *typestr;
  755.     if (islower(type))
  756.         type = toupper(type);
  757.     
  758.     if (SetType(type) < 0)
  759.         PrintF("Unknown type '%s'\n", typestr);
  760.     else {
  761.         gTransferType = gCurType;
  762.         /* We only "remember" this type for next time, if the user
  763.          * explicitly issued a type command.
  764.          */
  765.         gRmtInfo.xferType = gTransferType;
  766.     }
  767. }    /* DoType */
  768.  
  769.  
  770.  
  771.  
  772. int TypeCmd(int argc, char **argv)
  773. {
  774.     if ((argc == 1) && (argv[0][0] != 't')) {
  775.         /* Check for aliased commands binary and ascii, which come here. */
  776.         DoType(argv[0]);
  777.     } if (argc > 1) {
  778.         DoType(argv[1]);
  779.     } else {
  780.         PrintF("Transfer type is %c.\n", gTransferType);
  781.     }
  782.     return 0;
  783. }    /* TypeCmd */
  784.  
  785.  
  786.  
  787. int ModeCmd(int argc, char **argv)
  788. {
  789.     int c;
  790.  
  791.     if (argc > 1) {
  792.         c = (int) argv[1][0];
  793.         switch(c) {
  794.             case 's':
  795.             case 'S':
  796.                 SetMode('S');
  797.                 break;
  798.             case 'b':
  799.             case 'B':
  800.                 SetMode('B');
  801.                 break;
  802.             default:
  803.                 EPrintF("Only supported FTP transfer modes are \"stream\" and \"block.\"\n");
  804.         }
  805.     } else {
  806.         PrintF("Transfer mode is %c.\n", gMode);
  807.     }
  808.     return 0;
  809. }    /* ModeCmd */
  810.  
  811.  
  812.  
  813. void DoQuit(int exitStatus)
  814. {
  815.     /* Only do this once, in case we get caught with infinite recursion. */
  816.     if (++gDoneApplication <= 1) {
  817.         if (gConnected)
  818.             DoClose(1);
  819.         (void) RunPrefixedMacro("quit.", "ncftp");
  820.         (void) RunPrefixedMacro("end.", "ncftp");
  821.         if (gOtherSessionRunning == 0) {
  822.             WriteBookmarkFile();
  823.             CloseLogs();
  824.             WritePrefs();
  825.             SaveHistory();
  826.         }
  827.     }
  828.     Exit(exitStatus);
  829. }    /* DoQuit */
  830.  
  831.  
  832.  
  833. int QuitCmd(void)
  834. {
  835.     DoQuit(kExitNoErr);
  836.     /*NOTREACHED*/
  837.     return 0;
  838. }    /* QuitCmd */
  839.  
  840.  
  841.  
  842.  
  843. /* Prints the command list, or gives a little more detail about a
  844.  * specified command.
  845.  */
  846. int HelpCmd(int argc, char **argv)
  847. {
  848.     CommandPtr c;
  849.     MacroNodePtr macp;
  850.     int showall = 0, helpall = 0;
  851.     char *arg;
  852.     int i, j, k, n;
  853.     int nRows, nCols;
  854.     int nCmds2Print;
  855.     int screenColumns;
  856.     int len, widestName;
  857.     char *cp, **cmdnames, spec[16];
  858.     CMNamePtr cm;
  859.  
  860.     MultiLineInit();
  861.     if (argc == 2) {
  862.         showall = (STREQ(argv[1], "showall"));
  863.         helpall = (STREQ(argv[1], "helpall"));
  864.     }
  865.     if (argc == 1 || showall) {
  866.         MultiLinePrintF("\
  867. Commands may be abbreviated.  'help showall' shows aliases, invisible and\n\
  868. unsupported commands.  'help <command>' gives a brief description of <command>.\n\n");
  869.  
  870.         /* First, see how many commands we will be printing to the screen.
  871.          * Unless 'showall' was given, we won't be printing the hidden
  872.          * (i.e. not very useful to the end-user) commands.
  873.          */
  874.         c = gCommands;
  875.         nCmds2Print = 0;
  876.         for (n = 0; n < gNumCommands; c++, n++)
  877.             if ((!iscntrl(c->name[0])) && (!(c->flags & kCmdHidden) || showall))
  878.                 nCmds2Print++;
  879.  
  880.         if ((cmdnames = (char **) malloc(sizeof(char *) * nCmds2Print)) == NULL)
  881.             OutOfMemory();
  882.  
  883.         /* Now form the list we'll be printing, and noting what the maximum
  884.          * length of a command name was, so we can use that when determining
  885.          * how to print in columns.
  886.          */
  887.         c = gCommands;
  888.         i = 0;
  889.         widestName = 0;
  890.         for (n = 0; n < gNumCommands; c++, n++) {
  891.             if ((!iscntrl(c->name[0])) && (!(c->flags & kCmdHidden) || showall)) {
  892.                 cmdnames[i++] = c->name;
  893.                 len = (int) strlen(c->name);
  894.                 if (len > widestName)
  895.                     widestName = len;
  896.             }
  897.         }
  898.  
  899.         if ((cp = (char *) getenv("COLUMNS")) == NULL)
  900.             screenColumns = 80;
  901.         else
  902.             screenColumns = atoi(cp);
  903.  
  904.         /* Leave an extra bit of whitespace for the margins between columns. */
  905.         widestName += 2;
  906.         
  907.         nCols = (screenColumns + 0) / widestName;
  908.         nRows = nCmds2Print / nCols;
  909.         if ((nCmds2Print % nCols) > 0)
  910.             nRows++;
  911.  
  912.         for (i = 0; i < nRows; i++) {
  913.             for (j = 0; j < nCols; j++) {
  914.                 k = nRows * j + i;
  915.                 if (k < nCmds2Print) {
  916.                     (void) sprintf(spec, "%%-%ds",
  917.                         (j < nCols - 1) ? widestName : widestName - 2
  918.                     );
  919.                     MultiLinePrintF(spec, cmdnames[k]);
  920.                 }
  921.             }
  922.             MultiLinePrintF("\n");
  923.         }
  924.         free(cmdnames);
  925.         
  926.         if (gNumGlobalMacros > 0) {
  927.             MultiLinePrintF("\nMacros:\n\n");
  928.             /* Now do the same for the macros. */
  929.             if ((cmdnames = (char **) malloc(sizeof(char *) * gNumGlobalMacros)) == NULL)
  930.                 OutOfMemory();
  931.     
  932.             /* Form the list we'll be printing, and noting what the maximum
  933.              * length of a command name was, so we can use that when determining
  934.              * how to print in columns.
  935.              */
  936.             macp = gFirstMacro;
  937.             widestName = 0;
  938.             for (i = 0; macp != NULL; macp = macp->next) {
  939.                     cmdnames[i++] = macp->name;
  940.                     len = (int) strlen(macp->name);
  941.                     if (len > widestName)
  942.                         widestName = len;
  943.             }
  944.             nCmds2Print = i;
  945.     
  946.             /* Leave an extra bit of whitespace for the margins between columns. */
  947.             widestName += 2;
  948.             
  949.             nCols = (screenColumns + 0) / widestName;
  950.             nRows = nCmds2Print / nCols;
  951.             if ((nCmds2Print % nCols) > 0)
  952.                 nRows++;
  953.     
  954.             for (i = 0; i < nRows; i++) {
  955.                 for (j = 0; j < nCols; j++) {
  956.                     k = nRows * j + i;
  957.                     if (k < nCmds2Print) {
  958.                         (void) sprintf(spec, "%%-%ds",
  959.                             (j < nCols - 1) ? widestName : widestName - 2
  960.                         );
  961.                         MultiLinePrintF(spec, cmdnames[k]);
  962.                     }
  963.                 }
  964.                 MultiLinePrintF("\n");
  965.             }
  966.             free(cmdnames);
  967.         }
  968.     } else if (helpall) {
  969.         /* Really intended for me, so I can debug the help strings. */
  970.         for (c = gCommands, n = 0; n < gNumCommands; c++, n++) {
  971.             PrintCmdHelp(c);
  972.             PrintCmdUsage(c);
  973.         }
  974.     } else {
  975.         /* For each command name specified, print its help stuff. */
  976.         while (--argc > 0) {
  977.             arg = *++argv;
  978.             cm = GetCommandOrMacro(arg, kAbbreviatedMatchAllowed);
  979.             if (cm == kAmbiguousName)
  980.                 MultiLinePrintF("\"%s:\" Ambiguous command or macro name.\n", arg);
  981.             else if (cm == kNoName)
  982.                 MultiLinePrintF("\"%s:\" Invalid command or macro name.\n", arg);
  983.             else if (cm->isCmd) {
  984.                 c = cm->u.cmd;
  985.                 PrintCmdHelp(c);
  986.                 PrintCmdUsage(c);
  987.             } else {
  988.                 MultiLinePrintF("\"%s\" is a macro, so no help is available.\n", arg);
  989.             }
  990.         }
  991.     }
  992.     return 0;
  993. }                                       /* HelpCmd */
  994.  
  995.  
  996.  
  997. int VersionCmd(void)
  998. {
  999.     int i;
  1000.     longstring line;
  1001.     longstring sym;
  1002.     char num[32];
  1003.     int symsOnLine;
  1004.     int symLen;
  1005.     int lineLen;
  1006.     
  1007.     MultiLineInit();
  1008.     MultiLinePrintF("Version:       %s\n", gVersion);
  1009.     MultiLinePrintF("Author:        Mike Gleason, NCEMRSoft (mgleason@probe.net)\n");
  1010. #if (kBeta == 0) && (kAlpha == 0)
  1011.     MultiLinePrintF("Archived at:   ftp://ftp.probe.net/pub/ncftp/ncftp.tgz\n");
  1012. #else
  1013.     MultiLinePrintF("Archived in:   ftp://ftp.probe.net/pub/ncftp/BETA/\n");
  1014. #endif
  1015.  
  1016. #ifdef __DATE__
  1017.     MultiLinePrintF("Compile Date:  %s\n", __DATE__);
  1018. #endif
  1019. #ifdef MK
  1020.     MultiLinePrintF("MK: %s\n", MK);
  1021. #endif
  1022.  
  1023.     MultiLinePrintF("\nCompile options:\n\n");
  1024.     line[0] = '\0';
  1025.     symsOnLine = 0;
  1026.     lineLen = 0;
  1027.     for (i=0; i<gNumCppSymbols; i++) {
  1028.         STRNCPY(sym, gCppSymbols[i].name);
  1029.         if (gCppSymbols[i].symType == 0) {
  1030.             if (gCppSymbols[i].l != 1L) {
  1031.                 sprintf(num, "=%ld", gCppSymbols[i].l);
  1032.                 STRNCAT(sym, num);
  1033.             }
  1034.             STRNCAT(sym, "  ");
  1035.         } else {
  1036.             STRNCAT(sym, "=\"");
  1037.             STRNCAT(sym, gCppSymbols[i].s);
  1038.             STRNCAT(sym, "\"  ");
  1039.         }
  1040.         symLen = (int) strlen(sym);
  1041.         if (lineLen + symLen > 79) {
  1042.             MultiLinePrintF("%s\n", line);
  1043.             line[0] = '\0';
  1044.             symsOnLine = 0;
  1045.             lineLen = 0;
  1046.         }
  1047.         STRNCAT(line, sym);
  1048.         ++symsOnLine;
  1049.         lineLen += symLen;
  1050.     }
  1051.     if (symsOnLine) {
  1052.         MultiLinePrintF("%s\n", line);
  1053.     }
  1054.     return 0;
  1055. }    /* VersionCmd */
  1056.  
  1057.  
  1058.  
  1059.  
  1060. int GenericGlobCmd(int argc, char **argv, char *cmd, int printMode)
  1061. {
  1062.     ResponsePtr rp;
  1063.     int i;
  1064.     int result, errs;
  1065.     LineList globFiles;
  1066.     LinePtr globFile;
  1067.  
  1068.     rp = InitResponse();
  1069.     for (i=1, errs=0; i<argc; i++) {
  1070.         InitLineList(&globFiles);
  1071.         RemoteGlob(&globFiles, argv[i], kListNoFlags);
  1072.         for (globFile = globFiles.first; globFile != NULL;
  1073.             globFile = globFile->next)
  1074.         {
  1075.             rp->printMode = printMode;
  1076.             result = RCmd(rp, "%s %s", cmd, globFile->line);
  1077.             if (result != 2) {
  1078.                 --errs;
  1079.                 if (UNIMPLEMENTED_CMD(rp->code)) {
  1080.                     DoneWithResponse(rp);
  1081.                     DisposeLineListContents(&globFiles);
  1082.                     return (errs);
  1083.                 }
  1084.             }
  1085.             ReInitResponse(rp);
  1086.         }
  1087.         DisposeLineListContents(&globFiles);
  1088.     }
  1089.  
  1090.     DoneWithResponse(rp);
  1091.     return (errs);
  1092. }    /* GenericGlobCmd */
  1093.  
  1094.  
  1095.  
  1096.  
  1097. int GenericCmd(int argc, char **argv, char *cmd, int printMode)
  1098. {
  1099.     ResponsePtr rp;
  1100.     int i;
  1101.     int result, errs;
  1102.  
  1103.     rp = InitResponse();
  1104.     for (i=1, errs=0; i<argc; i++) {
  1105.         rp->printMode = printMode;
  1106.         result = RCmd(rp, "%s %s", cmd, argv[i]);
  1107.         if (result != 2) {
  1108.             --errs;
  1109.             if (UNIMPLEMENTED_CMD(rp->code))
  1110.                 goto done;
  1111.         }
  1112.         ReInitResponse(rp);
  1113.     }
  1114.  
  1115. done:
  1116.     DoneWithResponse(rp);
  1117.     return (errs);
  1118. }    /* GenericCmd */
  1119.  
  1120.  
  1121.  
  1122.  
  1123. int DeleteCmd(int argc, char **argv)
  1124. {
  1125.     return GenericGlobCmd(argc, argv, "DELE", kDefaultPrint);
  1126. }    /* DeleteCmd */
  1127.  
  1128.  
  1129.  
  1130.  
  1131. int RmdirCmd(int argc, char **argv)
  1132. {
  1133.     return GenericGlobCmd(argc, argv, "RMD", kDefaultPrint);
  1134. }    /* RmdirCmd */
  1135.  
  1136.  
  1137.  
  1138.  
  1139. int MkdirCmd(int argc, char **argv)
  1140. {
  1141.     return GenericCmd(argc, argv, "MKD", kDefaultPrint);
  1142. }    /* MkdirCmd */
  1143.  
  1144.  
  1145.  
  1146.  
  1147. int RenameCmd(int argcUNUSED, char **argv)
  1148. {
  1149.     if (RCmd(kDefaultResponse, "RNFR %s", argv[1]) == 3) {
  1150.         RCmd(kDefaultResponse, "RNTO %s", argv[2]);
  1151.     }
  1152.     return 0;
  1153. }    /* MkdirCmd */
  1154.  
  1155.  
  1156.  
  1157.  
  1158. int QuoteCmd(int argc, char **argv)
  1159. {
  1160.     longstring str;
  1161.     ResponsePtr rp;
  1162.     int i;
  1163.  
  1164.     str[0] = '\0';
  1165.     for (i=1; i<argc; i++) {
  1166.         if (i > 1)
  1167.             STRNCAT(str, " ");
  1168.         STRNCAT(str, argv[i]);
  1169.     }
  1170.  
  1171.     rp = InitResponse();
  1172.     rp->printMode = kDoPrint;
  1173.     (void) RCmd(rp, "%s%s",
  1174.         argv[0][0] == 's' ? "SITE " : "",
  1175.         str
  1176.     );
  1177.     DoneWithResponse(rp);
  1178.     return 0;
  1179. }    /* QuoteCmd */
  1180.  
  1181.  
  1182.  
  1183.  
  1184. int ClearCmd(void)
  1185. {
  1186.     UpdateScreen(1);
  1187.     return 0;
  1188. }    /* ClearCmd */
  1189.  
  1190.  
  1191.  
  1192.  
  1193. int RmtHelpCmd(int argc, char **argv)
  1194. {
  1195.     ResponsePtr rp;
  1196.  
  1197.     if (argc > 1)
  1198.         GenericCmd(argc, argv, "HELP", kDoPrint);
  1199.     else {
  1200.         rp = InitResponse();
  1201.         rp->printMode = kDoPrint;
  1202.         (void) RCmd(rp, "HELP");
  1203.         DoneWithResponse(rp);
  1204.     }
  1205.     return 0;
  1206. }    /* RmtHelpCmd */
  1207.  
  1208.  
  1209.  
  1210.  
  1211. int ShellCmd(int argc, char **argv)
  1212. {
  1213.     int result;
  1214.     char *volatile theShell;
  1215.     char *cmdLine;
  1216.     VSig_t si;
  1217.  
  1218.     si = (VSig_t) 0;
  1219.     if ((theShell = (char *) getenv("SHELL")) == NULL)
  1220.         theShell = gUserInfo.shell;
  1221.     if (theShell == NULL)
  1222.         theShell = "/bin/sh";
  1223.  
  1224.     SaveScreen();
  1225.     if (setjmp(gShellJmp) != 0) {
  1226.         /* Command was interrupted. */
  1227.         (void) SIGNAL(SIGINT, SIG_IGN);
  1228.         result = 1;
  1229.     } else {
  1230.         si = SIGNAL(SIGINT, SigLocalPage);
  1231.         if (argc < 2)
  1232.             result = system(theShell);
  1233.         else {
  1234.             /* We have a hack where we keep a copy of the original
  1235.              * command line before parsing at position argc + 2.
  1236.              */
  1237.             cmdLine = CMDLINEFROMARGS(argc, argv);
  1238.             
  1239.             /* Skip the ! and whitespace after it. */
  1240.             while ((*cmdLine == '!') || isspace(*cmdLine))
  1241.                 cmdLine++;
  1242.             result = system(cmdLine);
  1243.         }
  1244.     }
  1245.     RestoreScreen(1);
  1246.     if (si != (VSig_t) 0)
  1247.         (void) SIGNAL(SIGINT, si);
  1248.     return result;
  1249. }    /* ShellCmd */
  1250.  
  1251.  
  1252.  
  1253.  
  1254. int EchoCmd(int argc, char **argv)
  1255. {
  1256.     longstring str;
  1257.     int i;
  1258.     int noNewLine = 0;
  1259.  
  1260.     for (i=1; i<argc; i++) {
  1261.         (void) FlagStrCopy(str, sizeof(str), argv[i]);
  1262.         /* The above writes an @ sign after the nul if we were supposed
  1263.          * to not issue a final newline.
  1264.          */
  1265.         noNewLine = (str[strlen(str) + 1] == '@');
  1266.         PrintF("%s%s", (i > 1 ? " " : ""), str);
  1267.     }
  1268.     if (!noNewLine)
  1269.         PrintF("\n");
  1270.     return 0;
  1271. }    /* EchoCmd */
  1272.  
  1273.  
  1274.  
  1275.  
  1276. int LookupCmd(int argc, char **argv)
  1277. {
  1278.     int i, j;
  1279.     struct hostent *hp;
  1280.     char *host, **cpp;
  1281.     struct in_addr ip_address;
  1282.     int shortMode, opt;
  1283.     char ipStr[16];
  1284.  
  1285.     shortMode = 1;
  1286.     
  1287.     GetoptReset();
  1288.     while ((opt = Getopt(argc, argv, "v")) >= 0) {
  1289.         if (opt == 'v')
  1290.             shortMode = 0;
  1291.         else
  1292.             return kUsageErr;
  1293.     }
  1294.  
  1295.     for (i=gOptInd; i<argc; i++) {
  1296.         hp = GetHostEntry((host = argv[i]), &ip_address);
  1297.         if ((i > gOptInd) && (shortMode == 0))
  1298.             PrintF("\n");
  1299.         if (hp == NULL) {
  1300.             PrintF("Unable to get information about site %s.\n", host);
  1301.         } else if (shortMode) {
  1302.             MyInetAddr(ipStr, sizeof(ipStr), hp->h_addr_list, 0);
  1303.             PrintF("%-40s %s\n", hp->h_name, ipStr);
  1304.         } else {
  1305.             PrintF("%s:\n", host);
  1306.             PrintF("    Name:     %s\n", hp->h_name);
  1307.             for (cpp = hp->h_aliases; *cpp != NULL; cpp++)
  1308.                 PrintF("    Alias:    %s\n", *cpp);
  1309.             for (j = 0, cpp = hp->h_addr_list; *cpp != NULL; cpp++, ++j) {
  1310.                 MyInetAddr(ipStr, sizeof(ipStr), hp->h_addr_list, j);
  1311.                 PrintF("    Address:  %s\n", ipStr);    
  1312.             }
  1313.         }
  1314.     }
  1315.     return 0;
  1316. }    /* LookupCmd */
  1317.  
  1318.  
  1319.  
  1320.  
  1321. int BookmarkCmd(int argcUNUSED, char **argv)
  1322. {
  1323.     SaveBookmark(argv[1]);
  1324.     return 0;
  1325. }    /* BookmarkCmd */
  1326.