From e28deeb8e98e59f2b7e78624b31e49a21abf1182 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sat, 22 Apr 2023 01:59:33 +0200 Subject: [PATCH] libmisc/yesno.c: Use getline(3) and rpmatch(3) getline(3) is much more readable than manually looping. It has some overhead due to the allocation of a buffer, but that shouldn't be a problem here. If that was a problem, we could reuse the buffer (thus making the function non-reentrant), but I don't think that's worth the extra complexity. Using rpmatch(3) instead of a simple y/n test provides i18n to the response checking. We have a fall-back minimalistic implementation for systems that lack this function (e.g., musl libc). While we're at it, apply some other minor improvements to this file: - Remove comment saying which files use this function. That's likely to get outdated. And anyway, it's just a grep(1) away, so it doesn't really add any value. - Remove unnecessary casts to (void) that were used to verbosely ignore errors from stdio calls. They add clutter without really adding much value to the code (or I don't see it). - Remove comments from the function body. They make the function less readable. Instead, centralize the description of the function into a man-page-like comment before the function definition. This keeps the function body short and sweet. - Add '#include ', which was missing. - Minor whitespace style changes (it doesn't hurt the diff at this point, since most of the affected lines were already touched by other changes, so I applied my preferred style :). Acked-by: Samanta Navarro Cc: Serge Hallyn Signed-off-by: Alejandro Colomar --- configure.ac | 1 + libmisc/yesno.c | 95 +++++++++++++++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 35 deletions(-) diff --git a/configure.ac b/configure.ac index f4c29ca4..d75b99ff 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,7 @@ AC_CHECK_FUNCS(arc4random_buf futimes \ initgroups lckpwdf lutimes mempcpy \ setgroups updwtmp updwtmpx innetgr \ getspnam_r \ + rpmatch \ memset_explicit explicit_bzero stpecpy stpeprintf) AC_SYS_LARGEFILE diff --git a/libmisc/yesno.c b/libmisc/yesno.c index d8847e40..2ef4d9fa 100644 --- a/libmisc/yesno.c +++ b/libmisc/yesno.c @@ -1,59 +1,84 @@ /* * SPDX-FileCopyrightText: 1992 - 1994, Julianne Frances Haugh * SPDX-FileCopyrightText: 2007 - 2008, Nicolas François + * SPDX-FileCopyrightText: 2023, Alejandro Colomar * * SPDX-License-Identifier: BSD-3-Clause */ -/* - * Common code for yes/no prompting - * - * Used by pwck.c and grpck.c - */ #include #ident "$Id$" +#include #include +#include #include "prototypes.h" -/* - * yes_or_no - get answer to question from the user - * - * It returns false if no. - * - * If the read_only flag is set, it will print No, and will return - * false. - */ -bool yes_or_no (bool read_only) -{ - int c; - bool result; - /* - * In read-only mode all questions are answered "no". - */ +/* + * Synopsis + * bool yes_or_no(bool read_only); + * + * Arguments + * read_only + * In read-only mode, all questions are answered "no". It + * will print "No" to stdout. + * + * Description + * After a yes/no question, this function gets the answer from the + * user. + * + * Calls to this function will normally be preceeded by a prompt on + * stdout, so we should fflush(3). + * + * Return value + * false "no" + * true "yes" + * + * See also + * rpmatch(3) + */ + + +#if !defined(HAVE_RPMATCH) +static int rpmatch(const char *response); +#endif + + +bool +yes_or_no(bool read_only) +{ + bool ret; + char *buf; + if (read_only) { - (void) puts (_("No")); + puts(_("No")); return false; } - /* - * Typically, there's a prompt on stdout, sometimes unflushed. - */ - (void) fflush (stdout); + fflush(stdout); - /* - * Get a line and see what the first character is. - */ - c = fgetc(stdin); - /* TODO: use gettext */ - result = (c == 'y' || c == 'Y'); + ret = false; + if (getline(&buf, NULL, stdin) != NULL) + ret = rpmatch(buf) == 1; - while (c != '\n' && c != EOF) - c = fgetc(stdin); - - return result; + free(buf); + return ret; } + +#if !defined(HAVE_RPMATCH) +static int +rpmatch(const char *response) +{ + if (response[0] == 'y' || response[0] == 'Y') + return 1; + + if (response[0] == 'n' || response[0] == 'n') + return 0; + + return -1; +} +#endif