diff options
author | 1997-02-21 06:54:35 +0000 | |
---|---|---|
committer | 1997-02-21 06:54:35 +0000 | |
commit | ae5e8a9bc5efbe41ac6d96a58870257739c0284d (patch) | |
tree | d302b99e913addb809123539374b2a8da76eb01c /gnu/usr.bin/cvs/src | |
parent | Missed files from new release from Cyclic Software (diff) | |
download | wireguard-openbsd-ae5e8a9bc5efbe41ac6d96a58870257739c0284d.tar.xz wireguard-openbsd-ae5e8a9bc5efbe41ac6d96a58870257739c0284d.zip |
Integrate local changes
Diffstat (limited to 'gnu/usr.bin/cvs/src')
-rw-r--r-- | gnu/usr.bin/cvs/src/checkout.c | 171 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/commit.c | 332 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/cvs.h | 66 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/ignore.c | 14 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/lock.c | 273 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/main.c | 144 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/patch.c | 14 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcscmds.c | 10 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/server.c | 714 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/update.c | 135 |
10 files changed, 1280 insertions, 593 deletions
diff --git a/gnu/usr.bin/cvs/src/checkout.c b/gnu/usr.bin/cvs/src/checkout.c index e876cd22bd2..c2b18c53df6 100644 --- a/gnu/usr.bin/cvs/src/checkout.c +++ b/gnu/usr.bin/cvs/src/checkout.c @@ -311,7 +311,7 @@ checkout (argc, argv) if (expand_modules) { - client_send_expansions (local, where); + client_send_expansions (local, where, 1); } else { @@ -343,7 +343,7 @@ checkout (argc, argv) */ if (argc > 1 && where != NULL) { - char repository[PATH_MAX]; + char *repository; (void) CVS_MKDIR (where, 0777); if ( CVS_CHDIR (where) < 0) @@ -352,6 +352,7 @@ checkout (argc, argv) where = (char *) NULL; if (!isfile (CVSADM)) { + repository = xmalloc (strlen (CVSroot_directory) + 80); (void) sprintf (repository, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSNULLREPOS); if (!isfile (repository)) @@ -381,6 +382,7 @@ checkout (argc, argv) #endif } } + free (repository); } /* If we will be calling history_write, work out the name to pass @@ -433,12 +435,20 @@ checkout (argc, argv) static int safe_location () { - char current[PATH_MAX]; + char *current; char hardpath[PATH_MAX+5]; size_t hardpath_len; int x; + int retval; +#ifdef HAVE_READLINK + /* FIXME-arbitrary limit: should be retrying this like xgetwd. + But how does readlink let us know that the buffer was too small? + (by returning sizeof hardpath - 1?). */ x = readlink(CVSroot_directory, hardpath, sizeof hardpath - 1); +#else + x = -1; +#endif if (x == -1) { strcpy(hardpath, CVSroot_directory); @@ -447,22 +457,26 @@ safe_location () { hardpath[x] = '\0'; } - getwd (current); + current = xgetwd (); hardpath_len = strlen (hardpath); - if (strncmp (current, hardpath, hardpath_len) == 0) + if (strlen (current) >= hardpath_len + && strncmp (current, hardpath, hardpath_len) == 0) { if (/* Current is a subdirectory of hardpath. */ current[hardpath_len] == '/' /* Current is hardpath itself. */ || current[hardpath_len] == '\0') - return 0; + retval = 0; else /* It isn't a problem. For example, current is "/foo/cvsroot-bar" and hardpath is "/foo/cvsroot". */ - return 1; + retval = 1; } - return (1); + else + retval = 1; + free (current); + return retval; } /* @@ -486,8 +500,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, int which; char *cp; char *cp2; - char repository[PATH_MAX]; - char xwhere[PATH_MAX]; + char *repository; + char *xwhere = NULL; char *oldupdate = NULL; char *prepath; char *realdirs; @@ -503,6 +517,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ /* set up the repository (maybe) for the bottom directory */ + repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) + 5); (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); /* save the original value of preload_update_dir */ @@ -512,12 +527,14 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, /* fix up argv[] for the case of partial modules */ if (mfile != NULL) { - char file[PATH_MAX]; + char *file; /* if mfile is really a path, straighten it out first */ if ((cp = strrchr (mfile, '/')) != NULL) { *cp = 0; + repository = xrealloc (repository, + strlen (repository) + strlen (mfile) + 10); (void) strcat (repository, "/"); (void) strcat (repository, mfile); @@ -534,9 +551,15 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, 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 @@ -551,6 +574,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, mfile = cp + 1; } + file = xmalloc (strlen (repository) + strlen (mfile) + 5); (void) sprintf (file, "%s/%s", repository, mfile); if (isdir (file)) { @@ -559,7 +583,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, * The portion of a module was a directory, so kludge up where to * be the subdir, and fix up repository */ - (void) strcpy (repository, file); + free (repository); + repository = xstrdup (file); /* * At this point, if shorten is not enabled, we make where either @@ -572,9 +597,15 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, 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) @@ -597,6 +628,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (where == NULL) where = mwhere; } + free (file); } /* @@ -607,7 +639,7 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, { if ((cp = strrchr (argv[0], '/')) != NULL) { - (void) strcpy (xwhere, cp + 1); + xwhere = xstrdup (cp + 1); where = xwhere; } } @@ -619,18 +651,18 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, where = mwhere; else { - (void) strcpy (xwhere, argv[0]); + xwhere = xstrdup (argv[0]); where = xwhere; } } if (preload_update_dir != NULL) { - char tmp[PATH_MAX]; - - (void) sprintf (tmp, "%s/%s", preload_update_dir, where); - free (preload_update_dir); - preload_update_dir = xstrdup (tmp); + preload_update_dir = + xrealloc (preload_update_dir, + strlen (preload_update_dir) + strlen (where) + 5); + strcat (preload_update_dir, "/"); + strcat (preload_update_dir, where); } else preload_update_dir = xstrdup (where); @@ -675,9 +707,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, { error (0, 0, "ignoring module %s", omodule); free (prepath); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (1); + err = 1; + goto out; } /* clean up */ @@ -725,9 +756,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, repos, repository); error (0, 0, "ignoring module %s", omodule); free (repos); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (1); + err = 1; + goto out; } free (repos); } @@ -743,9 +773,8 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if ( CVS_CHDIR (repository) < 0) { error (0, errno, "cannot chdir to %s", repository); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (1); + err = 1; + goto out; } which = W_REPOS; if (tag != NULL && !tag_validated) @@ -789,14 +818,15 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, if (strcmp (command_name, "export") != 0 && !pipeout) history_write ('O', preload_update_dir, history_name, where, repository); + else if (strcmp (command_name, "export") == 0 && !pipeout) + history_write ('E', preload_update_dir, tag ? tag : date, where, + repository); err += do_update (0, (char **) NULL, options, tag, date, force_tag_match, 0 /* !local */ , 1 /* update -d */ , aflag, checkout_prune_dirs, pipeout, which, join_rev1, join_rev2, preload_update_dir); - free (preload_update_dir); - preload_update_dir = oldupdate; - return (err); + goto out; } if (!pipeout) @@ -851,8 +881,12 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, force_tag_match, local_specified, 1 /* update -d */, aflag, checkout_prune_dirs, pipeout, which, join_rev1, join_rev2, preload_update_dir); +out: free (preload_update_dir); preload_update_dir = oldupdate; + if (xwhere != NULL) + free (xwhere); + free (repository); return (err); } @@ -881,31 +915,58 @@ build_dirs_and_chdir (dir, prepath, realdir, sticky) int sticky; { FILE *fp; - char repository[PATH_MAX]; - char path[PATH_MAX]; - char path2[PATH_MAX]; + char *path; + char *path2; char *slash; char *slash2; char *cp; char *cp2; + int retval = 0; - (void) strcpy (path, dir); - (void) strcpy (path2, realdir); + path = xstrdup (dir); + path2 = xstrdup (realdir); for (cp = path, cp2 = path2; (slash = strchr (cp, '/')) != NULL && (slash2 = strchr (cp2, '/')) != NULL; cp = slash + 1, cp2 = slash2 + 1) { *slash = '\0'; *slash2 = '\0'; + if (!isfile (CVSADM) && strcmp (command_name, "export") != 0) + { + char *repository; + + repository = xmalloc (strlen (prepath) + strlen (path2) + 5); + (void) sprintf (repository, "%s/%s", prepath, path2); + /* I'm not sure whether this check is redundant. */ + if (!isdir (repository)) + error (1, 0, "there is no repository %s", repository); + Create_Admin (".", path, repository, sticky ? (char *) NULL : tag, + sticky ? (char *) NULL : date); + if (!noexec) + { + 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 (path, repository); +#endif + } + free (repository); + } + mkdir_if_needed (cp); Subdir_Register ((List *) NULL, (char *) NULL, cp); - (void) CVS_MKDIR (cp, 0777); if ( CVS_CHDIR (cp) < 0) { error (0, errno, "cannot chdir to %s", cp); - return (1); + retval = 1; + goto out; } if (!isfile (CVSADM) && strcmp (command_name, "export") != 0) { + char *repository; + + repository = xmalloc (strlen (prepath) + strlen (path2) + 5); (void) sprintf (repository, "%s/%s", prepath, path2); /* I'm not sure whether this check is redundant. */ if (!isdir (repository)) @@ -922,16 +983,44 @@ build_dirs_and_chdir (dir, prepath, realdir, sticky) server_set_entstat (path, repository); #endif } + free (repository); } *slash = '/'; *slash2 = '/'; } + if (!isfile (CVSADM) && strcmp (command_name, "export") != 0) + { + char *repository; + + repository = xmalloc (strlen (prepath) + strlen (path2) + 5); + (void) sprintf (repository, "%s/%s", prepath, path2); + /* I'm not sure whether this check is redundant. */ + if (!isdir (repository)) + error (1, 0, "there is no repository %s", repository); + Create_Admin (".", path, repository, sticky ? (char *) NULL : tag, + sticky ? (char *) NULL : date); + if (!noexec) + { + 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 (path, repository); +#endif + } + free (repository); + } + mkdir_if_needed (cp); Subdir_Register ((List *) NULL, (char *) NULL, cp); - (void) CVS_MKDIR (cp, 0777); if ( CVS_CHDIR (cp) < 0) { error (0, errno, "cannot chdir to %s", cp); - return (1); + retval = 1; + goto out; } - return (0); +out: + free (path); + free (path2); + return retval; } diff --git a/gnu/usr.bin/cvs/src/commit.c b/gnu/usr.bin/cvs/src/commit.c index 5d9c804e9b3..308c726684c 100644 --- a/gnu/usr.bin/cvs/src/commit.c +++ b/gnu/usr.bin/cvs/src/commit.c @@ -54,7 +54,7 @@ static void fixbranch PROTO((RCSNode *, char *branch)); static void unlockrcs PROTO((RCSNode *rcs)); static void ci_delproc PROTO((Node *p)); static void masterlist_delproc PROTO((Node *p)); -static void locate_rcs PROTO((char *file, char *repository, char *rcs)); +static char *locate_rcs PROTO((char *file, char *repository)); struct commit_info { @@ -271,6 +271,7 @@ find_fileproc (callerdat, finfo) data = (struct logfile_info *) xmalloc (sizeof (struct logfile_info)); data->type = status; data->tag = xstrdup (vers->tag); + data->rev_old = data->rev_new = NULL; node->type = UPDATE; node->delproc = update_delproc; @@ -469,6 +470,11 @@ commit (argc, argv) if (use_editor) do_editor (".", &message, (char *)NULL, find_args.ulist); + /* Run the user-defined script to verify/check information in + *the log message + */ + do_verify (message, (char *)NULL); + /* We always send some sort of message, even if empty. */ option_with_arg ("-m", message); @@ -527,7 +533,7 @@ commit (argc, argv) previous versions of client/server CVS, but it probably is a Good Thing, or at least Not Such A Bad Thing. */ send_file_names (find_args.argc, find_args.argv, 0); - send_files (find_args.argc, find_args.argv, local, 0); + send_files (find_args.argc, find_args.argv, local, 0, 0); send_to_server ("ci\012", 0); return get_responses_and_close (); @@ -558,7 +564,7 @@ commit (argc, argv) argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1); if (err) { - lock_tree_cleanup (); + Lock_Cleanup (); error (1, 0, "correct above errors first!"); } @@ -574,7 +580,7 @@ commit (argc, argv) /* * Unlock all the dirs and clean up */ - lock_tree_cleanup (); + Lock_Cleanup (); dellist (&mulist); if (last_register_time) @@ -591,25 +597,18 @@ commit (argc, argv) return (err); } -/* - * Check to see if a file is ok to commit and make sure all files are - * up-to-date - */ -/* ARGSUSED */ -static int -check_fileproc (callerdat, finfo) - void *callerdat; +/* This routine determines the status of a given file and retrieves + the version information that is associated with that file. */ + +static +Ctype +classify_file_internal (finfo, vers) struct file_info *finfo; + Vers_TS **vers; { - Ctype status; - char *xdir; - Node *p; - List *ulist, *cilist; - Vers_TS *vers; - struct commit_info *ci; - struct logfile_info *li; int save_noexec, save_quiet, save_really_quiet; - + Ctype status; + save_noexec = noexec; save_quiet = quiet; save_really_quiet = really_quiet; @@ -622,15 +621,15 @@ check_fileproc (callerdat, finfo) if (numdots (tag) < 2) { status = Classify_File (finfo, (char *) NULL, (char *) NULL, - (char *) NULL, 1, aflag, &vers, 0); + (char *) NULL, 1, aflag, vers, 0); if (status == T_UPTODATE || status == T_MODIFIED || status == T_ADDED) { Ctype xstatus; - freevers_ts (&vers); + freevers_ts (vers); xstatus = Classify_File (finfo, tag, (char *) NULL, - (char *) NULL, 1, aflag, &vers, 0); + (char *) NULL, 1, aflag, vers, 0); if (xstatus == T_REMOVE_ENTRY) status = T_MODIFIED; else if (status == T_MODIFIED && xstatus == T_CONFLICT) @@ -654,31 +653,54 @@ check_fileproc (callerdat, finfo) *cp = '\0'; } status = Classify_File (finfo, xtag, (char *) NULL, - (char *) NULL, 1, aflag, &vers, 0); + (char *) NULL, 1, aflag, vers, 0); if ((status == T_REMOVE_ENTRY || status == T_CONFLICT) && (cp = strrchr (xtag, '.')) != NULL) { /* pluck one more dot off the revision */ *cp = '\0'; - freevers_ts (&vers); + freevers_ts (vers); status = Classify_File (finfo, xtag, (char *) NULL, - (char *) NULL, 1, aflag, &vers, 0); + (char *) NULL, 1, aflag, vers, 0); if (status == T_UPTODATE || status == T_REMOVE_ENTRY) status = T_MODIFIED; } /* now, muck with vers to make the tag correct */ - free (vers->tag); - vers->tag = xstrdup (tag); + free ((*vers)->tag); + (*vers)->tag = xstrdup (tag); free (xtag); } } else status = Classify_File (finfo, tag, (char *) NULL, (char *) NULL, - 1, 0, &vers, 0); + 1, 0, vers, 0); noexec = save_noexec; quiet = save_quiet; really_quiet = save_really_quiet; + return status; +} + +/* + * Check to see if a file is ok to commit and make sure all files are + * up-to-date + */ +/* ARGSUSED */ +static int +check_fileproc (callerdat, finfo) + void *callerdat; + struct file_info *finfo; +{ + Ctype status; + char *xdir; + Node *p; + List *ulist, *cilist; + Vers_TS *vers; + struct commit_info *ci; + struct logfile_info *li; + + status = classify_file_internal (finfo, &vers); + /* * If the force-commit option is enabled, and the file in question * appears to be up-to-date, just make it look modified so that @@ -768,22 +790,7 @@ check_fileproc (callerdat, finfo) return (1); } - /* - * If the timestamps differ, look for Conflict indicators - * in the file to see if we should block the commit anyway - */ - run_setup ("%s", GREP); - run_arg (RCS_MERGE_PAT); - run_arg (finfo->file); - retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY); - - if (retcode == -1) - { - error (1, errno, - "fork failed while examining conflict in `%s'", - finfo->fullname); - } - else if (retcode == 0) + if (file_has_markers (finfo)) { error (0, 0, "file `%s' still contains conflict indicators", @@ -805,7 +812,12 @@ check_fileproc (callerdat, finfo) { if (vers->tag == NULL) { - char rcs[PATH_MAX]; + char *rcs; + + rcs = xmalloc (strlen (finfo->repository) + + strlen (finfo->file) + + sizeof RCSEXT + + 5); /* Don't look in the attic; if it exists there we will move it back out in checkaddfile. */ @@ -817,8 +829,10 @@ check_fileproc (callerdat, finfo) "cannot add file `%s' when RCS file `%s' already exists", finfo->fullname, rcs); freevers_ts (&vers); + free (rcs); return (1); } + free (rcs); } if (vers->tag && isdigit (*vers->tag) && numdots (vers->tag) > 1) @@ -868,6 +882,8 @@ check_fileproc (callerdat, finfo) xmalloc (sizeof (struct logfile_info))); li->type = status; li->tag = xstrdup (vers->tag); + li->rev_old = xstrdup (vers->vn_rcs); + li->rev_new = NULL; p->data = (char *) li; (void) addnode (ulist, p); @@ -1017,7 +1033,7 @@ check_filesdoneproc (callerdat, err, repos, update_dir, entries) * Do the work of committing a file */ static int maxrev; -static char sbranch[PATH_MAX]; +static char *sbranch; /* ARGSUSED */ static int @@ -1049,11 +1065,13 @@ commit_fileproc (callerdat, finfo) * with files as args from the command line. In that latter case, we * need to get the commit message ourselves */ - if (use_editor && !got_message) - { + if (!(got_message)) + { got_message = 1; - do_editor (finfo->update_dir, &message, finfo->repository, ulist); - } + if (use_editor) + do_editor (finfo->update_dir, &message, finfo->repository, ulist); + do_verify (message, finfo->repository); + } p = findnode (cilist, finfo->file); if (p == NULL) @@ -1172,6 +1190,33 @@ out: if (p) delnode (p); } + else + { + /* On success, retrieve the new version number of the file and + copy it into the log information (see logmsg.c + (logfile_write) for more details). We should only update + the version number for files that have been added or + modified but not removed. Why? classify_file_internal + will return the version number of a file even after it has + been removed from the archive, which is not the behavior we + want for our commitlog messages; we want the old version + number and then "NONE." */ + + if (ci->status != T_REMOVED) + { + p = findnode (ulist, finfo->file); + if (p) + { + Vers_TS *vers; + struct logfile_info *li; + + (void) classify_file_internal (finfo, &vers); + li = (struct logfile_info *) p->data; + li->rev_new = xstrdup (vers->vn_rcs); + freevers_ts (&vers); + } + } + } return (err); } @@ -1249,10 +1294,12 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) line[--line_length] = '\0'; repository = Name_Repository ((char *) NULL, update_dir); run_setup ("%s %s", line, repository); - (void) printf ("%s %s: Executing '", program_name, - command_name); + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (command_name, 0); + cvs_output (": Executing '", 0); run_print (stdout); - (void) printf ("'\n"); + cvs_output ("'\n", 0); (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); free (repository); } @@ -1309,13 +1356,12 @@ commit_direntproc (callerdat, dir, repos, update_dir, entries) error (0, 0, "Committing %s", update_dir); /* get commit message */ + real_repos = Name_Repository (dir, update_dir); + got_message = 1; if (use_editor) - { - got_message = 1; - real_repos = Name_Repository (dir, update_dir); do_editor (update_dir, &message, real_repos, ulist); - free (real_repos); - } + do_verify (message, real_repos); + free (real_repos); return (R_PROCESS); } @@ -1423,7 +1469,9 @@ remove_file (finfo, tag, message) /* commit a new, dead revision. */ /* Print message indicating that file is going to be removed. */ - (void) printf ("Removing %s;\n", finfo->fullname); + cvs_output ("Removing ", 0); + cvs_output (finfo->fullname, 0); + cvs_output (";\n", 0); rev = NULL; lockflag = 1; @@ -1543,10 +1591,12 @@ remove_file (finfo, tag, message) } /* Print message that file was removed. */ - (void) printf ("%s <-- %s\n", old_path, finfo->file); - (void) printf ("new revision: delete; "); - (void) printf ("previous revision: %s\n", prev_rev); - (void) printf ("done\n"); + cvs_output (old_path, 0); + cvs_output (" <-- ", 0); + cvs_output (finfo->file, 0); + cvs_output ("\nnew revision: delete; previous revision: ", 0); + cvs_output (prev_rev, 0); + cvs_output ("\ndone\n", 0); free(prev_rev); if (old_path != finfo->rcs->path) @@ -1567,20 +1617,23 @@ finaladd (finfo, rev, tag, options) char *options; { int ret; - char tmp[PATH_MAX]; - char rcs[PATH_MAX]; + char *rcs; - locate_rcs (finfo->file, finfo->repository, rcs); + rcs = locate_rcs (finfo->file, finfo->repository); ret = Checkin ('A', finfo, rcs, rev, tag, options, message); if (ret == 0) { + char *tmp = xmalloc (strlen (finfo->file) + sizeof (CVSADM) + + sizeof (CVSEXT_LOG) + 10); (void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG); (void) unlink_file (tmp); + free (tmp); } else fixaddfile (finfo->file, finfo->repository); (void) time (&last_register_time); + free (rcs); return (ret); } @@ -1608,10 +1661,10 @@ fixaddfile (file, repository) char *repository; { RCSNode *rcsfile; - char rcs[PATH_MAX]; + char *rcs; int save_really_quiet; - locate_rcs (file, repository, rcs); + rcs = locate_rcs (file, repository); save_really_quiet = really_quiet; really_quiet = 1; if ((rcsfile = RCS_parsercsfile (rcs)) == NULL) @@ -1619,6 +1672,7 @@ fixaddfile (file, repository) else freercsnode (&rcsfile); really_quiet = save_really_quiet; + free (rcs); } /* @@ -1631,7 +1685,7 @@ fixbranch (rcs, branch) { int retcode; - if (branch != NULL && branch[0] != '\0') + if (branch != NULL) { if ((retcode = RCS_setbranch (rcs, branch)) != 0) error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, @@ -1653,15 +1707,18 @@ checkaddfile (file, repository, tag, options, rcsnode) char *options; RCSNode **rcsnode; { - char rcs[PATH_MAX]; - char fname[PATH_MAX]; + char *rcs; + char *fname; mode_t omask; int retcode = 0; int newfile = 0; RCSNode *rcsfile = NULL; + int retval; if (tag) { + rcs = xmalloc (strlen (repository) + strlen (file) + + sizeof (RCSEXT) + sizeof (CVSATTIC) + 10); (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); if (! isreadable (rcs)) { @@ -1675,59 +1732,85 @@ checkaddfile (file, repository, tag, options, rcsnode) } } else - locate_rcs (file, repository, rcs); + rcs = locate_rcs (file, repository); - if (isreadable(rcs)) + if (isreadable (rcs)) { /* file has existed in the past. Prepare to resurrect. */ - char oldfile[PATH_MAX]; char *rev; if ((rcsfile = *rcsnode) == NULL) { error (0, 0, "could not find parsed rcsfile %s", file); - return (1); + retval = 1; + goto out; } if (tag == NULL) { + char *oldfile; + /* we are adding on the trunk, so move the file out of the Attic. */ - strcpy (oldfile, rcs); + oldfile = xstrdup (rcs); sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); - if (strcmp (oldfile, rcs) == 0 - || CVS_RENAME (oldfile, rcs) != 0 - || isreadable (oldfile) + if (strcmp (oldfile, rcs) == 0) + { + error (0, 0, "internal error: confused about attic for %s", + oldfile); + out1: + free (oldfile); + retval = 1; + goto out; + } + if (CVS_RENAME (oldfile, rcs) != 0) + { + error (0, errno, "failed to move `%s' out of the attic", + oldfile); + goto out1; + } + if (isreadable (oldfile) || !isreadable (rcs)) { - error (0, 0, "failed to move `%s' out of the attic.", - file); - return (1); + error (0, 0, "\ +internal error: `%s' didn't move out of the attic", + oldfile); + goto out1; } + free (oldfile); free (rcsfile->path); rcsfile->path = xstrdup (rcs); } rev = RCS_getversion (rcsfile, tag, NULL, 1, (int *) NULL); /* and lock it */ - if (lock_RCS (file, rcsfile, rev, repository)) { + if (lock_RCS (file, rcsfile, rev, repository)) + { error (0, 0, "cannot lock `%s'.", rcs); - free (rev); - return (1); + if (rev != NULL) + free (rev); + retval = 1; + goto out; } - free (rev); - } else { + if (rev != NULL) + free (rev); + } + else + { /* this is the first time we have ever seen this file; create an rcs file. */ run_setup ("%s%s -x,v/ -i", Rcsbin, RCS); + fname = xmalloc (strlen (file) + sizeof (CVSADM) + + sizeof (CVSEXT_LOG) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG); /* If the file does not exist, no big deal. In particular, the server does not (yet at least) create CVSEXT_LOG files. */ if (isfile (fname)) run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG); + free (fname); /* Set RCS keyword expansion options. */ if (options && options[0] == '-' && options[1] == 'k') @@ -1737,7 +1820,8 @@ checkaddfile (file, repository, tag, options, rcsnode) { error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not create %s", rcs); - return (1); + retval = 1; + goto out; } newfile = 1; } @@ -1749,6 +1833,8 @@ checkaddfile (file, repository, tag, options, rcsnode) char *tmp; /* move the new file out of the way. */ + fname = xmalloc (strlen (file) + sizeof (CVSADM) + + sizeof (CVSPREFIX) + 10); (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file); rename_file (file, fname); copy_file (DEVNULL, file); @@ -1764,18 +1850,21 @@ checkaddfile (file, repository, tag, options, rcsnode) { error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not create initial dead revision %s", rcs); - return (1); + retval = 1; + goto out; } /* put the new file back where it was */ rename_file (fname, file); + free (fname); assert (rcsfile == NULL); rcsfile = RCS_parse (file, repository); if (rcsfile == NULL) { error (0, 0, "could not read %s", rcs); - return (1); + retval = 1; + goto out; } if (rcsnode != NULL) { @@ -1784,9 +1873,11 @@ checkaddfile (file, repository, tag, options, rcsnode) } /* and lock it once again. */ - if (lock_RCS (file, rcsfile, NULL, repository)) { + if (lock_RCS (file, rcsfile, NULL, repository)) + { error (0, 0, "cannot lock `%s'.", rcs); - return (1); + retval = 1; + goto out; } } @@ -1805,16 +1896,18 @@ checkaddfile (file, repository, tag, options, rcsnode) if (rcsfile == NULL) { error (0, 0, "could not read %s", rcs); - return (1); + retval = 1; + goto out; } } } - if (!RCS_nodeisbranch (rcsfile, tag)) { + if (!RCS_nodeisbranch (rcsfile, tag)) + { /* branch does not exist. Stub it. */ char *head; char *magicrev; - + head = RCS_getversion (rcsfile, NULL, NULL, 0, (int *) NULL); magicrev = RCS_magicrev (rcsfile, head); @@ -1827,15 +1920,18 @@ checkaddfile (file, repository, tag, options, rcsnode) { error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0, "could not stub branch %s for %s", tag, rcs); - return (1); + retval = 1; + goto out; } } else { /* lock the branch. (stubbed branches need not be locked.) */ - if (lock_RCS (file, rcsfile, NULL, repository)) { + if (lock_RCS (file, rcsfile, NULL, repository)) + { error (0, 0, "cannot lock `%s'.", rcs); - return (1); + retval = 1; + goto out; } } @@ -1849,7 +1945,11 @@ checkaddfile (file, repository, tag, options, rcsnode) fileattr_newfile (file); fix_rcs_modes (rcs, file); - return (0); + retval = 0; + + out: + free (rcs); + return retval; } /* @@ -1900,13 +2000,14 @@ lock_RCS (user, rcs, rev, repository) if (err == 0) { + if (sbranch != NULL) + free (sbranch); if (branch) { - (void) strcpy (sbranch, branch); - free (branch); + sbranch = branch; } else - sbranch[0] = '\0'; + sbranch = NULL; return (0); } @@ -1950,6 +2051,10 @@ update_delproc (p) li = (struct logfile_info *) p->data; if (li->tag) free (li->tag); + if (li->rev_old) + free (li->rev_old); + if (li->rev_new) + free (li->rev_new); free (li); } @@ -1987,15 +2092,23 @@ masterlist_delproc (p) free (ml); } -/* - * Find an RCS file in the repository. - */ -static void -locate_rcs (file, repository, rcs) +/* Find an RCS file in the repository. Most parts of CVS will want to + rely instead on RCS_parse which performs a similar operation and is + called by recurse.c which then puts the result in useful places + like the rcs field of struct file_info. + + REPOSITORY is the repository (including the directory) and FILE is + the filename within that directory (without RCSEXT). Returns a + newly-malloc'd array containing the absolute pathname of the RCS + file that was found. */ +static char * +locate_rcs (file, repository) char *file; char *repository; - char *rcs; { + char *rcs; + + rcs = xmalloc (strlen (repository) + strlen (file) + sizeof (RCSEXT) + 10); (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); if (!isreadable (rcs)) { @@ -2003,4 +2116,5 @@ locate_rcs (file, repository, rcs) if (!isreadable (rcs)) (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); } + return rcs; } diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h index dd96dd063a3..1d48096bdf9 100644 --- a/gnu/usr.bin/cvs/src/cvs.h +++ b/gnu/usr.bin/cvs/src/cvs.h @@ -1,5 +1,3 @@ -/* $CVSid: @(#)cvs.h 1.86 94/10/22 $ */ - /* * basic information used in all source files * @@ -170,6 +168,7 @@ extern int errno; #define CVSROOTADM_COMMITINFO "commitinfo" #define CVSROOTADM_TAGINFO "taginfo" #define CVSROOTADM_EDITINFO "editinfo" +#define CVSROOTADM_VERIFYMSG "verifymsg" #define CVSROOTADM_HISTORY "history" #define CVSROOTADM_VALTAGS "val-tags" #define CVSROOTADM_IGNORE "cvsignore" @@ -177,6 +176,9 @@ extern int errno; #define CVSROOTADM_WRAPPER "cvswrappers" #define CVSROOTADM_NOTIFY "notify" #define CVSROOTADM_USERS "users" +#define CVSROOTADM_READERS "readers" +#define CVSROOTADM_WRITERS "writers" +#define CVSROOTADM_PASSWD "passwd" #define CVSROOTADM_OPTIONS "options" #define CVSNULLREPOS "Emptydir" /* an empty directory */ @@ -199,8 +201,21 @@ extern int errno; #define CVSDOTIGNORE ".cvsignore" #define CVSDOTWRAPPER ".cvswrappers" +/* Command attributes -- see function lookup_command_attribute(). */ +#define CVS_CMD_IGNORE_ADMROOT 1 +#define CVS_CMD_USES_WORK_DIR 2 +#define CVS_CMD_MODIFIES_REPOSITORY 4 + /* miscellaneous CVS defines */ + +/* This is the string which is at the start of the non-log-message lines + that we put up for the user when they edit the log message. */ #define CVSEDITPREFIX "CVS: " +/* Number of characters in CVSEDITPREFIX to compare when deciding to strip + off those lines. We don't check for the space, to accomodate users who + have editors which strip trailing spaces. */ +#define CVSEDITPREFIXLEN 4 + #define CVSLCKAGE (60*60) /* 1-hour old lock files cleaned up */ #define CVSLCKSLEEP 30 /* wait 30 seconds before retrying */ #define CVSBRANCH "1.1.1" /* RCS branch used for vendor srcs */ @@ -361,6 +376,12 @@ extern int noexec; /* Don't modify disk anywhere */ extern int readonlyfs; /* fail on all write locks; succeed all read locks */ extern int logoff; /* Don't write history entry */ +#ifdef AUTH_SERVER_SUPPORT +extern char *Pserver_Repos; /* used to check that same repos is + transmitted in pserver auth and in + CVS protocol. */ +#endif /* AUTH_SERVER_SUPPORT */ + extern char hostname[]; /* Externs that are included directly in the CVS sources */ @@ -411,10 +432,8 @@ char *xstrdup PROTO((const char *str)); void strip_trailing_newlines PROTO((char *str)); typedef int (*CALLPROC) PROTO((char *repository, char *value)); int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all)); -int Reader_Lock PROTO((char *xrepository)); typedef RETSIGTYPE (*SIGCLEANUPPROC) PROTO(()); int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup)); -int Writer_Lock PROTO((List * list)); int isdir PROTO((const char *file)); int isfile PROTO((const char *file)); int islink PROTO((const char *file)); @@ -439,15 +458,17 @@ time_t get_date PROTO((char *date, struct timeb *now)); void Create_Admin PROTO((char *dir, char *update_dir, char *repository, char *tag, char *date)); +/* Locking subsystem (implemented in lock.c). */ + +int Reader_Lock PROTO((char *xrepository)); void Lock_Cleanup PROTO((void)); /* Writelock an entire subtree, well the part specified by ARGC, ARGV, LOCAL, and AFLAG, anyway. */ void lock_tree_for_write PROTO ((int argc, char **argv, int local, int aflag)); -/* Remove locks set by lock_tree_for_write. Currently removes readlocks - too. */ -void lock_tree_cleanup PROTO ((void)); +/* See lock.c for description. */ +extern void lock_dir_for_write PROTO ((char *)); void ParseTag PROTO((char **tagp, char **datep)); void Scratch_Entry PROTO((List * list, char *fname)); @@ -456,7 +477,6 @@ void cat_module PROTO((int status)); void check_entries PROTO((char *dir)); void close_module PROTO((DBM * db)); void copy_file PROTO((const char *from, const char *to)); -void (*error_set_cleanup PROTO((void (*) (void)))) PROTO ((void)); void fperror PROTO((FILE * fp, int status, int errnum, char *message,...)); void free_names PROTO((int *pargc, char *argv[])); @@ -488,7 +508,6 @@ void rename_file PROTO((const char *from, const char *to)); extern void expand_wild PROTO ((int argc, char **argv, int *pargc, char ***pargv)); -void strip_path PROTO((char *path)); void strip_trailing_slashes PROTO((char *path)); void update_delproc PROTO((Node * p)); void usage PROTO((const char *const *cpp)); @@ -503,6 +522,8 @@ void Update_Logfile PROTO((char *repository, char *xmessage, FILE * xlogfp, void do_editor PROTO((char *dir, char **messagep, char *repository, List * changes)); +void do_verify PROTO((char *message, char *repository)); + typedef int (*CALLBACKPROC) PROTO((int *pargc, char *argv[], char *where, char *mwhere, char *mfile, int horten, int local_specified, char *omodule, char *msg)); @@ -565,6 +586,7 @@ void SIG_endCrSect PROTO((void)); void read_cvsrc PROTO((int *argc, char ***argv, char *cmdname)); char *make_message_rcslegal PROTO((char *message)); +extern int file_has_markers PROTO ((struct file_info *)); /* flags for run_exec(), the fast system() for CVS */ #define RUN_NORMAL 0x0000 /* no special behaviour */ @@ -699,6 +721,10 @@ struct logfile_info { enum classify_type type; char *tag; + char *rev_old; /* rev number before a commit/modify, + NULL for add or import */ + char *rev_new; /* rev number after a commit/modify, + add, or import, NULL for remove */ }; /* Wrappers. */ @@ -736,6 +762,25 @@ int unedit PROTO ((int argc, char **argv)); int editors PROTO ((int argc, char **argv)); int watchers PROTO ((int argc, char **argv)); extern int annotate PROTO ((int argc, char **argv)); +extern int add PROTO ((int argc, char **argv)); +extern int admin PROTO ((int argc, char **argv)); +extern int checkout PROTO ((int argc, char **argv)); +extern int commit PROTO ((int argc, char **argv)); +extern int diff PROTO ((int argc, char **argv)); +extern int history PROTO ((int argc, char **argv)); +extern int import PROTO ((int argc, char **argv)); +extern int cvslog PROTO ((int argc, char **argv)); +#ifdef AUTH_CLIENT_SUPPORT +extern int login PROTO((int argc, char **argv)); +#endif /* AUTH_CLIENT_SUPPORT */ +extern int patch PROTO((int argc, char **argv)); +extern int release PROTO((int argc, char **argv)); +extern int cvsremove PROTO((int argc, char **argv)); +extern int rtag PROTO((int argc, char **argv)); +extern int status PROTO((int argc, char **argv)); +extern int cvstag PROTO((int argc, char **argv)); + +extern unsigned long int lookup_command_attribute PROTO((char *)); #if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT) char *scramble PROTO ((char *str)); @@ -749,12 +794,11 @@ char *get_cvs_password PROTO((void)); extern void tag_check_valid PROTO ((char *, int, char **, int, int, char *)); extern void tag_check_valid_join PROTO ((char *, int, char **, int, int, char *)); -extern void tag_lockdir PROTO ((char *)); -extern void tag_unlockdir PROTO ((void)); extern void cvs_output PROTO ((const char *, size_t)); extern void cvs_outerr PROTO ((const char *, size_t)); extern void cvs_flusherr PROTO ((void)); +extern void cvs_flushout PROTO ((void)); #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) #include "server.h" diff --git a/gnu/usr.bin/cvs/src/ignore.c b/gnu/usr.bin/cvs/src/ignore.c index 6fa9e89ea0b..1a839586f46 100644 --- a/gnu/usr.bin/cvs/src/ignore.c +++ b/gnu/usr.bin/cvs/src/ignore.c @@ -43,7 +43,6 @@ void ign_setup () { char *home_dir; - char file[PATH_MAX]; char *tmp; ign_inhibit_server = 0; @@ -61,18 +60,23 @@ ign_setup () if (!client_active) #endif { + char *file = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + + sizeof (CVSROOTADM_IGNORE) + 10); /* Then add entries found in repository, if it exists */ (void) sprintf (file, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSROOTADM_IGNORE); ign_add_file (file, 0); + free (file); } /* Then add entries found in home dir, (if user has one) and file exists */ home_dir = get_homedir (); if (home_dir) { + char *file = xmalloc (strlen (home_dir) + sizeof (CVSDOTIGNORE) + 10); (void) sprintf (file, "%s/%s", home_dir, CVSDOTIGNORE); ign_add_file (file, 0); + free (file); } /* Then add entries found in CVSIGNORE environment variable. */ @@ -92,6 +96,7 @@ ign_add_file (file, hold) int hold; { FILE *fp; + /* FIXME: arbitrary limit. */ char line[1024]; /* restore the saved list (if any) */ @@ -424,11 +429,16 @@ ignore_files (ilist, entries, update_dir, proc) { if (! subdirs) { - char temp[PATH_MAX]; + char *temp; + temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10); (void) sprintf (temp, "%s/%s", file, CVSADM); if (isdir (temp)) + { + free (temp); continue; + } + free (temp); } } #ifdef S_ISLNK diff --git a/gnu/usr.bin/cvs/src/lock.c b/gnu/usr.bin/cvs/src/lock.c index 81179bde66c..99772392d5b 100644 --- a/gnu/usr.bin/cvs/src/lock.c +++ b/gnu/usr.bin/cvs/src/lock.c @@ -74,39 +74,86 @@ #include "cvs.h" +struct lock { + /* This is the directory in which we may have a lock named by the + readlock variable, a lock named by the writelock variable, and/or + a lock named CVSLCK. The storage is not allocated along with the + struct lock; it is allocated by the Reader_Lock caller or in the + case of writelocks, it is just a pointer to the storage allocated + for the ->key field. */ + char *repository; + /* Do we have a lock named CVSLCK? */ + int have_lckdir; + /* Note there is no way of knowing whether the readlock and writelock + exist. The code which sets the locks doesn't use SIG_beginCrSect + to set a flag like we do for CVSLCK. */ +}; + +static void remove_locks PROTO((void)); static int readers_exist PROTO((char *repository)); -static int set_lock PROTO((char *repository, int will_wait)); -static void clear_lock PROTO((void)); +static int set_lock PROTO ((struct lock *lock, int will_wait)); +static void clear_lock PROTO ((struct lock *lock)); static void set_lockers_name PROTO((struct stat *statp)); static int set_writelock_proc PROTO((Node * p, void *closure)); static int unlock_proc PROTO((Node * p, void *closure)); -static int write_lock PROTO((char *repository)); -static void lock_simple_remove PROTO((char *repository)); +static int write_lock PROTO ((struct lock *lock)); +static void lock_simple_remove PROTO ((struct lock *lock)); static void lock_wait PROTO((char *repository)); static void lock_obtained PROTO((char *repository)); -static int Check_Owner PROTO((char *lockdir)); static char lockers_name[20]; -static char *repository; static char readlock[PATH_MAX], writelock[PATH_MAX], masterlock[PATH_MAX]; -static int cleanup_lckdir; static List *locklist; #define L_OK 0 /* success */ #define L_ERROR 1 /* error condition */ #define L_LOCKED 2 /* lock owned by someone else */ +/* This is the (single) readlock which is set by Reader_Lock. The + repository field is NULL if there is no such lock. */ +static struct lock global_readlock; + +/* List of locks set by lock_tree_for_write. This is redundant + with locklist, sort of. */ +static List *lock_tree_list; + +/* If we set locks with lock_dir_for_write, then locked_dir contains + the malloc'd name of the repository directory which we have locked. + locked_list is the same thing packaged into a list and is redundant + with locklist the same way that lock_tree_list is. */ +static char *locked_dir; +static List *locked_list; + /* * Clean up all outstanding locks */ void Lock_Cleanup () { + remove_locks (); + + dellist (&lock_tree_list); + + if (locked_dir != NULL) + { + dellist (&locked_list); + free (locked_dir); + locked_dir = NULL; + locked_list = NULL; + } +} + +/* + * Remove locks without discarding the lock information + */ +static void +remove_locks () +{ /* clean up simple locks (if any) */ - if (repository != NULL) + if (global_readlock.repository != NULL) { - lock_simple_remove (repository); - repository = (char *) NULL; + lock_simple_remove (&global_readlock); + global_readlock.repository = NULL; } /* clean up multiple locks (if any) */ @@ -125,87 +172,51 @@ unlock_proc (p, closure) Node *p; void *closure; { - lock_simple_remove (p->key); + lock_simple_remove ((struct lock *)p->data); return (0); } -/* - * Remove the lock files (without complaining if they are not there), - */ +/* Remove the lock files. */ static void -lock_simple_remove (repository) - char *repository; +lock_simple_remove (lock) + struct lock *lock; { char tmp[PATH_MAX]; + /* If readlock is set, the lock directory *might* have been created, but + since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock + does, we don't know that. That is why we need to check for + existence_error here. */ if (readlock[0] != '\0') { - (void) sprintf (tmp, "%s/%s", repository, readlock); + (void) sprintf (tmp, "%s/%s", lock->repository, readlock); if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); } + /* If writelock is set, the lock directory *might* have been created, but + since write_lock doesn't use SIG_beginCrSect the way that set_lock + does, we don't know that. That is why we need to check for + existence_error here. */ if (writelock[0] != '\0') { - (void) sprintf (tmp, "%s/%s", repository, writelock); + (void) sprintf (tmp, "%s/%s", lock->repository, writelock); if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno)) error (0, errno, "failed to remove lock %s", tmp); } - /* - * Only remove the lock directory if it is ours, note that this does - * lead to the limitation that one user ID should not be committing - * files into the same Repository directory at the same time. Oh well. - */ - if (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)) + if (lock->have_lckdir) { - (void) sprintf (tmp, "%s/%s", repository, CVSLCK); - if (Check_Owner(tmp)) - { -#ifdef AFSCVS - char rmuidlock[PATH_MAX]; - sprintf(rmuidlock, "rm -f %s/uidlock%d", tmp, geteuid() ); - system(rmuidlock); -#endif - (void) CVS_RMDIR (tmp); - } + (void) sprintf (tmp, "%s/%s", lock->repository, CVSLCK); + SIG_beginCrSect (); + if (CVS_RMDIR (tmp) < 0) + error (0, errno, "failed to remove lock dir %s", tmp); + lock->have_lckdir = 0; + SIG_endCrSect (); } - cleanup_lckdir = 0; } /* - * Check the owner of a lock. Returns 1 if we own it, 0 otherwise. - */ -static int -Check_Owner(lockdir) - char *lockdir; -{ - struct stat sb; - -#ifdef AFSCVS - /* In the Andrew File System (AFS), user ids from stat don't match - those from geteuid(). The AFSCVS code can deal with either AFS or - non-AFS repositories; the non-AFSCVS code is faster. */ - char uidlock[PATH_MAX]; - - /* Check if the uidlock is in the lock directory */ - sprintf(uidlock, "%s/uidlock%d", lockdir, geteuid() ); - if( stat(uidlock, &sb) != -1) - return 1; /* The file exists, therefore we own the lock */ - else - return 0; /* The file didn't exist or some other error. - * Assume that we don't own it. - */ -#else - if ( CVS_STAT (lockdir, &sb) != -1 && sb.st_uid == geteuid ()) - return 1; - else - return 0; -#endif -} /* end Check_Owner() */ - - -/* * Create a lock file for readers */ int @@ -220,7 +231,7 @@ Reader_Lock (xrepository) return (0); /* we only do one directory at a time for read locks! */ - if (repository != NULL) + if (global_readlock.repository != NULL) { error (0, 0, "Reader_Lock called while read locks set - Help!"); return (1); @@ -235,15 +246,18 @@ Reader_Lock (xrepository) #endif (long) getpid ()); - /* remember what we're locking (for lock_cleanup) */ - repository = xrepository; + /* remember what we're locking (for Lock_Cleanup) */ + global_readlock.repository = xrepository; /* get the lock dir for our own */ - if (set_lock (xrepository, 1) != L_OK) + if (set_lock (&global_readlock, 1) != L_OK) { error (0, 0, "failed to obtain dir lock in repository `%s'", xrepository); readlock[0] = '\0'; + /* We don't set global_readlock.repository to NULL. I think this + only works because recurse.c will give a fatal error if we return + a nonzero value. */ return (1); } @@ -258,7 +272,7 @@ Reader_Lock (xrepository) } /* free the lock dir */ - clear_lock(); + clear_lock (&global_readlock); return (err); } @@ -268,7 +282,10 @@ Reader_Lock (xrepository) */ static char *lock_error_repos; static int lock_error; -int + +static int Writer_Lock PROTO ((List * list)); + +static int Writer_Lock (list) List *list; { @@ -305,7 +322,7 @@ Writer_Lock (list) return (1); case L_LOCKED: /* Someone already had a lock */ - Lock_Cleanup (); /* clean up any locks we set */ + remove_locks (); /* clean up any locks we set */ lock_wait (lock_error_repos); /* sleep a while and try again */ wait_repos = xstrdup (lock_error_repos); continue; @@ -342,7 +359,7 @@ set_writelock_proc (p, closure) /* apply the write lock */ lock_error_repos = p->key; - lock_error = write_lock (p->key); + lock_error = write_lock ((struct lock *)p->data); return (0); } @@ -351,8 +368,8 @@ set_writelock_proc (p, closure) * lock held by someone else or L_ERROR if an error occurred */ static int -write_lock (repository) - char *repository; +write_lock (lock) + struct lock *lock; { int status; FILE *fp; @@ -368,16 +385,16 @@ write_lock (repository) (long) getpid()); /* make sure the lock dir is ours (not necessarily unique to us!) */ - status = set_lock (repository, 0); + status = set_lock (lock, 0); if (status == L_OK) { /* we now own a writer - make sure there are no readers */ - if (readers_exist (repository)) + if (readers_exist (lock->repository)) { /* clean up the lock dir if we created it */ if (status == L_OK) { - clear_lock(); + clear_lock (lock); } /* indicate we failed due to read locks instead of error */ @@ -385,7 +402,7 @@ write_lock (repository) } /* write the write-lock file */ - (void) sprintf (tmp, "%s/%s", repository, writelock); + (void) sprintf (tmp, "%s/%s", lock->repository, writelock); if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF) { int xerrno = errno; @@ -396,12 +413,12 @@ write_lock (repository) /* free the lock dir if we created it */ if (status == L_OK) { - clear_lock(); + clear_lock (lock); } /* return the error */ error (0, xerrno, "cannot create write lock in repository `%s'", - repository); + lock->repository); return (L_ERROR); } return (L_OK); @@ -509,8 +526,8 @@ set_lockers_name (statp) * seconds old, just try to remove the directory. */ static int -set_lock (repository, will_wait) - char *repository; +set_lock (lock, will_wait) + struct lock *lock; int will_wait; { int waited; @@ -520,7 +537,7 @@ set_lock (repository, will_wait) time_t now; #endif - (void) sprintf (masterlock, "%s/%s", repository, CVSLCK); + (void) sprintf (masterlock, "%s/%s", lock->repository, CVSLCK); /* * Note that it is up to the callers of set_lock() to arrange for signal @@ -528,7 +545,7 @@ set_lock (repository, will_wait) * directory before they exit. */ waited = 0; - cleanup_lckdir = 0; + lock->have_lckdir = 0; for (;;) { int status = -1; @@ -536,29 +553,11 @@ set_lock (repository, will_wait) SIG_beginCrSect (); if (CVS_MKDIR (masterlock, 0777) == 0) { -#ifdef AFSCVS - char uidlock[PATH_MAX]; - FILE *fp; - - sprintf(uidlock, "%s/uidlock%d", masterlock, geteuid() ); - if ((fp = CVS_FOPEN (uidlock, "w+")) == NULL) - { - /* We failed to create the uidlock, - so rm masterlock and leave */ - CVS_RMDIR (masterlock); - SIG_endCrSect (); - status = L_ERROR; - goto out; - } - - /* We successfully created the uid lock, so close the file */ - fclose(fp); -#endif - cleanup_lckdir = 1; + lock->have_lckdir = 1; SIG_endCrSect (); status = L_OK; if (waited) - lock_obtained (repository); + lock_obtained (lock->repository); goto out; } SIG_endCrSect (); @@ -571,7 +570,7 @@ set_lock (repository, will_wait) { error (0, errno, "failed to create lock directory in repository `%s'", - repository); + lock->repository); return (L_ERROR); } @@ -596,12 +595,6 @@ set_lock (repository, will_wait) (void) time (&now); if (now >= (sb.st_ctime + CVSLCKAGE)) { -#ifdef AFSCVS - /* Remove the uidlock first */ - char rmuidlock[PATH_MAX]; - sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() ); - system(rmuidlock); -#endif if (CVS_RMDIR (masterlock) >= 0) continue; } @@ -613,7 +606,7 @@ set_lock (repository, will_wait) /* if he wasn't willing to wait, return an error */ if (!will_wait) return (L_LOCKED); - lock_wait (repository); + lock_wait (lock->repository); waited = 1; } } @@ -623,17 +616,14 @@ set_lock (repository, will_wait) * clear_lock is never called except after a successful set_lock(). */ static void -clear_lock() +clear_lock (lock) + struct lock *lock; { -#ifdef AFSCVS - /* Remove the uidlock first */ - char rmuidlock[PATH_MAX]; - sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() ); - system(rmuidlock); -#endif + SIG_beginCrSect (); if (CVS_RMDIR (masterlock) < 0) error (0, errno, "failed to remove lock dir `%s'", masterlock); - cleanup_lckdir = 0; + lock->have_lckdir = 0; + SIG_endCrSect (); } /* @@ -675,8 +665,6 @@ static int lock_filesdoneproc PROTO ((void *callerdat, int err, List *entries)); static int fsortcmp PROTO((const Node * p, const Node * q)); -static List *lock_tree_list; - /* * Create a list of repositories to lock */ @@ -694,6 +682,10 @@ lock_filesdoneproc (callerdat, err, repository, update_dir, entries) p = getnode (); p->type = LOCK; p->key = xstrdup (repository); + p->data = xmalloc (sizeof (struct lock)); + ((struct lock *)p->data)->repository = p->key; + ((struct lock *)p->data)->have_lckdir = 0; + /* FIXME-KRP: this error condition should not simply be passed by. */ if (p->key == NULL || addnode (lock_tree_list, p) != 0) freenode (p); @@ -731,10 +723,33 @@ lock_tree_for_write (argc, argv, local, aflag) if (Writer_Lock (lock_tree_list) != 0) error (1, 0, "lock failed - giving up"); } - + +/* Lock a single directory in REPOSITORY. It is OK to call this if + a lock has been set with lock_dir_for_write; the new lock will replace + the old one. If REPOSITORY is NULL, don't do anything. */ void -lock_tree_cleanup () +lock_dir_for_write (repository) + char *repository; { - Lock_Cleanup (); - dellist (&lock_tree_list); + if (repository != NULL + && (locked_dir == NULL + || strcmp (locked_dir, repository) != 0)) + { + Node *node; + + if (locked_dir != NULL) + Lock_Cleanup (); + + locked_dir = xstrdup (repository); + locked_list = getlist (); + node = getnode (); + node->type = LOCK; + node->key = xstrdup (repository); + node->data = xmalloc (sizeof (struct lock)); + ((struct lock *)node->data)->repository = node->key; + ((struct lock *)node->data)->have_lckdir = 0; + + (void) addnode (locked_list, node); + Writer_Lock (locked_list); + } } diff --git a/gnu/usr.bin/cvs/src/main.c b/gnu/usr.bin/cvs/src/main.c index d882632b666..1196ce4d105 100644 --- a/gnu/usr.bin/cvs/src/main.c +++ b/gnu/usr.bin/cvs/src/main.c @@ -52,27 +52,6 @@ char *CurDir; char *Rcsbin = RCSBIN_DFLT; char *Tmpdir = TMPDIR_DFLT; char *Editor = EDITOR_DFLT; -/* - * The path found in CVS/Root must match $CVSROOT and/or 'cvs -d 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)); -#ifdef AUTH_CLIENT_SUPPORT -int login PROTO((int argc, char **argv)); -#endif /* AUTH_CLIENT_SUPPORT */ -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)); static const struct cmd { @@ -100,7 +79,7 @@ static const struct cmd char *nick1; char *nick2; - + int (*func) (); /* Function takes (argc, argv) arguments. */ } cmds[] = @@ -132,7 +111,7 @@ static const struct cmd { "remove", "rm", "delete", cvsremove }, { "status", "st", "stat", status }, { "rtag", "rt", "rfreeze", rtag }, - { "tag", "ta", "freeze", tag }, + { "tag", "ta", "freeze", cvstag }, { "unedit", NULL, NULL, unedit }, { "update", "up", "upd", update }, { "watch", NULL, NULL, watch }, @@ -240,6 +219,50 @@ cmd_synonyms () return (const char * const*) synonyms; /* will never be freed */ } + +unsigned long int +lookup_command_attribute (char *cmd_name) +{ + unsigned long int ret = 0; + + if (strcmp (cmd_name, "import") != 0) + { + ret |= CVS_CMD_IGNORE_ADMROOT; + } + + + if ((strcmp (cmd_name, "checkout") != 0) && + (strcmp (cmd_name, "login") != 0) && + (strcmp (cmd_name, "rdiff") != 0) && + (strcmp (cmd_name, "release") != 0) && + (strcmp (cmd_name, "rtag") != 0)) + { + ret |= CVS_CMD_USES_WORK_DIR; + } + + + /* The following commands do not modify the repository; we + conservatively assume that everything else does. Feel free to + add to this list if you are _certain_ something is safe. */ + if ((strcmp (cmd_name, "checkout") != 0) && + (strcmp (cmd_name, "diff") != 0) && + (strcmp (cmd_name, "update") != 0) && + (strcmp (cmd_name, "history") != 0) && + (strcmp (cmd_name, "editors") != 0) && + (strcmp (cmd_name, "export") != 0) && + (strcmp (cmd_name, "history") != 0) && + (strcmp (cmd_name, "log") != 0) && + (strcmp (cmd_name, "noop") != 0) && + (strcmp (cmd_name, "watchers") != 0) && + (strcmp (cmd_name, "status") != 0)) + { + ret |= CVS_CMD_MODIFIES_REPOSITORY; + } + + return ret; +} + + static RETSIGTYPE main_cleanup (sig) int sig; @@ -287,16 +310,6 @@ main_cleanup (sig) #endif /* !DONT_USE_SIGNALS */ } -static void -error_cleanup PROTO((void)) -{ - Lock_Cleanup(); -#ifdef SERVER_SUPPORT - if (server_active) - server_cleanup (0); -#endif -} - int main (argc, argv) int argc; @@ -325,14 +338,18 @@ main (argc, argv) int option_index = 0; int need_to_create_root = 0; - error_set_cleanup (error_cleanup); - #ifdef SYSTEM_INITIALIZE /* Hook for OS-specific behavior, for example socket subsystems on NT and OS2 or dealing with windows and arguments on Mac. */ SYSTEM_INITIALIZE (&argc, &argv); #endif +#ifdef HAVE_TZSET + /* On systems that have tzset (which is almost all the ones I know + of), it's a good idea to call it. */ + tzset (); +#endif + /* * Just save the last component of the path for error messages */ @@ -616,13 +633,13 @@ main (argc, argv) ignores CVS directories and CVS/Root is likely to specify a different repository than the one we are importing to. */ -#if 0 - if (lookup_command_attribute (command_name) & CVS_CMD_IGNORE_ADMROOT) - CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); -#else - if (strcmp (command_name, "import") != 0) + + if (lookup_command_attribute (command_name) + & CVS_CMD_IGNORE_ADMROOT) + { CVSADM_Root = Name_Root((char *) NULL, (char *) NULL); -#endif + } + if (CVSADM_Root != NULL) { if (CVSroot == NULL || !cvs_update_env) @@ -651,16 +668,12 @@ main (argc, argv) "cvs login" command. Ahh, the things one discovers. */ -#if 0 - if (lookup_command_attribute (command_name) & CVS_CMD_USES_WORK_DIR) -#else - if ((strcmp (command_name, "checkout") != 0) && - (strcmp (command_name, "login") != 0) && - (strcmp (command_name, "rdiff") != 0) && - (strcmp (command_name, "release") != 0) && - (strcmp (command_name, "rtag") != 0)) -#endif + if (lookup_command_attribute (command_name) + & CVS_CMD_USES_WORK_DIR) + { need_to_create_root = 1; + } + } } @@ -827,28 +840,13 @@ main (argc, argv) 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. - */ -# ifndef KLUDGE_FOR_WNT_TESTSUITE - - if (strcmp (cm->fullname, "patch")) -# ifdef BUFSIZ /* traditional SysV chokes when size == 0 */ - (void) setvbuf (stdout, (char *) NULL, _IOLBF, BUFSIZ); -# else - (void) setvbuf (stdout, (char *) NULL, _IOLBF, 0); -# endif - -# else /* KLUDGE_FOR_WNT_TESTSUITE */ - - (void) setvbuf (stdout, (char *) NULL, _IONBF, 0); - (void) setvbuf (stderr, (char *) NULL, _IONBF, 0); - -# endif /* KLUDGE_FOR_WNT_TESTSUITE */ - -#endif /* HAVE_SETVBUF */ +#ifdef KLUDGE_FOR_WNT_TESTSUITE + /* Probably the need for this will go away at some point once + we call fflush enough places (e.g. fflush (stdout) in + cvs_outerr). */ + (void) setvbuf (stdout, (char *) NULL, _IONBF, 0); + (void) setvbuf (stderr, (char *) NULL, _IONBF, 0); +#endif /* KLUDGE_FOR_WNT_TESTSUITE */ if (use_cvsrc) read_cvsrc (&argc, &argv, command_name); @@ -910,7 +908,7 @@ usage (cpp) (void) fprintf (stderr, *cpp++, program_name, command_name); for (; *cpp; cpp++) (void) fprintf (stderr, *cpp); - exit (EXIT_FAILURE); + error_exit(); } void diff --git a/gnu/usr.bin/cvs/src/patch.c b/gnu/usr.bin/cvs/src/patch.c index fd0fdd0295e..10e33b065c6 100644 --- a/gnu/usr.bin/cvs/src/patch.c +++ b/gnu/usr.bin/cvs/src/patch.c @@ -356,6 +356,11 @@ patch_fileproc (callerdat, finfo) char *cp1, *cp2; FILE *fp; + line1 = NULL; + line1_chars_allocated = 0; + line2 = NULL; + line2_chars_allocated = 0; + /* find the parsed rcs file */ if ((rcsfile = finfo->rcs) == NULL) return (1); @@ -449,7 +454,7 @@ patch_fileproc (callerdat, finfo) if (vers_tag != NULL) { retcode = RCS_checkout (rcsfile, (char *) NULL, vers_tag, - (char *) NULL, options, tmpfile1); + rev1, options, tmpfile1); if (retcode != 0) { if (!really_quiet) @@ -471,7 +476,7 @@ patch_fileproc (callerdat, finfo) if (vers_head != NULL) { retcode = RCS_checkout (rcsfile, (char *) NULL, vers_head, - (char *) NULL, options, tmpfile2); + rev2, options, tmpfile2); if (retcode != 0) { if (!really_quiet) @@ -488,11 +493,6 @@ patch_fileproc (callerdat, finfo) run_arg (tmpfile1); run_arg (tmpfile2); - line1 = NULL; - line1_chars_allocated = 0; - line2 = NULL; - line2_chars_allocated = 0; - switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_REALLY)) { case -1: /* fork/wait failure */ diff --git a/gnu/usr.bin/cvs/src/rcscmds.c b/gnu/usr.bin/cvs/src/rcscmds.c index 7b2c440d5fc..3bfa944c233 100644 --- a/gnu/usr.bin/cvs/src/rcscmds.c +++ b/gnu/usr.bin/cvs/src/rcscmds.c @@ -152,12 +152,10 @@ RCS_merge(path, options, rev1, rev2) #ifndef HAVE_RCS5 if (status == 0) { - /* Run GREP to see if there appear to be conflicts in the file */ - run_setup ("%s", GREP); - run_arg (RCS_MERGE_PAT); - run_arg (path); - status = (run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_NORMAL) == 0); - + error (1, 0, "CVS no longer supports RCS versions older than RCS5"); + /* This case needs to call file_has_markers to see if the file + contains conflict indicators. But is anyone using the !HAVE_RCS5 + code any more? */ } #endif return status; diff --git a/gnu/usr.bin/cvs/src/server.c b/gnu/usr.bin/cvs/src/server.c index 20f973b2058..2490230c79a 100644 --- a/gnu/usr.bin/cvs/src/server.c +++ b/gnu/usr.bin/cvs/src/server.c @@ -1,3 +1,13 @@ +/* This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + #include <assert.h> #include "cvs.h" #include "watch.h" @@ -8,6 +18,10 @@ #ifdef SERVER_SUPPORT +#ifdef HAVE_WINSOCK_H +#include <winsock.h> +#endif + #if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) #include <sys/socket.h> #endif @@ -30,7 +44,17 @@ static Key_schedule sched; #ifdef HAVE_SYS_BSDTYPES_H #include <sys/bsdtypes.h> #endif -#include <sys/time.h> + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif #if HAVE_SYS_SELECT_H #include <sys/select.h> @@ -63,22 +87,20 @@ static Key_schedule sched; #endif /* AUTH_SERVER_SUPPORT */ -/* Functions which the server calls. */ -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)); +#ifdef AUTH_SERVER_SUPPORT + +/* The cvs username sent by the client, which might or might not be + the same as the system username the server eventually switches to + run as. CVS_Username gets set iff password authentication is + successful. */ +static char *CVS_Username = NULL; + +/* Used to check that same repos is transmitted in pserver auth and in + later CVS protocol. Exported because root.c also uses. */ +char *Pserver_Repos = NULL; + +#endif /* AUTH_SERVER_SUPPORT */ + /* While processing requests, this buffer accumulates data to be sent to the client, and then once we are in do_cvs_command, we use it @@ -395,7 +417,9 @@ print_pending_error () /* Is an error pending? */ #define error_pending() (pending_error || pending_error_text) -int +static int supported_response PROTO ((char *)); + +static int supported_response (name) char *name; { @@ -448,6 +472,16 @@ serve_valid_responses (arg) cause deadlock, as noted in server_cleanup. */ buf_flush (buf_to_net, 1); + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } else if (rs->status == rs_optional) @@ -455,8 +489,6 @@ serve_valid_responses (arg) } } -static int use_dir_and_repos = 0; - static void serve_root (arg) char *arg; @@ -656,7 +688,13 @@ static void serve_repository (arg) char *arg; { - dirswitch (arg + 1, arg); + pending_error_text = malloc (80); + if (pending_error_text == NULL) + pending_error = ENOMEM; + else + strcpy (pending_error_text, + "E Repository request is obsolete; aborted"); + return; } static void @@ -666,7 +704,6 @@ serve_directory (arg) int status; char *repos; - use_dir_and_repos = 1; status = buf_read_line (buf_from_net, &repos, (int *) NULL); if (status == 0) { @@ -1016,56 +1053,11 @@ serve_modified (arg) } } -#endif /* SERVER_SUPPORT */ -#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) - -int use_unchanged = 0; - -#endif -#ifdef SERVER_SUPPORT - static void serve_enable_unchanged (arg) char *arg; { - use_unchanged = 1; -} - -static void -serve_lost (arg) - char *arg; -{ - if (use_unchanged) - { - /* A missing file already indicates it is nonexistent. */ - return; - } - else - { - struct utimbuf ut; - int fd = CVS_OPEN (arg, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0 || close (fd) < 0) - { - pending_error = errno; - pending_error_text = malloc (80 + strlen(arg)); - sprintf(pending_error_text, "E cannot open %s", arg); - return; - } - /* - * Set the times to the beginning of the epoch to tell time_stamp() - * that the file was lost. - */ - ut.actime = 0; - ut.modtime = 0; - if (utime (arg, &ut) < 0) - { - pending_error = errno; - pending_error_text = malloc (80 + strlen(arg)); - sprintf(pending_error_text, "E cannot utime %s", arg); - return; - } - } } struct an_entry { @@ -1079,43 +1071,36 @@ static void serve_unchanged (arg) char *arg; { + struct an_entry *p; + char *name; + char *cp; + char *timefield; + if (error_pending ()) return; - if (!use_unchanged) - { - /* A missing file already indicates it is unchanged. */ - return; - } - else - { - struct an_entry *p; - char *name; - char *cp; - char *timefield; - /* Rewrite entries file to have `=' in timestamp field. */ - for (p = entries; p != NULL; p = p->next) + /* Rewrite entries file to have `=' in timestamp field. */ + for (p = entries; p != NULL; p = p->next) + { + name = p->entry + 1; + cp = strchr (name, '/'); + if (cp != NULL + && strlen (arg) == cp - name + && strncmp (arg, name, cp - name) == 0) { - name = p->entry + 1; - cp = strchr (name, '/'); - if (cp != NULL - && strlen (arg) == cp - name - && strncmp (arg, name, cp - name) == 0) + timefield = strchr (cp + 1, '/') + 1; + if (*timefield != '=') { - timefield = strchr (cp + 1, '/') + 1; - if (*timefield != '=') + cp = timefield + strlen (timefield); + cp[1] = '\0'; + while (cp > timefield) { - cp = timefield + strlen (timefield); - cp[1] = '\0'; - while (cp > timefield) - { - *cp = cp[-1]; - --cp; - } - *timefield = '='; + *cp = cp[-1]; + --cp; } - break; + *timefield = '='; } + break; } } } @@ -1338,9 +1323,6 @@ server_notify () { struct notify_note *p; char *repos; - List *list; - Node *node; - int status; while (notify_list != NULL) { @@ -1351,14 +1333,7 @@ server_notify () } repos = Name_Repository (NULL, NULL); - /* Now writelock. */ - list = getlist (); - node = getnode (); - node->type = LOCK; - node->key = xstrdup (repos); - status = addnode (list, node); - assert (status == 0); - Writer_Lock (list); + lock_dir_for_write (repos); fileattr_startdir (repos); @@ -1366,7 +1341,6 @@ server_notify () notify_list->val, notify_list->watches, repos); buf_output0 (buf_to_net, "Notified "); - if (use_dir_and_repos) { char *dir = notify_list->dir + strlen (server_temp_dir) + 1; if (dir[0] == '\0') @@ -1391,9 +1365,7 @@ server_notify () fileattr_write (); fileattr_free (); - /* Remove the writelock. */ Lock_Cleanup (); - dellist (&list); } /* The code used to call fflush (stdout) here, but that is no @@ -1642,8 +1614,7 @@ error ENOMEM Virtual memory exhausted.\n"; /* If this gives an error, not much we could do. syslog() it? */ write (STDOUT_FILENO, msg, sizeof (msg) - 1); - server_cleanup (0); - exit (EXIT_FAILURE); + error_exit (); } static void @@ -1653,6 +1624,164 @@ input_memory_error (buf) outbuf_memory_error (buf); } + + +/* If command is legal, return 1. + * Else if command is illegal and croak_on_illegal is set, then die. + * Else just return 0 to indicate that command is illegal. + */ +static int +check_command_legal_p (char *cmd_name) +{ + /* Right now, only pserver notices illegal commands -- namely, + * write attempts by a read-only user. Therefore, if CVS_Username + * is not set, this just returns 1, because CVS_Username unset + * means pserver is not active. + */ +#ifdef AUTH_SERVER_SUPPORT + if (CVS_Username == NULL) + return 1; + + if (lookup_command_attribute (cmd_name) & CVS_CMD_MODIFIES_REPOSITORY) + { + /* This command has the potential to modify the repository, so + * we check if the user have permission to do that. + * + * (Only relevant for remote users -- local users can do + * whatever normal Unix file permissions allow them to do.) + * + * The decision method: + * + * If $CVSROOT/CVSADMROOT_READERS exists and user is listed + * in it, then read-only access for user. + * + * Or if $CVSROOT/CVSADMROOT_WRITERS exists and user NOT + * listed in it, then also read-only access for user. + * + * Else read-write access for user. + */ + + char *linebuf = NULL; + int num_red = 0; + size_t linebuf_len = 0; + char *fname; + size_t flen; + FILE *fp; + int found_it = 0; + + /* else */ + flen = strlen (CVSroot_directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_READERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_READERS); + + fp = fopen (fname, "r"); + free (fname); + + if (fp == NULL) + goto do_writers; + else /* successfully opened readers file */ + { + while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + /* Hmmm, is it worth importing my own readline + library into CVS? It takes care of chopping + leading and trailing whitespace, "#" comments, and + newlines automatically when so requested. Would + save some code here... -kff */ + + /* Chop newline by hand, for strcmp()'s sake. */ + if (linebuf[num_red - 1] == '\n') + linebuf[num_red - 1] = '\0'; + + if (strcmp (linebuf, CVS_Username) == 0) + { + free (linebuf); + linebuf = NULL; + linebuf_len = 0; + goto handle_illegal; + } + /* else */ + free (linebuf); + linebuf = NULL; + linebuf_len = 0; + } + + /* If not listed specifically as a reader, then this user + has write access by default unless writers are also + specified in a file . */ + fclose (fp); + goto do_writers; + } + + do_writers: + + flen = strlen (CVSroot_directory) + + strlen (CVSROOTADM) + + strlen (CVSROOTADM_WRITERS) + + 3; + + fname = xmalloc (flen); + (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + CVSROOTADM, CVSROOTADM_WRITERS); + + fp = fopen (fname, "r"); + free (fname); + + if (fp == NULL) + { + /* writers file does not exist, so everyone is a writer, + by default */ + return 1; + } + + /* else */ + + found_it = 0; + while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + /* Chop newline by hand, for strcmp()'s sake. */ + if (linebuf[num_red - 1] == '\n') + linebuf[num_red - 1] = '\0'; + + if (strcmp (linebuf, CVS_Username) == 0) + { + free (linebuf); + linebuf = NULL; + linebuf_len = 0; + found_it = 1; + break; + } + /* else */ + free (linebuf); + linebuf = NULL; + linebuf_len = 0; + } + + if (found_it) + { + fclose (fp); + return 1; + } + else /* writers file exists, but this user not listed in it */ + { + handle_illegal: + fclose (fp); + return 0; + } + } +#endif /* AUTH_SERVER_SUPPORT */ + + /* If ever reach end of this function, command must be legal. */ + return 1; +} + + + /* Execute COMMAND in a subprocess with the approriate funky things done. */ static struct fd_set_wrapper { fd_set fds; } command_fds_to_drain; @@ -1663,7 +1792,8 @@ static int flowcontrol_pipe[2]; #endif /* SERVER_FLOWCONTROL */ static void -do_cvs_command (command) +do_cvs_command (cmd_name, command) + char *cmd_name; int (*command) PROTO((int argc, char **argv)); { /* @@ -1699,6 +1829,21 @@ do_cvs_command (command) if (print_pending_error ()) goto free_args_and_return; + /* Global `command_name' is probably "server" right now -- only + serve_export() sets it to anything else. So we will use local + parameter `cmd_name' to determine if this command is legal for + this user. */ + if (!check_command_legal_p (cmd_name)) + { + buf_output0 (buf_to_net, "E "); + buf_output0 (buf_to_net, program_name); + buf_output0 (buf_to_net, " [server aborted]: \""); + buf_output0 (buf_to_net, cmd_name); + buf_output0 (buf_to_net, "\" requires write access to the repository\n\ +error \n"); + goto free_args_and_return; + } + (void) server_notify (); /* @@ -1734,7 +1879,7 @@ do_cvs_command (command) set_nonblock_fd (flowcontrol_pipe[1]); #endif /* SERVER_FLOWCONTROL */ - dev_null_fd = CVS_OPEN ("/dev/null", O_RDONLY); + dev_null_fd = CVS_OPEN (DEVNULL, O_RDONLY); if (dev_null_fd < 0) { print_error (errno); @@ -2266,6 +2411,9 @@ server_pause_check() } #endif /* SERVER_FLOWCONTROL */ +/* This variable commented in server.h. */ +char *server_dir = NULL; + static void output_dir PROTO((char *, char *)); static void @@ -2273,14 +2421,16 @@ output_dir (update_dir, repository) char *update_dir; char *repository; { - if (use_dir_and_repos) + if (server_dir != NULL) { - if (update_dir[0] == '\0') - buf_output0 (protocol, "."); - else - buf_output0 (protocol, update_dir); - buf_output0 (protocol, "/\n"); + buf_output0 (protocol, server_dir); + buf_output0 (protocol, "/"); } + if (update_dir[0] == '\0') + buf_output0 (protocol, "."); + else + buf_output0 (protocol, update_dir); + buf_output0 (protocol, "/\n"); buf_output0 (protocol, repository); buf_output0 (protocol, "/"); } @@ -2426,11 +2576,12 @@ new_entries_line () entries_line = NULL; } + static void serve_ci (arg) char *arg; { - do_cvs_command (commit); + do_cvs_command ("commit", commit); } static void @@ -2525,91 +2676,91 @@ static void serve_update (arg) char *arg; { - do_cvs_command (update); + do_cvs_command ("update", update); } static void serve_diff (arg) char *arg; { - do_cvs_command (diff); + do_cvs_command ("diff", diff); } static void serve_log (arg) char *arg; { - do_cvs_command (cvslog); + do_cvs_command ("cvslog", cvslog); } static void serve_add (arg) char *arg; { - do_cvs_command (add); + do_cvs_command ("add", add); } static void serve_remove (arg) char *arg; { - do_cvs_command (cvsremove); + do_cvs_command ("cvsremove", cvsremove); } static void serve_status (arg) char *arg; { - do_cvs_command (status); + do_cvs_command ("status", status); } static void serve_rdiff (arg) char *arg; { - do_cvs_command (patch); + do_cvs_command ("patch", patch); } static void serve_tag (arg) char *arg; { - do_cvs_command (tag); + do_cvs_command ("cvstag", cvstag); } static void serve_rtag (arg) char *arg; { - do_cvs_command (rtag); + do_cvs_command ("rtag", rtag); } static void serve_import (arg) char *arg; { - do_cvs_command (import); + do_cvs_command ("import", import); } static void serve_admin (arg) char *arg; { - do_cvs_command (admin); + do_cvs_command ("admin", admin); } static void serve_history (arg) char *arg; { - do_cvs_command (history); + do_cvs_command ("history", history); } static void serve_release (arg) char *arg; { - do_cvs_command (release); + do_cvs_command ("release", release); } static void serve_watch_on PROTO ((char *)); @@ -2618,7 +2769,7 @@ static void serve_watch_on (arg) char *arg; { - do_cvs_command (watch_on); + do_cvs_command ("watch_on", watch_on); } static void serve_watch_off PROTO ((char *)); @@ -2627,7 +2778,7 @@ static void serve_watch_off (arg) char *arg; { - do_cvs_command (watch_off); + do_cvs_command ("watch_off", watch_off); } static void serve_watch_add PROTO ((char *)); @@ -2636,7 +2787,7 @@ static void serve_watch_add (arg) char *arg; { - do_cvs_command (watch_add); + do_cvs_command ("watch_add", watch_add); } static void serve_watch_remove PROTO ((char *)); @@ -2645,7 +2796,7 @@ static void serve_watch_remove (arg) char *arg; { - do_cvs_command (watch_remove); + do_cvs_command ("watch_remove", watch_remove); } static void serve_watchers PROTO ((char *)); @@ -2654,7 +2805,7 @@ static void serve_watchers (arg) char *arg; { - do_cvs_command (watchers); + do_cvs_command ("watchers", watchers); } static void serve_editors PROTO ((char *)); @@ -2663,7 +2814,7 @@ static void serve_editors (arg) char *arg; { - do_cvs_command (editors); + do_cvs_command ("editors", editors); } static int noop PROTO ((int, char **)); @@ -2682,7 +2833,7 @@ static void serve_noop (arg) char *arg; { - do_cvs_command (noop); + do_cvs_command ("noop", noop); } static void serve_init PROTO ((char *)); @@ -2693,7 +2844,7 @@ serve_init (arg) { set_local_cvsroot (arg); - do_cvs_command (init); + do_cvs_command ("init", init); } static void serve_annotate PROTO ((char *)); @@ -2702,7 +2853,7 @@ static void serve_annotate (arg) char *arg; { - do_cvs_command (annotate); + do_cvs_command ("annotate", annotate); } static void @@ -2751,7 +2902,15 @@ serve_co (arg) } free (tempdir); } - do_cvs_command (checkout); + + /* Compensate for server_export()'s setting of command_name. + * + * [It probably doesn't matter if do_cvs_command() gets "export" + * or "checkout", but we ought to be accurate where possible.] + */ + do_cvs_command ((strcmp (command_name, "export") == 0) ? + "export" : "checkout", + checkout); } static void @@ -2871,9 +3030,8 @@ server_updated (finfo, vers, updated, file_info, checksum) char *mode_string; /* FIXME: When we check out files the umask of the server - (set in .bashrc if rsh is in use, or set in main.c in - the kerberos case, I think) affects what mode we send, - and it shouldn't. */ + (set in .bashrc if rsh is in use) affects what mode we + send, and it shouldn't. */ if (file_info != NULL) mode_string = mode_to_string (file_info->st_mode); else @@ -3249,11 +3407,6 @@ serve_expand_modules (arg) DBM *db; err = 0; - /* - * FIXME: error handling is bogus; do_module can write to stdout and/or - * stderr and we're not using do_cvs_command. - */ - server_expanding = 1; db = open_module (); for (i = 1; i < argument_count; i++) @@ -3392,7 +3545,7 @@ struct request requests[] = REQ_LINE("Valid-responses", serve_valid_responses, rq_essential), REQ_LINE("valid-requests", serve_valid_requests, rq_essential), REQ_LINE("Repository", serve_repository, rq_essential), - REQ_LINE("Directory", serve_directory, rq_optional), + REQ_LINE("Directory", serve_directory, rq_essential), REQ_LINE("Max-dotdot", serve_max_dotdot, rq_optional), REQ_LINE("Static-directory", serve_static_directory, rq_optional), REQ_LINE("Sticky", serve_sticky, rq_optional), @@ -3400,9 +3553,13 @@ struct request requests[] = REQ_LINE("Update-prog", serve_update_prog, rq_optional), REQ_LINE("Entry", serve_entry, rq_essential), REQ_LINE("Modified", serve_modified, rq_essential), - REQ_LINE("Lost", serve_lost, rq_optional), + + /* The client must send this request to interoperate with CVS 1.5 + through 1.9 servers. The server must support it (although it can + be and is a noop) to interoperate with CVS 1.5 to 1.9 clients. */ REQ_LINE("UseUnchanged", serve_enable_unchanged, rq_enableme), - REQ_LINE("Unchanged", serve_unchanged, rq_optional), + + REQ_LINE("Unchanged", serve_unchanged, rq_essential), REQ_LINE("Notify", serve_notify, rq_optional), REQ_LINE("Questionable", serve_questionable, rq_optional), REQ_LINE("Case", serve_case, rq_optional), @@ -3685,6 +3842,17 @@ server (argc, argv) { printf ("E Fatal server error, aborting.\n\ error ENOMEM Virtual memory exhausted.\n"); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } putenv (env); @@ -3717,6 +3885,8 @@ error ENOMEM Virtual memory exhausted.\n"); } else { + int status; + server_temp_dir = malloc (strlen (Tmpdir) + 80); if (server_temp_dir == NULL) { @@ -3726,6 +3896,18 @@ error ENOMEM Virtual memory exhausted.\n"); */ printf ("E Fatal server error, aborting.\n\ error ENOMEM Virtual memory exhausted.\n"); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket + subsystems on NT and OS2 or dealing with windows + and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } strcpy (server_temp_dir, Tmpdir); @@ -3746,15 +3928,41 @@ error ENOMEM Virtual memory exhausted.\n"); sprintf (p, "%ld", (long) getpid ()); orig_server_temp_dir = server_temp_dir; + + /* Create the temporary directory, and set the mode to + 700, to discourage random people from tampering with + it. */ + status = mkdir_p (server_temp_dir); + if (status == EEXIST) + status = 0; +#ifndef CHMOD_BROKEN + if (status == 0) + status = chmod (server_temp_dir, S_IRWXU); +#endif + if (status != 0) + { + pending_error_text = "E can't create temporary directory"; + pending_error = status; + } } } +#ifdef SIGHUP (void) SIG_register (SIGHUP, server_cleanup); +#endif +#ifdef SIGINT (void) SIG_register (SIGINT, server_cleanup); +#endif +#ifdef SIGQUIT (void) SIG_register (SIGQUIT, server_cleanup); +#endif +#ifdef SIGPIPE (void) SIG_register (SIGPIPE, server_cleanup); +#endif +#ifdef SIGTERM (void) SIG_register (SIGTERM, server_cleanup); - +#endif + /* Now initialize our argument vector (for arguments from the client). */ /* Small for testing. */ @@ -3769,6 +3977,17 @@ error ENOMEM Virtual memory exhausted.\n"); */ printf ("E Fatal server error, aborting.\n\ error ENOMEM Virtual memory exhausted.\n"); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } @@ -3851,6 +4070,16 @@ switch_to_user (username) { printf ("E Fatal error, aborting.\n\ error 0 %s: no such user\n", username); + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } @@ -3872,9 +4101,10 @@ error 0 %s: no such user\n", username); #endif setuid (pw->pw_uid); - /* Inhibit access by randoms. Don't want people randomly - changing our temporary tree before we check things in. */ - umask (077); + /* We don't want our umask to change file modes. The modes should + be set by the modes used in the repository, and by the umask of + the client. */ + umask (0); #if HAVE_PUTENV /* Set LOGNAME and USER in the environment, in case they are @@ -3903,6 +4133,12 @@ extern char *crypt PROTO((const char *, const char *)); * 0 means no entry found for this user. * 1 means entry found and password matches. * 2 means entry found, but password does not match. + * + * If success, host_user_ptr will be set to point at the system + * username (i.e., the "real" identity, which may or may not be the + * CVS username) of this user; caller may free this. Global + * CVS_Username will point at an allocated copy of cvs username (i.e., + * the username argument below). */ static int check_repository_password (username, password, repository, host_user_ptr) @@ -3916,16 +4152,20 @@ check_repository_password (username, password, repository, host_user_ptr) int found_it = 0; int namelen; + /* We don't use CVSroot_directory because it hasn't been set yet + * -- our `repository' argument came from the authentication + * protocol, not the regular CVS protocol. + */ + filename = xmalloc (strlen (repository) + 1 - + strlen ("CVSROOT") + + strlen (CVSROOTADM) + 1 - + strlen ("passwd") + + strlen (CVSROOTADM_PASSWD) + 1); - strcpy (filename, repository); - strcat (filename, "/CVSROOT"); - strcat (filename, "/passwd"); + (void) sprintf (filename, "%s/%s/%s", repository, + CVSROOTADM, CVSROOTADM_PASSWD); fp = CVS_FOPEN (filename, "r"); if (fp == NULL) @@ -3956,16 +4196,27 @@ check_repository_password (username, password, repository, host_user_ptr) /* If found_it != 0, then linebuf contains the information we need. */ if (found_it) { - char *found_password; + char *found_password, *host_user_tmp; strtok (linebuf, ":"); found_password = strtok (NULL, ": \n"); - *host_user_ptr = strtok (NULL, ": \n"); - if (*host_user_ptr == NULL) *host_user_ptr = username; + host_user_tmp = strtok (NULL, ": \n"); + if (host_user_tmp == NULL) + host_user_tmp = username; + if (strcmp (found_password, crypt (password, found_password)) == 0) + { + /* Give host_user_ptr permanent storage. */ + *host_user_ptr = xmalloc (strlen (host_user_tmp) + 1); + strcpy (*host_user_ptr, host_user_tmp); + retval = 1; + } else - retval = 2; + { + *host_user_ptr = NULL; + retval = 2; + } } else { @@ -3985,7 +4236,7 @@ check_password (username, password, repository) char *username, *password, *repository; { int rc; - char *host_user; + char *host_user = NULL; /* First we see if this user has a password in the CVS-specific password file. If so, that's enough to authenticate with. If @@ -3994,10 +4245,16 @@ check_password (username, password, repository) rc = check_repository_password (username, password, repository, &host_user); + if (rc == 2) + return NULL; + + /* else */ + if (rc == 1) - return host_user; - else if (rc == 2) - return 0; + { + /* host_user already set by reference, so just return. */ + goto handle_return; + } else if (rc == 0) { /* No cvs password found, so try /etc/passwd. */ @@ -4025,23 +4282,56 @@ check_password (username, password, repository) { printf ("E Fatal error, aborting.\n\ error 0 %s: no such user\n", username); + + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } if (found_passwd && *found_passwd) - return ((! strcmp (found_passwd, crypt (password, found_passwd))) - ? username : NULL); + { + host_user = ((! strcmp (found_passwd, + crypt (password, found_passwd))) + ? username : NULL); + goto handle_return; + } else if (password && *password) - return username; + { + host_user = username; + goto handle_return; + } else - return NULL; + { + host_user = NULL; + goto handle_return; + } } else { /* Something strange happened. We don't know what it was, but we certainly won't grant authorization. */ - return NULL; + host_user = NULL; + goto handle_return; + } + +handle_return: + if (host_user) + { + /* Set CVS_Username here, in allocated space. + It might or might not be the same as host_user. */ + CVS_Username = xmalloc (strlen (username) + 1); + strcpy (CVS_Username, username); } + + return host_user; } /* Read username and password from client (i.e., stdin). @@ -4152,12 +4442,35 @@ pserver_authenticate_connection () { printf ("I HATE YOU\n"); fflush (stdout); + /* I'm doing this manually rather than via error_exit () + because I'm not sure whether we want to call server_cleanup. + Needs more investigation.... */ + +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (EXIT_FAILURE); } /* Don't go any farther if we're just responding to "cvs login". */ if (verify_and_exit) + { +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif + exit (0); + } + + /* Set Pserver_Repos so that we can check later that the same + repository is sent in later client/server protocol. */ + Pserver_Repos = xmalloc (strlen (repository) + 1); + strcpy (Pserver_Repos, repository); /* Switch to run as this user. */ switch_to_user (host_user); @@ -4188,6 +4501,11 @@ kserver_authenticate_connection () { printf ("E Fatal error, aborting.\n\ error %s getpeername or getsockname failed\n", strerror (errno)); +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif exit (EXIT_FAILURE); } @@ -4209,6 +4527,11 @@ error %s getpeername or getsockname failed\n", strerror (errno)); { printf ("E Fatal error, aborting.\n\ error 0 kerberos: %s\n", krb_get_err_text(status)); +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif exit (EXIT_FAILURE); } @@ -4220,6 +4543,11 @@ error 0 kerberos: %s\n", krb_get_err_text(status)); { printf ("E Fatal error, aborting.\n\ error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status)); +#ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); +#endif exit (EXIT_FAILURE); } @@ -4691,6 +5019,12 @@ cvs_outerr (str, len) size_t to_write = len; const char *p = str; + /* Make sure that output appears in order if stdout and stderr + point to the same place. For the server case this is taken + care of by the fact that saved_outerr always holds less + than a line. */ + fflush (stdout); + while (to_write > 0) { written = fwrite (p, 1, to_write, stderr); @@ -4724,3 +5058,29 @@ cvs_flusherr () #endif fflush (stderr); } + +/* Make it possible for the user to see what has been written to + stdout (it is up to the implementation to decide exactly how far it + should go to ensure this). */ + +void +cvs_flushout () +{ +#ifdef SERVER_SUPPORT + if (error_use_protocol) + { + /* Flush what we can to the network, but don't block. */ + buf_flush (buf_to_net, 0); + } + else if (server_active) + { + /* Just do nothing. This is because the code which + cvs_flushout replaces, setting stdout to line buffering in + main.c, didn't get called in the server child process. But + in the future it is quite plausible that we'll want to make + this case work analogously to cvs_flusherr. */ + } + else +#endif + fflush (stdout); +} diff --git a/gnu/usr.bin/cvs/src/update.c b/gnu/usr.bin/cvs/src/update.c index c1a8d11846f..87d338a0e3f 100644 --- a/gnu/usr.bin/cvs/src/update.c +++ b/gnu/usr.bin/cvs/src/update.c @@ -34,6 +34,7 @@ */ #include "cvs.h" +#include "savecwd.h" #ifdef SERVER_SUPPORT #include "md5.h" #endif @@ -49,7 +50,6 @@ static int patch_file PROTO ((struct file_info *finfo, int *docheckout, struct stat *file_info, unsigned char *checksum)); #endif -static int isemptydir PROTO((char *dir)); static int merge_file PROTO ((struct file_info *finfo, Vers_TS *vers)); static int scratch_file PROTO((struct file_info *finfo)); static Dtype update_dirent_proc PROTO ((void *callerdat, char *dir, @@ -256,7 +256,7 @@ update (argc, argv) if (failed_patches == NULL) { send_file_names (argc, argv, SEND_EXPAND_WILD); - send_files (argc, argv, local, aflag); + send_files (argc, argv, local, aflag, update_build_dirs); } else { @@ -265,7 +265,7 @@ update (argc, argv) (void) printf ("%s client: refetching unpatchable files\n", program_name); - if (toplevel_wd[0] != '\0' + if (toplevel_wd != NULL && CVS_CHDIR (toplevel_wd) < 0) { error (1, errno, "could not chdir to %s", toplevel_wd); @@ -275,7 +275,7 @@ update (argc, argv) (void) unlink_file (failed_patches[i]); send_file_names (failed_patches_count, failed_patches, 0); send_files (failed_patches_count, failed_patches, local, - aflag); + aflag, update_build_dirs); } failed_patches = NULL; @@ -539,22 +539,11 @@ update_fileproc (callerdat, finfo) if (retcode) { - /* - * If the timestamps differ, look for Conflict - * indicators to see if 'C' anyway. - */ - run_setup ("%s", GREP); - run_arg (RCS_MERGE_PAT); - run_arg (finfo->file); - retcode = run_exec (RUN_TTY, DEVNULL, - RUN_TTY,RUN_NORMAL); - if (retcode == -1) - { - error (1, errno, - "fork failed while examining conflict in `%s'", - finfo->fullname); - } + /* The timestamps differ. But if there are conflict + markers print 'C' anyway. */ + retcode = !file_has_markers (finfo); } + if (!retcode) { (void) write_letter (finfo->file, 'C', finfo->update_dir); @@ -840,9 +829,12 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries) if ((cp = strrchr (line, '\n')) != NULL) *cp = '\0'; run_setup ("%s %s", line, repository); - (void) printf ("%s %s: Executing '", program_name, command_name); + cvs_output (program_name, 0); + cvs_output (" ", 1); + cvs_output (command_name, 0); + cvs_output (": Executing '", 0); run_print (stdout); - (void) printf ("'\n"); + cvs_output ("'\n", 0); (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); } (void) fclose (fp); @@ -854,7 +846,7 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries) /* FIXME: chdir ("..") loses with symlinks. */ /* Prune empty dirs on the way out - if necessary */ (void) CVS_CHDIR (".."); - if (update_prune_dirs && isemptydir (dir)) + if (update_prune_dirs && isemptydir (dir, 0)) { /* I'm not sure the existence_error is actually possible (except in cases where we really should print a message), but since @@ -868,30 +860,92 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries) return (err); } -/* - * Returns 1 if the argument directory is completely empty, other than the - * existence of the CVS directory entry. Zero otherwise. - */ +static int isremoved PROTO ((Node *, void *)); + +/* Returns 1 if the file indicated by node has been removed. */ static int -isemptydir (dir) +isremoved (node, closure) + Node *node; + void *closure; +{ + Entnode *entdata = (Entnode*) node->data; + + /* If the first character of the version is a '-', the file has been + removed. */ + return (entdata->version && entdata->version[0] == '-') ? 1 : 0; +} + +/* Returns 1 if the argument directory is completely empty, other than the + existence of the CVS directory entry. Zero otherwise. If MIGHT_NOT_EXIST + and the directory doesn't exist, then just return 0. */ +int +isemptydir (dir, might_not_exist) char *dir; + int might_not_exist; { DIR *dirp; struct dirent *dp; if ((dirp = CVS_OPENDIR (dir)) == NULL) { - error (0, 0, "cannot open directory %s for empty check", dir); + if (might_not_exist && existence_error (errno)) + return 0; + error (0, errno, "cannot open directory %s for empty check", dir); return (0); } + errno = 0; while ((dp = readdir (dirp)) != NULL) { - if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 && - strcmp (dp->d_name, CVSADM) != 0) + if (strcmp (dp->d_name, ".") != 0 + && strcmp (dp->d_name, "..") != 0) { - (void) closedir (dirp); - return (0); + if (strcmp (dp->d_name, CVSADM) != 0) + { + /* An entry other than the CVS directory. The directory + is certainly not empty. */ + (void) closedir (dirp); + return (0); + } + else + { + /* The CVS directory entry. We don't have to worry about + this unless the Entries file indicates that files have + been removed, but not committed, in this directory. + (Removing the directory would prevent people from + comitting the fact that they removed the files!) */ + List *l; + int files_removed; + struct saved_cwd cwd; + + if (save_cwd (&cwd)) + error_exit (); + + if (CVS_CHDIR (dir) < 0) + error (1, errno, "cannot change directory to %s", dir); + l = Entries_Open (0); + files_removed = walklist (l, isremoved, 0); + Entries_Close (l); + + if (restore_cwd (&cwd, NULL)) + error_exit (); + free_cwd (&cwd); + + if (files_removed != 0) + { + /* There are files that have been removed, but not + committed! Do not consider the directory empty. */ + (void) closedir (dirp); + return (0); + } + } } + errno = 0; + } + if (errno != 0) + { + error (0, errno, "cannot read directory %s", dir); + (void) closedir (dirp); + return (0); } (void) closedir (dirp); return (1); @@ -947,12 +1001,17 @@ checkout_file (finfo, vers_ts, adding) { if (!quiet) { - (void) fprintf (stderr, "\ -===================================================================\n"); - (void) fprintf (stderr, "Checking out %s\n", finfo->fullname); - (void) fprintf (stderr, "RCS: %s\n", vers_ts->srcfile->path); - (void) fprintf (stderr, "VERS: %s\n", vers_ts->vn_rcs); - (void) fprintf (stderr, "***************\n"); + cvs_outerr ("\ +===================================================================\n\ +Checking out ", 0); + cvs_outerr (finfo->fullname, 0); + cvs_outerr ("\n\ +RCS: ", 0); + cvs_outerr (vers_ts->srcfile->path, 0); + cvs_outerr ("\n\ +VERS: ", 0); + cvs_outerr (vers_ts->vn_rcs, 0); + cvs_outerr ("\n***************\n", 0); } } |