1 /* This program is free software; you can redistribute it and/or modify 2 it under the terms of the GNU General Public License as published by 3 the Free Software Foundation; either version 2, or (at your option) 4 any later version. 5 6 This program is distributed in the hope that it will be useful, 7 but WITHOUT ANY WARRANTY; without even the implied warranty of 8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 GNU General Public License for more details. */ 10 11 /* 12 * .cvsignore file support contributed by David G. Grubbs <dgg@odi.com> 13 */ 14 15 #include "cvs.h" 16 #include "getline.h" 17 18 /* 19 * Ignore file section. 20 * 21 * "!" may be included any time to reset the list (i.e. ignore nothing); 22 * "*" may be specified to ignore everything. It stays as the first 23 * element forever, unless a "!" clears it out. 24 */ 25 26 static char **ign_list; /* List of files to ignore in update 27 * and import */ 28 static char **s_ign_list = NULL; 29 static int ign_count; /* Number of active entries */ 30 static int s_ign_count = 0; 31 static int ign_size; /* This many slots available (plus 32 * one for a NULL) */ 33 static int ign_hold = -1; /* Index where first "temporary" item 34 * is held */ 35 36 const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\ 37 .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\ 38 *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$ *.depend"; 39 40 #define IGN_GROW 16 /* grow the list by 16 elements at a 41 * time */ 42 43 /* Nonzero if we have encountered an -I ! directive, which means one should 44 no longer ask the server about what is in CVSROOTADM_IGNORE. */ 45 int ign_inhibit_server; 46 47 /* 48 * To the "ignore list", add the hard-coded default ignored wildcards above, 49 * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in 50 * ~/.cvsignore and the wildcards found in the CVSIGNORE environment 51 * variable. 52 */ 53 void 54 ign_setup () 55 { 56 char *home_dir; 57 char *tmp; 58 59 ign_inhibit_server = 0; 60 61 /* Start with default list and special case */ 62 tmp = xstrdup (ign_default); 63 ign_add (tmp, 0); 64 free (tmp); 65 66 #ifdef CLIENT_SUPPORT 67 /* The client handles another way, by (after it does its own ignore file 68 processing, and only if !ign_inhibit_server), letting the server 69 know about the files and letting it decide whether to ignore 70 them based on CVSROOOTADM_IGNORE. */ 71 if (!current_parsed_root->isremote) 72 #endif 73 { 74 char *file = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) 75 + sizeof (CVSROOTADM_IGNORE) + 10); 76 /* Then add entries found in repository, if it exists */ 77 (void) sprintf (file, "%s/%s/%s", current_parsed_root->directory, 78 CVSROOTADM, CVSROOTADM_IGNORE); 79 ign_add_file (file, 0); 80 free (file); 81 } 82 83 /* Then add entries found in home dir, (if user has one) and file exists */ 84 home_dir = get_homedir (); 85 /* If we can't find a home directory, ignore ~/.cvsignore. This may 86 make tracking down problems a bit of a pain, but on the other 87 hand it might be obnoxious to complain when CVS will function 88 just fine without .cvsignore (and many users won't even know what 89 .cvsignore is). */ 90 if (home_dir) 91 { 92 char *file = xmalloc (strlen (home_dir) + sizeof (CVSDOTIGNORE) + 10); 93 (void) sprintf (file, "%s/%s", home_dir, CVSDOTIGNORE); 94 ign_add_file (file, 0); 95 free (file); 96 } 97 98 /* Then add entries found in CVSIGNORE environment variable. */ 99 ign_add (getenv (IGNORE_ENV), 0); 100 101 /* Later, add ignore entries found in -I arguments */ 102 } 103 104 /* 105 * Open a file and read lines, feeding each line to a line parser. Arrange 106 * for keeping a temporary list of wildcards at the end, if the "hold" 107 * argument is set. 108 */ 109 void 110 ign_add_file (file, hold) 111 char *file; 112 int hold; 113 { 114 FILE *fp; 115 char *line = NULL; 116 size_t line_allocated = 0; 117 118 /* restore the saved list (if any) */ 119 if (s_ign_list != NULL) 120 { 121 int i; 122 123 for (i = 0; i < s_ign_count; i++) 124 ign_list[i] = s_ign_list[i]; 125 ign_count = s_ign_count; 126 ign_list[ign_count] = NULL; 127 128 s_ign_count = 0; 129 free (s_ign_list); 130 s_ign_list = NULL; 131 } 132 133 /* is this a temporary ignore file? */ 134 if (hold) 135 { 136 /* re-set if we had already done a temporary file */ 137 if (ign_hold >= 0) 138 { 139 int i; 140 141 for (i = ign_hold; i < ign_count; i++) 142 free (ign_list[i]); 143 ign_count = ign_hold; 144 ign_list[ign_count] = NULL; 145 } 146 else 147 { 148 ign_hold = ign_count; 149 } 150 } 151 152 /* load the file */ 153 fp = CVS_FOPEN (file, "r"); 154 if (fp == NULL) 155 { 156 if (! existence_error (errno)) 157 error (0, errno, "cannot open %s", file); 158 return; 159 } 160 while (getline (&line, &line_allocated, fp) >= 0) 161 ign_add (line, hold); 162 if (ferror (fp)) 163 error (0, errno, "cannot read %s", file); 164 if (fclose (fp) < 0) 165 error (0, errno, "cannot close %s", file); 166 free (line); 167 } 168 169 /* Parse a line of space-separated wildcards and add them to the list. */ 170 void 171 ign_add (ign, hold) 172 char *ign; 173 int hold; 174 { 175 if (!ign || !*ign) 176 return; 177 178 for (; *ign; ign++) 179 { 180 char *mark; 181 char save; 182 183 /* ignore whitespace before the token */ 184 if (isspace ((unsigned char) *ign)) 185 continue; 186 187 /* 188 * if we find a single character !, we must re-set the ignore list 189 * (saving it if necessary). We also catch * as a special case in a 190 * global ignore file as an optimization 191 */ 192 if ((!*(ign+1) || isspace ((unsigned char) *(ign+1))) 193 && (*ign == '!' || *ign == '*')) 194 { 195 if (!hold) 196 { 197 /* permanently reset the ignore list */ 198 int i; 199 200 for (i = 0; i < ign_count; i++) 201 free (ign_list[i]); 202 ign_count = 0; 203 ign_list[0] = NULL; 204 205 /* if we are doing a '!', continue; otherwise add the '*' */ 206 if (*ign == '!') 207 { 208 ign_inhibit_server = 1; 209 continue; 210 } 211 } 212 else if (*ign == '!') 213 { 214 /* temporarily reset the ignore list */ 215 int i; 216 217 if (ign_hold >= 0) 218 { 219 for (i = ign_hold; i < ign_count; i++) 220 free (ign_list[i]); 221 ign_hold = -1; 222 } 223 s_ign_list = (char **) xmalloc (ign_count * sizeof (char *)); 224 for (i = 0; i < ign_count; i++) 225 s_ign_list[i] = ign_list[i]; 226 s_ign_count = ign_count; 227 ign_count = 0; 228 ign_list[0] = NULL; 229 continue; 230 } 231 } 232 233 /* If we have used up all the space, add some more */ 234 if (ign_count >= ign_size) 235 { 236 ign_size += IGN_GROW; 237 ign_list = (char **) xrealloc ((char *) ign_list, 238 (ign_size + 1) * sizeof (char *)); 239 } 240 241 /* find the end of this token */ 242 for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++) 243 /* do nothing */ ; 244 245 save = *mark; 246 *mark = '\0'; 247 248 ign_list[ign_count++] = xstrdup (ign); 249 ign_list[ign_count] = NULL; 250 251 *mark = save; 252 if (save) 253 ign = mark; 254 else 255 ign = mark - 1; 256 } 257 } 258 259 /* Set to 1 if filenames should be matched in a case-insensitive 260 fashion. Note that, contrary to the name and placement in ignore.c, 261 this is no longer just for ignore patterns. */ 262 int ign_case; 263 264 /* Return 1 if the given filename should be ignored by update or import. */ 265 int 266 ign_name (name) 267 char *name; 268 { 269 char **cpp = ign_list; 270 271 if (cpp == NULL) 272 return (0); 273 274 if (ign_case) 275 { 276 /* We do a case-insensitive match by calling fnmatch on copies of 277 the pattern and the name which have been converted to 278 lowercase. FIXME: would be much cleaner to just unify this 279 with the other case-insensitive fnmatch stuff (FOLD_FN_CHAR 280 in lib/fnmatch.c; os2_fnmatch in emx/system.c). */ 281 char *name_lower; 282 char *pat_lower; 283 char *p; 284 285 name_lower = xstrdup (name); 286 for (p = name_lower; *p != '\0'; ++p) 287 *p = tolower (*p); 288 while (*cpp) 289 { 290 pat_lower = xstrdup (*cpp++); 291 for (p = pat_lower; *p != '\0'; ++p) 292 *p = tolower (*p); 293 if (CVS_FNMATCH (pat_lower, name_lower, 0) == 0) 294 goto matched; 295 free (pat_lower); 296 } 297 free (name_lower); 298 return 0; 299 matched: 300 free (name_lower); 301 free (pat_lower); 302 return 1; 303 } 304 else 305 { 306 while (*cpp) 307 if (CVS_FNMATCH (*cpp++, name, 0) == 0) 308 return 1; 309 return 0; 310 } 311 } 312 313 /* FIXME: This list of dirs to ignore stuff seems not to be used. 314 Really? send_dirent_proc and update_dirent_proc both call 315 ignore_directory and do_module calls ign_dir_add. No doubt could 316 use some documentation/testsuite work. */ 317 318 static char **dir_ign_list = NULL; 319 static int dir_ign_max = 0; 320 static int dir_ign_current = 0; 321 322 /* Add a directory to list of dirs to ignore. */ 323 void 324 ign_dir_add (name) 325 char *name; 326 { 327 /* Make sure we've got the space for the entry. */ 328 if (dir_ign_current <= dir_ign_max) 329 { 330 dir_ign_max += IGN_GROW; 331 dir_ign_list = 332 (char **) xrealloc (dir_ign_list, 333 (dir_ign_max + 1) * sizeof (char *)); 334 } 335 336 dir_ign_list[dir_ign_current++] = xstrdup (name); 337 } 338 339 340 /* Return nonzero if NAME is part of the list of directories to ignore. */ 341 342 int 343 ignore_directory (name) 344 char *name; 345 { 346 int i; 347 348 if (!dir_ign_list) 349 return 0; 350 351 i = dir_ign_current; 352 while (i--) 353 { 354 if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])) == 0) 355 return 1; 356 } 357 358 return 0; 359 } 360 361 /* 362 * Process the current directory, looking for files not in ILIST and 363 * not on the global ignore list for this directory. If we find one, 364 * call PROC passing it the name of the file and the update dir. 365 * ENTRIES is the entries list, which is used to identify known 366 * directories. ENTRIES may be NULL, in which case we assume that any 367 * directory with a CVS administration directory is known. 368 */ 369 void 370 ignore_files (ilist, entries, update_dir, proc) 371 List *ilist; 372 List *entries; 373 char *update_dir; 374 Ignore_proc proc; 375 { 376 int subdirs; 377 DIR *dirp; 378 struct dirent *dp; 379 struct stat sb; 380 char *file; 381 char *xdir; 382 List *files; 383 Node *p; 384 385 /* Set SUBDIRS if we have subdirectory information in ENTRIES. */ 386 if (entries == NULL) 387 subdirs = 0; 388 else 389 { 390 struct stickydirtag *sdtp; 391 392 sdtp = (struct stickydirtag *) entries->list->data; 393 subdirs = sdtp == NULL || sdtp->subdirs; 394 } 395 396 /* we get called with update_dir set to "." sometimes... strip it */ 397 if (strcmp (update_dir, ".") == 0) 398 xdir = ""; 399 else 400 xdir = update_dir; 401 402 dirp = CVS_OPENDIR ("."); 403 if (dirp == NULL) 404 { 405 error (0, errno, "cannot open current directory"); 406 return; 407 } 408 409 ign_add_file (CVSDOTIGNORE, 1); 410 wrap_add_file (CVSDOTWRAPPER, 1); 411 412 /* Make a list for the files. */ 413 files = getlist (); 414 415 while (errno = 0, (dp = CVS_READDIR (dirp)) != NULL) 416 { 417 file = dp->d_name; 418 if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0) 419 continue; 420 if (findnode_fn (ilist, file) != NULL) 421 continue; 422 if (subdirs) 423 { 424 Node *node; 425 426 node = findnode_fn (entries, file); 427 if (node != NULL 428 && ((Entnode *) node->data)->type == ENT_SUBDIR) 429 { 430 char *p; 431 int dir; 432 433 /* For consistency with past behaviour, we only ignore 434 this directory if there is a CVS subdirectory. 435 This will normally be the case, but the user may 436 have messed up the working directory somehow. */ 437 p = xmalloc (strlen (file) + sizeof CVSADM + 10); 438 sprintf (p, "%s/%s", file, CVSADM); 439 dir = isdir (p); 440 free (p); 441 if (dir) 442 continue; 443 } 444 } 445 446 /* We could be ignoring FIFOs and other files which are neither 447 regular files nor directories here. */ 448 if (ign_name (file)) 449 continue; 450 451 if ( 452 #ifdef DT_DIR 453 dp->d_type != DT_UNKNOWN || 454 #endif 455 lstat(file, &sb) != -1) 456 { 457 458 if ( 459 #ifdef DT_DIR 460 dp->d_type == DT_DIR 461 || (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode)) 462 #else 463 S_ISDIR (sb.st_mode) 464 #endif 465 ) 466 { 467 if (! subdirs) 468 { 469 char *temp; 470 471 temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10); 472 (void) sprintf (temp, "%s/%s", file, CVSADM); 473 if (isdir (temp)) 474 { 475 free (temp); 476 continue; 477 } 478 free (temp); 479 } 480 } 481 #ifdef S_ISLNK 482 else if ( 483 #ifdef DT_DIR 484 dp->d_type == DT_LNK 485 || (dp->d_type == DT_UNKNOWN && S_ISLNK(sb.st_mode)) 486 #else 487 S_ISLNK (sb.st_mode) 488 #endif 489 ) 490 { 491 continue; 492 } 493 #endif 494 } 495 496 p = getnode (); 497 p->type = FILES; 498 p->key = xstrdup (file); 499 (void) addnode (files, p); 500 } 501 if (errno != 0) 502 error (0, errno, "error reading current directory"); 503 (void) CVS_CLOSEDIR (dirp); 504 505 sortlist (files, fsortcmp); 506 for (p = files->list->next; p != files->list; p = p->next) 507 (*proc) (p->key, xdir); 508 dellist (&files); 509 } 510