Remove bb_ prefixes from xfuncs.c (and a few other places), consolidate
[platform/upstream/busybox.git] / findutils / xargs.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini xargs implementation for busybox
4  * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
5  *
6  * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
7  *
8  * Special thanks
9  * - Mark Whitley and Glenn McGrath for stimulus to rewrite :)
10  * - Mike Rendell <michael@cs.mun.ca>
11  * and David MacKenzie <djm@gnu.ai.mit.edu>.
12  *
13  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
14  *
15  * xargs is described in the Single Unix Specification v3 at
16  * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
17  *
18  */
19
20 #include "busybox.h"
21
22 /* COMPAT:  SYSV version defaults size (and has a max value of) to 470.
23    We try to make it as large as possible. */
24 #if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
25 #define ARG_MAX sysconf (_SC_ARG_MAX)
26 #endif
27 #ifndef ARG_MAX
28 #define ARG_MAX 470
29 #endif
30
31
32 #ifdef TEST
33 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
34 #  define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
35 # endif
36 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
37 #  define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
38 # endif
39 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
40 #  define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
41 # endif
42 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
43 #  define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
44 # endif
45 #endif
46
47 /*
48    This function have special algorithm.
49    Don`t use fork and include to main!
50 */
51 static int xargs_exec(char *const *args)
52 {
53         pid_t p;
54         volatile int exec_errno = 0;    /* shared vfork stack */
55
56         if ((p = vfork()) >= 0) {
57                 if (p == 0) {
58                         /* vfork -- child */
59                         execvp(args[0], args);
60                         exec_errno = errno;     /* set error to shared stack */
61                         _exit(1);
62                 } else {
63                         /* vfork -- parent */
64                         int status;
65
66                         while (wait(&status) == (pid_t) - 1)
67                                 if (errno != EINTR)
68                                         break;
69                         if (exec_errno) {
70                                 errno = exec_errno;
71                                 bb_perror_msg("%s", args[0]);
72                                 return exec_errno == ENOENT ? 127 : 126;
73                         } else {
74                                 if (WEXITSTATUS(status) == 255) {
75                                         bb_error_msg("%s: exited with status 255; aborting", args[0]);
76                                         return 124;
77                                 }
78                                 if (WIFSTOPPED(status)) {
79                                         bb_error_msg("%s: stopped by signal %d",
80                                                 args[0], WSTOPSIG(status));
81                                         return 125;
82                                 }
83                                 if (WIFSIGNALED(status)) {
84                                         bb_error_msg("%s: terminated by signal %d",
85                                                 args[0], WTERMSIG(status));
86                                         return 125;
87                                 }
88                                 if (WEXITSTATUS(status) != 0)
89                                         return 123;
90                                 return 0;
91                         }
92                 }
93         } else {
94                 bb_perror_msg_and_die("vfork");
95         }
96 }
97
98
99 typedef struct xlist_s {
100         char *data;
101         size_t lenght;
102         struct xlist_s *link;
103 } xlist_t;
104
105 static int eof_stdin_detected;
106
107 #define ISBLANK(c) ((c) == ' ' || (c) == '\t')
108 #define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
109                     || (c) == '\f' || (c) == '\v')
110
111 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
112 static xlist_t *process_stdin(xlist_t * list_arg,
113         const char *eof_str, size_t mc, char *buf)
114 {
115 #define NORM      0
116 #define QUOTE     1
117 #define BACKSLASH 2
118 #define SPACE     4
119
120         char *s = NULL;         /* start word */
121         char *p = NULL;         /* pointer to end word */
122         char q = 0;             /* quote char */
123         char state = NORM;
124         char eof_str_detected = 0;
125         size_t line_l = 0;      /* size loaded args line */
126         int c;                  /* current char */
127         xlist_t *cur;
128         xlist_t *prev;
129
130         for (prev = cur = list_arg; cur; cur = cur->link) {
131                 line_l += cur->lenght;  /* previous allocated */
132                 if (prev != cur)
133                         prev = prev->link;
134         }
135
136         while (!eof_stdin_detected) {
137                 c = getchar();
138                 if (c == EOF) {
139                         eof_stdin_detected++;
140                         if (s)
141                                 goto unexpected_eof;
142                         break;
143                 }
144                 if (eof_str_detected)
145                         continue;
146                 if (state == BACKSLASH) {
147                         state = NORM;
148                         goto set;
149                 } else if (state == QUOTE) {
150                         if (c == q) {
151                                 q = 0;
152                                 state = NORM;
153                         } else {
154                                 goto set;
155                         }
156                 } else { /* if(state == NORM) */
157
158                         if (ISSPACE(c)) {
159                                 if (s) {
160 unexpected_eof:
161                                         state = SPACE;
162                                         c = 0;
163                                         goto set;
164                                 }
165                         } else {
166                                 if (s == NULL)
167                                         s = p = buf;
168                                 if (c == '\\') {
169                                         state = BACKSLASH;
170                                 } else if (c == '\'' || c == '"') {
171                                         q = c;
172                                         state = QUOTE;
173                                 } else {
174 set:
175                                         if ((size_t)(p - buf) >= mc)
176                                                 bb_error_msg_and_die("argument line too long");
177                                         *p++ = c;
178                                 }
179                         }
180                 }
181                 if (state == SPACE) {   /* word's delimiter or EOF detected */
182                         if (q) {
183                                 bb_error_msg_and_die("unmatched %s quote",
184                                         q == '\'' ? "single" : "double");
185                         }
186                         /* word loaded */
187                         if (eof_str) {
188                                 eof_str_detected = strcmp(s, eof_str) == 0;
189                         }
190                         if (!eof_str_detected) {
191                                 size_t lenght = (p - buf);
192
193                                 cur = xmalloc(sizeof(xlist_t) + lenght);
194                                 cur->data = memcpy(cur + 1, s, lenght);
195                                 cur->lenght = lenght;
196                                 cur->link = NULL;
197                                 if (prev == NULL) {
198                                         list_arg = cur;
199                                 } else {
200                                         prev->link = cur;
201                                 }
202                                 prev = cur;
203                                 line_l += lenght;
204                                 if (line_l > mc) {
205                                         /* stop memory usage :-) */
206                                         break;
207                                 }
208                         }
209                         s = NULL;
210                         state = NORM;
211                 }
212         }
213         return list_arg;
214 }
215 #else
216 /* The variant does not support single quotes, double quotes or backslash */
217 static xlist_t *process_stdin(xlist_t * list_arg,
218         const char *eof_str, size_t mc, char *buf)
219 {
220
221         int c;                  /* current char */
222         int eof_str_detected = 0;
223         char *s = NULL;         /* start word */
224         char *p = NULL;         /* pointer to end word */
225         size_t line_l = 0;      /* size loaded args line */
226         xlist_t *cur;
227         xlist_t *prev;
228
229         for (prev = cur = list_arg; cur; cur = cur->link) {
230                 line_l += cur->lenght;  /* previous allocated */
231                 if (prev != cur)
232                         prev = prev->link;
233         }
234
235         while (!eof_stdin_detected) {
236                 c = getchar();
237                 if (c == EOF) {
238                         eof_stdin_detected++;
239                 }
240                 if (eof_str_detected)
241                         continue;
242                 if (c == EOF || ISSPACE(c)) {
243                         if (s == NULL)
244                                 continue;
245                         c = EOF;
246                 }
247                 if (s == NULL)
248                         s = p = buf;
249                 if ((p - buf) >= mc)
250                         bb_error_msg_and_die("argument line too long");
251                 *p++ = c == EOF ? 0 : c;
252                 if (c == EOF) { /* word's delimiter or EOF detected */
253                         /* word loaded */
254                         if (eof_str) {
255                                 eof_str_detected = strcmp(s, eof_str) == 0;
256                         }
257                         if (!eof_str_detected) {
258                                 size_t lenght = (p - buf);
259
260                                 cur = xmalloc(sizeof(xlist_t) + lenght);
261                                 cur->data = memcpy(cur + 1, s, lenght);
262                                 cur->lenght = lenght;
263                                 cur->link = NULL;
264                                 if (prev == NULL) {
265                                         list_arg = cur;
266                                 } else {
267                                         prev->link = cur;
268                                 }
269                                 prev = cur;
270                                 line_l += lenght;
271                                 if (line_l > mc) {
272                                         /* stop memory usage :-) */
273                                         break;
274                                 }
275                                 s = NULL;
276                         }
277                 }
278         }
279         return list_arg;
280 }
281 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
282
283
284 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
285 /* Prompt the user for a response, and
286    if the user responds affirmatively, return true;
287    otherwise, return false. Used "/dev/tty", not stdin. */
288 static int xargs_ask_confirmation(void)
289 {
290         static FILE *tty_stream;
291         int c, savec;
292
293         if (!tty_stream) {
294                 tty_stream = xfopen(CURRENT_TTY, "r");
295                 /* pranoidal security by vodz */
296                 fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
297         }
298         fputs(" ?...", stderr);
299         fflush(stderr);
300         c = savec = getc(tty_stream);
301         while (c != EOF && c != '\n')
302                 c = getc(tty_stream);
303         if (savec == 'y' || savec == 'Y')
304                 return 1;
305         return 0;
306 }
307
308 # define OPT_INC_P 1
309 #else
310 # define OPT_INC_P 0
311 # define xargs_ask_confirmation() 1
312 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
313
314 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
315 # define OPT_INC_X 1
316 #else
317 # define OPT_INC_X 0
318 #endif
319
320 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
321 static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str ATTRIBUTE_UNUSED,
322                                                            size_t mc, char *buf)
323 {
324         int c;                  /* current char */
325         char *s = NULL;         /* start word */
326         char *p = NULL;         /* pointer to end word */
327         size_t line_l = 0;      /* size loaded args line */
328         xlist_t *cur;
329         xlist_t *prev;
330
331         for (prev = cur = list_arg; cur; cur = cur->link) {
332                 line_l += cur->lenght;  /* previous allocated */
333                 if (prev != cur)
334                         prev = prev->link;
335         }
336
337         while (!eof_stdin_detected) {
338                 c = getchar();
339                 if (c == EOF) {
340                         eof_stdin_detected++;
341                         if (s == NULL)
342                                 break;
343                         c = 0;
344                 }
345                 if (s == NULL)
346                         s = p = buf;
347                 if ((size_t)(p - buf) >= mc)
348                         bb_error_msg_and_die("argument line too long");
349                 *p++ = c;
350                 if (c == 0) {   /* word's delimiter or EOF detected */
351                         /* word loaded */
352                         size_t lenght = (p - buf);
353
354                         cur = xmalloc(sizeof(xlist_t) + lenght);
355                         cur->data = memcpy(cur + 1, s, lenght);
356                         cur->lenght = lenght;
357                         cur->link = NULL;
358                         if (prev == NULL) {
359                                 list_arg = cur;
360                         } else {
361                                 prev->link = cur;
362                         }
363                         prev = cur;
364                         line_l += lenght;
365                         if (line_l > mc) {
366                                 /* stop memory usage :-) */
367                                 break;
368                         }
369                         s = NULL;
370                 }
371         }
372         return list_arg;
373 }
374
375 # define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc)
376 # define OPT_INC_0 1    /* future use */
377 #else
378 # define OPT_INC_0 0    /* future use */
379 # define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc)
380 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
381
382
383 #define OPT_VERBOSE     (1<<0)
384 #define OPT_NO_EMPTY    (1<<1)
385 #define OPT_UPTO_NUMBER (1<<2)
386 #define OPT_UPTO_SIZE   (1<<3)
387 #define OPT_EOF_STRING  (1<<4)
388 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
389 #define OPT_INTERACTIVE (1<<5)
390 #else
391 #define OPT_INTERACTIVE (0)     /* require for algorithm &| */
392 #endif
393 #define OPT_TERMINATE   (1<<(5+OPT_INC_P))
394 #define OPT_ZEROTERM    (1<<(5+OPT_INC_P+OPT_INC_X))
395 /* next future
396 #define OPT_NEXT_OTHER  (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0))
397 */
398
399 int xargs_main(int argc, char **argv)
400 {
401         char **args;
402         int i, a, n;
403         xlist_t *list = NULL;
404         xlist_t *cur;
405         int child_error = 0;
406         char *max_args, *max_chars;
407         int n_max_arg;
408         size_t n_chars = 0;
409         long orig_arg_max;
410         const char *eof_str = "_";
411         unsigned long opt;
412         size_t n_max_chars;
413
414 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
415         xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin;
416 #endif
417
418 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
419         bb_opt_complementally = "pt";
420 #endif
421
422         opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::"
423 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
424         "p"
425 #endif
426 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
427         "x"
428 #endif
429 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
430         "0"
431 #endif
432         ,&max_args, &max_chars, &eof_str);
433
434         a = argc - optind;
435         argv += optind;
436         if (a == 0) {
437                 /* default behavior is to echo all the filenames */
438                 *argv = "echo";
439                 a++;
440         }
441
442         orig_arg_max = ARG_MAX;
443         if (orig_arg_max == -1)
444                 orig_arg_max = LONG_MAX;
445         orig_arg_max -= 2048;   /* POSIX.2 requires subtracting 2048.  */
446         if ((opt & OPT_UPTO_SIZE)) {
447                 n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max);
448                 for (i = 0; i < a; i++) {
449                         n_chars += strlen(*argv) + 1;
450                 }
451                 if (n_max_chars < n_chars) {
452                         bb_error_msg_and_die("can not fit single argument within argument list size limit");
453                 }
454                 n_max_chars -= n_chars;
455         } else {
456                 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
457                    have it at 1 meg).  Things will work fine with a large ARG_MAX but it
458                    will probably hurt the system more than it needs to; an array of this
459                    size is allocated.  */
460                 if (orig_arg_max > 20 * 1024)
461                         orig_arg_max = 20 * 1024;
462                 n_max_chars = orig_arg_max;
463         }
464         max_chars = xmalloc(n_max_chars);
465
466         if ((opt & OPT_UPTO_NUMBER)) {
467                 n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX);
468         } else {
469                 n_max_arg = n_max_chars;
470         }
471
472 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
473         if (opt & OPT_ZEROTERM)
474                 read_args = process0_stdin;
475 #endif
476
477         while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL ||
478                 (opt & OPT_NO_EMPTY) == 0)
479         {
480                 opt |= OPT_NO_EMPTY;
481                 n = 0;
482                 n_chars = 0;
483 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
484                 for (cur = list; cur;) {
485                         n_chars += cur->lenght;
486                         n++;
487                         cur = cur->link;
488                         if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
489                                 if (opt & OPT_TERMINATE)
490                                         bb_error_msg_and_die("argument list too long");
491                                 break;
492                         }
493                 }
494 #else
495                 for (cur = list; cur; cur = cur->link) {
496                         n_chars += cur->lenght;
497                         n++;
498                         if (n_chars > n_max_chars || n == n_max_arg) {
499                                 break;
500                         }
501                 }
502 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
503
504                 /* allocating pointers for execvp:
505                    a*arg, n*arg from stdin, NULL */
506                 args = xcalloc(n + a + 1, sizeof(char *));
507
508                 /* Store the command to be executed
509                    (taken from the command line) */
510                 for (i = 0; i < a; i++)
511                         args[i] = argv[i];
512                 /* (taken from stdin) */
513                 for (cur = list; n; cur = cur->link) {
514                         args[i++] = cur->data;
515                         n--;
516                 }
517
518                 if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) {
519                         for (i = 0; args[i]; i++) {
520                                 if (i)
521                                         fputc(' ', stderr);
522                                 fputs(args[i], stderr);
523                         }
524                         if ((opt & OPT_INTERACTIVE) == 0)
525                                 fputc('\n', stderr);
526                 }
527                 if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) {
528                         child_error = xargs_exec(args);
529                 }
530
531                 /* clean up */
532                 for (i = a; args[i]; i++) {
533                         cur = list;
534                         list = list->link;
535                         free(cur);
536                 }
537                 free(args);
538                 if (child_error > 0 && child_error != 123) {
539                         break;
540                 }
541         }
542 #ifdef CONFIG_FEATURE_CLEAN_UP
543         free(max_chars);
544 #endif
545         return child_error;
546 }
547
548
549 #ifdef TEST
550
551 const char *bb_applet_name = "debug stuff usage";
552
553 void bb_show_usage(void)
554 {
555         fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
556                 bb_applet_name);
557         exit(1);
558 }
559
560 int main(int argc, char **argv)
561 {
562         return xargs_main(argc, argv);
563 }
564 #endif /* TEST */