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