]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/ignore.c
Import of slightly trimmed cvs-1.8 distribution. Generated files
[FreeBSD/FreeBSD.git] / contrib / cvs / src / ignore.c
1 /*
2  * .cvsignore file support contributed by David G. Grubbs <dgg@odi.com>
3  */
4
5 #include "cvs.h"
6
7 /*
8  * Ignore file section.
9  * 
10  *      "!" may be included any time to reset the list (i.e. ignore nothing);
11  *      "*" may be specified to ignore everything.  It stays as the first
12  *          element forever, unless a "!" clears it out.
13  */
14
15 static char **ign_list;                 /* List of files to ignore in update
16                                          * and import */
17 static char **s_ign_list = NULL;
18 static int ign_count;                   /* Number of active entries */
19 static int s_ign_count = 0;
20 static int ign_size;                    /* This many slots available (plus
21                                          * one for a NULL) */
22 static int ign_hold;                    /* Index where first "temporary" item
23                                          * is held */
24
25 const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\
26  .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\
27  *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$";
28
29 #define IGN_GROW 16                     /* grow the list by 16 elements at a
30                                          * time */
31
32 /* Nonzero if we have encountered an -I ! directive, which means one should
33    no longer ask the server about what is in CVSROOTADM_IGNORE.  */
34 int ign_inhibit_server;
35
36 /*
37  * To the "ignore list", add the hard-coded default ignored wildcards above,
38  * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
39  * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
40  * variable.
41  */
42 void
43 ign_setup ()
44 {
45     struct passwd *pw;
46     char file[PATH_MAX];
47     char *tmp;
48
49     ign_inhibit_server = 0;
50
51     /* Start with default list and special case */
52     tmp = xstrdup (ign_default);
53     ign_add (tmp, 0);
54     free (tmp);
55
56 #ifdef CLIENT_SUPPORT
57     /* The client handles another way, by (after it does its own ignore file
58        processing, and only if !ign_inhibit_server), letting the server
59        know about the files and letting it decide whether to ignore
60        them based on CVSROOOTADM_IGNORE.  */
61     if (!client_active)
62 #endif
63     {
64         /* Then add entries found in repository, if it exists */
65         (void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM,
66                         CVSROOTADM_IGNORE);
67         ign_add_file (file, 0);
68     }
69
70     /* Then add entries found in home dir, (if user has one) and file exists */
71     if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir)
72     {
73         (void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTIGNORE);
74         ign_add_file (file, 0);
75     }
76
77     /* Then add entries found in CVSIGNORE environment variable. */
78     ign_add (getenv (IGNORE_ENV), 0);
79
80     /* Later, add ignore entries found in -I arguments */
81 }
82
83 /*
84  * Open a file and read lines, feeding each line to a line parser. Arrange
85  * for keeping a temporary list of wildcards at the end, if the "hold"
86  * argument is set.
87  */
88 void
89 ign_add_file (file, hold)
90     char *file;
91     int hold;
92 {
93     FILE *fp;
94     char line[1024];
95
96     /* restore the saved list (if any) */
97     if (s_ign_list != NULL)
98     {
99         int i;
100
101         for (i = 0; i < s_ign_count; i++)
102             ign_list[i] = s_ign_list[i];
103         ign_count = s_ign_count;
104         ign_list[ign_count] = NULL;
105
106         s_ign_count = 0;
107         free (s_ign_list);
108         s_ign_list = NULL;
109     }
110
111     /* is this a temporary ignore file? */
112     if (hold)
113     {
114         /* re-set if we had already done a temporary file */
115         if (ign_hold)
116         {
117             int i;
118
119             for (i = ign_hold; i < ign_count; i++)
120                 free (ign_list[i]);
121             ign_count = ign_hold;
122             ign_list[ign_count] = NULL;
123         }
124         else
125         {
126             ign_hold = ign_count;
127         }
128     }
129
130     /* load the file */
131     fp = fopen (file, "r");
132     if (fp == NULL)
133     {
134         if (! existence_error (errno))
135             error (0, errno, "cannot open %s", file);
136         return;
137     }
138     while (fgets (line, sizeof (line), fp))
139         ign_add (line, hold);
140     if (fclose (fp) < 0)
141         error (0, errno, "cannot close %s", file);
142 }
143
144 /* Parse a line of space-separated wildcards and add them to the list. */
145 void
146 ign_add (ign, hold)
147     char *ign;
148     int hold;
149 {
150     if (!ign || !*ign)
151         return;
152
153     for (; *ign; ign++)
154     {
155         char *mark;
156         char save;
157
158         /* ignore whitespace before the token */
159         if (isspace (*ign))
160             continue;
161
162         /*
163          * if we find a single character !, we must re-set the ignore list
164          * (saving it if necessary).  We also catch * as a special case in a
165          * global ignore file as an optimization
166          */
167         if ((!*(ign+1) || isspace (*(ign+1))) && (*ign == '!' || *ign == '*'))
168         {
169             if (!hold)
170             {
171                 /* permanently reset the ignore list */
172                 int i;
173
174                 for (i = 0; i < ign_count; i++)
175                     free (ign_list[i]);
176                 ign_count = 0;
177                 ign_list[0] = NULL;
178
179                 /* if we are doing a '!', continue; otherwise add the '*' */
180                 if (*ign == '!')
181                 {
182                     ign_inhibit_server = 1;
183                     continue;
184                 }
185             }
186             else if (*ign == '!')
187             {
188                 /* temporarily reset the ignore list */
189                 int i;
190
191                 if (ign_hold)
192                 {
193                     for (i = ign_hold; i < ign_count; i++)
194                         free (ign_list[i]);
195                     ign_hold = 0;
196                 }
197                 s_ign_list = (char **) xmalloc (ign_count * sizeof (char *));
198                 for (i = 0; i < ign_count; i++)
199                     s_ign_list[i] = ign_list[i];
200                 s_ign_count = ign_count;
201                 ign_count = 0;
202                 ign_list[0] = NULL;
203                 continue;
204             }
205         }
206
207         /* If we have used up all the space, add some more */
208         if (ign_count >= ign_size)
209         {
210             ign_size += IGN_GROW;
211             ign_list = (char **) xrealloc ((char *) ign_list,
212                                            (ign_size + 1) * sizeof (char *));
213         }
214
215         /* find the end of this token */
216         for (mark = ign; *mark && !isspace (*mark); mark++)
217              /* do nothing */ ;
218
219         save = *mark;
220         *mark = '\0';
221
222         ign_list[ign_count++] = xstrdup (ign);
223         ign_list[ign_count] = NULL;
224
225         *mark = save;
226         if (save)
227             ign = mark;
228         else
229             ign = mark - 1;
230     }
231 }
232
233 /* Set to 1 if ignore file patterns should be matched in a case-insensitive
234    fashion.  */
235 int ign_case;
236
237 /* Return 1 if the given filename should be ignored by update or import. */
238 int
239 ign_name (name)
240     char *name;
241 {
242     char **cpp = ign_list;
243
244     if (cpp == NULL)
245         return (0);
246
247     if (ign_case)
248     {
249         /* We do a case-insensitive match by calling fnmatch on copies of
250            the pattern and the name which have been converted to
251            lowercase.  */
252         char *name_lower;
253         char *pat_lower;
254         char *p;
255
256         name_lower = xstrdup (name);
257         for (p = name_lower; *p != '\0'; ++p)
258             *p = tolower (*p);
259         while (*cpp)
260         {
261             pat_lower = xstrdup (*cpp++);
262             for (p = pat_lower; *p != '\0'; ++p)
263                 *p = tolower (*p);
264             if (fnmatch (pat_lower, name_lower, 0) == 0)
265                 goto matched;
266             free (pat_lower);
267         }
268         free (name_lower);
269         return 0;
270       matched:
271         free (name_lower);
272         free (pat_lower);
273         return 1;
274     }
275     else
276     {
277         while (*cpp)
278             if (fnmatch (*cpp++, name, 0) == 0)
279                 return 1;
280         return 0;
281     }
282 }
283 \f
284 /* FIXME: This list of dirs to ignore stuff seems not to be used.  */
285
286 static char **dir_ign_list = NULL;
287 static int dir_ign_max = 0;
288 static int dir_ign_current = 0;
289
290 /* add a directory to list of dirs to ignore */
291 void ign_dir_add (name)
292      char *name;
293 {
294   /* make sure we've got the space for the entry */
295   if (dir_ign_current <= dir_ign_max)
296     {
297       dir_ign_max += IGN_GROW;
298       dir_ign_list = (char **) xrealloc ((char *) dir_ign_list, (dir_ign_max+1) * sizeof(char*));
299     }
300
301   dir_ign_list[dir_ign_current] = name;
302
303   dir_ign_current += 1 ;
304 }
305
306
307 /* this function returns 1 (true) if the given directory name is part of
308  * the list of directories to ignore
309  */
310
311 int ignore_directory (name)
312      char *name;
313 {
314   int i;
315
316   if (!dir_ign_list)
317     return 0;
318
319   i = dir_ign_current;
320   while (i--)
321     {
322       if (strncmp(name, dir_ign_list[i], strlen(dir_ign_list[i])) == 0)
323         return 1;
324     }
325
326   return 0;
327 }
328 \f
329 /*
330  * Process the current directory, looking for files not in ILIST and not on
331  * the global ignore list for this directory.  If we find one, call PROC
332  * passing it the name of the file and the update dir.
333  */
334 void
335 ignore_files (ilist, update_dir, proc)
336     List *ilist;
337     char *update_dir;
338     Ignore_proc proc;
339 {
340     DIR *dirp;
341     struct dirent *dp;
342     struct stat sb;
343     char *file;
344     char *xdir;
345
346     /* we get called with update_dir set to "." sometimes... strip it */
347     if (strcmp (update_dir, ".") == 0)
348         xdir = "";
349     else
350         xdir = update_dir;
351
352     dirp = opendir (".");
353     if (dirp == NULL)
354         return;
355
356     ign_add_file (CVSDOTIGNORE, 1);
357     wrap_add_file (CVSDOTWRAPPER, 1);
358
359     while ((dp = readdir (dirp)) != NULL)
360     {
361         file = dp->d_name;
362         if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
363             continue;
364         if (findnode_fn (ilist, file) != NULL)
365             continue;
366
367         if (
368 #ifdef DT_DIR
369                 dp->d_type != DT_UNKNOWN ||
370 #endif
371                 lstat(file, &sb) != -1) 
372         {
373
374             if (
375 #ifdef DT_DIR
376                 dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN &&
377 #endif
378                 S_ISDIR(sb.st_mode))
379             {
380                 char temp[PATH_MAX];
381
382                 (void) sprintf (temp, "%s/%s", file, CVSADM);
383                 if (isdir (temp))
384                     continue;
385             }
386 #ifdef S_ISLNK
387             else if (
388 #ifdef DT_DIR
389                 dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && 
390 #endif
391                 S_ISLNK(sb.st_mode))
392             {
393                 continue;
394             }
395 #endif
396         }
397
398         /* We could be ignoring FIFOs and other files which are neither
399            regular files nor directories here.  */
400         if (ign_name (file))
401             continue;
402         (*proc) (file, xdir);
403     }
404     (void) closedir (dirp);
405 }