URI: 
       tMake prototype/function declaration consistent - pm - barely a pack manager
  HTML git clone git://z3bra.org/pm
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit ce53dfcafe58e9315780df8764352dfb85df965f
   DIR parent 8ac938b57e4fe44d05aa7662a32e21939291f555
  HTML Author: z3bra <willyatmailoodotorg>
       Date:   Sat, 18 Jun 2016 22:40:19 +0200
       
       Make prototype/function declaration consistent
       
       Diffstat:
         M pm.c                                |     712 ++++++++++++++++---------------
       
       1 file changed, 359 insertions(+), 353 deletions(-)
       ---
   DIR diff --git a/pm.c b/pm.c
       t@@ -64,18 +64,17 @@ struct pack *pack_load_tarball(char *path);
        struct pack *pack_load_directory(const char *datadir, char *path);
        struct pack *pack_load(const char *datadir, char *path);
        void pack_free(struct pack *p);
       +int pack_extract(const char *rootfs, const char *datadir, struct pack *p);
        int pack_install(const char *rootfs, const char *datadir, struct pack *p);
        int pack_delete(const char *rootfs, const char *datadir, struct pack *p);
        
       +int inspect_version(const char *datadir, const char *name, char version[]);
        int inspect_collision(const char *rootfs, struct pack *p);
       -int inspect_system(int fd, const char *datadir);
        int inspect_files(int fd, const char *datadir, const char *packname);
       -int inspect_version(const char *datadir, const char *name, char version[]);
       -int inspect(const char *datadir, const char *packname);
       +int inspect_system(int fd, const char *datadir);
        
        int write_metadata(const char *datadir, struct pack *pack);
        int write_entry(struct archive *a, struct archive *w);
       -int unpack(const char *rootfs, const char *datadir, struct pack *p);
        int delete_node(char *path);
        int delete_content(char *map, size_t size);
        
       t@@ -83,6 +82,7 @@ int delete_content(char *map, size_t size);
        int install(const char *rootfs, const char *datadir, char *path);
        int update(const char *rootfs, const char *datadir, char *path);
        int delete(const char *rootfs, const char *datadir, char *name);
       +int inspect(const char *datadir, const char *packname);
        
        int verbose = LOG_NONE;
        int overwrite = 0;
       t@@ -90,7 +90,7 @@ int overwrite = 0;
        void
        usage(char *name)
        {
       -        fprintf(stderr, "usage: %s -adfiuv [pack]\n" , name);
       +        fprintf(stderr, "usage: %s [-vvv] -adfiu [pack]\n" , name);
        }
        
        
       t@@ -156,200 +156,121 @@ base_name(char *path)
        }
        
        
       -int
       -inspect_version(const char *datadir, const char *name, char version[LINE_MAX])
       -{
       -        FILE *stream;
       -        char tmp[PATH_MAX] = "", *lf = NULL;
       -
       -        snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, name);
       -        if ((stream = fopen(tmp, "r")) == NULL) {
       -                sprintf(version, "(unknown)");
       -                return 1;
       -        } else {
       -                fgets(version, LINE_MAX, stream);
       -                if ((lf = strchr(version, '\n')) != NULL)
       -                        *lf = 0;
       -        }
       -
       -        if (stream)
       -                fclose(stream);
       -
       -        return 0;
       -}
       -
       -
        /*
       - * Check for collisions between the filesystem and the tarball
       + * Load a pack from a tarball and return a pack structure
         */
       -int
       -inspect_collision(const char *rootfs, struct pack *p)
       +struct pack *
       +pack_load_tarball(char *path)
        {
       -        int r = 0;
       -        char cwd[PATH_MAX] = "";
       -        struct stat st;
       -        struct archive *a;
       -        struct archive_entry *e;
       -
       -        a = archive_read_new();
       -        archive_read_support_filter_bzip2(a);
       -        archive_read_support_format_tar(a);
       +        int fd;
       +        struct pack *pack = NULL;
       +        char tmp[PATH_MAX] = "";
       +        char *p;
        
       -        r = archive_read_open_filename(a, p->path, 0);
       -        if (r != ARCHIVE_OK) {
       -                fprintf(stderr, "%s: %s\n", p->path, archive_error_string(a));
       -                return -1;
       +        if ((fd = open(path, O_RDONLY)) < 0) {
       +                perror(path);
       +                return NULL;
                }
       +        close(fd);
        
       -        getcwd(cwd, PATH_MAX);
       -        chdir(rootfs);
       -        while (archive_read_next_header(a, &e) == ARCHIVE_OK) {
       -                if (stat(archive_entry_pathname(e), &st) == 0 && !S_ISDIR(st.st_mode)) {
       -                        fprintf(stderr, "%s: file exists\n", archive_entry_pathname(e));
       -                        r = -1;
       -                        break;
       -                }
       -                archive_read_data_skip(a);
       +        if (!(pack = malloc(sizeof(struct pack)))) {
       +                perror(path);
       +                return NULL;
                }
        
       -        archive_read_free(a);
       -        chdir(cwd);
       -
       -        return r;
       -}
       -
       +        pack->path = NULL;
       +        pack->name = NULL;
       +        pack->version = NULL;
        
       -/*
       - * Write files installed by a pack to a file descriptor
       - */
       -int
       -inspect_files(int fd, const char *datadir, const char *packname)
       -{
       -        int meta;
       -        char tmp[PATH_MAX] = "";
       -        size_t len;
       +        pack->path = strdup(path);
       +        snprintf(tmp, PATH_MAX, "%s", base_name(path));
        
       -        snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, packname);
       -        if ((meta = open(tmp, O_RDONLY)) < 0) {
       -                perror(tmp);
       -                return -1;
       +        for (p = tmp; *p && *p != PACK_SEPARATOR; p++);
       +        if (!*p) {
       +                pack_free(pack);
       +                return NULL;
                }
       +        *p = 0;
       +        pack->name = strdup(tmp);
       +        *p = PACK_SEPARATOR;
        
       -        while ((len = read(meta, tmp, PATH_MAX)) > 0) {
       -                tmp[len] = 0;
       -                dprintf(fd, "%s", tmp);
       +        snprintf(tmp, PATH_MAX, "%s", p + 1);
       +        for (p = tmp; *p && strcmp(p, PACK_EXTENSION); p++);
       +        if (!*p) {
       +                pack_free(pack);
       +                return NULL;
                }
       +        *p = 0;
       +        pack->version = strdup(tmp);
       +        *p = '.';
        
       -        return 0;
       +        return pack;
        }
        
        
        /*
       - * Write packs installed in datadir to a file descriptor
       + * Load a pack from a metadata directory and return a pack structure
         */
       -int
       -inspect_system(int fd, const char *datadir)
       +struct pack *
       +pack_load_directory(const char *datadir, char *path)
        {
       -        DIR *d;
       -        struct dirent *p;
       -        char ver[LINE_MAX];
       +        struct pack *pack = NULL;
        
       -        if (!(d = opendir(datadir))) {
       -                perror(datadir);
       -                return -1;
       +        if (!(pack = malloc(sizeof(struct pack)))) {
       +                perror(path);
       +                return NULL;
                }
        
       -        while ((p = readdir(d)))
       -                if (p->d_name[0] != '.') {
       -                        inspect_version(datadir, p->d_name, ver);
       -                        dprintf(fd, "%s\t%s\n", p->d_name, ver);
       -                }
       -
       -        closedir(d);
       -        return 0;
       -}
       -
       +        pack->path = NULL;
       +        pack->name = NULL;
       +        pack->version = malloc(LINE_MAX);
        
       -/*
       - * Inspect the system, either by looking into a pack, checking installed
       - * files, or listing packs actually installed
       - */
       -int
       -inspect(const char *datadir, const char *packname)
       -{
       -        /* name is NULL, list packs installed */
       -        if (!packname)
       -                return inspect_system(1, datadir);
       +        pack->path = strdup(path);
       +        pack->name = strdup(base_name(path));
       +        inspect_version(datadir, pack->name, pack->version);
        
       -        /* otherwise, list files installed by a pack */
       -        return inspect_files(1, datadir, packname);
       +        return pack;
        }
        
        
        /*
       - * Write metadata about a pack file:
       - * + datadir/packname/version - version of the pack installed
       + * Guess how a pack should be loaded depending on the path given
         */
       -int
       -write_metadata(const char *datadir, struct pack *p)
       +struct pack *
       +pack_load(const char *datadir, char *path)
        {
       -        int fd, r;
       +        struct pack *p = NULL;
                struct stat st;
       -        char tmp[PATH_MAX];
       -
       -        snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name);
       -
       -        if (stat(tmp, &st) < 0 && errno == ENOENT) {
       -                log(LOG_DEBUG, "creating metadata directory %s\n", tmp);
       -                if ((r = mkdir_parents(tmp, 0755)) < 0)
       -                        return r;
       -        }
       -
       -        snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, p->name);
       -
       -        log(LOG_DEBUG, "openning %s for writing\n", tmp);
       -        if ((fd = open(tmp, O_CREAT|O_WRONLY|O_TRUNC, 0644)) < 0) {
       -                perror(tmp);
       -                return -1;
       -        }
       -
       -        r  = write(fd, p->version, strnlen(p->version, LINE_MAX));
       -        r += write(fd, "\n", 1);
        
       -        if (r < 1) {
       -                perror(tmp);
       -                close(fd);
       -                return -1;
       +        if (stat(path, &st) < 0) {
       +                perror(path);
       +        } else if (S_ISDIR(st.st_mode)) {
       +                p = pack_load_directory(datadir, path);
       +        } else if (S_ISREG(st.st_mode)) {
       +                p = pack_load_tarball(path);
                }
        
       -        close(fd);
       -        return 0;
       +        return p;
        }
        
        
        /*
       - * Write an archive entry on the disk, thus creating the file
       + * Free a pack structure
         */
       -int
       -write_entry(struct archive *a, struct archive *w)
       +void
       +pack_free(struct pack *p)
        {
       -        int r;
       -        const void *buf;
       -        size_t len;
       -        off_t off;
       +        if (!p)
       +                return;
        
       -        for (;;) {
       -                r = archive_read_data_block(a, &buf, &len, &off);
       -                switch (r) {
       -                case ARCHIVE_EOF: return 0;
       -                case ARCHIVE_OK: break;
       -                default: return r;
       -                }
       +        if (p->path)
       +                free(p->path);
       +        if (p->name)
       +                free(p->name);
       +        if (p->version)
       +                free(p->version);
        
       -                r = archive_write_data_block(w, buf, len, off);
       -                if (r != ARCHIVE_OK)
       -                        return r;
       -        }
       +        free(p);
        }
        
        
       t@@ -357,7 +278,7 @@ write_entry(struct archive *a, struct archive *w)
         * Extract a tarball to the given directory
         */
        int
       -unpack(const char *rootfs, const char *datadir, struct pack *p)
       +pack_extract(const char *rootfs, const char *datadir, struct pack *p)
        {
                int r, fd;
                struct archive *a;
       t@@ -455,32 +376,285 @@ pack_install(const char *rootfs, const char *datadir, struct pack *p)
                }
        
                log(LOG_VERBOSE, "extracting pack to %s\n", rootfs);
       -        r = unpack(rootfs, datadir, p);
       +        r = pack_extract(rootfs, datadir, p);
        
                return r;
        }
        
        
        /*
       - * Deletes a node, be it a file or a directory.
       - * In case the node doesn't exists, we're done
       + * Delete a pack from the system.
       + * This will read the file datadir/name/files, change directory to rootfs
       + * and call delete_content()
         */
        int
       -delete_node(char *path)
       +pack_delete(const char *rootfs, const char *datadir, struct pack *p)
        {
       -        int r = 0;
       +        int fd;
       +        char *addr = NULL;
       +        char tmp[PATH_MAX], cwd[PATH_MAX];
                struct stat st;
       -        size_t len = 0;
       -
       -        len = strnlen(path, PATH_MAX);
       -        /* remove potential trailing '/' */
       -        if (path[len - 1] == '/')
       -                path[--len] = 0;
        
       -        log(LOG_DEBUG, "- %s\n", path);
       +        snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, p->name);
       +        if (stat(tmp, &st) < 0 && errno == ENOENT) {
       +                fprintf(stderr, "%s: not installed\n", p->name);
       +                return -1;
       +        }
       +        if ((fd = open(tmp, O_RDONLY)) < 0) {
       +                perror(tmp);
       +                return ERR_DELETE;
       +        }
        
       -        /*
       -         * if path doesn't exist anymore, it's all good :)
       +        addr = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
       +        if (addr == MAP_FAILED) {
       +                perror(tmp);
       +                close(fd);
       +        }
       +
       +        getcwd(cwd, PATH_MAX);
       +        if (chdir(rootfs) < 0) {
       +                perror(rootfs);
       +                close(fd);
       +                return ERR_DELETE;
       +        }
       +
       +        log(LOG_VERBOSE, "deleting installed files\n");
       +        /* ignore errors so everything gets deleted */
       +        if (delete_content(addr, st.st_size) < 0) {
       +                fprintf(stderr, "%s: pack not removed\n", p->name);
       +                close(fd);
       +                return ERR_DELETE;
       +        }
       +        close(fd);
       +
       +        log(LOG_VERBOSE, "removing %s\n", tmp);
       +        if (unlink(tmp) < 0) {
       +                perror(tmp);
       +                return ERR_DELETE;
       +        }
       +
       +        snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, p->name);
       +
       +        log(LOG_VERBOSE, "removing %s\n", tmp);
       +        if (unlink(tmp) < 0) {
       +                perror(tmp);
       +                return ERR_DELETE;
       +        }
       +
       +        /* remove metadata directory, no matter what */
       +        snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name);
       +        log(LOG_DEBUG, "removing %s\n", tmp);
       +        rmdir(tmp);
       +
       +        chdir(cwd);
       +
       +        return 0;
       +}
       +
       +
       +/*
       + * Read the version file for a pack and fill the given pointer with it
       + */
       +int
       +inspect_version(const char *datadir, const char *name, char version[LINE_MAX])
       +{
       +        FILE *stream;
       +        char tmp[PATH_MAX] = "", *lf = NULL;
       +
       +        snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, name);
       +        if ((stream = fopen(tmp, "r")) == NULL) {
       +                sprintf(version, "(unknown)");
       +                return 1;
       +        } else {
       +                fgets(version, LINE_MAX, stream);
       +                if ((lf = strchr(version, '\n')) != NULL)
       +                        *lf = 0;
       +        }
       +
       +        fclose(stream);
       +
       +        return 0;
       +}
       +
       +
       +/*
       + * Check for collisions between the filesystem and the tarball
       + */
       +int
       +inspect_collision(const char *rootfs, struct pack *p)
       +{
       +        int r = 0;
       +        char cwd[PATH_MAX] = "";
       +        struct stat st;
       +        struct archive *a;
       +        struct archive_entry *e;
       +
       +        a = archive_read_new();
       +        archive_read_support_filter_bzip2(a);
       +        archive_read_support_format_tar(a);
       +
       +        r = archive_read_open_filename(a, p->path, 0);
       +        if (r != ARCHIVE_OK) {
       +                fprintf(stderr, "%s: %s\n", p->path, archive_error_string(a));
       +                return -1;
       +        }
       +
       +        getcwd(cwd, PATH_MAX);
       +        chdir(rootfs);
       +        while (archive_read_next_header(a, &e) == ARCHIVE_OK) {
       +                if (stat(archive_entry_pathname(e), &st) == 0 && !S_ISDIR(st.st_mode)) {
       +                        fprintf(stderr, "%s: file exists\n", archive_entry_pathname(e));
       +                        r = -1;
       +                        break;
       +                }
       +                archive_read_data_skip(a);
       +        }
       +
       +        archive_read_free(a);
       +        chdir(cwd);
       +
       +        return r;
       +}
       +
       +
       +/*
       + * Write files installed by a pack to a file descriptor
       + */
       +int
       +inspect_files(int fd, const char *datadir, const char *packname)
       +{
       +        int meta;
       +        char tmp[PATH_MAX] = "";
       +        size_t len;
       +
       +        snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, packname);
       +        if ((meta = open(tmp, O_RDONLY)) < 0) {
       +                perror(tmp);
       +                return -1;
       +        }
       +
       +        while ((len = read(meta, tmp, PATH_MAX)) > 0) {
       +                tmp[len] = 0;
       +                dprintf(fd, "%s", tmp);
       +        }
       +
       +        return 0;
       +}
       +
       +
       +/*
       + * Write packs installed in datadir to a file descriptor
       + */
       +int
       +inspect_system(int fd, const char *datadir)
       +{
       +        DIR *d;
       +        struct dirent *p;
       +        char ver[LINE_MAX];
       +
       +        if (!(d = opendir(datadir))) {
       +                perror(datadir);
       +                return -1;
       +        }
       +
       +        while ((p = readdir(d)))
       +                if (p->d_name[0] != '.') {
       +                        inspect_version(datadir, p->d_name, ver);
       +                        dprintf(fd, "%s\t%s\n", p->d_name, ver);
       +                }
       +
       +        closedir(d);
       +        return 0;
       +}
       +
       +
       +/*
       + * Write metadata about a pack file:
       + * + datadir/packname/version - version of the pack installed
       + */
       +int
       +write_metadata(const char *datadir, struct pack *p)
       +{
       +        int fd, r;
       +        struct stat st;
       +        char tmp[PATH_MAX];
       +
       +        snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name);
       +
       +        if (stat(tmp, &st) < 0 && errno == ENOENT) {
       +                log(LOG_DEBUG, "creating metadata directory %s\n", tmp);
       +                if ((r = mkdir_parents(tmp, 0755)) < 0)
       +                        return r;
       +        }
       +
       +        snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, p->name);
       +
       +        log(LOG_DEBUG, "openning %s for writing\n", tmp);
       +        if ((fd = open(tmp, O_CREAT|O_WRONLY|O_TRUNC, 0644)) < 0) {
       +                perror(tmp);
       +                return -1;
       +        }
       +
       +        r  = write(fd, p->version, strnlen(p->version, LINE_MAX));
       +        r += write(fd, "\n", 1);
       +
       +        if (r < 1) {
       +                perror(tmp);
       +                close(fd);
       +                return -1;
       +        }
       +
       +        close(fd);
       +        return 0;
       +}
       +
       +
       +/*
       + * Write an archive entry on the disk, thus creating the file
       + */
       +int
       +write_entry(struct archive *a, struct archive *w)
       +{
       +        int r;
       +        const void *buf;
       +        size_t len;
       +        off_t off;
       +
       +        for (;;) {
       +                r = archive_read_data_block(a, &buf, &len, &off);
       +                switch (r) {
       +                case ARCHIVE_EOF: return 0;
       +                case ARCHIVE_OK: break;
       +                default: return r;
       +                }
       +
       +                r = archive_write_data_block(w, buf, len, off);
       +                if (r != ARCHIVE_OK)
       +                        return r;
       +        }
       +}
       +
       +
       +/*
       + * Deletes a node, be it a file or a directory.
       + * In case the node doesn't exists, we're done
       + */
       +int
       +delete_node(char *path)
       +{
       +        int r = 0;
       +        struct stat st;
       +        size_t len = 0;
       +
       +        len = strnlen(path, PATH_MAX);
       +        /* remove potential trailing '/' */
       +        if (path[len - 1] == '/')
       +                path[--len] = 0;
       +
       +        log(LOG_DEBUG, "- %s\n", path);
       +
       +        /*
       +         * if path doesn't exist anymore, it's all good :)
                 * we use lstat here so that dangling links can be delt with too
                 */
                if (lstat(path, &st) < 0 && errno == ENOENT)
       t@@ -501,6 +675,7 @@ delete_node(char *path)
                return 0;
        }
        
       +
        /*
         * Delete all entries listed in the given file.
         * if the entry doesn't exist, it will be ignored
       t@@ -518,7 +693,7 @@ delete_content(char *map, size_t size)
                        /*
                         * it is assumed here that the file is POSIX and thus that
                         * the last char will be \n.
       -                 * This might sound stupid; but this is what unpack() will do.
       +                 * This might sound stupid; but this is what pack_extract() will do.
                         * We read the mmap file backward until we encounter \n or the
                         * beginning of the file. If it's an \n, we replace it by 0 and
                         * process to the deletion of the inode, either with rmdir or
       t@@ -544,76 +719,6 @@ delete_content(char *map, size_t size)
        
        
        /*
       - * Delete a pack from the system.
       - * This will read the file datadir/name/files, change directory to rootfs
       - * and call delete_content()
       - */
       -int
       -pack_delete(const char *rootfs, const char *datadir, struct pack *p)
       -{
       -        int fd;
       -        char *addr = NULL;
       -        char tmp[PATH_MAX], cwd[PATH_MAX];
       -        struct stat st;
       -
       -        snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, p->name);
       -        if (stat(tmp, &st) < 0 && errno == ENOENT) {
       -                fprintf(stderr, "%s: not installed\n", p->name);
       -                return -1;
       -        }
       -        if ((fd = open(tmp, O_RDONLY)) < 0) {
       -                perror(tmp);
       -                return ERR_DELETE;
       -        }
       -
       -        addr = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
       -        if (addr == MAP_FAILED) {
       -                perror(tmp);
       -                close(fd);
       -        }
       -
       -        getcwd(cwd, PATH_MAX);
       -        if (chdir(rootfs) < 0) {
       -                perror(rootfs);
       -                close(fd);
       -                return ERR_DELETE;
       -        }
       -
       -        log(LOG_VERBOSE, "deleting installed files\n");
       -        /* ignore errors so everything gets deleted */
       -        if (delete_content(addr, st.st_size) < 0) {
       -                fprintf(stderr, "%s: pack not removed\n", p->name);
       -                close(fd);
       -                return ERR_DELETE;
       -        }
       -        close(fd);
       -
       -        log(LOG_VERBOSE, "removing %s\n", tmp);
       -        if (unlink(tmp) < 0) {
       -                perror(tmp);
       -                return ERR_DELETE;
       -        }
       -
       -        snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, p->name);
       -
       -        log(LOG_VERBOSE, "removing %s\n", tmp);
       -        if (unlink(tmp) < 0) {
       -                perror(tmp);
       -                return ERR_DELETE;
       -        }
       -
       -        /* remove metadata directory, no matter what */
       -        snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name);
       -        log(LOG_DEBUG, "removing %s\n", tmp);
       -        rmdir(tmp);
       -
       -        chdir(cwd);
       -
       -        return 0;
       -}
       -
       -
       -/*
         * Install a pack from the given path. This wraps load/install of a pack
         */
        int
       t@@ -664,6 +769,9 @@ update(const char *rootfs, const char *datadir, char *path)
        }
        
        
       +/*
       + * Delete a currently installed pack. This wraps load/delete functions
       + */
        int
        delete(const char *rootfs, const char *datadir, char *name)
        {
       t@@ -688,120 +796,18 @@ delete(const char *rootfs, const char *datadir, char *name)
        
        
        /*
       - * Load a pack from a tarball and return a pack structure
       - */
       -struct pack *
       -pack_load_tarball(char *path)
       -{
       -        int fd;
       -        struct pack *pack = NULL;
       -        char tmp[PATH_MAX] = "";
       -        char *p;
       -
       -        if ((fd = open(path, O_RDONLY)) < 0) {
       -                perror(path);
       -                return NULL;
       -        }
       -        close(fd);
       -
       -        if (!(pack = malloc(sizeof(struct pack)))) {
       -                perror(path);
       -                return NULL;
       -        }
       -
       -        pack->path = NULL;
       -        pack->name = NULL;
       -        pack->version = NULL;
       -
       -        pack->path = strdup(path);
       -        snprintf(tmp, PATH_MAX, "%s", base_name(path));
       -
       -        for (p = tmp; *p && *p != PACK_SEPARATOR; p++);
       -        if (!*p) {
       -                pack_free(pack);
       -                return NULL;
       -        }
       -        *p = 0;
       -        pack->name = strdup(tmp);
       -        *p = PACK_SEPARATOR;
       -
       -        snprintf(tmp, PATH_MAX, "%s", p + 1);
       -        for (p = tmp; *p && strcmp(p, PACK_EXTENSION); p++);
       -        if (!*p) {
       -                pack_free(pack);
       -                return NULL;
       -        }
       -        *p = 0;
       -        pack->version = strdup(tmp);
       -        *p = '.';
       -
       -        return pack;
       -}
       -
       -
       -/*
       - * Load a pack from a metadata directory and return a pack structure
       - */
       -struct pack *
       -pack_load_directory(const char *datadir, char *path)
       -{
       -        struct pack *pack = NULL;
       -
       -        if (!(pack = malloc(sizeof(struct pack)))) {
       -                perror(path);
       -                return NULL;
       -        }
       -
       -        pack->path = NULL;
       -        pack->name = NULL;
       -        pack->version = malloc(LINE_MAX);
       -
       -        pack->path = strdup(path);
       -        pack->name = strdup(base_name(path));
       -        inspect_version(datadir, pack->name, pack->version);
       -
       -        return pack;
       -}
       -
       -
       -/*
       - * Guess how a pack should be loaded depending on the path given
       - */
       -struct pack *
       -pack_load(const char *datadir, char *path)
       -{
       -        struct pack *p = NULL;
       -        struct stat st;
       -
       -        if (stat(path, &st) < 0) {
       -                perror(path);
       -        } else if (S_ISDIR(st.st_mode)) {
       -                p = pack_load_directory(datadir, path);
       -        } else if (S_ISREG(st.st_mode)) {
       -                p = pack_load_tarball(path);
       -        }
       -
       -        return p;
       -}
       -
       -
       -/*
       - * Free a pack structure
       + * Inspect the system, either by looking into a pack, checking installed
       + * files, or listing packs actually installed
         */
       -void
       -pack_free(struct pack *p)
       +int
       +inspect(const char *datadir, const char *packname)
        {
       -        if (!p)
       -                return;
       -
       -        if (p->path)
       -                free(p->path);
       -        if (p->name)
       -                free(p->name);
       -        if (p->version)
       -                free(p->version);
       +        /* name is NULL, list packs installed */
       +        if (!packname)
       +                return inspect_system(1, datadir);
        
       -        free(p);
       +        /* otherwise, list files installed by a pack */
       +        return inspect_files(1, datadir, packname);
        }