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
ign_setup()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
ign_add_file(file,hold)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
ign_add(ign,hold)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
ign_name(name)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
ign_dir_add(name)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
ignore_directory(name)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
ignore_files(ilist,entries,update_dir,proc)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 }
504