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 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 * 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 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 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 * 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 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 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