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