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 #include <sys/cdefs.h>
11 __RCSID("$NetBSD: ignore.c,v 1.6 2019/09/24 21:03:29 kamil Exp $");
12
13 /*
14 * .cvsignore file support contributed by David G. Grubbs <dgg@odi.com>
15 */
16
17 #include "cvs.h"
18 #include "getline.h"
19 #include "lstat.h"
20
21 /*
22 * Ignore file section.
23 *
24 * "!" may be included any time to reset the list (i.e. ignore nothing);
25 * "*" may be specified to ignore everything. It stays as the first
26 * element forever, unless a "!" clears it out.
27 */
28
29 static char **ign_list; /* List of files to ignore in update
30 * and import */
31 static char **s_ign_list = NULL;
32 static int ign_count; /* Number of active entries */
33 static int s_ign_count = 0;
34 static int ign_size; /* This many slots available (plus
35 * one for a NULL) */
36 static int ign_hold = -1; /* Index where first "temporary" item
37 * is held */
38
39 const char *ign_default = ". .. *.core RCSLOG tags TAGS RCS SCCS .make.state\
40 .nse_depinfo #* .#* cvslog.* ,* CVS.adm .del-* *.a *.olb *.o *.obj\
41 .gitignore .gitattributes .gitmodules .hgignore\
42 *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$";
43 extern const char *cvsDir;
44
45 #define IGN_GROW 16 /* grow the list by 16 elements at a
46 * time */
47
48 /* Nonzero if we have encountered an -I ! directive, which means one should
49 no longer ask the server about what is in CVSROOTADM_IGNORE. */
50 int ign_inhibit_server;
51
52
53
54 /*
55 * To the "ignore list", add the hard-coded default ignored wildcards above,
56 * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
57 * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
58 * variable.
59 */
60 void
ign_setup(void)61 ign_setup (void)
62 {
63 char *home_dir;
64 char *tmp;
65
66 ign_inhibit_server = 0;
67
68 /* Start with default list and special case */
69 tmp = xstrdup (ign_default);
70 ign_add (tmp, 0);
71 free (tmp);
72 tmp = xstrdup (cvsDir);
73 ign_add (tmp, 0);
74 free (tmp);
75
76 /* The client handles another way, by (after it does its own ignore file
77 processing, and only if !ign_inhibit_server), letting the server
78 know about the files and letting it decide whether to ignore
79 them based on CVSROOOTADM_IGNORE. */
80 if (!current_parsed_root->isremote)
81 {
82 char *file = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
83 CVSROOTADM, CVSROOTADM_IGNORE);
84 /* Then add entries found in repository, if it exists */
85 ign_add_file (file, 0);
86 free (file);
87 }
88
89 /* Then add entries found in home dir, (if user has one) and file exists */
90 home_dir = get_homedir ();
91 /* If we can't find a home directory, ignore ~/.cvsignore. This may
92 make tracking down problems a bit of a pain, but on the other
93 hand it might be obnoxious to complain when CVS will function
94 just fine without .cvsignore (and many users won't even know what
95 .cvsignore is). */
96 if (home_dir)
97 {
98 char *file = strcat_filename_onto_homedir (home_dir, CVSDOTIGNORE);
99 ign_add_file (file, 0);
100 free (file);
101 }
102
103 /* Then add entries found in CVSIGNORE environment variable. */
104 ign_add (getenv (IGNORE_ENV), 0);
105
106 /* Later, add ignore entries found in -I arguments */
107 }
108
109
110
111 /*
112 * Open a file and read lines, feeding each line to a line parser. Arrange
113 * for keeping a temporary list of wildcards at the end, if the "hold"
114 * argument is set.
115 */
116 void
ign_add_file(char * file,int hold)117 ign_add_file (char *file, int hold)
118 {
119 FILE *fp;
120 char *line = NULL;
121 size_t line_allocated = 0;
122
123 /* restore the saved list (if any) */
124 if (s_ign_list != NULL)
125 {
126 int i;
127
128 for (i = 0; i < s_ign_count; i++)
129 ign_list[i] = s_ign_list[i];
130 ign_count = s_ign_count;
131 ign_list[ign_count] = NULL;
132
133 s_ign_count = 0;
134 free (s_ign_list);
135 s_ign_list = NULL;
136 }
137
138 /* is this a temporary ignore file? */
139 if (hold)
140 {
141 /* re-set if we had already done a temporary file */
142 if (ign_hold >= 0)
143 {
144 int i;
145
146 for (i = ign_hold; i < ign_count; i++)
147 free (ign_list[i]);
148 ign_count = ign_hold;
149 ign_list[ign_count] = NULL;
150 }
151 else
152 {
153 ign_hold = ign_count;
154 }
155 }
156
157 /* load the file */
158 fp = CVS_FOPEN (file, "r");
159 if (fp == NULL)
160 {
161 if (! existence_error (errno))
162 error (0, errno, "cannot open %s", file);
163 return;
164 }
165 while (getline (&line, &line_allocated, fp) >= 0)
166 ign_add (line, hold);
167 if (ferror (fp))
168 error (0, errno, "cannot read %s", file);
169 if (fclose (fp) < 0)
170 error (0, errno, "cannot close %s", file);
171 free (line);
172 }
173
174
175
176 /* Parse a line of space-separated wildcards and add them to the list. */
177 void
ign_add(char * ign,int hold)178 ign_add (char *ign, 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 = xnrealloc (ign_list, ign_size + 1, sizeof (char *));
199 }
200
201 /*
202 * if we find a single character !, we must re-set the ignore list
203 * (saving it if necessary). We also catch * as a special case in a
204 * global ignore file as an optimization
205 */
206 if ((!*(ign+1) || isspace ((unsigned char) *(ign+1)))
207 && (*ign == '!' || *ign == '*'))
208 {
209 if (!hold)
210 {
211 /* permanently reset the ignore list */
212 int i;
213
214 for (i = 0; i < ign_count; i++)
215 free (ign_list[i]);
216 ign_count = 1;
217 /* Always ignore the "CVS" directory. */
218 ign_list[0] = xstrdup ("CVS");
219 ign_list[1] = NULL;
220
221 /* if we are doing a '!', continue; otherwise add the '*' */
222 if (*ign == '!')
223 {
224 ign_inhibit_server = 1;
225 continue;
226 }
227 }
228 else if (*ign == '!')
229 {
230 /* temporarily reset the ignore list */
231 int i;
232
233 if (ign_hold >= 0)
234 {
235 for (i = ign_hold; i < ign_count; i++)
236 free (ign_list[i]);
237 ign_hold = -1;
238 }
239 if (s_ign_list)
240 {
241 /* Don't save the ignore list twice - if there are two
242 * bangs in a local .cvsignore file then we don't want to
243 * save the new list the first bang created.
244 *
245 * We still need to free the "new" ignore list.
246 */
247 for (i = 0; i < ign_count; i++)
248 free (ign_list[i]);
249 }
250 else
251 {
252 /* Save the ignore list for later. */
253 s_ign_list = xnmalloc (ign_count, sizeof (char *));
254 for (i = 0; i < ign_count; i++)
255 s_ign_list[i] = ign_list[i];
256 s_ign_count = ign_count;
257 }
258 ign_count = 1;
259 /* Always ignore the "CVS" directory. */
260 ign_list[0] = xstrdup ("CVS");
261 ign_list[1] = NULL;
262 continue;
263 }
264 }
265
266 /* find the end of this token */
267 for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++)
268 /* do nothing */ ;
269
270 save = *mark;
271 *mark = '\0';
272
273 ign_list[ign_count++] = xstrdup (ign);
274 ign_list[ign_count] = NULL;
275
276 *mark = save;
277 if (save)
278 ign = mark;
279 else
280 ign = mark - 1;
281 }
282 }
283
284
285
286 /* Return true if the given filename should be ignored by update or import,
287 * else return false.
288 */
289 int
ign_name(char * name)290 ign_name (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
ign_dir_add(char * name)317 ign_dir_add (char *name)
318 {
319 /* Make sure we've got the space for the entry. */
320 if (dir_ign_current <= dir_ign_max)
321 {
322 dir_ign_max += IGN_GROW;
323 dir_ign_list = xnrealloc (dir_ign_list,
324 dir_ign_max + 1, sizeof (char *));
325 }
326
327 dir_ign_list[dir_ign_current++] = xstrdup (name);
328 }
329
330
331 /* Return nonzero if NAME is part of the list of directories to ignore. */
332
333 int
ignore_directory(const char * name)334 ignore_directory (const char *name)
335 {
336 int i;
337
338 if (!dir_ign_list)
339 return 0;
340
341 i = dir_ign_current;
342 while (i--)
343 {
344 if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])+1) == 0)
345 return 1;
346 }
347
348 return 0;
349 }
350
351
352
353 /*
354 * Process the current directory, looking for files not in ILIST and
355 * not on the global ignore list for this directory. If we find one,
356 * call PROC passing it the name of the file and the update dir.
357 * ENTRIES is the entries list, which is used to identify known
358 * directories. ENTRIES may be NULL, in which case we assume that any
359 * directory with a CVS administration directory is known.
360 */
361 void
ignore_files(List * ilist,List * entries,const char * update_dir,Ignore_proc proc)362 ignore_files (List *ilist, List *entries, const char *update_dir,
363 Ignore_proc proc)
364 {
365 int subdirs;
366 DIR *dirp;
367 struct dirent *dp;
368 struct stat sb;
369 char *file;
370 const char *xdir;
371 List *files;
372 Node *p;
373
374 /* Set SUBDIRS if we have subdirectory information in ENTRIES. */
375 if (entries == NULL)
376 subdirs = 0;
377 else
378 {
379 struct stickydirtag *sdtp = entries->list->data;
380
381 subdirs = sdtp == NULL || sdtp->subdirs;
382 }
383
384 /* we get called with update_dir set to "." sometimes... strip it */
385 if (strcmp (update_dir, ".") == 0)
386 xdir = "";
387 else
388 xdir = update_dir;
389
390 dirp = CVS_OPENDIR (".");
391 if (dirp == NULL)
392 {
393 error (0, errno, "cannot open current directory");
394 return;
395 }
396
397 ign_add_file (CVSDOTIGNORE, 1);
398 wrap_add_file (CVSDOTWRAPPER, 1);
399
400 /* Make a list for the files. */
401 files = getlist ();
402
403 while (errno = 0, (dp = CVS_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 = Xasprintf ("%s/%s", file, CVSADM);
426 dir = isdir (p);
427 free (p);
428 if (dir)
429 continue;
430 }
431 }
432
433 /* We could be ignoring FIFOs and other files which are neither
434 regular files nor directories here. */
435 if (ign_name (file))
436 continue;
437
438 if (
439 #ifdef DT_DIR
440 dp->d_type != DT_UNKNOWN ||
441 #endif
442 lstat (file, &sb) != -1)
443 {
444
445 if (
446 #ifdef DT_DIR
447 dp->d_type == DT_DIR
448 || (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode))
449 #else
450 S_ISDIR (sb.st_mode)
451 #endif
452 )
453 {
454 if (!subdirs)
455 {
456 char *temp = Xasprintf ("%s/%s", file, CVSADM);
457 if (isdir (temp))
458 {
459 free (temp);
460 continue;
461 }
462 free (temp);
463 }
464 }
465 #ifdef S_ISLNK
466 else if (
467 #ifdef DT_DIR
468 dp->d_type == DT_LNK
469 || (dp->d_type == DT_UNKNOWN && S_ISLNK (sb.st_mode))
470 #else
471 S_ISLNK (sb.st_mode)
472 #endif
473 )
474 {
475 continue;
476 }
477 #endif
478 }
479
480 p = getnode ();
481 p->type = FILES;
482 p->key = xstrdup (file);
483 (void) addnode (files, p);
484 }
485 if (errno != 0)
486 error (0, errno, "error reading current directory");
487 (void) CVS_CLOSEDIR (dirp);
488
489 sortlist (files, fsortcmp);
490 for (p = files->list->next; p != files->list; p = p->next)
491 (*proc) (p->key, xdir);
492 dellist (&files);
493 }
494