1 /* Clean up working files. */
2
3 /* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert
4 Distributed under license by the Free Software Foundation, Inc.
5
6 This file is part of RCS.
7
8 RCS is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
12
13 RCS is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with RCS; see the file COPYING.
20 If not, write to the Free Software Foundation,
21 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 Report problems and direct all questions to:
24
25 rcs-bugs@cs.purdue.edu
26
27 */
28 /*
29 * $FreeBSD: src/gnu/usr.bin/rcs/rcsclean/rcsclean.c,v 1.5 1999/08/27 23:36:54 peter Exp $
30 * $DragonFly: src/gnu/usr.bin/rcs/rcsclean/rcsclean.c,v 1.2 2003/06/17 04:25:48 dillon Exp $
31 */
32
33 #include "rcsbase.h"
34
35 #if has_dirent
36 static int get_directory P((char const*,char***));
37 #endif
38
39 static int unlock P((struct hshentry *));
40 static void cleanup P((void));
41
42 static RILE *workptr;
43 static int exitstatus;
44
45 mainProg(rcscleanId, "rcsclean", "$DragonFly: src/gnu/usr.bin/rcs/rcsclean/rcsclean.c,v 1.2 2003/06/17 04:25:48 dillon Exp $")
46 {
47 static char const usage[] =
48 "\nrcsclean: usage: rcsclean -ksubst -{nqru}[rev] -T -Vn -xsuff -zzone file ...";
49
50 static struct buf revision;
51
52 char *a, **newargv;
53 char const *rev, *p;
54 int dounlock, expmode, perform, unlocked, unlockflag, waslocked;
55 int Ttimeflag;
56 struct hshentries *deltas;
57 struct hshentry *delta;
58 struct stat workstat;
59
60 setrid();
61
62 expmode = -1;
63 rev = 0;
64 suffixes = X_DEFAULT;
65 perform = true;
66 unlockflag = false;
67 Ttimeflag = false;
68
69 argc = getRCSINIT(argc, argv, &newargv);
70 argv = newargv;
71 for (;;) {
72 if (--argc < 1) {
73 # if has_dirent
74 argc = get_directory(".", &newargv);
75 argv = newargv;
76 break;
77 # else
78 faterror("no pathnames specified");
79 # endif
80 }
81 a = *++argv;
82 if (!*a || *a++ != '-')
83 break;
84 switch (*a++) {
85 case 'k':
86 if (0 <= expmode)
87 redefined('k');
88 if ((expmode = str2expmode(a)) < 0)
89 goto unknown;
90 break;
91
92 case 'n':
93 perform = false;
94 goto handle_revision;
95
96 case 'q':
97 quietflag = true;
98 /* fall into */
99 case 'r':
100 handle_revision:
101 if (*a) {
102 if (rev)
103 warn("redefinition of revision number");
104 rev = a;
105 }
106 break;
107
108 case 'T':
109 if (*a)
110 goto unknown;
111 Ttimeflag = true;
112 break;
113
114 case 'u':
115 unlockflag = true;
116 goto handle_revision;
117
118 case 'V':
119 setRCSversion(*argv);
120 break;
121
122 case 'x':
123 suffixes = a;
124 break;
125
126 case 'z':
127 zone_set(a);
128 break;
129
130 default:
131 unknown:
132 error("unknown option: %s%s", *argv, usage);
133 }
134 }
135
136 dounlock = perform & unlockflag;
137
138 if (nerror)
139 cleanup();
140 else
141 for (; 0 < argc; cleanup(), ++argv, --argc) {
142
143 ffree();
144
145 if (!(
146 0 < pairnames(
147 argc, argv,
148 dounlock ? rcswriteopen : rcsreadopen,
149 true, true
150 ) &&
151 (workptr = Iopen(workname, FOPEN_R_WORK, &workstat))
152 ))
153 continue;
154
155 if (same_file(RCSstat, workstat, 0)) {
156 rcserror("RCS file is the same as working file %s.",
157 workname
158 );
159 continue;
160 }
161
162 gettree();
163
164 p = 0;
165 if (rev) {
166 if (!fexpandsym(rev, &revision, workptr))
167 continue;
168 p = revision.string;
169 } else if (Head)
170 switch (unlockflag ? findlock(false,&delta) : 0) {
171 default:
172 continue;
173 case 0:
174 p = Dbranch ? Dbranch : "";
175 break;
176 case 1:
177 p = delta->num;
178 break;
179 }
180 delta = 0;
181 deltas = 0; /* Keep lint happy. */
182 if (p && !(delta = genrevs(p,(char*)0,(char*)0,(char*)0,&deltas)))
183 continue;
184
185 waslocked = delta && delta->lockedby;
186 locker_expansion = unlock(delta);
187 unlocked = locker_expansion & unlockflag;
188 if (unlocked<waslocked && workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH))
189 continue;
190
191 if (unlocked && !checkaccesslist())
192 continue;
193
194 if (dorewrite(dounlock, unlocked) != 0)
195 continue;
196
197 if (0 <= expmode)
198 Expand = expmode;
199 else if (
200 waslocked &&
201 Expand == KEYVAL_EXPAND &&
202 WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
203 )
204 Expand = KEYVALLOCK_EXPAND;
205
206 getdesc(false);
207
208 if (
209 !delta ? workstat.st_size!=0 :
210 0 < rcsfcmp(
211 workptr, &workstat,
212 buildrevision(deltas, delta, (FILE*)0, false),
213 delta
214 )
215 )
216 continue;
217
218 if (quietflag < unlocked)
219 aprintf(stdout, "rcs -u%s %s\n", delta->num, RCSname);
220
221 if (perform & unlocked) {
222 if_advise_access(deltas->first != delta, finptr, MADV_SEQUENTIAL);
223 if (donerewrite(true,
224 Ttimeflag ? RCSstat.st_mtime : (time_t)-1
225 ) != 0)
226 continue;
227 }
228
229 if (!quietflag)
230 aprintf(stdout, "rm -f %s\n", workname);
231 Izclose(&workptr);
232 if (perform && un_link(workname) != 0)
233 eerror(workname);
234
235 }
236
237 tempunlink();
238 if (!quietflag)
239 Ofclose(stdout);
240 exitmain(exitstatus);
241 }
242
243 static void
cleanup()244 cleanup()
245 {
246 if (nerror) exitstatus = EXIT_FAILURE;
247 Izclose(&finptr);
248 Izclose(&workptr);
249 Ozclose(&fcopy);
250 ORCSclose();
251 dirtempunlink();
252 }
253
254 #if RCS_lint
255 # define exiterr rcscleanExit
256 #endif
257 void
exiterr()258 exiterr()
259 {
260 ORCSerror();
261 dirtempunlink();
262 tempunlink();
263 _exit(EXIT_FAILURE);
264 }
265
266 static int
unlock(delta)267 unlock(delta)
268 struct hshentry *delta;
269 {
270 register struct rcslock **al, *l;
271
272 if (delta && delta->lockedby && strcmp(getcaller(),delta->lockedby)==0)
273 for (al = &Locks; (l = *al); al = &l->nextlock)
274 if (l->delta == delta) {
275 *al = l->nextlock;
276 delta->lockedby = 0;
277 return true;
278 }
279 return false;
280 }
281
282 #if has_dirent
283 static int
get_directory(dirname,aargv)284 get_directory(dirname, aargv)
285 char const *dirname;
286 char ***aargv;
287 /*
288 * Put a vector of all DIRNAME's directory entries names into *AARGV.
289 * Ignore names of RCS files.
290 * Yield the number of entries found. Terminate the vector with 0.
291 * Allocate the storage for the vector and entry names.
292 * Do not sort the names. Do not include '.' and '..'.
293 */
294 {
295 int i, entries = 0, entries_max = 64;
296 size_t chars = 0, chars_max = 1024;
297 size_t *offset = tnalloc(size_t, entries_max);
298 char *a = tnalloc(char, chars_max), **p;
299 DIR *d;
300 struct dirent *e;
301
302 if (!(d = opendir(dirname)))
303 efaterror(dirname);
304 while ((errno = 0, e = readdir(d))) {
305 char const *en = e->d_name;
306 size_t s = strlen(en) + 1;
307 if (en[0]=='.' && (!en[1] || (en[1]=='.' && !en[2])))
308 continue;
309 if (rcssuffix(en))
310 continue;
311 while (chars_max < s + chars)
312 a = trealloc(char, a, chars_max<<=1);
313 if (entries == entries_max)
314 offset = trealloc(size_t, offset, entries_max<<=1);
315 offset[entries++] = chars;
316 VOID strcpy(a+chars, en);
317 chars += s;
318 }
319 # if void_closedir
320 # define close_directory(d) (closedir(d), 0)
321 # else
322 # define close_directory(d) closedir(d)
323 # endif
324 if (errno || close_directory(d) != 0)
325 efaterror(dirname);
326 if (chars)
327 a = trealloc(char, a, chars);
328 else
329 tfree(a);
330 *aargv = p = tnalloc(char*, entries+1);
331 for (i=0; i<entries; i++)
332 *p++ = a + offset[i];
333 *p = 0;
334 tfree(offset);
335 return entries;
336 }
337 #endif
338