aboutsummaryrefslogtreecommitdiff
path: root/findutils
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 12:56:34 +1000
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 12:56:34 +1000
commitc31744ca1a86b2276c37c6d9a884660185debed6 (patch)
treebb051c35c3430a3793ceef173a01cb522fb4ae05 /findutils
parent1a286d510c2125bdab601ce47afd4d27b6ce6f41 (diff)
parente329089c62ed813e97344f8c61d7dc34221fd5ee (diff)
downloadbusybox-w32-c31744ca1a86b2276c37c6d9a884660185debed6.tar.gz
busybox-w32-c31744ca1a86b2276c37c6d9a884660185debed6.tar.bz2
busybox-w32-c31744ca1a86b2276c37c6d9a884660185debed6.zip
Merge branch 'origin/master' (early part)
Diffstat (limited to 'findutils')
-rw-r--r--findutils/find.c90
-rw-r--r--findutils/grep.c63
-rw-r--r--findutils/xargs.c519
3 files changed, 398 insertions, 274 deletions
diff --git a/findutils/find.c b/findutils/find.c
index ca630b6c5..297081489 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -53,8 +53,10 @@
53 * diff -u /tmp/std_find /tmp/bb_find && echo Identical 53 * diff -u /tmp/std_find /tmp/bb_find && echo Identical
54 */ 54 */
55 55
56//applet:IF_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_DROP, find))
57
56//kbuild:lib-$(CONFIG_FIND) += find.o 58//kbuild:lib-$(CONFIG_FIND) += find.o
57//config: 59
58//config:config FIND 60//config:config FIND
59//config: bool "find" 61//config: bool "find"
60//config: default y 62//config: default y
@@ -1044,6 +1046,92 @@ static action*** parse_params(char **argv)
1044#undef ALLOC_ACTION 1046#undef ALLOC_ACTION
1045} 1047}
1046 1048
1049//usage:#define find_trivial_usage
1050//usage: "[PATH]... [EXPRESSION]"
1051//usage:#define find_full_usage "\n\n"
1052//usage: "Search for files. The default PATH is the current directory,\n"
1053//usage: "default EXPRESSION is '-print'\n"
1054//usage: "\nEXPRESSION may consist of:"
1055//usage: "\n -follow Follow symlinks"
1056//usage: IF_FEATURE_FIND_XDEV(
1057//usage: "\n -xdev Don't descend directories on other filesystems"
1058//usage: )
1059//usage: IF_FEATURE_FIND_MAXDEPTH(
1060//usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
1061//usage: "\n tests/actions to command line arguments only"
1062//usage: )
1063//usage: "\n -mindepth N Don't act on first N levels"
1064//usage: "\n -name PATTERN File name (w/o directory name) matches PATTERN"
1065//usage: "\n -iname PATTERN Case insensitive -name"
1066//usage: IF_FEATURE_FIND_PATH(
1067//usage: "\n -path PATTERN Path matches PATTERN"
1068//usage: )
1069//usage: IF_FEATURE_FIND_REGEX(
1070//usage: "\n -regex PATTERN Path matches regex PATTERN"
1071//usage: )
1072//usage: IF_FEATURE_FIND_TYPE(
1073//usage: "\n -type X File type is X (X is one of: f,d,l,b,c,...)"
1074//usage: )
1075//usage: IF_FEATURE_FIND_PERM(
1076//usage: "\n -perm NNN Permissions match any of (+NNN), all of (-NNN),"
1077//usage: "\n or exactly NNN"
1078//usage: )
1079//usage: IF_FEATURE_FIND_MTIME(
1080//usage: "\n -mtime DAYS Modified time is greater than (+N), less than (-N),"
1081//usage: "\n or exactly N days"
1082//usage: )
1083//usage: IF_FEATURE_FIND_MMIN(
1084//usage: "\n -mmin MINS Modified time is greater than (+N), less than (-N),"
1085//usage: "\n or exactly N minutes"
1086//usage: )
1087//usage: IF_FEATURE_FIND_NEWER(
1088//usage: "\n -newer FILE Modified time is more recent than FILE's"
1089//usage: )
1090//usage: IF_FEATURE_FIND_INUM(
1091//usage: "\n -inum N File has inode number N"
1092//usage: )
1093//usage: IF_FEATURE_FIND_USER(
1094//usage: "\n -user NAME File is owned by user NAME (numeric user ID allowed)"
1095//usage: )
1096//usage: IF_FEATURE_FIND_GROUP(
1097//usage: "\n -group NAME File belongs to group NAME (numeric group ID allowed)"
1098//usage: )
1099//usage: IF_FEATURE_FIND_DEPTH(
1100//usage: "\n -depth Process directory name after traversing it"
1101//usage: )
1102//usage: IF_FEATURE_FIND_SIZE(
1103//usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
1104//usage: "\n +/-N: file size is bigger/smaller than N"
1105//usage: )
1106//usage: IF_FEATURE_FIND_LINKS(
1107//usage: "\n -links N Number of links is greater than (+N), less than (-N),"
1108//usage: "\n or exactly N"
1109//usage: )
1110//usage: "\n -print Print (default and assumed)"
1111//usage: IF_FEATURE_FIND_PRINT0(
1112//usage: "\n -print0 Delimit output with null characters rather than"
1113//usage: "\n newlines"
1114//usage: )
1115//usage: IF_FEATURE_FIND_CONTEXT(
1116//usage: "\n -context File has specified security context"
1117//usage: )
1118//usage: IF_FEATURE_FIND_EXEC(
1119//usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
1120//usage: "\n matching files"
1121//usage: )
1122//usage: IF_FEATURE_FIND_PRUNE(
1123//usage: "\n -prune Stop traversing current subtree"
1124//usage: )
1125//usage: IF_FEATURE_FIND_DELETE(
1126//usage: "\n -delete Delete files, turns on -depth option"
1127//usage: )
1128//usage: IF_FEATURE_FIND_PAREN(
1129//usage: "\n (EXPR) Group an expression"
1130//usage: )
1131//usage:
1132//usage:#define find_example_usage
1133//usage: "$ find / -name passwd\n"
1134//usage: "/etc/passwd\n"
1047 1135
1048int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1136int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1049int find_main(int argc UNUSED_PARAM, char **argv) 1137int find_main(int argc UNUSED_PARAM, char **argv)
diff --git a/findutils/grep.c b/findutils/grep.c
index fedf12d8a..9eb5e5a1b 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -14,13 +14,16 @@
14 * 2004,2006 (C) Vladimir Oleynik <dzo@simtreas.ru> - 14 * 2004,2006 (C) Vladimir Oleynik <dzo@simtreas.ru> -
15 * correction "-e pattern1 -e pattern2" logic and more optimizations. 15 * correction "-e pattern1 -e pattern2" logic and more optimizations.
16 * precompiled regex 16 * precompiled regex
17 */ 17 *
18/*
19 * (C) 2006 Jac Goudsmit added -o option 18 * (C) 2006 Jac Goudsmit added -o option
20 */ 19 */
21 20
21//applet:IF_GREP(APPLET(grep, _BB_DIR_BIN, _BB_SUID_DROP))
22//applet:IF_FEATURE_GREP_EGREP_ALIAS(APPLET_ODDNAME(egrep, grep, _BB_DIR_BIN, _BB_SUID_DROP, egrep))
23//applet:IF_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_DROP, fgrep))
24
22//kbuild:lib-$(CONFIG_GREP) += grep.o 25//kbuild:lib-$(CONFIG_GREP) += grep.o
23//config: 26
24//config:config GREP 27//config:config GREP
25//config: bool "grep" 28//config: bool "grep"
26//config: default y 29//config: default y
@@ -57,17 +60,67 @@
57#include "libbb.h" 60#include "libbb.h"
58#include "xregex.h" 61#include "xregex.h"
59 62
63
60/* options */ 64/* options */
65//usage:#define grep_trivial_usage
66//usage: "[-HhnlLoqvsriw"
67//usage: "F"
68//usage: IF_FEATURE_GREP_EGREP_ALIAS("E")
69//usage: IF_EXTRA_COMPAT("z")
70//usage: "] [-m N] "
71//usage: IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ")
72//usage: "PATTERN/-e PATTERN.../-f FILE [FILE]..."
73//usage:#define grep_full_usage "\n\n"
74//usage: "Search for PATTERN in FILEs (or stdin)\n"
75//usage: "\nOptions:"
76//usage: "\n -H Add 'filename:' prefix"
77//usage: "\n -h Do not add 'filename:' prefix"
78//usage: "\n -n Add 'line_no:' prefix"
79//usage: "\n -l Show only names of files that match"
80//usage: "\n -L Show only names of files that don't match"
81//usage: "\n -c Show only count of matching lines"
82//usage: "\n -o Show only the matching part of line"
83//usage: "\n -q Quiet. Return 0 if PATTERN is found, 1 otherwise"
84//usage: "\n -v Select non-matching lines"
85//usage: "\n -s Suppress open and read errors"
86//usage: "\n -r Recurse"
87//usage: "\n -i Ignore case"
88//usage: "\n -w Match whole words only"
89//usage: "\n -F PATTERN is a literal (not regexp)"
90//usage: IF_FEATURE_GREP_EGREP_ALIAS(
91//usage: "\n -E PATTERN is an extended regexp"
92//usage: )
93//usage: IF_EXTRA_COMPAT(
94//usage: "\n -z Input is NUL terminated"
95//usage: )
96//usage: "\n -m N Match up to N times per file"
97//usage: IF_FEATURE_GREP_CONTEXT(
98//usage: "\n -A N Print N lines of trailing context"
99//usage: "\n -B N Print N lines of leading context"
100//usage: "\n -C N Same as '-A N -B N'"
101//usage: )
102//usage: "\n -e PTRN Pattern to match"
103//usage: "\n -f FILE Read pattern from file"
104//usage:
105//usage:#define grep_example_usage
106//usage: "$ grep root /etc/passwd\n"
107//usage: "root:x:0:0:root:/root:/bin/bash\n"
108//usage: "$ grep ^[rR]oo. /etc/passwd\n"
109//usage: "root:x:0:0:root:/root:/bin/bash\n"
110//usage:
111//usage:#define egrep_trivial_usage NOUSAGE_STR
112//usage:#define egrep_full_usage ""
113//usage:#define fgrep_trivial_usage NOUSAGE_STR
114//usage:#define fgrep_full_usage ""
115
61#define OPTSTR_GREP \ 116#define OPTSTR_GREP \
62 "lnqvscFiHhe:f:Lorm:w" \ 117 "lnqvscFiHhe:f:Lorm:w" \
63 IF_FEATURE_GREP_CONTEXT("A:B:C:") \ 118 IF_FEATURE_GREP_CONTEXT("A:B:C:") \
64 IF_FEATURE_GREP_EGREP_ALIAS("E") \ 119 IF_FEATURE_GREP_EGREP_ALIAS("E") \
65 IF_EXTRA_COMPAT("z") \ 120 IF_EXTRA_COMPAT("z") \
66 "aI" 121 "aI"
67
68/* ignored: -a "assume all files to be text" */ 122/* ignored: -a "assume all files to be text" */
69/* ignored: -I "assume binary files have no matches" */ 123/* ignored: -I "assume binary files have no matches" */
70
71enum { 124enum {
72 OPTBIT_l, /* list matched file names only */ 125 OPTBIT_l, /* list matched file names only */
73 OPTBIT_n, /* print line# */ 126 OPTBIT_n, /* print line# */
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 9133b8f6c..46a62cbf1 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -1,7 +1,6 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * Mini xargs implementation for busybox 3 * Mini xargs implementation for busybox
4 * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
5 * 4 *
6 * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru> 5 * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
7 * 6 *
@@ -14,11 +13,12 @@
14 * 13 *
15 * xargs is described in the Single Unix Specification v3 at 14 * xargs is described in the Single Unix Specification v3 at
16 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html 15 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
17 *
18 */ 16 */
19 17
18//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, _BB_DIR_USR_BIN, _BB_SUID_DROP, xargs))
19
20//kbuild:lib-$(CONFIG_XARGS) += xargs.o 20//kbuild:lib-$(CONFIG_XARGS) += xargs.o
21//config: 21
22//config:config XARGS 22//config:config XARGS
23//config: bool "xargs" 23//config: bool "xargs"
24//config: default y 24//config: default y
@@ -59,10 +59,6 @@
59//config: are not special. 59//config: are not special.
60 60
61#include "libbb.h" 61#include "libbb.h"
62
63/* This is a NOEXEC applet. Be very careful! */
64
65
66/* COMPAT: SYSV version defaults size (and has a max value of) to 470. 62/* COMPAT: SYSV version defaults size (and has a max value of) to 470.
67 We try to make it as large as possible. */ 63 We try to make it as large as possible. */
68#if !defined(ARG_MAX) && defined(_SC_ARG_MAX) 64#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
@@ -72,6 +68,12 @@
72# define ARG_MAX 470 68# define ARG_MAX 470
73#endif 69#endif
74 70
71/* This is a NOEXEC applet. Be very careful! */
72
73
74//#define dbg_msg(...) bb_error_msg(__VA_ARGS__)
75#define dbg_msg(...) ((void)0)
76
75 77
76#ifdef TEST 78#ifdef TEST
77# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 79# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
@@ -88,26 +90,36 @@
88# endif 90# endif
89#endif 91#endif
90 92
93
94struct globals {
95 char **args;
96 const char *eof_str;
97 int idx;
98} FIX_ALIASING;
99#define G (*(struct globals*)&bb_common_bufsiz1)
100#define INIT_G() do { } while (0)
101
102
91/* 103/*
92 This function has special algorithm. 104 * This function has special algorithm.
93 Don't use fork and include to main! 105 * Don't use fork and include to main!
94*/ 106 */
95static int xargs_exec(char **args) 107static int xargs_exec(void)
96{ 108{
97 int status; 109 int status;
98 110
99 status = spawn_and_wait(args); 111 status = spawn_and_wait(G.args);
100 if (status < 0) { 112 if (status < 0) {
101 bb_simple_perror_msg(args[0]); 113 bb_simple_perror_msg(G.args[0]);
102 return errno == ENOENT ? 127 : 126; 114 return errno == ENOENT ? 127 : 126;
103 } 115 }
104 if (status == 255) { 116 if (status == 255) {
105 bb_error_msg("%s: exited with status 255; aborting", args[0]); 117 bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
106 return 124; 118 return 124;
107 } 119 }
108 if (status >= 0x180) { 120 if (status >= 0x180) {
109 bb_error_msg("%s: terminated by signal %d", 121 bb_error_msg("%s: terminated by signal %d",
110 args[0], status - 0x180); 122 G.args[0], status - 0x180);
111 return 125; 123 return 125;
112 } 124 }
113 if (status) 125 if (status)
@@ -115,75 +127,75 @@ static int xargs_exec(char **args)
115 return 0; 127 return 0;
116} 128}
117 129
130/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
131 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
132 */
133#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
118 134
119typedef struct xlist_t { 135static void store_param(char *s)
120 struct xlist_t *link; 136{
121 size_t length; 137 /* Grow by 256 elements at once */
122 char xstr[1]; 138 if (!(G.idx & 0xff)) { /* G.idx == N*256 */
123} xlist_t; 139 /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */
124 140 G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
125static smallint eof_stdin_detected; 141 }
142 G.args[G.idx++] = s;
143}
126 144
127#define ISBLANK(c) ((c) == ' ' || (c) == '\t') 145/* process[0]_stdin:
128#define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \ 146 * Read characters into buf[n_max_chars+1], and when parameter delimiter
129 || (c) == '\f' || (c) == '\v') 147 * is seen, store the address of a new parameter to args[].
148 * If reading discovers that last chars do not form the complete
149 * parameter, the pointer to the first such "tail character" is returned.
150 * (buf has extra byte at the end to accomodate terminating NUL
151 * of "tail characters" string).
152 * Otherwise, the returned pointer points to NUL byte.
153 * On entry, buf[] may contain some "seed chars" which are to become
154 * the beginning of the first parameter.
155 */
130 156
131#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 157#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
132static xlist_t *process_stdin(xlist_t *list_arg, 158static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
133 const char *eof_str, size_t mc, char *buf)
134{ 159{
135#define NORM 0 160#define NORM 0
136#define QUOTE 1 161#define QUOTE 1
137#define BACKSLASH 2 162#define BACKSLASH 2
138#define SPACE 4 163#define SPACE 4
139 164 char q = '\0'; /* quote char */
140 char *s = NULL; /* start word */
141 char *p = NULL; /* pointer to end word */
142 char q = '\0'; /* quote char */
143 char state = NORM; 165 char state = NORM;
144 char eof_str_detected = 0; 166 char *s = buf; /* start of the word */
145 size_t line_l = 0; /* size loaded args line */ 167 char *p = s + strlen(buf); /* end of the word */
146 int c; /* current char */
147 xlist_t *cur;
148 xlist_t *prev;
149 168
150 prev = cur = list_arg; 169 buf += n_max_chars; /* past buffer's end */
151 while (1) { 170
152 if (!cur) break; 171 /* "goto ret" is used instead of "break" to make control flow
153 prev = cur; 172 * more obvious: */
154 line_l += cur->length;
155 cur = cur->link;
156 }
157 173
158 while (!eof_stdin_detected) { 174 while (1) {
159 c = getchar(); 175 int c = getchar();
160 if (c == EOF) { 176 if (c == EOF) {
161 eof_stdin_detected = 1; 177 if (p != s)
162 if (s) 178 goto close_word;
163 goto unexpected_eof; 179 goto ret;
164 break;
165 } 180 }
166 if (eof_str_detected)
167 continue;
168 if (state == BACKSLASH) { 181 if (state == BACKSLASH) {
169 state = NORM; 182 state = NORM;
170 goto set; 183 goto set;
171 } else if (state == QUOTE) { 184 }
185 if (state == QUOTE) {
172 if (c != q) 186 if (c != q)
173 goto set; 187 goto set;
174 q = '\0'; 188 q = '\0';
175 state = NORM; 189 state = NORM;
176 } else { /* if (state == NORM) */ 190 } else { /* if (state == NORM) */
177 if (ISSPACE(c)) { 191 if (ISSPACE(c)) {
178 if (s) { 192 if (p != s) {
179 unexpected_eof: 193 close_word:
180 state = SPACE; 194 state = SPACE;
181 c = '\0'; 195 c = '\0';
182 goto set; 196 goto set;
183 } 197 }
184 } else { 198 } else {
185 if (s == NULL)
186 s = p = buf;
187 if (c == '\\') { 199 if (c == '\\') {
188 state = BACKSLASH; 200 state = BACKSLASH;
189 } else if (c == '\'' || c == '"') { 201 } else if (c == '\'' || c == '"') {
@@ -191,8 +203,6 @@ static xlist_t *process_stdin(xlist_t *list_arg,
191 state = QUOTE; 203 state = QUOTE;
192 } else { 204 } else {
193 set: 205 set:
194 if ((size_t)(p - buf) >= mc)
195 bb_error_msg_and_die("argument line too long");
196 *p++ = c; 206 *p++ = c;
197 } 207 }
198 } 208 }
@@ -202,105 +212,122 @@ static xlist_t *process_stdin(xlist_t *list_arg,
202 bb_error_msg_and_die("unmatched %s quote", 212 bb_error_msg_and_die("unmatched %s quote",
203 q == '\'' ? "single" : "double"); 213 q == '\'' ? "single" : "double");
204 } 214 }
205 /* word loaded */ 215 /* A full word is loaded */
206 if (eof_str) { 216 if (G.eof_str) {
207 eof_str_detected = (strcmp(s, eof_str) == 0); 217 if (strcmp(s, G.eof_str) == 0) {
208 } 218 while (getchar() != EOF)
209 if (!eof_str_detected) { 219 continue;
210 size_t length = (p - buf); 220 p = s;
211 /* Dont xzalloc - it can be quite big */ 221 goto ret;
212 cur = xmalloc(offsetof(xlist_t, xstr) + length);
213 cur->link = NULL;
214 cur->length = length;
215 memcpy(cur->xstr, s, length);
216 if (prev == NULL) {
217 list_arg = cur;
218 } else {
219 prev->link = cur;
220 }
221 prev = cur;
222 line_l += length;
223 if (line_l > mc) {
224 /* stop memory usage :-) */
225 break;
226 } 222 }
227 } 223 }
228 s = NULL; 224 store_param(s);
225 dbg_msg("args[]:'%s'", s);
226 s = p;
227 n_max_arg--;
228 if (n_max_arg == 0) {
229 goto ret;
230 }
229 state = NORM; 231 state = NORM;
230 } 232 }
233 if (p == buf) {
234 goto ret;
235 }
231 } 236 }
232 return list_arg; 237 ret:
238 *p = '\0';
239 /* store_param(NULL) - caller will do it */
240 dbg_msg("return:'%s'", s);
241 return s;
233} 242}
234#else 243#else
235/* The variant does not support single quotes, double quotes or backslash */ 244/* The variant does not support single quotes, double quotes or backslash */
236static xlist_t *process_stdin(xlist_t *list_arg, 245static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
237 const char *eof_str, size_t mc, char *buf)
238{ 246{
247 char *s = buf; /* start of the word */
248 char *p = s + strlen(buf); /* end of the word */
239 249
240 int c; /* current char */ 250 buf += n_max_chars; /* past buffer's end */
241 char eof_str_detected = 0;
242 char *s = NULL; /* start word */
243 char *p = NULL; /* pointer to end word */
244 size_t line_l = 0; /* size loaded args line */
245 xlist_t *cur;
246 xlist_t *prev;
247 251
248 prev = cur = list_arg;
249 while (1) { 252 while (1) {
250 if (!cur) break; 253 int c = getchar();
251 prev = cur;
252 line_l += cur->length;
253 cur = cur->link;
254 }
255
256 while (!eof_stdin_detected) {
257 c = getchar();
258 if (c == EOF) { 254 if (c == EOF) {
259 eof_stdin_detected = 1; 255 if (p == s)
256 goto ret;
260 } 257 }
261 if (eof_str_detected)
262 continue;
263 if (c == EOF || ISSPACE(c)) { 258 if (c == EOF || ISSPACE(c)) {
264 if (s == NULL) 259 if (p == s)
265 continue; 260 continue;
266 c = EOF; 261 c = EOF;
267 } 262 }
268 if (s == NULL)
269 s = p = buf;
270 if ((size_t)(p - buf) >= mc)
271 bb_error_msg_and_die("argument line too long");
272 *p++ = (c == EOF ? '\0' : c); 263 *p++ = (c == EOF ? '\0' : c);
273 if (c == EOF) { /* word's delimiter or EOF detected */ 264 if (c == EOF) { /* word's delimiter or EOF detected */
274 /* word loaded */ 265 /* A full word is loaded */
275 if (eof_str) { 266 if (G.eof_str) {
276 eof_str_detected = (strcmp(s, eof_str) == 0); 267 if (strcmp(s, G.eof_str) == 0) {
277 } 268 while (getchar() != EOF)
278 if (!eof_str_detected) { 269 continue;
279 size_t length = (p - buf); 270 p = s;
280 /* Dont xzalloc - it can be quite big */ 271 goto ret;
281 cur = xmalloc(offsetof(xlist_t, xstr) + length);
282 cur->link = NULL;
283 cur->length = length;
284 memcpy(cur->xstr, s, length);
285 if (prev == NULL) {
286 list_arg = cur;
287 } else {
288 prev->link = cur;
289 } 272 }
290 prev = cur;
291 line_l += length;
292 if (line_l > mc) {
293 /* stop memory usage :-) */
294 break;
295 }
296 s = NULL;
297 } 273 }
274 store_param(s);
275 dbg_msg("args[]:'%s'", s);
276 s = p;
277 n_max_arg--;
278 if (n_max_arg == 0) {
279 goto ret;
280 }
281 }
282 if (p == buf) {
283 goto ret;
298 } 284 }
299 } 285 }
300 return list_arg; 286 ret:
287 *p = '\0';
288 /* store_param(NULL) - caller will do it */
289 dbg_msg("return:'%s'", s);
290 return s;
301} 291}
302#endif /* FEATURE_XARGS_SUPPORT_QUOTES */ 292#endif /* FEATURE_XARGS_SUPPORT_QUOTES */
303 293
294#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
295static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
296{
297 char *s = buf; /* start of the word */
298 char *p = s + strlen(buf); /* end of the word */
299
300 buf += n_max_chars; /* past buffer's end */
301
302 while (1) {
303 int c = getchar();
304 if (c == EOF) {
305 if (p == s)
306 goto ret;
307 c = '\0';
308 }
309 *p++ = c;
310 if (c == '\0') { /* word's delimiter or EOF detected */
311 /* A full word is loaded */
312 store_param(s);
313 dbg_msg("args[]:'%s'", s);
314 s = p;
315 n_max_arg--;
316 if (n_max_arg == 0) {
317 goto ret;
318 }
319 }
320 if (p == buf) {
321 goto ret;
322 }
323 }
324 ret:
325 *p = '\0';
326 /* store_param(NULL) - caller will do it */
327 dbg_msg("return:'%s'", s);
328 return s;
329}
330#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
304 331
305#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 332#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
306/* Prompt the user for a response, and 333/* Prompt the user for a response, and
@@ -322,65 +349,30 @@ static int xargs_ask_confirmation(void)
322} 349}
323#else 350#else
324# define xargs_ask_confirmation() 1 351# define xargs_ask_confirmation() 1
325#endif /* FEATURE_XARGS_SUPPORT_CONFIRMATION */ 352#endif
326
327#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
328static xlist_t *process0_stdin(xlist_t *list_arg,
329 const char *eof_str UNUSED_PARAM, size_t mc, char *buf)
330{
331 int c; /* current char */
332 char *s = NULL; /* start word */
333 char *p = NULL; /* pointer to end word */
334 size_t line_l = 0; /* size loaded args line */
335 xlist_t *cur;
336 xlist_t *prev;
337
338 prev = cur = list_arg;
339 while (1) {
340 if (!cur) break;
341 prev = cur;
342 line_l += cur->length;
343 cur = cur->link;
344 }
345 353
346 while (!eof_stdin_detected) { 354//usage:#define xargs_trivial_usage
347 c = getchar(); 355//usage: "[OPTIONS] [PROG ARGS]"
348 if (c == EOF) { 356//usage:#define xargs_full_usage "\n\n"
349 eof_stdin_detected = 1; 357//usage: "Run PROG on every item given by stdin\n"
350 if (s == NULL) 358//usage: "\nOptions:"
351 break; 359//usage: IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(
352 c = '\0'; 360//usage: "\n -p Ask user whether to run each command"
353 } 361//usage: )
354 if (s == NULL) 362//usage: "\n -r Don't run command if input is empty"
355 s = p = buf; 363//usage: IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(
356 if ((size_t)(p - buf) >= mc) 364//usage: "\n -0 Input is separated by NUL characters"
357 bb_error_msg_and_die("argument line too long"); 365//usage: )
358 *p++ = c; 366//usage: "\n -t Print the command on stderr before execution"
359 if (c == '\0') { /* word's delimiter or EOF detected */ 367//usage: "\n -e[STR] STR stops input processing"
360 /* word loaded */ 368//usage: "\n -n N Pass no more than N args to PROG"
361 size_t length = (p - buf); 369//usage: "\n -s N Pass command line of no more than N bytes"
362 /* Dont xzalloc - it can be quite big */ 370//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT(
363 cur = xmalloc(offsetof(xlist_t, xstr) + length); 371//usage: "\n -x Exit if size is exceeded"
364 cur->link = NULL; 372//usage: )
365 cur->length = length; 373//usage:#define xargs_example_usage
366 memcpy(cur->xstr, s, length); 374//usage: "$ ls | xargs gzip\n"
367 if (prev == NULL) { 375//usage: "$ find . -name '*.c' -print | xargs rm\n"
368 list_arg = cur;
369 } else {
370 prev->link = cur;
371 }
372 prev = cur;
373 line_l += length;
374 if (line_l > mc) {
375 /* stop memory usage :-) */
376 break;
377 }
378 s = NULL;
379 }
380 }
381 return list_arg;
382}
383#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
384 376
385/* Correct regardless of combination of CONFIG_xxx */ 377/* Correct regardless of combination of CONFIG_xxx */
386enum { 378enum {
@@ -412,44 +404,42 @@ enum {
412int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 404int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
413int xargs_main(int argc, char **argv) 405int xargs_main(int argc, char **argv)
414{ 406{
415 char **args; 407 int i;
416 int i, n;
417 xlist_t *list = NULL;
418 xlist_t *cur;
419 int child_error = 0; 408 int child_error = 0;
420 char *max_args, *max_chars; 409 char *max_args;
421 int n_max_arg; 410 char *max_chars;
422 const char *eof_str = NULL; 411 char *buf;
423 unsigned opt; 412 unsigned opt;
424 size_t n_max_chars; 413 int n_max_chars;
414 int n_max_arg;
425#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 415#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
426 xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin; 416 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
427#else 417#else
428#define read_args process_stdin 418#define read_args process_stdin
429#endif 419#endif
430 420
431 opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &eof_str, &eof_str); 421 INIT_G();
422
423 G.eof_str = NULL;
424 opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str);
432 425
433 /* -E ""? You may wonder why not just omit -E? 426 /* -E ""? You may wonder why not just omit -E?
434 * This is used for portability: 427 * This is used for portability:
435 * old xargs was using "_" as default for -E / -e */ 428 * old xargs was using "_" as default for -E / -e */
436 if ((opt & OPT_EOF_STRING1) && eof_str[0] == '\0') 429 if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
437 eof_str = NULL; 430 G.eof_str = NULL;
438 431
439 if (opt & OPT_ZEROTERM) 432 if (opt & OPT_ZEROTERM)
440 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); 433 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
441 434
442 argv += optind; 435 argv += optind;
443 argc -= optind; 436 argc -= optind;
444 if (!argc) { 437 if (!argv[0]) {
445 /* default behavior is to echo all the filenames */ 438 /* default behavior is to echo all the filenames */
446 *argv = (char*)"echo"; 439 *--argv = (char*)"echo";
447 argc++; 440 argc++;
448 } 441 }
449 442
450 n_max_chars = ARG_MAX; /* might be calling sysconf(_SC_ARG_MAX) */
451 if (n_max_chars < 4*1024); /* paranoia */
452 n_max_chars = LONG_MAX;
453 /* The Open Group Base Specifications Issue 6: 443 /* The Open Group Base Specifications Issue 6:
454 * "The xargs utility shall limit the command line length such that 444 * "The xargs utility shall limit the command line length such that
455 * when the command line is invoked, the combined argument 445 * when the command line is invoked, the combined argument
@@ -457,101 +447,94 @@ int xargs_main(int argc, char **argv)
457 * in the System Interfaces volume of IEEE Std 1003.1-2001) 447 * in the System Interfaces volume of IEEE Std 1003.1-2001)
458 * shall not exceed {ARG_MAX}-2048 bytes". 448 * shall not exceed {ARG_MAX}-2048 bytes".
459 */ 449 */
450 n_max_chars = ARG_MAX; /* might be calling sysconf(_SC_ARG_MAX) */
451 if (n_max_chars < 4*1024); /* paranoia */
452 n_max_chars = 4*1024;
460 n_max_chars -= 2048; 453 n_max_chars -= 2048;
461 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which 454 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
462 * have it at 1 meg). Things will work fine with a large ARG_MAX but it 455 * have it at 1 meg). Things will work fine with a large ARG_MAX
463 * will probably hurt the system more than it needs to; an array of this 456 * but it will probably hurt the system more than it needs to;
464 * size is allocated. 457 * an array of this size is allocated.
465 */ 458 */
466 if (n_max_chars > 20 * 1024) 459 if (n_max_chars > 20 * 1024)
467 n_max_chars = 20 * 1024; 460 n_max_chars = 20 * 1024;
468 461
469 if (opt & OPT_UPTO_SIZE) { 462 if (opt & OPT_UPTO_SIZE) {
470 size_t n_chars = 0; 463 size_t n_chars = 0;
471 n_max_chars = xatoul_range(max_chars, 1, n_max_chars); 464 n_max_chars = xatou_range(max_chars, 1, INT_MAX);
472 for (i = 0; i < argc; i++) { 465 for (i = 0; argv[i]; i++) {
473 n_chars += strlen(*argv) + 1; 466 n_chars += strlen(argv[i]) + 1;
474 } 467 }
475 if (n_max_chars <= n_chars) { 468 n_max_chars -= n_chars;
469 if (n_max_chars <= 0) {
476 bb_error_msg_and_die("can't fit single argument within argument list size limit"); 470 bb_error_msg_and_die("can't fit single argument within argument list size limit");
477 } 471 }
478 n_max_chars -= n_chars;
479 } 472 }
480 max_chars = xmalloc(n_max_chars);
481 473
474 buf = xzalloc(n_max_chars + 1);
475
476 n_max_arg = n_max_chars;
482 if (opt & OPT_UPTO_NUMBER) { 477 if (opt & OPT_UPTO_NUMBER) {
483 n_max_arg = xatoul_range(max_args, 1, INT_MAX); 478 n_max_arg = xatou_range(max_args, 1, INT_MAX);
484 } else {
485 n_max_arg = n_max_chars;
486 } 479 }
487 480
488 while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL || 481 /* Allocate pointers for execvp */
489 !(opt & OPT_NO_EMPTY)) 482 /* We can statically allocate (argc + n_max_arg + 1) elements
490 { 483 * and do not bother with resizing args[], but on 64-bit machines
491 size_t n_chars = 0; 484 * this results in args[] vector which is ~8 times bigger
492 opt |= OPT_NO_EMPTY; 485 * than n_max_chars! That is, with n_max_chars == 20k,
493 n = 0; 486 * args[] will take 160k (!), which will most likely be
494#if ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 487 * almost entirely unused.
495 for (cur = list; cur;) { 488 */
496 n_chars += cur->length; 489 /* See store_param() for matching 256-step growth logic */
497 n++; 490 G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
498 cur = cur->link; 491
499 if (n_chars > n_max_chars || (n == n_max_arg && cur)) { 492 /* Store the command to be executed, part 1 */
500 if (opt & OPT_TERMINATE) 493 for (i = 0; argv[i]; i++)
501 bb_error_msg_and_die("argument list too long"); 494 G.args[i] = argv[i];
502 break; 495
503 } 496 while (1) {
504 } 497 char *rem;
505#else 498
506 for (cur = list; cur; cur = cur->link) { 499 G.idx = argc;
507 n_chars += cur->length; 500 rem = read_args(n_max_chars, n_max_arg, buf);
508 n++; 501 store_param(NULL);
509 if (n_chars > n_max_chars || n == n_max_arg) { 502
503 if (!G.args[argc]) {
504 if (*rem != '\0')
505 bb_error_msg_and_die("argument line too long");
506 if (opt & OPT_NO_EMPTY)
510 break; 507 break;
511 }
512 }
513#endif /* FEATURE_XARGS_SUPPORT_TERMOPT */
514
515 /* allocate pointers for execvp:
516 argc*arg, n*arg from stdin, NULL */
517 args = xzalloc((n + argc + 1) * sizeof(char *));
518
519 /* store the command to be executed
520 (taken from the command line) */
521 for (i = 0; i < argc; i++)
522 args[i] = argv[i];
523 /* (taken from stdin) */
524 for (cur = list; n; cur = cur->link) {
525 args[i++] = cur->xstr;
526 n--;
527 } 508 }
509 opt |= OPT_NO_EMPTY;
528 510
529 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { 511 if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
512 const char *fmt = " %s" + 1;
513 char **args = G.args;
530 for (i = 0; args[i]; i++) { 514 for (i = 0; args[i]; i++) {
531 if (i) 515 fprintf(stderr, fmt, args[i]);
532 bb_putchar_stderr(' '); 516 fmt = " %s";
533 fputs(args[i], stderr);
534 } 517 }
535 if (!(opt & OPT_INTERACTIVE)) 518 if (!(opt & OPT_INTERACTIVE))
536 bb_putchar_stderr('\n'); 519 bb_putchar_stderr('\n');
537 } 520 }
521
538 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { 522 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
539 child_error = xargs_exec(args); 523 child_error = xargs_exec();
540 } 524 }
541 525
542 /* clean up */
543 for (i = argc; args[i]; i++) {
544 cur = list;
545 list = list->link;
546 free(cur);
547 }
548 free(args);
549 if (child_error > 0 && child_error != 123) { 526 if (child_error > 0 && child_error != 123) {
550 break; 527 break;
551 } 528 }
529
530 overlapping_strcpy(buf, rem);
552 } /* while */ 531 } /* while */
553 if (ENABLE_FEATURE_CLEAN_UP) 532
554 free(max_chars); 533 if (ENABLE_FEATURE_CLEAN_UP) {
534 free(G.args);
535 free(buf);
536 }
537
555 return child_error; 538 return child_error;
556} 539}
557 540