]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/ignore.c
Import cvs-1.9.23 as at 19980123. There are a number of really nice
[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 (home_dir)
86     {
87         char *file = xmalloc (strlen (home_dir) + sizeof (CVSDOTIGNORE) + 10);
88         (void) sprintf (file, "%s/%s", home_dir, CVSDOTIGNORE);
89         ign_add_file (file, 0);
90         free (file);
91     }
92
93     /* Then add entries found in CVSIGNORE environment variable. */
94     ign_add (getenv (IGNORE_ENV), 0);
95
96     /* Later, add ignore entries found in -I arguments */
97 }
98
99 /*
100  * Open a file and read lines, feeding each line to a line parser. Arrange
101  * for keeping a temporary list of wildcards at the end, if the "hold"
102  * argument is set.
103  */
104 void
105 ign_add_file (file, hold)
106     char *file;
107     int hold;
108 {
109     FILE *fp;
110     char *line = NULL;
111     size_t line_allocated = 0;
112
113     /* restore the saved list (if any) */
114     if (s_ign_list != NULL)
115     {
116         int i;
117
118         for (i = 0; i < s_ign_count; i++)
119             ign_list[i] = s_ign_list[i];
120         ign_count = s_ign_count;
121         ign_list[ign_count] = NULL;
122
123         s_ign_count = 0;
124         free (s_ign_list);
125         s_ign_list = NULL;
126     }
127
128     /* is this a temporary ignore file? */
129     if (hold)
130     {
131         /* re-set if we had already done a temporary file */
132         if (ign_hold >= 0)
133         {
134             int i;
135
136             for (i = ign_hold; i < ign_count; i++)
137                 free (ign_list[i]);
138             ign_count = ign_hold;
139             ign_list[ign_count] = NULL;
140         }
141         else
142         {
143             ign_hold = ign_count;
144         }
145     }
146
147     /* load the file */
148     fp = CVS_FOPEN (file, "r");
149     if (fp == NULL)
150     {
151         if (! existence_error (errno))
152             error (0, errno, "cannot open %s", file);
153         return;
154     }
155     while (getline (&line, &line_allocated, fp) >= 0)
156         ign_add (line, hold);
157     if (ferror (fp))
158         error (0, errno, "cannot read %s", file);
159     if (fclose (fp) < 0)
160         error (0, errno, "cannot close %s", file);
161     free (line);
162 }
163
164 /* Parse a line of space-separated wildcards and add them to the list. */
165 void
166 ign_add (ign, hold)
167     char *ign;
168     int hold;
169 {
170     if (!ign || !*ign)
171         return;
172
173     for (; *ign; ign++)
174     {
175         char *mark;
176         char save;
177
178         /* ignore whitespace before the token */
179         if (isspace (*ign))
180             continue;
181
182         /*
183          * if we find a single character !, we must re-set the ignore list
184          * (saving it if necessary).  We also catch * as a special case in a
185          * global ignore file as an optimization
186          */
187         if ((!*(ign+1) || isspace (*(ign+1))) && (*ign == '!' || *ign == '*'))
188         {
189             if (!hold)
190             {
191                 /* permanently reset the ignore list */
192                 int i;
193
194                 for (i = 0; i < ign_count; i++)
195                     free (ign_list[i]);
196                 ign_count = 0;
197                 ign_list[0] = NULL;
198
199                 /* if we are doing a '!', continue; otherwise add the '*' */
200                 if (*ign == '!')
201                 {
202                     ign_inhibit_server = 1;
203                     continue;
204                 }
205             }
206             else if (*ign == '!')
207             {
208                 /* temporarily reset the ignore list */
209                 int i;
210
211                 if (ign_hold >= 0)
212                 {
213                     for (i = ign_hold; i < ign_count; i++)
214                         free (ign_list[i]);
215                     ign_hold = -1;
216                 }
217                 s_ign_list = (char **) xmalloc (ign_count * sizeof (char *));
218                 for (i = 0; i < ign_count; i++)
219                     s_ign_list[i] = ign_list[i];
220                 s_ign_count = ign_count;
221                 ign_count = 0;
222                 ign_list[0] = NULL;
223                 continue;
224             }
225         }
226
227         /* If we have used up all the space, add some more */
228         if (ign_count >= ign_size)
229         {
230             ign_size += IGN_GROW;
231             ign_list = (char **) xrealloc ((char *) ign_list,
232                                            (ign_size + 1) * sizeof (char *));
233         }
234
235         /* find the end of this token */
236         for (mark = ign; *mark && !isspace (*mark); mark++)
237              /* do nothing */ ;
238
239         save = *mark;
240         *mark = '\0';
241
242         ign_list[ign_count++] = xstrdup (ign);
243         ign_list[ign_count] = NULL;
244
245         *mark = save;
246         if (save)
247             ign = mark;
248         else
249             ign = mark - 1;
250     }
251 }
252
253 /* Set to 1 if filenames should be matched in a case-insensitive
254    fashion.  Note that, contrary to the name and placement in ignore.c,
255    this is no longer just for ignore patterns.  */
256 int ign_case;
257
258 /* Return 1 if the given filename should be ignored by update or import. */
259 int
260 ign_name (name)
261     char *name;
262 {
263     char **cpp = ign_list;
264
265     if (cpp == NULL)
266         return (0);
267
268     if (ign_case)
269     {
270         /* We do a case-insensitive match by calling fnmatch on copies of
271            the pattern and the name which have been converted to
272            lowercase.  FIXME: would be much cleaner to just unify this
273            with the other case-insensitive fnmatch stuff (FOLD_FN_CHAR
274            in lib/fnmatch.c; os2_fnmatch in emx/system.c).  */
275         char *name_lower;
276         char *pat_lower;
277         char *p;
278
279         name_lower = xstrdup (name);
280         for (p = name_lower; *p != '\0'; ++p)
281             *p = tolower (*p);
282         while (*cpp)
283         {
284             pat_lower = xstrdup (*cpp++);
285             for (p = pat_lower; *p != '\0'; ++p)
286                 *p = tolower (*p);
287             if (CVS_FNMATCH (pat_lower, name_lower, 0) == 0)
288                 goto matched;
289             free (pat_lower);
290         }
291         free (name_lower);
292         return 0;
293       matched:
294         free (name_lower);
295         free (pat_lower);
296         return 1;
297     }
298     else
299     {
300         while (*cpp)
301             if (CVS_FNMATCH (*cpp++, name, 0) == 0)
302                 return 1;
303         return 0;
304     }
305 }
306 \f
307 /* FIXME: This list of dirs to ignore stuff seems not to be used.
308    Really?  send_dirent_proc and update_dirent_proc both call
309    ignore_directory and do_module calls ign_dir_add.  No doubt could
310    use some documentation/testsuite work.  */
311
312 static char **dir_ign_list = NULL;
313 static int dir_ign_max = 0;
314 static int dir_ign_current = 0;
315
316 /* Add a directory to list of dirs to ignore.  */
317 void
318 ign_dir_add (name)
319     char *name;
320 {
321     /* Make sure we've got the space for the entry.  */
322     if (dir_ign_current <= dir_ign_max)
323     {
324         dir_ign_max += IGN_GROW;
325         dir_ign_list =
326             (char **) xrealloc (dir_ign_list,
327                                 (dir_ign_max + 1) * sizeof (char *));
328     }
329
330     dir_ign_list[dir_ign_current] = name;
331
332     dir_ign_current += 1 ;
333 }
334
335
336 /* Return nonzero if NAME is part of the list of directories to ignore.  */
337
338 int
339 ignore_directory (name)
340     char *name;
341 {
342     int i;
343
344     if (!dir_ign_list)
345         return 0;
346
347     i = dir_ign_current;
348     while (i--)
349     {
350         if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])) == 0)
351             return 1;
352     }
353
354     return 0;
355 }
356 \f
357 /*
358  * Process the current directory, looking for files not in ILIST and
359  * not on the global ignore list for this directory.  If we find one,
360  * call PROC passing it the name of the file and the update dir.
361  * ENTRIES is the entries list, which is used to identify known
362  * directories.  ENTRIES may be NULL, in which case we assume that any
363  * directory with a CVS administration directory is known.
364  */
365 void
366 ignore_files (ilist, entries, update_dir, proc)
367     List *ilist;
368     List *entries;
369     char *update_dir;
370     Ignore_proc proc;
371 {
372     int subdirs;
373     DIR *dirp;
374     struct dirent *dp;
375     struct stat sb;
376     char *file;
377     char *xdir;
378
379     /* Set SUBDIRS if we have subdirectory information in ENTRIES.  */
380     if (entries == NULL)
381         subdirs = 0;
382     else
383     {
384         struct stickydirtag *sdtp;
385
386         sdtp = (struct stickydirtag *) entries->list->data;
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         return;
399
400     ign_add_file (CVSDOTIGNORE, 1);
401     wrap_add_file (CVSDOTWRAPPER, 1);
402
403     while ((dp = 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 = xmalloc (strlen (file) + sizeof CVSADM + 10);
426                 sprintf (p, "%s/%s", file, CVSADM);
427                 dir = isdir (p);
428                 free (p);
429                 if (dir)
430                     continue;
431             }
432         }
433
434         /* We could be ignoring FIFOs and other files which are neither
435            regular files nor directories here.  */
436         if (ign_name (file))
437             continue;
438
439         if (
440 #ifdef DT_DIR
441                 dp->d_type != DT_UNKNOWN ||
442 #endif
443                 lstat(file, &sb) != -1) 
444         {
445
446             if (
447 #ifdef DT_DIR
448                 dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN &&
449 #endif
450                 S_ISDIR(sb.st_mode))
451             {
452                 if (! subdirs)
453                 {
454                     char *temp;
455
456                     temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10);
457                     (void) sprintf (temp, "%s/%s", file, CVSADM);
458                     if (isdir (temp))
459                     {
460                         free (temp);
461                         continue;
462                     }
463                     free (temp);
464                 }
465             }
466 #ifdef S_ISLNK
467             else if (
468 #ifdef DT_DIR
469                 dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && 
470 #endif
471                 S_ISLNK(sb.st_mode))
472             {
473                 continue;
474             }
475 #endif
476         }
477
478         (*proc) (file, xdir);
479     }
480     (void) closedir (dirp);
481 }