Add ENABLE_COMPLEX_DEPS flag
[platform/upstream/libsolv.git] / src / filelistfilter.c
1 /*
2  * Copyright (c) 2018, SUSE LLC
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * filelistfilter.c
10  *
11  * Support repodata with a filelist filtered by a custom filter
12  */
13
14 #define _GNU_SOURCE
15 #include <string.h>
16 #include <fnmatch.h>
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <unistd.h>
22
23 #include "repo.h"
24 #include "pool.h"
25 #include "util.h"
26
27 static Id default_filelist_filter;
28
29 #define FF_EXACT        0
30 #define FF_END          1
31 #define FF_START        2
32 #define FF_SUB          3               /* FF_END | FF_START */
33 #define FF_GLOB         4
34 #define FF_START5       5
35
36 void
37 repodata_free_filelistfilter(Repodata *data)
38 {
39   if (data->filelistfilter)
40     {
41       if (data->filelistfilter != &default_filelist_filter)
42         solv_free(data->filelistfilter);
43       data->filelistfilter = 0;
44     }
45   data->filelistfilterdata = solv_free(data->filelistfilterdata);
46 }
47
48 static void
49 repodata_set_filelistfilter(Repodata *data)
50 {
51   Id type;
52   Queue q;
53   int i, j;
54   char *filterdata;
55   int nfilterdata;
56
57   if (data->filelistfilter && data->filelistfilter != &default_filelist_filter)
58     data->filelistfilter = solv_free(data->filelistfilter);
59   data->filelistfilterdata = solv_free(data->filelistfilterdata);
60   type = repodata_lookup_type(data, SOLVID_META, REPOSITORY_FILTEREDFILELIST);
61   if (type != REPOKEY_TYPE_IDARRAY)
62     {
63       data->filelistfilter = &default_filelist_filter;
64       return;
65     }
66   queue_init(&q);
67   repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_FILTEREDFILELIST, &q);
68   if (q.count == 3)
69     {
70       /* check if this is the default filter */
71       int t = 0;
72       for (i = 0; i < 3; i++)
73         {
74           Id id = q.elements[i];
75           const char *g = data->localpool ? stringpool_id2str(&data->spool, id) : pool_id2str(data->repo->pool, id); 
76           if (!strcmp(g, "*bin/*"))
77             t |= 1;
78           else if (!strcmp(g, "/etc/*"))
79             t |= 2;
80           else if (!strcmp(g, "/usr/lib/sendmail"))
81             t |= 4;
82         }
83       if (t == 7)
84         {
85           queue_free(&q);
86           data->filelistfilter = &default_filelist_filter;
87           return;
88         }
89     }
90   data->filelistfilter = solv_calloc(q.count * 2 + 1, sizeof(Id));
91   filterdata = solv_calloc_block(1, 1, 255);
92   nfilterdata = 1;
93   
94   for (i = j = 0; i < q.count; i++)
95     {
96       Id id = q.elements[i];
97       const char *g = data->localpool ? stringpool_id2str(&data->spool, id) : pool_id2str(data->repo->pool, id); 
98       const char *p;
99       int t = FF_EXACT;
100       int gl;
101       if (!id || !g || !*g)
102         continue;
103       for (p = g; *p && t != FF_GLOB; p++)
104         {
105           if (*p == '*')
106             {
107               if (p == g)
108                 t |= FF_END;
109               else if (!p[1])
110                 t |= FF_START;
111               else
112                 t = FF_GLOB;
113             }
114           else if (*p == '[' || *p == '?')
115             t = FF_GLOB;
116         }
117       gl = strlen(g);
118       if (t == FF_END)  /* not supported */
119         t = FF_GLOB;
120       if (t == FF_START && gl == 5)
121         t = FF_START5;
122       filterdata = solv_extend(filterdata, nfilterdata, gl + 1, 1, 255);
123       data->filelistfilter[j++] = nfilterdata;
124       data->filelistfilter[j++] = t;
125       switch (t)
126         {
127         case FF_START:
128         case FF_START5:
129           strcpy(filterdata + nfilterdata, g);
130           filterdata[nfilterdata + gl - 1] = 0;
131           nfilterdata += gl;
132           break;
133         case FF_SUB:
134           strcpy(filterdata + nfilterdata, g + 1);
135           filterdata[nfilterdata + gl - 2] = 0;
136           nfilterdata += gl - 1;
137           break;
138         default:
139           strcpy(filterdata + nfilterdata, g);
140           nfilterdata += gl + 1;
141           break;
142         }
143     }
144   filterdata = solv_realloc(filterdata, nfilterdata);
145   data->filelistfilter[j++] = 0;
146   data->filelistfilterdata = filterdata;
147   queue_free(&q);
148 }
149
150 int
151 repodata_filelistfilter_matches(Repodata *data, const char *str)
152 {
153   Id *ff;
154   if (data && !data->filelistfilter)
155     repodata_set_filelistfilter(data);
156   if (!data || data->filelistfilter == &default_filelist_filter)
157     {
158       /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
159       if (strstr(str, "bin/"))
160         return 1;
161       if (!strncmp(str, "/etc/", 5))
162         return 1;
163       if (!strcmp(str, "/usr/lib/sendmail"))
164         return 1;
165       return 0;
166     }
167   for (ff = data->filelistfilter; *ff; ff += 2)
168     {
169       const char *g = data->filelistfilterdata + *ff;
170       switch (ff[1])
171         {
172         case FF_EXACT:
173           if (!strcmp(str, g))
174             return 1;
175           break;
176         case FF_START:
177           if (!strncmp(str, g, strlen(g)))
178             return 1;
179           break;
180         case FF_SUB:
181           if (!strstr(str, g))
182             return 1;
183           break;
184         case FF_START5:
185           if (!strncmp(str, g, 5))
186             return 1;
187           break;
188         default:
189           if (!fnmatch(g, str, 0))
190             return 1;
191           break;
192         }
193     }
194   return 0;
195 }
196