xref: /openbsd/gnu/usr.bin/cvs/src/find_names.c (revision 43c1707e)
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  *
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS source distribution.
7  *
8  * Find Names
9  *
10  * Finds all the pertinent file names, both from the administration and from the
11  * repository
12  *
13  * Find Dirs
14  *
15  * Finds all pertinent sub-directories of the checked out instantiation and the
16  * repository (and optionally the attic)
17  */
18 
19 #include "cvs.h"
20 
21 static int find_dirs PROTO((char *dir, List * list, int checkadm,
22 			    List *entries));
23 static int find_rcs PROTO((char *dir, List * list));
24 static int add_subdir_proc PROTO((Node *, void *));
25 static int register_subdir_proc PROTO((Node *, void *));
26 
27 static List *filelist;
28 
29 /*
30  * add the key from entry on entries list to the files list
31  */
32 static int add_entries_proc PROTO((Node *, void *));
33 static int
add_entries_proc(node,closure)34 add_entries_proc (node, closure)
35      Node *node;
36      void *closure;
37 {
38     Entnode *entnode;
39     Node *fnode;
40 
41     entnode = (Entnode *) node->data;
42     if (entnode->type != ENT_FILE)
43 	return (0);
44 
45     fnode = getnode ();
46     fnode->type = FILES;
47     fnode->key = xstrdup (node->key);
48     if (addnode (filelist, fnode) != 0)
49 	freenode (fnode);
50     return (0);
51 }
52 
53 /* Find files in the repository and/or working directory.  On error,
54    may either print a nonfatal error and return NULL, or just give
55    a fatal error.  On success, return non-NULL (even if it is an empty
56    list).  */
57 
58 List *
Find_Names(repository,which,aflag,optentries)59 Find_Names (repository, which, aflag, optentries)
60     char *repository;
61     int which;
62     int aflag;
63     List **optentries;
64 {
65     List *entries;
66     List *files;
67 
68     /* make a list for the files */
69     files = filelist = getlist ();
70 
71     /* look at entries (if necessary) */
72     if (which & W_LOCAL)
73     {
74 	/* parse the entries file (if it exists) */
75 	entries = Entries_Open (aflag, NULL);
76 	if (entries != NULL)
77 	{
78 	    /* walk the entries file adding elements to the files list */
79 	    (void) walklist (entries, add_entries_proc, NULL);
80 
81 	    /* if our caller wanted the entries list, return it; else free it */
82 	    if (optentries != NULL)
83 		*optentries = entries;
84 	    else
85 		Entries_Close (entries);
86 	}
87     }
88 
89     if ((which & W_REPOS) && repository && !isreadable (CVSADM_ENTSTAT))
90     {
91 	/* search the repository */
92 	if (find_rcs (repository, files) != 0)
93 	{
94 	    error (0, errno, "cannot open directory %s", repository);
95 	    goto error_exit;
96 	}
97 
98 	/* search the attic too */
99 	if (which & W_ATTIC)
100 	{
101 	    char *dir;
102 	    dir = xmalloc (strlen (repository) + sizeof (CVSATTIC) + 10);
103 	    (void) sprintf (dir, "%s/%s", repository, CVSATTIC);
104 	    if (find_rcs (dir, files) != 0
105 		&& !existence_error (errno))
106 		/* For now keep this a fatal error, seems less useful
107 		   for access control than the case above.  */
108 		error (1, errno, "cannot open directory %s", dir);
109 	    free (dir);
110 	}
111     }
112 
113     /* sort the list into alphabetical order and return it */
114     sortlist (files, fsortcmp);
115     return (files);
116  error_exit:
117     dellist (&files);
118     return NULL;
119 }
120 
121 /*
122  * Add an entry from the subdirs list to the directories list.  This
123  * is called via walklist.
124  */
125 
126 static int
add_subdir_proc(p,closure)127 add_subdir_proc (p, closure)
128      Node *p;
129      void *closure;
130 {
131     List *dirlist = (List *) closure;
132     Entnode *entnode;
133     Node *dnode;
134 
135     entnode = (Entnode *) p->data;
136     if (entnode->type != ENT_SUBDIR)
137 	return 0;
138 
139     dnode = getnode ();
140     dnode->type = DIRS;
141     dnode->key = xstrdup (entnode->user);
142     if (addnode (dirlist, dnode) != 0)
143 	freenode (dnode);
144     return 0;
145 }
146 
147 /*
148  * Register a subdirectory.  This is called via walklist.
149  */
150 
151 /*ARGSUSED*/
152 static int
register_subdir_proc(p,closure)153 register_subdir_proc (p, closure)
154      Node *p;
155      void *closure;
156 {
157     List *entries = (List *) closure;
158 
159     Subdir_Register (entries, (char *) NULL, p->key);
160     return 0;
161 }
162 
163 /*
164  * create a list of directories to traverse from the current directory
165  */
166 List *
Find_Directories(repository,which,entries)167 Find_Directories (repository, which, entries)
168     char *repository;
169     int which;
170     List *entries;
171 {
172     List *dirlist;
173 
174     /* make a list for the directories */
175     dirlist = getlist ();
176 
177     /* find the local ones */
178     if (which & W_LOCAL)
179     {
180 	List *tmpentries;
181 	struct stickydirtag *sdtp;
182 
183 	/* Look through the Entries file.  */
184 
185 	if (entries != NULL)
186 	    tmpentries = entries;
187 	else if (isfile (CVSADM_ENT))
188 	    tmpentries = Entries_Open (0, NULL);
189 	else
190 	    tmpentries = NULL;
191 
192 	if (tmpentries != NULL)
193 	    sdtp = (struct stickydirtag *) tmpentries->list->data;
194 
195 	/* If we do have an entries list, then if sdtp is NULL, or if
196            sdtp->subdirs is nonzero, all subdirectory information is
197            recorded in the entries list.  */
198 	if (tmpentries != NULL && (sdtp == NULL || sdtp->subdirs))
199 	    walklist (tmpentries, add_subdir_proc, (void *) dirlist);
200 	else
201 	{
202 	    /* This is an old working directory, in which subdirectory
203                information is not recorded in the Entries file.  Find
204                the subdirectories the hard way, and, if possible, add
205                it to the Entries file for next time.  */
206 
207 	    /* FIXME-maybe: find_dirs is bogus for this usage because
208 	       it skips CVSATTIC and CVSLCK directories--those names
209 	       should be special only in the repository.  However, in
210 	       the interests of not perturbing this code, we probably
211 	       should leave well enough alone unless we want to write
212 	       a sanity.sh test case (which would operate by manually
213 	       hacking on the CVS/Entries file).  */
214 
215 	    if (find_dirs (".", dirlist, 1, tmpentries) != 0)
216 		error (1, errno, "cannot open current directory");
217 	    if (tmpentries != NULL)
218 	    {
219 		if (! list_isempty (dirlist))
220 		    walklist (dirlist, register_subdir_proc,
221 			      (void *) tmpentries);
222 		else
223 		    Subdirs_Known (tmpentries);
224 	    }
225 	}
226 
227 	if (entries == NULL && tmpentries != NULL)
228 	    Entries_Close (tmpentries);
229     }
230 
231     /* look for sub-dirs in the repository */
232     if ((which & W_REPOS) && repository)
233     {
234 	/* search the repository */
235 	if (find_dirs (repository, dirlist, 0, entries) != 0)
236 	    error (1, errno, "cannot open directory %s", repository);
237 
238 	/* We don't need to look in the attic because directories
239 	   never go in the attic.  In the future, there hopefully will
240 	   be a better mechanism for detecting whether a directory in
241 	   the repository is alive or dead; it may or may not involve
242 	   moving directories to the attic.  */
243     }
244 
245     /* sort the list into alphabetical order and return it */
246     sortlist (dirlist, fsortcmp);
247     return (dirlist);
248 }
249 
250 /*
251  * Finds all the ,v files in the argument directory, and adds them to the
252  * files list.  Returns 0 for success and non-zero if the argument directory
253  * cannot be opened, in which case errno is set to indicate the error.
254  * In the error case LIST is left in some reasonable state (unchanged, or
255  * containing the files which were found before the error occurred).
256  */
257 static int
find_rcs(dir,list)258 find_rcs (dir, list)
259     char *dir;
260     List *list;
261 {
262     Node *p;
263     struct dirent *dp;
264     DIR *dirp;
265 
266     /* set up to read the dir */
267     if ((dirp = CVS_OPENDIR (dir)) == NULL)
268 	return (1);
269 
270     /* read the dir, grabbing the ,v files */
271     errno = 0;
272     while ((dp = CVS_READDIR (dirp)) != NULL)
273     {
274 	if (CVS_FNMATCH (RCSPAT, dp->d_name, 0) == 0)
275 	{
276 	    char *comma;
277 
278 	    comma = strrchr (dp->d_name, ',');	/* strip the ,v */
279 	    *comma = '\0';
280 	    p = getnode ();
281 	    p->type = FILES;
282 	    p->key = xstrdup (dp->d_name);
283 	    if (addnode (list, p) != 0)
284 		freenode (p);
285 	}
286 	errno = 0;
287     }
288     if (errno != 0)
289     {
290 	int save_errno = errno;
291 	(void) CVS_CLOSEDIR (dirp);
292 	errno = save_errno;
293 	return 1;
294     }
295     (void) CVS_CLOSEDIR (dirp);
296     return (0);
297 }
298 
299 /*
300  * Finds all the subdirectories of the argument dir and adds them to
301  * the specified list.  Sub-directories without a CVS administration
302  * directory are optionally ignored.  If ENTRIES is not NULL, all
303  * files on the list are ignored.  Returns 0 for success or 1 on
304  * error, in which case errno is set to indicate the error.
305  */
306 static int
find_dirs(dir,list,checkadm,entries)307 find_dirs (dir, list, checkadm, entries)
308     char *dir;
309     List *list;
310     int checkadm;
311     List *entries;
312 {
313     Node *p;
314     char *tmp = NULL;
315     size_t tmp_size = 0;
316     struct dirent *dp;
317     DIR *dirp;
318     int skip_emptydir = 0;
319 
320     /* First figure out whether we need to skip directories named
321        Emptydir.  Except in the CVSNULLREPOS case, Emptydir is just
322        a normal directory name.  */
323     if (isabsolute (dir)
324 	&& strncmp (dir, current_parsed_root->directory, strlen (current_parsed_root->directory)) == 0
325 	&& ISDIRSEP (dir[strlen (current_parsed_root->directory)])
326 	&& strcmp (dir + strlen (current_parsed_root->directory) + 1, CVSROOTADM) == 0)
327 	skip_emptydir = 1;
328 
329     /* set up to read the dir */
330     if ((dirp = CVS_OPENDIR (dir)) == NULL)
331 	return (1);
332 
333     /* read the dir, grabbing sub-dirs */
334     errno = 0;
335     while ((dp = CVS_READDIR (dirp)) != NULL)
336     {
337 	if (strcmp (dp->d_name, ".") == 0 ||
338 	    strcmp (dp->d_name, "..") == 0 ||
339 	    strcmp (dp->d_name, CVSATTIC) == 0 ||
340 	    strcmp (dp->d_name, CVSLCK) == 0 ||
341 	    strcmp (dp->d_name, CVSREP) == 0)
342 	    goto do_it_again;
343 
344 	/* findnode() is going to be significantly faster than stat()
345 	   because it involves no system calls.  That is why we bother
346 	   with the entries argument, and why we check this first.  */
347 	if (entries != NULL && findnode (entries, dp->d_name) != NULL)
348 	    goto do_it_again;
349 
350 	if (skip_emptydir
351 	    && strcmp (dp->d_name, CVSNULLREPOS) == 0)
352 	    goto do_it_again;
353 
354 #ifdef DT_DIR
355 	if (dp->d_type != DT_DIR)
356 	{
357 	    if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK)
358 		goto do_it_again;
359 #endif
360 	    /* don't bother stating ,v files */
361 	    if (CVS_FNMATCH (RCSPAT, dp->d_name, 0) == 0)
362 		goto do_it_again;
363 
364 	    expand_string (&tmp,
365 			   &tmp_size,
366 			   strlen (dir) + strlen (dp->d_name) + 10);
367 	    sprintf (tmp, "%s/%s", dir, dp->d_name);
368 	    if (!isdir (tmp))
369 		goto do_it_again;
370 
371 #ifdef DT_DIR
372 	}
373 #endif
374 
375 	/* check for administration directories (if needed) */
376 	if (checkadm)
377 	{
378 	    /* blow off symbolic links to dirs in local dir */
379 #ifdef DT_DIR
380 	    if (dp->d_type != DT_DIR)
381 	    {
382 		/* we're either unknown or a symlink at this point */
383 		if (dp->d_type == DT_LNK)
384 		    goto do_it_again;
385 #endif
386 		/* Note that we only get here if we already set tmp
387 		   above.  */
388 		if (islink (tmp))
389 		    goto do_it_again;
390 #ifdef DT_DIR
391 	    }
392 #endif
393 
394 	    /* check for new style */
395 	    expand_string (&tmp,
396 			   &tmp_size,
397 			   (strlen (dir) + strlen (dp->d_name)
398 			    + sizeof (CVSADM) + 10));
399 	    (void) sprintf (tmp, "%s/%s/%s", dir, dp->d_name, CVSADM);
400 	    if (!isdir (tmp))
401 		goto do_it_again;
402 	}
403 
404 	/* put it in the list */
405 	p = getnode ();
406 	p->type = DIRS;
407 	p->key = xstrdup (dp->d_name);
408 	if (addnode (list, p) != 0)
409 	    freenode (p);
410 
411     do_it_again:
412 	errno = 0;
413     }
414     if (errno != 0)
415     {
416 	int save_errno = errno;
417 	(void) CVS_CLOSEDIR (dirp);
418 	errno = save_errno;
419 	return 1;
420     }
421     (void) CVS_CLOSEDIR (dirp);
422     if (tmp != NULL)
423 	free (tmp);
424     return (0);
425 }
426