117721Speter/*
2175261Sobrien * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3175261Sobrien *
4175261Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5175261Sobrien *                                  and others.
6175261Sobrien *
7175261Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8175261Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner
917721Speter *
1017721Speter * You may distribute under the terms of the GNU General Public License as
1132785Speter * specified in the README file that comes with the CVS source distribution.
1217721Speter *
1317721Speter * Find Names
1417721Speter *
1517721Speter * Finds all the pertinent file names, both from the administration and from the
1617721Speter * repository
1717721Speter *
1817721Speter * Find Dirs
1917721Speter *
2017721Speter * Finds all pertinent sub-directories of the checked out instantiation and the
2117721Speter * repository (and optionally the attic)
2217721Speter */
2317721Speter
2417721Speter#include "cvs.h"
2517721Speter
2625839Speterstatic int find_dirs PROTO((char *dir, List * list, int checkadm,
2725839Speter			    List *entries));
2817721Speterstatic int find_rcs PROTO((char *dir, List * list));
2925839Speterstatic int add_subdir_proc PROTO((Node *, void *));
3025839Speterstatic int register_subdir_proc PROTO((Node *, void *));
3117721Speter
3217721Speter/*
3317721Speter * add the key from entry on entries list to the files list
3417721Speter */
3517721Speterstatic int add_entries_proc PROTO((Node *, void *));
3617721Speterstatic int
3717721Speteradd_entries_proc (node, closure)
3817721Speter     Node *node;
3917721Speter     void *closure;
4017721Speter{
4117721Speter    Node *fnode;
42128266Speter    List *filelist = closure;
43128266Speter    Entnode *entnode = node->data;
4417721Speter
4525839Speter    if (entnode->type != ENT_FILE)
4625839Speter	return (0);
4725839Speter
4817721Speter    fnode = getnode ();
4917721Speter    fnode->type = FILES;
5017721Speter    fnode->key = xstrdup (node->key);
5117721Speter    if (addnode (filelist, fnode) != 0)
5217721Speter	freenode (fnode);
5317721Speter    return (0);
5417721Speter}
5517721Speter
5654427Speter/* Find files in the repository and/or working directory.  On error,
5754427Speter   may either print a nonfatal error and return NULL, or just give
5854427Speter   a fatal error.  On success, return non-NULL (even if it is an empty
5954427Speter   list).  */
6054427Speter
6117721SpeterList *
6217721SpeterFind_Names (repository, which, aflag, optentries)
6317721Speter    char *repository;
6417721Speter    int which;
6517721Speter    int aflag;
6617721Speter    List **optentries;
6717721Speter{
6817721Speter    List *entries;
6917721Speter    List *files;
7017721Speter
7117721Speter    /* make a list for the files */
72102840Speter    files = getlist ();
7317721Speter
7417721Speter    /* look at entries (if necessary) */
7517721Speter    if (which & W_LOCAL)
7617721Speter    {
7717721Speter	/* parse the entries file (if it exists) */
7834461Speter	entries = Entries_Open (aflag, NULL);
7917721Speter	if (entries != NULL)
8017721Speter	{
8117721Speter	    /* walk the entries file adding elements to the files list */
82102840Speter	    (void) walklist (entries, add_entries_proc, files);
8317721Speter
8417721Speter	    /* if our caller wanted the entries list, return it; else free it */
8517721Speter	    if (optentries != NULL)
8617721Speter		*optentries = entries;
8717721Speter	    else
8817721Speter		Entries_Close (entries);
8917721Speter	}
9017721Speter    }
9117721Speter
9217721Speter    if ((which & W_REPOS) && repository && !isreadable (CVSADM_ENTSTAT))
9317721Speter    {
9417721Speter	/* search the repository */
9517721Speter	if (find_rcs (repository, files) != 0)
9654427Speter	{
9754427Speter	    error (0, errno, "cannot open directory %s", repository);
9854427Speter	    goto error_exit;
9954427Speter	}
10017721Speter
10117721Speter	/* search the attic too */
10217721Speter	if (which & W_ATTIC)
10317721Speter	{
10425839Speter	    char *dir;
10525839Speter	    dir = xmalloc (strlen (repository) + sizeof (CVSATTIC) + 10);
10617721Speter	    (void) sprintf (dir, "%s/%s", repository, CVSATTIC);
10754427Speter	    if (find_rcs (dir, files) != 0
10854427Speter		&& !existence_error (errno))
10954427Speter		/* For now keep this a fatal error, seems less useful
11054427Speter		   for access control than the case above.  */
11154427Speter		error (1, errno, "cannot open directory %s", dir);
11225839Speter	    free (dir);
11317721Speter	}
11417721Speter    }
11517721Speter
11617721Speter    /* sort the list into alphabetical order and return it */
11717721Speter    sortlist (files, fsortcmp);
11817721Speter    return (files);
11954427Speter error_exit:
12054427Speter    dellist (&files);
12154427Speter    return NULL;
12217721Speter}
12317721Speter
12417721Speter/*
12525839Speter * Add an entry from the subdirs list to the directories list.  This
12625839Speter * is called via walklist.
12725839Speter */
12825839Speter
12925839Speterstatic int
13025839Speteradd_subdir_proc (p, closure)
13125839Speter     Node *p;
13225839Speter     void *closure;
13325839Speter{
134128266Speter    List *dirlist = closure;
135128266Speter    Entnode *entnode = p->data;
13625839Speter    Node *dnode;
13725839Speter
13825839Speter    if (entnode->type != ENT_SUBDIR)
13925839Speter	return 0;
14025839Speter
14125839Speter    dnode = getnode ();
14225839Speter    dnode->type = DIRS;
14325839Speter    dnode->key = xstrdup (entnode->user);
14425839Speter    if (addnode (dirlist, dnode) != 0)
14525839Speter	freenode (dnode);
14625839Speter    return 0;
14725839Speter}
14825839Speter
14925839Speter/*
15025839Speter * Register a subdirectory.  This is called via walklist.
15125839Speter */
15225839Speter
15325839Speter/*ARGSUSED*/
15425839Speterstatic int
15525839Speterregister_subdir_proc (p, closure)
15625839Speter     Node *p;
15725839Speter     void *closure;
15825839Speter{
15925839Speter    List *entries = (List *) closure;
16025839Speter
16125839Speter    Subdir_Register (entries, (char *) NULL, p->key);
16225839Speter    return 0;
16325839Speter}
16425839Speter
16525839Speter/*
16617721Speter * create a list of directories to traverse from the current directory
16717721Speter */
16817721SpeterList *
16925839SpeterFind_Directories (repository, which, entries)
17017721Speter    char *repository;
17117721Speter    int which;
17225839Speter    List *entries;
17317721Speter{
17417721Speter    List *dirlist;
17517721Speter
17617721Speter    /* make a list for the directories */
17717721Speter    dirlist = getlist ();
17817721Speter
17917721Speter    /* find the local ones */
18017721Speter    if (which & W_LOCAL)
18117721Speter    {
18225839Speter	List *tmpentries;
18325839Speter	struct stickydirtag *sdtp;
18425839Speter
18525839Speter	/* Look through the Entries file.  */
18625839Speter
18725839Speter	if (entries != NULL)
18825839Speter	    tmpentries = entries;
18925839Speter	else if (isfile (CVSADM_ENT))
19034461Speter	    tmpentries = Entries_Open (0, NULL);
19125839Speter	else
19225839Speter	    tmpentries = NULL;
19325839Speter
19425839Speter	if (tmpentries != NULL)
195128266Speter	    sdtp = tmpentries->list->data;
19625839Speter
19725839Speter	/* If we do have an entries list, then if sdtp is NULL, or if
19825839Speter           sdtp->subdirs is nonzero, all subdirectory information is
19925839Speter           recorded in the entries list.  */
20025839Speter	if (tmpentries != NULL && (sdtp == NULL || sdtp->subdirs))
20125839Speter	    walklist (tmpentries, add_subdir_proc, (void *) dirlist);
20225839Speter	else
20325839Speter	{
20425839Speter	    /* This is an old working directory, in which subdirectory
20525839Speter               information is not recorded in the Entries file.  Find
20625839Speter               the subdirectories the hard way, and, if possible, add
20725839Speter               it to the Entries file for next time.  */
20832785Speter
20932785Speter	    /* FIXME-maybe: find_dirs is bogus for this usage because
21032785Speter	       it skips CVSATTIC and CVSLCK directories--those names
21132785Speter	       should be special only in the repository.  However, in
21232785Speter	       the interests of not perturbing this code, we probably
21332785Speter	       should leave well enough alone unless we want to write
21432785Speter	       a sanity.sh test case (which would operate by manually
21532785Speter	       hacking on the CVS/Entries file).  */
21632785Speter
21725839Speter	    if (find_dirs (".", dirlist, 1, tmpentries) != 0)
21825839Speter		error (1, errno, "cannot open current directory");
21925839Speter	    if (tmpentries != NULL)
22025839Speter	    {
22125839Speter		if (! list_isempty (dirlist))
22225839Speter		    walklist (dirlist, register_subdir_proc,
22325839Speter			      (void *) tmpentries);
22425839Speter		else
22525839Speter		    Subdirs_Known (tmpentries);
22625839Speter	    }
22725839Speter	}
22825839Speter
22925839Speter	if (entries == NULL && tmpentries != NULL)
23025839Speter	    Entries_Close (tmpentries);
23117721Speter    }
23217721Speter
23317721Speter    /* look for sub-dirs in the repository */
23417721Speter    if ((which & W_REPOS) && repository)
23517721Speter    {
23617721Speter	/* search the repository */
23725839Speter	if (find_dirs (repository, dirlist, 0, entries) != 0)
23817721Speter	    error (1, errno, "cannot open directory %s", repository);
23917721Speter
24025839Speter	/* We don't need to look in the attic because directories
24125839Speter	   never go in the attic.  In the future, there hopefully will
24225839Speter	   be a better mechanism for detecting whether a directory in
24325839Speter	   the repository is alive or dead; it may or may not involve
24425839Speter	   moving directories to the attic.  */
24517721Speter    }
24617721Speter
24717721Speter    /* sort the list into alphabetical order and return it */
24817721Speter    sortlist (dirlist, fsortcmp);
24917721Speter    return (dirlist);
25017721Speter}
25117721Speter
25217721Speter/*
25317721Speter * Finds all the ,v files in the argument directory, and adds them to the
25417721Speter * files list.  Returns 0 for success and non-zero if the argument directory
25554427Speter * cannot be opened, in which case errno is set to indicate the error.
25654427Speter * In the error case LIST is left in some reasonable state (unchanged, or
25754427Speter * containing the files which were found before the error occurred).
25817721Speter */
25917721Speterstatic int
26017721Speterfind_rcs (dir, list)
26117721Speter    char *dir;
26217721Speter    List *list;
26317721Speter{
26417721Speter    Node *p;
26517721Speter    struct dirent *dp;
26617721Speter    DIR *dirp;
26717721Speter
26817721Speter    /* set up to read the dir */
26925839Speter    if ((dirp = CVS_OPENDIR (dir)) == NULL)
27017721Speter	return (1);
27117721Speter
27217721Speter    /* read the dir, grabbing the ,v files */
27354427Speter    errno = 0;
27481404Speter    while ((dp = CVS_READDIR (dirp)) != NULL)
27517721Speter    {
27626801Speter	if (CVS_FNMATCH (RCSPAT, dp->d_name, 0) == 0)
27717721Speter	{
27817721Speter	    char *comma;
27917721Speter
28017721Speter	    comma = strrchr (dp->d_name, ',');	/* strip the ,v */
28117721Speter	    *comma = '\0';
28217721Speter	    p = getnode ();
28317721Speter	    p->type = FILES;
28417721Speter	    p->key = xstrdup (dp->d_name);
28517721Speter	    if (addnode (list, p) != 0)
28617721Speter		freenode (p);
28717721Speter	}
28854427Speter	errno = 0;
28917721Speter    }
29054427Speter    if (errno != 0)
29154427Speter    {
29254427Speter	int save_errno = errno;
29381404Speter	(void) CVS_CLOSEDIR (dirp);
29454427Speter	errno = save_errno;
29554427Speter	return 1;
29654427Speter    }
29781404Speter    (void) CVS_CLOSEDIR (dirp);
29817721Speter    return (0);
29917721Speter}
30017721Speter
30117721Speter/*
30225839Speter * Finds all the subdirectories of the argument dir and adds them to
30325839Speter * the specified list.  Sub-directories without a CVS administration
30425839Speter * directory are optionally ignored.  If ENTRIES is not NULL, all
30525839Speter * files on the list are ignored.  Returns 0 for success or 1 on
30654427Speter * error, in which case errno is set to indicate the error.
30717721Speter */
30817721Speterstatic int
30925839Speterfind_dirs (dir, list, checkadm, entries)
31017721Speter    char *dir;
31117721Speter    List *list;
31217721Speter    int checkadm;
31325839Speter    List *entries;
31417721Speter{
31517721Speter    Node *p;
31625839Speter    char *tmp = NULL;
31725839Speter    size_t tmp_size = 0;
31817721Speter    struct dirent *dp;
31917721Speter    DIR *dirp;
32032785Speter    int skip_emptydir = 0;
32117721Speter
32232785Speter    /* First figure out whether we need to skip directories named
32332785Speter       Emptydir.  Except in the CVSNULLREPOS case, Emptydir is just
32432785Speter       a normal directory name.  */
32532785Speter    if (isabsolute (dir)
32681404Speter	&& strncmp (dir, current_parsed_root->directory, strlen (current_parsed_root->directory)) == 0
32781404Speter	&& ISDIRSEP (dir[strlen (current_parsed_root->directory)])
32881404Speter	&& strcmp (dir + strlen (current_parsed_root->directory) + 1, CVSROOTADM) == 0)
32932785Speter	skip_emptydir = 1;
33032785Speter
33117721Speter    /* set up to read the dir */
33225839Speter    if ((dirp = CVS_OPENDIR (dir)) == NULL)
33317721Speter	return (1);
33417721Speter
33517721Speter    /* read the dir, grabbing sub-dirs */
33654427Speter    errno = 0;
33781404Speter    while ((dp = CVS_READDIR (dirp)) != NULL)
33817721Speter    {
33917721Speter	if (strcmp (dp->d_name, ".") == 0 ||
34017721Speter	    strcmp (dp->d_name, "..") == 0 ||
34117721Speter	    strcmp (dp->d_name, CVSATTIC) == 0 ||
34217721Speter	    strcmp (dp->d_name, CVSLCK) == 0 ||
34317721Speter	    strcmp (dp->d_name, CVSREP) == 0)
34454427Speter	    goto do_it_again;
34517721Speter
34625839Speter	/* findnode() is going to be significantly faster than stat()
34725839Speter	   because it involves no system calls.  That is why we bother
34825839Speter	   with the entries argument, and why we check this first.  */
34925839Speter	if (entries != NULL && findnode (entries, dp->d_name) != NULL)
35054427Speter	    goto do_it_again;
35125839Speter
35232785Speter	if (skip_emptydir
35332785Speter	    && strcmp (dp->d_name, CVSNULLREPOS) == 0)
35454427Speter	    goto do_it_again;
35532785Speter
35617721Speter#ifdef DT_DIR
35717721Speter	if (dp->d_type != DT_DIR)
35817721Speter	{
35917721Speter	    if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK)
36054427Speter		goto do_it_again;
36117721Speter#endif
36217721Speter	    /* don't bother stating ,v files */
36326801Speter	    if (CVS_FNMATCH (RCSPAT, dp->d_name, 0) == 0)
36454427Speter		goto do_it_again;
36517721Speter
36625839Speter	    expand_string (&tmp,
36725839Speter			   &tmp_size,
36825839Speter			   strlen (dir) + strlen (dp->d_name) + 10);
36917721Speter	    sprintf (tmp, "%s/%s", dir, dp->d_name);
37017721Speter	    if (!isdir (tmp))
37154427Speter		goto do_it_again;
37217721Speter
37317721Speter#ifdef DT_DIR
37417721Speter	}
37517721Speter#endif
37617721Speter
37717721Speter	/* check for administration directories (if needed) */
37817721Speter	if (checkadm)
37917721Speter	{
38017721Speter	    /* blow off symbolic links to dirs in local dir */
38117721Speter#ifdef DT_DIR
38217721Speter	    if (dp->d_type != DT_DIR)
38317721Speter	    {
38417721Speter		/* we're either unknown or a symlink at this point */
38517721Speter		if (dp->d_type == DT_LNK)
38654427Speter		    goto do_it_again;
38717721Speter#endif
38825839Speter		/* Note that we only get here if we already set tmp
38925839Speter		   above.  */
39017721Speter		if (islink (tmp))
39154427Speter		    goto do_it_again;
39217721Speter#ifdef DT_DIR
39317721Speter	    }
39417721Speter#endif
39517721Speter
39617721Speter	    /* check for new style */
39725839Speter	    expand_string (&tmp,
39825839Speter			   &tmp_size,
39925839Speter			   (strlen (dir) + strlen (dp->d_name)
40025839Speter			    + sizeof (CVSADM) + 10));
40117721Speter	    (void) sprintf (tmp, "%s/%s/%s", dir, dp->d_name, CVSADM);
40217721Speter	    if (!isdir (tmp))
40354427Speter		goto do_it_again;
40417721Speter	}
40517721Speter
40617721Speter	/* put it in the list */
40717721Speter	p = getnode ();
40817721Speter	p->type = DIRS;
40917721Speter	p->key = xstrdup (dp->d_name);
41017721Speter	if (addnode (list, p) != 0)
41117721Speter	    freenode (p);
41254427Speter
41354427Speter    do_it_again:
41454427Speter	errno = 0;
41517721Speter    }
41654427Speter    if (errno != 0)
41754427Speter    {
41854427Speter	int save_errno = errno;
41981404Speter	(void) CVS_CLOSEDIR (dirp);
42054427Speter	errno = save_errno;
42154427Speter	return 1;
42254427Speter    }
42381404Speter    (void) CVS_CLOSEDIR (dirp);
42425839Speter    if (tmp != NULL)
42525839Speter	free (tmp);
42617721Speter    return (0);
42717721Speter}
428