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