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