+#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;
+}