[pacman-dev,2/2] parse stdin as newline-separated

Message ID 20170218220950.4734-2-andrew.gregory.8@gmail.com
State Accepted, archived
Headers show
Series [pacman-dev,1/2] add alpm_list_append_strdup | expand

Commit Message

Andrew Gregory Feb. 18, 2017, 10:09 p.m. UTC
Newline-separated input is more reliable because most of the arguments
we accept over stdin can validly contain spaces but not newlines.

Resolves FS#52992

Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
---
 src/pacman/pacman.c | 55 ++++++++++++++++++++++-------------------------------
 1 file changed, 23 insertions(+), 32 deletions(-)

Comments

Andrew Gregory Feb. 18, 2017, 10:39 p.m. UTC | #1
On 02/18/17 at 05:09pm, Andrew Gregory wrote:
> Newline-separated input is more reliable because most of the arguments
> we accept over stdin can validly contain spaces but not newlines.
> 
> Resolves FS#52992
> 
> Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
> ---
>  src/pacman/pacman.c | 55 ++++++++++++++++++++++-------------------------------
>  1 file changed, 23 insertions(+), 32 deletions(-)
> 
> diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
> index a459cf00..7651df3c 100644
> --- a/src/pacman/pacman.c
> +++ b/src/pacman/pacman.c
> @@ -1094,7 +1094,6 @@ static void cl_to_log(int argc, char *argv[])
>  int main(int argc, char *argv[])
>  {
>  	int ret = 0;
> -	size_t i;
>  	uid_t myuid = getuid();
>  
>  	install_segv_handler();
> @@ -1142,44 +1141,36 @@ int main(int argc, char *argv[])
>  	if(alpm_list_find_str(pm_targets, "-")) {
>  		if(!isatty(fileno(stdin))) {
>  			int target_found = 0;
> -			size_t current_size = PATH_MAX;
> -			char *vdata, *line = malloc(current_size);
> -			int c;
> +			char *vdata, *line = NULL;
> +			size_t line_size = 0;
> +			ssize_t nread;
>  
>  			/* remove the '-' from the list */
>  			pm_targets = alpm_list_remove_str(pm_targets, "-", &vdata);
>  			free(vdata);
>  
> -			i = 0;
> -			do {
> -				c = fgetc(stdin);
> -				if(c == EOF || isspace(c)) {
> -					/* avoid adding zero length arg when multiple spaces separate args */
> -					if(i > 0) {
> -						line[i] = '\0';
> -						pm_targets = alpm_list_add(pm_targets, strdup(line));
> -						target_found = 1;
> -						i = 0;
> -					}
> -				} else {
> -					line[i++] = (char)c;
> -					/* we may be at the end of our allocated buffer now */
> -					if(i >= current_size) {
> -						char *new = realloc(line, current_size * 2);
> -						if(new) {
> -							line = new;
> -							current_size *= 2;
> -						} else {
> -							free(line);
> -							pm_printf(ALPM_LOG_ERROR,
> -									_("memory exhausted in argument parsing\n"));
> -							cleanup(EXIT_FAILURE);
> -						}
> -					}
> +			while((nread = getline(&line, &line_size, stdin)) != -1) {
> +				if(line[nread - 1] == '\n') {
> +					/* remove trailing newline */
> +					line[nread - 1] = '\0';
>  				}
> -			} while(c != EOF);
> -
> +				if(line[0] == '\0') {
> +					/* skip empty lines */
> +					continue;
> +				}
> +				if(!alpm_list_append_strdup(&pm_targets, line)) {
> +					break;
> +				}
> +				target_found = 1;
> +			}
>  			free(line);
> +
> +			if(ferror(stdin)) {
> +				pm_printf(ALPM_LOG_ERROR,
> +						_("failed to read arguments from stdin: (%s)\n"), strerror(errno));
> +				cleanup(EXIT_FAILURE);
> +			}

This, of course, won't catch errors from appending to pm_targets.
Switched to using errno for error detection in my repo.

> +
>  			if(!freopen(ctermid(NULL), "r", stdin)) {
>  				pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
>  						strerror(errno));
> -- 
> 2.11.1

Patch

diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index a459cf00..7651df3c 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -1094,7 +1094,6 @@  static void cl_to_log(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
 	int ret = 0;
-	size_t i;
 	uid_t myuid = getuid();
 
 	install_segv_handler();
@@ -1142,44 +1141,36 @@  int main(int argc, char *argv[])
 	if(alpm_list_find_str(pm_targets, "-")) {
 		if(!isatty(fileno(stdin))) {
 			int target_found = 0;
-			size_t current_size = PATH_MAX;
-			char *vdata, *line = malloc(current_size);
-			int c;
+			char *vdata, *line = NULL;
+			size_t line_size = 0;
+			ssize_t nread;
 
 			/* remove the '-' from the list */
 			pm_targets = alpm_list_remove_str(pm_targets, "-", &vdata);
 			free(vdata);
 
-			i = 0;
-			do {
-				c = fgetc(stdin);
-				if(c == EOF || isspace(c)) {
-					/* avoid adding zero length arg when multiple spaces separate args */
-					if(i > 0) {
-						line[i] = '\0';
-						pm_targets = alpm_list_add(pm_targets, strdup(line));
-						target_found = 1;
-						i = 0;
-					}
-				} else {
-					line[i++] = (char)c;
-					/* we may be at the end of our allocated buffer now */
-					if(i >= current_size) {
-						char *new = realloc(line, current_size * 2);
-						if(new) {
-							line = new;
-							current_size *= 2;
-						} else {
-							free(line);
-							pm_printf(ALPM_LOG_ERROR,
-									_("memory exhausted in argument parsing\n"));
-							cleanup(EXIT_FAILURE);
-						}
-					}
+			while((nread = getline(&line, &line_size, stdin)) != -1) {
+				if(line[nread - 1] == '\n') {
+					/* remove trailing newline */
+					line[nread - 1] = '\0';
 				}
-			} while(c != EOF);
-
+				if(line[0] == '\0') {
+					/* skip empty lines */
+					continue;
+				}
+				if(!alpm_list_append_strdup(&pm_targets, line)) {
+					break;
+				}
+				target_found = 1;
+			}
 			free(line);
+
+			if(ferror(stdin)) {
+				pm_printf(ALPM_LOG_ERROR,
+						_("failed to read arguments from stdin: (%s)\n"), strerror(errno));
+				cleanup(EXIT_FAILURE);
+			}
+
 			if(!freopen(ctermid(NULL), "r", stdin)) {
 				pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
 						strerror(errno));