From 00fec5e2503a8272ebac9f240e03d655131ec216 Mon Sep 17 00:00:00 2001
From: Xavier Chantry <chantry.xavier@gmail.com>
Date: Sun, 17 Oct 2010 17:09:25 +0200
Subject: pacman/sync: implement interactive group selection

Signed-off-by: Xavier Chantry <chantry.xavier@gmail.com>
---
 src/pacman/sync.c |   3 +-
 src/pacman/util.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/pacman/util.h |   1 +
 3 files changed, 110 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/pacman/sync.c b/src/pacman/sync.c
index 63e5766e..1e622d37 100644
--- a/src/pacman/sync.c
+++ b/src/pacman/sync.c
@@ -636,9 +636,8 @@ static int process_group(alpm_list_t *dbs, char *group)
 	printf(_(":: There are %d members in group %s:\n"), count,
 			group);
 	select_display(pkgs);
-	select_question(count);
 	char *array = malloc(count);
-	memset(array, 1, count);
+	multiselect_question(array, count);
 	int n = 0;
 	for(i = pkgs; i; i = alpm_list_next(i)) {
 		if(array[n++] == 0)
diff --git a/src/pacman/util.c b/src/pacman/util.c
index 133dccc2..51abbf4d 100644
--- a/src/pacman/util.c
+++ b/src/pacman/util.c
@@ -694,6 +694,114 @@ void select_display(const alpm_list_t *pkglist)
 	FREELIST(list);
 }
 
+static int parseindex(char *s, int *val, int min, int max)
+{
+	char *endptr = NULL;
+	int n = strtol(s, &endptr, 10);
+	if(*endptr == '\0') {
+		if(n < min || n > max) {
+			fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"),
+					n, min, max);
+			return(-1);
+		}
+		*val = n;
+		return(0);
+	} else {
+		fprintf(stderr, _("Invalid number: %s\n"), s);
+		return(-1);
+	}
+}
+
+static int multiselect_parse(char *array, int count, char *response)
+{
+	char *str, *saveptr;
+
+	for (str = response; ; str = NULL) {
+		int include = 1;
+		int start, end;
+		char *ends = NULL;
+		char *starts = strtok_r(str, " ", &saveptr);
+
+		if (starts == NULL)
+			break;
+		strtrim(starts);
+		int len = strlen(starts);
+		if(len == 0)
+			continue;
+
+		if (*starts == '^') {
+			starts++;
+			len--;
+			include = 0;
+		} else if(str) {
+			/* if first token is including, we unselect all targets */
+			memset(array, 0, count);
+		}
+
+		if(len > 1) {
+			/* check for range */
+			char *p;
+			if((p = strchr(starts+1, '-'))) {
+				*p = 0;
+				ends = p+1;
+			}
+		}
+
+		if(parseindex(starts, &start, 1, count) != 0)
+			return(-1);
+
+		if(!ends) {
+			array[start-1] = include;
+		} else {
+			if(parseindex(ends, &end, start, count) != 0)
+				return(-1);
+			for(int d = start; d <= end; d++) {
+				array[d-1] = include;
+			}
+		}
+	}
+
+	return(0);
+}
+
+int multiselect_question(char *array, int count)
+{
+	char response[64];
+	FILE *stream;
+
+	if(config->noconfirm) {
+		stream = stdout;
+	} else {
+		/* Use stderr so questions are always displayed when redirecting output */
+		stream = stderr;
+	}
+
+	while(1) {
+		memset(array, 1, count);
+
+		fprintf(stream, "\n");
+		fprintf(stream, _("Enter a selection (default=all)"));
+		fprintf(stream,	": ");
+
+		if(config->noconfirm) {
+			fprintf(stream, "\n");
+			break;
+		}
+
+		if(fgets(response, sizeof(response), stdin)) {
+			strtrim(response);
+			if(strlen(response) > 0) {
+				if(multiselect_parse(array, count, response) == -1) {
+					/* only loop if user gave an invalid answer */
+					continue;
+				}
+			}
+		}
+		break;
+	}
+	return(0);
+}
+
 int select_question(int count)
 {
 	char response[32];
diff --git a/src/pacman/util.h b/src/pacman/util.h
index 399f9bc8..234a631d 100644
--- a/src/pacman/util.h
+++ b/src/pacman/util.h
@@ -61,6 +61,7 @@ void display_optdepends(pmpkg_t *pkg);
 void print_packages(const alpm_list_t *packages);
 void select_display(const alpm_list_t *pkglist);
 int select_question(int count);
+int multiselect_question(char *array, int count);
 int yesno(char *fmt, ...);
 int noyes(char *fmt, ...);
 int pm_printf(pmloglevel_t level, const char *format, ...) __attribute__((format(printf,2,3)));
-- 
cgit v1.2.3-70-g09d2