1 -----------------------------------------------------------------------------
4 -- Copyright : (c) The University of Glasgow 2001
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : libraries@haskell.org
8 -- Stability : provisional
9 -- Portability : portable
11 -- Executing an external command.
13 -----------------------------------------------------------------------------
16 ( system, -- :: String -> IO ExitCode
17 #ifdef __GLASGOW_HASKELL__
18 rawSystem, -- :: FilePath -> [String] -> IO ExitCode
24 #ifdef __GLASGOW_HASKELL__
37 import System (system)
40 -- ---------------------------------------------------------------------------
44 Computation @system cmd@ returns the exit code
45 produced when the operating system processes the command @cmd@.
47 This computation may fail with
49 * @PermissionDenied@: The process has insufficient privileges to
50 perform the operation.
52 * @ResourceExhausted@: Insufficient resources are available to
53 perform the operation.
55 * @UnsupportedOperation@: The implementation does not support
58 On Windows, 'system' is implemented using Windows's native system
59 call, which ignores the @SHELL@ environment variable, and always
60 passes the command to the Windows command interpreter (@CMD.EXE@ or
61 @COMMAND.COM@), hence Unixy shell tricks will not work.
63 #ifdef __GLASGOW_HASKELL__
64 system :: String -> IO ExitCode
65 system "" = ioException (IOError Nothing InvalidArgument "system" "null command" Nothing)
67 withCString cmd $ \s -> do
68 status <- throwErrnoIfMinus1 "system" (primSystem s)
70 0 -> return ExitSuccess
71 n -> return (ExitFailure n)
73 foreign import ccall unsafe "systemCmd" primSystem :: CString -> IO Int
76 ------------------------------------------------------------------------
80 ------------------------------------------------------------------------
83 The computation @rawSystem cmd args@ runs the operating system command
84 whose file name is @cmd@, passing it the arguments @args@. It
85 bypasses the shell, so that @cmd@ should see precisely the argument
86 strings @args@, with no funny escaping or shell meta-syntax expansion.
87 (Unix users will recognise this behaviour
88 as @execvp@, and indeed that's how it's implemented.)
89 It will therefore behave more portably between operating systems than @system@.
91 The return codes are the same as for @system@.
94 rawSystem :: FilePath -> [String] -> IO ExitCode
96 {- -------------------------------------------------------------------------
97 IMPORTANT IMPLEMENTATION NOTES
98 (see also libraries/base/cbits/rawSystem.c)
100 On Unix, rawSystem is easy to implement: use execvp.
102 On Windows it's more tricky. We use CreateProcess, passing a single
103 command-line string (lpCommandLine) as its argument. (CreateProcess
104 is well documented on http://msdn.microsoft/com.)
106 - It parses the beginning of the string to find the command. If the
107 file name has embedded spaces, it must be quoted, using double
109 "foo\this that\cmd" arg1 arg2
111 - The invoked command can in turn access the entire lpCommandLine string,
112 and the C runtime does indeed do so, parsing it to generate the
113 traditional argument vector argv[0], argv[1], etc. Again, to
114 break it into argument items, any spaces must be quoted using
116 cmd "this is arg 1" "this is arg 2"
118 What if an argument itself contains double-quotes? (File names can't
119 can't, on Windows.) Then the quote must be escaped with a backslash.
120 If we call Create Process with this lpArgument:
121 cmd "Foo=\"baz\"" arg2
122 then cmd will see argv[1] as
124 However, experiments show that backslashes themselves must *not* be escaped.
125 That is, to get a backslash in an argument, just put backslash, even inside
126 quotes. For eaxmple, this works fine to show the contents of the file
129 If you escape the backslash, thus
131 then @cat@ will see argument foo\\baz, and on WinME/98/95 you'll get
132 "can't find file foo\\baz". (As it happens, WinNT/XP commands don't
133 mind double backslashes, but it's still a bug, given rawSystem's claim
134 to pass exactly args to the command.)
137 1 We wrap the command, and each argument, in quotes
138 2 Inside the quotes, we escape any double-quote characters
140 3 Then concatenate all these quoted things together, separated with
143 Steps 1,2 are done by the function 'translate' below.
145 The exact rules used by the C runtime to unscramble quoted argumets
146 are quite complex. For example, how do you get the string \" into an
147 argument? You can find the rules in MSDN, here:
148 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/progs_12.asp
150 Here are some pages that give informations on Windows-related
151 limitations and deviations from Unix conventions:
153 http://support.microsoft.com/default.aspx?scid=kb;en-us;830473
154 Command lines and environment variables effectively limited to 8191
155 characters on Win XP, 2047 on NT/2000 (probably even less on Win 9x):
157 http://www.microsoft.com/windowsxp/home/using/productdoc/en/default.asp?url=/WINDOWSXP/home/using/productdoc/en/percent.asp
158 Command-line substitution under Windows XP. IIRC these facilities (or at
159 least a large subset of them) are available on Win NT and 2000. Some
160 might be available on Win 9x.
162 http://www.microsoft.com/windowsxp/home/using/productdoc/en/default.asp?url=/WINDOWSXP/home/using/productdoc/en/Cmd.asp
163 How CMD.EXE processes command lines.
167 Note: CreateProcess does have a separate argument (lpApplicationName)
168 with which you can specify the command, but we have to slap the
169 command into lpCommandLine anyway, so that argv[0] is what a C program
170 expects (namely the application name). So it seems simpler to just
171 use lpCommandLine alone, which CreateProcess supports.
173 ----------------------------------------------------------------------------- -}
175 #ifndef mingw32_TARGET_OS
178 withCString cmd $ \pcmd ->
179 withMany withCString (cmd:args) $ \cstrs ->
180 withArray0 nullPtr cstrs $ \arr -> do
181 status <- throwErrnoIfMinus1 "rawSystem" (c_rawSystem pcmd arr)
183 0 -> return ExitSuccess
184 n -> return (ExitFailure n)
186 foreign import ccall unsafe "rawSystem"
187 c_rawSystem :: CString -> Ptr CString -> IO Int
191 -- On Windows, the command line is passed to the operating system as
192 -- a single string. Command-line parsing is done by the executable
194 rawSystem cmd args = do
195 -- NOTE: 'cmd' is assumed to contain the application to run _only_,
196 -- as it'll be quoted surrounded in quotes here.
197 let cmdline = translate cmd ++ concat (map ((' ':) . translate) args)
198 withCString cmdline $ \pcmdline -> do
199 status <- throwErrnoIfMinus1 "rawSystem" (c_rawSystem pcmdline)
201 0 -> return ExitSuccess
202 n -> return (ExitFailure n)
204 translate :: String -> String
205 translate str@('"':_) = str -- already escaped.
206 translate str = '"' : foldr escape "\"" str
207 where escape '"' str = '\\' : '"' : str
208 escape c str = c : str
210 foreign import ccall unsafe "rawSystem"
211 c_rawSystem :: CString -> IO Int
215 #endif /* __GLASGOW_HASKELL__ */