33namespace Lkrms \Cli ;
44
55use Lkrms \Cli \Catalog \CliHelpSectionName ;
6- use Lkrms \Cli \Catalog \CliHelpType ;
76use Lkrms \Cli \Catalog \CliOptionValueType ;
87use Lkrms \Cli \Catalog \CliOptionVisibility ;
98use Lkrms \Cli \Contract \ICliApplication ;
@@ -801,26 +800,28 @@ function (string $key, $value) use (&$argValues, &$saved, &$option) {
801800 }
802801 };
803802 $ merged = [];
803+ $ positional = [];
804804
805805 for ($ i = 0 ; $ i < count ($ args ); $ i ++) {
806806 $ arg = $ args [$ i ];
807807 $ short = false ;
808808 $ saved = false ;
809809 if (preg_match ('/^-([a-z0-9_])(.*)/i ' , $ arg , $ matches )) {
810810 $ name = $ matches [1 ];
811- $ value = $ matches [2 ] ?: null ;
811+ $ value = $ matches [2 ] === '' ? null : $ matches [ 2 ] ;
812812 $ short = true ;
813813 } elseif (preg_match ('/^--([a-z0-9_][-a-z0-9_]+)(?:=(.*))?$/i ' , $ arg , $ matches , PREG_UNMATCHED_AS_NULL )) {
814814 $ name = $ matches [1 ];
815815 $ value = $ matches [2 ];
816- } else {
817- if ($ arg === '-- ' ) {
818- $ i ++;
819- } elseif (($ arg [0 ] ?? null ) === '- ' ) {
820- $ this ->optionError (sprintf ("invalid argument '%s' " , $ arg ));
821- continue ;
822- }
816+ } elseif ($ arg === '-- ' ) {
817+ $ i ++;
823818 break ;
819+ } elseif ($ arg === '- ' || ($ arg [0 ] ?? null ) !== '- ' ) {
820+ $ positional [] = $ arg ;
821+ continue ;
822+ } else {
823+ $ this ->optionError (sprintf ("invalid argument '%s' " , $ arg ));
824+ continue ;
824825 }
825826
826827 $ option = $ this ->OptionsByName [$ name ] ?? null ;
@@ -839,7 +840,7 @@ function (string $key, $value) use (&$argValues, &$saved, &$option) {
839840 $ valueIsDefault = false ;
840841 if ($ option ->IsFlag ) {
841842 // Handle multiple short flags per argument, e.g. `cp -rv`
842- if ($ short && $ value ) {
843+ if ($ short && $ value !== null ) {
843844 $ args [$ i ] = "- $ value " ;
844845 $ i --;
845846 }
@@ -867,17 +868,23 @@ function (string $key, $value) use (&$argValues, &$saved, &$option) {
867868 }
868869
869870 if ($ option ->MultipleAllowed && !$ option ->IsFlag ) {
870- // Interpret "--option=" as "clear previous --option values"
871+ // Interpret the first use of "--opt=" as "clear default or
872+ // previous values" without changing the meaning of "--opt ''"
871873 if ($ option ->ValueRequired && $ value === '' ) {
872- $ merged [$ key ] = [];
873- $ argValues [$ key ] = [];
874- continue ;
874+ if ($ args [$ i ] === '' || ($ merged [$ key ] ?? null ) === []) {
875+ $ value = ['' ];
876+ } else {
877+ $ merged [$ key ] = [];
878+ $ argValues [$ key ] = [];
879+ continue ;
880+ }
881+ } else {
882+ $ value = $ option ->maybeSplitValue ($ value );
875883 }
876884 // Use $value to extend $option->DefaultValue if:
877885 // - $option->DefaultValue wasn't just assigned to $value
878886 // - extension of default values is enabled, and
879887 // - this is $option's first appearance in $args
880- $ value = $ option ->maybeSplitValue ($ value );
881888 $ saveArgValue ($ key , $ value );
882889 if (!$ valueIsDefault &&
883890 ($ option ->KeepDefault || $ option ->KeepEnv ) &&
@@ -894,6 +901,11 @@ function (string $key, $value) use (&$argValues, &$saved, &$option) {
894901 }
895902 }
896903
904+ // Splice $positional into $args to ensure $nextArgumentIndex is correct
905+ if ($ positional ) {
906+ $ i -= count ($ positional );
907+ array_splice ($ args , $ i , count ($ positional ), $ positional );
908+ }
897909 $ pending = count ($ this ->PositionalOptions );
898910 foreach ($ this ->PositionalOptions as $ option ) {
899911 if (!($ i < count ($ args ))) {
@@ -1129,7 +1141,8 @@ final protected function getEffectiveArgument(
11291141 $ value = $ this ->OptionValues [$ option ->Key ] ?? null ;
11301142 }
11311143
1132- if ($ value === null || $ value === [] || $ value === false || $ value === 0 ) {
1144+ if ($ value === null || $ value === false || $ value === 0 ||
1145+ ($ value === [] && $ option ->ValueRequired )) {
11331146 return null ;
11341147 }
11351148
@@ -1144,14 +1157,15 @@ final protected function getEffectiveArgument(
11441157 if (is_array ($ value )) {
11451158 $ value = implode (', ' , $ value );
11461159 }
1147- if ($ shellEscape && is_string ($ value )) {
1160+ if ($ shellEscape && is_string ($ value ) &&
1161+ ($ value !== '' || $ option ->IsPositional )) {
11481162 $ value = Convert::toShellArg ($ value );
11491163 }
11501164
11511165 return $ option ->IsPositional
11521166 ? $ value
11531167 : ($ option ->Long
1154- ? "-- {$ option ->Long }" . (is_string ($ value ) ? "= $ value " : '' )
1168+ ? "-- {$ option ->Long }" . (is_string ($ value ) && ( $ value !== '' || $ option -> ValueRequired ) ? "= $ value " : '' )
11551169 : "- {$ option ->Short }" . $ value );
11561170 }
11571171
0 commit comments