Guitar
ProcessWinHelper.h
Go to the documentation of this file.
1 #ifndef PROCESSWINHELPER_H
2 #define PROCESSWINHELPER_H
3 
4 #ifndef _WIN32
5 #ifndef HANDLE
6 #error Must include <windows.h> before this file.
7 #endif
8 #endif
9 
10 namespace {
11 
12 inline bool IS_VALID_HANDLE(HANDLE h)
13 {
14  return h && h != INVALID_HANDLE_VALUE;
15 }
16 
17 inline bool IS_VALID_HANDLE(PROCESS_INFORMATION const &h)
18 {
19  return IS_VALID_HANDLE(h.hProcess) || IS_VALID_HANDLE(h.hThread);
20 }
21 
22 inline bool write_all(HANDLE handle, char const *data, size_t size)
23 {
24  if (!IS_VALID_HANDLE(handle) || (!data && size != 0)) {
25  return false;
26  }
27  // 匿名パイプへのWriteFileは要求サイズ未満で成功する場合があるため、全量を書き切る。
28  while (size > 0) {
29  DWORD written = 0;
30  DWORD chunk = size > MAXDWORD ? MAXDWORD : static_cast<DWORD>(size);
31  if (!WriteFile(handle, data, chunk, &written, nullptr) || written == 0) {
32  return false;
33  }
34  data += written;
35  size -= written;
36  }
37  return true;
38 }
39 
40 inline std::wstring convert_str_to_wstr(std::string_view const &str)
41 {
42  std::wstring wstr;
43  if (str.empty()) return wstr;
44  int len = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
45  if (len > 0) {
46  wstr.resize(len);
47  MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], len);
48  }
49  return wstr;
50 }
51 
52 inline std::string convert_wstr_to_str(std::wstring_view const &str)
53 {
54  std::string s;
55  if (str.empty()) return s;
56  int len = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
57  if (len > 0) {
58  s.resize(len);
59  WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &s[0], len, nullptr, nullptr);
60  }
61  return s;
62 }
63 
64 inline std::string_view getProgram(std::string_view cmdline)
65 {
66  char const *begin = cmdline.data();
67  char const *end = begin + cmdline.size();
68  char const *ptr = begin;
69  bool quote = 0;
70  while (1) {
71  char c = 0;
72  if (ptr < end) {
73  c = *ptr;
74  }
75  if (c == '\"') {
76  if (quote) {
77  quote = false;
78  } else {
79  quote = true;
80  }
81  ptr++;
82  } else if (quote && c != 0) {
83  ptr++;
84  } else if (isspace((unsigned char)c) || c == 0) {
85  break;
86  } else {
87  ptr++;
88  }
89  }
90  char const *left = begin;
91  char const *right = ptr;
92  if (left + 1 < right) {
93  if (left[0] == '\"' && right[-1] == '\"') {
94  left++;
95  right--;
96  }
97  }
98  return std::string_view(left, right - left);
99 }
100 
101 template <typename HANDLE> class AbstractAutoHandle {
102 private:
103  HANDLE h_ = { };
104 
105 protected:
106  virtual void close_handle(HANDLE &h) = 0;
107 
108 public:
109  AbstractAutoHandle() = default;
110  AbstractAutoHandle(HANDLE h)
111  : h_(h)
112  {
113  }
114  AbstractAutoHandle(AbstractAutoHandle const &) = delete;
115  AbstractAutoHandle &operator=(AbstractAutoHandle const &) = delete;
116  AbstractAutoHandle(AbstractAutoHandle &&r) noexcept
117  : h_(r.detach())
118  {
119  }
120  AbstractAutoHandle &operator=(AbstractAutoHandle &&r) noexcept
121  {
122  if (this != std::addressof(r)) {
123  close();
124  h_ = r.detach();
125  }
126  return *this;
127  }
128  virtual ~AbstractAutoHandle()
129  {
130  close();
131  }
132  HANDLE detach()
133  {
134  HANDLE h = h_;
135  h_ = { };
136  return h;
137  }
138  void close()
139  {
140  HANDLE h = detach();
141  if (IS_VALID_HANDLE(h)) {
142  close_handle(h);
143  }
144  }
145  HANDLE *operator->()
146  {
147  return &h_;
148  }
149  HANDLE *operator&()
150  {
151  close();
152  return &h_;
153  }
154  HANDLE &operator=(HANDLE h)
155  {
156  if (h_ == h) return h_;
157  close();
158  h_ = h;
159  return h_;
160  }
161  operator HANDLE &()
162  {
163  return h_;
164  }
165 };
166 
167 class AutoHandle : public AbstractAutoHandle<HANDLE> {
168 protected:
169  void close_handle(HANDLE &h)
170  {
171  CloseHandle(h);
172  h = nullptr;
173  }
174 
175 public:
176  using AbstractAutoHandle<HANDLE>::AbstractAutoHandle;
177  using AbstractAutoHandle<HANDLE>::operator=;
178 };
179 
180 class AutoProcessInformation : public AbstractAutoHandle<PROCESS_INFORMATION> {
181 private:
182  PROCESS_INFORMATION pi = { };
183 
184 protected:
185  void close_handle(PROCESS_INFORMATION &pi)
186  {
187  if (IS_VALID_HANDLE(pi.hProcess)) {
188  CloseHandle(pi.hProcess);
189  }
190  if (IS_VALID_HANDLE(pi.hThread)) {
191  CloseHandle(pi.hThread);
192  }
193  pi = { };
194  }
195 
196 public:
197  using AbstractAutoHandle<PROCESS_INFORMATION>::AbstractAutoHandle;
198  using AbstractAutoHandle<PROCESS_INFORMATION>::operator=;
199 };
200 
201 class AutoPseudoConsole {
202 private:
203  HPCON hPC = nullptr;
204 
205 public:
206  AutoPseudoConsole() = default;
207  AutoPseudoConsole(AutoPseudoConsole const &) = delete;
208  AutoPseudoConsole &operator=(AutoPseudoConsole const &) = delete;
209  AutoPseudoConsole(AutoPseudoConsole &&r) noexcept
210  : hPC(r.hPC)
211  {
212  r.hPC = nullptr;
213  }
214  AutoPseudoConsole &operator=(AutoPseudoConsole &&r) noexcept
215  {
216  if (this != std::addressof(r)) {
217  close();
218  hPC = r.hPC;
219  r.hPC = nullptr;
220  }
221  return *this;
222  }
223  ~AutoPseudoConsole()
224  {
225  close();
226  }
227  void close()
228  {
229  if (IS_VALID_HANDLE(hPC)) {
230  ClosePseudoConsole(hPC);
231  hPC = nullptr;
232  }
233  }
234  operator HPCON &()
235  {
236  return hPC;
237  }
238  HPCON *operator&()
239  {
240  return &hPC;
241  }
242 };
243 
244 // ReadFileの境界をまたぐVTシーケンスも除去できるよう、解析状態を保持する。
245 class VtStripper {
246 public:
247  std::string append(std::string_view input)
248  {
249  std::string output;
250  for (unsigned char c : input) {
251  switch (state_) {
252  case State::Text:
253  if (c == 0x1b) {
254  state_ = State::Escape;
255  } else {
256  output.push_back(static_cast<char>(c));
257  }
258  break;
259 
260  case State::Escape:
261  if (c == '[') {
262  state_ = State::Csi;
263  } else if (c == ']') {
264  state_ = State::Osc;
265  } else if (c == 'P' || c == 'X' || c == '^' || c == '_') {
266  state_ = State::String;
267  } else if (c >= 0x20 && c <= 0x2f) {
268  state_ = State::EscapeIntermediate;
269  } else if (c == 0x1b) {
270  state_ = State::Escape;
271  } else {
272  state_ = State::Text;
273  }
274  break;
275 
276  case State::EscapeIntermediate:
277  if (c >= 0x30 && c <= 0x7e) {
278  state_ = State::Text;
279  } else if (c == 0x1b) {
280  state_ = State::Escape;
281  }
282  break;
283 
284  case State::Csi:
285  if (c >= 0x40 && c <= 0x7e) {
286  state_ = State::Text;
287  } else if (c == 0x1b) {
288  state_ = State::Escape;
289  }
290  break;
291 
292  case State::Osc:
293  if (c == 0x07) {
294  state_ = State::Text;
295  } else if (c == 0x1b) {
296  state_ = State::OscEscape;
297  }
298  break;
299 
300  case State::OscEscape:
301  if (c == '\\') {
302  state_ = State::Text;
303  } else if (c != 0x1b) {
304  state_ = State::Osc;
305  }
306  break;
307 
308  case State::String:
309  if (c == 0x1b) {
310  state_ = State::StringEscape;
311  }
312  break;
313 
314  case State::StringEscape:
315  if (c == '\\') {
316  state_ = State::Text;
317  } else if (c != 0x1b) {
318  state_ = State::String;
319  }
320  break;
321  }
322  }
323  return output;
324  }
325 
326 private:
327  enum class State {
328  Text,
329  Escape,
330  EscapeIntermediate,
331  Csi,
332  Osc,
333  OscEscape,
334  String,
335  StringEscape,
336  };
337 
338  State state_ = State::Text;
339 };
340 
341 }
342 
343 #endif // PROCESSWINHELPER_H
@ String
Definition: jstream.h:399
static void append(std::vector< char > *out, char const *ptr, size_t len)
Definition: misc.h:174
std::u16string convert_str_to_wstr(std::string_view const &str)
Definition: wstring.cpp:41
std::string convert_wstr_to_str(std::u16string_view const &str)
Definition: wstring.cpp:118
std::string_view getProgram(std::string_view cmdline)
Definition: misc.cpp:777