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