From e081eae7a469e0ddeebedd51c3d83bcbaccb23e3 Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Mon, 25 Sep 2000 20:23:21 +0000 Subject: [PATCH] Final (I think) version of xargs. Throw away all that tedious string scrubbing, and quit using system. Instead, use fork() and exec(), which yields and smaller, simpler, and cleaner design. -Erik --- findutils/xargs.c | 156 ++++++++++++++++++---------------------------- xargs.c | 156 ++++++++++++++++++---------------------------- 2 files changed, 124 insertions(+), 188 deletions(-) diff --git a/findutils/xargs.c b/findutils/xargs.c index 21bfb6bbe..b8ac31aa0 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c @@ -28,68 +28,19 @@ #include #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; -} - +#include +#include int xargs_main(int argc, char **argv) { char *in_from_stdin = NULL; - char *args_from_cmdline = NULL; + char *args = NULL; char *cmd_to_be_executed = NULL; char traceflag = 0; - int len_args_from_cmdline, len_cmd_to_be_executed, len, opt; + int len_args=10, len_cmd_to_be_executed, opt; + pid_t pid; + int wpid, status; /* Note that we do not use getopt here, since * we only want to interpret initial options, @@ -102,50 +53,45 @@ int xargs_main(int argc, char **argv) break; default: fatalError(xargs_usage); - } + } } } - /* Store the command and arguments to be executed (from the command line) */ + /* Store the command to be executed (taken from the command line) */ if (argc == 0) { - len_args_from_cmdline = 6; - args_from_cmdline = xmalloc(len_args_from_cmdline); - strcat(args_from_cmdline, "echo "); + len_cmd_to_be_executed=6; + cmd_to_be_executed = xmalloc(len_cmd_to_be_executed); + strcat(cmd_to_be_executed, "echo"); } else { opt=strlen(*argv); - len_args_from_cmdline = (opt > 10)? opt : 10; - args_from_cmdline = xcalloc(len_args_from_cmdline, sizeof(char)); - while (argc-- > 0) { - if (strlen(*argv) + strlen(args_from_cmdline) > - len_args_from_cmdline) { - len_args_from_cmdline += strlen(*argv); - args_from_cmdline = - xrealloc(args_from_cmdline, - len_args_from_cmdline+1); - } - strcat(args_from_cmdline, *argv); - strcat(args_from_cmdline, " "); - ++argv; - } + len_cmd_to_be_executed = (opt > 10)? opt : 10; + cmd_to_be_executed = xcalloc(len_cmd_to_be_executed, sizeof(char)); + strcat(cmd_to_be_executed, *argv); } - /* Set up some space for the command to be executed to be held in */ - 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); - /* Now, read in one line at a time from stdin, and run command+args on it */ - in_from_stdin = get_sh_safe_line_from_file(stdin); + /* Now, read in one line at a time from stdin, and stroe this to be used later + * as an argument to the command we just stored */ + in_from_stdin = get_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); + len_args += opt + 3; + args=xrealloc(args, len_args); /* 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])) @@ -157,26 +103,49 @@ int xargs_main(int argc, char **argv) while(isspace(*tmp)) tmp++; - strcat(cmd_to_be_executed, tmp); - strcat(cmd_to_be_executed, " "); - + strcat(args, tmp); + strcat(args, " "); + free(in_from_stdin); - in_from_stdin = get_sh_safe_line_from_file(stdin); + in_from_stdin = get_line_from_file(stdin); } - if (traceflag==1) + if (traceflag==1) { fputs(cmd_to_be_executed, stderr); + fputs(args, stderr); + } - if ((system(cmd_to_be_executed) != 0) && (errno != 0)) - fatalError("%s", strerror(errno)); + if ((pid = fork()) == 0) { + char *cmd[255]; + int i=1; + + //printf("argv[0]='%s'\n", cmd_to_be_executed); + cmd[0] = cmd_to_be_executed; + while (--argc && ++argv && *argv ) { + //printf("argv[%d]='%s'\n", i, *argv); + cmd[i++]=*argv; + } + //printf("argv[%d]='%s'\n", i, args); + cmd[i++] = args; + cmd[i] = NULL; + execvp(cmd_to_be_executed, cmd); + + /* What? Still here? Exit with an error */ + fatalError("%s: %s\n", cmd_to_be_executed, strerror(errno)); + } + /* Wait for a child process to exit */ + wpid = wait(&status); #ifdef BB_FEATURE_CLEAN_UP - free(args_from_cmdline); + free(args); free(cmd_to_be_executed); #endif - return 0; + if (wpid > 0) + return (WEXITSTATUS(status)); + else + return EXIT_FAILURE; } /* Local Variables: @@ -185,4 +154,3 @@ c-basic-offset: 4 tab-width: 4 End: */ - diff --git a/xargs.c b/xargs.c index 21bfb6bbe..b8ac31aa0 100644 --- a/xargs.c +++ b/xargs.c @@ -28,68 +28,19 @@ #include #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; -} - +#include +#include int xargs_main(int argc, char **argv) { char *in_from_stdin = NULL; - char *args_from_cmdline = NULL; + char *args = NULL; char *cmd_to_be_executed = NULL; char traceflag = 0; - int len_args_from_cmdline, len_cmd_to_be_executed, len, opt; + int len_args=10, len_cmd_to_be_executed, opt; + pid_t pid; + int wpid, status; /* Note that we do not use getopt here, since * we only want to interpret initial options, @@ -102,50 +53,45 @@ int xargs_main(int argc, char **argv) break; default: fatalError(xargs_usage); - } + } } } - /* Store the command and arguments to be executed (from the command line) */ + /* Store the command to be executed (taken from the command line) */ if (argc == 0) { - len_args_from_cmdline = 6; - args_from_cmdline = xmalloc(len_args_from_cmdline); - strcat(args_from_cmdline, "echo "); + len_cmd_to_be_executed=6; + cmd_to_be_executed = xmalloc(len_cmd_to_be_executed); + strcat(cmd_to_be_executed, "echo"); } else { opt=strlen(*argv); - len_args_from_cmdline = (opt > 10)? opt : 10; - args_from_cmdline = xcalloc(len_args_from_cmdline, sizeof(char)); - while (argc-- > 0) { - if (strlen(*argv) + strlen(args_from_cmdline) > - len_args_from_cmdline) { - len_args_from_cmdline += strlen(*argv); - args_from_cmdline = - xrealloc(args_from_cmdline, - len_args_from_cmdline+1); - } - strcat(args_from_cmdline, *argv); - strcat(args_from_cmdline, " "); - ++argv; - } + len_cmd_to_be_executed = (opt > 10)? opt : 10; + cmd_to_be_executed = xcalloc(len_cmd_to_be_executed, sizeof(char)); + strcat(cmd_to_be_executed, *argv); } - /* Set up some space for the command to be executed to be held in */ - 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); - /* Now, read in one line at a time from stdin, and run command+args on it */ - in_from_stdin = get_sh_safe_line_from_file(stdin); + /* Now, read in one line at a time from stdin, and stroe this to be used later + * as an argument to the command we just stored */ + in_from_stdin = get_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); + len_args += opt + 3; + args=xrealloc(args, len_args); /* 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])) @@ -157,26 +103,49 @@ int xargs_main(int argc, char **argv) while(isspace(*tmp)) tmp++; - strcat(cmd_to_be_executed, tmp); - strcat(cmd_to_be_executed, " "); - + strcat(args, tmp); + strcat(args, " "); + free(in_from_stdin); - in_from_stdin = get_sh_safe_line_from_file(stdin); + in_from_stdin = get_line_from_file(stdin); } - if (traceflag==1) + if (traceflag==1) { fputs(cmd_to_be_executed, stderr); + fputs(args, stderr); + } - if ((system(cmd_to_be_executed) != 0) && (errno != 0)) - fatalError("%s", strerror(errno)); + if ((pid = fork()) == 0) { + char *cmd[255]; + int i=1; + + //printf("argv[0]='%s'\n", cmd_to_be_executed); + cmd[0] = cmd_to_be_executed; + while (--argc && ++argv && *argv ) { + //printf("argv[%d]='%s'\n", i, *argv); + cmd[i++]=*argv; + } + //printf("argv[%d]='%s'\n", i, args); + cmd[i++] = args; + cmd[i] = NULL; + execvp(cmd_to_be_executed, cmd); + + /* What? Still here? Exit with an error */ + fatalError("%s: %s\n", cmd_to_be_executed, strerror(errno)); + } + /* Wait for a child process to exit */ + wpid = wait(&status); #ifdef BB_FEATURE_CLEAN_UP - free(args_from_cmdline); + free(args); free(cmd_to_be_executed); #endif - return 0; + if (wpid > 0) + return (WEXITSTATUS(status)); + else + return EXIT_FAILURE; } /* Local Variables: @@ -185,4 +154,3 @@ c-basic-offset: 4 tab-width: 4 End: */ -