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