]> pd.if.org Git - zpackage/commitdiff
add support for notes
authorNathan Wagner <nw@hydaspes.if.org>
Fri, 19 Oct 2018 08:15:46 +0000 (08:15 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Sat, 3 Nov 2018 12:39:52 +0000 (12:39 +0000)
coalesce devminor and devmajor into device

.gitignore
Makefile
db.sql
lib/dbquery.c
lib/integ.c
lib/notes.c [new file with mode: 0644]
lib/seterror.c [new file with mode: 0644]
zpm-note [deleted file]
zpm-syncfs.c
zpm.h

index 70122a687e1d2dbcc949582ed212f5c697589b2c..0290ec3498cd0b20eede639a5e9281591368b04e 100644 (file)
@@ -4,6 +4,7 @@
 uncompress
 zpm-addfile
 zpm-foreach-path
+zpm-note
 zpm-vercmp
 zpm-verify
 zpm-extract
index fb88665abf683baf99571d03a6bb3fe31168119d..1fb6e6ce1e90316493d80d7cea8d16392ac4e469 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ lib/jsw/jsw_rbtree.c
 JSWOBJ=$(JSWSRC:%.c=%.o)
 LIBZPMSRC=sha256.c db.c compress.c uncompress.c zpm.c zpm_hash.c \
          foreach_path.c vercmp.c findpkg.c quote.c dbquery.c script_hash.c \
-         parse.c integ.c
+         parse.c integ.c seterror.c notes.c
 
 LIBZPMOBJ=$(addprefix lib/, $(LIBZPMSRC:%.c=%.o))
 
@@ -29,7 +29,7 @@ curdir=$(shell pwd)
 ZPKGBIN=zpm-addfile zpm-extract zpm-init zpm-vercmp zpm-stat zpm-hash \
        zpm-findpkg zpm-shell zpm-soneed zpm-foreach-path zpm-parse \
        zpm-runscript zpm-soname zpm-syncfs zpm-packagehash zpm-verify \
-       zpm-elftype zpm-quote
+       zpm-elftype zpm-quote zpm-note
 
 SCRIPTS=zpm zpm-install zpm-merge zpm-list zpm-preserve zpm-test zpm-log \
        zpm-contents zpm-uninstall zpm-pathmod zpm-rmpackage zpm-newpackage \
@@ -142,6 +142,9 @@ zpm-foreach-path: zpm-foreach-path.o libzpm.a sqlite/sqlite3.h
 zpm-findpkg: zpm-findpkg.o libzpm.a
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf
 
+zpm-note: zpm-note.o libzpm.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf
+
 zpm-syncfs: zpm-syncfs.o libzpm.a libelf.a
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf -lm
 
diff --git a/db.sql b/db.sql
index be71bde3428047a6368f6e6ed611ab0f7d12b867..020f228d658dcb0f464e92afef8e2de0ab04e910 100644 (file)
--- a/db.sql
+++ b/db.sql
@@ -123,6 +123,7 @@ create table packagefiles (
        uid     integer, -- numeric uid, generally ignored
        gid     integer, -- numeric gid, generally ignored
        configuration integer not null default 0, -- boolean if config file
+       confhash text, -- last hash on disk
        filetype varchar not null default 'r',
        -- r regular file
        -- d directory
@@ -132,10 +133,9 @@ create table packagefiles (
        -- b block special -- not supported
        -- c and b device special files add dev number column
        -- p fifos (i.e. pipe) -- not supported
+       -- s unix domain socket -- not supported
        target  text, -- link target for links
-       -- device file dev numbers
-       devmajor        integer,
-       devminor        integer,
+       device  integer, -- device file dev_t
        hash    text, -- null if not a regular file
        mtime   integer, -- seconds since epoch, finer resolution not needed
        primary key (package,version,release,path),
@@ -147,8 +147,8 @@ create table packagefiles (
        check (target is null or length(target) between 1 and 4095),
        check (hash is null or length(hash) between 1 and 1024),
        check (not (filetype = 'r' and hash is null)),
-       check (not (filetype = 'c' and (devmajor is null or devminor is null))),
-       check (not (filetype = 'b' and (devmajor is null or devminor is null))),
+       check (not (filetype = 'c' and device is null)),
+       check (not (filetype = 'b' and device is null)),
        check (filetype in ('r','d','l','h','c','b','p')),
        check(length(username) between 1 and 256),
        check(length(groupname) between 1 and 256),
@@ -183,8 +183,7 @@ begin
        configuration = NEW.configuration,
        filetype = NEW.filetype,
        target = NEW.target,
-       devmajor = NEW.devmajor,
-       devminor = NEW.devminor,
+       device = NEW.device,
        hash = NEW.hash,
        mtime = NEW.mtime
        where package = OLD.package
@@ -368,6 +367,16 @@ create table zpmlog (
        info    text -- human readable
 );
 
+create table notes (
+       id      integer primary key, -- rowid alias
+       ts      text default (strftime('%Y-%m-%d %H:%M:%f', 'now')),
+       note    text not null,
+       pkgid   text, -- package
+       path    text, -- file path involved
+       file    text, -- hash of file
+       ack     integer default 0
+);
+
 create table history (
        ts      integer, -- again, probably needs timestamp sub second
        cmd     text,
@@ -473,7 +482,7 @@ syncstatus as (
 newfiles as (
        select distinct
                path,username,uid,groupname,gid,mode,filetype,mtime,hash,
-       target,devminor,devmajor
+       configuration,target,device, null as ohash
        from syncstatus SS
        where path not in (select path from syncstatus where
                rstatus in ('installed', 'updating', 'removing')
@@ -486,7 +495,8 @@ modified as (
        SS.path, 
        SS.username,
                SS.uid, SS.groupname, SS.gid, SS.mode,
-       SS.filetype, SS.mtime, SS.hash, SS.target, SS.devminor, SS.devmajor
+       SS.filetype, SS.mtime, SS.hash,SS.configuration, SS.target, SS.device,
+       null as ohash
        from syncstatus SS
        join syncstatus OS
        on SS.path = OS.path and SS.pkgid is not OS.pkgid
@@ -507,7 +517,7 @@ needed as (
 preserve as (
        select distinct
                path,username,uid,groupname,gid,mode,filetype,mtime,hash,
-       target,devminor,devmajor
+       configuration,target,device, null as ohash
        from syncstatus SS
        where path in (select library from needed)
        and SS.rstatus in ('removing', 'removed')
@@ -516,7 +526,7 @@ preserve as (
 remove as (
        select distinct
                path,username,uid,groupname,gid,mode,filetype,mtime,hash,
-       target,devminor,devmajor
+       configuration,target,device, null as ohash
        from syncstatus SS
        where path not in (
                select path from syncstatus where
@@ -529,7 +539,7 @@ remove as (
 expired as (
        select distinct
                path,username,uid,groupname,gid,mode,filetype,mtime,hash,
-       target,devminor,devmajor
+       configuration,target,device, null as ohash
        from syncstatus BASE
        where hash in (select file from elflibraries where file is not null)
        and path not in (select path from preserve)
index ab03984697b2f255fc88aaa9ebc9d0c0a8d78ba5..cba95378ae2a258c8351b9f3ac07191bc46ce337 100644 (file)
@@ -62,6 +62,26 @@ sqlite3_stmt *zpm_dbquery(struct zpm *zpm, char *query, ...) {
        return st;
 }
 
+void zpm_db_run(struct zpm *zpm, char *query, ...) {
+       sqlite3_stmt *st;
+       va_list args;
+       int rv;
+
+       va_start(args, query);
+       st = zpm_dbqueryv(zpm, query, args);
+       va_end(args);
+
+       rv = sqlite3_step(st);
+
+       if (rv != SQLITE_DONE) {
+               zpm->error = 1;
+               zpm_seterror(zpm, "db error: %s", sqlite3_errstr(rv));
+       }
+
+       sqlite3_finalize(st);
+       return ;
+}
+
 char *zpm_db_string(struct zpm *zpm, char *query, ...) {
        sqlite3_stmt *st;
        va_list args;
@@ -84,3 +104,24 @@ char *zpm_db_string(struct zpm *zpm, char *query, ...) {
        sqlite3_finalize(st);
        return result;
 }
+
+int zpm_db_int(struct zpm *zpm, char *query, ...) {
+       sqlite3_stmt *st;
+       va_list args;
+       int rv;
+       int result = 0;
+
+       va_start(args, query);
+       st = zpm_dbqueryv(zpm, query, args);
+       va_end(args);
+
+       rv = sqlite3_step(st);
+
+       if (rv == SQLITE_ROW) {
+               result = sqlite3_column_int(st, 0);
+       }
+       /* TODO set error if it's not SQLITE_ROW */
+
+       sqlite3_finalize(st);
+       return result;
+}
index 6abfcf340cff7087ea82a75c9e83cffd215fa130..81667eae77cee6ebd721fabd5d93b1d555a6217a 100644 (file)
@@ -155,7 +155,7 @@ int zpm_package_hash(struct zpm *zpm, char *pkgid, char *hash) {
        /* hash package files */
 
        sql = sqlite3_mprintf("select path, mode, username, groupname, configuration, "
-               "filetype, target, devmajor, devminor, mtime, hash "
+               "filetype, target, device, mtime, hash "
                "from packagefiles_pkgid where pkgid = %Q order by path",
                pkgid);
        hash_query(zpm, sql, &d);
diff --git a/lib/notes.c b/lib/notes.c
new file mode 100644 (file)
index 0000000..9622bae
--- /dev/null
@@ -0,0 +1,180 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sqlite3.h"
+#include "zpm.h"
+
+void zpm_note_ack(struct zpm *zpm, int64_t note) {
+       char *in = "update notes set ack = 1 where id = %" PRId64;
+       zpm_db_run(zpm, in, note);
+}
+
+void zpm_note_unack(struct zpm *zpm, int64_t note) {
+       char *in = "update notes set ack = 0 where id = %" PRId64;
+       zpm_db_run(zpm, in, note);
+}
+
+void zpm_note_del(struct zpm *zpm, int64_t note) {
+       char *in = "delete from notes where id = %" PRId64;
+       zpm_db_run(zpm, in, note);
+}
+
+static char *colstring(sqlite3_stmt *s, int col) {
+       const char *val;
+       char *dup = 0;
+
+       val = (const char *)sqlite3_column_text(s, col);
+       if (val) {
+               dup = strdup(val);
+       }
+       return dup;
+}
+
+/* normally unacked only */
+/* 0x1 = next note,
+ * 0x2 = include acked
+ * 0x4 = suppress unack, implies 0x2
+ */
+/* TODO filter on pkgid/path/hash if not null */
+int64_t zpm_note(struct zpm *zpm, struct zpm_note *n, unsigned int flags) {
+       char *op = "=", *ack = " and ack = 0";
+
+       sqlite3_stmt *st;
+       int64_t id = 0;
+
+       if (flags & 0x1) {
+               op = ">";
+       }
+
+       if (flags & 0x4) {
+               ack = " and ack = 1";
+       } else if (flags & 0x2) {
+               ack = "";
+       }
+
+       st = zpm_dbquery(zpm, "select id,ts,note,pkgid,path,file,ack "
+                       "from notes where id %s %" PRId64 "%s %s",
+                       op, n->id, ack);
+
+       switch (sqlite3_step(st)) {
+               case SQLITE_DONE: /* not found */
+                       break;
+               case SQLITE_ROW:
+                       n->note = colstring(st, 2);
+                       n->pkgid = colstring(st, 3);
+                       n->path = colstring(st, 4);
+                       n->file = colstring(st, 5);
+                       n->ack = sqlite3_column_int(st, 6);
+                       n->ts = sqlite3_column_int(st, 1);
+                       n->id = sqlite3_column_int64(st, 0);
+                       id = n->id;
+                       break;
+               default: zpm->error = 1;
+                        zpm->errmsg = strdup(sqlite3_errmsg(zpm->db));
+                        break;
+       }
+
+       return id;
+}
+
+/* free any memory */
+void zpm_note_free(struct zpm_note *n) {
+       free(n->note);
+       free(n->pkgid);
+       free(n->path);
+       free(n->file);
+}
+
+int zpm_notes_available(struct zpm *zpm, int flags) {
+       int total;
+       char *sql;
+
+       if (!flags) {
+               sql = "select count(*) from notes where ack = 0";
+       } else {
+               sql = "select count(*) from notes";
+       }
+
+       total = zpm_db_int(zpm, sql);
+       return total;
+}
+
+/* get up to n notes following.  return total number of notes found */
+/* set the first note id to the id before the first one you want
+ * or to 0 to start at the beginning.  This can then
+ * be iterated up to n notes at a time by setting the id of the
+ * first one to the id found in the last one.
+ */
+int zpm_notes(struct zpm *zpm, int n, struct zpm_note *note) {
+       int64_t id;
+       int i;
+
+       for (i = 0; i < n; i++) {
+               id = zpm_note(zpm, note, 1);
+               if (!id) {
+                       break;
+               }
+               note++;
+               if (i < 0) {
+                       note->id = id;
+               }
+       }
+
+       return i;
+}
+
+int64_t zpm_note_next(struct zpm *zpm, struct zpm_note *n) {
+       return zpm_note(zpm, n, 1);
+}
+
+int64_t zpm_note_add(struct zpm *zpm, char *pkgid, char *path, char *filehash,
+               char *notefmt, ...) {
+       sqlite3_stmt *st;
+       char *note;
+       va_list ap;
+       int64_t id = 0;
+       char *in = "insert into notes (note,pkgid,path,file) values (?,?,?,?);";
+
+       if (!notefmt) {
+               return 0;
+       }
+
+       st = zpm_dbquery(zpm, in);
+
+       if (!st) {
+               zpm->error = 1;
+               zpm->errmsg = strdup(sqlite3_errmsg(zpm->db));
+               return 0;
+       }
+
+       va_start(ap, notefmt);
+       note = sqlite3_vmprintf(notefmt, ap);
+       va_end(ap);
+
+       if (!note) {
+               zpm->error = 1;
+               zpm_seterror(zpm, "can't alloc");
+       }
+
+       sqlite3_bind_text(st, 1, note, -1, SQLITE_STATIC);
+       sqlite3_bind_text(st, 2, pkgid, -1, SQLITE_STATIC);
+       sqlite3_bind_text(st, 3, path, -1, SQLITE_STATIC);
+       sqlite3_bind_text(st, 4, filehash, -1, SQLITE_STATIC);
+
+       switch (sqlite3_step(st)) {
+               case SQLITE_DONE:
+                       id = sqlite3_last_insert_rowid(zpm->db);
+                       break;
+               default: zpm->error = 1;
+                        zpm->errmsg = strdup(sqlite3_errmsg(zpm->db));
+                        break;
+       }
+
+       sqlite3_finalize(st);
+       sqlite3_free(note);
+       return id;
+}
diff --git a/lib/seterror.c b/lib/seterror.c
new file mode 100644 (file)
index 0000000..337c722
--- /dev/null
@@ -0,0 +1,31 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "zpm.h"
+
+void zpm_seterror(struct zpm *zpm, char *msgfmt, ...) {
+       char msg[1024];
+       va_list ap;
+
+       if (zpm->errmsg) {
+               free(zpm->errmsg);
+               zpm->errmsg = 0;
+       }
+
+       if (msgfmt != NULL) {
+               va_start(ap, msgfmt);
+               vsnprintf(msg, sizeof msg, msgfmt, ap);
+               va_end(ap);
+
+               msg[1023] = 0;
+               if (zpm->errmsg) {
+                       free(zpm->errmsg);
+               }
+
+               zpm->errmsg = strdup(msg);
+       }
+}
diff --git a/zpm-note b/zpm-note
deleted file mode 100755 (executable)
index f78bdd8..0000000
--- a/zpm-note
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/bin/sh
-
-# Admin notes:
-# 
-# Each note is just a file.  Stored under /var/spool/admin/notes/[open|ack]/<package>/<note>
-# 
-# note: list notes
-# note ack <note> - moves note to "acknowledged" spool
-# note list [package] show notes for a given package, or all notes
-# note delete <note> - delete a note from the system
-# note edit <package> [file] - edit or create a note
-
-# TODO
-# discard unchanged new note file
-# quiet options to suppress new note name echo
-# semi-quiet to just output the number
-# renumber? to renumber all notes/reset sequence
-# lock the temp files
-# clean up unlocked temp files
-
-: ${ZPMSPOOL:=/var/lib/admin/notes}
-
-OPEN=$ZPMSPOOL/open
-
-set -e
-
-umask 007
-
-#cd $SPOOL || { echo "can't chdir to $SPOOL" ; exit 1; }
-
-[ -z "$1" ] && set list
-
-quiet=
-
-cmd=$1
-shift
-
-findnote() {
-       file=$(find $ZPMSPOOL -type f -name "$1")
-       if [ -z "$file" ] ; then
-               [ -z "$quiet" ] || printf "no such note $1\n" 1>&2
-               exit 1
-       fi
-       echo $file
-}
-
-findopen() {
-       file=$(find $ZPMSPOOL/open -type f -name "$1")
-       if [ -z "$file" ] ; then
-               [ -z "$quiet" ] || printf "no such open note $1\n" 1>&2
-               exit 1
-       fi
-       echo $file
-}
-
-case $cmd in
-       category)
-               notefile=$(findnote $1)
-               printf "%s\n" $(basename $(dirname $notefile))
-               ;;
-       list)
-               case $1 in
-                       -a) find $ZPMSPOOL/ack -type f -printf '%P\n' | sort -n
-                               ;;
-                       ?*) if [ -d $ZPMSPOOL/open/$1 ] ; then
-                               find $ZPMSPOOL/open/$1 -type f -printf '%P\n' | sort -n
-                       else
-                               exit 1
-                       fi
-                               ;;
-                       *) find $ZPMSPOOL/open -type f -printf '%P\n' | sort -n
-                               ;;
-               esac
-               ;;
-       detail)
-               len=$(find $ZPMSPOOL/open -type f -printf "%P\n" | awk ' { if ( length > x ) { x = length } }END{ print x }')
-               find $ZPMSPOOL/open -type f -printf "%P\n" | sort -n | while read note ; do
-                       subject=$(head -1 $ZPMSPOOL/open/$note) 
-                       date=$(stat -c '%y' $ZPMSPOOL/open/$note | cut -f1 -d' ')
-                       owner=$(stat -c '%U' $ZPMSPOOL/open/$note)
-                       printf '%*s %8s %s %s\n' $len $note $owner "$date" "$subject"
-               done
-               ;;
-       ack)
-               file=$(findopen $1)
-               subfile=$(echo $file | sed -e "s|$ZPMSPOOL/open/||")
-               filedir=$(dirname $subfile)
-               mkdir -p $ZPMSPOOL/ack/$filedir
-               mv $file $ZPMSPOOL/ack/$filedir
-               ;;
-       new)
-               # new -- edit a new file for package general (or ZPMNOTEPACKAGE)
-               # new package -- edit a new note for package
-               # new package file -- use file for new package note
-               if [ $# -eq 0 ]; then set ${ZPMNOTEPACKAGE:-general}; fi
-
-               # edit a new file for the note
-               if [ $# -eq 1 ]; then
-                       tmp=$(mktemp -p $ZPMSPOOL/open)
-                       pkg=$1
-                       [ -f $ZPMSPOOL/.template ] && cp $ZPMSPOOL/.template $tmp
-                       #flock -n -E3 $tmp vim $tmp
-                       #if [ $? -eq 3 ]; then echo already editing $1; exit 1; fi
-                       ${EDITOR:-vi} $tmp
-               else
-                       # take note from file or stdin
-                       pkg=$1 ; shift
-                       mkdir -p $ZPMSPOOL/open/$pkg
-                       case $1 in
-                               '-')
-                                       tmp=$(mktemp -p $ZPMSPOOL/open)
-                                       cat - > $tmp
-                                       ;;
-                               *)
-                                       tmp=$(mktemp -p $ZPMSPOOL/open)
-                                       cp $1 $tmp
-                                       ;;
-                       esac
-               fi
-               file=$(zpm sequence notes)
-               mkdir -p $ZPMSPOOL/open/$pkg
-               mv $tmp $ZPMSPOOL/open/$pkg/$file || { rm -f $tmp; exit 1; }
-               # TODO run newhooks in $ZPMSPOOL/.hook/newnote
-               echo "$pkg/$file"
-               exit 0
-               ;;
-       edit)
-               file=$(findnote $1)
-               flock -n -E3 $file vim $file
-               if [ $? -eq 3 ]; then echo already editing $1; exit 1; fi
-               ;;
-       resolve)
-               file=$(findnote $1)
-               echo $file
-               ;;
-       remove)
-               file=$(findnote $1)
-               rm $file
-               ;;
-       show)
-               file=$(findnote $1)
-               ${PAGER:-less} $file
-               ;;
-       *)
-               echo '$0: unknown command ' $cmd 1>&2
-               exit 1
-esac
index 935e71509f2591ae20cc3a9801c22548c0a53ae1..d2847cf2387f295b9e2d393e94dcd8e29189cbca 100644 (file)
@@ -40,11 +40,12 @@ struct nitem {
        gid_t gid;
        char *dest;
        char *path;
-       char *hash;
+       char *hash, *ohash;
        char *target;
        time_t mtime;
        mode_t mode;
        int ftype;
+       int configuration;
        struct timespec times[2];
 };
 
@@ -285,6 +286,7 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) {
        struct config *conf = f;
        char *dest;
        struct stat st;
+       int flags = 0;
 
        dest = COL("dest");
        if (!dest) return seterror(conf,"no file dest");
@@ -306,21 +308,22 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) {
        }
 
        if (S_ISDIR(st.st_mode)) {
-               if (conf->verbose) {
-                       fprintf(stderr, "rmdir(%s)\n", dest);
-               }
-               rmdir(dest);
-       } else if (S_ISREG(st.st_mode)) {
-               /* TODO conf to import before removal */
-               if (conf->verbose) {
-                       fprintf(stderr, "unlink(%s)\n", dest);
-               }
-               if (unlink(dest) == -1) {
-                       return seterror(conf, "can't unlink");
-               }
-       } else {
-               if (unlink(dest) == -1) {
-                       return seterror(conf, "can't unlink");
+               flags = AT_REMOVEDIR;
+       }
+       /* TODO check that expected filetype matches actual filetype */
+
+       if (conf->verbose) {
+               fprintf(stderr, "%s(%s)\n", flags ? "rmdir" : "unlink", dest);
+       }
+
+       errno = 0;
+
+       if (unlinkat(AT_FDCWD, dest, flags) == -1) {
+               switch (errno) {
+                       case ENOENT:
+                               break;
+                       default:
+                               return seterror(conf, "can't unlink");
                }
        }
        
@@ -339,6 +342,7 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) {
 #define D_GID 0x80
 #define D_MODE 0x100
 #define D_MTIME 0x200
+#define D_OHASH 0x400
 #define D_ERROR 0x1000
 #define D_STATERROR 0x2000
 #define D_RLERROR 0x4000
@@ -376,6 +380,9 @@ static unsigned int file_compare(struct nitem *n, struct stat *st) {
                        if (strcmp(n->hash, ehash) != 0) {
                                diff |= D_HASH;
                        }
+                       if (n->ohash && strcmp(n->ohash, ehash) != 0) {
+                               diff |= D_OHASH;
+                       }
                }
                if (n->hash && etype == S_IFLNK && stat_type == S_IFLNK) {
                        lsize = readlink(n->dest, link, sizeof link);
@@ -460,6 +467,13 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols,
 
        n->mode = strtoul(val, NULL, 8);
 
+       val = COL("configuration");
+       if (!val) {
+               seterror(conf, "can't determine config status");
+               return 0;
+       }
+       n->configuration = strtoul(val, NULL, 10);
+
        val = COL("filetype");
        if (!val || strlen(val) == 0) {
                seterror(conf, "can't determine file type");
@@ -467,6 +481,8 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols,
        }
        n->ftype = *val;
 
+       n->ohash = COL("hash");
+
        if (n->ftype == 'r') {
                n->hash = COL("hash");
                if (!n->hash) {
@@ -601,6 +617,12 @@ static int set_md(struct config *conf, struct nitem *item) {
 /* flags: 1 = set md, 2 = create leading dirs, 4 = unlink existing file,
  * 8 = rmdir existing dir, 16 = return true/false
  */
+#define INS_MD 0x1
+#define INS_CLD 0x2
+#define INS_UNLINK 0x4
+#define INS_RMDIR 0x8
+#define INS_RTF 0x10
+#define INS_ZPMNEW 0x20
 static int install(struct config *conf, struct nitem *item, unsigned int flags) {
        int rv = 1;
        struct zpm *source;
@@ -686,6 +708,7 @@ static int install_files(void *f, int ncols, char **vals, char **cols) {
        struct nitem nitem;
        struct stat existing;
        int update = 0;
+       char dest[4096];
 
        /* TODO put the result row in a hash table.  May not actually
         * be faster
@@ -747,6 +770,22 @@ static int install_files(void *f, int ncols, char **vals, char **cols) {
                        return install(conf, &nitem, 3);
                }
 
+               if (nitem.configuration) {
+                       /* ohash == nhash, not an update */
+                       /* fhash == ohash, just update */
+                       /* fhash != ohash, install as dest.zpmnew, warn */
+                       /* TODO handle replacing config file
+                        * with config directory */
+                       if (diffs & D_OHASH) {
+                               if (strlen(nitem.dest) > sizeof dest - 8) {
+                                       return seterror(conf,"config file path too long for install as %s.zpmnew", nitem.dest);
+                               }
+                               fprintf(stderr, "installing as .zpmnew\n");
+                               sprintf(dest, "%s.zpmnew", nitem.dest);
+                               nitem.dest = dest;
+                       }
+               }
+
                /* file exists in filesystem */
                if (sametype) {
                        if (mdsame && hashsame) {
diff --git a/zpm.h b/zpm.h
index 64e5ac63ec9c49437cc2b4da2310111aa724d85c..c7144649940bfd6bbf8008732a480d4f646a85ef 100644 (file)
--- a/zpm.h
+++ b/zpm.h
@@ -176,6 +176,7 @@ int zpm_foreach_path(struct zpm *zpm, char *pkgid, char *where,
 int (*callback)(void *f, int ncols, char **vals, char **cols),
 void *data, char **errmsg);
 
+
 int zpm_script_hash(struct zpm *zpm, char *pkgstr, char *phase, char *hash);
 int zpm_package_hash(struct zpm *zpm, char *pkgid, char *hash);
 int zpm_package_sethash(struct zpm *zpm, char *pkgid, char *hash);
@@ -183,7 +184,32 @@ int zpm_package_sethash(struct zpm *zpm, char *pkgid, char *hash);
 sqlite3_stmt *zpm_dbqueryv(struct zpm *zpm, char *query, va_list args);
 sqlite3_stmt *zpm_dbquery(struct zpm *zpm, char *query, ...);
 char *zpm_db_string(struct zpm *zpm, char *query, ...);
+int zpm_db_int(struct zpm *zpm, char *query, ...);
+void zpm_db_run(struct zpm *zpm, char *query, ...);
+void zpm_seterror(struct zpm *zpm, char *msgfmt, ...);
 
 struct zpm *zpm_clearmem(struct zpm *zpm);
 
+struct zpm_note {
+       int64_t id;
+       time_t ts; /* or timespec */
+       char *note;
+       char *pkgid;
+       char *path;
+       char *file;
+       char *hash;
+       int ack;
+};
+
+void zpm_note_ack(struct zpm *zpm, int64_t note);
+void zpm_note_unack(struct zpm *zpm, int64_t note);
+void zpm_note_del(struct zpm *zpm, int64_t note);
+int64_t zpm_note(struct zpm *zpm, struct zpm_note *n, unsigned int flags);
+void zpm_note_free(struct zpm_note *n);
+int zpm_notes(struct zpm *zpm, int n, struct zpm_note *note);
+int64_t zpm_note_next(struct zpm *zpm, struct zpm_note *n);
+int64_t zpm_note_add(struct zpm *zpm, char *pkgid, char *path, char *filehash,
+               char *notefmt, ...);
+int zpm_notes_available(struct zpm *zpm, int flags);
+
 #endif