4
0

exclist.c 7.4 KB

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