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