exclist.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /* Per-directory exclusion files for tar.
  2. Copyright 2014 Free Software Foundation, Inc.
  3. This file is part of GNU tar.
  4. GNU tar is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. GNU tar is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include <system.h>
  16. #include <quotearg.h>
  17. #include <fnmatch.h>
  18. #include <wordsplit.h>
  19. #include "common.h"
  20. typedef void (*add_fn) (struct exclude *, char const *, int, void *);
  21. struct vcs_ignore_file
  22. {
  23. char const *filename;
  24. int flags;
  25. add_fn addfn;
  26. void *(*initfn) (void *);
  27. void *data;
  28. };
  29. static struct vcs_ignore_file *get_vcs_ignore_file (const char *name);
  30. struct excfile
  31. {
  32. struct excfile *next;
  33. int flags;
  34. char name[1];
  35. };
  36. static struct excfile *excfile_head, *excfile_tail;
  37. void
  38. excfile_add (const char *name, int flags)
  39. {
  40. struct excfile *p = xmalloc (sizeof (*p) + strlen (name));
  41. p->next = NULL;
  42. p->flags = flags;
  43. strcpy (p->name, name);
  44. if (excfile_tail)
  45. excfile_tail->next = p;
  46. else
  47. excfile_head = p;
  48. excfile_tail = p;
  49. }
  50. struct exclist
  51. {
  52. struct exclist *next, *prev;
  53. int flags;
  54. struct exclude *excluded;
  55. };
  56. void
  57. info_attach_exclist (struct tar_stat_info *dir)
  58. {
  59. struct excfile *file;
  60. struct exclist *head = NULL, *tail = NULL, *ent;
  61. struct vcs_ignore_file *vcsfile;
  62. if (dir->exclude_list)
  63. return;
  64. for (file = excfile_head; file; file = file->next)
  65. {
  66. if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0)
  67. {
  68. FILE *fp;
  69. struct exclude *ex = NULL;
  70. int fd = subfile_open (dir, file->name, O_RDONLY);
  71. if (fd == -1)
  72. {
  73. open_error (file->name);
  74. continue;
  75. }
  76. fp = fdopen (fd, "r");
  77. if (!fp)
  78. {
  79. ERROR ((0, errno, _("%s: fdopen failed"), file->name));
  80. close (fd);
  81. continue;
  82. }
  83. if (!ex)
  84. ex = new_exclude ();
  85. vcsfile = get_vcs_ignore_file (file->name);
  86. if (vcsfile->initfn)
  87. vcsfile->data = vcsfile->initfn (vcsfile->data);
  88. if (add_exclude_fp (vcsfile->addfn, ex, fp,
  89. EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED, '\n',
  90. vcsfile->data))
  91. {
  92. int e = errno;
  93. FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name)));
  94. }
  95. fclose (fp);
  96. ent = xmalloc (sizeof (*ent));
  97. ent->excluded = ex;
  98. ent->flags = file->flags == EXCL_DEFAULT
  99. ? file->flags : vcsfile->flags;
  100. ent->prev = tail;
  101. ent->next = NULL;
  102. if (tail)
  103. tail->next = ent;
  104. else
  105. head = ent;
  106. tail = ent;
  107. }
  108. }
  109. dir->exclude_list = head;
  110. }
  111. void
  112. info_free_exclist (struct tar_stat_info *dir)
  113. {
  114. struct exclist *ep = dir->exclude_list;
  115. while (ep)
  116. {
  117. struct exclist *next = ep->next;
  118. free_exclude (ep->excluded);
  119. free (ep);
  120. ep = next;
  121. }
  122. dir->exclude_list = NULL;
  123. }
  124. /* Return nonzero if file NAME is excluded. */
  125. bool
  126. excluded_name (char const *name, struct tar_stat_info *st)
  127. {
  128. struct exclist *ep;
  129. const char *rname = NULL;
  130. char *bname = NULL;
  131. bool result;
  132. int nr = 0;
  133. name += FILE_SYSTEM_PREFIX_LEN (name);
  134. /* Try global exclusion list first */
  135. if (excluded_file_name (excluded, name))
  136. return true;
  137. if (!st)
  138. return false;
  139. for (result = false; st && !result; st = st->parent, nr = EXCL_NON_RECURSIVE)
  140. {
  141. for (ep = st->exclude_list; ep; ep = ep->next)
  142. {
  143. if (ep->flags & nr)
  144. continue;
  145. if ((result = excluded_file_name (ep->excluded, name)))
  146. break;
  147. if (!rname)
  148. {
  149. rname = name;
  150. /* Skip leading ./ */
  151. while (*rname == '.' && ISSLASH (rname[1]))
  152. rname += 2;
  153. }
  154. if ((result = excluded_file_name (ep->excluded, rname)))
  155. break;
  156. if (!bname)
  157. bname = base_name (name);
  158. if ((result = excluded_file_name (ep->excluded, bname)))
  159. break;
  160. }
  161. }
  162. free (bname);
  163. return result;
  164. }
  165. static void
  166. cvs_addfn (struct exclude *ex, char const *pattern, int options, void *data)
  167. {
  168. struct wordsplit ws;
  169. size_t i;
  170. if (wordsplit (pattern, &ws,
  171. WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS))
  172. return;
  173. for (i = 0; i < ws.ws_wordc; i++)
  174. add_exclude (ex, ws.ws_wordv[i], options);
  175. wordsplit_free (&ws);
  176. }
  177. static void
  178. git_addfn (struct exclude *ex, char const *pattern, int options, void *data)
  179. {
  180. while (isspace (*pattern))
  181. ++pattern;
  182. if (*pattern == 0 || *pattern == '#')
  183. return;
  184. if (*pattern == '\\' && pattern[1] == '#')
  185. ++pattern;
  186. add_exclude (ex, pattern, options);
  187. }
  188. static void
  189. bzr_addfn (struct exclude *ex, char const *pattern, int options, void *data)
  190. {
  191. while (isspace (*pattern))
  192. ++pattern;
  193. if (*pattern == 0 || *pattern == '#')
  194. return;
  195. if (*pattern == '!')
  196. {
  197. if (*++pattern == '!')
  198. ++pattern;
  199. else
  200. options |= EXCLUDE_INCLUDE;
  201. }
  202. /* FIXME: According to the docs, globbing patterns are rsync-style,
  203. and regexps are perl-style. */
  204. if (strncmp (pattern, "RE:", 3) == 0)
  205. {
  206. pattern += 3;
  207. options &= ~EXCLUDE_WILDCARDS;
  208. options |= EXCLUDE_REGEX;
  209. }
  210. add_exclude (ex, pattern, options);
  211. }
  212. static void *
  213. hg_initfn (void *data)
  214. {
  215. int *hgopt;
  216. static int hg_options;
  217. if (!data)
  218. hgopt = &hg_options;
  219. *hgopt = EXCLUDE_REGEX;
  220. return hgopt;
  221. }
  222. static void
  223. hg_addfn (struct exclude *ex, char const *pattern, int options, void *data)
  224. {
  225. int *hgopt = data;
  226. size_t len;
  227. while (isspace (*pattern))
  228. ++pattern;
  229. if (*pattern == 0 || *pattern == '#')
  230. return;
  231. if (strncmp (pattern, "syntax:", 7) == 0)
  232. {
  233. for (pattern += 7; isspace (*pattern); ++pattern)
  234. ;
  235. if (strcmp (pattern, "regexp") == 0)
  236. /* FIXME: Regexps must be perl-style */
  237. *hgopt = EXCLUDE_REGEX;
  238. else if (strcmp (pattern, "glob") == 0)
  239. *hgopt = EXCLUDE_WILDCARDS;
  240. /* Ignore unknown syntax */
  241. return;
  242. }
  243. len = strlen(pattern);
  244. if (pattern[len-1] == '/')
  245. {
  246. char *p;
  247. --len;
  248. p = xmalloc (len+1);
  249. memcpy (p, pattern, len);
  250. p[len] = 0;
  251. pattern = p;
  252. exclude_add_pattern_buffer (ex, p);
  253. options |= FNM_LEADING_DIR|EXCLUDE_ALLOC;
  254. }
  255. add_exclude (ex, pattern,
  256. ((*hgopt == EXCLUDE_REGEX)
  257. ? (options & ~EXCLUDE_WILDCARDS)
  258. : (options & ~EXCLUDE_REGEX)) | *hgopt);
  259. }
  260. static struct vcs_ignore_file vcs_ignore_files[] = {
  261. { ".cvsignore", EXCL_NON_RECURSIVE, cvs_addfn, NULL, NULL },
  262. { ".gitignore", 0, git_addfn, NULL, NULL },
  263. { ".bzrignore", 0, bzr_addfn, NULL, NULL },
  264. { ".hgignore", 0, hg_addfn, hg_initfn , NULL },
  265. { NULL, 0, git_addfn, NULL, NULL }
  266. };
  267. static struct vcs_ignore_file *
  268. get_vcs_ignore_file (const char *name)
  269. {
  270. struct vcs_ignore_file *p;
  271. for (p = vcs_ignore_files; p->filename; p++)
  272. if (strcmp (p->filename, name) == 0)
  273. break;
  274. return p;
  275. }
  276. void
  277. exclude_vcs_ignores (void)
  278. {
  279. struct vcs_ignore_file *p;
  280. for (p = vcs_ignore_files; p->filename; p++)
  281. excfile_add (p->filename, EXCL_DEFAULT);
  282. }