--- /dev/null
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "zpm.h"
+#include "sqlite/sqlite3.h"
+
+struct opts {
+ char *package, *version;
+ int release;
+ int complete, addcontent, isconfig, opt_l, recursive, verbose;
+ int followsymlinks;
+ int xargs, noclear;
+ mode_t clmode;
+
+ char *cltype, *clhash, *clmtime, *prefix, *strip, *cltarget;
+ char *clgroup, *cluser;
+};
+
+#if 0
+static void warn(char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+#endif
+
+static int dieval = EXIT_FAILURE;
+
+static void die(char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ exit(dieval);
+}
+
+char *cleanpath(char *path, char *strip, char *prefix) {
+ char *s = path;
+ char *t = s;
+ size_t len = 0;
+ char *new;
+
+ if (strip) {
+ len = strlen(strip);
+ if (strncmp(path, strip, len)) {
+ /* strip is not a prefix */
+ return 0;
+ }
+ s += len;
+ }
+
+ len += strlen(s)+1;
+
+ new = malloc(len);
+ memset(new, 0, len);
+
+ if (!new) {
+ return NULL;
+ }
+ t = new;
+
+ if (prefix) {
+ strcpy(new, prefix);
+ t = new + strlen(prefix);
+ }
+
+ /* skip leading curdir */
+ if (*s && *s == '.' && s[1] && s[1] == '/') {
+ s += 2;
+ }
+
+ /* skip leading slashes */
+ while (*s == '/') {
+ s++;
+ }
+
+ if (*t != '/') {
+ *t++ = '/';
+ }
+
+ for (; *s; s++) {
+ /* skip multiple slashes */
+ if (*s == '/' && s[1] && s[1] == '/') {
+ continue;
+ }
+ /* skip trailing slash */
+ if (*s == '/' && !s[1]) {
+ continue;
+ }
+
+ /* skip curdirs */
+ if (*s == '/' && s[1] && s[1] == '.' && s[2] && s[2] == '/') {
+ s += 1;
+ continue;
+ }
+
+ if (*s == '/' && s[1] && s[1] == '.' && s[2] == 0) {
+ s += 1;
+ continue;
+ }
+
+ *t++ = *s;
+ }
+ *t = 0;
+
+ return new;
+}
+
+char *getuser(uid_t uid) {
+ struct passwd *pw;
+
+ pw = getpwuid(uid);
+ if (pw) {
+ return strdup(pw->pw_name);
+ }
+ return 0;
+}
+
+char *getgroup(uid_t uid) {
+ struct passwd *pw;
+
+ pw = getpwuid(uid);
+ if (pw) {
+ return strdup(pw->pw_name);
+ }
+ return 0;
+}
+
+void free_file(struct zpm_file *file) {
+ free(file->path);
+ free(file->owner);
+ free(file->group);
+ free(file->target);
+ file->path = file->owner = file->group = file->target = 0;
+}
+
+int stat_file(struct zpm_file *file, char *path, struct opts *opt) {
+ struct stat st;
+ int rv;
+
+ file->data = path;
+
+ errno = 0;
+ if (opt->followsymlinks) {
+ rv = stat(path, &st);
+ } else {
+ rv = lstat(path, &st);
+ }
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ switch (st.st_mode & S_IFMT) {
+ case S_IFBLK: file->type = 'b'; break;
+ case S_IFCHR: file->type = 'c'; break;
+ case S_IFDIR: file->type = 'd'; break;
+ case S_IFIFO: file->type = 'p'; break;
+ case S_IFLNK: file->type = 'l'; break;
+ case S_IFREG: file->type = 'r'; break;
+ case S_IFSOCK: file->type = 's'; break;
+ default: file->type = 0; break;
+ }
+
+ file->configuration = opt->isconfig;
+
+ if (file->type == 0) {
+ return 0;
+ }
+
+ if (file->type == 'l') {
+ if (opt->cltarget) {
+ file->target = strdup(opt->cltarget);
+ } else {
+ char linkval[4096];
+ size_t n;
+ n = readlink(path, linkval, sizeof linkval);
+ if (n >= sizeof linkval) {
+ return 0;
+ }
+ file->target = strdup(linkval);
+ }
+ }
+
+ /* strip and prefix */
+ file->path = cleanpath(path, opt->strip, opt->prefix);
+ if (file->path == 0) {
+ free_file(file);
+ return 0;
+ }
+
+ if (opt->clmode) {
+ file->mode = opt->clmode;
+ } else {
+ file->mode = st.st_mode;
+ }
+
+ if (opt->cluser) {
+ file->owner = strdup(opt->cluser);
+ } else {
+ file->owner = getuser(st.st_uid);
+ }
+
+ if (file->owner == 0) {
+ free_file(file);
+ return 0;
+ }
+
+ if (opt->clgroup) {
+ file->group = strdup(opt->clgroup);
+ } else {
+ file->group = getgroup(st.st_gid);
+ }
+ if (file->group == 0) {
+ free_file(file);
+ return 0;
+ }
+
+ file->mtime = st.st_mtime;
+
+ return 1;
+}
+
+int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt);
+
+int add_dir(struct zpm *zpm, char *path, struct opts *opt) {
+ DIR *dir;
+ struct dirent *de;
+ struct zpm_file file = { 0 };
+
+ dir = opendir(path);
+
+ if (!dir) {
+ zpm_seterror(zpm, "can't open dir %s: %s", path, strerror(errno));
+ return 0;
+ }
+
+ while ((de = readdir(dir))) {
+ if (!strcmp(de->d_name, ".")) {
+ continue;
+ }
+ if (!strcmp(de->d_name, "..")) {
+ continue;
+ }
+
+ if (!stat_file(&file, de->d_name, opt)) {
+ zpm_seterror(zpm, "stat %s failed: %s",
+ de->d_name, strerror(errno));
+ return 0;
+ }
+
+ if (!add_file(zpm, &file, opt)) {
+ free_file(&file);
+ return 0;
+ }
+ free_file(&file);
+ }
+ return 1;
+}
+
+int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt) {
+ char hash[ZPM_HASH_STRLEN+1];
+
+ if (file->type == 'r') {
+ if (opt->addcontent) {
+ if (zpm_import(zpm, file->data, 0, hash)) {
+ strcpy(file->hash, hash);
+ } else {
+ return 0;
+ }
+ } else {
+ zpm_hash(file->data, hash, 0);
+ strcpy(file->hash, hash);
+ }
+ }
+
+ zpm_db_run(zpm, "insert or replace into packagefiles "
+ "(package,version,release,path,mode,mtime,username,"
+ "groupname,filetype,hash,configuration,target)"
+ " values "
+ "(%Q, %Q, %d, %Q, %o, %d, %Q, %Q, '%c', %Q, %d, %Q)",
+ opt->package, opt->version, opt->release, file->path,
+ file->mode, (int)file->mtime, file->owner, file->group,
+ file->type,
+ file->hash, file->configuration, file->target
+ );
+
+ if (zpm->error) {
+ return 0;
+ }
+
+ if (opt->verbose > 1) {
+ printf("%c%o %s:%s %s\n", file->type, file->mode, file->owner, file->group, file->path);
+ } else if (opt->verbose > 0) {
+
+ printf("%s\n", file->path);
+
+ }
+
+ if (opt->recursive && file->type == 'd') {
+ if (!add_dir(zpm, file->data, opt)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int main(int ac, char **av) {
+ struct zpm zpm;
+ struct opts opt = { 0 };
+ int i;
+ int option;
+
+ char hash[ZPM_HASH_STRLEN+1];
+
+ char *dbfile = getenv("ZPMDB");
+
+ if (!dbfile) {
+ dbfile = "/var/lib/zpm/local.db";
+ }
+
+ opt.addcontent = 1;
+
+ while ((option = getopt(ac, av, ":f:vr:l:P:S:cu:g:NCm:M:T:H:F:zxh")) != -1) {
+ switch (option) {
+ case 'C': opt.complete = 1; break;
+ case 'F': opt.cltype = optarg; break;
+ case 'H': opt.clhash = optarg; break;
+ case 'M': opt.clmtime = optarg; break;
+ case 'N': opt.addcontent = 0; break;
+ case 'P': opt.prefix = optarg; break;
+ case 'S': opt.strip = optarg; break;
+ case 'T': opt.cltarget = optarg; break;
+ case 'c': opt.isconfig = 1; break;
+ case 'f': dbfile = optarg; break;
+ case 'g': opt.clgroup = optarg; break;
+ case 'h': opt.followsymlinks = 1; break;
+ case 'l': opt.opt_l = 1; break;
+ case 'm': opt.clmode = strtol(optarg, NULL, 8); break;
+ case 'r': opt.recursive = 1; break;
+ case 'u': opt.cluser = optarg; break;
+ case 'v': opt.verbose++; break;
+ case 'x': opt.xargs = 1; dieval = 255; break;
+ case 'z': opt.noclear = 1; break;
+ default:
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if (ac < optind) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (!zpm_open(&zpm, dbfile)) {
+ die("can't open zpm db %s", dbfile);
+ }
+
+#if 0
+ i = sqlite3_config(SQLITE_CONFIG_MMAP_SIZE,98222080);
+ if (i != SQLITE_OK) {
+ exit(3);
+ }
+#endif
+
+ /* package is first arg */
+ char *pkgstr;
+ char *pkgid;
+ pkgstr = av[optind++];
+ pkgid = zpm_findpkg(&zpm, pkgstr, NULL);
+ /* TODO if not found, error */
+ if (!pkgid) {
+ die("package %s not found\n", pkgstr);
+ }
+
+ char package[128];
+ char version[64];
+ int release;
+
+ zpm_parse_package(pkgid, package, version, &release);
+ opt.package = package;
+ opt.version = version;
+ opt.release = release;
+
+ if (opt.verbose) {
+ printf("adding to %s %s\n", dbfile, pkgid);
+ }
+ fflush(stdout);
+
+ //zpm_begin(&zpm);
+
+ struct zpm_file file;
+ for (i=optind; av[i]; i++) {
+ if (!stat_file(&file, av[i], &opt)) {
+ die("can't stat %s: %s", av[i], strerror(errno));
+ }
+ file.data = av[i];
+ if (add_file(&zpm, &file, &opt)) {
+ free_file(&file);
+ } else {
+ die("error: %s", zpm.errmsg);
+ }
+ }
+
+ if (opt.complete) {
+ zpm_db_run(&zpm, "update packages set build_time = %d where package = %Q and version = %Q and release = %d", time(NULL), package, version, release);
+ zpm_package_hash(&zpm, pkgid, hash);
+ zpm_package_sethash(&zpm, pkgid, hash);
+ } else if (!opt.noclear) {
+ zpm_db_run(&zpm, "update packages set build_time = null, hash = null where package = %Q and version = %Q and release = %d", package, version, release);
+ }
+ /* TODO error check */
+
+ //zpm_commit(&zpm);
+ zpm_close(&zpm);
+ return 0;
+}