[lvm-devel] master - display: yes no prompt improvement

Zdenek Kabelac zkabelac at fedoraproject.org
Fri Jun 10 14:03:57 UTC 2016


Gitweb:        http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=35612bd27c203774948c84005c9fb9237746c34a
Commit:        35612bd27c203774948c84005c9fb9237746c34a
Parent:        cfdc87b62331875c2f10cca2c41f486d40b2a67c
Author:        Zdenek Kabelac <zkabelac at redhat.com>
AuthorDate:    Wed Jun 8 20:52:14 2016 +0200
Committer:     Zdenek Kabelac <zkabelac at redhat.com>
CommitterDate: Fri Jun 10 16:00:31 2016 +0200

display: yes no prompt improvement

Original code missed to catch all apperances of SIGINT.
Also enhance logging when running in shell without tty.
Accept this regex as valid input:
'^[ ^t]*([Yy]([Ee]([Ss]|)|)|[Nn]([Oo]|))[ ^t]*$'
---
 WHATS_NEW             |    1 +
 lib/display/display.c |  100 +++++++++++++++++++++++++++++++++++++------------
 2 files changed, 77 insertions(+), 24 deletions(-)

diff --git a/WHATS_NEW b/WHATS_NEW
index 0afd527..0f8ebda 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
 Version 2.02.156 - 
 ================================
+  Yes/No prompt accepts '^[ ^t]*([Yy]([Ee]([Ss]|)|)|[Nn]([Oo]|))[ ^t]*$'.
   If available, also collect output from lsblk command when running lvmdump -s.
   Fix regression in blkdeactivate causing dm and md devices to be skipped. (2.02.155)
 
diff --git a/lib/display/display.c b/lib/display/display.c
index 0151d01..19421e7 100644
--- a/lib/display/display.c
+++ b/lib/display/display.c
@@ -831,50 +831,102 @@ void display_name_error(name_error_t name_error)
  * Prompt for y or n from stdin.
  * Defaults to 'no' in silent mode.
  * All callers should support --yes and/or --force to override this.
+ *
+ * Accepted are either _yes[] or _no[] strings or just their outset.
+ * When running without 'tty' stdin is printed to stderr.
+ * 'Yes' is accepted ONLY with '\n'.
  */
 char yes_no_prompt(const char *prompt, ...)
 {
-	int c = 0, ret = 0, cb = 0;
+	/* Lowercase Yes/No strings */
+	static const char _yes[] = "yes";
+	static const char _no[] = "no";
+	const char *answer = NULL;
+	int c = silent_mode() ? EOF : 0;
+	int i, ret = 0, sig = 0;
+	char buf[12];
 	va_list ap;
 
 	sigint_allow();
-	do {
-		if (c == '\n' || !c) {
+
+	for (;;) {
+		if (!ret) {
+			/* Show prompt */
 			va_start(ap, prompt);
 			vfprintf(stderr, prompt, ap);
 			va_end(ap);
 			fflush(stderr);
-			if (silent_mode()) {
-				fputc('n', stderr);
-				ret = 'n';
+
+			if (c == EOF)
 				break;
-			}
-			ret = 0;
+
+			i = 0;
+			answer = NULL;
 		}
 
+	nextchar:
+		if ((sig = sigint_caught()))
+			break;	/* Check if already interrupted before getchar() */
+
 		if ((c = getchar()) == EOF) {
-			ret = 'n'; /* SIGINT */
-			cb = 1;
-			break;
+			/* SIGNAL or no chars on stdin (missing '\n') or ^D */
+			if (!i)
+				break; /* Just shown prompt,-> print [n]\n */
+
+			goto invalid; /* Note:  c holds EOF */
 		}
 
+		if ((i < (sizeof(buf) - 4)) && isprint(c))
+			buf[i++] = c;
+
 		c = tolower(c);
-		if ((c == 'y') || (c == 'n')) {
-			/* If both 'y' and 'n' given, begin again. */
-			if (ret && c != ret)
-				ret = -1;
-			else
-				ret = c;
-		}
-	} while (ret < 1 || c != '\n');
 
-	sigint_restore();
+		if ((ret > 0) && (c == answer[0]))
+			answer++;	/* Matching, next char */
+		else if (c == '\n') {
+			if (feof(stdin))
+				fputc('\n', stderr);
+			if (ret > 0)
+				break;	/* Answered */
+	invalid:
+			if (i >= (sizeof(buf) - 4)) {
+				/* '...'  for missing input */
+				i = sizeof(buf) - 1;
+				buf[i - 1] = buf[i - 2] = buf[i - 3] = '.';
+			}
+			buf[i] = 0;
+			log_warn("WARNING: Invalid input '%s'.", buf);
+			ret = 0;	/* Otherwise refresh prompt */
+		} else if (!ret && (c == _yes[0])) {
+			ret = 'y';
+			answer = _yes + 1;	/* Expecting 'Yes' */
+		} else if (!ret && (c == _no[0])) {
+			ret = 'n';
+			answer = _no + 1;	/* Expecting 'No' */
+		} else if (!ret && isspace(c)) {
+			/* Ignore any whitespace before */
+			--i;
+			goto nextchar;
+		} else if ((ret > 0) && isspace(c)) {
+			/* Ignore any whitespace after */
+			while (*answer)
+				answer++; /* jump to end-of-word */
+		} else
+			ret = -1;	/* Read till '\n' and refresh */
+	}
 
-	if (cb && !sigint_caught())
-		fputc(ret, stderr);
+	sigint_restore();
 
-	if (c != '\n')
-		fputc('\n', stderr);
+	/* For other then Yes answer check there is really no interrupt */
+	if (sig || sigint_caught()) {
+		stack;
+		ret = 'n';
+	} else if (c == EOF) {
+		fputs("[n]\n", stderr);
+		ret = 'n';
+	} else
+		/* Not knowing if it's terminal, makes this hard.... */
+		log_verbose("Accepted input: [%c]", ret);
 
 	return ret;
 }




More information about the lvm-devel mailing list