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