From d697639ca6483bdee0ab043f85a2266fe7c62956 Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Sun, 9 Dec 2018 15:11:08 +0000 Subject: [PATCH] rework zpm-note zpm-note now takes a range of note ids with -n, and both delete and ack will work on that range. The show output can take a -j option to output json, and the date of the note was added to the list output. --- Makefile | 2 +- doc/zpm-note.8 | 86 ++++++++++++++++++++++++++ lib/notes.c | 5 +- schema/main.sql | 2 +- zpm-note.c | 160 ++++++++++++++++++++++++++++++++++++++++++++---- zpm.h | 2 +- 6 files changed, 240 insertions(+), 17 deletions(-) create mode 100644 doc/zpm-note.8 diff --git a/Makefile b/Makefile index 85a5d13..dd84d52 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ ZPKGBIN=zpm-addfile zpm-extract zpm-init zpm-vercmp zpm-stat zpm-hash \ SCRIPTS=zpm zpm-install zpm-merge zpm-list zpm-preserve zpm-test zpm-log \ zpm-contents zpm-uninstall zpm-pathmod zpm-rmpackage zpm-newpackage \ zpm-pkg zpm-add zpm-pkgfile zpm-gc zpm-repo zpm-update zpm-confgit -MANPAGES=doc/zpm.8 $(addprefix doc/zpm-, list.8 contents.8 hash.8 quote.8 pathmod.8 ) +MANPAGES=doc/zpm.8 $(addprefix doc/zpm-, list.8 contents.8 hash.8 quote.8 pathmod.8 note.8) COMPILED=$(ZPKGBIN) PROGRAMS=$(SCRIPTS) $(COMPILED) diff --git a/doc/zpm-note.8 b/doc/zpm-note.8 new file mode 100644 index 0000000..29650db --- /dev/null +++ b/doc/zpm-note.8 @@ -0,0 +1,86 @@ +.TH zpm-note 8 2018-12-09 "ZPM 0.4" +.SH NAME +zpm-note \- manage zpm admin notes +.SH SYNOPSIS +.B zpm note +[ +.BI -f " pkgfile" +] +[ +.B -ljseDAa +] +.RI [ package ...] +.RB [ : +.IR glob ...\fR] +.SH DESCRIPTION +\fBzpm-note\fR lists, shows, creates, or deletes admin notes. +.SH OPTIONS +.TP +\-f +specify the package file to find packages in +.TP +.BI \-n " range" +specify the range of notes to be operated on in show, list, ack, and delete +modes. The \fIrange\fR can be in the form of +\fIstart\fB-\fIend\fR, +\fB-\fIend\fR, +\fIstart\fB-\fR, or +\fin\fR, where \fIn\fR is a single note, and the other forms +specify an inclusive range, if either the start or the end is not +given in a range, it is taken to be unbounded. +.TP +\-a +include acknowledged notes in the range +\-u +suppress unacknowledged notes, i.e. just select acknowledged notes +in the range. Implies \-a. +.TP +\-l +list a range of notes. The output will be constrained to fit on one +line. Only as many characters from the first line of the note as will fit +will be printed. +.TP +\-s +show a range of notes in full format +.TP +\-j +use json output if in show (-s) mode +.TP +\-D +delete a range of notes +.TP +\-A +acknowledge a range of notes +.TP +\-e +when adding a note, print the resulting note to stdout as if the new note were +to be listed with -l. By default, only the new note number is printed to +stdout. +.TP +\-m \fimessage\fR +specify the note message +.TP +\-a +operate on all notes in the range. by default, only un-acknowledged notes +are operated on. +.TP +-F \fIfile\fR +Take the note message from the given file. If the file is "-", the +message is taken from standard input. +.SH EXAMPLES +.TP +zpm note -m 'foo' +create a new note named foo +.TP +zpm note -l -n 1-10 +list unacknowledged notes from 1-10 +.SH EXIT STATUS +0 on success non zero on failure +.SH FILES +/var/lib/zpm/local.db +.SH ENVIRONMENT +ZPMDB +.SH AUTHOR +Nathan Wagner +.SH SEE ALSO +zpm(8) diff --git a/lib/notes.c b/lib/notes.c index b337fd4..b29cbc7 100644 --- a/lib/notes.c +++ b/lib/notes.c @@ -69,7 +69,7 @@ int64_t zpm_note(struct zpm *zpm, struct zpm_note *n, unsigned int flags) { 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->ts = colstring(st, 1); n->id = sqlite3_column_int64(st, 0); id = n->id; break; @@ -87,7 +87,8 @@ void zpm_note_free(struct zpm_note *n) { free(n->pkgid); free(n->path); free(n->file); - n->note = n->pkgid = n->path = n->file = 0; + free(n->ts); + n->note = n->ts = n->pkgid = n->path = n->file = 0; } int zpm_notes_available(struct zpm *zpm, int flags) { diff --git a/schema/main.sql b/schema/main.sql index c9c79a2..b067550 100644 --- a/schema/main.sql +++ b/schema/main.sql @@ -372,7 +372,7 @@ create table zpmlog ( create table notes ( id integer primary key, -- rowid alias - ts text default (strftime('%Y-%m-%d %H:%M:%f', 'now')), + ts text default (strftime('%Y-%m-%dT%H:%M:%f', 'now')), note text not null, pkgid text, -- package path text, -- file path involved diff --git a/zpm-note.c b/zpm-note.c index 927f30a..c94e37b 100644 --- a/zpm-note.c +++ b/zpm-note.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -12,8 +13,76 @@ void usage(void) { fprintf(stderr, "zpm-note [-I] [-s ...] [-S ] [package]\n"); } +void show_note(struct zpm_note *n) { + printf("Note %" PRId64 " %s\n", n->id, n->ts); + if (n->pkgid) { + printf("Package: %s\n", n->pkgid); + } + if (n->file) { + printf("Hash: %s\n", n->file); + } + + printf("%s\n", n->note); +} + +void jstring(char *field, char *value, int indent, int final) { + while (indent--) { + putchar('\t'); + } + printf("\"%s\": ", field); + if (value) { + printf("\"%s\": ", value); + } else { + printf("null"); + } + if (!final) { + printf(","); + } + putchar('\n'); +} + +void show_json(struct zpm_note *n) { + size_t len = 0; + + if (n->pkgid) { + len = strcspn(n->note, "\n\r"); + } + + printf("{\n\t\"id\": %" PRId64 ",\n", n->id); + jstring("package", n->pkgid, 1, 0); + jstring("hash", n->file, 1, 0); + + if (len) { + printf("\t\"subject\": \"%.*s\",\n", (int)len, n->note); + } else { + printf("\t\"subject\": null,\n"); + } + jstring("note", n->note, 1, 1); + printf("}\n"); +} + void print_note(struct zpm_note *n) { - printf("%" PRId64 " %s %s\n", n->id, n->pkgid ? n->pkgid : "-", n->note); + size_t len; + size_t space = 66; + uint64_t id = n->id; + + if (n->pkgid) { + space -= strlen(n->pkgid); + } else { + space -= 1; + } + + do { + space--; + } while (id /= 10); + + len = strcspn(n->note, "\n\r"); + if (len > space) { + len = space; + } + + printf("%" PRId64 " %.10s %s %.*s\n", n->id, n->ts, + n->pkgid ? n->pkgid : "-", (int)len, n->note); } int main(int ac, char **av){ @@ -21,6 +90,8 @@ int main(int ac, char **av){ struct zpm pkg; char *dbfile; int fail, echo = 0; + int64_t start = 0, end = INT64_MAX; + char *range = 0; int select = 1; @@ -28,6 +99,7 @@ int main(int ac, char **av){ n.pkgid = 0; n.hash = 0; n.path = 0; + n.ts = 0; dbfile = getenv("ZPMDB"); if (!dbfile) { @@ -40,7 +112,6 @@ int main(int ac, char **av){ * -n note id * -D delete * -H hash - * -a all * -m note message, otherwise EDITOR/vi to edit a text file * -e echo note after creating * zpm note -p pkgid -P path -H hash -m 'foo' @@ -48,20 +119,25 @@ int main(int ac, char **av){ * zpm note -D * zpm note -A */ - int list = 0, delete = 0, ack = 0; - while ((opt = getopt(ac, av, "f:p:P:H:m:n:lDAae")) != -1) { + int list = 0, delete = 0, ack = 0, show = 0, json = 0; + char *notefrom = 0; + while ((opt = getopt(ac, av, "f:p:P:H:m:n:lDAauesjF:")) != -1) { switch (opt) { case 'f': dbfile = optarg; break; case 'p': n.pkgid = optarg; break; case 'P': n.path = optarg; break; case 'H': n.hash = optarg; break; case 'm': n.note = optarg; break; - case 'n': n.id = strtoll(optarg, NULL, 10); break; + case 'F': notefrom = optarg; break; + case 'n': range = optarg; break; case 'l': list = 1; break; + case 'j': json = 1; break; + case 's': show = 1; break; case 'e': echo = 1; break; case 'D': delete = 1; break; case 'A': ack = 1; break; - case 'a': select = 3; break; + case 'a': select |= 2; break; + case 'u': select |= 6; break; default: usage(); @@ -69,6 +145,29 @@ int main(int ac, char **av){ break; } } +/* 0x1 = next note, + * 0x2 = include acked + * 0x4 = suppress unack, implies 0x2 + */ + + if (range) { + if (*range == '-') { + start = 0; + end = strtoll(range+1, NULL, 10); + } else if (isdigit(*range)) { + char *next; + start = strtoll(range, &next, 10); + if (next && *next == '-') { + end = -1; + next++; + if (isdigit(*next)) { + end = strtoll(next, NULL, 10); + } + } else { + end = start; + } + } + } if (!dbfile) { fprintf(stderr, "must specify db\n"); @@ -77,15 +176,55 @@ int main(int ac, char **av){ /* given a package name, get the packages */ /* no package name, get all */ + char *filenote = 0; + if (notefrom) { + size_t in = 0; + ssize_t cread = 0; + FILE *input; + + if (strcmp(notefrom, "-")) { + input = fopen(notefrom, "r"); + } else { + input = stdin; + } + if (!input) { + perror("can't read input file"); + exit(EXIT_FAILURE); + } + cread = getdelim(&filenote, &in, 0, input); + if (cread == -1) { + perror("error reading file"); + exit(EXIT_FAILURE); + } + n.note = filenote; + } if (zpm_open(&pkg, dbfile)) { - if (list) { + if (list || show || delete || ack) { + n.id = start ? start - 1 : start; while (zpm_note(&pkg, &n, select)) { - print_note(&n); + if (n.id > end) { + break; + } + if (delete) { + zpm_note_del(&pkg, n.id); + } else if (ack) { + zpm_note_ack(&pkg, n.id); + } else if (show) { + if (json) { + show_json(&n); + } else { + show_note(&n); + } + } else { + print_note(&n); + } zpm_note_free(&n); } + zpm_note_free(&n); } else if (n.note) { n.id = zpm_note_add(&pkg, n.pkgid, n.path, n.hash, "%s", n.note); + zpm_note(&pkg, &n, 2); if (n.id) { if (echo) { print_note(&n); @@ -96,15 +235,12 @@ int main(int ac, char **av){ fprintf(stderr, "unable to create note\n"); pkg.error = 1; } - } else if (delete) { - zpm_note_del(&pkg, n.id); - } else if (ack) { - zpm_note_ack(&pkg, n.id); } } else { fprintf(stderr, "unable to open %s\n", dbfile); } fail = pkg.error; zpm_close(&pkg); + free(filenote); return fail ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/zpm.h b/zpm.h index 9be5f60..ea9cd6c 100644 --- a/zpm.h +++ b/zpm.h @@ -201,7 +201,7 @@ struct zpm *zpm_clearmem(struct zpm *zpm); struct zpm_note { int64_t id; - time_t ts; /* or timespec */ + char *ts; /* applications can parse it if they need to */ char *note; char *pkgid; char *path; -- 2.40.0