+ return success;
+ }
+
+ switch (item->ftype) {
+ case 'd': rv = mkdir(item->dest, item->mode);
+ break;
+ case 'l': rv = symlink(item->target, item->dest);
+ break;
+ default: /* error */
+ break;
+ }
+
+ if (rv == -1) {
+ setsyserr(conf, "installing %s failed", item->dest);
+ return failure;
+ }
+
+ if (setmd) {
+ return set_md(conf, item) == 0 ? success : failure;
+ }
+
+ return success;
+}
+
+static int install_files(void *f, int ncols, char **vals, char **cols) {
+ struct config *conf = f;
+ struct nitem nitem;
+ struct stat existing;
+ int update = 0;
+
+ /* TODO put the result row in a hash table. May not actually
+ * be faster
+ */
+ if (!read_item(conf, ncols, vals, cols, &nitem)) {
+ fprintf(stderr, "can't read item\n");
+ return conf->errabort;
+ }
+
+ if (conf->verbose) {
+ fprintf(stderr, "%d '%c' %s\n", nitem.op, nitem.ftype,
+ nitem.dest);
+ }
+
+ if (conf->dryrun) {
+ printf("new %c%o %d:%d %s -> %s\n", nitem.ftype, nitem.mode,
+ nitem.uid, nitem.gid, nitem.path, nitem.dest);
+ return 0;
+ }
+
+ unsigned int diffs = file_compare(&nitem, &existing);
+ if (diffs >= D_ERROR) {
+ return seterror(conf, "can't check %s", nitem.dest);
+ }
+
+ if (conf->verbose) {
+ fprintf(stderr, "diffs = %u\n", diffs);
+ }
+
+ /* updates:
+ * exist & same type & md same & hash same: do nothing, but warn bug
+ * exist & same type & md diff & hash same: fix md
+ * exist & same type & md same & hash diff: replace
+ * exist & same type & md diff & hash diff: replace & fix
+ * no exist: install and warn
+ * dir & not dir : remove, mkdir
+ * not dir & not dir & diff type: remove, install
+ * not dir & dir : remove dir if empty, error if not empty, install
+ *
+ * installs:
+ * no exist: create leading dirs, install
+ *
+ * exist & same type & md same & hash same & accept or over: do nothing
+ * exist & same & md diff or hash diff & overwrite : update
+ * exist & same & md diff or hash diff & accept : error, can't accept
+ * exist & same & md diff or hash diff & not accept : error
+ *
+ * exist & different type & not overwrite : error
+ * not dir & not dir & overwrite : remove and install
+ * not dir & dir & overwrite: remove empty or error, install
+ * dir & dir & overwrite: fix md
+ * dir & not dir & overwrite: remove and mkdir
+ */
+ int exist = (!(diffs & D_NOEXIST));
+ int sametype = (!(diffs & D_TYPE));
+ int mdsame = (!(diffs & D_MD));
+ int hashsame = (!(diffs & D_HASH));
+ int isdir = (diffs & D_ISDIR);
+ int eisdir = (diffs & D_EISDIR);
+ int accept = conf->absorb;
+ int overwrite = conf->overwrite;
+ int installing = (nitem.op == OP_NEW);
+ update = (nitem.op == OP_UPDATE);
+
+ if (update) {
+ if (!exist) {
+ /* warn, it should exist */
+ fprintf(stderr, "%s missing, installing", nitem.dest);
+ return install(conf, &nitem, 3);