]>
Commit | Line | Data |
---|---|---|
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 |
35 | const 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 |
48 | static 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 | ||
62 | const 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 | */ | |
75 | static 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 | ||
110 | END: | |
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 | */ | |
120 | static 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 | */ | |
142 | static 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 | */ | |
222 | static 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 | 296 | Id 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 | ||
334 | int 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 |
365 | ERROR: |
366 | if (buffer) | |
367 | free(buffer); | |
368 | ||
369 | return r; | |
2a623cf8 | 370 | } |