summary refs log tree commit diff
path: root/Functions/Misc/zargs
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Misc/zargs')
-rw-r--r--Functions/Misc/zargs112
1 files changed, 68 insertions, 44 deletions
diff --git a/Functions/Misc/zargs b/Functions/Misc/zargs
index f4345d29c..928b1ffbf 100644
--- a/Functions/Misc/zargs
+++ b/Functions/Misc/zargs
@@ -40,13 +40,17 @@
 # With the --max-procs option, zargs may not correctly capture the exit
 # status of the backgrounded jobs, because of limitations of the "wait"
 # builtin.  If the zsh/parameter module is not available, the status is
-# NEVER correctly returned.
+# NEVER correctly returned, otherwise the status of the longest-running
+# job in each batch is captured.
+#
+# Also because of "wait" limitations, --max-procs spawns max-procs jobs,
+# then waits for all of those, then spawns another batch, etc.
 #
 
 emulate -L zsh || return 1
 local -a opts eof n s l P i
 
-local ZARGS_VERSION="1.0"
+local ZARGS_VERSION="1.3"
 
 if zparseopts -a opts -D -- \
 	-eof::=eof e::=eof \
@@ -153,31 +157,16 @@ else command=( print -r -- )
 fi
 
 local wait bg
-if (( P != 1 ))
-then
-    setopt nonotify nomonitor
-    bg='&'
-fi
-if (( P > 1 ))
-then
-    if zmodload -i zsh/parameter 2>/dev/null
-    then
-	integer j=$#jobtexts
-	wait='(( $#jobtexts - j < P )) || wait %${(k)^jobtexts} 2>/dev/null;'
-    else
-	wait='{ (( P )) && (( P-- )) } || wait;'
-    fi
-fi
-
-local last='return $ret' execute='
+local execute='
     if (( $opts[(I)-(-interactive|p)] ))
-    then read -q "?$call?..." || eval "$last"
+    then read -q "?$call?..." || continue
     elif (( $opts[(I)-(-verbose|t)] ))
     then print -u2 -r -- "$call"
     fi
     eval "{
 	\$call
-    } $bg $wait"
+    } $bg"'
+local ret=0 analyze='
     case $? in
     (0) ;;
     (<1-125>|128)  ret=123;;
@@ -186,21 +175,21 @@ local last='return $ret' execute='
     (126)       return 126;;
     (127)       return 127;;
     (*)         return 1;;
-    esac
-    eval "$last"'
+    esac'
 
 if (( ARGC == 0 ))
 then
     if (( $opts[(I)-(-no-run-if-empty|r)] ))
     then return 0
-    else call=($command); eval "$execute"
+    else
+	call=($command)
+	# Use "repeat" here so "continue" won't complain.
+	repeat 1 eval "$execute ; $analyze"
+	return $ret
     fi
 fi
 
 n=${${n##-(n|-max-args(=|))}:-$[ARGC+c]}
-s=${${s##-(s|-max-chars(=|))}:-20480}
-l=${${l##-(l|-max-lines(=|))}:-${${l[1]:+1}:-$ARGC}}
-P=${${P##-(P|-max-procs(=|))}:-1}
 
 if (( n > c ))
 then (( n -= c ))
@@ -209,27 +198,62 @@ else
     return 1
 fi
 
-last='shift $((end > ARGC ? ARGC : end)); continue'
-while ((ARGC))
-do
-    for (( end=l; end && ${(c)#argv[1,end]} > s; end/=2 )) :
-    (( end > n && ( end = n ) ))
-    args=( $argv[1,end] )
-    if (( $#i ))
-    then call=( ${command/$i/$args} )
-    else call=( $command $args )
-    fi
-    if (( ${(c)#call} > s ))
+P=${${P##-(P|-max-procs(=|))}:-1}
+
+if (( P != 1 && ARGC > 1 ))
+then
+    # These setopts are necessary for "wait" on multiple jobs to work.
+    setopt nonotify nomonitor
+    bg='&'
+    if zmodload -i zsh/parameter 2>/dev/null
     then
-	print -u2 zargs: cannot fit single argument within size limit
-	# GNU xargs exits here whether or not -x,
-	# but that just makes the option useless.
-	(( $opts[(I)-(-exit|x)] )) && return 1
-	eval "$last"
+	wait='wait %${(k)^jobstates[(R)running:*]}'
     else
-	eval "$execute"
+	wait='wait'
+    fi
+fi
+
+s=${${s##-(s|-max-chars(=|))}:-20480}
+l=${${l##-(l|-max-lines(=|))}:-${${l[1]:+1}:-$ARGC}}
+
+# Everything has to be in a subshell just in case of backgrounding jobs,
+# so that we don't unintentionally "wait" for jobs of the parent shell.
+(
+
+while ((ARGC))
+do
+    if (( P == 0 || P > ARGC ))
+    then (( P = ARGC ))
     fi
+
+    repeat $P
+    do
+	((ARGC)) || break
+	for (( end=l; end && ${(c)#argv[1,end]} > s; end/=2 )) :
+	(( end > n && ( end = n ) ))
+	args=( $argv[1,end] )
+	shift $((end > ARGC ? ARGC : end))
+	if (( $#i ))
+	then call=( ${command/$i/$args} )
+	else call=( $command $args )
+	fi
+	if (( ${(c)#call} > s ))
+	then
+	    print -u2 zargs: cannot fit single argument within size limit
+	    # GNU xargs exits here whether or not -x,
+	    # but that just makes the option useless.
+	    (( $opts[(I)-(-exit|x)] )) && return 1
+	    continue
+	else
+	    eval "$execute"
+	fi
+    done
+
+    eval "$wait
+	$analyze"
 done
 return $ret
 
+)
+
 # }