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