1+ " {opts} is a list of command line options
2+ " {cmd} is the argv list for the process to run
3+ " {opts} should be *sudo-specific*, while {cmd} is passed to any suda#executable
4+ " Note: {cmd} can not have any sudo flags. Put these into {opts}, as '--' is passed before {cmd}
5+ " Similarly, {opts} should *not* contain '--'
16function ! s: get_command (opts, cmd)
2- " TODO: should we pass '--' between a:opts and a:cmd?
3- " TODO: should we change this api to use lists? system() allows either
4- " strings or lists. We don't need a intermediate shell for anything though.
5- " TODO: Should we move shell escaping to the responsibility of
6- " suda#system/s:get_command to avoid forgetting it at the call site ?
7- return g: suda #executable == # " sudo " && len ( a: opts ) > 0
8- \ ? printf ( ' %s %s %s ' , g: suda # executable , a: opts , a: cmd )
9- \ : printf ( ' %s %s ' , g: suda #executable , a: cmd)
7+ if g: suda # executable == # ' sudo '
8+ return [ g: suda # executable ] + a: opts + [ ' -- ' ] + a: cmd
9+ endif
10+ " TODO:
11+ " Should we pass '--' before cmd when using a custom suda#executable ?
12+ " Should suda#executable be split? Should we allow suda#executable to be a list instead?
13+ " This behavior is entirely undocumented
14+ return [ g: suda #executable ] + a: cmd
1015endfunction
1116
12- function ! suda#system (cmd, ... ) abort
17+ " {cmd} is a argv list for the process
18+ " {input} (a:1) is a string to pass as stdin to the command
19+ " Returns a list of the command's output, split by NLs, with NULs replaced with NLs.
20+ function ! suda#systemlist (cmd, ... ) abort
1321 let cmd = has (' win32' ) || g: suda #nopass
14- \ ? s: get_command (' ' , a: cmd )
15- \ : s: get_command (' -p '''' -n' , a: cmd )
22+ \ ? s: get_command ([] , a: cmd )
23+ \ : s: get_command ([ ' -p' , ' ' , ' -n' ] , a: cmd )
1624 if &verbose
1725 echomsg ' [suda]' cmd
1826 endif
19- let result = a: 0 ? system (cmd, a: 1 ) : system (cmd)
27+ let result = a: 0 ? systemlist (cmd, a: 1 ) : systemlist (cmd)
2028 if v: shell_error == 0
2129 return result
2230 endif
@@ -28,24 +36,34 @@ function! suda#system(cmd, ...) abort
2836 " configuation file. It does not work with 'ppid', 'kernel' or 'tty'.
2937 " Note: for non-sudo commands, don't do this, instead *always* ask for the password
3038 if g: suda #executable == # " sudo"
31- let cmd = s: get_command (" -n" , " true" )
32- let result = system (cmd)
39+ let cmd = s: get_command ([ " -n" ], [ " true" ] )
40+ let result = systemlist (cmd)
3341 if v: shell_error == 0
34- let cmd = s: get_command (' ' , a: cmd )
42+ let cmd = s: get_command ([] , a: cmd )
3543 let ask_pass = 0
3644 endif
3745 endif
3846 if ask_pass == 1
3947 try
4048 call inputsave ()
41- redraw | let password = inputsecret (g: suda #prompt )
49+ redraw | let password = inputsecret (g: suda #prompt )
4250 finally
4351 call inputrestore ()
4452 endtry
45- let cmd = s: get_command (' -p '''' -S' , a: cmd )
53+ let cmd = s: get_command ([ ' -p' , ' ' , ' -S' ] , a: cmd )
4654 endif
47- return system (cmd, password . " \n " . (a: 0 ? a: 1 : ' ' ))
55+ return systemlist (cmd, password . " \n " . (a: 0 ? a: 1 : ' ' ))
4856endfunction
57+ " {cmd} is a argv list for the process
58+ " {input} (a:1) is a string to pass as stdin to the command
59+ " Returns the command's output as a string with NULs replaced with SOH (\u0001)
60+ function ! suda#system (cmd, ... ) abort
61+ let output = call (" suda#systemlist" , [a: cmd ] + a: 000 )
62+ " Emulate system()'s handling of output - replace NULs (represented by NL), join by NLs
63+ return join ( map (l: output , { k , v - > substitute (v: val , ' \n' , ' ' , ' g' ) }), ' \n' )
64+ endfunction
65+
66+
4967
5068function ! suda#read (expr , ... ) abort range
5169 let path = s: strip_prefix (expand (a: expr ))
@@ -57,6 +75,7 @@ function! suda#read(expr, ...) abort range
5775 \)
5876
5977 if filereadable (path )
78+ " TODO: (aarondill) can we use readfile() here?
6079 return substitute (execute (printf (
6180 \ ' %sread %s %s' ,
6281 \ options .range ,
@@ -67,17 +86,15 @@ function! suda#read(expr, ...) abort range
6786
6887 let tempfile = tempname ()
6988 try
70- let redirect = &shellredir = ~# ' %s'
71- \ ? printf (&shellredir , shellescape (tempfile))
72- \ : &shellredir . shellescape (tempfile)
73- let result = suda#system (printf (
74- \ ' cat %s %s' ,
75- \ shellescape (fnamemodify (path , ' :p' )),
76- \ redirect,
77- \) )
89+ " NOTE: use systemlist to avoid changing newlines. Get the results of the
90+ " command (as a list) to avoid having to spawn a shell to do a redirection
91+ let resultlist = suda#systemlist ([' cat' , fnamemodify (path , ' :p' )])
7892 if v: shell_error
79- throw result
93+ throw resultlist
8094 else
95+ " write with 'b' to ensure contents are the same
96+ call writefile (resultlist, tempfile, ' b' )
97+ " TODO: (aarondill) can we use readfile() here?
8198 let echo_message = execute (printf (
8299 \ ' %sread %s %s' ,
83100 \ options .range ,
@@ -110,6 +127,7 @@ function! suda#write(expr, ...) abort range
110127 let tempfile = tempname ()
111128 try
112129 let path_exists = ! empty (getftype (path ))
130+ " TODO: (aarondill) can we use writefile() here?
113131 let echo_message = execute (printf (
114132 \ ' %swrite%s %s %s' ,
115133 \ options .range ,
@@ -125,17 +143,12 @@ function! suda#write(expr, ...) abort range
125143 " Using a full path for tee command to avoid this problem.
126144 let tee_cmd = exepath (' tee' )
127145 let result = suda#system (
128- \ printf (' %s %s' , shellescape (tee_cmd), shellescape (path )),
129- \ join (readfile (tempfile, ' b' ), " \n " )
130- \)
146+ \ [tee_cmd, path ],
147+ \ join (readfile (tempfile, ' b' ), " \n " ) )
131148 else
132149 " `bs=1048576` is equivalent to `bs=1M` for GNU dd or `bs=1m` for BSD dd
133150 " Both `bs=1M` and `bs=1m` are non-POSIX
134- let result = suda#system (printf (
135- \ ' dd if=%s of=%s bs=1048576' ,
136- \ shellescape (tempfile),
137- \ shellescape (path )
138- \) )
151+ let result = suda#system ([' dd' , ' if=' .tempfile, ' of=' .path , ' bs=1048576' ])
139152 endif
140153 if v: shell_error
141154 throw result
0 commit comments