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 #include "lstat.h"
18
19 /*
20 * Ignore file section.
21 *
22 * "!" may be included any time to reset the list (i.e. ignore nothing);
23 * "*" may be specified to ignore everything. It stays as the first
24 * element forever, unless a "!" clears it out.
25 */
26
27 static char **ign_list; /* List of files to ignore in update
28 * and import */
29 static char **s_ign_list = NULL;
30 static int ign_count; /* Number of active entries */
31 static int s_ign_count = 0;
32 static int ign_size; /* This many slots available (plus
33 * one for a NULL) */
34 static int ign_hold = -1; /* Index where first "temporary" item
35 * is held */
36
37 const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\
38 .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\
39 *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$";
40
41 #define IGN_GROW 16 /* grow the list by 16 elements at a
42 * time */
43
44 /* Nonzero if we have encountered an -I ! directive, which means one should
45 no longer ask the server about what is in CVSROOTADM_IGNORE. */
46 int ign_inhibit_server;
47
48
49
50 /*
51 * To the "ignore list", add the hard-coded default ignored wildcards above,
52 * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
53 * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
54 * variable.
55 */
56 void
ign_setup(void)57 ign_setup (void)
58 {
59 char *home_dir;
60 char *tmp;
61
62 ign_inhibit_server = 0;
63
64 /* Start with default list and special case */
65 tmp = xstrdup (ign_default);
66 ign_add (tmp, 0);
67 free (tmp);
68
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 {
75 char *file = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
76 CVSROOTADM, CVSROOTADM_IGNORE);
77 /* Then add entries found in repository, if it exists */
78 ign_add_file (file, 0);
79 free (file);
80 }
81
82 /* Then add entries found in home dir, (if user has one) and file exists */
83 home_dir = get_homedir ();
84 /* If we can't find a home directory, ignore ~/.cvsignore. This may
85 make tracking down problems a bit of a pain, but on the other
86 hand it might be obnoxious to complain when CVS will function
87 just fine without .cvsignore (and many users won't even know what
88 .cvsignore is). */
89 if (home_dir)
90 {
91 char *file = strcat_filename_onto_homedir (home_dir, CVSDOTIGNORE);
92 ign_add_file (file, 0);
93 free (file);
94 }
95
96 /* Then add entries found in CVSIGNORE environment variable. */
97 ign_add (getenv (IGNORE_ENV), 0);
98
99 /* Later, add ignore entries found in -I arguments */
100 }
101
102
103
104 /*
105 * Open a file and read lines, feeding each line to a line parser. Arrange
106 * for keeping a temporary list of wildcards at the end, if the "hold"
107 * argument is set.
108 */
109 void
ign_add_file(char * file,int hold)110 ign_add_file (char *file, int hold)
111 {
112 FILE *fp;
113 char *line = NULL;
114 size_t line_allocated = 0;
115
116 /* restore the saved list (if any) */
117 if (s_ign_list != NULL)
118 {
119 int i;
120
121 for (i = 0; i < s_ign_count; i++)
122 ign_list[i] = s_ign_list[i];
123 ign_count = s_ign_count;
124 ign_list[ign_count] = NULL;
125
126 s_ign_count = 0;
127 free (s_ign_list);
128 s_ign_list = NULL;
129 }
130
131 /* is this a temporary ignore file? */
132 if (hold)
133 {
134 /* re-set if we had already done a temporary file */
135 if (ign_hold >= 0)
136 {
137 int i;
138
139 for (i = ign_hold; i < ign_count; i++)
140 free (ign_list[i]);
141 ign_count = ign_hold;
142 ign_list[ign_count] = NULL;
143 }
144 else
145 {
146 ign_hold = ign_count;
147 }
148 }
149
150 /* load the file */
151 fp = CVS_FOPEN (file, "r");
152 if (fp == NULL)
153 {
154 if (! existence_error (errno))
155 error (0, errno, "cannot open %s", file);
156 return;
157 }
158 while (getline (&line, &line_allocated, fp) >= 0)
159 ign_add (line, hold);
160 if (ferror (fp))
161 error (0, errno, "cannot read %s", file);
162 if (fclose (fp) < 0)
163 error (0, errno, "cannot close %s", file);
164 free (line);
165 }
166
167
168
169 /* Parse a line of space-separated wildcards and add them to the list. */
170 void
ign_add(char * ign,int hold)171 ign_add (char *ign, int hold)
172 {
173 if (!ign || !*ign)
174 return;
175
176 for (; *ign; ign++)
177 {
178 char *mark;
179 char save;
180
181 /* ignore whitespace before the token */
182 if (isspace ((unsigned char) *ign))
183 continue;
184
185 /* If we have used up all the space, add some more. Do this before
186 processing `!', since an "empty" list still contains the `CVS'
187 entry. */
188 if (ign_count >= ign_size)
189 {
190 ign_size += IGN_GROW;
191 ign_list = xnrealloc (ign_list, ign_size + 1, sizeof (char *));
192 }
193
194 /*
195 * if we find a single character !, we must re-set the ignore list
196 * (saving it if necessary). We also catch * as a special case in a
197 * global ignore file as an optimization
198 */
199 if ((!*(ign+1) || isspace ((unsigned char) *(ign+1)))
200 && (*ign == '!' || *ign == '*'))
201 {
202 if (!hold)
203 {
204 /* permanently reset the ignore list */
205 int i;
206
207 for (i = 0; i < ign_count; i++)
208 free (ign_list[i]);
209 ign_count = 1;
210 /* Always ignore the "CVS" directory. */
211 ign_list[0] = xstrdup ("CVS");
212 ign_list[1] = NULL;
213
214 /* if we are doing a '!', continue; otherwise add the '*' */
215 if (*ign == '!')
216 {
217 ign_inhibit_server = 1;
218 continue;
219 }
220 }
221 else if (*ign == '!')
222 {
223 /* temporarily reset the ignore list */
224 int i;
225
226 if (ign_hold >= 0)
227 {
228 for (i = ign_hold; i < ign_count; i++)
229 free (ign_list[i]);
230 ign_hold = -1;
231 }
232 if (s_ign_list)
233 {
234 /* Don't save the ignore list twice - if there are two
235 * bangs in a local .cvsignore file then we don't want to
236 * save the new list the first bang created.
237 *
238 * We still need to free the "new" ignore list.
239 */
240 for (i = 0; i < ign_count; i++)
241 free (ign_list[i]);
242 }
243 else
244 {
245 /* Save the ignore list for later. */
246 s_ign_list = xnmalloc (ign_count, sizeof (char *));
247 for (i = 0; i < ign_count; i++)
248 s_ign_list[i] = ign_list[i];
249 s_ign_count = ign_count;
250 }
251 ign_count = 1;
252 /* Always ignore the "CVS" directory. */
253 ign_list[0] = xstrdup ("CVS");
254 ign_list[1] = NULL;
255 continue;
256 }
257 }
258
259 /* find the end of this token */
260 for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++)
261 /* do nothing */ ;
262
263 save = *mark;
264 *mark = '\0';
265
266 ign_list[ign_count++] = xstrdup (ign);
267 ign_list[ign_count] = NULL;
268
269 *mark = save;
270 if (save)
271 ign = mark;
272 else
273 ign = mark - 1;
274 }
275 }
276
277
278
279 /* Return true if the given filename should be ignored by update or import,
280 * else return false.
281 */
282 int
ign_name(char * name)283 ign_name (char *name)
284 {
285 char **cpp = ign_list;
286
287 if (cpp == NULL)
288 return 0;
289
290 while (*cpp)
291 if (CVS_FNMATCH (*cpp++, name, 0) == 0)
292 return 1;
293
294 return 0;
295 }
296
297
298
299 /* FIXME: This list of dirs to ignore stuff seems not to be used.
300 Really? send_dirent_proc and update_dirent_proc both call
301 ignore_directory and do_module calls ign_dir_add. No doubt could
302 use some documentation/testsuite work. */
303
304 static char **dir_ign_list = NULL;
305 static int dir_ign_max = 0;
306 static int dir_ign_current = 0;
307
308 /* Add a directory to list of dirs to ignore. */
309 void
ign_dir_add(char * name)310 ign_dir_add (char *name)
311 {
312 /* Make sure we've got the space for the entry. */
313 if (dir_ign_current <= dir_ign_max)
314 {
315 dir_ign_max += IGN_GROW;
316 dir_ign_list = xnrealloc (dir_ign_list,
317 dir_ign_max + 1, sizeof (char *));
318 }
319
320 dir_ign_list[dir_ign_current++] = xstrdup (name);
321 }
322
323
324 /* Return nonzero if NAME is part of the list of directories to ignore. */
325
326 int
ignore_directory(const char * name)327 ignore_directory (const char *name)
328 {
329 int i;
330
331 if (!dir_ign_list)
332 return 0;
333
334 i = dir_ign_current;
335 while (i--)
336 {
337 if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])+1) == 0)
338 return 1;
339 }
340
341 return 0;
342 }
343
344
345
346 /*
347 * Process the current directory, looking for files not in ILIST and
348 * not on the global ignore list for this directory. If we find one,
349 * call PROC passing it the name of the file and the update dir.
350 * ENTRIES is the entries list, which is used to identify known
351 * directories. ENTRIES may be NULL, in which case we assume that any
352 * directory with a CVS administration directory is known.
353 */
354 void
ignore_files(List * ilist,List * entries,const char * update_dir,Ignore_proc proc)355 ignore_files (List *ilist, List *entries, 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 = Xasprintf ("%s/%s", file, CVSADM);
419 dir = isdir (p);
420 free (p);
421 if (dir)
422 continue;
423 }
424 }
425
426 /* We could be ignoring FIFOs and other files which are neither
427 regular files nor directories here. */
428 if (ign_name (file))
429 continue;
430
431 if (
432 #ifdef DT_DIR
433 dp->d_type != DT_UNKNOWN ||
434 #endif
435 lstat (file, &sb) != -1)
436 {
437
438 if (
439 #ifdef DT_DIR
440 dp->d_type == DT_DIR
441 || (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode))
442 #else
443 S_ISDIR (sb.st_mode)
444 #endif
445 )
446 {
447 if (!subdirs)
448 {
449 char *temp = Xasprintf ("%s/%s", file, CVSADM);
450 if (isdir (temp))
451 {
452 free (temp);
453 continue;
454 }
455 free (temp);
456 }
457 }
458 #ifdef S_ISLNK
459 else if (
460 #ifdef DT_DIR
461 dp->d_type == DT_LNK
462 || (dp->d_type == DT_UNKNOWN && S_ISLNK (sb.st_mode))
463 #else
464 S_ISLNK (sb.st_mode)
465 #endif
466 )
467 {
468 continue;
469 }
470 #endif
471 }
472
473 p = getnode ();
474 p->type = FILES;
475 p->key = xstrdup (file);
476 (void) addnode (files, p);
477 }
478 if (errno != 0)
479 error (0, errno, "error reading current directory");
480 (void) CVS_CLOSEDIR (dirp);
481
482 sortlist (files, fsortcmp);
483 for (p = files->list->next; p != files->list; p = p->next)
484 (*proc) (p->key, xdir);
485 dellist (&files);
486 }
487