]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/cvs/src/ignore.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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
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     /* 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  */
110 void
111 ign_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. */
173 void
174 ign_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  */
288 int
289 ign_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
311 static char **dir_ign_list = NULL;
312 static int dir_ign_max = 0;
313 static int dir_ign_current = 0;
314
315 /* Add a directory to list of dirs to ignore.  */
316 void
317 ign_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
335 int
336 ignore_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  */
364 void
365 ignore_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 }