Branch data 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 : :
33 : : #include <err.h>
34 : : #include <getopt.h>
35 : : #include <stdio.h>
36 : : #include <stdlib.h>
37 : : #include <string.h>
38 : : #include <unistd.h>
39 : :
40 : : #include <pkg.h>
41 : :
42 : : #include "pkgcli.h"
43 : :
44 : : enum action {
45 : : NONE,
46 : : ADD,
47 : : MODIFY,
48 : : DELETE,
49 : : SHOW,
50 : : };
51 : :
52 : : void
53 : 0 : usage_annotate(void)
54 : : {
55 : 0 : fprintf(stderr,
56 : : "Usage: pkg annotate [-Cgiqxy] [-A|M] <pkg-name> <tag> [<value>]\n");
57 : 0 : fprintf(stderr,
58 : : " pkg annotate [-Cgiqxy] [-S|D] <pkg-name> <tag>\n");
59 : 0 : fprintf(stderr,
60 : : " pkg annotate [-qy] -a [-A|M] <tag> [<value>]\n");
61 : 0 : fprintf(stderr,
62 : : " pkg annotate [-qy] -a [-S|D] <tag>\n\n");
63 : 0 : fprintf(stderr,
64 : : "For more information see 'pkg help annotate'.\n");
65 : 0 : }
66 : :
67 : : static int
68 : 4 : do_add(struct pkgdb *db, struct pkg *pkg, const char *tag, const char *value)
69 : : {
70 : 4 : int ret = EPKG_OK;
71 : :
72 : :
73 [ - + # # ]: 4 : if (yes || query_tty_yesno(false, "%n-%v: Add annotation tagged: %S with "
74 : 0 : "value: %S? ", pkg, pkg, tag, value)) {
75 : :
76 : 4 : ret = pkgdb_add_annotation(db, pkg, tag, value);
77 [ - + ]: 4 : if (ret == EPKG_OK) {
78 [ - + ]: 4 : if (!quiet)
79 : 4 : pkg_printf("%n-%v: added annotation tagged:"
80 : 4 : " %S\n", pkg, pkg, tag);
81 [ # # ]: 4 : } else if (ret == EPKG_WARN) {
82 [ # # ]: 0 : if (!quiet) {
83 : 0 : pkg_warnx("%n-%v: Cannot add annotation tagged:"
84 : : " %S\n", pkg, pkg, tag);
85 : 0 : }
86 : 0 : } else {
87 : 0 : pkg_warnx("%n-%v: Failed to add annotation tagged:"
88 : : " %S\n", pkg, pkg, tag);
89 : : }
90 : 4 : }
91 : 4 : return (ret);
92 : : }
93 : :
94 : : static int
95 : 4 : do_modify(struct pkgdb *db, struct pkg *pkg, const char *tag, const char *value)
96 : : {
97 : 4 : int ret = EPKG_OK;
98 : :
99 : :
100 [ - + # # ]: 4 : if (yes || query_tty_yesno(false, "%n-%v: Change annotation tagged: %S to "
101 : 0 : "new value: %S? ", pkg, pkg, tag, value)) {
102 : 4 : ret = pkgdb_modify_annotation(db, pkg, tag, value);
103 [ - + # # ]: 4 : if (ret == EPKG_OK || ret == EPKG_WARN) {
104 [ - + ]: 4 : if (!quiet)
105 : 4 : pkg_printf("%n-%v: Modified annotation "
106 : 4 : "tagged: %S\n", pkg, pkg, tag);
107 : 4 : } else {
108 : 0 : pkg_warnx("%n-%v: Failed to modify annotation tagged:"
109 : : " %S", pkg, pkg, tag);
110 : : }
111 : 4 : }
112 : 4 : return (ret);
113 : : }
114 : :
115 : : static int
116 : 5 : do_delete(struct pkgdb *db, struct pkg *pkg, const char *tag)
117 : : {
118 : 5 : int ret = EPKG_OK;
119 : :
120 [ - + # # ]: 5 : if (yes || query_tty_yesno(false, "%n-%v: Delete annotation tagged: %S? ",
121 : 0 : pkg, pkg, tag)) {
122 : 5 : ret = pkgdb_delete_annotation(db, pkg, tag);
123 [ - + ]: 5 : if (ret == EPKG_OK) {
124 [ - + ]: 5 : if (!quiet)
125 : 5 : pkg_printf("%n-%v: Deleted annotation "
126 : 5 : "tagged: %S\n", pkg, pkg, tag);
127 [ # # ]: 5 : } else if (ret == EPKG_WARN) {
128 [ # # ]: 0 : if (!quiet) {
129 : 0 : pkg_warnx("%n-%v: Cannot delete annotation "
130 : : "tagged: %S -- because there is none\n",
131 : : pkg, pkg, tag);
132 : 0 : }
133 : 0 : } else {
134 : 0 : pkg_warnx("%n-%v: Failed to delete annotation tagged: %S\n",
135 : : pkg, pkg, tag);
136 : : }
137 : 5 : }
138 : 5 : return (ret);
139 : : }
140 : :
141 : : static int
142 : 8 : do_show(struct pkg *pkg, const char *tag)
143 : : {
144 : : struct pkg_kvlist_iterator *kit;
145 : 8 : struct pkg_kvlist *kl = NULL;
146 : : struct pkg_kv *note;
147 : 8 : int ret = EPKG_OK;
148 : :
149 : 8 : pkg_get(pkg, PKG_ATTR_ANNOTATIONS, &kl);
150 : 8 : kit = pkg_kvlist_iterator(kl);
151 [ + + ]: 17 : while ((note = pkg_kvlist_next(kit))) {
152 [ + + ]: 15 : if (STREQ(tag, note->key)) {
153 [ - + ]: 6 : if (quiet)
154 : 0 : printf("%s\n", note->value);
155 : : else
156 : 6 : pkg_printf("%n-%v: Tag: %S Value: %S\n",
157 : 6 : pkg, pkg, note->key, note->value);
158 : 6 : return (EPKG_OK);
159 : : }
160 : : }
161 : 2 : free(kit);
162 : 2 : free(kl);
163 : :
164 : 2 : return (ret);
165 : 8 : }
166 : :
167 : :
168 : : static char *
169 : 1 : read_input(void)
170 : : {
171 : : xstring *input;
172 : : int ch;
173 : :
174 : 1 : input = xstring_new();
175 : :
176 : 7 : for (;;) {
177 [ - + + + ]: 7 : ch = getc(stdin);
178 [ + + ]: 7 : if (ch == EOF) {
179 [ + - + + ]: 1 : if (feof(stdin))
180 : 1 : break;
181 [ # # # # ]: 2 : if (ferror(stdin))
182 : 0 : err(EXIT_FAILURE, "Failed to read stdin");
183 : 0 : }
184 : 6 : fputc(ch, input->fp);
185 : : }
186 : :
187 : 1 : return (xstring_get(input));
188 : : }
189 : :
190 : : int
191 : 15 : exec_annotate(int argc, char **argv)
192 : : {
193 : 15 : struct pkgdb *db = NULL;
194 : 15 : struct pkgdb_it *it = NULL;
195 : 15 : struct pkg *pkg = NULL;
196 : 15 : enum action action = NONE;
197 : : const char *tag;
198 : : const char *value;
199 : : const char *pkgname;
200 : 15 : char *input = NULL;
201 : : int ch;
202 : 15 : int match = MATCH_EXACT;
203 : : int retcode;
204 : 15 : int exitcode = EXIT_SUCCESS;
205 : 15 : int flags = 0;
206 : 15 : int lock_type = PKGDB_LOCK_EXCLUSIVE;
207 : 15 : int mode = PKGDB_MODE_READ;
208 : :
209 : 15 : struct option longopts[] = {
210 : : { "all", no_argument, NULL, 'a' },
211 : : { "add", no_argument, NULL, 'A' },
212 : : { "case-insensitive", no_argument, NULL, 'C' },
213 : : { "delete", no_argument, NULL, 'D' },
214 : : { "glob", no_argument, NULL, 'g' },
215 : : { "case-insensitive", no_argument, NULL, 'i' },
216 : : { "modify", no_argument, NULL, 'M' },
217 : : { "quiet", no_argument, NULL, 'q' },
218 : : { "show", no_argument, NULL, 'S' },
219 : : { "regex", no_argument, NULL, 'x' },
220 : : { "yes", no_argument, NULL, 'y' },
221 : : { NULL, 0, NULL, 0 },
222 : : };
223 : :
224 : : /* Set default case sensitivity for searching */
225 : 15 : pkgdb_set_case_sensitivity(
226 : 15 : pkg_object_bool(pkg_config_get("CASE_SENSITIVE_MATCH"))
227 : : );
228 : :
229 [ + + + + ]: 46 : while ((ch = getopt_long(argc, argv, "+aACDgiMqSxy", longopts, NULL))
230 : 46 : != -1) {
231 [ + + - + : 31 : switch (ch) {
- - + - +
- + - ]
232 : : case 'a':
233 : 6 : match = MATCH_ALL;
234 : 6 : break;
235 : : case 'A':
236 : 3 : action = ADD;
237 : 3 : break;
238 : : case 'C':
239 : 0 : pkgdb_set_case_sensitivity(true);
240 : 0 : break;
241 : : case 'D':
242 : 4 : action = DELETE;
243 : 4 : break;
244 : : case 'g':
245 : 0 : match = MATCH_GLOB;
246 : 0 : break;
247 : : case 'i':
248 : 0 : pkgdb_set_case_sensitivity(false);
249 : 0 : break;
250 : : case 'M':
251 : 3 : action = MODIFY;
252 : 3 : break;
253 : : case 'q':
254 : 0 : quiet = true;
255 : 0 : break;
256 : : case 'S':
257 : 5 : action = SHOW;
258 : 5 : lock_type = PKGDB_LOCK_READONLY;
259 : 5 : flags |= PKG_LOAD_ANNOTATIONS;
260 : 5 : break;
261 : : case 'x':
262 : 0 : match = MATCH_REGEX;
263 : 0 : break;
264 : : case 'y':
265 : 10 : yes = true;
266 : 10 : break;
267 : : default:
268 : 0 : usage_annotate();
269 : 0 : return (EXIT_FAILURE);
270 : : }
271 : : }
272 : 15 : argc -= optind;
273 : 15 : argv += optind;
274 : :
275 [ - + + - ]: 24 : if (action == NONE ||
276 [ + + + - ]: 15 : (match == MATCH_ALL && argc < 1) ||
277 [ + + ]: 15 : (match != MATCH_ALL && argc < 2)) {
278 : 0 : usage_annotate();
279 : 0 : return (EXIT_FAILURE);
280 : : }
281 : :
282 [ + + ]: 15 : if (match == MATCH_ALL) {
283 : 6 : pkgname = NULL;
284 : 6 : tag = argv[0];
285 [ + + ]: 6 : value = (argc > 1) ? argv[1] : NULL;
286 : 6 : } else {
287 : 9 : pkgname = argv[0];
288 : 9 : tag = argv[1];
289 [ + + ]: 9 : value = (argc > 2) ? argv[2] : NULL;
290 : : }
291 : :
292 [ + + + + : 15 : if ((action == ADD || action == MODIFY) && value == NULL) {
+ + ]
293 : : /* try and read data for the value from stdin. */
294 : 1 : value = input = read_input();
295 : 1 : }
296 : :
297 [ + + ]: 15 : if (lock_type == PKGDB_LOCK_EXCLUSIVE)
298 : 10 : mode |= PKGDB_MODE_WRITE;
299 : 15 : retcode = pkgdb_access(mode, PKGDB_DB_LOCAL);
300 [ - + ]: 15 : if (retcode == EPKG_ENODB) {
301 [ # # ]: 0 : if (match == MATCH_ALL) {
302 : 0 : exitcode = EXIT_SUCCESS;
303 : 0 : goto cleanup;
304 : : }
305 [ # # ]: 0 : if (!quiet)
306 : 0 : warnx("No packages installed. Nothing to do!");
307 : 0 : exitcode = EXIT_SUCCESS;
308 : 0 : goto cleanup;
309 [ - + ]: 15 : } else if (retcode == EPKG_ENOACCESS) {
310 : 0 : warnx("Insufficient privileges to modify the package database");
311 : 0 : exitcode = EXIT_FAILURE;
312 : 0 : goto cleanup;
313 [ - + ]: 15 : } else if (retcode != EPKG_OK) {
314 : 0 : warnx("Error accessing the package database");
315 : 0 : exitcode = EXIT_FAILURE;
316 : 0 : goto cleanup;
317 : : }
318 : :
319 : 15 : retcode = pkgdb_open(&db, PKGDB_DEFAULT);
320 [ - + ]: 15 : if (retcode != EPKG_OK) {
321 : 0 : free(input);
322 : 0 : return (EXIT_FAILURE);
323 : : }
324 : :
325 [ - + ]: 15 : if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
326 : 0 : pkgdb_close(db);
327 : 0 : warnx("Cannot get an exclusive lock on a database, it is locked by another process");
328 : 0 : return (EXIT_FAILURE);
329 : : }
330 : :
331 [ - + ]: 15 : if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
332 : 0 : exitcode = EXIT_FAILURE;
333 : 0 : goto cleanup;
334 : : }
335 : :
336 [ + + ]: 36 : while ((retcode = pkgdb_it_next(it, &pkg, flags)) == EPKG_OK) {
337 : :
338 [ - + + + : 21 : switch(action) {
- + ]
339 : : case NONE: /* Should never happen */
340 : 0 : usage_annotate();
341 : 0 : exitcode = EXIT_FAILURE;
342 : 0 : break;
343 : : case ADD:
344 : 4 : retcode = do_add(db, pkg, tag, value);
345 : 4 : break;
346 : : case MODIFY:
347 : 4 : retcode = do_modify(db, pkg, tag, value);
348 : 4 : break;
349 : : case DELETE:
350 : 5 : retcode = do_delete(db, pkg, tag);
351 : 5 : break;
352 : : case SHOW:
353 : 8 : retcode = do_show(pkg, tag);
354 : 8 : break;
355 : : }
356 : :
357 [ + - ]: 21 : if (retcode == EPKG_WARN)
358 : 0 : exitcode = EXIT_FAILURE;
359 : :
360 [ - + # # ]: 21 : if (retcode != EPKG_OK && retcode != EPKG_WARN) {
361 : 0 : exitcode = EXIT_FAILURE;
362 : 0 : goto cleanup;
363 : : }
364 : : }
365 : :
366 : : cleanup:
367 : 15 : pkg_free(pkg);
368 : 15 : pkgdb_it_free(it);
369 : :
370 : 15 : pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
371 : 15 : pkgdb_close(db);
372 : 15 : free(input);
373 : :
374 : 15 : return (exitcode);
375 : 15 : }
|