diff options
Diffstat (limited to 'contrib/cvs/src/checkout.c')
-rw-r--r-- | contrib/cvs/src/checkout.c | 540 |
1 files changed, 290 insertions, 250 deletions
diff --git a/contrib/cvs/src/checkout.c b/contrib/cvs/src/checkout.c index e36e729..3c1eef7 100644 --- a/contrib/cvs/src/checkout.c +++ b/contrib/cvs/src/checkout.c @@ -3,7 +3,7 @@ * 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. + * specified in the README file that comes with the CVS source distribution. * * Create Version * @@ -61,6 +61,7 @@ static const char *const checkout_usage[] = "\t-d dir\tCheck out into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", "\t-j rev\tMerge in changes made between current revision and rev.\n", + "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -76,6 +77,7 @@ static const char *const export_usage[] = "\t-D date\tExport revisions as of date.\n", "\t-d dir\tExport into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", + "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -257,7 +259,7 @@ checkout (argc, argv) server send the check-in and update programs for the various modules/dirs requested. If we turn this off and simply request the names of the modules and directories (as - below in !expand_modules), those files (CVS/Checking.prog + below in !expand_modules), those files (CVS/Checkin.prog or CVS/Update.prog) don't get created. Grrr. */ expand_modules = (!cat && !status && !pipeout @@ -332,54 +334,16 @@ checkout (argc, argv) } db = open_module (); - /* - * if we have more than one argument and where was specified, we make the - * where, cd into it, and try to shorten names as much as possible. - * Otherwise, we pass the where as a single argument to do_module. - */ - if (argc > 1 && where != NULL) - { - (void) CVS_MKDIR (where, 0777); - if ( CVS_CHDIR (where) < 0) - error (1, errno, "cannot chdir to %s", where); - preload_update_dir = xstrdup (where); - where = (char *) NULL; - if (!isfile (CVSADM)) - { - char *repository; - repository = xmalloc (strlen (CVSroot_directory) + 80); - (void) sprintf (repository, "%s/%s/%s", CVSroot_directory, - CVSROOTADM, CVSNULLREPOS); - if (!isfile (repository)) - { - mode_t omask; - omask = umask (cvsumask); - (void) CVS_MKDIR (repository, 0777); - (void) umask (omask); - } + /* If we've specified something like "cvs co foo/bar baz/quux" + don't try to shorten names. There are a few cases in which we + could shorten (e.g. "cvs co foo/bar foo/baz"), but we don't + handle those yet. Better to have an extra directory created + than the thing checked out under the wrong directory name. */ - /* I'm not sure whether this check is redundant. */ - if (!isdir (repository)) - error (1, 0, "there is no repository %s", repository); + if (argc > 1) + shorten = 0; - Create_Admin (".", preload_update_dir, repository, - (char *) NULL, (char *) NULL, 0); - if (!noexec) - { - FILE *fp; - - fp = open_file (CVSADM_ENTSTAT, "w+"); - if (fclose(fp) == EOF) - error(1, errno, "cannot close %s", CVSADM_ENTSTAT); -#ifdef SERVER_SUPPORT - if (server_active) - server_set_entstat (preload_update_dir, repository); -#endif - } - free (repository); - } - } /* If we will be calling history_write, work out the name to pass it. */ @@ -396,29 +360,6 @@ checkout (argc, argv) history_name = date; } - /* - * if where was specified (-d) and we have not taken care of it already - * with the multiple arg stuff, and it was not a simple directory name - * but rather a path, we strip off everything but the last component and - * attempt to cd to the indicated place. where then becomes simply the - * last component - */ - if (where != NULL && strchr (where, '/') != NULL) - { - char *slash; - - slash = strrchr (where, '/'); - *slash = '\0'; - - if ( CVS_CHDIR (where) < 0) - error (1, errno, "cannot chdir to %s", where); - - preload_update_dir = xstrdup (where); - - where = slash + 1; - if (*where == '\0') - where = NULL; - } for (i = 0; i < argc; i++) err += do_module (db, argv[i], m_type, "Updating", checkout_proc, @@ -456,6 +397,8 @@ safe_location () hardpath[x] = '\0'; } current = xgetwd (); + if (current == NULL) + error (1, errno, "could not get working directory"); hardpath_len = strlen (hardpath); if (strlen (current) >= hardpath_len && strncmp (current, hardpath, hardpath_len) == 0) @@ -506,17 +449,18 @@ build_one_dir (repository, dirpath, sticky) if (!isdir (repository)) error (1, 0, "there is no repository %s", repository); - Create_Admin (".", dirpath, repository, - sticky ? (char *) NULL : tag, - sticky ? (char *) NULL : date, + if (Create_Admin (".", dirpath, repository, + sticky ? (char *) NULL : tag, + sticky ? (char *) NULL : date, - /* FIXME? This is a guess. If it is important - for nonbranch to be set correctly here I - think we need to write it one way now and - then rewrite it later via WriteTag, once - we've had a chance to call RCS_nodeisbranch - on each file. */ - 0); + /* FIXME? This is a guess. If it is important + for nonbranch to be set correctly here I + think we need to write it one way now and + then rewrite it later via WriteTag, once + we've had a chance to call RCS_nodeisbranch + on each file. */ + 0, 1)) + return; if (!noexec) { @@ -536,11 +480,11 @@ build_one_dir (repository, dirpath, sticky) */ /* ARGSUSED */ static int -checkout_proc (pargc, argv, where, mwhere, mfile, shorten, +checkout_proc (pargc, argv, where_orig, mwhere, mfile, shorten, local_specified, omodule, msg) int *pargc; char **argv; - char *where; + char *where_orig; char *mwhere; char *mfile; int shorten; @@ -551,11 +495,9 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, int err = 0; int which; char *cp; - char *cp2; char *repository; - char *xwhere = NULL; char *oldupdate = NULL; - char *prepath; + char *where; /* * OK, so we're doing the checkout! Our args are as follows: @@ -563,152 +505,172 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, * where contains where to put it (if supplied by checkout) * mwhere contains the module name or -d from module file * mfile says do only that part of the module - * shorten = TRUE says shorten as much as possible + * shorten = 1 says shorten as much as possible * omodule is the original arg to do_module() */ - /* set up the repository (maybe) for the bottom directory */ - repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + 5); + /* Set up the repository (maybe) for the bottom directory. + Allocate more space than we need so we don't need to keep + reallocating this string. */ + repository = xmalloc (strlen (CVSroot_directory) + + strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile)) + + 10); (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); + Sanitize_Repository_Name (repository); + /* save the original value of preload_update_dir */ if (preload_update_dir != NULL) oldupdate = xstrdup (preload_update_dir); - /* fix up argv[] for the case of partial modules */ - if (mfile != NULL) - { - char *file; - /* if mfile is really a path, straighten it out first */ - if ((cp = strrchr (mfile, '/')) != NULL) + /* Allocate space and set up the where variable. We allocate more + space than necessary here so that we don't have to keep + reallocaing it later on. */ + + where = xmalloc (strlen (argv[0]) + + (mfile == NULL ? 0 : strlen (mfile)) + + (mwhere == NULL ? 0 : strlen (mwhere)) + + (where_orig == NULL ? 0 : strlen (where_orig)) + + 10); + + /* Yes, this could be written in a less verbose way, but in this + form it is quite easy to read. + + FIXME? The following code that sets should probably be moved + to do_module in modules.c, since there is similar code in + patch.c and rtag.c. */ + + if (shorten) + { + if (where_orig != NULL) { - *cp = 0; - repository = xrealloc (repository, - strlen (repository) + strlen (mfile) + 10); - (void) strcat (repository, "/"); - (void) strcat (repository, mfile); - - /* - * Now we need to fill in the where correctly. if !shorten, tack - * the rest of the path onto where if where is filled in - * otherwise tack the rest of the path onto mwhere and make that - * the where - * - * If shorten is enabled, we might use mwhere to set where if - * nobody set it yet, so we'll need to setup mwhere as the last - * component of the path we are tacking onto repository - */ - if (!shorten) - { - if (where != NULL) - { - xwhere = xmalloc (strlen (where) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", where, mfile); - } - else - { - xwhere = xmalloc (strlen (mwhere) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", mwhere, mfile); - } - where = xwhere; - } - else - { - char *slash; - - if ((slash = strrchr (mfile, '/')) != NULL) - mwhere = slash + 1; - else - mwhere = mfile; - } - mfile = cp + 1; + /* If the user has specified a directory with `-d' on the + command line, use it preferentially, even over the `-d' + flag in the modules file. */ + + (void) strcpy (where, where_orig); } - - file = xmalloc (strlen (repository) + strlen (mfile) + 5); - (void) sprintf (file, "%s/%s", repository, mfile); - if (isdir (file)) + else if (mwhere != NULL) { + /* Second preference is the value of mwhere, which is from + the `-d' flag in the modules file. */ - /* - * The portion of a module was a directory, so kludge up where to - * be the subdir, and fix up repository - */ - free (repository); - repository = xstrdup (file); - - /* - * At this point, if shorten is not enabled, we make where either - * where with mfile concatenated, or if where hadn't been set we - * set it to mwhere with mfile concatenated. - * - * If shorten is enabled and where hasn't been set yet, then where - * becomes mfile - */ - if (!shorten) - { - if (where != NULL) - { - xwhere = xmalloc (strlen (where) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", where, mfile); - } - else - { - xwhere = xmalloc (strlen (mwhere) + strlen (mfile) + 5); - (void) sprintf (xwhere, "%s/%s", mwhere, mfile); - } - where = xwhere; - } - else if (where == NULL) - where = mfile; + (void) strcpy (where, mwhere); } else { - int i; - - /* - * The portion of a module was a file, so kludge up argv to be - * correct - */ - for (i = 1; i < *pargc; i++)/* free the old ones */ - free (argv[i]); - /* FIXME: Normally one has to realloc argv to make sure - argv[1] exists. But this argv does not points to the - beginning of an allocated block. For now we allocate - at least 4 entries for argv (in line2argv). */ - argv[1] = xstrdup (mfile); /* set up the new one */ - *pargc = 2; - - /* where gets mwhere if where isn't set */ - if (where == NULL) - where = mwhere; + /* Third preference is the directory specified in argv[0] + which is this module'e directory in the repository. */ + + (void) strcpy (where, argv[0]); } - free (file); } - - /* - * if shorten is enabled and where isn't specified yet, we pluck the last - * directory component of argv[0] and make it the where - */ - if (shorten && where == NULL) + else { - if ((cp = strrchr (argv[0], '/')) != NULL) + /* Use the same preferences here, bug don't shorten -- that + is, tack on where_orig if it exists. */ + + *where = '\0'; + + if (where_orig != NULL) { - xwhere = xstrdup (cp + 1); - where = xwhere; + (void) strcat (where, where_orig); + (void) strcat (where, "/"); } + + if (mwhere != NULL) + (void) strcat (where, mwhere); + else + (void) strcat (where, argv[0]); } + strip_trailing_slashes (where); /* necessary? */ + - /* if where is still NULL, use mwhere if set or the argv[0] dir */ - if (where == NULL) + /* At this point, the user may have asked for a single file or + directory from within a module. In that case, we should modify + where, repository, and argv as appropriate. */ + + if (mfile != NULL) { - if (mwhere) - where = mwhere; + /* The mfile variable can have one or more path elements. If + it has multiple elements, we want to tack those onto both + repository and where. The last element may refer to either + a file or directory. Here's what to do: + + it refers to a directory + -> simply tack it on to where and repository + it refers to a file + -> munge argv to contain `basename mfile` */ + + char *cp; + char *path; + + + /* Paranoia check. */ + + if (mfile[strlen (mfile) - 1] == '/') + { + error (0, 0, "checkout_proc: trailing slash on mfile (%s)!", + mfile); + } + + + /* Does mfile have multiple path elements? */ + + cp = strrchr (mfile, '/'); + if (cp != NULL) + { + *cp = '\0'; + (void) strcat (repository, "/"); + (void) strcat (repository, mfile); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + mfile = cp + 1; + } + + + /* Now mfile is a single path element. */ + + path = xmalloc (strlen (repository) + strlen (mfile) + 5); + (void) sprintf (path, "%s/%s", repository, mfile); + if (isdir (path)) + { + /* It's a directory, so tack it on to repository and + where, as we did above. */ + + (void) strcat (repository, "/"); + (void) strcat (repository, mfile); + (void) strcat (where, "/"); + (void) strcat (where, mfile); + } else { - xwhere = xstrdup (argv[0]); - where = xwhere; + /* It's a file, which means we have to screw around with + argv. */ + + int i; + + + /* Paranoia check. */ + + if (*pargc > 1) + { + error (0, 0, "checkout_proc: trashing argv elements!"); + for (i = 1; i < *pargc; i++) + { + error (0, 0, "checkout_proc: argv[%d] `%s'", + i, argv[i]); + } + } + + for (i = 1; i < *pargc; i++) + free (argv[i]); + argv[1] = xstrdup (mfile); + (*pargc) = 2; } + free (path); } if (preload_update_dir != NULL) @@ -725,6 +687,12 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, /* * At this point, where is the directory we want to build, repository is * the repository for the lowest level of the path. + * + * We need to tell build_dirs not only the path we want it to + * build, but also the repositories we want it to populate the + * path with. To accomplish this, we walk the path backwards, one + * pathname component at a time, constucting a linked list of + * struct dir_to_build. */ /* @@ -733,20 +701,11 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ if (!pipeout) { - size_t root_len; struct dir_to_build *head; + char *reposcopy; - /* We need to tell build_dirs not only the path we want it to - build, but also the repositories we want it to populate the - path with. To accomplish this, we walk the path backwards, - one pathname component at a time, constucting a linked - list of struct dir_to_build. */ - prepath = xstrdup (repository); - - /* We don't want to start stripping elements off prepath after we - get to CVSROOT. */ - root_len = strlen (CVSroot_directory); - if (strncmp (repository, CVSroot_directory, root_len) != 0) + if (strncmp (repository, CVSroot_directory, + strlen (CVSroot_directory)) != 0) error (1, 0, "\ internal error: %s doesn't start with %s in checkout_proc", repository, CVSroot_directory); @@ -760,9 +719,14 @@ internal error: %s doesn't start with %s in checkout_proc", head->dirpath = xstrdup (where); head->next = NULL; - cp = strrchr (where, '/'); - cp2 = strrchr (prepath, '/'); + /* Make a copy of the repository name to play with. */ + reposcopy = xstrdup (repository); + + /* FIXME: this should be written in terms of last_component instead + of hardcoding '/'. This presumably affects OS/2, NT, &c, if + the user specifies '\'. Likewise for the call to findslash. */ + cp = strrchr (where, '/'); while (cp != NULL) { struct dir_to_build *new; @@ -771,42 +735,97 @@ internal error: %s doesn't start with %s in checkout_proc", new->dirpath = xmalloc (strlen (where)); strncpy (new->dirpath, where, cp - where); new->dirpath[cp - where] = '\0'; - if (cp2 == NULL || cp2 < prepath + root_len) + + /* Now figure out what repository directory to generate. + The most complete case would be something like this: + + The modules file contains + foo -d bar/baz quux + + The command issued was: + cvs co -d what/ever -N foo + + The results in the CVS/Repository files should be: + . -> . (this is where we executed the cmd) + what -> Emptydir (generated dir -- not in repos) + ever -> . (same as "cd what/ever; cvs co -N foo") + bar -> Emptydir (generated dir -- not in repos) + baz -> quux (finally!) */ + + if (strcmp (reposcopy, CVSroot_directory) == 0) { - /* Don't walk up past CVSROOT; instead put in CVSNULLREPOS. */ - new->repository = - xmalloc (strlen (CVSroot_directory) + 80); - (void) sprintf (new->repository, "%s/%s/%s", - CVSroot_directory, - CVSROOTADM, CVSNULLREPOS); - if (!isfile (new->repository)) - { - mode_t omask; - omask = umask (cvsumask); - if (CVS_MKDIR (new->repository, 0777) < 0) - error (0, errno, "cannot create %s", - new->repository); - (void) umask (omask); - } + /* We can't walk up past CVSROOT. Instead, the + repository should be Emptydir. */ + new->repository = emptydir_name (); } else { - new->repository = xmalloc (strlen (prepath)); - strncpy (new->repository, prepath, cp2 - prepath); - new->repository[cp2 - prepath] = '\0'; + if ((where_orig != NULL) + && (strcmp (new->dirpath, where_orig) == 0)) + { + /* It's the case that the user specified a + * destination directory with the "-d" flag. The + * repository in this directory should be "." + * since the user's command is equivalent to: + * + * cd <dir>; cvs co blah */ + + strcpy (reposcopy, CVSroot_directory); + goto allocate_repos; + } + else if (mwhere != NULL) + { + /* This is a generated directory, so point to + CVSNULLREPOS. */ + + new->repository = emptydir_name (); + } + else + { + /* It's a directory in the repository! */ + + char *rp = strrchr (reposcopy, '/'); + + /* We'll always be below CVSROOT, but check for + paranoia's sake. */ + if (rp == NULL) + error (1, 0, + "internal error: %s doesn't contain a slash", + reposcopy); + + *rp = '\0'; + + allocate_repos: + new->repository = xmalloc (strlen (reposcopy) + 5); + (void) strcpy (new->repository, reposcopy); + + if (strcmp (reposcopy, CVSroot_directory) == 0) + { + /* Special case -- the repository name needs + to be "/path/to/repos/." (the trailing dot + is important). We might be able to get rid + of this after the we check out the other + code that handles repository names. */ + (void) strcat (new->repository, "/."); + } + } } + new->next = head; head = new; cp = findslash (where, cp - 1); - cp2 = findslash (prepath, cp2 - 1); } - /* First build the top-level CVSADM directory. The value we - pass in here for repository is probably wrong; see modules3-7f - in the testsuite. */ - build_one_dir (head->repository != NULL ? head->repository : prepath, - ".", *pargc <= 1); + /* clean up */ + free (reposcopy); + + /* The top-level CVSADM directory should always be + CVSroot_directory. Create it. + + It may be argued that we shouldn't set any sticky bits for + the top-level repository. FIXME? */ + build_one_dir (CVSroot_directory, ".", *pargc <= 1); /* * build dirs on the path if necessary and leave us in the bottom @@ -817,14 +836,10 @@ internal error: %s doesn't start with %s in checkout_proc", if (build_dirs_and_chdir (head, *pargc <= 1) != 0) { error (0, 0, "ignoring module %s", omodule); - free (prepath); err = 1; goto out; } - /* clean up */ - free (prepath); - /* set up the repository (or make sure the old one matches) */ if (!isfile (CVSADM)) { @@ -837,7 +852,7 @@ internal error: %s doesn't start with %s in checkout_proc", error (1, 0, "there is no repository %s", repository); Create_Admin (".", preload_update_dir, repository, - (char *) NULL, (char *) NULL, 0); + (char *) NULL, (char *) NULL, 0, 0); fp = open_file (CVSADM_ENTSTAT, "w+"); if (fclose(fp) == EOF) error(1, errno, "cannot close %s", CVSADM_ENTSTAT); @@ -860,7 +875,7 @@ internal error: %s doesn't start with %s in checkout_proc", then rewrite it later via WriteTag, once we've had a chance to call RCS_nodeisbranch on each file. */ - 0); + 0, 0); } } else @@ -1004,8 +1019,7 @@ internal error: %s doesn't start with %s in checkout_proc", out: free (preload_update_dir); preload_update_dir = oldupdate; - if (xwhere != NULL) - free (xwhere); + free (where); free (repository); return (err); } @@ -1026,6 +1040,32 @@ findslash (start, p) return (p); } +/* Return a newly malloc'd string containing a pathname for CVSNULLREPOS, + and make sure that it exists. If there is an error creating the + directory, give a fatal error. Otherwise, the directory is guaranteed + to exist when we return. */ +char * +emptydir_name () +{ + char *repository; + + repository = xmalloc (strlen (CVSroot_directory) + + sizeof (CVSROOTADM) + + sizeof (CVSNULLREPOS) + + 10); + (void) sprintf (repository, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSNULLREPOS); + if (!isfile (repository)) + { + mode_t omask; + omask = umask (cvsumask); + if (CVS_MKDIR (repository, 0777) < 0) + error (1, errno, "cannot create %s", repository); + (void) umask (omask); + } + return repository; +} + /* Build all the dirs along the path to DIRS with CVS subdirs with appropriate repositories. If ->repository is NULL, do not create a CVSADM directory for that subdirectory; just CVS_CHDIR into it. */ |