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