This is main.c in view mode; [Download] [Up]
/* * Copyright (c) 1992, Brian Berliner and Jeff Polk * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License * as specified in the README file that comes with the CVS 1.4 kit. * * This is the main C driver for the CVS system. * * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing * the shell-script CVS system that this is based on. * * Usage: * cvs [options] command [options] [files/modules...] * * Where "command" is composed of: * admin RCS command * checkout Check out a module/dir/file * export Like checkout, but used for exporting sources * update Brings work tree in sync with repository * commit Checks files into the repository * diff Runs diffs between revisions * log Prints "rlog" information for files * add Adds an entry to the repository * remove Removes an entry from the repository * status Status info on the revisions * rdiff "patch" format diff listing between releases * tag Add/delete a symbolic tag to the RCS file * rtag Add/delete a symbolic tag to the RCS file * import Import sources into CVS, using vendor branches * release Indicate that Module is no longer in use. * history Display history of Users and Modules. */ #include "cvs.h" #include "patchlevel.h" #if HAVE_KERBEROS #include <sys/socket.h> #include <netinet/in.h> #include <krb.h> #include <pwd.h> #ifndef HAVE_KRB_GET_ERR_TEXT #define krb_get_err_text(status) krb_err_txt[status] #endif #endif #ifndef lint static const char rcsid[] = "$CVSid: @(#)main.c 1.78 94/10/07 $\n"; USE(rcsid) #endif char *program_name; char *command_name = ""; /* * Since some systems don't define this... */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif char hostname[MAXHOSTNAMELEN]; int use_editor = TRUE; int use_cvsrc = TRUE; int cvswrite = !CVSREAD_DFLT; int really_quiet = FALSE; int quiet = FALSE; int trace = FALSE; int noexec = FALSE; int logoff = FALSE; char *CurDir; /* * Defaults, for the environment variables that are not set */ char *Rcsbin = RCSBIN_DFLT; char *Editor = EDITOR_DFLT; char *CVSroot = CVSROOT_DFLT; #ifdef CVSADM_ROOT /* * The path found in CVS/Root must match $CVSROOT and/or 'cvs -d root' */ char *CVSADM_Root = CVSROOT_DFLT; #endif /* CVSADM_ROOT */ int add PROTO((int argc, char **argv)); int admin PROTO((int argc, char **argv)); int checkout PROTO((int argc, char **argv)); int commit PROTO((int argc, char **argv)); int diff PROTO((int argc, char **argv)); int history PROTO((int argc, char **argv)); int import PROTO((int argc, char **argv)); int cvslog PROTO((int argc, char **argv)); int patch PROTO((int argc, char **argv)); int release PROTO((int argc, char **argv)); int cvsremove PROTO((int argc, char **argv)); int rtag PROTO((int argc, char **argv)); int status PROTO((int argc, char **argv)); int tag PROTO((int argc, char **argv)); int update PROTO((int argc, char **argv)); const struct cmd { char *fullname; /* Full name of the function (e.g. "commit") */ char *nick1; /* alternate name (e.g. "ci") */ char *nick2; /* another alternate names (e.g. "ci") */ int (*func) (); /* Function takes (argc, argv) arguments. */ #ifdef CLIENT_SUPPORT int (*client_func) (); /* Function to do it via the protocol. */ #endif } cmds[] = { #ifdef CLIENT_SUPPORT #define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1, f2 } #else #define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1 } #endif CMD_ENTRY("add", "ad", "new", add, client_add), #ifndef CVS_NOADMIN CMD_ENTRY("admin", "adm", "rcs", admin, client_admin), #endif CMD_ENTRY("checkout", "co", "get", checkout, client_checkout), CMD_ENTRY("commit", "ci", "com", commit, client_commit), CMD_ENTRY("diff", "di", "dif", diff, client_diff), CMD_ENTRY("export", "exp", "ex", checkout, client_export), CMD_ENTRY("history", "hi", "his", history, client_history), CMD_ENTRY("import", "im", "imp", import, client_import), CMD_ENTRY("log", "lo", "rlog", cvslog, client_log), CMD_ENTRY("rdiff", "patch", "pa", patch, client_rdiff), CMD_ENTRY("release", "re", "rel", release, client_release), CMD_ENTRY("remove", "rm", "delete", cvsremove, client_remove), CMD_ENTRY("status", "st", "stat", status, client_status), CMD_ENTRY("rtag", "rt", "rfreeze", rtag, client_rtag), CMD_ENTRY("tag", "ta", "freeze", tag, client_tag), CMD_ENTRY("update", "up", "upd", update, client_update), #ifdef SERVER_SUPPORT /* * The client_func is also server because we might have picked up a * CVSROOT environment variable containing a colon. The client will send * the real root later. */ CMD_ENTRY("server", "server", "server", server, server), #endif CMD_ENTRY(NULL, NULL, NULL, NULL, NULL), #undef CMD_ENTRY }; static const char *const usg[] = { "Usage: %s [cvs-options] command [command-options] [files...]\n", " Where 'cvs-options' are:\n", " -H Displays Usage information for command\n", " -Q Cause CVS to be really quiet.\n", " -q Cause CVS to be somewhat quiet.\n", " -r Make checked-out files read-only\n", " -w Make checked-out files read-write (default)\n", " -l Turn History logging off\n", " -n Do not execute anything that will change the disk\n", " -t Show trace of program execution -- Try with -n\n", " -v CVS version and copyright\n", " -b bindir Find RCS programs in 'bindir'\n", " -e editor Use 'editor' for editing log information\n", " -d CVS_root Overrides $CVSROOT as the root of the CVS tree\n", " -f Do not use the ~/.cvsrc file\n", #ifdef CLIENT_SUPPORT " -z # Use 'gzip -#' for net traffic if possible.\n", #endif "\n", " and where 'command' is:\n", " add Adds a new file/directory to the repository\n", " admin Administration front end for rcs\n", " checkout Checkout sources for editing\n", " commit Checks files into the repository\n", " diff Runs diffs between revisions\n", " history Shows status of files and users\n", " import Import sources into CVS, using vendor branches\n", " export Export sources from CVS, similar to checkout\n", " log Prints out 'rlog' information for files\n", " rdiff 'patch' format diffs between releases\n", " release Indicate that a Module is no longer in use\n", " remove Removes an entry from the repository\n", " status Status info on the revisions\n", " tag Add a symbolic tag to checked out version of RCS file\n", " rtag Add a symbolic tag to the RCS file\n", " update Brings work tree in sync with repository\n", NULL, }; static RETSIGTYPE main_cleanup () { exit (1); } static void error_cleanup () { Lock_Cleanup(); #ifdef SERVER_SUPPORT if (server_active) server_cleanup (0); #endif } int main (argc, argv) int argc; char **argv; { extern char *version_string; extern char *config_string; char *cp; const struct cmd *cm; int c, help = FALSE, err = 0; int rcsbin_update_env, cvs_update_env = 0; char tmp[PATH_MAX]; error_set_cleanup (error_cleanup); /* * Just save the last component of the path for error messages */ program_name = last_component (argv[0]); CurDir = xmalloc (PATH_MAX); #ifndef SERVER_SUPPORT if (!getwd (CurDir)) error (1, 0, "cannot get working directory: %s", CurDir); #endif /* * Query the environment variables up-front, so that * they can be overridden by command line arguments */ rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */ cvs_update_env = 0; if ((cp = getenv (RCSBIN_ENV)) != NULL) { Rcsbin = cp; rcsbin_update_env = 0; /* it's already there */ } if ((cp = getenv (EDITOR1_ENV)) != NULL) Editor = cp; else if ((cp = getenv (EDITOR2_ENV)) != NULL) Editor = cp; else if ((cp = getenv (EDITOR3_ENV)) != NULL) Editor = cp; if ((cp = getenv (CVSROOT_ENV)) != NULL) { CVSroot = cp; cvs_update_env = 0; /* it's already there */ } if (getenv (CVSREAD_ENV) != NULL) cvswrite = FALSE; optind = 1; while ((c = getopt (argc, argv, "Qqrwtnlvb:e:d:Hfz:")) != -1) { switch (c) { case 'Q': really_quiet = TRUE; /* FALL THROUGH */ case 'q': quiet = TRUE; break; case 'r': cvswrite = FALSE; break; case 'w': cvswrite = TRUE; break; case 't': trace = TRUE; break; case 'n': noexec = TRUE; case 'l': /* Fall through */ logoff = TRUE; break; case 'v': (void) fputs (version_string, stdout); (void) fputs (config_string, stdout); (void) sprintf (tmp, "Patch Level: %d\n", PATCHLEVEL); (void) fputs (tmp, stdout); (void) fputs ("\n", stdout); (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout); (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout); (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout); (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout); (void) fputs ("\n", stdout); (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout); (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout); exit (0); break; case 'b': Rcsbin = optarg; rcsbin_update_env = 1; /* need to update environment */ break; case 'e': Editor = optarg; break; case 'd': CVSroot = optarg; cvs_update_env = 1; /* need to update environment */ break; case 'H': use_cvsrc = FALSE; /* this ensure that cvs -H works */ help = TRUE; break; case 'f': use_cvsrc = FALSE; break; #ifdef CLIENT_SUPPORT case 'z': gzip_level = atoi (optarg); if (gzip_level <= 0 || gzip_level > 9) error (1, 0, "gzip compression level must be between 1 and 9"); break; #endif case '?': default: usage (usg); } } argc -= optind; argv += optind; if (argc < 1) usage (usg); #ifdef HAVE_KERBEROS /* If we are invoked with a single argument "kserver", then we are running as Kerberos server as root. Do the authentication as the very first thing, to minimize the amount of time we are running as root. */ if (strcmp (argv[0], "kserver") == 0) { int status; char instance[INST_SZ]; struct sockaddr_in peer; struct sockaddr_in laddr; int len; KTEXT_ST ticket; AUTH_DAT auth; char version[KRB_SENDAUTH_VLEN]; Key_schedule sched; char user[ANAME_SZ]; struct passwd *pw; strcpy (instance, "*"); len = sizeof peer; if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0 || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr, &len) < 0) { printf ("E Fatal error, aborting.\n\ error %s getpeername or getsockname failed\n", strerror (errno)); exit (1); } status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd", instance, &peer, &laddr, &auth, "", sched, version); if (status != KSUCCESS) { printf ("E Fatal error, aborting.\n\ error 0 kerberos: %s\n", krb_get_err_text(status)); exit (1); } /* Get the local name. */ status = krb_kntoln (&auth, user); if (status != KSUCCESS) { printf ("E Fatal error, aborting.\n\ error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status)); exit (1); } pw = getpwnam (user); if (pw == NULL) { printf ("E Fatal error, aborting.\n\ error 0 %s: no such user\n", user); exit (1); } initgroups (pw->pw_name, pw->pw_gid); setgid (pw->pw_gid); setuid (pw->pw_uid); /* Inhibit access by randoms. Don't want people randomly changing our temporary tree before we check things in. */ umask (077); #if HAVE_PUTENV /* Set LOGNAME and USER in the environment, in case they are already set to something else. */ { char *env; env = xmalloc (sizeof "LOGNAME=" + strlen (user)); (void) sprintf (env, "LOGNAME=%s", user); (void) putenv (env); env = xmalloc (sizeof "USER=" + strlen (user)); (void) sprintf (env, "USER=%s", user); (void) putenv (env); } #endif /* Pretend we were invoked as a plain server. */ argv[0] = "server"; } #endif /* HAVE_KERBEROS */ #ifdef CVSADM_ROOT /* * See if we are able to find a 'better' value for CVSroot in the * CVSADM_ROOT directory. */ #ifdef SERVER_SUPPORT if (strcmp (argv[0], "server") == 0 && CVSroot == NULL) CVSADM_Root = NULL; else CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); #else /* No SERVER_SUPPORT */ CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); #endif /* No SERVER_SUPPORT */ if (CVSADM_Root != NULL) { if (CVSroot == NULL || !cvs_update_env) { CVSroot = CVSADM_Root; cvs_update_env = 1; /* need to update environment */ } #ifdef CLIENT_SUPPORT else if (!getenv ("CVS_IGNORE_REMOTE_ROOT")) #else else #endif { /* * Now for the hard part, compare the two directories. If they * are not identical, then abort this command. */ if ((fncmp (CVSroot, CVSADM_Root) != 0) && !same_directories(CVSroot, CVSADM_Root)) { error (0, 0, "%s value for CVS Root found in %s", CVSADM_Root, CVSADM_ROOT); error (0, 0, "does not match command line -d %s setting", CVSroot); error (1, 0, "you may wish to try the cvs command again without the -d option "); } } } #endif /* CVSADM_ROOT */ /* * Specifying just the '-H' flag to the sub-command causes a Usage * message to be displayed. */ command_name = cp = argv[0]; if (help == TRUE || (argc > 1 && strcmp (argv[1], "-H") == 0)) argc = -1; else { /* * Check to see if we can write into the history file. If not, * we assume that we can't work in the repository. * BUT, only if the history file exists. */ #ifdef SERVER_SUPPORT if (strcmp (command_name, "server") != 0 || CVSroot != NULL) #endif { char path[PATH_MAX]; int save_errno; if (!CVSroot || !*CVSroot) error (1, 0, "You don't have a %s environment variable", CVSROOT_ENV); (void) sprintf (path, "%s/%s", CVSroot, CVSROOTADM); if (access (path, R_OK | X_OK)) { save_errno = errno; #ifdef CLIENT_SUPPORT if (strchr (CVSroot, ':') == NULL) { #endif error (0, 0, "Sorry, you don't have sufficient access to %s", CVSroot); error (1, save_errno, "%s", path); #ifdef CLIENT_SUPPORT } #endif } (void) strcat (path, "/"); (void) strcat (path, CVSROOTADM_HISTORY); if (isfile (path) && access (path, R_OK | W_OK)) { save_errno = errno; error (0, 0, "Sorry, you don't have read/write access to the history file"); error (1, save_errno, "%s", path); } } } #ifdef SERVER_SUPPORT if (strcmp (command_name, "server") == 0) /* This is only used for writing into the history file. Might be nice to have hostname and/or remote path, on the other hand I'm not sure whether it is worth the trouble. */ strcpy (CurDir, "<remote>"); else if (!getwd (CurDir)) error (1, 0, "cannot get working directory: %s", CurDir); #endif #ifdef HAVE_PUTENV /* Now, see if we should update the environment with the Rcsbin value */ if (cvs_update_env) { char *env; env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot) + 1 + 1); (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot); (void) putenv (env); /* do not free env, as putenv has control of it */ } if (rcsbin_update_env) { char *env; env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1); (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin); (void) putenv (env); /* do not free env, as putenv has control of it */ } #endif /* * If Rcsbin is set to something, make sure it is terminated with * a slash character. If not, add one. */ if (*Rcsbin) { int len = strlen (Rcsbin); char *rcsbin; if (Rcsbin[len - 1] != '/') { rcsbin = Rcsbin; Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */ (void) strcpy (Rcsbin, rcsbin); (void) strcat (Rcsbin, "/"); } } for (cm = cmds; cm->fullname; cm++) { if (cm->nick1 && !strcmp (cp, cm->nick1)) break; if (cm->nick2 && !strcmp (cp, cm->nick2)) break; if (!strcmp (cp, cm->fullname)) break; } if (!cm->fullname) usage (usg); /* no match */ else { command_name = cm->fullname; /* Global pointer for later use */ /* make sure we clean up on error */ #ifdef SIGHUP (void) SIG_register (SIGHUP, main_cleanup); (void) SIG_register (SIGHUP, Lock_Cleanup); #endif #ifdef SIGINT (void) SIG_register (SIGINT, main_cleanup); (void) SIG_register (SIGINT, Lock_Cleanup); #endif #ifdef SIGQUIT (void) SIG_register (SIGQUIT, main_cleanup); (void) SIG_register (SIGQUIT, Lock_Cleanup); #endif #ifdef SIGPIPE (void) SIG_register (SIGPIPE, main_cleanup); (void) SIG_register (SIGPIPE, Lock_Cleanup); #endif #ifdef SIGTERM (void) SIG_register (SIGTERM, main_cleanup); (void) SIG_register (SIGTERM, Lock_Cleanup); #endif gethostname(hostname, sizeof (hostname)); #ifdef HAVE_SETVBUF /* * Make stdout line buffered, so 'tail -f' can monitor progress. * Patch creates too much output to monitor and it runs slowly. */ if (strcmp (cm->fullname, "patch")) (void) setvbuf (stdout, (char *) NULL, _IOLBF, 0); #endif if (use_cvsrc) read_cvsrc(&argc, &argv); #ifdef CLIENT_SUPPORT /* If cvsroot contains a colon, try to do it via the protocol. */ { char *p = CVSroot == NULL ? NULL : strchr (CVSroot, ':'); if (p) err = (*(cm->client_func)) (argc, argv); else err = (*(cm->func)) (argc, argv); } #else /* No CLIENT_SUPPORT */ err = (*(cm->func)) (argc, argv); #endif /* No CLIENT_SUPPORT */ } /* * If the command's error count is modulo 256, we need to change it * so that we don't overflow the 8-bits we get to report exit status */ if (err && (err % 256) == 0) err = 1; Lock_Cleanup (); return (err); } char * Make_Date (rawdate) char *rawdate; { struct tm *ftm; time_t unixtime; char date[256]; /* XXX bigger than we'll ever need? */ char *ret; unixtime = get_date (rawdate, (struct timeb *) NULL); if (unixtime == (time_t) - 1) error (1, 0, "Can't parse date/time: %s", rawdate); #ifdef HAVE_RCS5 ftm = gmtime (&unixtime); #else ftm = localtime (&unixtime); #endif (void) sprintf (date, DATEFORM, ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900), ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour, ftm->tm_min, ftm->tm_sec); ret = xstrdup (date); return (ret); } void usage (cpp) register const char *const *cpp; { (void) fprintf (stderr, *cpp++, program_name, command_name); for (; *cpp; cpp++) (void) fprintf (stderr, *cpp); exit (1); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.