tDelete packages from the system - pm - barely a pack manager
 (HTM) git clone git://z3bra.org/pm
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 413bad1d1c122485dea24f1d3d70b08469225013
 (DIR) parent b1f152ac97a08e6bc3c5c401a36228643ce77672
 (HTM) Author: z3bra <willyatmailoodotorg>
       Date:   Wed, 30 Dec 2015 15:45:37 +0100
       
       Delete packages from the system
       
       Diffstat:
         M pm.c                                |     130 ++++++++++++++++++++++++++++---
       
       1 file changed, 119 insertions(+), 11 deletions(-)
       ---
 (DIR) diff --git a/pm.c b/pm.c
       t@@ -27,7 +27,7 @@ struct pkg {
        /* possible actions */
        enum {
                ACTION_INSTALL     = 0,
       -        ACTION_REMOVE      = 1,
       +        ACTION_DELETE      = 1,
                ACTION_UPDATE      = 2,
                ACTION_INSPECT     = 3,
                ACTION_LIST_FILES  = 4,
       t@@ -46,6 +46,7 @@ enum {
        };
        
        void usage(char *name);
       +int d_empty(char *dir);
        int p_mkdir(char *dir, mode_t mode);
        char *base_name(char *path);
        int inspect(int fd, char *filename);
       t@@ -53,6 +54,8 @@ int list_local(const char *datadir);
        int metadata(char *datadir, char *filename);
        int pack(char *out, char **filename);
        int unpack(char *root, char *in);
       +int delete_content(FILE *metafile);
       +int delete(const char *datadir, const char *rootfs, const char *name);
        struct pkg *pack_load_file(char *file);
        struct pkg *pack_load(char *name);
        
       t@@ -65,6 +68,29 @@ usage(char *name)
        }
        
        /*
       + * returns 0 if a directory is empty, -1 otherwise
       + */
       +int
       +d_empty(char *dir)
       +{
       +        DIR *d;
       +        struct dirent *p;
       +
       +        if (!(d = opendir(dir))) {
       +                perror("opendir");
       +                return -1;
       +        }
       +
       +        while ((p = readdir(d)))
       +                if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
       +                        closedir(d);
       +                        return -1;
       +                }
       +
       +        return 0;
       +}
       +
       +/*
         * recursive mkdir, taken from the ii project
         * http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html
         */
       t@@ -146,6 +172,7 @@ list_local(const char *datadir)
                        if (strcmp(p->d_name, ".") && strcmp(p->d_name, ".."))
                                printf("%s\n", p->d_name);
        
       +        closedir(d);
                return 0;
        }
        
       t@@ -295,6 +322,77 @@ unpack(char *root, char *in)
                return 0;
        }
        
       +/*
       + * Delete entries listed in a file recursively
       + * This will also remove directories if they are empty
       + */
       +int
       +delete_content(FILE *f)
       +{
       +        char file[PATH_MAX];
       +        struct stat st;
       +        size_t len;
       +
       +        if (fgets(file, PATH_MAX, f))
       +                delete_content(f);
       +
       +        /* remove trailing '\n' */
       +        len = strnlen(file, PATH_MAX);
       +        file[len - 1] = 0;
       +        len--;
       +
       +        /* remove potential trailing '/' */
       +        if (file[len - 1] == '/') {
       +                file[len - 1] = 0;
       +                len--;
       +        }
       +
       +        stat(file, &st);
       +        if (S_ISDIR(st.st_mode) && d_empty(file))
       +                rmdir(file);
       +        else
       +                unlink(file);
       +
       +        return 0;
       +}
       +
       +/*
       + * Delete all files related to a package. Installed files, but also metadata
       + */
       +int
       +delete(const char *datadir, const char *rootfs, const char *name)
       +{
       +        char *cwd;
       +        FILE *f;
       +        char meta[PATH_MAX];
       +
       +        snprintf(meta, PATH_MAX, "%s/%s/files", datadir, name);
       +
       +        if ((f = fopen(meta, "r")) == NULL) {
       +                perror("fopen");
       +                return -1;
       +        }
       +
       +        /* hack to allow relative path for metadata */
       +        cwd = getcwd(NULL, PATH_MAX);
       +        if (chdir(rootfs) < 0) {
       +                perror("chdir");
       +                return -1;
       +        }
       +
       +        delete_content(f);
       +        fclose(f);
       +
       +        /* hack again */
       +        chdir(cwd);
       +
       +        unlink(meta);
       +        snprintf(meta, PATH_MAX, "%s/%s", datadir, name);
       +        rmdir(meta);
       +
       +        return 0;
       +}
       +
        struct pkg *
        pack_load_file(char *path)
        {
       t@@ -342,6 +440,8 @@ pack_load(char *name)
        
                if (strchr(name, '/'))
                        p = pack_load_file(name);
       +        else
       +                return NULL;
        
                if (!p) {
                        fprintf(stderr, "could not load package %s\n", name);
       t@@ -355,7 +455,7 @@ pack_load(char *name)
        int
        main (int argc, char **argv)
        {
       -        char *fn = NULL;
       +        char *n = NULL;
                struct pkg *p = NULL;
                uint8_t action = ACTION_INVALID;
        
       t@@ -364,45 +464,53 @@ main (int argc, char **argv)
                        if (argc <3) /* what a cute variable */
                                usage(argv0);
                        action = ACTION_PACKAGE;
       -                fn = ARGF();
       +                n = ARGF();
                        break;
                case 'a':
                        action = ACTION_INSTALL;
       -                fn = EARGF(usage(argv0));
       +                n = EARGF(usage(argv0));
                        break;
                case 'i':
                        action = ACTION_INSPECT;
       -                fn = EARGF(usage(argv0));
       +                n = EARGF(usage(argv0));
                        break;
                case 'l':
                        action = ACTION_LIST_LOCAL;
                        break;
       +        case 'r':
       +                action = ACTION_DELETE;
       +                n = EARGF(usage(argv0));
       +                break;
                case 'h':
                default:
                        usage(argv0);
                        return 0;
                }ARGEND;
        
       -        if (fn) {
       -                p = pack_load(fn);
       -                if (!p)
       -                        return ERR_PACKAGE_LOAD;
       -        }
       +        if (n)
       +                p = pack_load(n);
        
                switch (action) {
                case ACTION_INSTALL:
       +                if (!p)
       +                        return ERR_PACKAGE_LOAD;
                        if (metadata(PACKAGE_DATA, p->path) == 0)
                                return unpack(PACKAGE_ROOT, p->path);
                        fprintf(stderr, "could not write metadata for %s\n", p->name);
                        return ERR_METADATA;
                case ACTION_INSPECT:
       +                if (!p)
       +                        return ERR_PACKAGE_LOAD;
                        return inspect(1, p->path);
                case ACTION_PACKAGE:
       +                if (!p)
       +                        return ERR_PACKAGE_LOAD;
                        return pack(p->path, argv);
                case ACTION_LIST_LOCAL:
                        return list_local(PACKAGE_DATA);
       +        case ACTION_DELETE:
       +                return delete(PACKAGE_DATA, PACKAGE_ROOT, n);
                /* handle me, Octave */
       -        case ACTION_REMOVE:
                case ACTION_UPDATE:
                case ACTION_LIST_FILES:
                case ACTION_LIST_DEPS: