summaryrefslogtreecommitdiff
path: root/orphan.c
blob: ceda13f9f9b2f16f972ba31a6d3ac82e4f1e8093 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
 * pkg - Orphan package finder
 * https://rawnix.org
 *
 * Copyright (c) 2025 zorz@gmx.com
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "orphan.h"

/* Check if package is orphan, ignoring packages in ignore list */
static bool is_orphan_internal(const char *pkg, pkgdb_t *pkgdb,
                               depdb_t *depdb, strarray_t *ignore)
{
    dep_entry_t *e;
    strarray_t *rdeps;
    bool orphan;
    size_t i;

    if (!pkg || !pkgdb || !depdb)
        return false;

    if (!pkgdb_installed(pkgdb, pkg))
        return false;

    e = depdb_find(depdb, pkg);
    if (!e)
        return false;  /* not in dep db = assume manual */

    if (e->reason != REASON_AUTO)
        return false;  /* manually installed = not orphan */

    /* check reverse dependencies */
    rdeps = depdb_rdeps(depdb, pkg);

    orphan = true;
    for (i = 0; i < rdeps->size; i++) {
        if (!ignore || !strarray_contains(ignore, rdeps->data[i])) {
            orphan = false;
            break;
        }
    }

    strarray_free(rdeps);
    free(rdeps);

    return orphan;
}

bool is_orphan(const char *pkg, pkgdb_t *pkgdb, depdb_t *depdb)
{
    return is_orphan_internal(pkg, pkgdb, depdb, NULL);
}

/* Find all orphans using iterative algorithm */
strarray_t *find_orphans(pkgdb_t *pkgdb, depdb_t *depdb)
{
    strarray_t *result, *installed, *found;
    size_t i;
    bool found_new;

    result = xmalloc(sizeof(strarray_t));
    strarray_init(result);

    if (!pkgdb || !depdb)
        return result;

    found = xmalloc(sizeof(strarray_t));
    strarray_init(found);

    installed = pkgdb_list(pkgdb);

    /* iterate until no new orphans discovered */
    do {
        found_new = false;
        /* free strings from previous iteration before reuse */
        strarray_free(found);
        strarray_init(found);

        for (i = 0; i < installed->size; i++) {
            const char *name = installed->data[i];

            if (strarray_contains(result, name))
                continue;

            if (is_orphan_internal(name, pkgdb, depdb, result)) {
                strarray_add(found, name);
                found_new = true;
            }
        }

        /* add new orphans to result */
        for (i = 0; i < found->size; i++)
            strarray_add(result, found->data[i]);

    } while (found_new);

    strarray_free(found);
    free(found);
    strarray_free(installed);
    free(installed);

    return result;
}