You are not logged in.

#1 2007-10-21 23:41:36

ploxiln
Member
Registered: 2006-10-27
Posts: 50

simple script inspired by debfoster

I recently had an itch to scratch and wrote a script. It's pretty messy since I'm really not familiar with shell scripting, but it works, and it's pretty reliable as far as I can tell. I offer it up to anyone who it might benefit.

How it works: You run it as root; for every package which is required by nothing else, it asks you if you want to keep it or not, and immediately removes the package if appropriate. Then when it gets to the end of this list, it rescans for packages newly "unrequired" by other packages, and asks you just about those, until a rescan turns up no newly "unrequired" packages.

So, not nearly as nice as "debfoster -n", much less safe, but whatever here's the script.

#!/bin/bash

# pacreview.sh
# helps you trim your collection of installed packages

# by Pierce Lopez 2007-10-21, released to the Public Domain

keepers=()
orphans=()

gen_orphs()
{
    echo "Generating list of orphans..."
    unset orphans
    for package in `pacman -Q | cut -d' ' -f1`
    do
        if [ "$(pacman -Qi $package | grep '^Required')" == "Required By    : None" ]
        then orphans[${#orphans[*]}]=$package
        fi
    done
}

check_keeps()
{
    if [ ${#keepers[*]} == 0 ]
    then return 0
    fi

    for keeper in ${keepers[*]}
    do
        if [ "$1" == "$keeper" ]
        then return 1
        fi
    done
    return 0
}

flush_input()
{
    while read -n 1 -t 1
    do
        false
    done
}

query_user()
{
    need_input=1
    while [ $need_input == 1 ]
    do
        need_input=0
        echo -ne 'Remove orphan package \e[1m'
        echo -n $1
        echo -ne '\e[m? [k,r,i,h=help]: '
        read -n 1 choice
        echo
        if [ "$choice" == k ]
        then keepers[${#keepers[*]}]=$1
        elif [ "$choice" == r ]
        then pacman -R "$1"
        elif [ "$choice" == i ]
        then
            pacman -Qi "$1"
            need_input=1
        elif [ "$choice" == h ]
        then
            echo "k = keep"
            echo "r = remove"
            echo "i = see package information"
            echo "h = show this help"
            need_input=1
        else need_input=1
        fi
    done
}

neworphs=1
while [ $neworphs == 1 ]
do
    neworphs=0
    gen_orphs

    flush_input
    for orphan in ${orphans[*]}
    do
        check_keeps $orphan
        if [ $? == 0 ]
        then
            query_user $orphan
            neworphs=1
        fi
    done
done

Offline

#2 2007-10-22 19:51:01

egates
Member
From: CMU, Pittsburgh, PA
Registered: 2007-10-22
Posts: 12
Website

Re: simple script inspired by debfoster

Hi there,

Here's the same basic thing but as a C program. As with his, I'm sure it needs work, I haven't done much programming outside of classes, so this is a first for me. 

The program runs through orphans and you can (r)emove, (k)eep, get (i)nformation, or (q)uit.  Every package you choose to remove is added to the transaction, which is completed after you go through the entire list.  Once those packages have been removed it starts again.  Same idea, except that packages aren't removed immediately.

Feel free to use and change it as you will, but I would ask you to at least post back and explain what changes you make and why, just so I can learn.

Usage: pac-orphan [-c cachedir] [-d dbpath] [-r root] [-g group] [-h]
The cachedir, root, and dbpath options have not been tested.  The -g group option is for groups that you want to keep, that the program shouldn't ask about.  Use the -g flag for each group that you want to keep.

Example

# pac-orphan -g base -g base-devel -g xorg

will automatically keep all packages in the groups base, base-devel, and xorg.

pac-orphan should be compiled with

gcc -lalpm pac-orphan.c

so that it can use libalpm

I use

gcc -lalpm -ansi -pedantic -W -Wall -o pac-orphan pac-orphan.c

to check for ansi compliance and get pac-orphan instead of a.out
(I get a warning when I compile that I can't seem to get rid of...)


And here is the code

#include <alpm.h>
#include <alpm_list.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void eprint(const char* errstr, ...){
    va_list ap;

    va_start(ap, errstr);
    vfprintf(stderr, errstr, ap);
    va_end(ap);
    exit(EXIT_FAILURE);
}

int find(alpm_list_t* list, alpm_list_t* black_list){
    while(list){
        if(alpm_list_find_str(black_list, alpm_grp_get_name(alpm_list_getdata(list))))
               return 1;
        list = alpm_list_next(list);
           }
    
    return 0;
}


void print_info(pmpkg_t* pkg){
    alpm_list_t* depends;
    
    printf("Name        : %s\n", alpm_pkg_get_name(pkg));
    printf("Version     : %s\n", alpm_pkg_get_version(pkg));
    printf("Depends on  : ");
    if((depends = alpm_pkg_get_depends(pkg)))
        do{
            printf("%s ", (char*) alpm_list_getdata(depends));
        }while((depends = alpm_list_next(depends)));
    printf("\n");
    printf("Description : %s\n", alpm_pkg_get_desc(pkg));
    printf("%s : ", alpm_pkg_get_name(pkg));
    fflush(stdout);
}

int list_orphans(alpm_list_t* pkgcache, alpm_list_t* keep, alpm_list_t* black_list){
    char choice[64];
    int num_orphans, trans;

    num_orphans = 0;

    if(alpm_trans_init(PM_TRANS_TYPE_REMOVE, PM_TRANS_FLAG_FORCE, NULL, NULL, NULL))
        eprint("alpm_trans_init failed pm_errno:%d\n", pm_errno);

    do{
        if(!alpm_pkg_get_requiredby(alpm_list_getdata(pkgcache)) &&
           !find(alpm_pkg_get_groups(alpm_list_getdata(pkgcache)), black_list)     &&
           !alpm_list_find(keep, alpm_list_getdata(pkgcache))){
            printf("%s : ", alpm_pkg_get_name(alpm_list_getdata(pkgcache)));
            fflush(stdout);
            do{
                scanf("%s", choice);
                switch(choice[0]){
                    case 'r': alpm_trans_addtarget(alpm_pkg_get_name(alpm_list_getdata(pkgcache)));
                          choice[0] = 0;
                          num_orphans++;
                          break;
                    case 'i': print_info(alpm_list_getdata(pkgcache));
                          break;
                    case 'k': choice[0] = 0;
                          keep = alpm_list_add(keep, alpm_list_getdata(pkgcache));
                          break;
                    case 'q': if(alpm_trans_release()) eprint("alpm_trans_release failed pm_errno:%d\n", pm_errno);
                          return 0;
                    default:  printf("[(r)emove, (k)eep, (i)nformation, (q)uit]: ");
                          fflush(stdout);
                          break;
                }
            }while(choice[0]);
        }
    }while((pkgcache = alpm_list_next(pkgcache)));

    keep     = alpm_list_first(keep    );
    pkgcache = alpm_list_first(pkgcache);

    if(num_orphans && (trans =  alpm_trans_prepare(&pkgcache)))fprintf(stderr, "alpm_trans_prepare failed pm_errno:%d\n", pm_errno);
    if(num_orphans && !trans && alpm_trans_commit (&pkgcache)) fprintf(stderr, "alpm_trans_commit  failed pm_errno:%d\n", pm_errno);
    if(alpm_trans_release()) fprintf(stderr, "alpm_trans_release failed pm_errno:%d\n", pm_errno);

    return num_orphans;
}

int main(int argc, char* argv[]){
    pmdb_t* db;
    alpm_list_t *black_list, *keep, *pkgcache;
    int i;

    /* initialize */
    if(alpm_initialize()) eprint("alpm_initialize failed pm_errno:%d\n", pm_errno);

    if(!(db = alpm_db_register("local"))) eprint("database null\n");

    if(!(pkgcache = alpm_db_getpkgcache(db))) eprint("pkgcache null\n");

    /*keep = alpm_list_new(); doesn't work*/
    if(!(keep = malloc(sizeof(alpm_list_t)))) eprint("malloc failed");
    keep->data = NULL;

    if(!(black_list = malloc(sizeof(alpm_list_t)))) eprint("malloc failed");
    black_list->data = NULL;

    /* read arguments */
    for(i = 1; i < argc; i++){
        if(!strcmp("-h", argv[i])) eprint("Usage: %s [-c cachedir] [-d dbpath] [-r root] [-g group] [-h]\n", argv[0]);
        if(!strcmp("-g", argv[i])) black_list = alpm_list_add(black_list, argv[++i]);
        if(!strcmp("-r", argv[i])) alpm_option_set_root    (argv[++i]);
        if(!strcmp("-d", argv[i])) alpm_option_set_dbpath  (argv[++i]);
        if(!strcmp("-c", argv[i])) alpm_option_set_cachedir(argv[++i]);
    }

    /* main loop */
    while(list_orphans(pkgcache, keep, black_list))
        ;

    /* free */
    alpm_list_free(keep);
    alpm_list_free(black_list);
    if(alpm_release()) eprint("alpm_release failed pm_errno:%d\n", pm_errno);

    return 0;
}

Offline

#3 2007-10-23 17:18:11

egates
Member
From: CMU, Pittsburgh, PA
Registered: 2007-10-22
Posts: 12
Website

Re: simple script inspired by debfoster

If anyone is interested, I took this a little too far.  The code is still pretty much the same, but I've created a man page, Makefile, and the like.  Here is a PKGBUILD if you want it.  Or just check out http://www.andrew.cmu.edu/user/egates  You can also just download the tarball, clone the mercurial repository, or browse the code.

# Contributors: Pierce Lopez <pierce.lopez@gmail.com>
#               Evan   Gates <evan.gates@gmail.com>

pkgname=pacorphan
pkgver=0.1
pkgrel=1
pkgdesc="An attempt to add deb[orphan/foster] functionality"
url="http://www.andrew.cmu.edu/user/egates"
license="MIT"
arch=('i686')
depends=('libdownload')
makedepends=('gcc')
provides=()
conflicts=()
source=($url/$pkgname/stable/$pkgname-$pkgver.tar.gz)
md5sums=('1ea60964d7cf16ea21bbafd72e88cbf9')

build() {
  cd $startdir/src/$pkgname-$pkgver
  make PREFIX=/usr DESTDIR=$startdir/pkg clean install
}

Last edited by egates (2007-10-23 17:21:41)

Offline

#4 2007-10-23 18:05:03

ploxiln
Member
Registered: 2006-10-27
Posts: 50

Re: simple script inspired by debfoster

It's pretty fast, too. thanks.

Offline

#5 2007-12-03 23:27:23

egates
Member
From: CMU, Pittsburgh, PA
Registered: 2007-10-22
Posts: 12
Website

Re: simple script inspired by debfoster

I've been slowly working on pacorphan.
Here are links to the
PKGBUILD: http://deepcube.res.cmu.edu/download/pa … 3.PKGBUILD
tarball:          http://deepcube.res.cmu.edu/download/pa … 0.3.tar.gz
repository:    http://deepcube.res.cmu.edu/hg/pacorphan
page:            http://deepcube.res.cmu.edu/wiki/code/pacorphan

and the PKGBUILD in text form, just for good measure

pkgname=pacorphan
pkgver=0.3
pkgrel=1
pkgdesc="An attempt to add deb[orphan/foster] functionality"
url="http://deepcube.res.cmu.edu"
license="MIT"
arch=('i686')
depends=('pacman')
makedepends=('gcc')
provides=()
conflicts=()
source=($url/download/$pkgname-$pkgver.tar.gz)
md5sums=('60d636f30766d6af7c869c5220705e24')

build() {
  cd $startdir/src/$pkgname-$pkgver
  make PREFIX=/usr DESTDIR=$startdir/pkg clean install
}

Last edited by egates (2007-12-03 23:28:17)

Offline

#6 2007-12-04 00:39:48

bender02
Member
From: UK
Registered: 2007-02-04
Posts: 1,328

Re: simple script inspired by debfoster

egates: nice work!
If you feel like working on it some more, here are some suggestions that I'd appreciate:
- when run, print out the options ([k]eep [r]emove [i]nfo [q]uit), since I forget things very quickly, and this would be a nice memory refresher
- this is a bit more involved: keep a list of packages that are "kept" by default. Reason: say I'm happy with my current state of the system, so I tell pacorphan to create a list of current orphans. After some time, I want to clean up the system a bit, but I want to keep those that I was happy with before, and so pacorphan will not ask me about lots of packages, but only about the new ones. The list would be specifiable as a parameter, and if it would be just a plain file, one could edit it... It would be like having a special group, whose members would be listed in a file.
- a parameter so that pacorphan doesn't ask any questions in the subsequent rounds, just uninstalls all new packages that come up (essentially uninstall a program with all the dependencies, so that one doesn't have to type "r"emove for all of them)

Keep up the good work!

Offline

#7 2007-12-05 22:14:46

egates
Member
From: CMU, Pittsburgh, PA
Registered: 2007-10-22
Posts: 12
Website

Re: simple script inspired by debfoster

new version out with all your suggestions, check the manpage for the new flags.
pacorphan is also in AUR now, so you can get it there, or use yaourt, or download from my site.

http://deepcube.res.cmu.edu/wiki/code/pacorphan

-Evan

Last edited by egates (2007-12-05 22:18:15)

Offline

#8 2007-12-05 22:54:33

bender02
Member
From: UK
Registered: 2007-02-04
Posts: 1,328

Re: simple script inspired by debfoster

Great! Thanks a lot!

Offline

#9 2007-12-06 05:25:38

djclue917
Member
Registered: 2006-12-03
Posts: 121

Re: simple script inspired by debfoster

How about this?

# pacman -R `pacman -Qe | cut -d' ' -f1`

Short and sweet. wink

Offline

#10 2007-12-06 05:35:22

bender02
Member
From: UK
Registered: 2007-02-04
Posts: 1,328

Re: simple script inspired by debfoster

djclue917 wrote:

How about this?

The point of pacorphan is to decide about all "leaf" packages (if you consider the dependency tree...), not only the "orphaned" (as defined within pacman grounds, ie those which were not explicitly installed and are "leaf").

EDIT: I guess it's similar to FreeBSD's pkg_cutleaves.

Last edited by bender02 (2007-12-06 05:41:49)

Offline

#11 2007-12-06 05:41:49

egates
Member
From: CMU, Pittsburgh, PA
Registered: 2007-10-22
Posts: 12
Website

Re: simple script inspired by debfoster

Short and sweet. wink

yes, yes it is, that is very true.
but that only asks about packages that were not explicitly installed
pacorphan is good for general cleanup, too
and it's possible that you may want to keep a package that was orphaned (I wanted to keep cdrkit when I removed k3b)
and I'll be completely honest, this started out just for fun.  I saw the script, and had recently discovered libalpm, so I decided to give it a go.  good learning experience, first time I've used an api, dealt with signal handling, got raw input from the terminal, used a Makefile, created a PKGBUILD, uploaded a package to AUR.  So it's been fun.

-Evan

Edit:
looks like bender02 beat me to the punch, yeah, what he said

Last edited by egates (2007-12-06 06:08:08)

Offline

#12 2007-12-06 11:45:33

djclue917
Member
Registered: 2006-12-03
Posts: 121

Re: simple script inspired by debfoster

bender02 wrote:
djclue917 wrote:

How about this?

The point of pacorphan is to decide about all "leaf" packages (if you consider the dependency tree...), not only the "orphaned" (as defined within pacman grounds, ie those which were not explicitly installed and are "leaf").

EDIT: I guess it's similar to FreeBSD's pkg_cutleaves.

Then don't you think the name is misleading? I mean... pacorphan. And BTW, I'm replying to the bash script by ploxiln.

Offline

#13 2007-12-06 11:54:25

djclue917
Member
Registered: 2006-12-03
Posts: 121

Re: simple script inspired by debfoster

egates wrote:

Short and sweet. wink

yes, yes it is, that is very true.
but that only asks about packages that were not explicitly installed
pacorphan is good for general cleanup, too
and it's possible that you may want to keep a package that was orphaned (I wanted to keep cdrkit when I removed k3b)
and I'll be completely honest, this started out just for fun.  I saw the script, and had recently discovered libalpm, so I decided to give it a go.  good learning experience, first time I've used an api, dealt with signal handling, got raw input from the terminal, used a Makefile, created a PKGBUILD, uploaded a package to AUR.  So it's been fun.

-Evan

Edit:
looks like bender02 beat me to the punch, yeah, what he said

Like I said... Since I wrote the "short and sweet" code as a combo of pacman commands, I'm replying to the shell script (since it's too long and too complicated).

Offline

#14 2007-12-06 12:19:20

shining
Pacman Developer
Registered: 2006-05-10
Posts: 2,043

Re: simple script inspired by debfoster

bender02 wrote:
djclue917 wrote:

How about this?

The point of pacorphan is to decide about all "leaf" packages (if you consider the dependency tree...), not only the "orphaned" (as defined within pacman grounds, ie those which were not explicitly installed and are "leaf").

http://bugs.archlinux.org/task/7343#comment17901


pacman roulette : pacman -S $(pacman -Slq | LANG=C sort -R | head -n $((RANDOM % 10)))

Offline

#15 2007-12-06 14:42:25

bender02
Member
From: UK
Registered: 2007-02-04
Posts: 1,328

Re: simple script inspired by debfoster

Ah! I totally forgot about that. The only thing missing in the pacman then would be an option to ignore "explicitly installed" when doing something like "pacman -Rs whatever" ... sometimes dependencies become explicitly installed for various reasons (like manual downgrade).

Offline

#16 2007-12-06 21:13:50

shining
Pacman Developer
Registered: 2006-05-10
Posts: 2,043

Re: simple script inspired by debfoster

bender02 wrote:

Ah! I totally forgot about that. The only thing missing in the pacman then would be an option to ignore "explicitly installed" when doing something like "pacman -Rs whatever" ... sometimes dependencies become explicitly installed for various reasons (like manual downgrade).

But how do you select the ones that were really explicitly installed?
Anyway, you will be able to fix that as well, but by reinstalling all packages that should be marked as dependencies like this:

pacman -S --asdeps <packages that should be marked as dependencies>

If that's not acceptable, feel free to file a feature request.


pacman roulette : pacman -S $(pacman -Slq | LANG=C sort -R | head -n $((RANDOM % 10)))

Offline

#17 2007-12-06 21:54:24

egates
Member
From: CMU, Pittsburgh, PA
Registered: 2007-10-22
Posts: 12
Website

Re: simple script inspired by debfoster

I based the name on deborphan, which has a different definition of orphan than pacman

deborphan finds "orphaned" packages on your system. It determines which packages have no other packages depending on their installation, and shows you a list of these packages. It is most useful when finding libraries, but it can be used on packages in all sections.

I've added a flag which only asks about orphans in the pacman sense of the word
But I think I might change the name to pacreview, as per the script, and add a flag that will ask about all packages, notifying which ones depend on it, and removing those too if you want to remove the current one.  Just a way to go through clean up an installation that has been building up a large number of packages.

-Evan

Offline

#18 2007-12-08 23:18:15

egates
Member
From: CMU, Pittsburgh, PA
Registered: 2007-10-22
Posts: 12
Website

Re: simple script inspired by debfoster

Well I've added the new feature, -l flag.  I Haven't changed the name though because I don't want to deal with that on AUR.  I've also updated the man page to be more explicit about which packages it affects.

-Evan

Offline

#19 2008-01-10 11:14:55

djclue917
Member
Registered: 2006-12-03
Posts: 121

Re: simple script inspired by debfoster

FYI, pacman, as of 3.1.0, can now "list all packages not required by any package" via:

pacman -Qt

Offline

Board footer

Powered by FluxBB