--- /dev/null
+#define _POSIX_C_SOURCE 200112L
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <string.h>
+
+#include "sqlite3.h"
+#include "zpm.h"
+#include "t/ctap/ctap.h"
+
+struct config {
+ char *dbfile;
+ int plan;
+ int ran;
+ int failed;
+ int quiet;
+ int failed_only;
+ int tap;
+ int check_owner;
+ int check_group;
+ int check_perms;
+ int check_content; /* ignored for config files */
+ int check_mtime;
+};
+
+static void usage() {
+ printf("usage: zpm foreach-path [-fncC] args ...\n");
+}
+
+static char *column(char *col, int ncols, char **vals, char **cols) {
+ int i = 0;
+ char *val = NULL;
+
+ for (i=0; i < ncols; i++) {
+ if (!strcmp(col, cols[i])) {
+ val = vals[i];
+ break;
+ }
+ }
+ return val;
+}
+
+static int count_plan(void *f, int ncols, char **vals, char **cols) {
+ struct config *conf = f;
+
+ int ftype, configfile = 0;
+ char *v;
+
+ v = column("filetype", ncols, vals, cols);
+ ftype = *v;
+ v = column("configuration", ncols, vals, cols);
+ if (*v == '1') {
+ configfile = 1;
+ }
+
+ if (ftype == 'r' && !configfile) {
+ conf->plan++;
+ }
+
+ conf->plan++;
+
+ return 0;
+}
+
+static int verify(void *f, int ncols, char **vals, char **cols) {
+ struct config *conf = f;
+ char *path;
+ struct stat st;
+ int rv;
+ int ftype, configfile = 0;
+ char *v, *hash, ehash[ZPM_HASH_STRLEN+1];
+
+ path = column("path", ncols, vals, cols);
+ hash = column("hash", ncols, vals, cols);
+ v = column("filetype", ncols, vals, cols);
+ ftype = *v;
+ v = column("configuration", ncols, vals, cols);
+ if (*v == '1') {
+ configfile = 1;
+ }
+
+ conf->ran++;
+
+ rv = lstat(path, &st);
+ if (rv == -1) {
+ switch (errno) {
+ case ENOENT: ok(0, "%s does not exist", path);
+ break;
+ default: ok(0, "cannot stat %s: %s", path, strerror(errno));
+ conf->failed++;
+ break;
+ }
+ if (ftype == 'r' && !configfile) {
+ skip("no hash");
+ }
+ return 0;
+ }
+
+ ok(1, "%s exists", path);
+
+ if (ftype == 'r' && !configfile) {
+ zpm_hash(path, ehash, 0);
+ if (!is_string(ehash, hash, "hash %s", path)) {
+ conf->failed++;
+ }
+ }
+
+ return 0;
+}
+
+int main(int ac, char **av) {
+ struct zpm pkg;
+ char *s;
+ int opt;
+ char *pkgid;
+
+ struct config conf = { 0 };
+ conf.dbfile = "/var/lib/zpm/local.db";
+
+ if ((s = getenv("ZPMDB"))) {
+ /* TODO does this need to be copied ? */
+ conf.dbfile = s;
+ }
+
+ while ((opt = getopt(ac, av, "f:")) != -1) {
+ switch (opt) {
+ case 'f': conf.dbfile = optarg;
+ break;
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ int argn = optind;
+
+ if (!zpm_open(&pkg, conf.dbfile)) {
+ fprintf(stderr, "can't open zpm db %s\n", conf.dbfile);
+ exit(EXIT_FAILURE);
+ }
+ char *errmsg = 0;
+
+ if (argn < ac) {
+ pkgid = av[argn];
+ argn++;
+ } else {
+ fprintf(stderr, "must specify pkgid\n");
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ /* TODO lookup pkgid via zpm-findpkg equivalent */
+
+ if (!zpm_foreach_path(&pkg, pkgid, 0, count_plan, &conf, &errmsg)) {
+ if (errmsg) {
+ fprintf(stderr, "database error: %s\n", errmsg);
+ exit(EXIT_FAILURE);
+ }
+ if (pkg.error == 1) {
+ fprintf(stderr, "unable to allocate memory\n");
+ }
+ fprintf(stderr, "unable to plan\n");
+ exit(EXIT_FAILURE);
+ }
+
+ plan(conf.plan);
+
+ if (!zpm_foreach_path(&pkg, pkgid, 0, verify, &conf, &errmsg)) {
+ if (errmsg) {
+ fprintf(stderr, "database error: %s\n", errmsg);
+ exit(EXIT_FAILURE);
+ }
+ if (pkg.error == 1) {
+ fprintf(stderr, "unable to allocate memory\n");
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ zpm_close(&pkg);
+ return conf.failed ? 1 : 0;
+}