xref: /openbsd/gnu/usr.bin/cvs/src/ignore.c (revision 494c65f6)
1c56b20e6Stholo /* This program is free software; you can redistribute it and/or modify
2c56b20e6Stholo    it under the terms of the GNU General Public License as published by
3c56b20e6Stholo    the Free Software Foundation; either version 2, or (at your option)
4c56b20e6Stholo    any later version.
5c56b20e6Stholo 
6c56b20e6Stholo    This program is distributed in the hope that it will be useful,
7c56b20e6Stholo    but WITHOUT ANY WARRANTY; without even the implied warranty of
8c56b20e6Stholo    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9c56b20e6Stholo    GNU General Public License for more details.  */
10c56b20e6Stholo 
111e72d8d2Sderaadt /*
121e72d8d2Sderaadt  * .cvsignore file support contributed by David G. Grubbs <dgg@odi.com>
131e72d8d2Sderaadt  */
141e72d8d2Sderaadt 
151e72d8d2Sderaadt #include "cvs.h"
16c56b20e6Stholo #include "getline.h"
171e72d8d2Sderaadt 
181e72d8d2Sderaadt /*
191e72d8d2Sderaadt  * Ignore file section.
201e72d8d2Sderaadt  *
211e72d8d2Sderaadt  *	"!" may be included any time to reset the list (i.e. ignore nothing);
221e72d8d2Sderaadt  *	"*" may be specified to ignore everything.  It stays as the first
231e72d8d2Sderaadt  *	    element forever, unless a "!" clears it out.
241e72d8d2Sderaadt  */
251e72d8d2Sderaadt 
261e72d8d2Sderaadt static char **ign_list;			/* List of files to ignore in update
271e72d8d2Sderaadt 					 * and import */
281e72d8d2Sderaadt static char **s_ign_list = NULL;
291e72d8d2Sderaadt static int ign_count;			/* Number of active entries */
301e72d8d2Sderaadt static int s_ign_count = 0;
311e72d8d2Sderaadt static int ign_size;			/* This many slots available (plus
321e72d8d2Sderaadt 					 * one for a NULL) */
33da0c1eddSniklas static int ign_hold = -1;		/* Index where first "temporary" item
347e4d5a85Stholo 					 * is held */
351e72d8d2Sderaadt 
36*494c65f6Ssthen const char *ign_default = ". .. RCSLOG tags TAGS RCS SCCS .make.state\
37*494c65f6Ssthen  .*.swp *.core .git\
389d9224ffStholo  .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\
3968d5f0b6Smichaels  *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$ *.depend";
401e72d8d2Sderaadt 
411e72d8d2Sderaadt #define IGN_GROW 16			/* grow the list by 16 elements at a
421e72d8d2Sderaadt 					 * time */
431e72d8d2Sderaadt 
4413571821Stholo /* Nonzero if we have encountered an -I ! directive, which means one should
4513571821Stholo    no longer ask the server about what is in CVSROOTADM_IGNORE.  */
4613571821Stholo int ign_inhibit_server;
4713571821Stholo 
481e72d8d2Sderaadt /*
491e72d8d2Sderaadt  * To the "ignore list", add the hard-coded default ignored wildcards above,
501e72d8d2Sderaadt  * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
511e72d8d2Sderaadt  * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
521e72d8d2Sderaadt  * variable.
531e72d8d2Sderaadt  */
541e72d8d2Sderaadt void
ign_setup()551e72d8d2Sderaadt ign_setup ()
561e72d8d2Sderaadt {
577e4d5a85Stholo     char *home_dir;
581e72d8d2Sderaadt     char *tmp;
591e72d8d2Sderaadt 
6013571821Stholo     ign_inhibit_server = 0;
6113571821Stholo 
621e72d8d2Sderaadt     /* Start with default list and special case */
631e72d8d2Sderaadt     tmp = xstrdup (ign_default);
641e72d8d2Sderaadt     ign_add (tmp, 0);
651e72d8d2Sderaadt     free (tmp);
661e72d8d2Sderaadt 
6713571821Stholo #ifdef CLIENT_SUPPORT
6813571821Stholo     /* The client handles another way, by (after it does its own ignore file
6913571821Stholo        processing, and only if !ign_inhibit_server), letting the server
7013571821Stholo        know about the files and letting it decide whether to ignore
7113571821Stholo        them based on CVSROOOTADM_IGNORE.  */
7279822b2aStholo     if (!current_parsed_root->isremote)
7313571821Stholo #endif
7413571821Stholo     {
7579822b2aStholo 	char *file = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM)
76ae5e8a9bStholo 			      + sizeof (CVSROOTADM_IGNORE) + 10);
771e72d8d2Sderaadt 	/* Then add entries found in repository, if it exists */
7879822b2aStholo 	(void) sprintf (file, "%s/%s/%s", current_parsed_root->directory,
797e4d5a85Stholo 			CVSROOTADM, CVSROOTADM_IGNORE);
801e72d8d2Sderaadt 	ign_add_file (file, 0);
81ae5e8a9bStholo 	free (file);
8213571821Stholo     }
831e72d8d2Sderaadt 
841e72d8d2Sderaadt     /* Then add entries found in home dir, (if user has one) and file exists */
857e4d5a85Stholo     home_dir = get_homedir ();
869fe7c2c3Stholo     /* If we can't find a home directory, ignore ~/.cvsignore.  This may
879fe7c2c3Stholo        make tracking down problems a bit of a pain, but on the other
889fe7c2c3Stholo        hand it might be obnoxious to complain when CVS will function
899fe7c2c3Stholo        just fine without .cvsignore (and many users won't even know what
909fe7c2c3Stholo        .cvsignore is).  */
917e4d5a85Stholo     if (home_dir)
921e72d8d2Sderaadt     {
93ae5e8a9bStholo 	char *file = xmalloc (strlen (home_dir) + sizeof (CVSDOTIGNORE) + 10);
947e4d5a85Stholo 	(void) sprintf (file, "%s/%s", home_dir, CVSDOTIGNORE);
951e72d8d2Sderaadt 	ign_add_file (file, 0);
96ae5e8a9bStholo 	free (file);
971e72d8d2Sderaadt     }
981e72d8d2Sderaadt 
991e72d8d2Sderaadt     /* Then add entries found in CVSIGNORE environment variable. */
1001e72d8d2Sderaadt     ign_add (getenv (IGNORE_ENV), 0);
1011e72d8d2Sderaadt 
1021e72d8d2Sderaadt     /* Later, add ignore entries found in -I arguments */
1031e72d8d2Sderaadt }
1041e72d8d2Sderaadt 
1051e72d8d2Sderaadt /*
1061e72d8d2Sderaadt  * Open a file and read lines, feeding each line to a line parser. Arrange
1071e72d8d2Sderaadt  * for keeping a temporary list of wildcards at the end, if the "hold"
1081e72d8d2Sderaadt  * argument is set.
1091e72d8d2Sderaadt  */
1101e72d8d2Sderaadt void
ign_add_file(file,hold)1111e72d8d2Sderaadt ign_add_file (file, hold)
1121e72d8d2Sderaadt     char *file;
1131e72d8d2Sderaadt     int hold;
1141e72d8d2Sderaadt {
1151e72d8d2Sderaadt     FILE *fp;
116c56b20e6Stholo     char *line = NULL;
117c56b20e6Stholo     size_t line_allocated = 0;
1181e72d8d2Sderaadt 
1191e72d8d2Sderaadt     /* restore the saved list (if any) */
1201e72d8d2Sderaadt     if (s_ign_list != NULL)
1211e72d8d2Sderaadt     {
1221e72d8d2Sderaadt 	int i;
1231e72d8d2Sderaadt 
1241e72d8d2Sderaadt 	for (i = 0; i < s_ign_count; i++)
1251e72d8d2Sderaadt 	    ign_list[i] = s_ign_list[i];
1261e72d8d2Sderaadt 	ign_count = s_ign_count;
1271e72d8d2Sderaadt 	ign_list[ign_count] = NULL;
1281e72d8d2Sderaadt 
1291e72d8d2Sderaadt 	s_ign_count = 0;
1301e72d8d2Sderaadt 	free (s_ign_list);
1311e72d8d2Sderaadt 	s_ign_list = NULL;
1321e72d8d2Sderaadt     }
1331e72d8d2Sderaadt 
1341e72d8d2Sderaadt     /* is this a temporary ignore file? */
1351e72d8d2Sderaadt     if (hold)
1361e72d8d2Sderaadt     {
1371e72d8d2Sderaadt 	/* re-set if we had already done a temporary file */
138da0c1eddSniklas 	if (ign_hold >= 0)
1391e72d8d2Sderaadt 	{
1401e72d8d2Sderaadt 	    int i;
1411e72d8d2Sderaadt 
1421e72d8d2Sderaadt 	    for (i = ign_hold; i < ign_count; i++)
1431e72d8d2Sderaadt 		free (ign_list[i]);
1441e72d8d2Sderaadt 	    ign_count = ign_hold;
1451e72d8d2Sderaadt 	    ign_list[ign_count] = NULL;
1461e72d8d2Sderaadt 	}
1471e72d8d2Sderaadt 	else
1481e72d8d2Sderaadt 	{
1491e72d8d2Sderaadt 	    ign_hold = ign_count;
1501e72d8d2Sderaadt 	}
1511e72d8d2Sderaadt     }
1521e72d8d2Sderaadt 
1531e72d8d2Sderaadt     /* load the file */
1547e4d5a85Stholo     fp = CVS_FOPEN (file, "r");
1551e72d8d2Sderaadt     if (fp == NULL)
1561e72d8d2Sderaadt     {
15713571821Stholo 	if (! existence_error (errno))
1581e72d8d2Sderaadt 	    error (0, errno, "cannot open %s", file);
1591e72d8d2Sderaadt 	return;
1601e72d8d2Sderaadt     }
161f9bbbf45Sfgsch     while (get_line (&line, &line_allocated, fp) >= 0)
1621e72d8d2Sderaadt 	ign_add (line, hold);
163c56b20e6Stholo     if (ferror (fp))
164c56b20e6Stholo 	error (0, errno, "cannot read %s", file);
1651e72d8d2Sderaadt     if (fclose (fp) < 0)
1661e72d8d2Sderaadt 	error (0, errno, "cannot close %s", file);
167c56b20e6Stholo     free (line);
1681e72d8d2Sderaadt }
1691e72d8d2Sderaadt 
1701e72d8d2Sderaadt /* Parse a line of space-separated wildcards and add them to the list. */
1711e72d8d2Sderaadt void
ign_add(ign,hold)1721e72d8d2Sderaadt ign_add (ign, hold)
1731e72d8d2Sderaadt     char *ign;
1741e72d8d2Sderaadt     int hold;
1751e72d8d2Sderaadt {
1761e72d8d2Sderaadt     if (!ign || !*ign)
1771e72d8d2Sderaadt 	return;
1781e72d8d2Sderaadt 
1791e72d8d2Sderaadt     for (; *ign; ign++)
1801e72d8d2Sderaadt     {
1811e72d8d2Sderaadt 	char *mark;
1821e72d8d2Sderaadt 	char save;
1831e72d8d2Sderaadt 
1841e72d8d2Sderaadt 	/* ignore whitespace before the token */
1859fe7c2c3Stholo 	if (isspace ((unsigned char) *ign))
1861e72d8d2Sderaadt 	    continue;
1871e72d8d2Sderaadt 
1881e72d8d2Sderaadt 	/*
1891e72d8d2Sderaadt 	 * if we find a single character !, we must re-set the ignore list
1901e72d8d2Sderaadt 	 * (saving it if necessary).  We also catch * as a special case in a
1911e72d8d2Sderaadt 	 * global ignore file as an optimization
1921e72d8d2Sderaadt 	 */
1939fe7c2c3Stholo 	if ((!*(ign+1) || isspace ((unsigned char) *(ign+1)))
1949fe7c2c3Stholo 	    && (*ign == '!' || *ign == '*'))
1951e72d8d2Sderaadt 	{
1961e72d8d2Sderaadt 	    if (!hold)
1971e72d8d2Sderaadt 	    {
1981e72d8d2Sderaadt 		/* permanently reset the ignore list */
1991e72d8d2Sderaadt 		int i;
2001e72d8d2Sderaadt 
2011e72d8d2Sderaadt 		for (i = 0; i < ign_count; i++)
2021e72d8d2Sderaadt 		    free (ign_list[i]);
2031e72d8d2Sderaadt 		ign_count = 0;
2041e72d8d2Sderaadt 		ign_list[0] = NULL;
2051e72d8d2Sderaadt 
2061e72d8d2Sderaadt 		/* if we are doing a '!', continue; otherwise add the '*' */
2071e72d8d2Sderaadt 		if (*ign == '!')
20813571821Stholo 		{
20913571821Stholo 		    ign_inhibit_server = 1;
2101e72d8d2Sderaadt 		    continue;
2111e72d8d2Sderaadt 		}
21213571821Stholo 	    }
2131e72d8d2Sderaadt 	    else if (*ign == '!')
2141e72d8d2Sderaadt 	    {
2151e72d8d2Sderaadt 		/* temporarily reset the ignore list */
2161e72d8d2Sderaadt 		int i;
2171e72d8d2Sderaadt 
218da0c1eddSniklas 		if (ign_hold >= 0)
2191e72d8d2Sderaadt 		{
2201e72d8d2Sderaadt 		    for (i = ign_hold; i < ign_count; i++)
2211e72d8d2Sderaadt 			free (ign_list[i]);
222da0c1eddSniklas 		    ign_hold = -1;
2231e72d8d2Sderaadt 		}
2241e72d8d2Sderaadt 		s_ign_list = (char **) xmalloc (ign_count * sizeof (char *));
2251e72d8d2Sderaadt 		for (i = 0; i < ign_count; i++)
2261e72d8d2Sderaadt 		    s_ign_list[i] = ign_list[i];
2271e72d8d2Sderaadt 		s_ign_count = ign_count;
2281e72d8d2Sderaadt 		ign_count = 0;
2291e72d8d2Sderaadt 		ign_list[0] = NULL;
2301e72d8d2Sderaadt 		continue;
2311e72d8d2Sderaadt 	    }
2321e72d8d2Sderaadt 	}
2331e72d8d2Sderaadt 
2341e72d8d2Sderaadt 	/* If we have used up all the space, add some more */
2351e72d8d2Sderaadt 	if (ign_count >= ign_size)
2361e72d8d2Sderaadt 	{
2371e72d8d2Sderaadt 	    ign_size += IGN_GROW;
2381e72d8d2Sderaadt 	    ign_list = (char **) xrealloc ((char *) ign_list,
2391e72d8d2Sderaadt 					   (ign_size + 1) * sizeof (char *));
2401e72d8d2Sderaadt 	}
2411e72d8d2Sderaadt 
2421e72d8d2Sderaadt 	/* find the end of this token */
2439fe7c2c3Stholo 	for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++)
2441e72d8d2Sderaadt 	     /* do nothing */ ;
2451e72d8d2Sderaadt 
2461e72d8d2Sderaadt 	save = *mark;
2471e72d8d2Sderaadt 	*mark = '\0';
2481e72d8d2Sderaadt 
2491e72d8d2Sderaadt 	ign_list[ign_count++] = xstrdup (ign);
2501e72d8d2Sderaadt 	ign_list[ign_count] = NULL;
2511e72d8d2Sderaadt 
2521e72d8d2Sderaadt 	*mark = save;
2531e72d8d2Sderaadt 	if (save)
2541e72d8d2Sderaadt 	    ign = mark;
2551e72d8d2Sderaadt 	else
2561e72d8d2Sderaadt 	    ign = mark - 1;
2571e72d8d2Sderaadt     }
2581e72d8d2Sderaadt }
2591e72d8d2Sderaadt 
26072ecca6cStholo /* Set to 1 if filenames should be matched in a case-insensitive
26172ecca6cStholo    fashion.  Note that, contrary to the name and placement in ignore.c,
26272ecca6cStholo    this is no longer just for ignore patterns.  */
2639d9224ffStholo int ign_case;
2649d9224ffStholo 
2651e72d8d2Sderaadt /* Return 1 if the given filename should be ignored by update or import. */
2661e72d8d2Sderaadt int
ign_name(name)2671e72d8d2Sderaadt ign_name (name)
2681e72d8d2Sderaadt     char *name;
2691e72d8d2Sderaadt {
2701e72d8d2Sderaadt     char **cpp = ign_list;
2711e72d8d2Sderaadt 
2721e72d8d2Sderaadt     if (cpp == NULL)
2731e72d8d2Sderaadt 	return (0);
2741e72d8d2Sderaadt 
2759d9224ffStholo     if (ign_case)
2769d9224ffStholo     {
2779d9224ffStholo 	/* We do a case-insensitive match by calling fnmatch on copies of
2789d9224ffStholo 	   the pattern and the name which have been converted to
279066e68faStholo 	   lowercase.  FIXME: would be much cleaner to just unify this
280066e68faStholo 	   with the other case-insensitive fnmatch stuff (FOLD_FN_CHAR
281066e68faStholo 	   in lib/fnmatch.c; os2_fnmatch in emx/system.c).  */
2829d9224ffStholo 	char *name_lower;
2839d9224ffStholo 	char *pat_lower;
2849d9224ffStholo 	char *p;
2859d9224ffStholo 
2869d9224ffStholo 	name_lower = xstrdup (name);
2879d9224ffStholo 	for (p = name_lower; *p != '\0'; ++p)
2889d9224ffStholo 	    *p = tolower (*p);
2899d9224ffStholo 	while (*cpp)
2909d9224ffStholo 	{
2919d9224ffStholo 	    pat_lower = xstrdup (*cpp++);
2929d9224ffStholo 	    for (p = pat_lower; *p != '\0'; ++p)
2939d9224ffStholo 		*p = tolower (*p);
294066e68faStholo 	    if (CVS_FNMATCH (pat_lower, name_lower, 0) == 0)
2959d9224ffStholo 		goto matched;
2969d9224ffStholo 	    free (pat_lower);
2979d9224ffStholo 	}
2989d9224ffStholo 	free (name_lower);
2999d9224ffStholo 	return 0;
3009d9224ffStholo       matched:
3019d9224ffStholo 	free (name_lower);
3029d9224ffStholo 	free (pat_lower);
3039d9224ffStholo 	return 1;
3049d9224ffStholo     }
3059d9224ffStholo     else
3069d9224ffStholo     {
3071e72d8d2Sderaadt 	while (*cpp)
308066e68faStholo 	    if (CVS_FNMATCH (*cpp++, name, 0) == 0)
3099d9224ffStholo 		return 1;
3109d9224ffStholo 	return 0;
3111e72d8d2Sderaadt     }
3129d9224ffStholo }
3139d9224ffStholo 
314b2b690deStholo /* FIXME: This list of dirs to ignore stuff seems not to be used.
315b2b690deStholo    Really?  send_dirent_proc and update_dirent_proc both call
316b2b690deStholo    ignore_directory and do_module calls ign_dir_add.  No doubt could
317b2b690deStholo    use some documentation/testsuite work.  */
3181e72d8d2Sderaadt 
3191e72d8d2Sderaadt static char **dir_ign_list = NULL;
3201e72d8d2Sderaadt static int dir_ign_max = 0;
3211e72d8d2Sderaadt static int dir_ign_current = 0;
3221e72d8d2Sderaadt 
323b2b690deStholo /* Add a directory to list of dirs to ignore.  */
324b2b690deStholo void
ign_dir_add(name)325b2b690deStholo ign_dir_add (name)
3261e72d8d2Sderaadt     char *name;
3271e72d8d2Sderaadt {
328b2b690deStholo     /* Make sure we've got the space for the entry.  */
3291e72d8d2Sderaadt     if (dir_ign_current <= dir_ign_max)
3301e72d8d2Sderaadt     {
3311e72d8d2Sderaadt 	dir_ign_max += IGN_GROW;
332b2b690deStholo 	dir_ign_list =
333b2b690deStholo 	    (char **) xrealloc (dir_ign_list,
334b2b690deStholo 				(dir_ign_max + 1) * sizeof (char *));
3351e72d8d2Sderaadt     }
3361e72d8d2Sderaadt 
337bde78045Stholo     dir_ign_list[dir_ign_current++] = xstrdup (name);
3381e72d8d2Sderaadt }
3391e72d8d2Sderaadt 
3401e72d8d2Sderaadt 
341b2b690deStholo /* Return nonzero if NAME is part of the list of directories to ignore.  */
3421e72d8d2Sderaadt 
343b2b690deStholo int
ignore_directory(name)344b2b690deStholo ignore_directory (name)
3451e72d8d2Sderaadt     char *name;
3461e72d8d2Sderaadt {
3471e72d8d2Sderaadt     int i;
3481e72d8d2Sderaadt 
3491e72d8d2Sderaadt     if (!dir_ign_list)
3501e72d8d2Sderaadt 	return 0;
3511e72d8d2Sderaadt 
3521e72d8d2Sderaadt     i = dir_ign_current;
3531e72d8d2Sderaadt     while (i--)
3541e72d8d2Sderaadt     {
3551e72d8d2Sderaadt 	if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])) == 0)
3561e72d8d2Sderaadt 	    return 1;
3571e72d8d2Sderaadt     }
3581e72d8d2Sderaadt 
3591e72d8d2Sderaadt     return 0;
3601e72d8d2Sderaadt }
3619d9224ffStholo 
36213571821Stholo /*
3637e4d5a85Stholo  * Process the current directory, looking for files not in ILIST and
3647e4d5a85Stholo  * not on the global ignore list for this directory.  If we find one,
3657e4d5a85Stholo  * call PROC passing it the name of the file and the update dir.
3667e4d5a85Stholo  * ENTRIES is the entries list, which is used to identify known
3677e4d5a85Stholo  * directories.  ENTRIES may be NULL, in which case we assume that any
3687e4d5a85Stholo  * directory with a CVS administration directory is known.
36913571821Stholo  */
37013571821Stholo void
ignore_files(ilist,entries,update_dir,proc)3717e4d5a85Stholo ignore_files (ilist, entries, update_dir, proc)
37213571821Stholo     List *ilist;
3737e4d5a85Stholo     List *entries;
37413571821Stholo     char *update_dir;
37513571821Stholo     Ignore_proc proc;
37613571821Stholo {
3777e4d5a85Stholo     int subdirs;
37813571821Stholo     DIR *dirp;
37913571821Stholo     struct dirent *dp;
38013571821Stholo     struct stat sb;
38113571821Stholo     char *file;
38213571821Stholo     char *xdir;
38379822b2aStholo     List *files;
38479822b2aStholo     Node *p;
38513571821Stholo 
3867e4d5a85Stholo     /* Set SUBDIRS if we have subdirectory information in ENTRIES.  */
3877e4d5a85Stholo     if (entries == NULL)
3887e4d5a85Stholo 	subdirs = 0;
3897e4d5a85Stholo     else
3907e4d5a85Stholo     {
3917e4d5a85Stholo 	struct stickydirtag *sdtp;
3927e4d5a85Stholo 
3937e4d5a85Stholo 	sdtp = (struct stickydirtag *) entries->list->data;
3947e4d5a85Stholo 	subdirs = sdtp == NULL || sdtp->subdirs;
3957e4d5a85Stholo     }
3967e4d5a85Stholo 
39713571821Stholo     /* we get called with update_dir set to "." sometimes... strip it */
39813571821Stholo     if (strcmp (update_dir, ".") == 0)
39913571821Stholo 	xdir = "";
40013571821Stholo     else
40113571821Stholo 	xdir = update_dir;
40213571821Stholo 
4037e4d5a85Stholo     dirp = CVS_OPENDIR (".");
40413571821Stholo     if (dirp == NULL)
4059fe7c2c3Stholo     {
4069fe7c2c3Stholo 	error (0, errno, "cannot open current directory");
40713571821Stholo 	return;
4089fe7c2c3Stholo     }
40913571821Stholo 
41013571821Stholo     ign_add_file (CVSDOTIGNORE, 1);
41113571821Stholo     wrap_add_file (CVSDOTWRAPPER, 1);
41213571821Stholo 
41379822b2aStholo     /* Make a list for the files.  */
41479822b2aStholo     files = getlist ();
41579822b2aStholo 
41679822b2aStholo     while (errno = 0, (dp = CVS_READDIR (dirp)) != NULL)
41713571821Stholo     {
41813571821Stholo 	file = dp->d_name;
41913571821Stholo 	if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
42079822b2aStholo 	    continue;
42113571821Stholo 	if (findnode_fn (ilist, file) != NULL)
42279822b2aStholo 	    continue;
4237e4d5a85Stholo 	if (subdirs)
4247e4d5a85Stholo 	{
4257e4d5a85Stholo 	    Node *node;
4267e4d5a85Stholo 
4277e4d5a85Stholo 	    node = findnode_fn (entries, file);
4287e4d5a85Stholo 	    if (node != NULL
4297e4d5a85Stholo 		&& ((Entnode *) node->data)->type == ENT_SUBDIR)
4307e4d5a85Stholo 	    {
4317e4d5a85Stholo 		char *p;
4327e4d5a85Stholo 		int dir;
4337e4d5a85Stholo 
4347e4d5a85Stholo 		/* For consistency with past behaviour, we only ignore
4357e4d5a85Stholo 		   this directory if there is a CVS subdirectory.
4367e4d5a85Stholo 		   This will normally be the case, but the user may
4377e4d5a85Stholo 		   have messed up the working directory somehow.  */
4387e4d5a85Stholo 		p = xmalloc (strlen (file) + sizeof CVSADM + 10);
4397e4d5a85Stholo 		sprintf (p, "%s/%s", file, CVSADM);
4407e4d5a85Stholo 		dir = isdir (p);
4417e4d5a85Stholo 		free (p);
4427e4d5a85Stholo 		if (dir)
44379822b2aStholo 		    continue;
4447e4d5a85Stholo 	    }
4457e4d5a85Stholo 	}
4467e4d5a85Stholo 
4477e4d5a85Stholo 	/* We could be ignoring FIFOs and other files which are neither
4487e4d5a85Stholo 	   regular files nor directories here.  */
4497e4d5a85Stholo 	if (ign_name (file))
45079822b2aStholo 	    continue;
45113571821Stholo 
45213571821Stholo 	if (
45313571821Stholo #ifdef DT_DIR
45413571821Stholo 		dp->d_type != DT_UNKNOWN ||
45513571821Stholo #endif
45613571821Stholo 		lstat(file, &sb) != -1)
45713571821Stholo 	{
45813571821Stholo 
45913571821Stholo 	    if (
46013571821Stholo #ifdef DT_DIR
461bde78045Stholo 		dp->d_type == DT_DIR
462bde78045Stholo 		|| (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode))
463bde78045Stholo #else
464bde78045Stholo 		S_ISDIR (sb.st_mode)
46513571821Stholo #endif
466bde78045Stholo 		)
46713571821Stholo 	    {
4687e4d5a85Stholo 		if (! subdirs)
4697e4d5a85Stholo 		{
470ae5e8a9bStholo 		    char *temp;
47113571821Stholo 
472ae5e8a9bStholo 		    temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10);
47313571821Stholo 		    (void) sprintf (temp, "%s/%s", file, CVSADM);
47413571821Stholo 		    if (isdir (temp))
475ae5e8a9bStholo 		    {
476ae5e8a9bStholo 			free (temp);
47779822b2aStholo 			continue;
47813571821Stholo 		    }
479ae5e8a9bStholo 		    free (temp);
480ae5e8a9bStholo 		}
4817e4d5a85Stholo 	    }
48213571821Stholo #ifdef S_ISLNK
48313571821Stholo 	    else if (
48413571821Stholo #ifdef DT_DIR
485bde78045Stholo 		     dp->d_type == DT_LNK
486bde78045Stholo 		     || (dp->d_type == DT_UNKNOWN && S_ISLNK(sb.st_mode))
487bde78045Stholo #else
488bde78045Stholo 		     S_ISLNK (sb.st_mode)
48913571821Stholo #endif
490bde78045Stholo 		     )
49113571821Stholo 	    {
49279822b2aStholo 		continue;
49313571821Stholo 	    }
49413571821Stholo #endif
49513571821Stholo 	}
49613571821Stholo 
49779822b2aStholo 	p = getnode ();
49879822b2aStholo 	p->type = FILES;
49979822b2aStholo 	p->key = xstrdup (file);
50079822b2aStholo 	(void) addnode (files, p);
50113571821Stholo     }
5029fe7c2c3Stholo     if (errno != 0)
5039fe7c2c3Stholo 	error (0, errno, "error reading current directory");
50479822b2aStholo     (void) CVS_CLOSEDIR (dirp);
50579822b2aStholo 
50679822b2aStholo     sortlist (files, fsortcmp);
50779822b2aStholo     for (p = files->list->next; p != files->list; p = p->next)
50879822b2aStholo 	(*proc) (p->key, xdir);
50979822b2aStholo     dellist (&files);
51013571821Stholo }
511