3bac658c134143177bef6e44f7b8a65231fd26c6
[haskell-directory.git] / System / RawSystem.hs-inc
1 ------------------------------------------------------------------------
2 --
3 --                      rawSystem
4 --
5 -- This is a separate file #included into Haskell source, because
6 -- we use it in a few places in the GHC source tree.
7 --
8 ------------------------------------------------------------------------
9
10 {- | 
11 The computation @rawSystem cmd args@ runs the operating system command
12 whose file name is @cmd@, passing it the arguments @args@.  It
13 bypasses the shell, so that @cmd@ should see precisely the argument
14 strings @args@, with no funny escaping or shell meta-syntax expansion.
15 (Unix users will recognise this behaviour 
16 as @execvp@, and indeed that's how it's implemented.)
17 It will therefore behave more portably between operating systems than @system@.
18
19 The return codes are the same as for @system@.
20 -}
21
22 rawSystem :: FilePath -> [String] -> IO ExitCode
23
24 {- -------------------------------------------------------------------------
25         IMPORTANT IMPLEMENTATION NOTES
26    (see also libraries/base/cbits/rawSystem.c)
27
28 On Unix, rawSystem is easy to implement: use execvp.
29
30 On Windows it's more tricky.  We use CreateProcess, passing a single
31 command-line string (lpCommandLine) as its argument.  (CreateProcess
32 is well documented on http://msdn.microsoft/com.)
33
34   - It parses the beginning of the string to find the command. If the
35         file name has embedded spaces, it must be quoted, using double
36         quotes thus 
37                 "foo\this that\cmd" arg1 arg2
38
39   - The invoked command can in turn access the entire lpCommandLine string,
40         and the C runtime does indeed do so, parsing it to generate the 
41         traditional argument vector argv[0], argv[1], etc.  It does this
42         using a complex and arcane set of rules which are described here:
43         
44            http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/progs_12.asp
45
46         (if this URL stops working, you might be able to find it by
47         searching for "Parsing C Command-Line Arguments" on MSDN.  Also,
48         the code in the Microsoft C runtime that does this translation
49         is shipped with VC++).
50
51
52 Our goal in rawSystem is to take a command filename and list of
53 arguments, and construct a string which inverts the translatsions
54 described above, such that the program at the other end sees exactly
55 the same arguments in its argv[] that we passed to rawSystem.
56
57 This inverse translation is implemented by 'translate' below.
58
59 Here are some pages that give informations on Windows-related 
60 limitations and deviations from Unix conventions:
61
62     http://support.microsoft.com/default.aspx?scid=kb;en-us;830473
63     Command lines and environment variables effectively limited to 8191 
64     characters on Win XP, 2047 on NT/2000 (probably even less on Win 9x):
65
66     http://www.microsoft.com/windowsxp/home/using/productdoc/en/default.asp?url=/WINDOWSXP/home/using/productdoc/en/percent.asp
67     Command-line substitution under Windows XP. IIRC these facilities (or at 
68     least a large subset of them) are available on Win NT and 2000. Some 
69     might be available on Win 9x.
70
71     http://www.microsoft.com/windowsxp/home/using/productdoc/en/default.asp?url=/WINDOWSXP/home/using/productdoc/en/Cmd.asp
72     How CMD.EXE processes command lines.
73
74
75 Note: CreateProcess does have a separate argument (lpApplicationName)
76 with which you can specify the command, but we have to slap the
77 command into lpCommandLine anyway, so that argv[0] is what a C program
78 expects (namely the application name).  So it seems simpler to just
79 use lpCommandLine alone, which CreateProcess supports.
80
81 ----------------------------------------------------------------------------- -}
82
83 #ifndef mingw32_TARGET_OS
84
85 rawSystem cmd args =
86   withCString cmd $ \pcmd ->
87     withMany withCString (cmd:args) $ \cstrs ->
88       withArray0 nullPtr cstrs $ \arr -> do
89         status <- throwErrnoIfMinus1 "rawSystem" (c_rawSystem pcmd arr)
90         case status of
91             0  -> return ExitSuccess
92             n  -> return (ExitFailure n)
93
94 foreign import ccall unsafe "rawSystem"
95   c_rawSystem :: CString -> Ptr CString -> IO Int
96
97 #else
98
99 -- On Windows, the command line is passed to the operating system as
100 -- a single string.  Command-line parsing is done by the executable
101 -- itself.
102 rawSystem cmd args = do
103         -- NOTE: 'cmd' is assumed to contain the application to run _only_,
104         -- as it'll be quoted surrounded in quotes here.
105   let cmdline = translate cmd ++ concat (map ((' ':) . translate) args)
106   withCString cmdline $ \pcmdline -> do
107     status <- throwErrnoIfMinus1 "rawSystem" (c_rawSystem pcmdline)
108     case status of
109        0  -> return ExitSuccess
110        n  -> return (ExitFailure n)
111
112 translate :: String -> String
113 translate str@('"':_) = str -- already escaped.
114         -- ToDo: this case is wrong.  It is only here because we
115         -- abuse the system in GHC's SysTools by putting arguments into
116         -- the command name; at some point we should fix it up and remove
117         -- the case above.
118 translate str = '"' : snd (foldr escape (True,"\"") str)
119   where escape '"'  (b,     str) = (True,  '\\' : '"'  : str)
120         escape '\\' (True,  str) = (True,  '\\' : '\\' : str)
121         escape '\\' (False, str) = (False, '\\' : str)
122         escape c    (b,     str) = (False, c : str)
123         -- See long comment above for what this function is trying to do.
124         --
125         -- The Bool passed back along the string is True iff the
126         -- rest of the string is a sequence of backslashes followed by
127         -- a double quote.
128
129 foreign import ccall unsafe "rawSystem"
130   c_rawSystem :: CString -> IO Int
131
132 #endif