Line data Source code
1 : /*-
2 : * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org>
3 : * All rights reserved.
4 : *
5 : * Redistribution and use in source and binary forms, with or without
6 : * modification, are permitted provided that the following conditions
7 : * are met:
8 : * 1. Redistributions of source code must retain the above copyright
9 : * notice, this list of conditions and the following disclaimer
10 : * in this position and unchanged.
11 : * 2. Redistributions in binary form must reproduce the above copyright
12 : * notice, this list of conditions and the following disclaimer in the
13 : * documentation and/or other materials provided with the distribution.
14 : *
15 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 : */
26 :
27 : #ifdef HAVE_CONFIG_H
28 : #include "pkg_config.h"
29 : #endif
30 :
31 : #include <sys/types.h>
32 : #include <sys/sbuf.h>
33 :
34 : #include <err.h>
35 : #include <getopt.h>
36 : #include <stdio.h>
37 : #include <string.h>
38 : #include <sysexits.h>
39 : #include <unistd.h>
40 :
41 : #include <pkg.h>
42 :
43 : #include "pkgcli.h"
44 :
45 : enum action {
46 : NONE,
47 : ADD,
48 : MODIFY,
49 : DELETE,
50 : SHOW,
51 : };
52 :
53 : void
54 0 : usage_annotate(void)
55 : {
56 0 : fprintf(stderr,
57 : "Usage: pkg annotate [-Cgiqxy] [-A|M] <pkg-name> <tag> [<value>]\n");
58 0 : fprintf(stderr,
59 : " pkg annotate [-Cgiqxy] [-S|D] <pkg-name> <tag>\n");
60 0 : fprintf(stderr,
61 : " pkg annotate [-qy] -a [-A|M] <tag> [<value>]\n");
62 0 : fprintf(stderr,
63 : " pkg annotate [-qy] -a [-S|D] <tag>\n");
64 0 : fprintf(stderr,
65 : "For more information see 'pkg help annotate'.\n");
66 0 : }
67 :
68 : static int
69 4 : do_add(struct pkgdb *db, struct pkg *pkg, const char *tag, const char *value)
70 : {
71 4 : int ret = EPKG_OK;
72 :
73 :
74 4 : if (yes || query_tty_yesno(false, "%n-%v: Add annotation tagged: %S with "
75 : "value: %S? ", pkg, pkg, tag, value)) {
76 :
77 4 : ret = pkgdb_add_annotation(db, pkg, tag, value);
78 4 : if (ret == EPKG_OK) {
79 4 : if (!quiet)
80 4 : pkg_printf("%n-%v: added annotation tagged:"
81 : " %S\n", pkg, pkg, tag);
82 0 : } else if (ret == EPKG_WARN) {
83 0 : if (!quiet) {
84 0 : pkg_warnx("%n-%v: Cannot add annotation tagged:"
85 : " %S\n", pkg, pkg, tag);
86 : }
87 : } else {
88 0 : pkg_warnx("%n-%v: Failed to add annotation tagged:"
89 : " %S\n", pkg, pkg, tag);
90 : }
91 : }
92 4 : return (ret);
93 : }
94 :
95 : static int
96 1 : do_modify(struct pkgdb *db, struct pkg *pkg, const char *tag, const char *value)
97 : {
98 1 : int ret = EPKG_OK;
99 :
100 :
101 1 : if (yes || query_tty_yesno(false, "%n-%v: Change annotation tagged: %S to "
102 : "new value: %S? ", pkg, pkg, tag, value)) {
103 1 : ret = pkgdb_modify_annotation(db, pkg, tag, value);
104 1 : if (ret == EPKG_OK || ret == EPKG_WARN) {
105 2 : if (!quiet)
106 1 : pkg_printf("%n-%v: Modified annotation "
107 : "tagged: %S\n", pkg, pkg, tag);
108 : } else {
109 0 : pkg_warnx("%n-%v: Failed to modify annotation tagged:"
110 : " %S", pkg, pkg, tag);
111 : }
112 : }
113 1 : return (ret);
114 : }
115 :
116 : static int
117 4 : do_delete(struct pkgdb *db, struct pkg *pkg, const char *tag)
118 : {
119 4 : int ret = EPKG_OK;
120 :
121 4 : if (yes || query_tty_yesno(false, "%n-%v: Delete annotation tagged: %S? ",
122 : pkg, pkg, tag)) {
123 4 : ret = pkgdb_delete_annotation(db, pkg, tag);
124 4 : if (ret == EPKG_OK) {
125 4 : if (!quiet)
126 4 : pkg_printf("%n-%v: Deleted annotation "
127 : "tagged: %S\n", pkg, pkg, tag);
128 0 : } else if (ret == EPKG_WARN) {
129 0 : if (!quiet) {
130 0 : pkg_warnx("%n-%v: Cannot delete annotation "
131 : "tagged: %S -- because there is none",
132 : pkg, pkg, tag);
133 : }
134 : } else {
135 0 : pkg_warnx("%n-%v: Failed to delete annotation tagged: %S",
136 : pkg, pkg, tag);
137 : }
138 : }
139 4 : return (ret);
140 : }
141 :
142 : static int
143 6 : do_show(struct pkg *pkg, const char *tag)
144 : {
145 : struct pkg_kv *note;
146 6 : int ret = EPKG_OK;
147 :
148 6 : ret = pkg_get(pkg, PKG_ANNOTATIONS, ¬e);
149 13 : while (note != NULL) {
150 5 : if (strcmp(tag, note->key) == 0) {
151 4 : if (quiet)
152 0 : printf("%s\n", note->value);
153 : else
154 8 : pkg_printf("%n-%v: Tag: %S Value: %S\n",
155 8 : pkg, pkg, note->key, note->value);
156 4 : return (EPKG_OK);
157 : }
158 1 : note = note->next;
159 : }
160 :
161 2 : return (ret);
162 : }
163 :
164 :
165 : static struct sbuf *
166 1 : read_input(void)
167 : {
168 : struct sbuf *input;
169 : int ch;
170 :
171 1 : input = sbuf_new_auto();
172 :
173 : for (;;) {
174 7 : ch = getc(stdin);
175 7 : if (ch == EOF) {
176 1 : if (feof(stdin))
177 1 : break;
178 0 : if (ferror(stdin))
179 0 : err(EX_NOINPUT, "Failed to read stdin");
180 : }
181 6 : sbuf_putc(input, ch);
182 6 : }
183 : #ifdef __DragonFly__
184 : sbuf_finish(input);
185 : #else
186 1 : if (sbuf_finish(input) != 0)
187 0 : err(EX_DATAERR, "Could not read value data");
188 : #endif
189 :
190 1 : return (input);
191 : }
192 :
193 : int
194 11 : exec_annotate(int argc, char **argv)
195 : {
196 11 : struct pkgdb *db = NULL;
197 11 : struct pkgdb_it *it = NULL;
198 11 : struct pkg *pkg = NULL;
199 11 : enum action action = NONE;
200 : const char *tag;
201 : const char *value;
202 : const char *pkgname;
203 11 : struct sbuf *input = NULL;
204 : int ch;
205 11 : int match = MATCH_EXACT;
206 : int retcode;
207 11 : int exitcode = EX_OK;
208 11 : int flags = 0;
209 :
210 11 : struct option longopts[] = {
211 : { "all", no_argument, NULL, 'a' },
212 : { "add", no_argument, NULL, 'A' },
213 : { "case-insensitive", no_argument, NULL, 'C' },
214 : { "delete", no_argument, NULL, 'D' },
215 : { "glob", no_argument, NULL, 'g' },
216 : { "case-insensitive", no_argument, NULL, 'i' },
217 : { "modify", no_argument, NULL, 'M' },
218 : { "quiet", no_argument, NULL, 'q' },
219 : { "show", no_argument, NULL, 'S' },
220 : { "regex", no_argument, NULL, 'x' },
221 : { "yes", no_argument, NULL, 'y' },
222 : { NULL, 0, NULL, 0 },
223 : };
224 :
225 : /* Set default case sensitivity for searching */
226 11 : pkgdb_set_case_sensitivity(
227 11 : pkg_object_bool(pkg_config_get("CASE_SENSITIVE_MATCH"))
228 : );
229 :
230 11 : while ((ch = getopt_long(argc, argv, "+aACDgiMqSxy", longopts, NULL))
231 : != -1) {
232 22 : switch (ch) {
233 : case 'a':
234 4 : match = MATCH_ALL;
235 4 : break;
236 : case 'A':
237 3 : action = ADD;
238 3 : break;
239 : case 'C':
240 0 : pkgdb_set_case_sensitivity(true);
241 0 : break;
242 : case 'D':
243 3 : action = DELETE;
244 3 : break;
245 : case 'g':
246 0 : match = MATCH_GLOB;
247 0 : break;
248 : case 'i':
249 0 : pkgdb_set_case_sensitivity(false);
250 0 : break;
251 : case 'M':
252 1 : action = MODIFY;
253 1 : break;
254 : case 'q':
255 0 : quiet = true;
256 0 : break;
257 : case 'S':
258 4 : action = SHOW;
259 4 : flags |= PKG_LOAD_ANNOTATIONS;
260 4 : break;
261 : case 'x':
262 0 : match = MATCH_REGEX;
263 0 : break;
264 : case 'y':
265 7 : yes = true;
266 7 : break;
267 : default:
268 0 : usage_annotate();
269 0 : return (EX_USAGE);
270 : }
271 : }
272 11 : argc -= optind;
273 11 : argv += optind;
274 :
275 11 : if (action == NONE ||
276 4 : (match == MATCH_ALL && argc < 1) ||
277 7 : (match != MATCH_ALL && argc < 2)) {
278 0 : usage_annotate();
279 0 : return (EX_USAGE);
280 : }
281 :
282 11 : if (match == MATCH_ALL) {
283 4 : pkgname = NULL;
284 4 : tag = argv[0];
285 4 : value = (argc > 1) ? argv[1] : NULL;
286 : } else {
287 7 : pkgname = argv[0];
288 7 : tag = argv[1];
289 7 : value = (argc > 2) ? argv[2] : NULL;
290 : }
291 :
292 11 : if ((action == ADD || action == MODIFY) && value == NULL) {
293 : /* try and read data for the value from stdin. */
294 1 : input = read_input();
295 1 : value = sbuf_data(input);
296 : }
297 :
298 11 : retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
299 : PKGDB_DB_LOCAL);
300 11 : if (retcode == EPKG_ENODB) {
301 0 : if (match == MATCH_ALL) {
302 0 : exitcode = EX_OK;
303 0 : goto cleanup;
304 : }
305 0 : if (!quiet)
306 0 : warnx("No packages installed. Nothing to do!");
307 0 : exitcode = EX_OK;
308 0 : goto cleanup;
309 11 : } else if (retcode == EPKG_ENOACCESS) {
310 0 : warnx("Insufficient privileges to modify the package database");
311 0 : exitcode = EX_NOPERM;
312 0 : goto cleanup;
313 11 : } else if (retcode != EPKG_OK) {
314 0 : warnx("Error accessing the package database");
315 0 : exitcode = EX_SOFTWARE;
316 0 : goto cleanup;
317 : }
318 :
319 11 : retcode = pkgdb_open(&db, PKGDB_DEFAULT);
320 11 : if (retcode != EPKG_OK) {
321 0 : return (EX_IOERR);
322 : }
323 :
324 11 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
325 0 : pkgdb_close(db);
326 0 : warnx("Cannot get an exclusive lock on a database, it is locked by another process");
327 0 : return (EX_TEMPFAIL);
328 : }
329 :
330 11 : if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
331 0 : exitcode = EX_IOERR;
332 0 : goto cleanup;
333 : }
334 :
335 37 : while ((retcode = pkgdb_it_next(it, &pkg, flags)) == EPKG_OK) {
336 :
337 15 : switch(action) {
338 : case NONE: /* Should never happen */
339 0 : usage_annotate();
340 0 : exitcode = EX_USAGE;
341 0 : break;
342 : case ADD:
343 4 : retcode = do_add(db, pkg, tag, value);
344 4 : break;
345 : case MODIFY:
346 1 : retcode = do_modify(db, pkg, tag, value);
347 1 : break;
348 : case DELETE:
349 4 : retcode = do_delete(db, pkg, tag);
350 4 : break;
351 : case SHOW:
352 6 : retcode = do_show(pkg, tag);
353 6 : break;
354 : }
355 :
356 15 : if (retcode == EPKG_WARN)
357 0 : exitcode = EX_DATAERR;
358 :
359 15 : if (retcode != EPKG_OK && retcode != EPKG_WARN) {
360 0 : exitcode = EX_IOERR;
361 0 : goto cleanup;
362 : }
363 : }
364 :
365 : cleanup:
366 11 : pkg_free(pkg);
367 11 : pkgdb_it_free(it);
368 :
369 11 : pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
370 11 : pkgdb_close(db);
371 11 : if (input != NULL)
372 1 : sbuf_delete(input);
373 :
374 11 : return (exitcode);
375 : }
|