Home | History | Annotate | Line # | Download | only in src
      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 (at) 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