xref: /openbsd/gnu/usr.bin/cvs/src/remove.c (revision 43c1707e)
11e72d8d2Sderaadt /*
21e72d8d2Sderaadt  * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt  * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt  *
51e72d8d2Sderaadt  * You may distribute under the terms of the GNU General Public License as
62286d8edStholo  * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt  *
81e72d8d2Sderaadt  * Remove a File
91e72d8d2Sderaadt  *
101e72d8d2Sderaadt  * Removes entries from the present version. The entries will be removed from
111e72d8d2Sderaadt  * the RCS repository upon the next "commit".
121e72d8d2Sderaadt  *
131e72d8d2Sderaadt  * "remove" accepts no options, only file names that are to be removed.  The
141e72d8d2Sderaadt  * file must not exist in the current directory for "remove" to work
151e72d8d2Sderaadt  * correctly.
161e72d8d2Sderaadt  */
171e72d8d2Sderaadt 
181e72d8d2Sderaadt #include "cvs.h"
191e72d8d2Sderaadt 
20780d15dfStholo #ifdef CLIENT_SUPPORT
21780d15dfStholo static int remove_force_fileproc PROTO ((void *callerdat,
22780d15dfStholo 					 struct file_info *finfo));
23780d15dfStholo #endif
2450bf276cStholo static int remove_fileproc PROTO ((void *callerdat, struct file_info *finfo));
2550bf276cStholo static Dtype remove_dirproc PROTO ((void *callerdat, char *dir,
2650bf276cStholo 				    char *repos, char *update_dir,
2750bf276cStholo 				    List *entries));
281e72d8d2Sderaadt 
291e72d8d2Sderaadt static int force;
301e72d8d2Sderaadt static int local;
311e72d8d2Sderaadt static int removed_files;
321e72d8d2Sderaadt static int existing_files;
331e72d8d2Sderaadt 
341e72d8d2Sderaadt static const char *const remove_usage[] =
351e72d8d2Sderaadt {
361e72d8d2Sderaadt     "Usage: %s %s [-flR] [files...]\n",
371e72d8d2Sderaadt     "\t-f\tDelete the file before removing it.\n",
381e72d8d2Sderaadt     "\t-l\tProcess this directory only (not recursive).\n",
391e72d8d2Sderaadt     "\t-R\tProcess directories recursively.\n",
402286d8edStholo     "(Specify the --help global option for a list of other help options)\n",
411e72d8d2Sderaadt     NULL
421e72d8d2Sderaadt };
431e72d8d2Sderaadt 
441e72d8d2Sderaadt int
cvsremove(argc,argv)451e72d8d2Sderaadt cvsremove (argc, argv)
461e72d8d2Sderaadt     int argc;
471e72d8d2Sderaadt     char **argv;
481e72d8d2Sderaadt {
491e72d8d2Sderaadt     int c, err;
501e72d8d2Sderaadt 
511e72d8d2Sderaadt     if (argc == -1)
521e72d8d2Sderaadt 	usage (remove_usage);
531e72d8d2Sderaadt 
542770ece5Stholo     optind = 0;
55b6c02222Stholo     while ((c = getopt (argc, argv, "+flR")) != -1)
561e72d8d2Sderaadt     {
571e72d8d2Sderaadt 	switch (c)
581e72d8d2Sderaadt 	{
591e72d8d2Sderaadt 	    case 'f':
601e72d8d2Sderaadt 		force = 1;
611e72d8d2Sderaadt 		break;
621e72d8d2Sderaadt 	    case 'l':
631e72d8d2Sderaadt 		local = 1;
641e72d8d2Sderaadt 		break;
651e72d8d2Sderaadt 	    case 'R':
661e72d8d2Sderaadt 		local = 0;
671e72d8d2Sderaadt 		break;
681e72d8d2Sderaadt 	    case '?':
691e72d8d2Sderaadt 	    default:
701e72d8d2Sderaadt 		usage (remove_usage);
711e72d8d2Sderaadt 		break;
721e72d8d2Sderaadt 	}
731e72d8d2Sderaadt     }
741e72d8d2Sderaadt     argc -= optind;
751e72d8d2Sderaadt     argv += optind;
761e72d8d2Sderaadt 
771e72d8d2Sderaadt     wrap_setup ();
781e72d8d2Sderaadt 
791e72d8d2Sderaadt #ifdef CLIENT_SUPPORT
80*43c1707eStholo     if (current_parsed_root->isremote) {
8150bf276cStholo 	/* Call expand_wild so that the local removal of files will
8250bf276cStholo            work.  It's ok to do it always because we have to send the
8350bf276cStholo            file names expanded anyway.  */
8450bf276cStholo 	expand_wild (argc, argv, &argc, &argv);
8550bf276cStholo 
8650bf276cStholo 	if (force)
8750bf276cStholo 	{
8850bf276cStholo 	    if (!noexec)
8950bf276cStholo 	    {
90780d15dfStholo 		start_recursion (remove_force_fileproc, (FILESDONEPROC) NULL,
91780d15dfStholo 				 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
92780d15dfStholo 				 (void *) NULL, argc, argv, local, W_LOCAL,
93780d15dfStholo 				 0, 0, (char *) NULL, 0);
9450bf276cStholo 	    }
9550bf276cStholo 	    /* else FIXME should probably act as if the file doesn't exist
9650bf276cStholo 	       in doing the following checks.  */
9750bf276cStholo 	}
9850bf276cStholo 
991e72d8d2Sderaadt 	start_server ();
1001e72d8d2Sderaadt 	ign_setup ();
1011e72d8d2Sderaadt 	if (local)
1021e72d8d2Sderaadt 	    send_arg("-l");
103b6c02222Stholo 	/* FIXME: Can't we set SEND_NO_CONTENTS here?  Needs investigation.  */
104b6c02222Stholo 	send_files (argc, argv, local, 0, 0);
105c71bc7e2Stholo 	send_file_names (argc, argv, 0);
106e77048c1Stholo 	free_names (&argc, argv);
10713571821Stholo 	send_to_server ("remove\012", 0);
1081e72d8d2Sderaadt         return get_responses_and_close ();
1091e72d8d2Sderaadt     }
1101e72d8d2Sderaadt #endif
1111e72d8d2Sderaadt 
1121e72d8d2Sderaadt     /* start the recursion processor */
11313571821Stholo     err = start_recursion (remove_fileproc, (FILESDONEPROC) NULL,
11450bf276cStholo                            remove_dirproc, (DIRLEAVEPROC) NULL, NULL,
11550bf276cStholo 			   argc, argv,
11650bf276cStholo                            local, W_LOCAL, 0, 1, (char *) NULL, 1);
1171e72d8d2Sderaadt 
118*43c1707eStholo     if (removed_files && !really_quiet)
1191e72d8d2Sderaadt 	error (0, 0, "use '%s commit' to remove %s permanently", program_name,
1201e72d8d2Sderaadt 	       (removed_files == 1) ? "this file" : "these files");
1211e72d8d2Sderaadt 
1221e72d8d2Sderaadt     if (existing_files)
1231e72d8d2Sderaadt 	error (0, 0,
1241e72d8d2Sderaadt 	       ((existing_files == 1) ?
125c26070a5Stholo 		"%d file exists; remove it first" :
126c26070a5Stholo 		"%d files exist; remove them first"),
127c26070a5Stholo 	       existing_files);
1281e72d8d2Sderaadt 
1291e72d8d2Sderaadt     return (err);
1301e72d8d2Sderaadt }
1311e72d8d2Sderaadt 
132780d15dfStholo #ifdef CLIENT_SUPPORT
133780d15dfStholo 
134780d15dfStholo /*
135780d15dfStholo  * This is called via start_recursion if we are running as the client
136780d15dfStholo  * and the -f option was used.  We just physically remove the file.
137780d15dfStholo  */
138780d15dfStholo 
139780d15dfStholo /*ARGSUSED*/
140780d15dfStholo static int
remove_force_fileproc(callerdat,finfo)141780d15dfStholo remove_force_fileproc (callerdat, finfo)
142780d15dfStholo      void *callerdat;
143780d15dfStholo      struct file_info *finfo;
144780d15dfStholo {
145780d15dfStholo     if (CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno))
146780d15dfStholo 	error (0, errno, "unable to remove %s", finfo->fullname);
147780d15dfStholo     return 0;
148780d15dfStholo }
149780d15dfStholo 
150780d15dfStholo #endif
151780d15dfStholo 
1521e72d8d2Sderaadt /*
1531e72d8d2Sderaadt  * remove the file, only if it has already been physically removed
1541e72d8d2Sderaadt  */
1551e72d8d2Sderaadt /* ARGSUSED */
1561e72d8d2Sderaadt static int
remove_fileproc(callerdat,finfo)15750bf276cStholo remove_fileproc (callerdat, finfo)
15850bf276cStholo     void *callerdat;
159c26070a5Stholo     struct file_info *finfo;
1601e72d8d2Sderaadt {
1611e72d8d2Sderaadt     Vers_TS *vers;
1621e72d8d2Sderaadt 
1631e72d8d2Sderaadt     if (force)
16413571821Stholo     {
16513571821Stholo 	if (!noexec)
16613571821Stholo 	{
16750bf276cStholo 	    if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno))
16813571821Stholo 	    {
169c2c61682Stholo 		error (0, errno, "unable to remove %s", finfo->fullname);
17013571821Stholo 	    }
17113571821Stholo 	}
17213571821Stholo 	/* else FIXME should probably act as if the file doesn't exist
17313571821Stholo 	   in doing the following checks.  */
17413571821Stholo     }
1751e72d8d2Sderaadt 
17650bf276cStholo     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
1771e72d8d2Sderaadt 
1781e72d8d2Sderaadt     if (vers->ts_user != NULL)
1791e72d8d2Sderaadt     {
1801e72d8d2Sderaadt 	existing_files++;
1811e72d8d2Sderaadt 	if (!quiet)
182c2c61682Stholo 	    error (0, 0, "file `%s' still in working directory",
183c2c61682Stholo 		   finfo->fullname);
1841e72d8d2Sderaadt     }
1851e72d8d2Sderaadt     else if (vers->vn_user == NULL)
1861e72d8d2Sderaadt     {
1871e72d8d2Sderaadt 	if (!quiet)
188c2c61682Stholo 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
1891e72d8d2Sderaadt     }
1901e72d8d2Sderaadt     else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
1911e72d8d2Sderaadt     {
192780d15dfStholo 	char *fname;
193780d15dfStholo 
1941e72d8d2Sderaadt 	/*
1951e72d8d2Sderaadt 	 * It's a file that has been added, but not commited yet. So,
19613571821Stholo 	 * remove the ,t file for it and scratch it from the
19713571821Stholo 	 * entries file.  */
198c26070a5Stholo 	Scratch_Entry (finfo->entries, finfo->file);
199780d15dfStholo 	fname = xmalloc (strlen (finfo->file)
200780d15dfStholo 			 + sizeof (CVSADM)
201780d15dfStholo 			 + sizeof (CVSEXT_LOG)
202780d15dfStholo 			 + 10);
203c26070a5Stholo 	(void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
204c71bc7e2Stholo 	if (unlink_file (fname) < 0
205c71bc7e2Stholo 	    && !existence_error (errno))
206c71bc7e2Stholo 	    error (0, errno, "cannot remove %s", CVSEXT_LOG);
2071e72d8d2Sderaadt 	if (!quiet)
208c2c61682Stholo 	    error (0, 0, "removed `%s'", finfo->fullname);
2091e72d8d2Sderaadt 
2101e72d8d2Sderaadt #ifdef SERVER_SUPPORT
2111e72d8d2Sderaadt 	if (server_active)
212c26070a5Stholo 	    server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
2131e72d8d2Sderaadt #endif
214780d15dfStholo 	free (fname);
2151e72d8d2Sderaadt     }
2161e72d8d2Sderaadt     else if (vers->vn_user[0] == '-')
2171e72d8d2Sderaadt     {
2181e72d8d2Sderaadt 	if (!quiet)
219c2c61682Stholo 	    error (0, 0, "file `%s' already scheduled for removal",
220c2c61682Stholo 		   finfo->fullname);
2211e72d8d2Sderaadt     }
222c71bc7e2Stholo     else if (vers->tag != NULL && isdigit ((unsigned char) *vers->tag))
2232286d8edStholo     {
2242286d8edStholo 	/* Commit will just give an error, and so there seems to be
2252286d8edStholo 	   little reason to allow the remove.  I mean, conflicts that
2262286d8edStholo 	   arise out of parallel development are one thing, but conflicts
2272286d8edStholo 	   that arise from sticky tags are quite another.
2282286d8edStholo 
2292286d8edStholo 	   I would have thought that non-branch sticky tags should be the
2302286d8edStholo 	   same but at least now, removing a file with a non-branch sticky
2312286d8edStholo 	   tag means to delete the tag from the file.  I'm not sure that
2322286d8edStholo 	   is a good behavior, but until it is changed, we need to allow
2332286d8edStholo 	   it.  */
2342286d8edStholo 	error (0, 0, "\
2352286d8edStholo cannot remove file `%s' which has a numeric sticky tag of `%s'",
2362286d8edStholo 	       finfo->fullname, vers->tag);
2372286d8edStholo     }
2381e72d8d2Sderaadt     else
2391e72d8d2Sderaadt     {
240780d15dfStholo 	char *fname;
241780d15dfStholo 
2421e72d8d2Sderaadt 	/* Re-register it with a negative version number.  */
243780d15dfStholo 	fname = xmalloc (strlen (vers->vn_user) + 5);
2441e72d8d2Sderaadt 	(void) strcpy (fname, "-");
2451e72d8d2Sderaadt 	(void) strcat (fname, vers->vn_user);
246c26070a5Stholo 	Register (finfo->entries, finfo->file, fname, vers->ts_rcs, vers->options,
2471e72d8d2Sderaadt 		  vers->tag, vers->date, vers->ts_conflict);
2481e72d8d2Sderaadt 	if (!quiet)
249c2c61682Stholo 	    error (0, 0, "scheduling `%s' for removal", finfo->fullname);
2501e72d8d2Sderaadt 	removed_files++;
2511e72d8d2Sderaadt 
2521e72d8d2Sderaadt #ifdef SERVER_SUPPORT
2531e72d8d2Sderaadt 	if (server_active)
254c26070a5Stholo 	    server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
2551e72d8d2Sderaadt #endif
256780d15dfStholo 	free (fname);
2571e72d8d2Sderaadt     }
2581e72d8d2Sderaadt 
2591e72d8d2Sderaadt     freevers_ts (&vers);
2601e72d8d2Sderaadt     return (0);
2611e72d8d2Sderaadt }
2621e72d8d2Sderaadt 
2631e72d8d2Sderaadt /*
2641e72d8d2Sderaadt  * Print a warm fuzzy message
2651e72d8d2Sderaadt  */
2661e72d8d2Sderaadt /* ARGSUSED */
2671e72d8d2Sderaadt static Dtype
remove_dirproc(callerdat,dir,repos,update_dir,entries)26850bf276cStholo remove_dirproc (callerdat, dir, repos, update_dir, entries)
26950bf276cStholo     void *callerdat;
2701e72d8d2Sderaadt     char *dir;
2711e72d8d2Sderaadt     char *repos;
2721e72d8d2Sderaadt     char *update_dir;
27350bf276cStholo     List *entries;
2741e72d8d2Sderaadt {
2751e72d8d2Sderaadt     if (!quiet)
2761e72d8d2Sderaadt 	error (0, 0, "Removing %s", update_dir);
2771e72d8d2Sderaadt     return (R_PROCESS);
2781e72d8d2Sderaadt }
279