xref: /openbsd/usr.bin/rcs/rcsclean.c (revision 6541b77c)
1 /*	$OpenBSD: rcsclean.c,v 1.57 2023/08/11 05:02:21 guenther Exp $	*/
2 /*
3  * Copyright (c) 2005 Joris Vink <joris@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 
30 #include <dirent.h>
31 #include <err.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <unistd.h>
36 
37 #include "rcsprog.h"
38 #include "diff.h"
39 
40 static void	rcsclean_file(char *, const char *);
41 
42 static int nflag = 0;
43 static int kflag = RCS_KWEXP_ERR;
44 static int uflag = 0;
45 static int flags = 0;
46 static char *locker = NULL;
47 
48 int
rcsclean_main(int argc,char ** argv)49 rcsclean_main(int argc, char **argv)
50 {
51 	int i, ch;
52 	char *rev_str;
53 	DIR *dirp;
54 	struct dirent *dp;
55 
56 	rev_str = NULL;
57 
58 	while ((ch = rcs_getopt(argc, argv, "k:n::q::r::Tu::Vx::")) != -1) {
59 		switch (ch) {
60 		case 'k':
61 			kflag = rcs_kflag_get(rcs_optarg);
62 			if (RCS_KWEXP_INVAL(kflag)) {
63 				warnx("invalid RCS keyword substitution mode");
64 				(usage)();
65 			}
66 			break;
67 		case 'n':
68 			rcs_setrevstr(&rev_str, rcs_optarg);
69 			nflag = 1;
70 			break;
71 		case 'q':
72 			rcs_setrevstr(&rev_str, rcs_optarg);
73 			flags |= QUIET;
74 			break;
75 		case 'r':
76 			rcs_setrevstr(&rev_str, rcs_optarg);
77 			break;
78 		case 'T':
79 			flags |= PRESERVETIME;
80 			break;
81 		case 'u':
82 			rcs_setrevstr(&rev_str, rcs_optarg);
83 			uflag = 1;
84 			break;
85 		case 'V':
86 			printf("%s\n", rcs_version);
87 			exit(0);
88 		case 'x':
89 			/* Use blank extension if none given. */
90 			rcs_suffixes = rcs_optarg ? rcs_optarg : "";
91 			break;
92 		default:
93 			(usage)();
94 		}
95 	}
96 
97 	argc -= rcs_optind;
98 	argv += rcs_optind;
99 
100 	if ((locker = getlogin()) == NULL)
101 		err(1, "getlogin");
102 
103 	if (argc == 0) {
104 		if ((dirp = opendir(".")) == NULL) {
105 			warn("opendir");
106 			(usage)();
107 		}
108 
109 		while ((dp = readdir(dirp)) != NULL) {
110 			if (dp->d_type == DT_DIR)
111 				continue;
112 			rcsclean_file(dp->d_name, rev_str);
113 		}
114 
115 		(void)closedir(dirp);
116 	} else
117 		for (i = 0; i < argc; i++)
118 			rcsclean_file(argv[i], rev_str);
119 
120 	return (0);
121 }
122 
123 __dead void
rcsclean_usage(void)124 rcsclean_usage(void)
125 {
126 	fprintf(stderr,
127 	    "usage: rcsclean [-TV] [-kmode] [-n[rev]] [-q[rev]] [-r[rev]]\n"
128 	    "                [-u[rev]] [-xsuffixes] [-ztz] [file ...]\n");
129 
130 	exit(1);
131 }
132 
133 static void
rcsclean_file(char * fname,const char * rev_str)134 rcsclean_file(char *fname, const char *rev_str)
135 {
136 	int fd, match;
137 	RCSFILE *file;
138 	char fpath[PATH_MAX], numb[RCS_REV_BUFSZ];
139 	RCSNUM *rev;
140 	BUF *b1, *b2;
141 	struct timespec rcs_mtime = { .tv_sec = 0, .tv_nsec = UTIME_OMIT };
142 
143 	b1 = b2 = NULL;
144 	file = NULL;
145 	rev = NULL;
146 
147 	if ((fd = rcs_choosefile(fname, fpath, sizeof(fpath))) < 0)
148 		goto out;
149 
150 	if ((file = rcs_open(fpath, fd, RCS_RDWR)) == NULL)
151 		goto out;
152 
153 	if (flags & PRESERVETIME)
154 		rcs_mtime = rcs_get_mtime(file);
155 
156 	rcs_kwexp_set(file, kflag);
157 
158 	if (rev_str == NULL)
159 		rev = file->rf_head;
160 	else if ((rev = rcs_getrevnum(rev_str, file)) == NULL) {
161 		warnx("%s: Symbolic name `%s' is undefined.", fpath, rev_str);
162 		goto out;
163 	}
164 
165 	if ((b1 = rcs_getrev(file, rev)) == NULL) {
166 		warnx("failed to get needed revision");
167 		goto out;
168 	}
169 	if ((b2 = buf_load(fname)) == NULL) {
170 		warnx("failed to load `%s'", fname);
171 		goto out;
172 	}
173 
174 	/* If buffer lengths are the same, compare contents as well. */
175 	if (buf_len(b1) != buf_len(b2))
176 		match = 0;
177 	else {
178 		size_t len, n;
179 
180 		len = buf_len(b1);
181 
182 		match = 1;
183 		for (n = 0; n < len; ++n)
184 			if (buf_getc(b1, n) != buf_getc(b2, n)) {
185 				match = 0;
186 				break;
187 			}
188 	}
189 
190 	if (match == 1) {
191 		if (uflag == 1 && !TAILQ_EMPTY(&(file->rf_locks))) {
192 			if (!(flags & QUIET) && nflag == 0) {
193 				printf("rcs -u%s %s\n",
194 				    rcsnum_tostr(rev, numb, sizeof(numb)),
195 				    fpath);
196 			}
197 			(void)rcs_lock_remove(file, locker, rev);
198 		}
199 
200 		if (TAILQ_EMPTY(&(file->rf_locks))) {
201 			if (!(flags & QUIET))
202 				printf("rm -f %s\n", fname);
203 
204 			if (nflag == 0)
205 				(void)unlink(fname);
206 		}
207 	}
208 
209 	rcs_write(file);
210 	if (flags & PRESERVETIME)
211 		rcs_set_mtime(file, rcs_mtime);
212 
213 out:
214 	buf_free(b1);
215 	buf_free(b2);
216 	if (file != NULL)
217 		rcs_close(file);
218 }
219