]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/ignore.c
This commit was generated by cvs2svn to compensate for changes in r56944,
[FreeBSD/FreeBSD.git] / contrib / cvs / src / ignore.c
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
26 static char **ign_list;                 /* List of files to ignore in update
27                                          * and import */
28 static char **s_ign_list = NULL;
29 static int ign_count;                   /* Number of active entries */
30 static int s_ign_count = 0;
31 static int ign_size;                    /* This many slots available (plus
32                                          * one for a NULL) */
33 static int ign_hold = -1;               /* Index where first "temporary" item
34                                          * is held */
35
36 const 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.  */
45 int ign_inhibit_server;
46
47 /*
48  * To the "ignore list", add the hard-coded default ignored wildcards above,
49  * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
50  * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
51  * variable.
52  */
53 void
54 ign_setup ()
55 {
56     char *home_dir;
57     char *tmp;
58
59     ign_inhibit_server = 0;
60
61     /* Start with default list and special case */
62     tmp = xstrdup (ign_default);
63     ign_add (tmp, 0);
64     free (tmp);
65
66 #ifdef CLIENT_SUPPORT
67     /* The client handles another way, by (after it does its own ignore file
68        processing, and only if !ign_inhibit_server), letting the server
69        know about the files and letting it decide whether to ignore
70        them based on CVSROOOTADM_IGNORE.  */
71     if (!client_active)
72 #endif
73     {
74         char *file = xmalloc (strlen (CVSroot_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", CVSroot_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 = xmalloc (strlen (home_dir) + sizeof (CVSDOTIGNORE) + 10);
93         (void) sprintf (file, "%s/%s", home_dir, CVSDOTIGNORE);
94         ign_add_file (file, 0);
95         free (file);
96     }
97
98     /* Then add entries found in CVSIGNORE environment variable. */
99     ign_add (getenv (IGNORE_ENV), 0);
100
101     /* Later, add ignore entries found in -I arguments */
102 }
103
104 /*
105  * Open a file and read lines, feeding each line to a line parser. Arrange
106  * for keeping a temporary list of wildcards at the end, if the "hold"
107  * argument is set.
108  */
109 void
110 ign_add_file (file, hold)
111     char *file;
112     int hold;
113 {
114     FILE *fp;
115     char *line = NULL;
116     size_t line_allocated = 0;
117
118     /* restore the saved list (if any) */
119     if (s_ign_list != NULL)
120     {
121         int i;
122
123         for (i = 0; i < s_ign_count; i++)
124             ign_list[i] = s_ign_list[i];
125         ign_count = s_ign_count;
126         ign_list[ign_count] = NULL;
127
128         s_ign_count = 0;
129         free (s_ign_list);
130         s_ign_list = NULL;
131     }
132
133     /* is this a temporary ignore file? */
134     if (hold)
135     {
136         /* re-set if we had already done a temporary file */
137         if (ign_hold >= 0)
138         {
139             int i;
140
141             for (i = ign_hold; i < ign_count; i++)
142                 free (ign_list[i]);
143             ign_count = ign_hold;
144             ign_list[ign_count] = NULL;
145         }
146         else
147         {
148             ign_hold = ign_count;
149         }
150     }
151
152     /* load the file */
153     fp = CVS_FOPEN (file, "r");
154     if (fp == NULL)
155     {
156         if (! existence_error (errno))
157             error (0, errno, "cannot open %s", file);
158         return;
159     }
160     while (getline (&line, &line_allocated, fp) >= 0)
161         ign_add (line, hold);
162     if (ferror (fp))
163         error (0, errno, "cannot read %s", file);
164     if (fclose (fp) < 0)
165         error (0, errno, "cannot close %s", file);
166     free (line);
167 }
168
169 /* Parse a line of space-separated wildcards and add them to the list. */
170 void
171 ign_add (ign, hold)
172     char *ign;
173     int hold;
174 {
175     if (!ign || !*ign)
176         return;
177
178     for (; *ign; ign++)
179     {
180         char *mark;
181         char save;
182
183         /* ignore whitespace before the token */
184         if (isspace ((unsigned char) *ign))
185             continue;
186
187         /*
188          * if we find a single character !, we must re-set the ignore list
189          * (saving it if necessary).  We also catch * as a special case in a
190          * global ignore file as an optimization
191          */
192         if ((!*(ign+1) || isspace ((unsigned char) *(ign+1)))
193             && (*ign == '!' || *ign == '*'))
194         {
195             if (!hold)
196             {
197                 /* permanently reset the ignore list */
198                 int i;
199
200                 for (i = 0; i < ign_count; i++)
201                     free (ign_list[i]);
202                 ign_count = 0;
203                 ign_list[0] = NULL;
204
205                 /* if we are doing a '!', continue; otherwise add the '*' */
206                 if (*ign == '!')
207                 {
208                     ign_inhibit_server = 1;
209                     continue;
210                 }
211             }
212             else if (*ign == '!')
213             {
214                 /* temporarily reset the ignore list */
215                 int i;
216
217                 if (ign_hold >= 0)
218                 {
219                     for (i = ign_hold; i < ign_count; i++)
220                         free (ign_list[i]);
221                     ign_hold = -1;
222                 }
223                 s_ign_list = (char **) xmalloc (ign_count * sizeof (char *));
224                 for (i = 0; i < ign_count; i++)
225                     s_ign_list[i] = ign_list[i];
226                 s_ign_count = ign_count;
227                 ign_count = 0;
228                 ign_list[0] = NULL;
229                 continue;
230             }
231         }
232
233         /* If we have used up all the space, add some more */
234         if (ign_count >= ign_size)
235         {
236             ign_size += IGN_GROW;
237             ign_list = (char **) xrealloc ((char *) ign_list,
238                                            (ign_size + 1) * sizeof (char *));
239         }
240
241         /* find the end of this token */
242         for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++)
243              /* do nothing */ ;
244
245         save = *mark;
246         *mark = '\0';
247
248         ign_list[ign_count++] = xstrdup (ign);
249         ign_list[ign_count] = NULL;
250
251         *mark = save;
252         if (save)
253             ign = mark;
254         else
255             ign = mark - 1;
256     }
257 }
258
259 /* Set to 1 if filenames should be matched in a case-insensitive
260    fashion.  Note that, contrary to the name and placement in ignore.c,
261    this is no longer just for ignore patterns.  */
262 int ign_case;
263
264 /* Return 1 if the given filename should be ignored by update or import. */
265 int
266 ign_name (name)
267     char *name;
268 {
269     char **cpp = ign_list;
270
271     if (cpp == NULL)
272         return (0);
273
274     if (ign_case)
275     {
276         /* We do a case-insensitive match by calling fnmatch on copies of
277            the pattern and the name which have been converted to
278            lowercase.  FIXME: would be much cleaner to just unify this
279            with the other case-insensitive fnmatch stuff (FOLD_FN_CHAR
280            in lib/fnmatch.c; os2_fnmatch in emx/system.c).  */
281         char *name_lower;
282         char *pat_lower;
283         char *p;
284
285         name_lower = xstrdup (name);
286         for (p = name_lower; *p != '\0'; ++p)
287             *p = tolower (*p);
288         while (*cpp)
289         {
290             pat_lower = xstrdup (*cpp++);
291             for (p = pat_lower; *p != '\0'; ++p)
292                 *p = tolower (*p);
293             if (CVS_FNMATCH (pat_lower, name_lower, 0) == 0)
294                 goto matched;
295             free (pat_lower);
296         }
297         free (name_lower);
298         return 0;
299       matched:
300         free (name_lower);
301         free (pat_lower);
302         return 1;
303     }
304     else
305     {
306         while (*cpp)
307             if (CVS_FNMATCH (*cpp++, name, 0) == 0)
308                 return 1;
309         return 0;
310     }
311 }
312 \f
313 /* FIXME: This list of dirs to ignore stuff seems not to be used.
314    Really?  send_dirent_proc and update_dirent_proc both call
315    ignore_directory and do_module calls ign_dir_add.  No doubt could
316    use some documentation/testsuite work.  */
317
318 static char **dir_ign_list = NULL;
319 static int dir_ign_max = 0;
320 static int dir_ign_current = 0;
321
322 /* Add a directory to list of dirs to ignore.  */
323 void
324 ign_dir_add (name)
325     char *name;
326 {
327     /* Make sure we've got the space for the entry.  */
328     if (dir_ign_current <= dir_ign_max)
329     {
330         dir_ign_max += IGN_GROW;
331         dir_ign_list =
332             (char **) xrealloc (dir_ign_list,
333                                 (dir_ign_max + 1) * sizeof (char *));
334     }
335
336     dir_ign_list[dir_ign_current] = name;
337
338     dir_ign_current += 1 ;
339 }
340
341
342 /* Return nonzero if NAME is part of the list of directories to ignore.  */
343
344 int
345 ignore_directory (name)
346     char *name;
347 {
348     int i;
349
350     if (!dir_ign_list)
351         return 0;
352
353     i = dir_ign_current;
354     while (i--)
355     {
356         if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])) == 0)
357             return 1;
358     }
359
360     return 0;
361 }
362 \f
363 /*
364  * Process the current directory, looking for files not in ILIST and
365  * not on the global ignore list for this directory.  If we find one,
366  * call PROC passing it the name of the file and the update dir.
367  * ENTRIES is the entries list, which is used to identify known
368  * directories.  ENTRIES may be NULL, in which case we assume that any
369  * directory with a CVS administration directory is known.
370  */
371 void
372 ignore_files (ilist, entries, update_dir, proc)
373     List *ilist;
374     List *entries;
375     char *update_dir;
376     Ignore_proc proc;
377 {
378     int subdirs;
379     DIR *dirp;
380     struct dirent *dp;
381     struct stat sb;
382     char *file;
383     char *xdir;
384
385     /* Set SUBDIRS if we have subdirectory information in ENTRIES.  */
386     if (entries == NULL)
387         subdirs = 0;
388     else
389     {
390         struct stickydirtag *sdtp;
391
392         sdtp = (struct stickydirtag *) entries->list->data;
393         subdirs = sdtp == NULL || sdtp->subdirs;
394     }
395
396     /* we get called with update_dir set to "." sometimes... strip it */
397     if (strcmp (update_dir, ".") == 0)
398         xdir = "";
399     else
400         xdir = update_dir;
401
402     dirp = CVS_OPENDIR (".");
403     if (dirp == NULL)
404     {
405         error (0, errno, "cannot open current directory");
406         return;
407     }
408
409     ign_add_file (CVSDOTIGNORE, 1);
410     wrap_add_file (CVSDOTWRAPPER, 1);
411
412     errno = 0;
413     while ((dp = readdir (dirp)) != NULL)
414     {
415         file = dp->d_name;
416         if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
417             continue;
418         if (findnode_fn (ilist, file) != NULL)
419             continue;
420         if (subdirs)
421         {
422             Node *node;
423
424             node = findnode_fn (entries, file);
425             if (node != NULL
426                 && ((Entnode *) node->data)->type == ENT_SUBDIR)
427             {
428                 char *p;
429                 int dir;
430
431                 /* For consistency with past behaviour, we only ignore
432                    this directory if there is a CVS subdirectory.
433                    This will normally be the case, but the user may
434                    have messed up the working directory somehow.  */
435                 p = xmalloc (strlen (file) + sizeof CVSADM + 10);
436                 sprintf (p, "%s/%s", file, CVSADM);
437                 dir = isdir (p);
438                 free (p);
439                 if (dir)
440                     continue;
441             }
442         }
443
444         /* We could be ignoring FIFOs and other files which are neither
445            regular files nor directories here.  */
446         if (ign_name (file))
447             continue;
448
449         if (
450 #ifdef DT_DIR
451                 dp->d_type != DT_UNKNOWN ||
452 #endif
453                 lstat(file, &sb) != -1) 
454         {
455
456             if (
457 #ifdef DT_DIR
458                 dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN &&
459 #endif
460                 S_ISDIR(sb.st_mode))
461             {
462                 if (! subdirs)
463                 {
464                     char *temp;
465
466                     temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10);
467                     (void) sprintf (temp, "%s/%s", file, CVSADM);
468                     if (isdir (temp))
469                     {
470                         free (temp);
471                         continue;
472                     }
473                     free (temp);
474                 }
475             }
476 #ifdef S_ISLNK
477             else if (
478 #ifdef DT_DIR
479                 dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && 
480 #endif
481                 S_ISLNK(sb.st_mode))
482             {
483                 continue;
484             }
485 #endif
486         }
487
488         (*proc) (file, xdir);
489         errno = 0;
490     }
491     if (errno != 0)
492         error (0, errno, "error reading current directory");
493     (void) closedir (dirp);
494 }