PageRenderTime 67ms CodeModel.GetById 11ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/cvs/src/ignore.c

https://bitbucket.org/freebsd/freebsd-head/
C | 503 lines | 324 code | 75 blank | 104 comment | 93 complexity | 585e0787fde7c5c600e87d38f5f44ae2 MD5 | raw file
  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
 18/*
 19 * Ignore file section.
 20 * 
 21 *	"!" may be included any time to reset the list (i.e. ignore nothing);
 22 *	"*" may be specified to ignore everything.  It stays as the first
 23 *	    element forever, unless a "!" clears it out.
 24 */
 25
 26static char **ign_list;			/* List of files to ignore in update
 27					 * and import */
 28static char **s_ign_list = NULL;
 29static int ign_count;			/* Number of active entries */
 30static int s_ign_count = 0;
 31static int ign_size;			/* This many slots available (plus
 32					 * one for a NULL) */
 33static int ign_hold = -1;		/* Index where first "temporary" item
 34					 * is held */
 35
 36const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\
 37 .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\
 38 *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$";
 39
 40#define IGN_GROW 16			/* grow the list by 16 elements at a
 41					 * time */
 42
 43/* Nonzero if we have encountered an -I ! directive, which means one should
 44   no longer ask the server about what is in CVSROOTADM_IGNORE.  */
 45int ign_inhibit_server;
 46
 47
 48
 49/*
 50 * To the "ignore list", add the hard-coded default ignored wildcards above,
 51 * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
 52 * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
 53 * variable.
 54 */
 55void
 56ign_setup ()
 57{
 58    char *home_dir;
 59    char *tmp;
 60
 61    ign_inhibit_server = 0;
 62
 63    /* Start with default list and special case */
 64    tmp = xstrdup (ign_default);
 65    ign_add (tmp, 0);
 66    free (tmp);
 67
 68    /* The client handles another way, by (after it does its own ignore file
 69       processing, and only if !ign_inhibit_server), letting the server
 70       know about the files and letting it decide whether to ignore
 71       them based on CVSROOOTADM_IGNORE.  */
 72    if (!current_parsed_root->isremote)
 73    {
 74	char *file = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM)
 75			      + sizeof (CVSROOTADM_IGNORE) + 10);
 76	/* Then add entries found in repository, if it exists */
 77	(void) sprintf (file, "%s/%s/%s", current_parsed_root->directory,
 78			CVSROOTADM, CVSROOTADM_IGNORE);
 79	ign_add_file (file, 0);
 80	free (file);
 81    }
 82
 83    /* Then add entries found in home dir, (if user has one) and file exists */
 84    home_dir = get_homedir ();
 85    /* If we can't find a home directory, ignore ~/.cvsignore.  This may
 86       make tracking down problems a bit of a pain, but on the other
 87       hand it might be obnoxious to complain when CVS will function
 88       just fine without .cvsignore (and many users won't even know what
 89       .cvsignore is).  */
 90    if (home_dir)
 91    {
 92	char *file = strcat_filename_onto_homedir (home_dir, CVSDOTIGNORE);
 93	ign_add_file (file, 0);
 94	free (file);
 95    }
 96
 97    /* Then add entries found in CVSIGNORE environment variable. */
 98    ign_add (getenv (IGNORE_ENV), 0);
 99
100    /* Later, add ignore entries found in -I arguments */
101}
102
103
104
105/*
106 * Open a file and read lines, feeding each line to a line parser. Arrange
107 * for keeping a temporary list of wildcards at the end, if the "hold"
108 * argument is set.
109 */
110void
111ign_add_file (file, hold)
112    char *file;
113    int hold;
114{
115    FILE *fp;
116    char *line = NULL;
117    size_t line_allocated = 0;
118
119    /* restore the saved list (if any) */
120    if (s_ign_list != NULL)
121    {
122	int i;
123
124	for (i = 0; i < s_ign_count; i++)
125	    ign_list[i] = s_ign_list[i];
126	ign_count = s_ign_count;
127	ign_list[ign_count] = NULL;
128
129	s_ign_count = 0;
130	free (s_ign_list);
131	s_ign_list = NULL;
132    }
133
134    /* is this a temporary ignore file? */
135    if (hold)
136    {
137	/* re-set if we had already done a temporary file */
138	if (ign_hold >= 0)
139	{
140	    int i;
141
142	    for (i = ign_hold; i < ign_count; i++)
143		free (ign_list[i]);
144	    ign_count = ign_hold;
145	    ign_list[ign_count] = NULL;
146	}
147	else
148	{
149	    ign_hold = ign_count;
150	}
151    }
152
153    /* load the file */
154    fp = CVS_FOPEN (file, "r");
155    if (fp == NULL)
156    {
157	if (! existence_error (errno))
158	    error (0, errno, "cannot open %s", file);
159	return;
160    }
161    while (getline (&line, &line_allocated, fp) >= 0)
162	ign_add (line, hold);
163    if (ferror (fp))
164	error (0, errno, "cannot read %s", file);
165    if (fclose (fp) < 0)
166	error (0, errno, "cannot close %s", file);
167    free (line);
168}
169
170
171
172/* Parse a line of space-separated wildcards and add them to the list. */
173void
174ign_add (ign, hold)
175    char *ign;
176    int hold;
177{
178    if (!ign || !*ign)
179	return;
180
181    for (; *ign; ign++)
182    {
183	char *mark;
184	char save;
185
186	/* ignore whitespace before the token */
187	if (isspace ((unsigned char) *ign))
188	    continue;
189
190	/* If we have used up all the space, add some more.  Do this before
191	   processing `!', since an "empty" list still contains the `CVS'
192	   entry.  */
193	if (ign_count >= ign_size)
194	{
195	    ign_size += IGN_GROW;
196	    ign_list = (char **) xrealloc ((char *) ign_list,
197					   (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 = xmalloc (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 */
288int
289ign_name (name)
290    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
311static char **dir_ign_list = NULL;
312static int dir_ign_max = 0;
313static int dir_ign_current = 0;
314
315/* Add a directory to list of dirs to ignore.  */
316void
317ign_dir_add (name)
318    char *name;
319{
320    /* Make sure we've got the space for the entry.  */
321    if (dir_ign_current <= dir_ign_max)
322    {
323	dir_ign_max += IGN_GROW;
324	dir_ign_list =
325	    (char **) xrealloc (dir_ign_list,
326				(dir_ign_max + 1) * sizeof (char *));
327    }
328
329    dir_ign_list[dir_ign_current++] = xstrdup (name);
330}
331
332
333/* Return nonzero if NAME is part of the list of directories to ignore.  */
334
335int
336ignore_directory (name)
337    const char *name;
338{
339    int i;
340
341    if (!dir_ign_list)
342	return 0;
343
344    i = dir_ign_current;
345    while (i--)
346    {
347	if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])+1) == 0)
348	    return 1;
349    }
350
351    return 0;
352}
353
354
355
356/*
357 * Process the current directory, looking for files not in ILIST and
358 * not on the global ignore list for this directory.  If we find one,
359 * call PROC passing it the name of the file and the update dir.
360 * ENTRIES is the entries list, which is used to identify known
361 * directories.  ENTRIES may be NULL, in which case we assume that any
362 * directory with a CVS administration directory is known.
363 */
364void
365ignore_files (ilist, entries, update_dir, proc)
366    List *ilist;
367    List *entries;
368    const char *update_dir;
369    Ignore_proc proc;
370{
371    int subdirs;
372    DIR *dirp;
373    struct dirent *dp;
374    struct stat sb;
375    char *file;
376    const char *xdir;
377    List *files;
378    Node *p;
379
380    /* Set SUBDIRS if we have subdirectory information in ENTRIES.  */
381    if (entries == NULL)
382	subdirs = 0;
383    else
384    {
385	struct stickydirtag *sdtp = entries->list->data;
386
387	subdirs = sdtp == NULL || sdtp->subdirs;
388    }
389
390    /* we get called with update_dir set to "." sometimes... strip it */
391    if (strcmp (update_dir, ".") == 0)
392	xdir = "";
393    else
394	xdir = update_dir;
395
396    dirp = CVS_OPENDIR (".");
397    if (dirp == NULL)
398    {
399	error (0, errno, "cannot open current directory");
400	return;
401    }
402
403    ign_add_file (CVSDOTIGNORE, 1);
404    wrap_add_file (CVSDOTWRAPPER, 1);
405
406    /* Make a list for the files.  */
407    files = getlist ();
408
409    while (errno = 0, (dp = CVS_READDIR (dirp)) != NULL)
410    {
411	file = dp->d_name;
412	if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
413	    continue;
414	if (findnode_fn (ilist, file) != NULL)
415	    continue;
416	if (subdirs)
417	{
418	    Node *node;
419
420	    node = findnode_fn (entries, file);
421	    if (node != NULL
422		&& ((Entnode *) node->data)->type == ENT_SUBDIR)
423	    {
424		char *p;
425		int dir;
426
427		/* For consistency with past behaviour, we only ignore
428		   this directory if there is a CVS subdirectory.
429		   This will normally be the case, but the user may
430		   have messed up the working directory somehow.  */
431		p = xmalloc (strlen (file) + sizeof CVSADM + 10);
432		sprintf (p, "%s/%s", file, CVSADM);
433		dir = isdir (p);
434		free (p);
435		if (dir)
436		    continue;
437	    }
438	}
439
440	/* We could be ignoring FIFOs and other files which are neither
441	   regular files nor directories here.  */
442	if (ign_name (file))
443	    continue;
444
445	if (
446#ifdef DT_DIR
447		dp->d_type != DT_UNKNOWN ||
448#endif
449		CVS_LSTAT (file, &sb) != -1) 
450	{
451
452	    if (
453#ifdef DT_DIR
454		dp->d_type == DT_DIR
455		|| (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode))
456#else
457		S_ISDIR (sb.st_mode)
458#endif
459		)
460	    {
461		if (! subdirs)
462		{
463		    char *temp;
464
465		    temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10);
466		    (void) sprintf (temp, "%s/%s", file, CVSADM);
467		    if (isdir (temp))
468		    {
469			free (temp);
470			continue;
471		    }
472		    free (temp);
473		}
474	    }
475#ifdef S_ISLNK
476	    else if (
477#ifdef DT_DIR
478		     dp->d_type == DT_LNK
479		     || (dp->d_type == DT_UNKNOWN && S_ISLNK(sb.st_mode))
480#else
481		     S_ISLNK (sb.st_mode)
482#endif
483		     )
484	    {
485		continue;
486	    }
487#endif
488	}
489
490	p = getnode ();
491	p->type = FILES;
492	p->key = xstrdup (file);
493	(void) addnode (files, p);
494    }
495    if (errno != 0)
496	error (0, errno, "error reading current directory");
497    (void) CVS_CLOSEDIR (dirp);
498
499    sortlist (files, fsortcmp);
500    for (p = files->list->next; p != files->list; p = p->next)
501	(*proc) (p->key, xdir);
502    dellist (&files);
503}