/contrib/cvs/src/ignore.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 503 lines · 324 code · 75 blank · 104 comment · 93 complexity · 585e0787fde7c5c600e87d38f5f44ae2 MD5 · raw file

  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. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details. */
  9. /*
  10. * .cvsignore file support contributed by David G. Grubbs <dgg@odi.com>
  11. */
  12. #include "cvs.h"
  13. #include "getline.h"
  14. /*
  15. * Ignore file section.
  16. *
  17. * "!" may be included any time to reset the list (i.e. ignore nothing);
  18. * "*" may be specified to ignore everything. It stays as the first
  19. * element forever, unless a "!" clears it out.
  20. */
  21. static char **ign_list; /* List of files to ignore in update
  22. * and import */
  23. static char **s_ign_list = NULL;
  24. static int ign_count; /* Number of active entries */
  25. static int s_ign_count = 0;
  26. static int ign_size; /* This many slots available (plus
  27. * one for a NULL) */
  28. static int ign_hold = -1; /* Index where first "temporary" item
  29. * is held */
  30. const char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state\
  31. .nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj\
  32. *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$";
  33. #define IGN_GROW 16 /* grow the list by 16 elements at a
  34. * time */
  35. /* Nonzero if we have encountered an -I ! directive, which means one should
  36. no longer ask the server about what is in CVSROOTADM_IGNORE. */
  37. int ign_inhibit_server;
  38. /*
  39. * To the "ignore list", add the hard-coded default ignored wildcards above,
  40. * the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
  41. * ~/.cvsignore and the wildcards found in the CVSIGNORE environment
  42. * variable.
  43. */
  44. void
  45. ign_setup ()
  46. {
  47. char *home_dir;
  48. char *tmp;
  49. ign_inhibit_server = 0;
  50. /* Start with default list and special case */
  51. tmp = xstrdup (ign_default);
  52. ign_add (tmp, 0);
  53. free (tmp);
  54. /* The client handles another way, by (after it does its own ignore file
  55. processing, and only if !ign_inhibit_server), letting the server
  56. know about the files and letting it decide whether to ignore
  57. them based on CVSROOOTADM_IGNORE. */
  58. if (!current_parsed_root->isremote)
  59. {
  60. char *file = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM)
  61. + sizeof (CVSROOTADM_IGNORE) + 10);
  62. /* Then add entries found in repository, if it exists */
  63. (void) sprintf (file, "%s/%s/%s", current_parsed_root->directory,
  64. CVSROOTADM, CVSROOTADM_IGNORE);
  65. ign_add_file (file, 0);
  66. free (file);
  67. }
  68. /* Then add entries found in home dir, (if user has one) and file exists */
  69. home_dir = get_homedir ();
  70. /* If we can't find a home directory, ignore ~/.cvsignore. This may
  71. make tracking down problems a bit of a pain, but on the other
  72. hand it might be obnoxious to complain when CVS will function
  73. just fine without .cvsignore (and many users won't even know what
  74. .cvsignore is). */
  75. if (home_dir)
  76. {
  77. char *file = strcat_filename_onto_homedir (home_dir, CVSDOTIGNORE);
  78. ign_add_file (file, 0);
  79. free (file);
  80. }
  81. /* Then add entries found in CVSIGNORE environment variable. */
  82. ign_add (getenv (IGNORE_ENV), 0);
  83. /* Later, add ignore entries found in -I arguments */
  84. }
  85. /*
  86. * Open a file and read lines, feeding each line to a line parser. Arrange
  87. * for keeping a temporary list of wildcards at the end, if the "hold"
  88. * argument is set.
  89. */
  90. void
  91. ign_add_file (file, hold)
  92. char *file;
  93. int hold;
  94. {
  95. FILE *fp;
  96. char *line = NULL;
  97. size_t line_allocated = 0;
  98. /* restore the saved list (if any) */
  99. if (s_ign_list != NULL)
  100. {
  101. int i;
  102. for (i = 0; i < s_ign_count; i++)
  103. ign_list[i] = s_ign_list[i];
  104. ign_count = s_ign_count;
  105. ign_list[ign_count] = NULL;
  106. s_ign_count = 0;
  107. free (s_ign_list);
  108. s_ign_list = NULL;
  109. }
  110. /* is this a temporary ignore file? */
  111. if (hold)
  112. {
  113. /* re-set if we had already done a temporary file */
  114. if (ign_hold >= 0)
  115. {
  116. int i;
  117. for (i = ign_hold; i < ign_count; i++)
  118. free (ign_list[i]);
  119. ign_count = ign_hold;
  120. ign_list[ign_count] = NULL;
  121. }
  122. else
  123. {
  124. ign_hold = ign_count;
  125. }
  126. }
  127. /* load the file */
  128. fp = CVS_FOPEN (file, "r");
  129. if (fp == NULL)
  130. {
  131. if (! existence_error (errno))
  132. error (0, errno, "cannot open %s", file);
  133. return;
  134. }
  135. while (getline (&line, &line_allocated, fp) >= 0)
  136. ign_add (line, hold);
  137. if (ferror (fp))
  138. error (0, errno, "cannot read %s", file);
  139. if (fclose (fp) < 0)
  140. error (0, errno, "cannot close %s", file);
  141. free (line);
  142. }
  143. /* Parse a line of space-separated wildcards and add them to the list. */
  144. void
  145. ign_add (ign, hold)
  146. char *ign;
  147. int hold;
  148. {
  149. if (!ign || !*ign)
  150. return;
  151. for (; *ign; ign++)
  152. {
  153. char *mark;
  154. char save;
  155. /* ignore whitespace before the token */
  156. if (isspace ((unsigned char) *ign))
  157. continue;
  158. /* If we have used up all the space, add some more. Do this before
  159. processing `!', since an "empty" list still contains the `CVS'
  160. entry. */
  161. if (ign_count >= ign_size)
  162. {
  163. ign_size += IGN_GROW;
  164. ign_list = (char **) xrealloc ((char *) ign_list,
  165. (ign_size + 1) * sizeof (char *));
  166. }
  167. /*
  168. * if we find a single character !, we must re-set the ignore list
  169. * (saving it if necessary). We also catch * as a special case in a
  170. * global ignore file as an optimization
  171. */
  172. if ((!*(ign+1) || isspace ((unsigned char) *(ign+1)))
  173. && (*ign == '!' || *ign == '*'))
  174. {
  175. if (!hold)
  176. {
  177. /* permanently reset the ignore list */
  178. int i;
  179. for (i = 0; i < ign_count; i++)
  180. free (ign_list[i]);
  181. ign_count = 1;
  182. /* Always ignore the "CVS" directory. */
  183. ign_list[0] = xstrdup("CVS");
  184. ign_list[1] = NULL;
  185. /* if we are doing a '!', continue; otherwise add the '*' */
  186. if (*ign == '!')
  187. {
  188. ign_inhibit_server = 1;
  189. continue;
  190. }
  191. }
  192. else if (*ign == '!')
  193. {
  194. /* temporarily reset the ignore list */
  195. int i;
  196. if (ign_hold >= 0)
  197. {
  198. for (i = ign_hold; i < ign_count; i++)
  199. free (ign_list[i]);
  200. ign_hold = -1;
  201. }
  202. if (s_ign_list)
  203. {
  204. /* Don't save the ignore list twice - if there are two
  205. * bangs in a local .cvsignore file then we don't want to
  206. * save the new list the first bang created.
  207. *
  208. * We still need to free the "new" ignore list.
  209. */
  210. for (i = 0; i < ign_count; i++)
  211. free (ign_list[i]);
  212. }
  213. else
  214. {
  215. /* Save the ignore list for later. */
  216. s_ign_list = xmalloc (ign_count * sizeof (char *));
  217. for (i = 0; i < ign_count; i++)
  218. s_ign_list[i] = ign_list[i];
  219. s_ign_count = ign_count;
  220. }
  221. ign_count = 1;
  222. /* Always ignore the "CVS" directory. */
  223. ign_list[0] = xstrdup ("CVS");
  224. ign_list[1] = NULL;
  225. continue;
  226. }
  227. }
  228. /* find the end of this token */
  229. for (mark = ign; *mark && !isspace ((unsigned char) *mark); mark++)
  230. /* do nothing */ ;
  231. save = *mark;
  232. *mark = '\0';
  233. ign_list[ign_count++] = xstrdup (ign);
  234. ign_list[ign_count] = NULL;
  235. *mark = save;
  236. if (save)
  237. ign = mark;
  238. else
  239. ign = mark - 1;
  240. }
  241. }
  242. /* Return true if the given filename should be ignored by update or import,
  243. * else return false.
  244. */
  245. int
  246. ign_name (name)
  247. char *name;
  248. {
  249. char **cpp = ign_list;
  250. if (cpp == NULL)
  251. return 0;
  252. while (*cpp)
  253. if (CVS_FNMATCH (*cpp++, name, 0) == 0)
  254. return 1;
  255. return 0;
  256. }
  257. /* FIXME: This list of dirs to ignore stuff seems not to be used.
  258. Really? send_dirent_proc and update_dirent_proc both call
  259. ignore_directory and do_module calls ign_dir_add. No doubt could
  260. use some documentation/testsuite work. */
  261. static char **dir_ign_list = NULL;
  262. static int dir_ign_max = 0;
  263. static int dir_ign_current = 0;
  264. /* Add a directory to list of dirs to ignore. */
  265. void
  266. ign_dir_add (name)
  267. char *name;
  268. {
  269. /* Make sure we've got the space for the entry. */
  270. if (dir_ign_current <= dir_ign_max)
  271. {
  272. dir_ign_max += IGN_GROW;
  273. dir_ign_list =
  274. (char **) xrealloc (dir_ign_list,
  275. (dir_ign_max + 1) * sizeof (char *));
  276. }
  277. dir_ign_list[dir_ign_current++] = xstrdup (name);
  278. }
  279. /* Return nonzero if NAME is part of the list of directories to ignore. */
  280. int
  281. ignore_directory (name)
  282. const char *name;
  283. {
  284. int i;
  285. if (!dir_ign_list)
  286. return 0;
  287. i = dir_ign_current;
  288. while (i--)
  289. {
  290. if (strncmp (name, dir_ign_list[i], strlen (dir_ign_list[i])+1) == 0)
  291. return 1;
  292. }
  293. return 0;
  294. }
  295. /*
  296. * Process the current directory, looking for files not in ILIST and
  297. * not on the global ignore list for this directory. If we find one,
  298. * call PROC passing it the name of the file and the update dir.
  299. * ENTRIES is the entries list, which is used to identify known
  300. * directories. ENTRIES may be NULL, in which case we assume that any
  301. * directory with a CVS administration directory is known.
  302. */
  303. void
  304. ignore_files (ilist, entries, update_dir, proc)
  305. List *ilist;
  306. List *entries;
  307. const char *update_dir;
  308. Ignore_proc proc;
  309. {
  310. int subdirs;
  311. DIR *dirp;
  312. struct dirent *dp;
  313. struct stat sb;
  314. char *file;
  315. const char *xdir;
  316. List *files;
  317. Node *p;
  318. /* Set SUBDIRS if we have subdirectory information in ENTRIES. */
  319. if (entries == NULL)
  320. subdirs = 0;
  321. else
  322. {
  323. struct stickydirtag *sdtp = entries->list->data;
  324. subdirs = sdtp == NULL || sdtp->subdirs;
  325. }
  326. /* we get called with update_dir set to "." sometimes... strip it */
  327. if (strcmp (update_dir, ".") == 0)
  328. xdir = "";
  329. else
  330. xdir = update_dir;
  331. dirp = CVS_OPENDIR (".");
  332. if (dirp == NULL)
  333. {
  334. error (0, errno, "cannot open current directory");
  335. return;
  336. }
  337. ign_add_file (CVSDOTIGNORE, 1);
  338. wrap_add_file (CVSDOTWRAPPER, 1);
  339. /* Make a list for the files. */
  340. files = getlist ();
  341. while (errno = 0, (dp = CVS_READDIR (dirp)) != NULL)
  342. {
  343. file = dp->d_name;
  344. if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
  345. continue;
  346. if (findnode_fn (ilist, file) != NULL)
  347. continue;
  348. if (subdirs)
  349. {
  350. Node *node;
  351. node = findnode_fn (entries, file);
  352. if (node != NULL
  353. && ((Entnode *) node->data)->type == ENT_SUBDIR)
  354. {
  355. char *p;
  356. int dir;
  357. /* For consistency with past behaviour, we only ignore
  358. this directory if there is a CVS subdirectory.
  359. This will normally be the case, but the user may
  360. have messed up the working directory somehow. */
  361. p = xmalloc (strlen (file) + sizeof CVSADM + 10);
  362. sprintf (p, "%s/%s", file, CVSADM);
  363. dir = isdir (p);
  364. free (p);
  365. if (dir)
  366. continue;
  367. }
  368. }
  369. /* We could be ignoring FIFOs and other files which are neither
  370. regular files nor directories here. */
  371. if (ign_name (file))
  372. continue;
  373. if (
  374. #ifdef DT_DIR
  375. dp->d_type != DT_UNKNOWN ||
  376. #endif
  377. CVS_LSTAT (file, &sb) != -1)
  378. {
  379. if (
  380. #ifdef DT_DIR
  381. dp->d_type == DT_DIR
  382. || (dp->d_type == DT_UNKNOWN && S_ISDIR (sb.st_mode))
  383. #else
  384. S_ISDIR (sb.st_mode)
  385. #endif
  386. )
  387. {
  388. if (! subdirs)
  389. {
  390. char *temp;
  391. temp = xmalloc (strlen (file) + sizeof (CVSADM) + 10);
  392. (void) sprintf (temp, "%s/%s", file, CVSADM);
  393. if (isdir (temp))
  394. {
  395. free (temp);
  396. continue;
  397. }
  398. free (temp);
  399. }
  400. }
  401. #ifdef S_ISLNK
  402. else if (
  403. #ifdef DT_DIR
  404. dp->d_type == DT_LNK
  405. || (dp->d_type == DT_UNKNOWN && S_ISLNK(sb.st_mode))
  406. #else
  407. S_ISLNK (sb.st_mode)
  408. #endif
  409. )
  410. {
  411. continue;
  412. }
  413. #endif
  414. }
  415. p = getnode ();
  416. p->type = FILES;
  417. p->key = xstrdup (file);
  418. (void) addnode (files, p);
  419. }
  420. if (errno != 0)
  421. error (0, errno, "error reading current directory");
  422. (void) CVS_CLOSEDIR (dirp);
  423. sortlist (files, fsortcmp);
  424. for (p = files->list->next; p != files->list; p = p->next)
  425. (*proc) (p->key, xdir);
  426. dellist (&files);
  427. }