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