Use the new utilities to format some strings
[libs/core.git] / source / core / windows / process.cpp
1 #include <windows.h>
2 #include <msp/core/systemerror.h>
3 #include <msp/io/handle_private.h>
4 #include <msp/strings/utils.h>
5 #include "process.h"
6 #include "process_private.h"
7
8 using namespace std;
9
10 namespace {
11
12 string quote_argument(const string &arg)
13 {
14         string result;
15         bool need_quotes = false;
16         bool backslash = false;
17         for(string::const_iterator i=arg.begin(); i!=arg.end(); ++i)
18         {
19                 if(*i=='\\')
20                         backslash = true;
21                 else if(*i=='"')
22                 {
23                         if(backslash)
24                                 result += '\\';
25                         result += '\\';
26                 }
27                 else if(*i==' ')
28                         need_quotes = true;
29                 result += *i;
30         }
31
32         if(need_quotes)
33                 return "\""+result+"\"";
34         else
35                 return result;
36 }
37
38 }
39
40 namespace Msp {
41
42 Process::~Process()
43 {
44         CloseHandle(priv->info.hProcess);
45         CloseHandle(priv->info.hThread);
46 }
47
48 void Process::platform_get_self_info(Private &priv)
49 {
50         priv.info.hProcess = GetCurrentProcess();
51         priv.info.hThread = 0;
52         priv.info.dwProcessId = GetCurrentProcessId();
53         priv.info.dwThreadId = 0;
54 }
55
56 void Process::execute(const string &command, bool path_search, const Arguments &args)
57 {
58         string cmdline = quote_argument(command);
59         for(Arguments::const_iterator i=args.begin(); i!=args.end(); ++i)
60                 append(cmdline, " ", quote_argument(*i));
61
62         STARTUPINFO startup;
63         startup.cb = sizeof(STARTUPINFO);
64         startup.lpReserved = 0;
65         startup.lpDesktop = 0;
66         startup.lpTitle = 0;
67         startup.dwFlags = 0;
68         startup.cbReserved2 = 0;
69         startup.lpReserved2 = 0;
70         if(redirect)
71         {
72                 startup.dwFlags |= STARTF_USESTDHANDLES;
73                 HANDLE self_handle = self().priv->info.hProcess;
74                 HANDLE cin_handle = (cin ? *cin->get_handle(IO::M_READ) : GetStdHandle(STD_INPUT_HANDLE));
75                 DuplicateHandle(self_handle, cin_handle, self_handle, &startup.hStdInput, 0, true, DUPLICATE_SAME_ACCESS);
76                 HANDLE cout_handle = (cout ? *cout->get_handle(IO::M_WRITE) : GetStdHandle(STD_OUTPUT_HANDLE));
77                 DuplicateHandle(self_handle, cout_handle, self_handle, &startup.hStdOutput, 0, true, DUPLICATE_SAME_ACCESS);
78                 HANDLE cerr_handle = (cerr ? *cerr->get_handle(IO::M_WRITE) : GetStdHandle(STD_ERROR_HANDLE));
79                 DuplicateHandle(self_handle, cerr_handle, self_handle, &startup.hStdError, 0, true, DUPLICATE_SAME_ACCESS);
80         }
81         const char *cmdptr = (path_search ? 0 : command.c_str());
82         const char *wd = (work_dir.empty() ? 0 : work_dir.c_str());
83         if(!CreateProcess(cmdptr, const_cast<char *>(cmdline.c_str()), 0, 0, false, 0, 0, wd, &startup, &priv->info))
84                 throw system_error("CreateProcess");
85         // XXX Should we close the duplicated handles?  What if CreateProcess fails?
86
87         running = true;
88
89         if(this==_self)
90                 TerminateProcess(priv->info.hProcess, 0);
91 }
92
93 bool Process::wait(bool block)
94 {
95         if(!running)
96                 throw logic_error("not running");
97
98         DWORD ret = WaitForSingleObject(priv->info.hProcess, (block ? INFINITE : 0));
99         if(ret==WAIT_FAILED)
100                 throw system_error("WaitForSingleObject");
101
102         if(ret==WAIT_OBJECT_0)
103         {
104                 running = false;
105                 finished = true;
106         }
107
108         return finished;
109 }
110
111 void Process::terminate()
112 {
113         TerminateProcess(priv->info.hProcess, 1);
114 }
115
116 void Process::kill()
117 {
118         TerminateProcess(priv->info.hProcess, 1);
119 }
120
121 void Process::interrupt()
122 {
123         TerminateProcess(priv->info.hProcess, 1);
124 }
125
126
127 Process::Private::Private()
128 {
129         info.hProcess = 0;
130         info.hThread = 0;
131         info.dwProcessId = 0;
132         info.dwThreadId = 0;
133 }
134
135 } // namespace Msp