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