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