]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/dependencies.c
dependencies: Add support for arch() namespace
[pakfire.git] / src / libpakfire / dependencies.c
CommitLineData
2a623cf8
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2022 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <ctype.h>
22#include <errno.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <solv/pool.h>
27
28#include <pakfire/dependencies.h>
29#include <pakfire/logging.h>
30#include <pakfire/package.h>
31#include <pakfire/pakfire.h>
d973a13d 32#include <pakfire/string.h>
2a623cf8
MT
33#include <pakfire/util.h>
34
6f3fad3b
MT
35const struct pakfire_dep pakfire_deps[] = {
36 { PAKFIRE_PKG_PROVIDES, "provides" },
37 { PAKFIRE_PKG_PREREQUIRES, "prerequires" },
38 { PAKFIRE_PKG_REQUIRES, "requires" },
39 { PAKFIRE_PKG_CONFLICTS, "conflicts" },
40 { PAKFIRE_PKG_OBSOLETES, "obsoletes" },
41 { PAKFIRE_PKG_RECOMMENDS, "recommends" },
42 { PAKFIRE_PKG_SUGGESTS, "suggests" },
43 { PAKFIRE_PKG_SUPPLEMENTS, "supplements" },
44 { PAKFIRE_PKG_ENHANCES, "enhances" },
45 { 0, NULL },
46};
47
2a623cf8
MT
48static const struct pakfire_rich_operation {
49 const char* keyword;
50 const int flags;
51} pakfire_rich_operations[] = {
52 { "and ", REL_AND },
53 { "or ", REL_OR },
54 { "if ", REL_COND },
55 { "unless ", REL_UNLESS },
56 { "else ", REL_ELSE },
57 { "with ", REL_WITH },
58 { "without ", REL_WITHOUT },
59 { NULL, 0 },
60};
61
62const char* pakfire_dep2str(struct pakfire* pakfire, Id id) {
63 Pool* pool = pakfire_get_solv_pool(pakfire);
64 if (!pool)
65 return NULL;
66
67 return pool_dep2str(pool, id);
68}
69
70/*
71 Reads the string until the next space is found.
72
73 This function considers opening and closing brackets.
74*/
75static size_t skip(const char** s, const char** n) {
76 const char* p = *s;
77
78 // Store p in n
79 *n = p;
80
81 int brackets = 0;
82
83 while (*p) {
84 switch (*p) {
f6d5b459 85 // End on space, newline, =, >, <, or comma
2a623cf8 86 case ' ':
f6d5b459 87 case '\n':
87b8c34b
MT
88 case '=':
89 case '>':
90 case '<':
2a623cf8
MT
91 case ',':
92 goto END;
93
94 // Increment counter on opening bracket
95 case '(':
96 brackets++;
97 break;
98
99 // End on the last closing bracket
100 case ')':
101 if (brackets-- <= 0)
102 goto END;
103 break;
104 }
105
106 // Move to next character
107 p++;
108 }
109
110END:
111 *s = p;
112
113 // Return the length of the skipped string
114 return p - *n;
115}
116
117/*
118 This function parses any namespaced dependencies
119*/
120static Id pakfire_parse_namespace(Pool* pool, const char* s) {
121 const char* p = strchr(s, '(');
122 if (!p)
123 return 0;
124
125 // Store the namespace ID
126 Id namespace = pool_strn2id(pool, s, p - s, 1);
127
128 // Find the end of the string
7e8fed41 129 s = strchr(p, ')');
2a623cf8
MT
130 if (!s)
131 return 0;
132
133 Id id = pool_strn2id(pool, p + 1, s - p - 1, 1);
134
135 // Bring it all together
136 return pool_rel2id(pool, namespace, id, REL_NAMESPACE, 1);
137}
138
139/*
140 This function parses a simple dependency like "foo = 1.2.3"
141*/
142static Id pakfire_parse_dep(struct pakfire* pakfire, const char** s) {
143 Id id = ID_NULL;
144
145 if (!s) {
146 errno = EINVAL;
147 return 0;
148 }
149
150 const char* p = *s;
151 const char* n = NULL;
152
153 // Ignore empty strings
154 if (!p)
155 return ID_NULL;
156
157 Pool* pool = pakfire_get_solv_pool(pakfire);
158
159 // Consume any leading space
166b1655 160 while (isspace(*p))
2a623cf8
MT
161 p++;
162
163 // Find the first part
164 size_t l = skip(&p, &n);
165
166 // Add name to pool
f6c74b93 167 if (pakfire_string_startswith(n, "pakfire(") || pakfire_string_startswith(n, "arch("))
2a623cf8
MT
168 id = pakfire_parse_namespace(pool, n);
169 else
170 id = pool_strn2id(pool, n, l, 1);
171
172 // Consume any more space
166b1655 173 while (isspace(*p))
2a623cf8
MT
174 p++;
175
176 if (*p == '<' || *p == '=' || *p == '>') {
177 int flags = 0;
178
179 while (1) {
180 if (*p == '<')
181 flags |= REL_LT;
182 else if (*p == '=')
183 flags |= REL_EQ;
184 else if (*p == '>')
185 flags |= REL_GT;
186 else
187 break;
188
189 p++;
190 }
191
192 // Consume any more space
166b1655 193 while (isspace(*p))
2a623cf8
MT
194 p++;
195
196 // Find the length of EVR
197 l = skip(&p, &n);
198
199 // Strip zero epoch
200 if (pakfire_string_startswith(n, "0:"))
201 n += 2;
202
203 // Add EVR to pool
204 Id evr = pool_strn2id(pool, n, l, 1);
205
206 // Combine everything
207 id = pool_rel2id(pool, id, evr, flags, 1);
208 }
209
166b1655
MT
210 // Consume any more space
211 while (isspace(*p))
212 p++;
213
2a623cf8
MT
214 *s = p;
215
216 return id;
217}
218
219/*
220 This function parses any rich dependencies
221*/
222static Id pakfire_parse_rich_dep(struct pakfire* pakfire, const char** dep, int flags) {
223 const char* p = *dep;
224 Id id;
225
226 // Do not try parsing empty strings
227 if (!*p)
228 return ID_NULL;
229
230 // Must be starting with an opening bracket
231 if (!flags && *p++ != '(')
232 return ID_NULL;
233
234 switch (*p) {
235 // A new rich dependency
236 case '(':
237 id = pakfire_parse_rich_dep(pakfire, &p, 0);
238 if (!id)
239 return id;
240 break;
241
242 // End
243 case ')':
244 return ID_NULL;
245
246 // Parse a regular dependency
247 default:
248 id = pakfire_parse_dep(pakfire, &p);
249 if (!id)
250 return id;
251 break;
252 }
253
254 // Consume any space
166b1655 255 while (isspace(*p))
2a623cf8
MT
256 p++;
257
258 // If we have successfully parsed something, we would expect a closing bracket
259 if (*p == ')') {
260 *dep = p + 1;
261 return id;
262 }
263
264 // Search for a keyword
265 for (const struct pakfire_rich_operation* op = pakfire_rich_operations; op->keyword; op++) {
266 if (pakfire_string_startswith(p, op->keyword)) {
267 // Skip the keyword
268 p += strlen(op->keyword);
269
270 // Store the flags
271 flags = op->flags;
272 break;
273 }
274 }
275
276 // If there was no keyword, we are done
277 if (!flags)
278 return ID_NULL;
279
280 // Parse the next bit
281 Id evr = pakfire_parse_rich_dep(pakfire, &p, flags);
282
283 // Abort if there was a problem
284 if (!evr)
285 return ID_NULL;
286
287 // Store until where we have parsed the string
288 *dep = p;
289
290 Pool* pool = pakfire_get_solv_pool(pakfire);
291
292 // Combine everything
293 return pool_rel2id(pool, id, evr, flags, 1);
294}
295
80df1d12 296Id pakfire_str2dep(struct pakfire* pakfire, const char* dep) {
2a623cf8
MT
297 Id id = ID_NULL;
298
299 // Invalid input
80df1d12 300 if (!dep) {
2a623cf8
MT
301 errno = EINVAL;
302 return id;
303 }
304
305 // Ignore empty strings
80df1d12 306 if (!*dep)
2a623cf8
MT
307 return id;
308
80df1d12
MT
309 const char* s = dep;
310
2a623cf8
MT
311 // Consume any leading space
312 if (isspace(*s))
313 s++;
314
315 // Ignore any comments
316 if (*s == '#')
317 return id;
318
319 // Parse any rich dependencies
320 if (*s == '(')
321 id = pakfire_parse_rich_dep(pakfire, &s, 0);
322 else
323 id = pakfire_parse_dep(pakfire, &s);
324
325 // Return nothing if we could not parse the entire string
80df1d12
MT
326 if (id && *s) {
327 DEBUG(pakfire, "Invalid dependency: %s\n", dep);
2a623cf8 328 id = ID_NULL;
80df1d12 329 }
2a623cf8
MT
330
331 return id;
332}
333
334int pakfire_str2deps(struct pakfire* pakfire, struct pakfire_package* pkg,
6f3fad3b 335 const enum pakfire_package_key key, const char* deps) {
2a623cf8 336 char* p = NULL;
6f3fad3b 337 int r = 0;
2a623cf8
MT
338
339 // Check for valid inputs
6f3fad3b 340 if (!deps) {
2a623cf8
MT
341 errno = EINVAL;
342 return 1;
343 }
344
345 // Copy deps into a working buffer
346 char* buffer = strdup(deps);
347 if (!buffer)
6f3fad3b 348 goto ERROR;
2a623cf8
MT
349
350 char* dep = strtok_r(buffer, "\n", &p);
351
352 // Walk through the buffer line by line
353 while (dep) {
354 DEBUG(pakfire, "Found dep '%s'\n", dep);
355
356 // Add the dependency
6f3fad3b
MT
357 r = pakfire_package_add_dep(pkg, key, dep);
358 if (r)
359 goto ERROR;
2a623cf8
MT
360
361 // Move on to next token
362 dep = strtok_r(NULL, "\n", &p);
363 }
364
6f3fad3b
MT
365ERROR:
366 if (buffer)
367 free(buffer);
368
369 return r;
2a623cf8 370}