Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/findutils
- Timestamp:
- Dec 20, 2016, 4:07:32 PM (7 years ago)
- Location:
- branches/3.3
- Files:
-
- 3 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
branches/3.3/mindi-busybox/findutils/find.c
r3232 r3621 138 138 //config: the files matched. 139 139 //config: 140 //config:config FEATURE_FIND_EXEC_PLUS 141 //config: bool "Enable -exec ... {} +" 142 //config: default y 143 //config: depends on FEATURE_FIND_EXEC 144 //config: help 145 //config: Support the 'find -exec ... {} +' option for executing commands 146 //config: for all matched files at once. 147 //config: Without this option, -exec + is a synonym for -exec ; 148 //config: (IOW: it works correctly, but without expected speedup) 149 //config: 140 150 //config:config FEATURE_FIND_USER 141 151 //config: bool "Enable -user: username/uid matching" … … 232 242 233 243 //usage:#define find_trivial_usage 234 //usage: "[ PATH]... [OPTIONS] [ACTIONS]"244 //usage: "[-HL] [PATH]... [OPTIONS] [ACTIONS]" 235 245 //usage:#define find_full_usage "\n\n" 236 246 //usage: "Search for files and perform actions on them.\n" 237 247 //usage: "First failed action stops processing of current file.\n" 238 248 //usage: "Defaults: PATH is current directory, action is '-print'\n" 239 //usage: "\n -follow Follow symlinks" 249 //usage: "\n -L,-follow Follow symlinks" 250 //usage: "\n -H ...on command line only" 240 251 //usage: IF_FEATURE_FIND_XDEV( 241 252 //usage: "\n -xdev Don't descend directories on other filesystems" … … 319 330 //usage: "\n file name. Fails if CMD exits with nonzero" 320 331 //usage: ) 332 //usage: IF_FEATURE_FIND_EXEC_PLUS( 333 //usage: "\n -exec CMD ARG + Run CMD with {} replaced by list of file names" 334 //usage: ) 321 335 //usage: IF_FEATURE_FIND_DELETE( 322 336 //usage: "\n -delete Delete current file/directory. Turns on -depth option" … … 329 343 #include <fnmatch.h> 330 344 #include "libbb.h" 345 #include "common_bufsiz.h" 331 346 #if ENABLE_FEATURE_FIND_REGEX 332 347 # include "xregex.h" … … 337 352 #endif 338 353 339 #define dbg(...) ((void)0) 340 /* #define dbg(...) bb_error_msg(__VA_ARGS__) */ 354 #if 1 355 # define dbg(...) ((void)0) 356 #else 357 # define dbg(...) bb_error_msg(__VA_ARGS__) 358 #endif 359 341 360 342 361 /* This is a NOEXEC applet. Be very careful! */ … … 375 394 IF_FEATURE_FIND_PRUNE( ACTS(prune)) 376 395 IF_FEATURE_FIND_DELETE( ACTS(delete)) 377 IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;)) 396 IF_FEATURE_FIND_EXEC( ACTS(exec, 397 char **exec_argv; /* -exec ARGS */ 398 unsigned *subst_count; 399 int exec_argc; /* count of ARGS */ 400 IF_FEATURE_FIND_EXEC_PLUS( 401 /* 402 * filelist is NULL if "exec ;" 403 * non-NULL if "exec +" 404 */ 405 char **filelist; 406 int filelist_idx; 407 int file_len; 408 ) 409 )) 378 410 IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;)) 379 411 IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;)) … … 389 421 smallint xdev_on; 390 422 recurse_flags_t recurse_flags; 423 IF_FEATURE_FIND_EXEC_PLUS(unsigned max_argv_len;) 391 424 } FIX_ALIASING; 392 #define G (*(struct globals*) &bb_common_bufsiz1)425 #define G (*(struct globals*)bb_common_bufsiz1) 393 426 #define INIT_G() do { \ 394 struct G_sizecheck { \ 395 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ 396 }; \ 427 setup_common_bufsiz(); \ 428 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ 397 429 /* we have to zero it out because of NOEXEC */ \ 398 430 memset(&G, 0, sizeof(G)); \ 399 431 IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \ 432 IF_FEATURE_FIND_EXEC_PLUS(G.max_argv_len = bb_arg_max() - 2048;) \ 400 433 G.need_print = 1; \ 401 434 G.recurse_flags = ACTION_RECURSE; \ 402 435 } while (0) 403 404 #if ENABLE_FEATURE_FIND_EXEC405 static unsigned count_subst(const char *str)406 {407 unsigned count = 0;408 while ((str = strstr(str, "{}")) != NULL) {409 count++;410 str++;411 }412 return count;413 }414 415 416 static char* subst(const char *src, unsigned count, const char* filename)417 {418 char *buf, *dst, *end;419 size_t flen = strlen(filename);420 /* we replace each '{}' with filename: growth by strlen-2 */421 buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);422 while ((end = strstr(src, "{}"))) {423 memcpy(dst, src, end - src);424 dst += end - src;425 src = end + 2;426 memcpy(dst, filename, flen);427 dst += flen;428 }429 strcpy(dst, src);430 return buf;431 }432 #endif433 436 434 437 /* Return values of ACTFs ('action functions') are a bit mask: … … 482 485 } 483 486 484 485 487 #if !FNM_CASEFOLD 486 488 static char *strcpy_upcase(char *dst, const char *src) … … 557 559 ACTF(perm) 558 560 { 559 /* -perm +mode: at least one of perm_mask bits are set */560 if (ap->perm_char == '+' )561 /* -perm [+/]mode: at least one of perm_mask bits are set */ 562 if (ap->perm_char == '+' || ap->perm_char == '/') 561 563 return (statbuf->st_mode & ap->perm_mask) != 0; 562 564 /* -perm -mode: all of perm_mask are set */ … … 606 608 #endif 607 609 #if ENABLE_FEATURE_FIND_EXEC 608 ACTF(exec)610 static int do_exec(action_exec *ap, const char *fileName) 609 611 { 610 612 int i, rc; 611 #if ENABLE_USE_PORTABLE_CODE 612 char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1)); 613 #else /* gcc 4.3.1 generates smaller code: */ 614 char *argv[ap->exec_argc + 1]; 615 #endif 616 for (i = 0; i < ap->exec_argc; i++) 617 argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName); 618 argv[i] = NULL; /* terminate the list */ 613 # if ENABLE_FEATURE_FIND_EXEC_PLUS 614 int size = ap->exec_argc + ap->filelist_idx + 1; 615 # else 616 int size = ap->exec_argc + 1; 617 # endif 618 # if ENABLE_USE_PORTABLE_CODE 619 char **argv = alloca(sizeof(char*) * size); 620 # else /* gcc 4.3.1 generates smaller code: */ 621 char *argv[size]; 622 # endif 623 char **pp = argv; 624 625 for (i = 0; i < ap->exec_argc; i++) { 626 const char *arg = ap->exec_argv[i]; 627 628 # if ENABLE_FEATURE_FIND_EXEC_PLUS 629 if (ap->filelist) { 630 /* Handling "-exec +" 631 * Only one exec_argv[i] has substitution in it. 632 * Expand that one exec_argv[i] into file list. 633 */ 634 if (ap->subst_count[i] == 0) { 635 *pp++ = xstrdup(arg); 636 } else { 637 int j = 0; 638 while (ap->filelist[j]) { 639 /* 2nd arg here should be ap->subst_count[i], but it is always 1: */ 640 *pp++ = xmalloc_substitute_string(arg, 1, "{}", ap->filelist[j]); 641 free(ap->filelist[j]); 642 j++; 643 } 644 } 645 } else 646 # endif 647 { 648 /* Handling "-exec ;" */ 649 *pp++ = xmalloc_substitute_string(arg, ap->subst_count[i], "{}", fileName); 650 } 651 } 652 *pp = NULL; /* terminate the list */ 653 654 # if ENABLE_FEATURE_FIND_EXEC_PLUS 655 if (ap->filelist) { 656 ap->filelist[0] = NULL; 657 ap->filelist_idx = 0; 658 ap->file_len = 0; 659 } 660 # endif 619 661 620 662 rc = spawn_and_wait(argv); … … 627 669 return rc == 0; /* return 1 if exitcode 0 */ 628 670 } 671 ACTF(exec) 672 { 673 # if ENABLE_FEATURE_FIND_EXEC_PLUS 674 if (ap->filelist) { 675 int rc; 676 677 ap->filelist = xrealloc_vector(ap->filelist, 8, ap->filelist_idx); 678 ap->filelist[ap->filelist_idx++] = xstrdup(fileName); 679 ap->file_len += strlen(fileName) + sizeof(char*) + 1; 680 /* If we have lots of files already, exec the command */ 681 rc = 1; 682 if (ap->file_len >= G.max_argv_len) 683 rc = do_exec(ap, NULL); 684 return rc; 685 } 686 # endif 687 return do_exec(ap, fileName); 688 } 689 # if ENABLE_FEATURE_FIND_EXEC_PLUS 690 static int flush_exec_plus(void) 691 { 692 action *ap; 693 action **app; 694 action ***appp = G.actions; 695 while ((app = *appp++) != NULL) { 696 while ((ap = *app++) != NULL) { 697 if (ap->f == (action_fp)func_exec) { 698 action_exec *ae = (void*)ap; 699 if (ae->filelist_idx != 0) { 700 int rc = do_exec(ae, NULL); 701 # if ENABLE_FEATURE_FIND_NOT 702 if (ap->invert) rc = !rc; 703 # endif 704 if (rc == 0) 705 return 1; 706 } 707 } 708 } 709 } 710 return 0; 711 } 712 # endif 629 713 #endif 630 714 #if ENABLE_FEATURE_FIND_USER … … 685 769 int rc; 686 770 if (S_ISDIR(statbuf->st_mode)) { 687 rc = rmdir(fileName); 771 /* "find . -delete" skips rmdir(".") */ 772 rc = 0; 773 if (NOT_LONE_CHAR(fileName, '.')) 774 rc = rmdir(fileName); 688 775 } else { 689 776 rc = unlink(fileName); … … 798 885 799 886 if (mask == 0 || type[1] != '\0') 800 bb_error_msg_and_die(bb_msg_invalid_arg , type, "-type");887 bb_error_msg_and_die(bb_msg_invalid_arg_to, type, "-type"); 801 888 802 889 return mask; … … 812 899 str++; 813 900 return str; 901 } 902 #endif 903 904 /* Say no to GCCism */ 905 #define USE_NESTED_FUNCTION 0 906 907 #if !USE_NESTED_FUNCTION 908 struct pp_locals { 909 action*** appp; 910 unsigned cur_group; 911 unsigned cur_action; 912 IF_FEATURE_FIND_NOT( bool invert_flag; ) 913 }; 914 static action* alloc_action(struct pp_locals *ppl, int sizeof_struct, action_fp f) 915 { 916 action *ap = xzalloc(sizeof_struct); 917 action **app; 918 action ***group = &ppl->appp[ppl->cur_group]; 919 *group = app = xrealloc(*group, (ppl->cur_action+2) * sizeof(ppl->appp[0][0])); 920 app[ppl->cur_action++] = ap; 921 app[ppl->cur_action] = NULL; 922 ap->f = f; 923 IF_FEATURE_FIND_NOT( ap->invert = ppl->invert_flag; ) 924 IF_FEATURE_FIND_NOT( ppl->invert_flag = 0; ) 925 return ap; 814 926 } 815 927 #endif … … 901 1013 ; 902 1014 1015 #if !USE_NESTED_FUNCTION 1016 struct pp_locals ppl; 1017 #define appp (ppl.appp ) 1018 #define cur_group (ppl.cur_group ) 1019 #define cur_action (ppl.cur_action ) 1020 #define invert_flag (ppl.invert_flag) 1021 #define ALLOC_ACTION(name) (action_##name*)alloc_action(&ppl, sizeof(action_##name), (action_fp) func_##name) 1022 #else 903 1023 action*** appp; 904 unsigned cur_group = 0;905 unsigned cur_action = 0;906 IF_FEATURE_FIND_NOT( bool invert_flag = 0; )1024 unsigned cur_group; 1025 unsigned cur_action; 1026 IF_FEATURE_FIND_NOT( bool invert_flag; ) 907 1027 908 1028 /* This is the only place in busybox where we use nested function. … … 913 1033 { 914 1034 action *ap; 915 appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof( *appp));1035 appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(appp[0][0])); 916 1036 appp[cur_group][cur_action++] = ap = xzalloc(sizeof_struct); 917 1037 appp[cur_group][cur_action] = NULL; … … 921 1041 return ap; 922 1042 } 923 924 1043 #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name) 925 1044 #endif 1045 1046 cur_group = 0; 1047 cur_action = 0; 1048 IF_FEATURE_FIND_NOT( invert_flag = 0; ) 926 1049 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */ 927 1050 … … 949 1072 /* Options */ 950 1073 if (parm == OPT_FOLLOW) { 951 952 1074 dbg("follow enabled: %d", __LINE__); 1075 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK; 953 1076 } 954 1077 #if ENABLE_FEATURE_FIND_XDEV … … 988 1111 /* start new OR group */ 989 1112 cur_group++; 990 appp = xrealloc(appp, (cur_group+2) * sizeof( *appp));1113 appp = xrealloc(appp, (cur_group+2) * sizeof(appp[0])); 991 1114 /*appp[cur_group] = NULL; - already NULL */ 992 1115 appp[cur_group+1] = NULL; … … 1031 1154 int i; 1032 1155 action_exec *ap; 1156 IF_FEATURE_FIND_EXEC_PLUS(int all_subst = 0;) 1033 1157 dbg("%d", __LINE__); 1034 1158 G.need_print = 0; … … 1043 1167 // find -exec echo Foo ">{}<" "+" 1044 1168 // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...". 1045 // TODO (so far we treat "+" just like ";")1046 1169 if ((argv[0][0] == ';' || argv[0][0] == '+') 1047 1170 && argv[0][1] == '\0' 1048 1171 ) { 1172 # if ENABLE_FEATURE_FIND_EXEC_PLUS 1173 if (argv[0][0] == '+') 1174 ap->filelist = xzalloc(sizeof(ap->filelist[0])); 1175 # endif 1049 1176 break; 1050 1177 } … … 1056 1183 ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); 1057 1184 i = ap->exec_argc; 1058 while (i--) 1059 ap->subst_count[i] = count_subst(ap->exec_argv[i]); 1185 while (i--) { 1186 ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}"); 1187 IF_FEATURE_FIND_EXEC_PLUS(all_subst += ap->subst_count[i];) 1188 } 1189 # if ENABLE_FEATURE_FIND_EXEC_PLUS 1190 /* 1191 * coreutils expects {} to appear only once in "-exec +" 1192 */ 1193 if (all_subst != 1 && ap->filelist) 1194 bb_error_msg_and_die("only one '{}' allowed for -exec +"); 1195 # endif 1060 1196 } 1061 1197 #endif … … 1120 1256 * Symbolic modes use mode 0 as a point of departure. 1121 1257 * -perm -BITS All of the BITS are set in file's mode. 1122 * -perm +BITS At least one of the BITS is set in file's mode.1258 * -perm [+/]BITS At least one of the BITS is set in file's mode. 1123 1259 */ 1124 1260 else if (parm == PARM_perm) { … … 1127 1263 ap = ALLOC_ACTION(perm); 1128 1264 ap->perm_char = arg1[0]; 1129 arg1 = plus_minus_num(arg1);1265 arg1 = (arg1[0] == '/' ? arg1+1 : plus_minus_num(arg1)); 1130 1266 /*ap->perm_mask = 0; - ALLOC_ACTION did it */ 1131 if (!bb_parse_mode(arg1, &ap->perm_mask)) 1267 ap->perm_mask = bb_parse_mode(arg1, ap->perm_mask); 1268 if (ap->perm_mask == (mode_t)-1) 1132 1269 bb_error_msg_and_die("invalid mode '%s'", arg1); 1133 1270 } … … 1246 1383 return appp; 1247 1384 #undef ALLOC_ACTION 1385 #undef appp 1386 #undef cur_action 1387 #undef invert_flag 1248 1388 } 1249 1389 … … 1252 1392 { 1253 1393 int i, firstopt, status = EXIT_SUCCESS; 1394 char **past_HLP, *saved; 1254 1395 1255 1396 INIT_G(); 1256 1397 1257 argv++; 1398 /* "find -type f" + getopt("+HLP") => disaster. 1399 * Need to avoid getopt running into a non-HLP option. 1400 * Do this by temporarily storing NULL there: 1401 */ 1402 past_HLP = argv; 1403 for (;;) { 1404 saved = *++past_HLP; 1405 if (!saved) 1406 break; 1407 if (saved[0] != '-') 1408 break; 1409 if (!saved[1]) 1410 break; /* it is "-" */ 1411 if ((saved+1)[strspn(saved+1, "HLP")] != '\0') 1412 break; 1413 } 1414 *past_HLP = NULL; 1415 /* "+": stop on first non-option */ 1416 i = getopt32(argv, "+HLP"); 1417 if (i & (1<<0)) 1418 G.recurse_flags |= ACTION_FOLLOWLINKS_L0 | ACTION_DANGLING_OK; 1419 if (i & (1<<1)) 1420 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK; 1421 /* -P is default and is ignored */ 1422 argv = past_HLP; /* same result as "argv += optind;" */ 1423 *past_HLP = saved; 1424 1258 1425 for (firstopt = 0; argv[firstopt]; firstopt++) { 1259 1426 if (argv[firstopt][0] == '-') … … 1298 1465 0) /* depth */ 1299 1466 ) { 1300 status = EXIT_FAILURE; 1301 } 1302 } 1303 1467 status |= EXIT_FAILURE; 1468 } 1469 } 1470 1471 IF_FEATURE_FIND_EXEC_PLUS(status |= flush_exec_plus();) 1304 1472 return status; 1305 1473 } -
branches/3.3/mindi-busybox/findutils/grep.c
r3232 r3621 59 59 60 60 #include "libbb.h" 61 #include "common_bufsiz.h" 61 62 #include "xregex.h" 62 63 … … 202 203 const char *cur_file; /* the current file we are reading */ 203 204 } FIX_ALIASING; 204 #define G (*(struct globals*) &bb_common_bufsiz1)205 #define G (*(struct globals*)bb_common_bufsiz1) 205 206 #define INIT_G() do { \ 206 struct G_sizecheck { \ 207 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ 208 }; \ 207 setup_common_bufsiz(); \ 208 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ 209 209 } while (0) 210 210 #define max_matches (G.max_matches ) … … 374 374 } 375 375 } else { 376 #if ENABLE_EXTRA_COMPAT 377 unsigned start_pos; 378 #else 379 int match_flg; 380 #endif 381 char *match_at; 382 376 383 if (!(gl->flg_mem_alocated_compiled & COMPILED)) { 377 384 gl->flg_mem_alocated_compiled |= COMPILED; … … 388 395 gl->matched_range.rm_so = 0; 389 396 gl->matched_range.rm_eo = 0; 390 #endif 397 match_flg = 0; 398 #else 399 start_pos = 0; 400 #endif 401 match_at = line; 402 opt_w_again: 403 //bb_error_msg("'%s' start_pos:%d line_len:%d", match_at, start_pos, line_len); 391 404 if ( 392 405 #if !ENABLE_EXTRA_COMPAT 393 regexec(&gl->compiled_regex, line, 1, &gl->matched_range, 0) == 0394 #else 395 re_search(&gl->compiled_regex, line, line_len,396 /*start:*/ 0, /*range:*/ line_len,406 regexec(&gl->compiled_regex, match_at, 1, &gl->matched_range, match_flg) == 0 407 #else 408 re_search(&gl->compiled_regex, match_at, line_len, 409 start_pos, /*range:*/ line_len, 397 410 &gl->matched_range) >= 0 398 411 #endif … … 400 413 if (option_mask32 & OPT_x) { 401 414 found = (gl->matched_range.rm_so == 0 402 && line[gl->matched_range.rm_eo] == '\0');415 && match_at[gl->matched_range.rm_eo] == '\0'); 403 416 } else 404 417 if (!(option_mask32 & OPT_w)) { … … 406 419 } else { 407 420 char c = ' '; 408 if (gl->matched_range.rm_so) 409 c = line[gl->matched_range.rm_so - 1]; 421 if (match_at > line || gl->matched_range.rm_so != 0) { 422 c = match_at[gl->matched_range.rm_so - 1]; 423 } 410 424 if (!isalnum(c) && c != '_') { 411 c = line[gl->matched_range.rm_eo]; 412 if (!c || (!isalnum(c) && c != '_')) 413 found = 1; 425 c = match_at[gl->matched_range.rm_eo]; 414 426 } 415 //BUG: "echo foop foo | grep -w foo" should match, but doesn't: 416 //we bail out on first "mismatch" because it's not a word. 427 if (!isalnum(c) && c != '_') { 428 found = 1; 429 } else { 430 /* 431 * Why check gl->matched_range.rm_eo? 432 * Zero-length match makes -w skip the line: 433 * "echo foo | grep ^" prints "foo", 434 * "echo foo | grep -w ^" prints nothing. 435 * Without such check, we can loop forever. 436 */ 437 #if !ENABLE_EXTRA_COMPAT 438 if (gl->matched_range.rm_eo != 0) { 439 match_at += gl->matched_range.rm_eo; 440 match_flg |= REG_NOTBOL; 441 goto opt_w_again; 442 } 443 #else 444 if (gl->matched_range.rm_eo > start_pos) { 445 start_pos = gl->matched_range.rm_eo; 446 goto opt_w_again; 447 } 448 #endif 449 } 417 450 } 418 451 } … … 636 669 recursive_action(dir, 637 670 /* recurse=yes */ ACTION_RECURSE | 638 /* followLinks= no */671 /* followLinks=command line only */ ACTION_FOLLOWLINKS_L0 | 639 672 /* depthFirst=yes */ ACTION_DEPTHFIRST, 640 673 /* fileAction= */ file_action_grep, … … 651 684 int matched; 652 685 llist_t *fopt = NULL; 686 #if ENABLE_FEATURE_GREP_CONTEXT 687 int Copt, opts; 688 #endif 689 INIT_G(); 690 691 /* For grep, exitcode of 1 is "not found". Other errors are 2: */ 692 xfunc_error_retval = 2; 653 693 654 694 /* do normal option parsing */ 655 695 #if ENABLE_FEATURE_GREP_CONTEXT 656 int Copt, opts;657 658 696 /* -H unsets -h; -C unsets -A,-B; -e,-f are lists; 659 697 * -m,-A,-B,-C have numeric param */ … … 711 749 712 750 #if !ENABLE_EXTRA_COMPAT 713 if (!(option_mask32 & (OPT_o | OPT_w )))751 if (!(option_mask32 & (OPT_o | OPT_w | OPT_x))) 714 752 reflags = REG_NOSUB; 715 753 #endif -
branches/3.3/mindi-busybox/findutils/xargs.c
r3232 r3621 54 54 //config: instead of whitespace, and the quotes and backslash 55 55 //config: are not special. 56 //config: 57 //config:config FEATURE_XARGS_SUPPORT_REPL_STR 58 //config: bool "Enable -I STR: string to replace" 59 //config: default y 60 //config: depends on XARGS 61 //config: help 62 //config: Support -I STR and -i[STR] options. 56 63 57 64 //applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs)) … … 60 67 61 68 #include "libbb.h" 69 #include "common_bufsiz.h" 62 70 63 71 /* This is a NOEXEC applet. Be very careful! */ … … 86 94 struct globals { 87 95 char **args; 96 #if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR 97 char **argv; 98 const char *repl_str; 99 char eol_ch; 100 #endif 88 101 const char *eof_str; 89 102 int idx; 90 103 } FIX_ALIASING; 91 #define G (*(struct globals*) &bb_common_bufsiz1)104 #define G (*(struct globals*)bb_common_bufsiz1) 92 105 #define INIT_G() do { \ 106 setup_common_bufsiz(); \ 93 107 G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ 108 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ 109 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ 94 110 } while (0) 95 111 96 112 97 /*98 * This function has special algorithm.99 * Don't use fork and include to main!100 */101 113 static int xargs_exec(void) 102 114 { … … 113 125 } 114 126 if (status >= 0x180) { 115 bb_error_msg(" %s:terminated by signal %d",127 bb_error_msg("'%s' terminated by signal %d", 116 128 G.args[0], status - 0x180); 117 129 return 125; … … 142 154 * If reading discovers that last chars do not form the complete 143 155 * parameter, the pointer to the first such "tail character" is returned. 144 * (buf has extra byte at the end to accom odate terminating NUL156 * (buf has extra byte at the end to accommodate terminating NUL 145 157 * of "tail characters" string). 146 158 * Otherwise, the returned pointer points to NUL byte. … … 302 314 } 303 315 *p++ = c; 304 if (c == '\0') { /* word's delimiteror EOF detected */316 if (c == '\0') { /* NUL or EOF detected */ 305 317 /* A full word is loaded */ 306 318 store_param(s); … … 324 336 #endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ 325 337 338 #if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR 339 /* 340 * Used if -I<repl> was specified. 341 * In this mode, words aren't appended to PROG ARGS. 342 * Instead, entire input line is read, then <repl> string 343 * in every PROG and ARG is replaced with the line: 344 * echo -e "ho ho\nhi" | xargs -I_ cmd __ _ 345 * results in "cmd 'ho hoho ho' 'ho ho'"; "cmd 'hihi' 'hi'". 346 * -n MAX_ARGS seems to be ignored. 347 * Tested with GNU findutils 4.5.10. 348 */ 349 //FIXME: n_max_chars is not handled the same way as in GNU findutils. 350 //FIXME: quoting is not implemented. 351 static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf) 352 { 353 int i; 354 char *end, *p; 355 356 /* Free strings from last invocation, if any */ 357 for (i = 0; G.args && G.args[i]; i++) 358 if (G.args[i] != G.argv[i]) 359 free(G.args[i]); 360 361 end = buf + n_max_chars; 362 p = buf; 363 364 while (1) { 365 int c = getchar(); 366 if (c == EOF || c == G.eol_ch) { 367 if (p == buf) 368 goto ret; /* empty line */ 369 c = '\0'; 370 } 371 *p++ = c; 372 if (c == '\0') { /* EOL or EOF detected */ 373 i = 0; 374 while (G.argv[i]) { 375 char *arg = G.argv[i]; 376 int count = count_strstr(arg, G.repl_str); 377 if (count != 0) 378 arg = xmalloc_substitute_string(arg, count, G.repl_str, buf); 379 store_param(arg); 380 dbg_msg("args[]:'%s'", arg); 381 i++; 382 } 383 p = buf; 384 goto ret; 385 } 386 if (p == end) { 387 goto ret; 388 } 389 } 390 ret: 391 *p = '\0'; 392 /* store_param(NULL) - caller will do it */ 393 dbg_msg("return:'%s'", buf); 394 return buf; 395 } 396 #endif 397 326 398 #if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 327 399 /* Prompt the user for a response, and 328 if the user responds affirmatively, return true; 329 otherwise, return false. Uses "/dev/tty", not stdin. */ 400 * if user responds affirmatively, return true; 401 * otherwise, return false. Uses "/dev/tty", not stdin. 402 */ 330 403 static int xargs_ask_confirmation(void) 331 404 { … … 361 434 //usage: "\n -n N Pass no more than N args to PROG" 362 435 //usage: "\n -s N Pass command line of no more than N bytes" 436 //usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR( 437 //usage: "\n -I STR Replace STR within PROG ARGS with input line" 438 //usage: ) 363 439 //usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT( 364 440 //usage: "\n -x Exit if size is exceeded" … … 379 455 IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,) 380 456 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,) 457 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR ,) 458 IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR1 ,) 381 459 382 460 OPT_VERBOSE = 1 << OPTBIT_VERBOSE , … … 389 467 OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0, 390 468 OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0, 469 OPT_REPLSTR = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR )) + 0, 470 OPT_REPLSTR1 = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR1 )) + 0, 391 471 }; 392 472 #define OPTION_STR "+trn:s:e::E:" \ 393 473 IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \ 394 474 IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \ 395 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") 475 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \ 476 IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::") 396 477 397 478 int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; … … 406 487 int n_max_chars; 407 488 int n_max_arg; 408 #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 489 #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \ 490 || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR 409 491 char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin; 410 492 #else … … 420 502 ; 421 503 #endif 422 opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str); 504 opt = getopt32(argv, OPTION_STR, 505 &max_args, &max_chars, &G.eof_str, &G.eof_str 506 IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str) 507 ); 423 508 424 509 /* -E ""? You may wonder why not just omit -E? … … 428 513 G.eof_str = NULL; 429 514 430 if (opt & OPT_ZEROTERM) 431 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); 515 if (opt & OPT_ZEROTERM) { 516 IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;) 517 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';) 518 } 432 519 433 520 argv += optind; … … 439 526 } 440 527 441 /* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate 442 * to use such a big value - first need to change code to use 443 * growable buffer instead of fixed one. 444 */ 445 n_max_chars = 32 * 1024; 446 /* Make smaller if system does not allow our default value. 528 /* 447 529 * The Open Group Base Specifications Issue 6: 448 530 * "The xargs utility shall limit the command line length such that … … 452 534 * shall not exceed {ARG_MAX}-2048 bytes". 453 535 */ 454 { 455 long arg_max = 0; 456 #if defined _SC_ARG_MAX 457 arg_max = sysconf(_SC_ARG_MAX) - 2048; 458 #elif defined ARG_MAX 459 arg_max = ARG_MAX - 2048; 460 #endif 461 if (arg_max > 0 && n_max_chars > arg_max) 462 n_max_chars = arg_max; 463 } 536 n_max_chars = bb_arg_max(); 537 if (n_max_chars > 32 * 1024) 538 n_max_chars = 32 * 1024; 539 /* 540 * POSIX suggests substracting 2048 bytes from sysconf(_SC_ARG_MAX) 541 * so that the process may safely modify its environment. 542 */ 543 n_max_chars -= 2048; 544 464 545 if (opt & OPT_UPTO_SIZE) { 465 546 n_max_chars = xatou_range(max_chars, 1, INT_MAX); … … 487 568 } 488 569 489 /* Allocate pointers for execvp */ 490 /* We can statically allocate (argc + n_max_arg + 1) elements 491 * and do not bother with resizing args[], but on 64-bit machines 492 * this results in args[] vector which is ~8 times bigger 493 * than n_max_chars! That is, with n_max_chars == 20k, 494 * args[] will take 160k (!), which will most likely be 495 * almost entirely unused. 496 */ 497 /* See store_param() for matching 256-step growth logic */ 498 G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); 499 500 /* Store the command to be executed, part 1 */ 501 for (i = 0; argv[i]; i++) 502 G.args[i] = argv[i]; 570 #if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR 571 if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) { 572 /* 573 * -I<str>: 574 * Unmodified args are kept in G.argv[i], 575 * G.args[i] receives malloced G.argv[i] with <str> replaced 576 * with input line. Setting this up: 577 */ 578 G.args = NULL; 579 G.argv = argv; 580 argc = 0; 581 read_args = process_stdin_with_replace; 582 /* Make -I imply -r. GNU findutils seems to do the same: */ 583 /* (otherwise "echo -n | xargs -I% echo %" would SEGV) */ 584 opt |= OPT_NO_EMPTY; 585 } else 586 #endif 587 { 588 /* Allocate pointers for execvp. 589 * We can statically allocate (argc + n_max_arg + 1) elements 590 * and do not bother with resizing args[], but on 64-bit machines 591 * this results in args[] vector which is ~8 times bigger 592 * than n_max_chars! That is, with n_max_chars == 20k, 593 * args[] will take 160k (!), which will most likely be 594 * almost entirely unused. 595 * 596 * See store_param() for matching 256-step growth logic 597 */ 598 G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); 599 /* Store the command to be executed, part 1 */ 600 for (i = 0; argv[i]; i++) 601 G.args[i] = argv[i]; 602 } 503 603 504 604 while (1) {
Note:
See TracChangeset
for help on using the changeset viewer.