XBPS Library API 20250624
The X Binary Package System
transaction_ops.c
1/*-
2 * Copyright (c) 2009-2020 Juan Romero Pardines.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <stdio.h>
27#include <stdbool.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31#include <fnmatch.h>
32
33#include "xbps_api_impl.h"
34
35/**
36 * @file lib/transaction_ops.c
37 * @brief Transaction handling routines
38 * @defgroup transaction Transaction handling functions
39 *
40 * The following image shows off the full transaction dictionary returned
41 * by xbps_transaction_prepare().
42 *
43 * @image html images/xbps_transaction_dictionary.png
44 *
45 * Legend:
46 * - <b>Salmon bg box</b>: The transaction dictionary.
47 * - <b>White bg box</b>: mandatory objects.
48 * - <b>Grey bg box</b>: optional objects.
49 * - <b>Green bg box</b>: possible value set in the object, only one of them
50 * will be set.
51 *
52 * Text inside of white boxes are the key associated with the object, its
53 * data type is specified on its edge, i.e string, array, integer, dictionary.
54 */
55static int
56trans_find_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
57{
58 xbps_dictionary_t pkg_pkgdb = NULL, pkg_repod = NULL, vpkg_pkgdb = NULL;
59 xbps_object_t obj;
60 xbps_array_t pkgs;
61 pkg_state_t state = 0;
63 const char *repoloc, *repopkgver, *instpkgver, *pkgname;
64 char buf[XBPS_NAME_SIZE] = {0};
65 bool autoinst = false;
66 int rv = 0;
67
68 assert(pkg != NULL);
69
70 /*
71 * Find out if pkg is installed first.
72 */
73 if (xbps_pkg_name(buf, sizeof(buf), pkg)) {
74 pkg_pkgdb = xbps_pkgdb_get_pkg(xhp, buf);
75 if (!pkg_pkgdb)
76 vpkg_pkgdb = xbps_pkgdb_get_virtualpkg(xhp, buf);
77 } else {
78 pkg_pkgdb = xbps_pkgdb_get_pkg(xhp, pkg);
79 if (!pkg_pkgdb)
80 vpkg_pkgdb = xbps_pkgdb_get_virtualpkg(xhp, pkg);
81 }
82
83 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
84 pkg_pkgdb = NULL;
85 ttype = XBPS_TRANS_DOWNLOAD;
86 }
87
88 if (vpkg_pkgdb) {
89 // virtual package installed, if there is no real package in
90 // the rpool, we are keeping the virtual package.
91 pkg_repod = xbps_rpool_get_pkg(xhp, pkg);
92 if (!pkg_repod)
93 pkg_pkgdb = vpkg_pkgdb;
94 }
95 if (pkg_pkgdb) {
96 // package already installed
97 if (force) {
98 ttype = XBPS_TRANS_REINSTALL;
99 } else {
100 ttype = XBPS_TRANS_UPDATE;
101 }
102 if (!xbps_dictionary_get_cstring_nocopy(pkg_pkgdb, "pkgname", &pkgname)) {
103 xbps_error_printf("missing `pkgname` property\n");
104 return EINVAL;
105 }
106 if (xbps_dictionary_get(pkg_pkgdb, "repolock")) {
107 struct xbps_repo *repo;
108 /* find update from repo */
109 xbps_dictionary_get_cstring_nocopy(pkg_pkgdb, "repository", &repoloc);
110 assert(repoloc);
111 if ((repo = xbps_regget_repo(xhp, repoloc)) == NULL) {
112 /* not found */
113 return ENOENT;
114 }
115 pkg_repod = xbps_repo_get_pkg(repo, pkgname);
116 } else {
117 /* find update from rpool */
118 pkg_repod = xbps_rpool_get_pkg(xhp, pkgname);
119 }
120 } else {
121 ttype = XBPS_TRANS_INSTALL;
122 pkg_repod = xbps_rpool_get_pkg(xhp, pkg);
123 if (!pkg_repod)
124 pkg_repod = xbps_rpool_get_virtualpkg(xhp, pkg);
125 }
126
127 if (!pkg_repod) {
128 /* not found */
129 return ENOENT;
130 }
131
132 xbps_dictionary_get_cstring_nocopy(pkg_repod, "pkgver", &repopkgver);
133
134 if (ttype == XBPS_TRANS_UPDATE) {
135 /*
136 * Compare installed version vs best pkg available in repos
137 * for pkg updates.
138 */
139 xbps_dictionary_get_cstring_nocopy(pkg_pkgdb,
140 "pkgver", &instpkgver);
141 if (xbps_cmpver(repopkgver, instpkgver) <= 0 &&
142 !xbps_pkg_reverts(pkg_repod, instpkgver)) {
143 xbps_dictionary_get_cstring_nocopy(pkg_repod,
144 "repository", &repoloc);
145 xbps_dbg_printf("[rpool] Skipping `%s' "
146 "(installed: %s) from repository `%s'\n",
147 repopkgver, instpkgver, repoloc);
148 return EEXIST;
149 }
150 } else if (ttype == XBPS_TRANS_REINSTALL) {
151 /*
152 * For reinstallation check if installed version is less than
153 * or equal to the pkg in repos, if true, continue with reinstallation;
154 * otherwise perform an update.
155 */
156 xbps_dictionary_get_cstring_nocopy(pkg_pkgdb, "pkgver", &instpkgver);
157 if (xbps_cmpver(repopkgver, instpkgver) == 1) {
158 ttype = XBPS_TRANS_UPDATE;
159 }
160 }
161
162 if (pkg_pkgdb) {
163 /*
164 * If pkg is already installed, respect some properties.
165 */
166 if ((obj = xbps_dictionary_get(pkg_pkgdb, "automatic-install")))
167 xbps_dictionary_set(pkg_repod, "automatic-install", obj);
168 if ((obj = xbps_dictionary_get(pkg_pkgdb, "hold")))
169 xbps_dictionary_set(pkg_repod, "hold", obj);
170 if ((obj = xbps_dictionary_get(pkg_pkgdb, "repolock")))
171 xbps_dictionary_set(pkg_repod, "repolock", obj);
172 }
173 /*
174 * Prepare transaction dictionary.
175 */
176 if ((rv = xbps_transaction_init(xhp)) != 0)
177 return rv;
178
179 pkgs = xbps_dictionary_get(xhp->transd, "packages");
180 /*
181 * Find out if package being updated matches the one already
182 * in transaction, in that case ignore it.
183 */
184 if (ttype == XBPS_TRANS_UPDATE) {
185 if (xbps_find_pkg_in_array(pkgs, repopkgver, 0)) {
186 xbps_dbg_printf("[update] `%s' already queued in "
187 "transaction.\n", repopkgver);
188 return EEXIST;
189 }
190 }
191
192 if (!xbps_dictionary_get_cstring_nocopy(pkg_repod, "pkgname", &pkgname)) {
193 xbps_error_printf("missing `pkgname` property\n");
194 return EINVAL;
195 }
196 /*
197 * Set package state in dictionary with same state than the
198 * package currently uses, otherwise not-installed.
199 */
200 if ((rv = xbps_pkg_state_installed(xhp, pkgname, &state)) != 0) {
201 if (rv != ENOENT) {
202 return rv;
203 }
204 /* Package not installed, don't error out */
205 state = XBPS_PKG_STATE_NOT_INSTALLED;
206 }
207 if ((rv = xbps_set_pkg_state_dictionary(pkg_repod, state)) != 0) {
208 return rv;
209 }
210
211 if (state == XBPS_PKG_STATE_NOT_INSTALLED)
212 ttype = XBPS_TRANS_INSTALL;
213
214 if (!force && xbps_dictionary_get(pkg_repod, "hold"))
215 ttype = XBPS_TRANS_HOLD;
216
217 /*
218 * Store pkgd from repo into the transaction.
219 */
220 if (!xbps_transaction_pkg_type_set(pkg_repod, ttype)) {
221 return EINVAL;
222 }
223
224 /*
225 * Set automatic-install to true if it was requested and this is a new install.
226 */
227 if (ttype == XBPS_TRANS_INSTALL)
228 autoinst = xhp->flags & XBPS_FLAG_INSTALL_AUTO;
229
230 if (!xbps_transaction_store(xhp, pkgs, pkg_repod, autoinst)) {
231 return EINVAL;
232 }
233
234 return 0;
235}
236
237/*
238 * Returns 1 if there's an update, 0 if none or -1 on error.
239 */
240static int
241xbps_autoupdate(struct xbps_handle *xhp)
242{
243 xbps_array_t rdeps;
244 xbps_dictionary_t pkgd;
245 const char *pkgver = NULL, *pkgname = NULL;
246 int rv;
247
248 /*
249 * Check if there's a new update for XBPS before starting
250 * another transaction.
251 */
252 if (((pkgd = xbps_pkgdb_get_pkg(xhp, "xbps")) == NULL) &&
253 ((pkgd = xbps_pkgdb_get_virtualpkg(xhp, "xbps")) == NULL))
254 return 0;
255
256 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver)) {
257 return EINVAL;
258 }
259 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname)) {
260 return EINVAL;
261 }
262
263 rv = trans_find_pkg(xhp, pkgname, false);
264
265 xbps_dbg_printf("%s: trans_find_pkg xbps: %d\n", __func__, rv);
266
267 if (rv == 0) {
268 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
269 return 0;
270 }
271 /* a new xbps version is available, check its revdeps */
272 rdeps = xbps_pkgdb_get_pkg_revdeps(xhp, "xbps");
273 for (unsigned int i = 0; i < xbps_array_count(rdeps); i++) {
274 const char *curpkgver = NULL;
275 char curpkgn[XBPS_NAME_SIZE] = {0};
276
277 xbps_array_get_cstring_nocopy(rdeps, i, &curpkgver);
278 xbps_dbg_printf("%s: processing revdep %s\n", __func__, curpkgver);
279
280 if (!xbps_pkg_name(curpkgn, sizeof(curpkgn), curpkgver)) {
281 abort();
282 }
283 rv = trans_find_pkg(xhp, curpkgn, false);
284 xbps_dbg_printf("%s: trans_find_pkg revdep %s: %d\n", __func__, curpkgver, rv);
285 if (rv && rv != ENOENT && rv != EEXIST && rv != ENODEV)
286 return -1;
287 }
288 /*
289 * Set XBPS_FLAG_FORCE_REMOVE_REVDEPS to ignore broken
290 * reverse dependencies in xbps_transaction_prepare().
291 *
292 * This won't skip revdeps of the xbps pkg, rather other
293 * packages in rootdir that could be broken indirectly.
294 *
295 * A sysup transaction after updating xbps should fix them
296 * again.
297 */
298 xhp->flags |= XBPS_FLAG_FORCE_REMOVE_REVDEPS;
299 return 1;
300 } else if (rv == ENOENT || rv == EEXIST || rv == ENODEV) {
301 /* no update */
302 return 0;
303 } else {
304 /* error */
305 return -1;
306 }
307
308 return 0;
309}
310
311int
313{
314 xbps_object_t obj;
315 xbps_object_iterator_t iter;
316 xbps_dictionary_t pkgd;
317 bool newpkg_found = false;
318 int rv = 0;
319
320 rv = xbps_autoupdate(xhp);
321 switch (rv) {
322 case 1:
323 /* xbps needs to be updated, don't allow any other update */
324 return EBUSY;
325 case -1:
326 /* error */
327 return EINVAL;
328 default:
329 break;
330 }
331
332 iter = xbps_dictionary_iterator(xhp->pkgdb);
333 assert(iter);
334
335 while ((obj = xbps_object_iterator_next(iter))) {
336 const char *pkgver = NULL;
337 char pkgname[XBPS_NAME_SIZE] = {0};
338
339 pkgd = xbps_dictionary_get_keysym(xhp->pkgdb, obj);
340 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver)) {
341 continue;
342 }
343 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
344 rv = EINVAL;
345 break;
346 }
347 rv = trans_find_pkg(xhp, pkgname, false);
348 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkgver, rv);
349 if (rv == 0) {
350 newpkg_found = true;
351 } else if (rv == ENOENT || rv == EEXIST || rv == ENODEV) {
352 /*
353 * missing pkg or installed version is greater than or
354 * equal than pkg in repositories.
355 */
356 rv = 0;
357 }
358 }
359 xbps_object_iterator_release(iter);
360
361 return newpkg_found ? rv : EEXIST;
362}
363
364int
365xbps_transaction_update_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
366{
367 xbps_array_t rdeps;
368 int rv;
369
370 rv = xbps_autoupdate(xhp);
371 xbps_dbg_printf("%s: xbps_autoupdate %d\n", __func__, rv);
372 switch (rv) {
373 case 1:
374 /* xbps needs to be updated, only allow xbps to be updated */
375 if (strcmp(pkg, "xbps"))
376 return EBUSY;
377 return 0;
378 case -1:
379 /* error */
380 return EINVAL;
381 default:
382 /* no update */
383 break;
384 }
385
386 /* update its reverse dependencies */
387 rdeps = xbps_pkgdb_get_pkg_revdeps(xhp, pkg);
388 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
389 rdeps = NULL;
390 }
391 for (unsigned int i = 0; i < xbps_array_count(rdeps); i++) {
392 const char *pkgver = NULL;
393 char pkgname[XBPS_NAME_SIZE] = {0};
394
395 if (!xbps_array_get_cstring_nocopy(rdeps, i, &pkgver)) {
396 rv = EINVAL;
397 break;
398 }
399 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
400 rv = EINVAL;
401 break;
402 }
403 rv = trans_find_pkg(xhp, pkgname, false);
404 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkgver, rv);
405 if (rv && rv != ENOENT && rv != EEXIST && rv != ENODEV) {
406 return rv;
407 }
408 }
409 /* add pkg repod */
410 rv = trans_find_pkg(xhp, pkg, force);
411 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkg, rv);
412 return rv;
413}
414
415int
416xbps_transaction_install_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
417{
418 xbps_array_t rdeps;
419 int rv;
420
421 rv = xbps_autoupdate(xhp);
422 switch (rv) {
423 case 1:
424 /* xbps needs to be updated, only allow xbps to be updated */
425 if (strcmp(pkg, "xbps"))
426 return EBUSY;
427 return 0;
428 case -1:
429 /* error */
430 return EINVAL;
431 default:
432 /* no update */
433 break;
434 }
435
436 /* update its reverse dependencies */
437 rdeps = xbps_pkgdb_get_pkg_revdeps(xhp, pkg);
438 if (xhp->flags & XBPS_FLAG_DOWNLOAD_ONLY) {
439 rdeps = NULL;
440 }
441 for (unsigned int i = 0; i < xbps_array_count(rdeps); i++) {
442 const char *pkgver = NULL;
443 char pkgname[XBPS_NAME_SIZE] = {0};
444
445 if (!xbps_array_get_cstring_nocopy(rdeps, i, &pkgver)) {
446 rv = EINVAL;
447 break;
448 }
449 if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) {
450 rv = EINVAL;
451 break;
452 }
453 rv = trans_find_pkg(xhp, pkgname, false);
454 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkgver, rv);
455 if (rv && rv != ENOENT && rv != EEXIST && rv != ENODEV) {
456 return rv;
457 }
458 }
459 rv = trans_find_pkg(xhp, pkg, force);
460 xbps_dbg_printf("%s: trans_find_pkg %s: %d\n", __func__, pkg, rv);
461 return rv;
462}
463
464int
466 const char *pkgname,
467 bool recursive)
468{
469 xbps_dictionary_t pkgd;
470 xbps_array_t pkgs, orphans, orphans_pkg;
471 xbps_object_t obj;
472 int rv = 0;
473
474 assert(xhp);
475 assert(pkgname);
476
477 if ((pkgd = xbps_pkgdb_get_pkg(xhp, pkgname)) == NULL) {
478 /* pkg not installed */
479 return ENOENT;
480 }
481 /*
482 * Prepare transaction dictionary and missing deps array.
483 */
484 if ((rv = xbps_transaction_init(xhp)) != 0)
485 return rv;
486
487 pkgs = xbps_dictionary_get(xhp->transd, "packages");
488
489 if (!recursive)
490 goto rmpkg;
491 /*
492 * If recursive is set, find out which packages would be orphans
493 * if the supplied package were already removed.
494 */
495 if ((orphans_pkg = xbps_array_create()) == NULL)
496 return ENOMEM;
497
498 xbps_array_set_cstring_nocopy(orphans_pkg, 0, pkgname);
499 orphans = xbps_find_pkg_orphans(xhp, orphans_pkg);
500 xbps_object_release(orphans_pkg);
501 if (xbps_object_type(orphans) != XBPS_TYPE_ARRAY)
502 return EINVAL;
503
504 for (unsigned int i = 0; i < xbps_array_count(orphans); i++) {
505 obj = xbps_array_get(orphans, i);
506 xbps_transaction_pkg_type_set(obj, XBPS_TRANS_REMOVE);
507 if (!xbps_transaction_store(xhp, pkgs, obj, false)) {
508 return EINVAL;
509 }
510 }
511 xbps_object_release(orphans);
512 return rv;
513
514rmpkg:
515 /*
516 * Add pkg dictionary into the transaction pkgs queue.
517 */
518 xbps_transaction_pkg_type_set(pkgd, XBPS_TRANS_REMOVE);
519 if (!xbps_transaction_store(xhp, pkgs, pkgd, false)) {
520 return EINVAL;
521 }
522 return rv;
523}
524
525int
527{
528 xbps_array_t orphans, pkgs;
529 xbps_object_t obj;
530 int rv = 0;
531
532 orphans = xbps_find_pkg_orphans(xhp, NULL);
533 if (xbps_array_count(orphans) == 0) {
534 /* no orphans? we are done */
535 goto out;
536 }
537 /*
538 * Prepare transaction dictionary and missing deps array.
539 */
540 if ((rv = xbps_transaction_init(xhp)) != 0)
541 goto out;
542
543 pkgs = xbps_dictionary_get(xhp->transd, "packages");
544 /*
545 * Add pkg orphan dictionary into the transaction pkgs queue.
546 */
547 for (unsigned int i = 0; i < xbps_array_count(orphans); i++) {
548 obj = xbps_array_get(orphans, i);
549 xbps_transaction_pkg_type_set(obj, XBPS_TRANS_REMOVE);
550 if (!xbps_transaction_store(xhp, pkgs, obj, false)) {
551 rv = EINVAL;
552 goto out;
553 }
554 }
555out:
556 if (orphans)
557 xbps_object_release(orphans);
558
559 return rv;
560}
561
563xbps_transaction_pkg_type(xbps_dictionary_t pkg_repod)
564{
565 uint8_t r;
566
567 if (xbps_object_type(pkg_repod) != XBPS_TYPE_DICTIONARY)
568 return 0;
569
570 if (!xbps_dictionary_get_uint8(pkg_repod, "transaction", &r))
571 return 0;
572
573 return r;
574}
575
576bool
577xbps_transaction_pkg_type_set(xbps_dictionary_t pkg_repod, xbps_trans_type_t ttype)
578{
579 uint8_t r;
580
581 if (xbps_object_type(pkg_repod) != XBPS_TYPE_DICTIONARY)
582 return false;
583
584 switch (ttype) {
585 case XBPS_TRANS_INSTALL:
586 case XBPS_TRANS_UPDATE:
587 case XBPS_TRANS_CONFIGURE:
588 case XBPS_TRANS_REMOVE:
589 case XBPS_TRANS_REINSTALL:
590 case XBPS_TRANS_HOLD:
591 case XBPS_TRANS_DOWNLOAD:
592 break;
593 default:
594 return false;
595 }
596 r = ttype;
597 if (!xbps_dictionary_set_uint8(pkg_repod, "transaction", r))
598 return false;
599
600 return true;
601}
xbps_dictionary_t pkgdb
Definition xbps.h:585
xbps_dictionary_t transd
Definition xbps.h:592
int flags
Definition xbps.h:688
Generic XBPS structure handler for initialization.
Definition xbps.h:559
xbps_array_t xbps_find_pkg_orphans(struct xbps_handle *xhp, xbps_array_t orphans)
xbps_dictionary_t xbps_pkgdb_get_virtualpkg(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:430
xbps_array_t xbps_pkgdb_get_pkg_revdeps(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:517
xbps_dictionary_t xbps_pkgdb_get_pkg(struct xbps_handle *xhp, const char *pkg)
Definition pkgdb.c:416
int xbps_pkg_state_installed(struct xbps_handle *xhp, const char *pkgname, pkg_state_t *state)
int xbps_set_pkg_state_dictionary(xbps_dictionary_t dict, pkg_state_t state)
pkg_state_t
Definition xbps.h:1762
xbps_dictionary_t xbps_repo_get_pkg(struct xbps_repo *repo, const char *pkg)
Definition repo.c:598
struct xbps_handle * xhp
Definition xbps.h:1432
Repository structure.
Definition xbps.h:1420
xbps_dictionary_t xbps_rpool_get_virtualpkg(struct xbps_handle *xhp, const char *pkg)
Definition rpool.c:323
xbps_dictionary_t xbps_rpool_get_pkg(struct xbps_handle *xhp, const char *pkg)
Definition rpool.c:329
xbps_trans_type_t xbps_transaction_pkg_type(xbps_dictionary_t pkg_repod)
int xbps_transaction_install_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
int xbps_transaction_autoremove_pkgs(struct xbps_handle *xhp)
xbps_trans_type_t
Definition xbps.h:1331
int xbps_transaction_remove_pkg(struct xbps_handle *xhp, const char *pkgname, bool recursive)
bool xbps_transaction_pkg_type_set(xbps_dictionary_t pkg_repod, xbps_trans_type_t ttype)
int xbps_transaction_update_pkg(struct xbps_handle *xhp, const char *pkg, bool force)
int xbps_transaction_update_packages(struct xbps_handle *xhp)
bool xbps_pkg_name(char *dst, size_t len, const char *pkg)
Definition util.c:253
bool xbps_pkg_reverts(xbps_dictionary_t pkg, const char *pkgver)
Definition util.c:587
int xbps_cmpver(const char *pkg1, const char *pkg2)
Definition dewey.c:273