/ src / annotate.c
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  }