Sync usage with man page.
[netbsd-mini2440.git] / external / gpl2 / xcvs / dist / src / ignore.c
bloba5a90a0be4df9afd811abc7930587468f828d063
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.
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. */
12 * .cvsignore file support contributed by David G. Grubbs <dgg@odi.com>
15 #include "cvs.h"
16 #include "getline.h"
17 #include "lstat.h"
20 * Ignore file section.
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.
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 */
37 const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\
38 .nse_depinfo #* .#* cvslog.* ,* CVS.adm .del-* *.a *.olb *.o *.obj\
39 *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$";
40 extern const char *cvsDir;
42 #define IGN_GROW 16 /* grow the list by 16 elements at a
43 * time */
45 /* Nonzero if we have encountered an -I ! directive, which means one should
46 no longer ask the server about what is in CVSROOTADM_IGNORE. */
47 int ign_inhibit_server;
52 * To the "ignore list", add the hard-coded default ignored wildcards above,
53 * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
54 * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
55 * variable.
57 void
58 ign_setup (void)
60 char *home_dir;
61 char *tmp;
63 ign_inhibit_server = 0;
65 /* Start with default list and special case */
66 tmp = xstrdup (ign_default);
67 ign_add (tmp, 0);
68 free (tmp);
69 tmp = xstrdup (cvsDir);
70 ign_add (tmp, 0);
71 free (tmp);
73 /* The client handles another way, by (after it does its own ignore file
74 processing, and only if !ign_inhibit_server), letting the server
75 know about the files and letting it decide whether to ignore
76 them based on CVSROOOTADM_IGNORE. */
77 if (!current_parsed_root->isremote)
79 char *file = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
80 CVSROOTADM, CVSROOTADM_IGNORE);
81 /* Then add entries found in repository, if it exists */
82 ign_add_file (file, 0);
83 free (file);
86 /* Then add entries found in home dir, (if user has one) and file exists */
87 home_dir = get_homedir ();
88 /* If we can't find a home directory, ignore ~/.cvsignore. This may
89 make tracking down problems a bit of a pain, but on the other
90 hand it might be obnoxious to complain when CVS will function
91 just fine without .cvsignore (and many users won't even know what
92 .cvsignore is). */
93 if (home_dir)
95 char *file = strcat_filename_onto_homedir (home_dir, CVSDOTIGNORE);
96 ign_add_file (file, 0);
97 free (file);
100 /* Then add entries found in CVSIGNORE environment variable. */
101 ign_add (getenv (IGNORE_ENV), 0);
103 /* Later, add ignore entries found in -I arguments */
109 * Open a file and read lines, feeding each line to a line parser. Arrange
110 * for keeping a temporary list of wildcards at the end, if the "hold"
111 * argument is set.
113 void
114 ign_add_file (char *file, int hold)
116 FILE *fp;
117 char *line = NULL;
118 size_t line_allocated = 0;
120 /* restore the saved list (if any) */
121 if (s_ign_list != NULL)
123 int i;
125 for (i = 0; i < s_ign_count; i++)
126 ign_list[i] = s_ign_list[i];
127 ign_count = s_ign_count;
128 ign_list[ign_count] = NULL;
130 s_ign_count = 0;
131 free (s_ign_list);
132 s_ign_list = NULL;
135 /* is this a temporary ignore file? */
136 if (hold)
138 /* re-set if we had already done a temporary file */
139 if (ign_hold >= 0)
141 int i;
143 for (i = ign_hold; i < ign_count; i++)
144 free (ign_list[i]);
145 ign_count = ign_hold;
146 ign_list[ign_count] = NULL;
148 else
150 ign_hold = ign_count;
154 /* load the file */
155 fp = CVS_FOPEN (file, "r");
156 if (fp == NULL)
158 if (! existence_error (errno))
159 error (0, errno, "cannot open %s", file);
160 return;
162 while (getline (&line, &line_allocated, fp) >= 0)
163 ign_add (line, hold);
164 if (ferror (fp))
165 error (0, errno, "cannot read %s", file);
166 if (fclose (fp) < 0)
167 error (0, errno, "cannot close %s", file);
168 free (line);
173 /* Parse a line of space-separated wildcards and add them to the list. */
174 void
175 ign_add (char *ign, int hold)
177 if (!ign || !*ign)
178 return;
180 for (; *ign; ign++)
182 char *mark;
183 char save;
185 /* ignore whitespace before the token */
186 if (isspace ((unsigned char) *ign))
187 continue;
189 /* If we have used up all the space, add some more. Do this before
190 processing `!', since an "empty" list still contains the `CVS'
191 entry. */
192 if (ign_count >= ign_size)
194 ign_size += IGN_GROW;
195 ign_list = xnrealloc (ign_list, ign_size + 1, sizeof (char *));
199 * if we find a single character !, we must re-set the ignore list
200 * (saving it if necessary). We also catch * as a special case in a
201 * global ignore file as an optimization
203 if ((!*(ign+1) || isspace ((unsigned char) *(ign+1)))
204 && (*ign == '!' || *ign == '*'))
206 if (!hold)
208 /* permanently reset the ignore list */
209 int i;
211 for (i = 0; i < ign_count; i++)
212 free (ign_list[i]);
213 ign_count = 1;
214 /* Always ignore the "CVS" directory. */
215 ign_list[0] = xstrdup ("CVS");
216 ign_list[1] = NULL;
218 /* if we are doing a '!', continue; otherwise add the '*' */
219 if (*ign == '!')
221 ign_inhibit_server = 1;
222 continue;
225 else if (*ign == '!')
227 /* temporarily reset the ignore list */
228 int i;
230 if (ign_hold >= 0)
232 for (i = ign_hold; i < ign_count; i++)
233 free (ign_list[i]);
234 ign_hold = -1;
236 if (s_ign_list)
238 /* Don't save the ignore list twice - if there are two
239 * bangs in a local .cvsignore file then we don't want to
240 * save the new list the first bang created.
242 * We still need to free the "new" ignore list.
244 for (i = 0; i < ign_count; i++)
245 free (ign_list[i]);
247 else
249 /* Save the ignore list for later. */
250 s_ign_list = xnmalloc (ign_count, sizeof (char *));
251 for (i = 0; i < ign_count; i++)
252 s_ign_list[i] = ign_list[i];
253 s_ign_count = ign_count;
255 ign_count = 1;
256 /* Always ignore the "CVS" directory. */
257 ign_list[0] = xstrdup ("CVS");
258 ign_list[1] = NULL;
259 continue;
263 /* find the end of this token */
264 for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++)
265 /* do nothing */ ;
267 save = *mark;
268 *mark = '\0';
270 ign_list[ign_count++] = xstrdup (ign);
271 ign_list[ign_count] = NULL;
273 *mark = save;
274 if (save)
275 ign = mark;
276 else
277 ign = mark - 1;
283 /* Return true if the given filename should be ignored by update or import,
284 * else return false.
287 ign_name (char *name)
289 char **cpp = ign_list;
291 if (cpp == NULL)
292 return 0;
294 while (*cpp)
295 if (CVS_FNMATCH (*cpp++, name, 0) == 0)
296 return 1;
298 return 0;
303 /* FIXME: This list of dirs to ignore stuff seems not to be used.
304 Really? send_dirent_proc and update_dirent_proc both call
305 ignore_directory and do_module calls ign_dir_add. No doubt could
306 use some documentation/testsuite work. */
308 static char **dir_ign_list = NULL;
309 static int dir_ign_max = 0;
310 static int dir_ign_current = 0;
312 /* Add a directory to list of dirs to ignore. */
313 void
314 ign_dir_add (char *name)
316 /* Make sure we've got the space for the entry. */
317 if (dir_ign_current <= dir_ign_max)
319 dir_ign_max += IGN_GROW;
320 dir_ign_list = xnrealloc (dir_ign_list,
321 dir_ign_max + 1, sizeof (char *));
324 dir_ign_list[dir_ign_current++] = xstrdup (name);
328 /* Return nonzero if NAME is part of the list of directories to ignore. */
331 ignore_directory (const char *name)
333 int i;
335 if (!dir_ign_list)
336 return 0;
338 i = dir_ign_current;
339 while (i--)
341 if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])+1) == 0)
342 return 1;
345 return 0;
351 * Process the current directory, looking for files not in ILIST and
352 * not on the global ignore list for this directory. If we find one,
353 * call PROC passing it the name of the file and the update dir.
354 * ENTRIES is the entries list, which is used to identify known
355 * directories. ENTRIES may be NULL, in which case we assume that any
356 * directory with a CVS administration directory is known.
358 void
359 ignore_files (List *ilist, List *entries, const char *update_dir,
360 Ignore_proc proc)
362 int subdirs;
363 DIR *dirp;
364 struct dirent *dp;
365 struct stat sb;
366 char *file;
367 const char *xdir;
368 List *files;
369 Node *p;
371 /* Set SUBDIRS if we have subdirectory information in ENTRIES. */
372 if (entries == NULL)
373 subdirs = 0;
374 else
376 struct stickydirtag *sdtp = entries->list->data;
378 subdirs = sdtp == NULL || sdtp->subdirs;
381 /* we get called with update_dir set to "." sometimes... strip it */
382 if (strcmp (update_dir, ".") == 0)
383 xdir = "";
384 else
385 xdir = update_dir;
387 dirp = CVS_OPENDIR (".");
388 if (dirp == NULL)
390 error (0, errno, "cannot open current directory");
391 return;
394 ign_add_file (CVSDOTIGNORE, 1);
395 wrap_add_file (CVSDOTWRAPPER, 1);
397 /* Make a list for the files. */
398 files = getlist ();
400 while (errno = 0, (dp = CVS_READDIR (dirp)) != NULL)
402 file = dp->d_name;
403 if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
404 continue;
405 if (findnode_fn (ilist, file) != NULL)
406 continue;
407 if (subdirs)
409 Node *node;
411 node = findnode_fn (entries, file);
412 if (node != NULL
413 && ((Entnode *) node->data)->type == ENT_SUBDIR)
415 char *p;
416 int dir;
418 /* For consistency with past behaviour, we only ignore
419 this directory if there is a CVS subdirectory.
420 This will normally be the case, but the user may
421 have messed up the working directory somehow. */
422 p = Xasprintf ("%s/%s", file, CVSADM);
423 dir = isdir (p);
424 free (p);
425 if (dir)
426 continue;
430 /* We could be ignoring FIFOs and other files which are neither
431 regular files nor directories here. */
432 if (ign_name (file))
433 continue;
435 if (
436 #ifdef DT_DIR
437 dp->d_type != DT_UNKNOWN ||
438 #endif
439 lstat (file, &sb) != -1)
442 if (
443 #ifdef DT_DIR
444 dp->d_type == DT_DIR
445 || (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode))
446 #else
447 S_ISDIR (sb.st_mode)
448 #endif
451 if (!subdirs)
453 char *temp = Xasprintf ("%s/%s", file, CVSADM);
454 if (isdir (temp))
456 free (temp);
457 continue;
459 free (temp);
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
472 continue;
474 #endif
477 p = getnode ();
478 p->type = FILES;
479 p->key = xstrdup (file);
480 (void) addnode (files, p);
482 if (errno != 0)
483 error (0, errno, "error reading current directory");
484 (void) CVS_CLOSEDIR (dirp);
486 sortlist (files, fsortcmp);
487 for (p = files->list->next; p != files->list; p = p->next)
488 (*proc) (p->key, xdir);
489 dellist (&files);