home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-src.tgz / tar.out / fsf / cvs / src / main.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  25KB  |  842 lines

  1. /*
  2.  *    Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  *    Copyright (c) 1989-1992, Brian Berliner
  4.  *
  5.  *    You may distribute under the terms of the GNU General Public License
  6.  *    as specified in the README file that comes with the CVS 1.4 kit.
  7.  *
  8.  * This is the main C driver for the CVS system.
  9.  *
  10.  * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
  11.  * the shell-script CVS system that this is based on.
  12.  *
  13.  */
  14.  
  15. #include "cvs.h"
  16.  
  17. #ifdef HAVE_WINSOCK_H
  18. #include <winsock.h>
  19. #else
  20. extern int gethostname ();
  21. #endif
  22.  
  23. char *program_name;
  24. char *program_path;
  25. /*
  26.  * Initialize comamnd_name to "cvs" so that the first call to
  27.  * read_cvsrc tries to find global cvs options.
  28.  */
  29. char *command_name = "";
  30.  
  31. /*
  32.  * Since some systems don't define this...
  33.  */
  34. #ifndef MAXHOSTNAMELEN
  35. #define MAXHOSTNAMELEN  256
  36. #endif
  37.  
  38. char hostname[MAXHOSTNAMELEN];
  39.  
  40. int use_editor = TRUE;
  41. int use_cvsrc = TRUE;
  42. int cvswrite = !CVSREAD_DFLT;
  43. int really_quiet = FALSE;
  44. int quiet = FALSE;
  45. int trace = FALSE;
  46. int noexec = FALSE;
  47. int logoff = FALSE;
  48. mode_t cvsumask = UMASK_DFLT;
  49.  
  50. char *CurDir;
  51.  
  52. /*
  53.  * Defaults, for the environment variables that are not set
  54.  */
  55. char *Rcsbin = RCSBIN_DFLT;
  56. char *Editor = EDITOR_DFLT;
  57. /*
  58.  * The path found in CVS/Root must match $CVSROOT and/or 'cvs -d root'
  59.  */
  60. int add PROTO((int argc, char **argv));
  61. int admin PROTO((int argc, char **argv));
  62. int checkout PROTO((int argc, char **argv));
  63. int commit PROTO((int argc, char **argv));
  64. int diff PROTO((int argc, char **argv));
  65. int history PROTO((int argc, char **argv));
  66. int import PROTO((int argc, char **argv));
  67. int cvslog PROTO((int argc, char **argv));
  68. #ifdef AUTH_CLIENT_SUPPORT
  69. int login PROTO((int argc, char **argv));
  70. #endif /* AUTH_CLIENT_SUPPORT */
  71. int patch PROTO((int argc, char **argv));
  72. int release PROTO((int argc, char **argv));
  73. int cvsremove PROTO((int argc, char **argv));
  74. int rtag PROTO((int argc, char **argv));
  75. int status PROTO((int argc, char **argv));
  76. int tag PROTO((int argc, char **argv));
  77. int update PROTO((int argc, char **argv));
  78.  
  79. static const struct cmd
  80. {
  81.     char *fullname;        /* Full name of the function (e.g. "commit") */
  82.  
  83.     /* Synonyms for the command, nick1 and nick2.  We supply them
  84.        mostly for two reasons: (1) CVS has always supported them, and
  85.        we need to maintain compatibility, (2) if there is a need for a
  86.        version which is shorter than the fullname, for ease in typing.
  87.        Synonyms have the disadvantage that people will see "new" and
  88.        then have to think about it, or look it up, to realize that is
  89.        the operation they know as "add".  Also, this means that one
  90.        cannot create a command "cvs new" with a different meaning.  So
  91.        new synonyms are probably best used sparingly, and where used
  92.        should be abbreviations of the fullname (preferably consisting
  93.        of the first 2 or 3 or so letters).  */
  94.  
  95.     char *nick1;
  96.     char *nick2;
  97.  
  98.     int (*func) ();        /* Function takes (argc, argv) arguments. */
  99. } cmds[] =
  100.  
  101. {
  102.     { "add",      "ad",       "new",       add },
  103.     { "admin",    "adm",      "rcs",       admin },
  104.     { "annotate", "ann",      NULL,        annotate },
  105.     { "checkout", "co",       "get",       checkout },
  106.     { "commit",   "ci",       "com",       commit },
  107.     { "diff",     "di",       "dif",       diff },
  108.     { "edit",     NULL,          NULL,       edit },
  109.     { "editors",  NULL,       NULL,       editors },
  110.     { "export",   "exp",      "ex",        checkout },
  111.     { "history",  "hi",       "his",       history },
  112.     { "import",   "im",       "imp",       import },
  113.     { "init",     NULL,       NULL,        init },
  114. #ifdef SERVER_SUPPORT
  115.     { "kserver",  NULL,       NULL,        server }, /* placeholder */
  116. #endif
  117.     { "log",      "lo",       "rlog",      cvslog },
  118. #ifdef AUTH_CLIENT_SUPPORT
  119.     { "login",    "logon",    "lgn",       login },
  120. #ifdef SERVER_SUPPORT
  121.     { "pserver",  NULL,       NULL,        server }, /* placeholder */
  122. #endif
  123. #endif /* AUTH_CLIENT_SUPPORT */
  124.     { "rdiff",    "patch",    "pa",        patch },
  125.     { "release",  "re",       "rel",       release },
  126.     { "remove",   "rm",       "delete",    cvsremove },
  127.     { "status",   "st",       "stat",      status },
  128.     { "rtag",     "rt",       "rfreeze",   rtag },
  129.     { "tag",      "ta",       "freeze",    tag },
  130.     { "unedit",   NULL,          NULL,       unedit },
  131.     { "update",   "up",       "upd",       update },
  132.     { "watch",    NULL,          NULL,       watch },
  133.     { "watchers", NULL,          NULL,       watchers },
  134. #ifdef SERVER_SUPPORT
  135.     { "server",   NULL,       NULL,        server },
  136. #endif
  137.     { NULL, NULL, NULL, NULL },
  138. };
  139.  
  140. static const char *const usg[] =
  141. {
  142.     "Usage: %s [cvs-options] command [command-options] [files...]\n",
  143.     "    Where 'cvs-options' are:\n",
  144.     "        -H           Displays Usage information for command\n",
  145.     "        -Q           Cause CVS to be really quiet.\n",
  146.     "        -q           Cause CVS to be somewhat quiet.\n",
  147.     "        -r           Make checked-out files read-only\n",
  148.     "        -w           Make checked-out files read-write (default)\n",
  149.     "        -l           Turn History logging off\n",
  150.     "        -n           Do not execute anything that will change the disk\n",
  151.     "        -t           Show trace of program execution -- Try with -n\n",
  152.     "        -v           CVS version and copyright\n",
  153.     "        -b bindir    Find RCS programs in 'bindir'\n",
  154.     "        -e editor    Use 'editor' for editing log information\n",
  155.     "        -d CVS_root  Overrides $CVSROOT as the root of the CVS tree\n",
  156.     "        -f           Do not use the ~/.cvsrc file\n",
  157. #ifdef CLIENT_SUPPORT
  158.     "        -z #         Use 'gzip -#' for net traffic if possible.\n",
  159.     "        -x           Encrypt all net traffic.\n",
  160. #endif
  161.     "        -s VAR=VAL   Set CVS user variable.\n",
  162.     "\n",
  163.     "    and where 'command' is: add, admin, etc. (use the --help-commands\n",
  164.     "    option for a list of commands)\n",
  165.     NULL,
  166. };
  167.  
  168. static const char *const cmd_usage[] =
  169. {
  170.     "CVS commands are:\n",
  171.     "        add          Add a new file/directory to the repository\n",
  172.     "        admin        Administration front end for rcs\n",
  173.     "        annotate     Show last revision where each line was modified\n",
  174.     "        checkout     Checkout sources for editing\n",
  175.     "        commit       Check files into the repository\n",
  176.     "        diff         Run diffs between revisions\n",
  177.     "        edit         Get ready to edit a watched file\n",
  178.     "        editors      See who is editing a watched file\n",
  179.     "        export       Export sources from CVS, similar to checkout\n",
  180.     "        history      Show repository access history\n",
  181.     "        import       Import sources into CVS, using vendor branches\n",
  182.     "        init         Create a CVS repository if it doesn't exist\n",
  183.     "        log          Print out history information for files\n",
  184. #ifdef AUTH_CLIENT_SUPPORT
  185.     "        login        Prompt for password for authenticating server.\n",
  186. #endif /* AUTH_CLIENT_SUPPORT */
  187.     "        rdiff        Create 'patch' format diffs between releases\n",
  188.     "        release      Indicate that a Module is no longer in use\n",
  189.     "        remove       Remove an entry from the repository\n",
  190.     "        rtag         Add a symbolic tag to a module\n",
  191.     "        status       Display status information on checked out files\n",
  192.     "        tag          Add a symbolic tag to checked out version of files\n",
  193.     "        unedit       Undo an edit command\n",
  194.     "        update       Bring work tree in sync with repository\n",
  195.     "        watch        Set watches\n",
  196.     "        watchers     See who is watching a file\n",
  197.     "(Use the --help-synonyms option for a list of alternate command names)\n",
  198.     NULL,
  199. };
  200.  
  201. static const char * const*
  202. cmd_synonyms ()
  203. {
  204.     char ** synonyms;
  205.     char ** line;
  206.     const struct cmd *c = &cmds[0];
  207.     int numcmds = 2;        /* two more for title and end */
  208.  
  209.     while (c->fullname != NULL)
  210.     {
  211.     numcmds++;
  212.     c++;
  213.     }
  214.     
  215.     synonyms = (char **) xmalloc(numcmds * sizeof(char *));
  216.     line = synonyms;
  217.     *line++ = "CVS command synonyms are:\n";
  218.     for (c = &cmds[0]; c->fullname != NULL; c++)
  219.     {
  220.     if (c->nick1 || c->nick2)
  221.     {
  222.         *line = xmalloc(100); /* wild guess */
  223.         sprintf(*line, "        %-12s %s %s\n", c->fullname,
  224.             c->nick1 ? c->nick1 : "",
  225.             c->nick2 ? c->nick2 : "");
  226.         line++;
  227.     }
  228.     }
  229.     *line = NULL;
  230.     
  231.     return (const char * const*) synonyms; /* will never be freed */
  232. }
  233.  
  234. static RETSIGTYPE
  235. main_cleanup (sig)
  236.     int sig;
  237. {
  238. #ifndef DONT_USE_SIGNALS
  239.     const char *name;
  240.     char temp[10];
  241.  
  242.     switch (sig)
  243.     {
  244. #ifdef SIGHUP
  245.     case SIGHUP:
  246.     name = "hangup";
  247.     break;
  248. #endif
  249. #ifdef SIGINT
  250.     case SIGINT:
  251.     name = "interrupt";
  252.     break;
  253. #endif
  254. #ifdef SIGQUIT
  255.     case SIGQUIT:
  256.     name = "quit";
  257.     break;
  258. #endif
  259. #ifdef SIGPIPE
  260.     case SIGPIPE:
  261.     name = "broken pipe";
  262.     break;
  263. #endif
  264. #ifdef SIGTERM
  265.     case SIGTERM:
  266.     name = "termination";
  267.     break;
  268. #endif
  269.     default:
  270.     /* This case should never be reached, because we list above all
  271.        the signals for which we actually establish a signal handler.  */
  272.     sprintf (temp, "%d", sig);
  273.     name = temp;
  274.     break;
  275.     }
  276.  
  277.     error (1, 0, "received %s signal", name);
  278. #endif /* !DONT_USE_SIGNALS */
  279. }
  280.  
  281. static void
  282. error_cleanup PROTO((void))
  283. {
  284.     Lock_Cleanup();
  285. #ifdef SERVER_SUPPORT
  286.     if (server_active)
  287.     server_cleanup (0);
  288. #endif
  289. }
  290.  
  291. int
  292. main (argc, argv)
  293.     int argc;
  294.     char **argv;
  295. {
  296.     char *CVSroot = CVSROOT_DFLT;
  297.     extern char *version_string;
  298.     extern char *config_string;
  299.     char *cp, *end;
  300.     const struct cmd *cm;
  301.     int c, err = 0;
  302.     int rcsbin_update_env, cvs_update_env;
  303.     int help = 0;        /* Has the user asked for help?  This
  304.                    lets us support the `cvs -H cmd'
  305.                    convention to give help for cmd. */
  306.     static struct option long_options[] =
  307.       {
  308.         {"help", 0, NULL, 'H'},
  309.         {"version", 0, NULL, 'v'},
  310.     {"help-commands", 0, NULL, 1},
  311.     {"help-synonyms", 0, NULL, 2},
  312.         {0, 0, 0, 0}
  313.       };
  314.     /* `getopt_long' stores the option index here, but right now we
  315.         don't use it. */
  316.     int option_index = 0;
  317.     int need_to_create_root = 0;
  318.  
  319.     error_set_cleanup (error_cleanup);
  320.  
  321. #ifdef SYSTEM_INITIALIZE
  322.     /* Hook for OS-specific behavior, for example socket subsystems on
  323.        NT and OS2 or dealing with windows and arguments on Mac.  */
  324.     SYSTEM_INITIALIZE (&argc, &argv);
  325. #endif
  326.  
  327.     /*
  328.      * Just save the last component of the path for error messages
  329.      */
  330.     program_path = xstrdup (argv[0]);
  331.     program_name = last_component (argv[0]);
  332.  
  333.     /*
  334.      * Query the environment variables up-front, so that
  335.      * they can be overridden by command line arguments
  336.      */
  337.     rcsbin_update_env = *Rcsbin;    /* RCSBIN_DFLT must be set */
  338.     cvs_update_env = 0;
  339.     if ((cp = getenv (RCSBIN_ENV)) != NULL)
  340.     {
  341.     Rcsbin = cp;
  342.     rcsbin_update_env = 0;        /* it's already there */
  343.     }
  344.     if ((cp = getenv (EDITOR1_ENV)) != NULL)
  345.      Editor = cp;
  346.     else if ((cp = getenv (EDITOR2_ENV)) != NULL)
  347.     Editor = cp;
  348.     else if ((cp = getenv (EDITOR3_ENV)) != NULL)
  349.     Editor = cp;
  350.     if ((cp = getenv (CVSROOT_ENV)) != NULL)
  351.     {
  352.     CVSroot = cp;
  353.     cvs_update_env = 0;        /* it's already there */
  354.     }
  355.     if (getenv (CVSREAD_ENV) != NULL)
  356.     cvswrite = FALSE;
  357.  
  358.     /* This has the effect of setting getopt's ordering to REQUIRE_ORDER,
  359.        which is what we need to distinguish between global options and
  360.        command options.  FIXME: It would appear to be possible to do this
  361.        much less kludgily by passing "+" as the first character to the
  362.        option string we pass to getopt_long.  */
  363.     optind = 1;
  364.  
  365.  
  366.     /* We have to parse the options twice because else there is no
  367.        chance to avoid reading the global options from ".cvsrc".  Set
  368.        opterr to 0 for avoiding error messages about invalid options.
  369.        */
  370.     opterr = 0;
  371.  
  372.     while ((c = getopt_long
  373.             (argc, argv, "f", NULL, NULL))
  374.            != EOF)
  375.       {
  376.     if (c == 'f')
  377.         use_cvsrc = FALSE;
  378.       }
  379.     
  380.     /*
  381.      * Scan cvsrc file for global options.
  382.      */
  383.     if (use_cvsrc)
  384.     read_cvsrc (&argc, &argv, "cvs");
  385.  
  386.     optind = 1;
  387.     opterr = 1;
  388.  
  389.     while ((c = getopt_long
  390.             (argc, argv, "Qqrwtnlvb:e:d:Hfz:s:x", long_options, &option_index))
  391.            != EOF)
  392.       {
  393.     switch (c)
  394.           {
  395.             case 1:
  396.             /* --help-commands */
  397.                 usage (cmd_usage);
  398.                 break;
  399.             case 2:
  400.             /* --help-synonyms */
  401.                 usage (cmd_synonyms());
  402.                 break;
  403.         case 'Q':
  404.         really_quiet = TRUE;
  405.         /* FALL THROUGH */
  406.         case 'q':
  407.         quiet = TRUE;
  408.         break;
  409.         case 'r':
  410.         cvswrite = FALSE;
  411.         break;
  412.         case 'w':
  413.         cvswrite = TRUE;
  414.         break;
  415.         case 't':
  416.         trace = TRUE;
  417.         break;
  418.         case 'n':
  419.         noexec = TRUE;
  420.         case 'l':            /* Fall through */
  421.         logoff = TRUE;
  422.         break;
  423.         case 'v':
  424.         (void) fputs (version_string, stdout);
  425.         (void) fputs (config_string, stdout);
  426.         (void) fputs ("\n", stdout);
  427.         (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout);
  428.         (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout);
  429.         (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout);
  430.         (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout);
  431.         (void) fputs ("\n", stdout);
  432.         (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
  433.         (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
  434.         exit (0);
  435.         break;
  436.         case 'b':
  437.         Rcsbin = optarg;
  438.         rcsbin_update_env = 1;    /* need to update environment */
  439.         break;
  440.         case 'e':
  441.         Editor = optarg;
  442.         break;
  443.         case 'd':
  444.         CVSroot = optarg;
  445.         cvs_update_env = 1;    /* need to update environment */
  446.         break;
  447.         case 'H':
  448.             help = 1;
  449.         break;
  450.             case 'f':
  451.         use_cvsrc = FALSE; /* unnecessary, since we've done it above */
  452.         break;
  453.         case 'z':
  454. #ifdef CLIENT_SUPPORT
  455.         gzip_level = atoi (optarg);
  456.         if (gzip_level <= 0 || gzip_level > 9)
  457.           error (1, 0,
  458.              "gzip compression level must be between 1 and 9");
  459. #endif
  460.         /* If no CLIENT_SUPPORT, we just silently ignore the gzip
  461.            level, so that users can have it in their .cvsrc and not
  462.            cause any trouble.  */
  463.         break;
  464.         case 's':
  465.         variable_set (optarg);
  466.         break;
  467.         case 'x':
  468. #ifdef CLIENT_SUPPORT
  469.             cvsencrypt = 1;
  470. #endif
  471.         /* If no CLIENT_SUPPORT, ignore -x, so that users can
  472.                    have it in their .cvsrc and not cause any trouble.  */
  473.         break;
  474.         case '?':
  475.         default:
  476.                 usage (usg);
  477.     }
  478.     }
  479.  
  480.     argc -= optind;
  481.     argv += optind;
  482.     if (argc < 1)
  483.     usage (usg);
  484.  
  485.  
  486.     /* Look up the command name. */
  487.  
  488.     command_name = argv[0];
  489.     for (cm = cmds; cm->fullname; cm++)
  490.     {
  491.     if (cm->nick1 && !strcmp (command_name, cm->nick1))
  492.         break;
  493.     if (cm->nick2 && !strcmp (command_name, cm->nick2))
  494.         break;
  495.     if (!strcmp (command_name, cm->fullname))
  496.         break;
  497.     }
  498.  
  499.     if (!cm->fullname)
  500.     usage (cmd_usage);            /* no match */
  501.     else
  502.     command_name = cm->fullname;    /* Global pointer for later use */
  503.  
  504.     if (strcmp (argv[0], "rlog") == 0)
  505.     {
  506.     error (0, 0, "warning: the rlog command is deprecated");
  507.     error (0, 0, "use the synonymous log command instead");
  508.     }
  509.  
  510.     if (help)
  511.     argc = -1;        /* some functions only check for this */
  512.     else
  513.     {
  514.     /* The user didn't ask for help, so go ahead and authenticate,
  515.            set up CVSROOT, and the rest of it. */
  516.  
  517.     /* The UMASK environment variable isn't handled with the
  518.        others above, since we don't want to signal errors if the
  519.        user has asked for help.  This won't work if somebody adds
  520.        a command-line flag to set the umask, since we'll have to
  521.        parse it before we get here. */
  522.  
  523.     if ((cp = getenv (CVSUMASK_ENV)) != NULL)
  524.     {
  525.         /* FIXME: Should be accepting symbolic as well as numeric mask.  */
  526.         cvsumask = strtol (cp, &end, 8) & 0777;
  527.         if (*end != '\0')
  528.         error (1, errno, "invalid umask value in %s (%s)",
  529.                CVSUMASK_ENV, cp);
  530.     }
  531.  
  532. #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
  533.     /* If we are invoked with a single argument "kserver", then we are
  534.        running as Kerberos server as root.  Do the authentication as
  535.        the very first thing, to minimize the amount of time we are
  536.        running as root.  */
  537.     if (strcmp (command_name, "kserver") == 0)
  538.     {
  539.         kserver_authenticate_connection ();
  540.  
  541.         /* Pretend we were invoked as a plain server.  */
  542.         command_name = "server";
  543.     }
  544. #endif /* HAVE_KERBEROS */
  545.  
  546.  
  547. #if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
  548.     if (strcmp (command_name, "pserver") == 0)
  549.     {
  550.         /* Gets username and password from client, authenticates, then
  551.            switches to run as that user and sends an ACK back to the
  552.            client. */
  553.         pserver_authenticate_connection ();
  554.       
  555.         /* Pretend we were invoked as a plain server.  */
  556.         command_name = "server";
  557.     }
  558. #endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */
  559.  
  560.  
  561.     /* Fiddling with CVSROOT doesn't make sense if we're running
  562.            in server mode, since the client will send the repository
  563.            directory after the connection is made. */
  564.  
  565. #ifdef SERVER_SUPPORT
  566.     if (strcmp (command_name, "server") != 0)
  567. #endif
  568.     {
  569.         char *CVSADM_Root;
  570.         
  571.         /* See if we are able to find a 'better' value for CVSroot
  572.            in the CVSADM_ROOT directory. */
  573.  
  574.         CVSADM_Root = NULL;
  575.  
  576.         /* "cvs import" shouldn't check CVS/Root; in general it
  577.            ignores CVS directories and CVS/Root is likely to
  578.            specify a different repository than the one we are
  579.            importing to.  */
  580.         if (strcmp (command_name, "import") != 0)
  581.         CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
  582.  
  583.         if (CVSADM_Root != NULL)
  584.         {
  585.         if (CVSroot == NULL || !cvs_update_env)
  586.         {
  587.             CVSroot = CVSADM_Root;
  588.             cvs_update_env = 1;    /* need to update environment */
  589.         }
  590.         /* Let -d override CVS/Root file.  The user might want
  591.            to change the access method, use a different server
  592.            (if there are two server machines which share the
  593.            repository using a networked file system), etc.  */
  594.         else if (
  595. #ifdef CLIENT_SUPPORT
  596.                  !getenv ("CVS_IGNORE_REMOTE_ROOT") &&
  597. #endif
  598.              strcmp (CVSroot, CVSADM_Root) != 0)
  599.         {
  600.             /* Once we have verified that this root is usable,
  601.                we will want to write it into CVS/Root.  */
  602.             need_to_create_root = 1;
  603.         }
  604.         }
  605.  
  606.         /* Now we've reconciled CVSROOT from the command line, the
  607.                CVS/Root file, and the environment variable.  Do the
  608.                last sanity checks on the variable. */
  609.  
  610.         if (! CVSroot)
  611.         {
  612.         error (0, 0,
  613.                "No CVSROOT specified!  Please use the `-d' option");
  614.         error (1, 0,
  615.                "or set the %s environment variable.", CVSROOT_ENV);
  616.         }
  617.         
  618.         if (! *CVSroot)
  619.         {
  620.         error (0, 0,
  621.                "CVSROOT is set but empty!  Make sure that the");
  622.         error (0, 0,
  623.                "specification of CVSROOT is legal, either via the");
  624.         error (0, 0,
  625.                "`-d' option, the %s environment variable, or the",
  626.                CVSROOT_ENV);
  627.         error (1, 0,
  628.                "CVS/Root file (if any).");
  629.         }
  630.  
  631.         /* Now we're 100% sure that we have a valid CVSROOT
  632.            variable.  Parse it to see if we're supposed to do
  633.            remote accesses or use a special access method. */
  634.  
  635.         if (parse_cvsroot (CVSroot))
  636.         error (1, 0, "Bad CVSROOT.");
  637.  
  638.         /*
  639.          * Check to see if we can write into the history file.  If not,
  640.          * we assume that we can't work in the repository.
  641.          * BUT, only if the history file exists.
  642.          */
  643.  
  644.         if (!client_active)
  645.         {
  646.         char path[PATH_MAX];
  647.         int save_errno;
  648.  
  649.         (void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM);
  650.         if (!isaccessible (path, R_OK | X_OK))
  651.         {
  652.             save_errno = errno;
  653.             /* If this is "cvs init", the root need not exist yet.  */
  654.             if (strcmp (command_name, "init") != 0)
  655.             {
  656.             error (1, save_errno, "%s", path);
  657.             }
  658.         }
  659.         (void) strcat (path, "/");
  660.         (void) strcat (path, CVSROOTADM_HISTORY);
  661.         if (isfile (path) && !isaccessible (path, R_OK | W_OK))
  662.         {
  663.             save_errno = errno;
  664.             error (0, 0, "Sorry, you don't have read/write access to the history file");
  665.             error (1, save_errno, "%s", path);
  666.         }
  667.         }
  668.  
  669. #ifdef HAVE_PUTENV
  670.         /* Update the CVSROOT environment variable if necessary. */
  671.  
  672.         if (cvs_update_env)
  673.         {
  674.         char *env;
  675.         env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot)
  676.                    + 1 + 1);
  677.         (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
  678.         (void) putenv (env);
  679.         /* do not free env, as putenv has control of it */
  680.         }
  681. #endif
  682.     }
  683.     
  684.     /* This is only used for writing into the history file.  For
  685.        remote connections, it might be nice to have hostname
  686.        and/or remote path, on the other hand I'm not sure whether
  687.        it is worth the trouble.  */
  688.  
  689.     CurDir = xmalloc (PATH_MAX);
  690. #ifdef SERVER_SUPPORT
  691.     if (strcmp (command_name, "server") == 0)
  692.         strcpy (CurDir, "<remote>");
  693.     else
  694. #endif
  695.     {
  696.             if (!getwd (CurDir))
  697.         error (1, 0, "cannot get working directory: %s", CurDir);
  698.     }
  699.  
  700. #ifdef HAVE_PUTENV
  701.     /* Now, see if we should update the environment with the
  702.            Rcsbin value */
  703.     if (rcsbin_update_env)
  704.     {
  705.         char *env;
  706.         env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
  707.         (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
  708.         (void) putenv (env);
  709.         /* do not free env, as putenv has control of it */
  710.     }
  711. #endif
  712.  
  713.     /*
  714.      * If Rcsbin is set to something, make sure it is terminated with
  715.      * a slash character.  If not, add one.
  716.      */
  717.     if (*Rcsbin)
  718.     {
  719.         int len = strlen (Rcsbin);
  720.         char *rcsbin;
  721.  
  722.         if (Rcsbin[len - 1] != '/')
  723.         {
  724.         rcsbin = Rcsbin;
  725.         Rcsbin = xmalloc (len + 2);    /* one for '/', one for NULL */
  726.         (void) strcpy (Rcsbin, rcsbin);
  727.         (void) strcat (Rcsbin, "/");
  728.         }
  729.     }
  730.  
  731. #ifndef DONT_USE_SIGNALS
  732.     /* make sure we clean up on error */
  733. #ifdef SIGHUP
  734.     (void) SIG_register (SIGHUP, main_cleanup);
  735.     (void) SIG_register (SIGHUP, Lock_Cleanup);
  736. #endif
  737. #ifdef SIGINT
  738.     (void) SIG_register (SIGINT, main_cleanup);
  739.     (void) SIG_register (SIGINT, Lock_Cleanup);
  740. #endif
  741. #ifdef SIGQUIT
  742.     (void) SIG_register (SIGQUIT, main_cleanup);
  743.     (void) SIG_register (SIGQUIT, Lock_Cleanup);
  744. #endif
  745. #ifdef SIGPIPE
  746.     (void) SIG_register (SIGPIPE, main_cleanup);
  747.     (void) SIG_register (SIGPIPE, Lock_Cleanup);
  748. #endif
  749. #ifdef SIGTERM
  750.     (void) SIG_register (SIGTERM, main_cleanup);
  751.     (void) SIG_register (SIGTERM, Lock_Cleanup);
  752. #endif
  753. #endif /* !DONT_USE_SIGNALS */
  754.  
  755.     gethostname(hostname, sizeof (hostname));
  756.  
  757. #ifdef HAVE_SETVBUF
  758.     /*
  759.      * Make stdout line buffered, so 'tail -f' can monitor progress.
  760.      * Patch creates too much output to monitor and it runs slowly.
  761.      */
  762. #  ifndef KLUDGE_FOR_WNT_TESTSUITE
  763.  
  764.     if (strcmp (cm->fullname, "patch"))
  765. #    ifdef BUFSIZ  /* traditional SysV chokes when size == 0 */
  766.         (void) setvbuf (stdout, (char *) NULL, _IOLBF, BUFSIZ);
  767. #    else
  768.         (void) setvbuf (stdout, (char *) NULL, _IOLBF, 0);
  769. #    endif
  770.  
  771. #  else /* KLUDGE_FOR_WNT_TESTSUITE */
  772.  
  773.         (void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
  774.         (void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
  775.  
  776. #  endif /* KLUDGE_FOR_WNT_TESTSUITE */
  777.  
  778. #endif /* HAVE_SETVBUF */
  779.  
  780.     if (use_cvsrc)
  781.         read_cvsrc (&argc, &argv, command_name);
  782.  
  783.     } /* end of stuff that gets done if the user DOESN'T ask for help */
  784.  
  785.     err = (*(cm->func)) (argc, argv);
  786.  
  787.     if (need_to_create_root)
  788.     {
  789.     /* Update the CVS/Root file.  We might want to do this in
  790.        all directories that we recurse into, but currently we
  791.        don't.  */
  792.     Create_Root (NULL, CVSroot);
  793.     }
  794.  
  795.     Lock_Cleanup ();
  796.  
  797. #ifdef SYSTEM_CLEANUP
  798.     /* Hook for OS-specific behavior, for example socket subsystems on
  799.        NT and OS2 or dealing with windows and arguments on Mac.  */
  800.     SYSTEM_CLEANUP ();
  801. #endif
  802.  
  803.     if (err)
  804.     return (EXIT_FAILURE);
  805.     return 0;
  806. }
  807.  
  808. char *
  809. Make_Date (rawdate)
  810.     char *rawdate;
  811. {
  812.     struct tm *ftm;
  813.     time_t unixtime;
  814.     char date[256];            /* XXX bigger than we'll ever need? */
  815.     char *ret;
  816.  
  817.     unixtime = get_date (rawdate, (struct timeb *) NULL);
  818.     if (unixtime == (time_t) - 1)
  819.     error (1, 0, "Can't parse date/time: %s", rawdate);
  820. #ifdef HAVE_RCS5
  821.     ftm = gmtime (&unixtime);
  822. #else
  823.     ftm = localtime (&unixtime);
  824. #endif
  825.     (void) sprintf (date, DATEFORM,
  826.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  827.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  828.             ftm->tm_min, ftm->tm_sec);
  829.     ret = xstrdup (date);
  830.     return (ret);
  831. }
  832.  
  833. void
  834. usage (cpp)
  835.     register const char *const *cpp;
  836. {
  837.     (void) fprintf (stderr, *cpp++, program_name, command_name);
  838.     for (; *cpp; cpp++)
  839.     (void) fprintf (stderr, *cpp);
  840.     exit (EXIT_FAILURE);
  841. }
  842.