home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume21 / coda / part03 / client.c next >
C/C++ Source or Header  |  1990-04-08  |  15KB  |  727 lines

  1. /*
  2. **  Copyright 1989 BBN Systems and Technologies Corporation.
  3. **  All Rights Reserved.
  4. **  This is free software, and may be distributed under the terms of the
  5. **  GNU Public License; see the file COPYING for more details.
  6. **
  7. **  Main code for CODA client.
  8. */
  9. #define MAINLINE
  10. #include "client.h"
  11. #ifndef    VMS
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #else
  15. #include <types.h>
  16. #include <stat.h>
  17. #endif    /* VMS */
  18. #ifdef    RCSID
  19. static char RCS[] =
  20.     "$Header: client.c,v 2.0 90/04/09 15:34:23 rsalz Exp $";
  21. #endif    /* RCSID */
  22.  
  23.  
  24. /*
  25. **  A whole mess of global variables.
  26. */
  27. STATIC char    ACK[] = "ACK-";        /* Header if command was OK    */
  28. STATIC char    DAT[] = "DAT ITEM ";    /* Data file for from command    */
  29. STATIC char    NAK[] = "NAK-";        /* Header if it wasn't        */
  30. STATIC char    **Lines;        /* Lines read from server    */
  31. STATIC char    *Directory;        /* Where sources reside        */
  32. STATIC char    *Filename;        /* File for server to read    */
  33. STATIC char    *Root;            /* Remote root directory    */
  34. STATIC char    *ServerHost;        /* Host server is on        */
  35. STATIC char    *UserName;        /* Remote user name        */
  36. STATIC char    *UserPass;        /* Remote user password        */
  37. STATIC int    MaxLines;        /* Number of lines allocated    */
  38. STATIC BOOL    Noaction;        /* Just say what's outdated?    */
  39. STATIC int    NumLines;        /* Number of lines used        */
  40. STATIC int    Port = PORTNUMBER;    /* Port server is listening    */
  41. STATIC int    Verbose;        /* Tell what we're doing?    */
  42. STATIC BOOL    ChangeOwner;        /* Try to give away files?    */
  43. STATIC BOOL    SlowMode;        /* Do byte comparisons?        */
  44. STATIC BOOL    YoungerMode;        /* Can client have newer files?    */
  45.  
  46.  
  47.  
  48. /*
  49. **  Print an error message and exit.
  50. */
  51. void
  52. Fatal(p)
  53.     char    *p;
  54. {
  55.     (void)fprintf(stderr, "Coda fatal error:\n\t%s\n", p);
  56.     exit(EXITERR);
  57.     /* NOTREACHED */
  58. }
  59.  
  60.  
  61. /*
  62. **  Get a line from the server; if it's a NAK, quit.
  63. */
  64. STATIC void
  65. QuitOnNack()
  66. {
  67.     char    buff[SIZE];
  68.  
  69.     if (!SRVget(buff, sizeof buff))
  70.     Fatal("Server did not reply");
  71.     if (strncmp(buff, ACK, sizeof ACK - 1) == 0)
  72.     return;
  73.     if (strncmp(buff, NAK, sizeof NAK - 1) == 0)
  74.     Fatal(&buff[sizeof NAK - 1]);
  75.     (void)fprintf(stderr, "Bad from server:\n\t%s\n", buff);
  76.     Fatal("Protocol error");
  77. }
  78.  
  79.  
  80. /*
  81. **  Print a warning if user wants to see them.
  82. */
  83. STATIC void
  84. Cant(Always, Action, Name)
  85.     BOOL    Always;
  86.     char    *Action;
  87.     char    *Name;
  88. {
  89.     char    buff[SIZE];
  90.     int        e;
  91.  
  92.     if (Always || Verbose > 2) {
  93.     e = errno;
  94.     (void)sprintf(buff, "Can't %s %s", Action, Name);
  95.     errno = e;
  96.     perror(buff);
  97.     }
  98. }
  99.  
  100.  
  101.  
  102. /*
  103. **  Set the mode, ownership, etc., on an item to be what they should be.
  104. */
  105. STATIC void
  106. SetStuff(Name, Mode, Uid, Gid, Time)
  107.     char    *Name;
  108.     int        Mode;
  109.     int        Uid;
  110.     int        Gid;
  111.     long    Time;
  112. {
  113.     struct stat     Sb;
  114. #ifdef    ATTsysv
  115.     struct utime_t {
  116.     time_t    x;
  117.     time_t    y;
  118.     } t, *vec = &t;
  119. #else
  120.     time_t     vec[2];
  121. #endif    /* ATTsysv */
  122.  
  123.     /* Get current status. */
  124.     if (stat(Name, &Sb) < 0) {
  125.     Cant(TRUE, "stat", Name);
  126.     return;
  127.     }
  128.  
  129.     /* Fix up the permissions on files. */
  130.     if ((Sb.st_mode & 0777) != Mode) {
  131.     if (chmod(Name, Mode) < 0)
  132.         Cant(FALSE, "chmod", Name);
  133.     else if (Verbose > 1)
  134.         (void)printf("chmod 0%o %s\n", Mode, Name);
  135.     }
  136.  
  137.     /* Fix up the modication time. */
  138.     if (Sb.st_mtime != Time) {
  139. #ifdef    ATTsysv
  140.     t.x = t.y = Time;
  141. #else
  142.     vec[0] = vec[1] = Time;
  143. #endif    /* ATTsysv */
  144.     if (utime(Name, vec) < 0)
  145.         Cant(FALSE, "set time", Name);
  146.     else if (Verbose > 1)
  147.         (void)printf("settime %ld %s\n", (long)Time, Name);
  148.     }
  149.  
  150.     /* Fix up the owner. */
  151.     if (ChangeOwner && (Sb.st_uid != Uid || Sb.st_gid != Gid)) {
  152.     if (chown(Name, Uid, Gid) < 0)
  153.         Cant(FALSE, "chown", Name);
  154.     else if (Verbose > 1)
  155.         (void)printf("chown %d chgrp %d %s\n", Uid, Gid, Name);
  156.     }
  157. }
  158.  
  159.  
  160. /*
  161. **  Do what's necessary to create a directory.
  162. */
  163. STATIC void
  164. DoDir(Name, Uid, Gid, Mode, Time)
  165.     char    *Name;
  166.     int        Uid;
  167.     int        Gid;
  168.     int        Mode;
  169.     long    Time;
  170. {
  171.     struct stat    Sb;
  172. #ifdef    VMS
  173.     char    buff[SIZE];
  174. #endif    /* VMS */
  175.  
  176. #ifdef    VMS
  177.     (void)sprintf(buff, "%s.dir", Name);
  178.     Name = buff;
  179. #endif    /* VMS */
  180.  
  181.     /* If directory doesn't exist, create it. */
  182.     if (stat(Name, &Sb) < 0) {
  183.     if (Verbose > 0 || Noaction) {
  184.         (void)printf("mkdir %s\n", Name);
  185.         if (Noaction)
  186.         return;
  187.     }
  188.     if (mkdir(Name, Mode) < 0)
  189.         Cant(TRUE, "mkdir", Name);
  190.     }
  191.  
  192.     /* Set the other bits. */
  193.     if (!Noaction)
  194.     SetStuff(Name, Mode, Uid, Gid, Time);
  195. }
  196.  
  197.  
  198.  
  199. /*
  200. **  Compare two files, if they're different, move the second onto
  201. **  the first.
  202. */
  203. STATIC void
  204. GetFile(Name, Size, KnowItsWrong)
  205.     char        *Name;
  206.     REGISTER long    Size;
  207.     BOOL        KnowItsWrong;
  208. {
  209.     REGISTER FILE    *F;
  210.     REGISTER FILE    *Old;
  211.     REGISTER char    *p;
  212.     REGISTER int    c;
  213.     REGISTER int     c2;
  214.     char        buff[SIZE];
  215.     char        temp[SIZE];
  216.  
  217.     if (!KnowItsWrong && Verbose > 4)
  218.     (void)printf("Checking %s\n", Name);
  219.  
  220.     /* Make a temporary filename in the same directory. */
  221.     (void)strcpy(temp, Name);
  222.     if (p = strrchr(temp, '/'))
  223.     p++;
  224.     else
  225.     p = temp;
  226.     (void)strcpy(p, "codaXXXXXX");
  227.     (void)mktemp(p);
  228.  
  229.     if ((F = fopen(temp, "w")) == NULL) {
  230.     Cant(TRUE, "fopen", temp);
  231.     return;
  232.     }
  233.  
  234.     /* Tell the server to send us the file. */
  235.     (void)sprintf(buff, "SEND %s\n", Name);
  236.     SRVput(buff);
  237.     if (!SRVget(buff, sizeof buff)
  238.      || strncmp(buff, NAK, sizeof NAK - 1) == 0) {
  239.     Cant(TRUE, "start to read", temp);
  240.     (void)fclose(F);
  241.     (void)unlink(temp);
  242.     return;
  243.     }
  244.  
  245.     /* Read it from the server. */
  246.     while (--Size >= 0) {
  247.     c = SRVcget();
  248.     (void)putc(c, F);
  249.     }
  250.     (void)fclose(F);
  251.     QuitOnNack();
  252.  
  253.     /* If we know it's wrong, just move it. */
  254.     if (KnowItsWrong) {
  255.     (void)unlink(Name);
  256.     if (rename(temp, Name) < 0)
  257.         Cant(TRUE, "rename to", Name);
  258.     (void)sprintf(buff, "MESG took %s\n", Name);
  259.     SRVput(buff);
  260.     QuitOnNack();
  261.     return;
  262.     }
  263.  
  264.     /* Open both old and new files. */
  265.     if ((F = fopen(temp, "r")) == NULL) {
  266.     Cant(TRUE, "reopen", temp);
  267.     return;
  268.     }
  269.     if ((Old = fopen(Name, "r")) == NULL) {
  270.     Cant(TRUE, "fopen", Name);
  271.     (void)fclose(F);
  272.     return;
  273.     }
  274.  
  275.     /* Do byte-for-byte comparisons. */
  276.     while ((c = getc(F)) == (c2 = getc(Old)))
  277.     if (c == EOF)
  278.         break;
  279.     (void)fclose(F);
  280.     (void)fclose(Old);
  281.  
  282.     /* Different? */
  283.     if (c != c2) {
  284.     if (Verbose || Noaction) {
  285.         (void)printf("Update %s\n", Name);
  286.         if (Noaction) {
  287.         (void)unlink(temp);
  288.         return;
  289.         }
  290.     }
  291.     (void)unlink(Name);
  292.     if (rename(temp, Name) < 0)
  293.         Cant(TRUE, "rename to", Name);
  294.     (void)sprintf(buff, "MESG took %s\n", Name);
  295.     }
  296.     else
  297.     (void)sprintf(buff, "MESG ignore %s\n", Name);
  298.     SRVput(buff);
  299.     QuitOnNack();
  300.     (void)unlink(temp);
  301. }
  302.  
  303.  
  304. /*
  305. **  Do what's necessary to create a file.
  306. */
  307. STATIC void
  308. DoFile(Name, Uid, Gid, Mode, Time, Size)
  309.     char        *Name;
  310.     int            Uid;
  311.     int            Gid;
  312.     int            Mode;
  313.     long        Time;
  314.     long        Size;
  315. {
  316.     struct stat        Sb;
  317.  
  318.     if (stat(Name, &Sb) < 0 || Sb.st_size != Size) {
  319.     /* File doesn't exist or size is wrong; get it. */
  320.     if (YoungerMode && Sb.st_mtime > Time) {
  321.         (void)printf("Younger %s\n", Name);
  322.         return;
  323.     }
  324.     if (Verbose || Noaction) {
  325.         (void)printf("Update %s\n", Name);
  326.         if (Noaction)
  327.         return;
  328.     }
  329.     GetFile(Name, Size, TRUE);
  330.     }
  331.     else {
  332.     if (YoungerMode && Sb.st_mtime > Time) {
  333.         (void)printf("Younger %s\n", Name);
  334.         return;
  335.     }
  336.     if (SlowMode || Sb.st_mtime != Time)
  337.         /* Slow mode or the times are different; get and check. */
  338.         GetFile(Name, Size, FALSE);
  339.     }
  340.  
  341.     /* Set the other bits. */
  342.     if (!Noaction)
  343.     SetStuff(Name, Mode, Uid, Gid, Time);
  344. }
  345.  
  346.  
  347. /*
  348. **  Read the list of files from the server, than parse them.
  349. */
  350. STATIC void
  351. ParseListResult()
  352. {
  353.     REGISTER char    **L;
  354.     REGISTER char    **Lend;
  355.     REGISTER char    *p;
  356.     char        buff[SIZE];
  357.     char        *Name;
  358.     int            Uid;
  359.     int            Gid;
  360.     long        Size;
  361.     long        Time;
  362.     int            Mode;
  363.     int            What;
  364.  
  365.     /* Read lines from server. */
  366.     for (NumLines = 0; SRVget(buff, sizeof buff); ) {
  367.     /* Check for end of list or a bad line. */
  368.     if (strncmp(buff, NAK, sizeof NAK - 1) == 0) {
  369.         (void)printf("%s\n", &buff[sizeof NAK - 1]);
  370.         return;
  371.     }
  372.     if (strncmp(buff, ACK, sizeof ACK - 1) == 0) {
  373.         (void)printf("%s\n", &buff[sizeof ACK - 1]);
  374.         break;
  375.     }
  376.     if (strncmp(buff, DAT, sizeof DAT - 1)) {
  377.         (void)fprintf(stderr, "Bogus line from the server:\n\t%s\n", buff);
  378.         continue;
  379.     }
  380.  
  381.     /* Need more room for this line? */
  382.     if (NumLines == MaxLines - 1) {
  383.         MaxLines += LINES_DELTA;
  384.         Lines = GROW(Lines, char*, MaxLines);
  385.     }
  386.     Lines[NumLines++] = COPY(&buff[sizeof DAT - 1]);
  387.     }
  388.  
  389.     /* Now loop over what we got and parse it. */
  390.     for (L = Lines, Lend = &Lines[NumLines]; L < Lend; L++) {
  391.     /* Set defaults to show the stuff is bad. */
  392.     Name = NULL;
  393.     Uid = -1;
  394.     Gid = -1;
  395.     Size = -1;
  396.     Time = -1;
  397.     Mode = -1;
  398.     What = -1;
  399.     for (p = strcpy(buff, *L); *p; ) {
  400.         switch (*p) {
  401.         default:
  402.         (void)fprintf(stderr, "Bad field from server:\n\t%s\n", *L);
  403.         break;
  404.         case 'N':
  405.         Name = &p[2];
  406.         break;
  407.         case 'W':
  408.         if (p[2] == 'd' || p[2] == 'f')
  409.             What = p[2];
  410.         break;
  411.         case 'U':
  412.         Uid = atoi(&p[2]);
  413.         break;
  414.         case 'G':
  415.         Gid = atoi(&p[2]);
  416.         break;
  417.         case 'M':
  418.         Mode = atoi(&p[2]);
  419.         break;
  420.         case 'S':
  421.         Size = atol(&p[2]);
  422.         break;
  423.         case 'T':
  424.         Time = atol(&p[2]);
  425.         break;
  426.         }
  427.  
  428.         /* Move to next field. */
  429.         while (*p && !WHITE(*p))
  430.         p++;
  431.         if (*p)
  432.         for (*p++ = '\0'; *p && WHITE(*p); )
  433.             p++;
  434.     }
  435.  
  436.     /* Got everything? */
  437.     if (What < 0 || Mode < 0 || Gid < 0 || Size < 0 || Time < 0
  438. #ifdef    NOBODY
  439.      || (Uid < 0 && Uid != NOBODY)
  440. #else
  441.      || Uid < 0
  442. #endif    /* NOBODY */
  443.      || Name == NULL)
  444.         (void)fprintf(stderr, "Badly formed line:\n\t%s\n", *L);
  445.     else if (What == 'd')
  446.         DoDir(Name, Uid, Gid, Mode, Time);
  447.     else
  448.         DoFile(Name, Uid, Gid, Mode, Time, Size);
  449.     }
  450. }
  451.  
  452.  
  453.  
  454. /*
  455. **  Scan a word for yes or no.
  456. */
  457. STATIC int
  458. GetYesOrNo(p, where, flag)
  459.     char    *p;
  460.     char    *where;
  461.     char    flag;
  462. {
  463.     char    buff[SIZE];
  464.  
  465.     switch (*p++) {
  466.     case 'Y': case 'y':
  467.     if (p[0] == '\0')
  468.         return TRUE;
  469.     if ((p[1] == 'E' || p[1] == 'e')
  470.      && (p[2] == 'S' || p[2] == 's')
  471.      && p[3] == '\0')
  472.     break;
  473.     case 'N': case 'n':
  474.     if (p[0] == '\0')
  475.         return TRUE;
  476.     if ((p[1] == 'O' || p[1] == 'o')
  477.      && p[2] == '\0')
  478.         return TRUE;
  479.     break;
  480.     }
  481.     (void)sprintf(buff, "Expecting one of [YyNn] %s for '%c' flag", where, flag);
  482.     Fatal(buff);
  483.     /* NOTREACHED */
  484. }
  485.  
  486.  
  487. /*
  488. **  Skip past the current word and any whitespace that follows it.
  489. */
  490. STATIC char *
  491. Skip(p)
  492.     char    *p;
  493. {
  494.     while (*p && !WHITE(*p))
  495.     p++;
  496.     while (*p && WHITE(*p))
  497.     p++;
  498.     return p;
  499. }
  500.  
  501.  
  502. /*
  503. **  Read the init file.
  504. */
  505. STATIC void
  506. ReadInitFile()
  507. {
  508.     static char    Where[] = "in init file";
  509.     FILE    *F;
  510.     char    *p;
  511.     char    buff[SIZE];
  512.  
  513.     /* Get the filename, open the file. */
  514.     if ((p = getenv("CODA")) == NULL || (F = fopen(p, "r")) == NULL) {
  515. #ifdef    VMS
  516.     if ((F = fopen(CODA_INIT, "r")) == NULL)
  517.         return;
  518. #else
  519.     if ((p = getenv("HOME")) == NULL)
  520.         return;
  521.     (void)sprintf(buff, CODA_INIT, p);
  522.     if ((F = fopen(buff, "r")) == NULL)
  523.         return;
  524. #endif    /* VMS */
  525.     }
  526.  
  527.     while (fgets(buff, sizeof buff, F)) {
  528.     /* Skip blank and comment lines. */
  529.     if (p = strchr(buff, '\n'))
  530.         *p = '\0';
  531.     if (buff[0] == '\0' || buff[0] == '#')
  532.         continue;
  533.  
  534.     if (strncmp(buff, "dir", 3) == 0) {
  535.         p = Skip(buff);
  536.         Directory = COPY(p);
  537.     }
  538.     else if (strncmp(buff, "file", 4) == 0) {
  539.         p = Skip(buff);
  540.         Filename = COPY(p);
  541.     }
  542.     else if (strncmp(buff, "host", 4) == 0) {
  543.         p = Skip(buff);
  544.         ServerHost = COPY(p);
  545.     }
  546.     else if (strncmp(buff, "owner", 5) == 0) {
  547.         p = Skip(buff);
  548.         ChangeOwner = GetYesOrNo(p, Where, 'o');
  549.     }
  550.     else if (strncmp(buff, "pass", 4) == 0) {
  551.         p = Skip(buff);
  552.         UserPass = COPY(p);
  553.     }
  554.     else if (strncmp(buff, "port", 4) == 0) {
  555.         p = Skip(buff);
  556.         Port = atoi(p);
  557.     }
  558.     else if (strncmp(buff, "root", 4) == 0) {
  559.         p = Skip(buff);
  560.         Root = COPY(p);
  561.     }
  562.     else if (strncmp(buff, "slow", 4) == 0) {
  563.         p = Skip(buff);
  564.         SlowMode = GetYesOrNo(p, Where, 's');
  565.     }
  566.     else if (strncmp(buff, "user", 4) == 0) {
  567.         p = Skip(buff);
  568.         UserName = COPY(p);
  569.     }
  570.     else if (strncmp(buff, "verb", 4) == 0) {
  571.         p = Skip(buff);
  572.         Verbose = atoi(p);
  573.     }
  574.     else if (strncmp(buff, "young", 5) == 0) {
  575.         p = Skip(buff);
  576.         YoungerMode = GetYesOrNo(p, Where, 'y');
  577.     }
  578.     else {
  579.         (void)fprintf(stderr, "Unknown line:\n\t%s\n", buff);
  580.         Fatal("Bad init file");
  581.     }
  582.     }
  583.     (void)fclose(F);
  584. }
  585.  
  586.  
  587. main(ac, av)
  588.     int        ac;
  589.     char    *av[];
  590. {
  591.     static char    Where[] = "on command line";
  592.     char    buff[SIZE];
  593.     char    pass[SIZE];
  594.     int        i;
  595.  
  596.     /* Get defaults from environment if possible. */
  597.     ReadInitFile();
  598.     (void)umask(0);
  599.  
  600.     /* Parse JCL. */
  601.     while ((i = getopt(ac, av, "cd:f:h:no:p:r:s:tu:v:x:y:")) != EOF)
  602.     switch (i) {
  603.     default:
  604.         Fatal("Bad calling sequence");
  605.         /* NOTREACHED */
  606.     case 'c':
  607.         Copyright();
  608.         exit(EXITOK);
  609.         /* NOTREACHED */
  610.     case 'd':
  611.         Directory = optarg;
  612.         break;
  613.     case 'f':
  614.         Filename = optarg;
  615.         break;
  616.     case 'h':
  617.         ServerHost = optarg;
  618.         break;
  619.     case 'n':
  620.         Noaction = TRUE;
  621.         break;
  622.     case 'o':
  623.         ChangeOwner = GetYesOrNo(optarg, Where, 'o');
  624.         break;
  625.     case 'p':
  626.         Port = atoi(optarg);
  627.         break;
  628.     case 'r':
  629.         Root = optarg;
  630.         break;
  631.     case 's':
  632.         SlowMode = GetYesOrNo(optarg, Where, 's');
  633.         break;
  634.     case 't':
  635.         SRVtrace = TRUE;
  636.         break;
  637.     case 'u':
  638.         UserName = optarg;
  639.         break;
  640.     case 'v':
  641.         Verbose = atoi(optarg);
  642.         break;
  643.     case 'x':
  644.         UserPass = optarg;
  645.         break;
  646.     case 'y':
  647.         YoungerMode = GetYesOrNo(optarg, Where, 'y');
  648.         break;
  649.     }
  650.     av += optind;
  651.  
  652.     /* Got everything we need? */
  653.     if (ServerHost == NULL)
  654.     Fatal("Need name of server host");
  655.     if (Port == 0)
  656.     Fatal("Need port to connect to");
  657.     if (UserName == NULL)
  658.     Fatal("Need your name on the server machine");
  659.  
  660.     /* Get passwword. */
  661.     if (UserPass == NULL) {
  662.     (void)printf("Enter password:");
  663.     (void)fflush(stdout);
  664.     GetPassword(pass, sizeof pass);
  665.     UserPass = pass;
  666.     }
  667.  
  668.     /* Talk to the server */
  669.     if (!SRVopen(ServerHost, Port))
  670.     Fatal("Can't open connection to server");
  671.     QuitOnNack();
  672.  
  673.     /* Log in. */
  674.     (void)sprintf(buff, "USER %s %s", UserName, UserPass);
  675.     SRVput(buff);
  676.     QuitOnNack();
  677.  
  678.     /* Tell the server where to go. */
  679.     if (Directory) {
  680.     (void)sprintf(buff, "GOTO %s", Directory);
  681.     SRVput(buff);
  682.     QuitOnNack();
  683.     }
  684.  
  685.     /* Read the file. */
  686.     if (Filename) {
  687.     (void)sprintf(buff, "READ %s", Filename);
  688.     SRVput(buff);
  689.     }
  690.     else
  691.     SRVput("READ");
  692.     QuitOnNack();
  693.  
  694.     /* Set the root directory. */
  695.     if (Root) {
  696.     (void)sprintf(buff, "ROOT %s", Root);
  697.     SRVput(buff);
  698.     QuitOnNack();
  699.     }
  700.  
  701.     /* Any other arguments are block names. */
  702.     if (*av) {
  703.     /* Get some space. */
  704.     MaxLines = LINES_DELTA;
  705.     Lines = NEW(char*, MaxLines);
  706.     for ( ; *av; av++) {
  707.         (void)printf("Doing %s...\n", *av);
  708.         (void)sprintf(buff, "LIST %s", *av);
  709.         SRVput(buff);
  710.         ParseListResult();
  711.     }
  712.     }
  713.     else {
  714.     /* No arguments means do the whole shebang. */
  715.     MaxLines = LINES_DELTA * 2;
  716.     Lines = NEW(char*, MaxLines);
  717.     (void)printf("Working...\n");
  718.     SRVput("LIST");
  719.     ParseListResult();
  720.     }
  721.  
  722.     /* That's all she wrote. */
  723.     SRVclose();
  724.     exit(EXITOK);
  725.     /* NOTREACHED */
  726. }
  727.