1235783Skib/* Clean up working files.  */
2235783Skib
3235783Skib/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert
4235783Skib   Distributed under license by the Free Software Foundation, Inc.
5235783Skib
6235783SkibThis file is part of RCS.
7235783Skib
8235783SkibRCS is free software; you can redistribute it and/or modify
9235783Skibit under the terms of the GNU General Public License as published by
10235783Skibthe Free Software Foundation; either version 2, or (at your option)
11235783Skibany later version.
12235783Skib
13235783SkibRCS is distributed in the hope that it will be useful,
14235783Skibbut WITHOUT ANY WARRANTY; without even the implied warranty of
15235783SkibMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16235783SkibGNU General Public License for more details.
17235783Skib
18235783SkibYou should have received a copy of the GNU General Public License
19235783Skibalong with RCS; see the file COPYING.
20235783SkibIf not, write to the Free Software Foundation,
21235783Skib59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22235783Skib
23235783SkibReport problems and direct all questions to:
24235783Skib
25235783Skib	rcs-bugs@cs.purdue.edu
26235783Skib
27235783Skib*/
28235783Skib
29235783Skib#include "rcsbase.h"
30235783Skib
31235783Skib#if has_dirent
32235783Skib	static int get_directory P((char const*,char***));
33235783Skib#endif
34235783Skib
35235783Skibstatic int unlock P((struct hshentry *));
36235783Skibstatic void cleanup P((void));
37235783Skib
38235783Skibstatic RILE *workptr;
39235783Skibstatic int exitstatus;
40235783Skib
41235783SkibmainProg(rcscleanId, "rcsclean", "$FreeBSD$")
42235783Skib{
43235783Skib	static char const usage[] =
44235783Skib		"\nrcsclean: usage: rcsclean -ksubst -{nqru}[rev] -T -Vn -xsuff -zzone file ...";
45235783Skib
46235783Skib	static struct buf revision;
47235783Skib
48235783Skib	char *a, **newargv;
49235783Skib	char const *rev, *p;
50235783Skib	int dounlock, expmode, perform, unlocked, unlockflag, waslocked;
51235783Skib	int Ttimeflag;
52235783Skib	struct hshentries *deltas;
53235783Skib	struct hshentry *delta;
54235783Skib	struct stat workstat;
55235783Skib
56235783Skib	setrid();
57282199Sdumbbell
58235783Skib	expmode = -1;
59235783Skib	rev = 0;
60235783Skib	suffixes = X_DEFAULT;
61235783Skib	perform = true;
62235783Skib	unlockflag = false;
63235783Skib	Ttimeflag = false;
64235783Skib
65282199Sdumbbell	argc = getRCSINIT(argc, argv, &newargv);
66282199Sdumbbell	argv = newargv;
67282199Sdumbbell	for (;;) {
68282199Sdumbbell		if (--argc < 1) {
69235783Skib#			if has_dirent
70235783Skib				argc = get_directory(".", &newargv);
71235783Skib				argv = newargv;
72235783Skib				break;
73235783Skib#			else
74235783Skib				faterror("no pathnames specified");
75282199Sdumbbell#			endif
76282199Sdumbbell		}
77282199Sdumbbell		a = *++argv;
78282199Sdumbbell		if (!*a  ||  *a++ != '-')
79282199Sdumbbell			break;
80282199Sdumbbell		switch (*a++) {
81282199Sdumbbell			case 'k':
82235783Skib				if (0 <= expmode)
83282199Sdumbbell					redefined('k');
84282199Sdumbbell				if ((expmode = str2expmode(a))  <  0)
85235783Skib					goto unknown;
86282199Sdumbbell				break;
87235783Skib
88282199Sdumbbell			case 'n':
89235783Skib				perform = false;
90282199Sdumbbell				goto handle_revision;
91282199Sdumbbell
92235783Skib			case 'q':
93235783Skib				quietflag = true;
94235783Skib				/* fall into */
95235783Skib			case 'r':
96235783Skib			handle_revision:
97282199Sdumbbell				if (*a) {
98235783Skib					if (rev)
99235783Skib						warn("redefinition of revision number");
100235783Skib					rev = a;
101235783Skib				}
102235783Skib				break;
103235783Skib
104235783Skib			case 'T':
105235783Skib				if (*a)
106282199Sdumbbell					goto unknown;
107282199Sdumbbell				Ttimeflag = true;
108235783Skib				break;
109235783Skib
110235783Skib			case 'u':
111235783Skib				unlockflag = true;
112235783Skib				goto handle_revision;
113235783Skib
114235783Skib			case 'V':
115235783Skib				setRCSversion(*argv);
116235783Skib				break;
117235783Skib
118235783Skib			case 'x':
119235783Skib				suffixes = a;
120282199Sdumbbell				break;
121235783Skib
122282199Sdumbbell			case 'z':
123235783Skib				zone_set(a);
124282199Sdumbbell				break;
125282199Sdumbbell
126282199Sdumbbell			default:
127282199Sdumbbell			unknown:
128282199Sdumbbell				error("unknown option: %s%s", *argv, usage);
129282199Sdumbbell		}
130282199Sdumbbell	}
131235783Skib
132235783Skib	dounlock = perform & unlockflag;
133235783Skib
134235783Skib	if (nerror)
135235783Skib	  cleanup();
136235783Skib	else
137235783Skib	  for (;  0 < argc;  cleanup(), ++argv, --argc) {
138254880Sdumbbell
139235783Skib		ffree();
140235783Skib
141282199Sdumbbell		if (!(
142235783Skib			0 < pairnames(
143282199Sdumbbell				argc, argv,
144235783Skib				dounlock ? rcswriteopen : rcsreadopen,
145235783Skib				true, true
146235783Skib			) &&
147235783Skib			(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))
148235783Skib		))
149235783Skib			continue;
150282199Sdumbbell
151282199Sdumbbell		if (same_file(RCSstat, workstat, 0)) {
152282199Sdumbbell			rcserror("RCS file is the same as working file %s.",
153282199Sdumbbell				workname
154235783Skib			);
155235783Skib			continue;
156235783Skib		}
157235783Skib
158282199Sdumbbell		gettree();
159235783Skib
160282199Sdumbbell		p = 0;
161282199Sdumbbell		if (rev) {
162235783Skib			if (!fexpandsym(rev, &revision, workptr))
163235783Skib				continue;
164235783Skib			p = revision.string;
165235783Skib		} else if (Head)
166282199Sdumbbell			switch (unlockflag ? findlock(false,&delta) : 0) {
167235783Skib				default:
168282199Sdumbbell					continue;
169235783Skib				case 0:
170282199Sdumbbell					p = Dbranch ? Dbranch : "";
171282199Sdumbbell					break;
172282199Sdumbbell				case 1:
173235783Skib					p = delta->num;
174282199Sdumbbell					break;
175282199Sdumbbell			}
176282199Sdumbbell		delta = 0;
177282199Sdumbbell		deltas = 0;  /* Keep lint happy.  */
178282199Sdumbbell		if (p  &&  !(delta = genrevs(p,(char*)0,(char*)0,(char*)0,&deltas)))
179282199Sdumbbell			continue;
180282199Sdumbbell
181282199Sdumbbell		waslocked = delta && delta->lockedby;
182282199Sdumbbell		locker_expansion = unlock(delta);
183282199Sdumbbell		unlocked = locker_expansion & unlockflag;
184282199Sdumbbell		if (unlocked<waslocked  &&  workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH))
185282199Sdumbbell			continue;
186282199Sdumbbell
187282199Sdumbbell		if (unlocked && !checkaccesslist())
188282199Sdumbbell			continue;
189282199Sdumbbell
190282199Sdumbbell		if (dorewrite(dounlock, unlocked) != 0)
191235783Skib			continue;
192282199Sdumbbell
193235783Skib		if (0 <= expmode)
194282199Sdumbbell			Expand = expmode;
195282199Sdumbbell		else if (
196282199Sdumbbell			waslocked  &&
197282199Sdumbbell			Expand == KEYVAL_EXPAND  &&
198235783Skib			WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
199235783Skib		)
200282199Sdumbbell			Expand = KEYVALLOCK_EXPAND;
201282199Sdumbbell
202282199Sdumbbell		getdesc(false);
203235783Skib
204282199Sdumbbell		if (
205282199Sdumbbell			!delta ? workstat.st_size!=0 :
206282199Sdumbbell			0 < rcsfcmp(
207282199Sdumbbell				workptr, &workstat,
208282199Sdumbbell				buildrevision(deltas, delta, (FILE*)0, false),
209235783Skib				delta
210282199Sdumbbell			)
211235783Skib		)
212282199Sdumbbell			continue;
213282199Sdumbbell
214282199Sdumbbell		if (quietflag < unlocked)
215282199Sdumbbell			aprintf(stdout, "rcs -u%s %s\n", delta->num, RCSname);
216282199Sdumbbell
217282199Sdumbbell		if (perform & unlocked) {
218282199Sdumbbell			if_advise_access(deltas->first != delta, finptr, MADV_SEQUENTIAL);
219282199Sdumbbell			if (donerewrite(true,
220282199Sdumbbell				Ttimeflag ? RCSstat.st_mtime : (time_t)-1
221235783Skib			) != 0)
222282199Sdumbbell				continue;
223282199Sdumbbell		}
224235783Skib
225282199Sdumbbell		if (!quietflag)
226282199Sdumbbell			aprintf(stdout, "rm -f %s\n", workname);
227282199Sdumbbell		Izclose(&workptr);
228282199Sdumbbell		if (perform  &&  un_link(workname) != 0)
229282199Sdumbbell			eerror(workname);
230282199Sdumbbell
231282199Sdumbbell	  }
232282199Sdumbbell
233282199Sdumbbell	tempunlink();
234282199Sdumbbell	if (!quietflag)
235282199Sdumbbell		Ofclose(stdout);
236282199Sdumbbell	exitmain(exitstatus);
237282199Sdumbbell}
238282199Sdumbbell
239282199Sdumbbell	static void
240282199Sdumbbellcleanup()
241235783Skib{
242282199Sdumbbell	if (nerror) exitstatus = EXIT_FAILURE;
243235783Skib	Izclose(&finptr);
244235783Skib	Izclose(&workptr);
245282199Sdumbbell	Ozclose(&fcopy);
246235783Skib	ORCSclose();
247282199Sdumbbell	dirtempunlink();
248282199Sdumbbell}
249282199Sdumbbell
250235783Skib#if RCS_lint
251282199Sdumbbell#	define exiterr rcscleanExit
252235783Skib#endif
253282199Sdumbbell	void
254282199Sdumbbellexiterr()
255282199Sdumbbell{
256282199Sdumbbell	ORCSerror();
257235783Skib	dirtempunlink();
258282199Sdumbbell	tempunlink();
259235783Skib	_exit(EXIT_FAILURE);
260282199Sdumbbell}
261282199Sdumbbell
262235783Skib	static int
263282199Sdumbbellunlock(delta)
264282199Sdumbbell	struct hshentry *delta;
265282199Sdumbbell{
266235783Skib	register struct rcslock **al, *l;
267282199Sdumbbell
268282199Sdumbbell	if (delta && delta->lockedby && strcmp(getcaller(),delta->lockedby)==0)
269235783Skib		for (al = &Locks;  (l = *al);  al = &l->nextlock)
270282199Sdumbbell			if (l->delta == delta) {
271282199Sdumbbell				*al = l->nextlock;
272282199Sdumbbell				delta->lockedby = 0;
273282199Sdumbbell				return true;
274282199Sdumbbell			}
275282199Sdumbbell	return false;
276235783Skib}
277282199Sdumbbell
278235783Skib#if has_dirent
279282199Sdumbbell	static int
280235783Skibget_directory(dirname, aargv)
281282199Sdumbbell	char const *dirname;
282235783Skib	char ***aargv;
283282199Sdumbbell/*
284282199Sdumbbell * Put a vector of all DIRNAME's directory entries names into *AARGV.
285282199Sdumbbell * Ignore names of RCS files.
286282199Sdumbbell * Yield the number of entries found.  Terminate the vector with 0.
287235783Skib * Allocate the storage for the vector and entry names.
288282199Sdumbbell * Do not sort the names.  Do not include '.' and '..'.
289235783Skib */
290282199Sdumbbell{
291282199Sdumbbell	int i, entries = 0, entries_max = 64;
292235783Skib	size_t chars = 0, chars_max = 1024;
293282199Sdumbbell	size_t *offset = tnalloc(size_t, entries_max);
294235783Skib	char *a = tnalloc(char, chars_max), **p;
295282199Sdumbbell	DIR *d;
296235783Skib	struct dirent *e;
297235783Skib
298282199Sdumbbell	if (!(d = opendir(dirname)))
299282199Sdumbbell		efaterror(dirname);
300235783Skib	while ((errno = 0,  e = readdir(d))) {
301282199Sdumbbell		char const *en = e->d_name;
302254836Sdumbbell		size_t s = strlen(en) + 1;
303235783Skib		if (en[0]=='.'   &&   (!en[1]  ||  (en[1]=='.' && !en[2])))
304282199Sdumbbell			continue;
305282199Sdumbbell		if (rcssuffix(en))
306254836Sdumbbell			continue;
307282199Sdumbbell		while (chars_max < s + chars)
308254836Sdumbbell			a = trealloc(char, a, chars_max<<=1);
309282199Sdumbbell		if (entries == entries_max)
310235783Skib			offset = trealloc(size_t, offset, entries_max<<=1);
311235783Skib		offset[entries++] = chars;
312235783Skib		VOID strcpy(a+chars, en);
313282199Sdumbbell		chars += s;
314282199Sdumbbell	}
315235783Skib#	if void_closedir
316282199Sdumbbell#		define close_directory(d) (closedir(d), 0)
317235783Skib#	else
318282199Sdumbbell#		define close_directory(d) closedir(d)
319235783Skib#	endif
320282199Sdumbbell	if (errno  ||  close_directory(d) != 0)
321282199Sdumbbell		efaterror(dirname);
322282199Sdumbbell	if (chars)
323282199Sdumbbell		a = trealloc(char, a, chars);
324235783Skib	else
325282199Sdumbbell		tfree(a);
326254836Sdumbbell	*aargv = p = tnalloc(char*, entries+1);
327282199Sdumbbell	for (i=0; i<entries; i++)
328282199Sdumbbell		*p++ = a + offset[i];
329282199Sdumbbell	*p = 0;
330282199Sdumbbell	tfree(offset);
331282199Sdumbbell	return entries;
332282199Sdumbbell}
333282199Sdumbbell#endif
334282199Sdumbbell