--- /dev/null
+#define _POSIX_C_SOURCE 2
+#include <stdlib.h>
+#include <stdio.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"
+
+struct config {
+ char *dbfile;
+ char *cmd;
+ char *program;
+ char *pkgid;
+ char *nullstr;
+ int errcontinue;
+ int errors;
+ int (*callback)(void*,int,char**,char**);
+};
+
+static void usage() {
+ printf("usage: ...\n");
+}
+
+static int run_program(void *f, int ncols, char **vals, char **cols) {
+ struct config *conf = f;
+ int i;
+ char *argv[ncols+1];
+ pid_t pid;
+ int status;
+ int rv;
+
+ argv[0] = conf->program;
+
+ for (i=0;i<ncols;i++) {
+ argv[i+1] = vals[i] ? vals[i] : conf->nullstr;
+ }
+ argv[i+1] = 0;
+
+ pid = fork();
+
+ if (pid == -1) {
+ perror("cannot fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* child */
+ rv = execvp(conf->program, argv);
+ if (rv == -1) {
+ perror("cannot exec");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ rv = wait(&status);
+ if (rv == -1) {
+ perror("error waiting for child");
+ exit(EXIT_FAILURE);
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return 0;
+ } else if (conf->errcontinue) {
+ conf->errors++;
+ return 0;
+ }
+ return 1;
+}
+
+static int run_shell(void *f, int ncols, char **vals, char **cols) {
+ struct config *conf = f;
+ int i;
+ char *argv[ncols+4];
+ pid_t pid;
+ int status;
+ int rv;
+
+ argv[0] = "sh";
+ argv[1] = "-c";
+ argv[2] = conf->cmd;
+ argv[3] = "sh";
+
+ for (i=0;i<ncols;i++) {
+ argv[i+4] = vals[i] ? vals[i] : conf->nullstr;
+ }
+ argv[i+4] = 0;
+
+ pid = fork();
+
+ if (pid == -1) {
+ perror("cannot fork");
+ return 1;
+ }
+
+ if (pid == 0) {
+ /* child */
+ rv = execvp("sh", argv);
+ if (rv == -1) {
+ perror("cannot exec");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ rv = wait(&status);
+ if (rv == -1) {
+ perror("error waiting for child");
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return 0;
+ } else if (conf->errcontinue) {
+ conf->errors++;
+ return 0;
+ }
+ return 1;
+}
+
+static int printpaths(void *f, int ncols, char **vals, char **cols) {
+
+ /* suppress unused warnings */
+
+ if (ncols < 4) {
+ fprintf(stderr, "can't find path names\n");
+ return 1;
+ }
+// printf("path cols %d %p %p\n", ncols, vals, cols);
+ fprintf(stdout, "%s\n", vals[3]);
+#if 0
+ cols = 0;
+ f = 0;
+ int i;
+ if (cols == 0) {
+ fprintf(stderr, "sqlite can't get column names\n");
+ }
+ for (i=0;i<ncols;i++) {
+// if (i>3) fprintf(out, "\t");
+ fprintf(stdout, "%s\n", vals[i]);
+ }
+#endif
+ return 0;
+}
+
+void parse_package(char *pstr, char *name, char *ver, int *rel) {
+ if (name) *name = 0;
+ if (ver) *ver = 0;
+ if (rel) *rel = -1;
+
+ /* string - ver - rel */
+ /* rel is all digits */
+ /* possible forms:
+ * ^(.+)-([0-9][^-]*)-([\d+])$
+ * ^(.+)-([0-9][^-]*)$
+ * ^(.+)$
+ * The main problem in parsing is that the package name itself
+ * can contain a '-', so you can't just split on '-'
+ * Also, the version can be just digits.
+ */
+
+ /* everything up to the first '-' is in the name */
+ while (*pstr) {
+ if (*pstr == '-' && isdigit(*(pstr+1))) {
+ break;
+ }
+ if (name) {
+ *name++ = *pstr;
+ }
+ pstr++;
+ }
+ if (name) *name = 0;
+ if (*pstr == '-') {
+ pstr++;
+ }
+ while (*pstr && *pstr != '-') {
+ if (ver) {
+ *ver++ = *pstr;
+ }
+ pstr++;
+ }
+ if (ver) *ver = 0;
+ if (*pstr == '-') {
+ pstr++;
+ }
+ if (rel && *pstr) {
+ *rel = atoi(pstr);
+ }
+}
+
+#ifdef PATH_MAX
+#define PATHLEN PATH_MAX
+#else
+#define PATHLEN 4096
+#endif
+
+int main(int ac, char **av){
+ struct zpm pkg;
+ char *s;
+ int opt;
+
+ struct config conf = { "/var/lib/db.zpm", 0, 0, 0, "", 0, 0, printpaths
+ };
+
+
+ if ((s = getenv("ZPMDB"))) {
+ /* TODO does this need to be copied ? */
+ conf.dbfile = s;
+ }
+
+ /*
+ * -c 'shell command'
+ * -f 'package database', otherwise regular default of env ZPMDB,
+ * or /var/lib/zpm/zpm.db, or die
+ *
+ * arg 1: pkgid triple, but will do a pkg find
+ * arg 2: program to run. equivalent of 'printf "%s\n" "$path"
+ * otherwise'
+ */
+
+ while ((opt = getopt(ac, av, "f:c:n:C")) != -1) {
+ switch (opt) {
+ case 'f': conf.dbfile = optarg;
+ break;
+ case 'c': conf.cmd = optarg;
+ conf.callback = run_shell;
+ break;
+ case 'C':
+ conf.errcontinue = 1;
+ break;
+ case 'n':
+ conf.nullstr = 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) {
+ conf.pkgid = av[argn];
+ // fprintf(stderr, "set pkgid to %s\n", conf.pkgid);
+ argn++;
+ } else {
+ fprintf(stderr, "must specify pkgid\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* TODO lookup pkgid via zpm-findpkg equivalent */
+
+ if (argn < ac) {
+ conf.program = av[argn];
+ conf.callback = run_program;
+ argn++;
+ /* TODO set conf.args to remaining arguments */
+ }
+
+ /* install a collation function */
+ // zpm_addvercmp(&pkg);
+
+ if (!zpm_foreach_path(&pkg, conf.pkgid, conf.callback, &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.errors ? 1 : 0;
+}