]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/dependencies.c
9064fb0f659041d2979243c0ef22d5b8d717cc55
[people/ms/pakfire.git] / src / libpakfire / dependencies.c
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/evr.h>
27 #include <solv/pool.h>
28
29 // Enable legacy logging
30 #define PAKFIRE_LEGACY_LOGGING
31
32 #include <pakfire/dependencies.h>
33 #include <pakfire/logging.h>
34 #include <pakfire/package.h>
35 #include <pakfire/pakfire.h>
36 #include <pakfire/private.h>
37 #include <pakfire/string.h>
38 #include <pakfire/util.h>
39
40 const struct pakfire_dep pakfire_deps[] = {
41 { PAKFIRE_PKG_PROVIDES, "provides" },
42 { PAKFIRE_PKG_PREREQUIRES, "prerequires" },
43 { PAKFIRE_PKG_REQUIRES, "requires" },
44 { PAKFIRE_PKG_CONFLICTS, "conflicts" },
45 { PAKFIRE_PKG_OBSOLETES, "obsoletes" },
46 { PAKFIRE_PKG_RECOMMENDS, "recommends" },
47 { PAKFIRE_PKG_SUGGESTS, "suggests" },
48 { PAKFIRE_PKG_SUPPLEMENTS, "supplements" },
49 { PAKFIRE_PKG_ENHANCES, "enhances" },
50 { 0, NULL },
51 };
52
53 static const struct pakfire_rich_operation {
54 const char* keyword;
55 const int flags;
56 } pakfire_rich_operations[] = {
57 { "and ", REL_AND },
58 { "or ", REL_OR },
59 { "if ", REL_COND },
60 { "unless ", REL_UNLESS },
61 { "else ", REL_ELSE },
62 { "with ", REL_WITH },
63 { "without ", REL_WITHOUT },
64 { NULL, 0 },
65 };
66
67 /*
68 This function can compare package versions without a Pakfire instance initialized.
69 */
70 PAKFIRE_EXPORT int pakfire_static_version_compare(const char* evr1, const char* evr2) {
71 Pool* pool = NULL;
72 int r;
73
74 // Initialize the pool
75 pool = pool_create();
76 if (!pool)
77 return 0;
78
79 // Set to RPM mode
80 pool_setdisttype(pool, DISTTYPE_RPM);
81
82 // Perform comparison
83 r = pool_evrcmp_str(pool, evr1, evr2, EVRCMP_COMPARE);
84
85 // Free the pool
86 pool_free(pool);
87
88 return r;
89 }
90
91 const char* pakfire_dep2str(struct pakfire* pakfire, Id id) {
92 Pool* pool = pakfire_get_solv_pool(pakfire);
93 if (!pool)
94 return NULL;
95
96 return pool_dep2str(pool, id);
97 }
98
99 /*
100 Reads the string until the next space is found.
101
102 This function considers opening and closing brackets.
103 */
104 static size_t skip(const char** s, const char** n) {
105 const char* p = *s;
106
107 // Store p in n
108 *n = p;
109
110 int brackets = 0;
111
112 while (*p) {
113 switch (*p) {
114 // End on space, newline, =, >, <, or comma
115 case ' ':
116 case '\n':
117 case '=':
118 case '>':
119 case '<':
120 case ',':
121 goto END;
122
123 // Increment counter on opening bracket
124 case '(':
125 brackets++;
126 break;
127
128 // End on the last closing bracket
129 case ')':
130 if (brackets-- <= 0)
131 goto END;
132 break;
133 }
134
135 // Move to next character
136 p++;
137 }
138
139 END:
140 *s = p;
141
142 // Return the length of the skipped string
143 return p - *n;
144 }
145
146 /*
147 This function parses any namespaced dependencies
148 */
149 static Id pakfire_parse_namespace(Pool* pool, const char* s) {
150 const char* p = strchr(s, '(');
151 if (!p)
152 return 0;
153
154 // Store the namespace ID
155 Id namespace = pool_strn2id(pool, s, p - s, 1);
156
157 // Find the end of the string
158 s = strchr(p, ')');
159 if (!s)
160 return 0;
161
162 Id id = pool_strn2id(pool, p + 1, s - p - 1, 1);
163
164 // Bring it all together
165 return pool_rel2id(pool, namespace, id, REL_NAMESPACE, 1);
166 }
167
168 /*
169 This function parses a simple dependency like "foo = 1.2.3"
170 */
171 static Id pakfire_parse_dep(struct pakfire* pakfire, const char** s) {
172 Id id = ID_NULL;
173
174 if (!s) {
175 errno = EINVAL;
176 return 0;
177 }
178
179 const char* p = *s;
180 const char* n = NULL;
181
182 // Ignore empty strings
183 if (!p)
184 return ID_NULL;
185
186 Pool* pool = pakfire_get_solv_pool(pakfire);
187
188 // Consume any leading space
189 while (isspace(*p))
190 p++;
191
192 // Find the first part
193 size_t l = skip(&p, &n);
194
195 // Add name to pool
196 if (pakfire_string_startswith(n, "pakfire(") || pakfire_string_startswith(n, "arch("))
197 id = pakfire_parse_namespace(pool, n);
198 else
199 id = pool_strn2id(pool, n, l, 1);
200
201 // Consume any more space
202 while (isspace(*p))
203 p++;
204
205 if (*p == '<' || *p == '=' || *p == '>') {
206 int flags = 0;
207
208 while (1) {
209 if (*p == '<')
210 flags |= REL_LT;
211 else if (*p == '=')
212 flags |= REL_EQ;
213 else if (*p == '>')
214 flags |= REL_GT;
215 else
216 break;
217
218 p++;
219 }
220
221 // Consume any more space
222 while (isspace(*p))
223 p++;
224
225 // Find the length of EVR
226 l = skip(&p, &n);
227
228 // Strip zero epoch
229 if (pakfire_string_startswith(n, "0:"))
230 n += 2;
231
232 // Add EVR to pool
233 Id evr = pool_strn2id(pool, n, l, 1);
234
235 // Combine everything
236 id = pool_rel2id(pool, id, evr, flags, 1);
237 }
238
239 // Consume any more space
240 while (isspace(*p))
241 p++;
242
243 *s = p;
244
245 return id;
246 }
247
248 /*
249 This function parses any rich dependencies
250 */
251 static Id pakfire_parse_rich_dep(struct pakfire* pakfire, const char** dep, int flags) {
252 const char* p = *dep;
253 Id id;
254
255 // Do not try parsing empty strings
256 if (!*p)
257 return ID_NULL;
258
259 // Must be starting with an opening bracket
260 if (!flags && *p++ != '(')
261 return ID_NULL;
262
263 switch (*p) {
264 // A new rich dependency
265 case '(':
266 id = pakfire_parse_rich_dep(pakfire, &p, 0);
267 if (!id)
268 return id;
269 break;
270
271 // End
272 case ')':
273 return ID_NULL;
274
275 // Parse a regular dependency
276 default:
277 id = pakfire_parse_dep(pakfire, &p);
278 if (!id)
279 return id;
280 break;
281 }
282
283 // Consume any space
284 while (isspace(*p))
285 p++;
286
287 // If we have successfully parsed something, we would expect a closing bracket
288 if (*p == ')') {
289 *dep = p + 1;
290 return id;
291 }
292
293 // Search for a keyword
294 for (const struct pakfire_rich_operation* op = pakfire_rich_operations; op->keyword; op++) {
295 if (pakfire_string_startswith(p, op->keyword)) {
296 // Skip the keyword
297 p += strlen(op->keyword);
298
299 // Store the flags
300 flags = op->flags;
301 break;
302 }
303 }
304
305 // If there was no keyword, we are done
306 if (!flags)
307 return ID_NULL;
308
309 // Parse the next bit
310 Id evr = pakfire_parse_rich_dep(pakfire, &p, flags);
311
312 // Abort if there was a problem
313 if (!evr)
314 return ID_NULL;
315
316 // Store until where we have parsed the string
317 *dep = p;
318
319 Pool* pool = pakfire_get_solv_pool(pakfire);
320
321 // Combine everything
322 return pool_rel2id(pool, id, evr, flags, 1);
323 }
324
325 Id pakfire_str2dep(struct pakfire* pakfire, const char* dep) {
326 Id id = ID_NULL;
327
328 // Invalid input
329 if (!dep) {
330 errno = EINVAL;
331 return id;
332 }
333
334 // Ignore empty strings
335 if (!*dep)
336 return id;
337
338 const char* s = dep;
339
340 // Consume any leading space
341 if (isspace(*s))
342 s++;
343
344 // Ignore any comments
345 if (*s == '#')
346 return id;
347
348 // Parse any rich dependencies
349 if (*s == '(')
350 id = pakfire_parse_rich_dep(pakfire, &s, 0);
351 else
352 id = pakfire_parse_dep(pakfire, &s);
353
354 // Return nothing if we could not parse the entire string
355 if (id && *s) {
356 DEBUG(pakfire, "Invalid dependency: %s\n", dep);
357 id = ID_NULL;
358 }
359
360 return id;
361 }
362
363 int pakfire_str2deps(struct pakfire* pakfire, struct pakfire_package* pkg,
364 const enum pakfire_package_key key, const char* deps) {
365 char* p = NULL;
366 int r = 0;
367
368 // Check for valid inputs
369 if (!deps)
370 return -EINVAL;
371
372 // Copy deps into a working buffer
373 char* buffer = strdup(deps);
374 if (!buffer)
375 goto ERROR;
376
377 char* dep = strtok_r(buffer, "\n", &p);
378
379 // Walk through the buffer line by line
380 while (dep) {
381 // Add the dependency
382 r = pakfire_package_add_dep(pkg, key, dep);
383 if (r)
384 goto ERROR;
385
386 // Move on to next token
387 dep = strtok_r(NULL, "\n", &p);
388 }
389
390 ERROR:
391 if (buffer)
392 free(buffer);
393
394 return r;
395 }