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