diff --git a/findutils/xargs.c b/findutils/xargs.c index 478e0ee6d..21bfb6bbe 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c @@ -29,6 +29,59 @@ #include #include +/* get_sh_safe_line_from_file() - This function reads an entire line from a text file + * up to a newline. It returns a malloc'ed char * which must be stored and + * free'ed by the caller. */ +extern char *get_sh_safe_line_from_file(FILE *file) +{ + static const int GROWBY = 80; /* how large we will grow strings by */ + + char ch, last_ch = 0; + char tmp[]=" "; + int idx = 0; + char *linebuf = NULL; + int linebufsz = 0; + + while (1) { + ch = fgetc(file); + if (ch == EOF) + break; + + /* grow the line buffer as necessary */ + while (idx > linebufsz-4) + linebuf = xrealloc(linebuf, linebufsz += GROWBY); + + /* Remove any extra spaces */ + if (last_ch == ' ' && ch == ' ') + continue; + + /* Replace any tabs with spaces */ + if (ch == '\t') + ch=' '; + + /* Escape any characters that are treated specially by /bin/sh */ + *tmp=ch; + if (strpbrk(tmp, "\\~`!$^&*()=|{}[];\"'<>?#") != NULL && last_ch!='\\') { + linebuf[idx++] = '\\'; + } + + linebuf[idx++] = ch; + last_ch=ch; + + if (ch == '\n') + break; + } + if (idx == 0 && last_ch!=0) + linebuf[idx++]=' '; + + if (idx == 0) + return NULL; + + linebuf[idx] = 0; + return linebuf; +} + + int xargs_main(int argc, char **argv) { @@ -54,7 +107,7 @@ int xargs_main(int argc, char **argv) } /* Store the command and arguments to be executed (from the command line) */ - if (argc == 1) { + if (argc == 0) { len_args_from_cmdline = 6; args_from_cmdline = xmalloc(len_args_from_cmdline); strcat(args_from_cmdline, "echo "); @@ -72,6 +125,7 @@ int xargs_main(int argc, char **argv) } strcat(args_from_cmdline, *argv); strcat(args_from_cmdline, " "); + ++argv; } } @@ -79,30 +133,19 @@ int xargs_main(int argc, char **argv) len_cmd_to_be_executed=10; cmd_to_be_executed = xcalloc(len_cmd_to_be_executed, sizeof(char)); strcpy(cmd_to_be_executed, args_from_cmdline); - strcat(cmd_to_be_executed, " \""); /* Now, read in one line at a time from stdin, and run command+args on it */ - in_from_stdin = get_line_from_file(stdin); + in_from_stdin = get_sh_safe_line_from_file(stdin); for (;in_from_stdin!=NULL;) { char *tmp; opt = strlen(in_from_stdin); len = opt + len_args_from_cmdline; len_cmd_to_be_executed+=len+3; cmd_to_be_executed=xrealloc(cmd_to_be_executed, len_cmd_to_be_executed); - + /* Strip out the final \n */ in_from_stdin[opt-1]=' '; - - /* Replace any tabs with spaces */ - while( (tmp = strchr(in_from_stdin, '\t')) != NULL ) - *tmp=' '; - - /* Strip out any extra intra-word spaces */ - while( (tmp = strstr(in_from_stdin, " ")) != NULL ) { - opt = strlen(in_from_stdin); - memmove(tmp, tmp+1, opt-(tmp-in_from_stdin)); - } - + /* trim trailing whitespace */ opt = strlen(in_from_stdin) - 1; while (isspace(in_from_stdin[opt])) @@ -118,10 +161,9 @@ int xargs_main(int argc, char **argv) strcat(cmd_to_be_executed, " "); free(in_from_stdin); - in_from_stdin = get_line_from_file(stdin); + in_from_stdin = get_sh_safe_line_from_file(stdin); } - strcat(cmd_to_be_executed, "\""); if (traceflag==1) fputs(cmd_to_be_executed, stderr); diff --git a/xargs.c b/xargs.c index 478e0ee6d..21bfb6bbe 100644 --- a/xargs.c +++ b/xargs.c @@ -29,6 +29,59 @@ #include #include +/* get_sh_safe_line_from_file() - This function reads an entire line from a text file + * up to a newline. It returns a malloc'ed char * which must be stored and + * free'ed by the caller. */ +extern char *get_sh_safe_line_from_file(FILE *file) +{ + static const int GROWBY = 80; /* how large we will grow strings by */ + + char ch, last_ch = 0; + char tmp[]=" "; + int idx = 0; + char *linebuf = NULL; + int linebufsz = 0; + + while (1) { + ch = fgetc(file); + if (ch == EOF) + break; + + /* grow the line buffer as necessary */ + while (idx > linebufsz-4) + linebuf = xrealloc(linebuf, linebufsz += GROWBY); + + /* Remove any extra spaces */ + if (last_ch == ' ' && ch == ' ') + continue; + + /* Replace any tabs with spaces */ + if (ch == '\t') + ch=' '; + + /* Escape any characters that are treated specially by /bin/sh */ + *tmp=ch; + if (strpbrk(tmp, "\\~`!$^&*()=|{}[];\"'<>?#") != NULL && last_ch!='\\') { + linebuf[idx++] = '\\'; + } + + linebuf[idx++] = ch; + last_ch=ch; + + if (ch == '\n') + break; + } + if (idx == 0 && last_ch!=0) + linebuf[idx++]=' '; + + if (idx == 0) + return NULL; + + linebuf[idx] = 0; + return linebuf; +} + + int xargs_main(int argc, char **argv) { @@ -54,7 +107,7 @@ int xargs_main(int argc, char **argv) } /* Store the command and arguments to be executed (from the command line) */ - if (argc == 1) { + if (argc == 0) { len_args_from_cmdline = 6; args_from_cmdline = xmalloc(len_args_from_cmdline); strcat(args_from_cmdline, "echo "); @@ -72,6 +125,7 @@ int xargs_main(int argc, char **argv) } strcat(args_from_cmdline, *argv); strcat(args_from_cmdline, " "); + ++argv; } } @@ -79,30 +133,19 @@ int xargs_main(int argc, char **argv) len_cmd_to_be_executed=10; cmd_to_be_executed = xcalloc(len_cmd_to_be_executed, sizeof(char)); strcpy(cmd_to_be_executed, args_from_cmdline); - strcat(cmd_to_be_executed, " \""); /* Now, read in one line at a time from stdin, and run command+args on it */ - in_from_stdin = get_line_from_file(stdin); + in_from_stdin = get_sh_safe_line_from_file(stdin); for (;in_from_stdin!=NULL;) { char *tmp; opt = strlen(in_from_stdin); len = opt + len_args_from_cmdline; len_cmd_to_be_executed+=len+3; cmd_to_be_executed=xrealloc(cmd_to_be_executed, len_cmd_to_be_executed); - + /* Strip out the final \n */ in_from_stdin[opt-1]=' '; - - /* Replace any tabs with spaces */ - while( (tmp = strchr(in_from_stdin, '\t')) != NULL ) - *tmp=' '; - - /* Strip out any extra intra-word spaces */ - while( (tmp = strstr(in_from_stdin, " ")) != NULL ) { - opt = strlen(in_from_stdin); - memmove(tmp, tmp+1, opt-(tmp-in_from_stdin)); - } - + /* trim trailing whitespace */ opt = strlen(in_from_stdin) - 1; while (isspace(in_from_stdin[opt])) @@ -118,10 +161,9 @@ int xargs_main(int argc, char **argv) strcat(cmd_to_be_executed, " "); free(in_from_stdin); - in_from_stdin = get_line_from_file(stdin); + in_from_stdin = get_sh_safe_line_from_file(stdin); } - strcat(cmd_to_be_executed, "\""); if (traceflag==1) fputs(cmd_to_be_executed, stderr);