OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "platform/globals.h" | 5 #include "platform/globals.h" |
6 #if defined(HOST_OS_WINDOWS) | 6 #if defined(HOST_OS_WINDOWS) |
7 | 7 |
8 #include "bin/file.h" | 8 #include "bin/file.h" |
9 | 9 |
10 #include <fcntl.h> // NOLINT | 10 #include <fcntl.h> // NOLINT |
11 #include <io.h> // NOLINT | 11 #include <io.h> // NOLINT |
12 #include <stdio.h> // NOLINT | 12 #include <stdio.h> // NOLINT |
13 #include <string.h> // NOLINT | 13 #include <string.h> // NOLINT |
14 #include <sys/stat.h> // NOLINT | 14 #include <sys/stat.h> // NOLINT |
15 #include <sys/utime.h> // NOLINT | 15 #include <sys/utime.h> // NOLINT |
16 #include <WinIoCtl.h> // NOLINT | 16 #include <WinIoCtl.h> // NOLINT |
17 | 17 |
18 #include "bin/builtin.h" | 18 #include "bin/builtin.h" |
19 #include "bin/log.h" | 19 #include "bin/log.h" |
20 #include "bin/utils.h" | 20 #include "bin/utils.h" |
21 #include "bin/utils_win.h" | 21 #include "bin/utils_win.h" |
22 #include "platform/utils.h" | 22 #include "platform/utils.h" |
23 | 23 |
24 namespace dart { | 24 namespace dart { |
25 namespace bin { | 25 namespace bin { |
26 | 26 |
27 class FileHandle { | 27 class FileHandle { |
28 public: | 28 public: |
29 explicit FileHandle(int fd) | 29 explicit FileHandle(int fd) : fd_(fd) {} |
30 : fd_(fd), real_fd_(-1), binary_(true), is_atty_(false) {} | |
31 ~FileHandle() {} | 30 ~FileHandle() {} |
32 int fd() const { return fd_; } | 31 int fd() const { return fd_; } |
33 void set_fd(int fd) { fd_ = fd; } | 32 void set_fd(int fd) { fd_ = fd; } |
34 | 33 |
35 int real_fd() const { | |
36 ASSERT(is_atty_); | |
37 return real_fd_; | |
38 } | |
39 void set_real_fd(int real_fd) { | |
40 ASSERT(is_atty_); | |
41 real_fd_ = real_fd; | |
42 } | |
43 | |
44 bool binary() const { return binary_; } | |
45 void SetBinary(bool binary) { | |
46 ASSERT(fd_ >= 0); | |
47 if (binary) { | |
48 // Setting the mode to _O_TEXT is needed first to reset _write to allow | |
49 // an odd number of bytes, which setting to _O_BINARY alone doesn't | |
50 // accomplish. | |
51 if (binary != binary_) { | |
52 _setmode(fd_, _O_TEXT); | |
53 } | |
54 _setmode(fd_, _O_BINARY); | |
55 } else { | |
56 // Only allow non-binary modes if we're attached to a terminal. | |
57 ASSERT(_isatty(fd_)); | |
58 _setmode(fd_, _O_WTEXT); | |
59 } | |
60 binary_ = binary; | |
61 } | |
62 | |
63 bool is_atty() const { return is_atty_; } | |
64 void set_is_atty(bool is_atty) { is_atty_ = is_atty; } | |
65 | |
66 private: | 34 private: |
67 int fd_; | 35 int fd_; |
68 int real_fd_; | |
69 bool binary_; | |
70 bool is_atty_; | |
71 | 36 |
72 DISALLOW_COPY_AND_ASSIGN(FileHandle); | 37 DISALLOW_COPY_AND_ASSIGN(FileHandle); |
73 }; | 38 }; |
74 | 39 |
75 | 40 |
76 File::~File() { | 41 File::~File() { |
77 if (!IsClosed() && handle_->fd() != _fileno(stdout) && | 42 if (!IsClosed() && handle_->fd() != _fileno(stdout) && |
78 handle_->fd() != _fileno(stderr)) { | 43 handle_->fd() != _fileno(stderr)) { |
79 Close(); | 44 Close(); |
80 } | 45 } |
81 delete handle_; | 46 delete handle_; |
82 } | 47 } |
83 | 48 |
84 | 49 |
85 void File::Close() { | 50 void File::Close() { |
86 ASSERT(handle_->fd() >= 0); | 51 ASSERT(handle_->fd() >= 0); |
87 int closing_fd; | 52 int closing_fd = handle_->fd(); |
88 if (handle_->is_atty()) { | |
89 close(handle_->fd()); | |
90 closing_fd = handle_->real_fd(); | |
91 } else { | |
92 closing_fd = handle_->fd(); | |
93 } | |
94 if ((closing_fd == _fileno(stdout)) || (closing_fd == _fileno(stderr))) { | 53 if ((closing_fd == _fileno(stdout)) || (closing_fd == _fileno(stderr))) { |
95 int fd = _open("NUL", _O_WRONLY); | 54 int fd = _open("NUL", _O_WRONLY); |
96 ASSERT(fd >= 0); | 55 ASSERT(fd >= 0); |
97 _dup2(fd, closing_fd); | 56 _dup2(fd, closing_fd); |
98 close(fd); | 57 close(fd); |
99 } else { | 58 } else { |
100 int err = close(closing_fd); | 59 int err = close(closing_fd); |
101 if (err != 0) { | 60 if (err != 0) { |
102 Log::PrintErr("%s\n", strerror(errno)); | 61 Log::PrintErr("%s\n", strerror(errno)); |
103 } | 62 } |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 | 125 |
167 int64_t File::Read(void* buffer, int64_t num_bytes) { | 126 int64_t File::Read(void* buffer, int64_t num_bytes) { |
168 ASSERT(handle_->fd() >= 0); | 127 ASSERT(handle_->fd() >= 0); |
169 return read(handle_->fd(), buffer, num_bytes); | 128 return read(handle_->fd(), buffer, num_bytes); |
170 } | 129 } |
171 | 130 |
172 | 131 |
173 int64_t File::Write(const void* buffer, int64_t num_bytes) { | 132 int64_t File::Write(const void* buffer, int64_t num_bytes) { |
174 int fd = handle_->fd(); | 133 int fd = handle_->fd(); |
175 ASSERT(fd >= 0); | 134 ASSERT(fd >= 0); |
176 if (handle_->binary()) { | 135 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd)); |
177 return _write(fd, buffer, num_bytes); | 136 DWORD written = 0; |
178 } else { | 137 BOOL result = WriteFile(handle, buffer, num_bytes, &written, NULL); |
179 // If we've done _setmode(fd, _O_WTEXT) then _write() expects | 138 if (!result) { |
180 // a buffer of wchar_t with an even unmber of bytes. | 139 return -1; |
181 Utf8ToWideScope wide(reinterpret_cast<const char*>(buffer), num_bytes); | |
182 ASSERT((wide.size_in_bytes() % 2) == 0); | |
183 return _write(fd, wide.wide(), wide.size_in_bytes()); | |
184 } | 140 } |
| 141 DWORD mode; |
| 142 int64_t bytes_written = written; |
| 143 if (GetConsoleMode(handle, &mode)) { |
| 144 // If `handle` is for a console, then `written` may refer to the number of |
| 145 // characters printed to the screen rather than the number of bytes of the |
| 146 // buffer that were actually consumed. To compute the number of bytes that |
| 147 // were actually consumed, we convert the buffer to a wchar_t using the |
| 148 // console's current code page, filling as many characters as were |
| 149 // printed, and then convert that many characters back to the encoding for |
| 150 // the code page, which gives the number of bytes of `buffer` used to |
| 151 // generate the characters that were printed. |
| 152 wchar_t* wide = new wchar_t[written]; |
| 153 int cp = GetConsoleOutputCP(); |
| 154 MultiByteToWideChar(cp, 0, reinterpret_cast<const char*>(buffer), -1, wide, |
| 155 written); |
| 156 int buffer_len = |
| 157 WideCharToMultiByte(cp, 0, wide, written, NULL, 0, NULL, NULL); |
| 158 delete wide; |
| 159 bytes_written = buffer_len; |
| 160 } |
| 161 return bytes_written; |
185 } | 162 } |
186 | 163 |
187 | 164 |
188 bool File::VPrint(const char* format, va_list args) { | 165 bool File::VPrint(const char* format, va_list args) { |
189 // Measure. | 166 // Measure. |
190 va_list measure_args; | 167 va_list measure_args; |
191 va_copy(measure_args, args); | 168 va_copy(measure_args, args); |
192 intptr_t len = _vscprintf(format, measure_args); | 169 intptr_t len = _vscprintf(format, measure_args); |
193 va_end(measure_args); | 170 va_end(measure_args); |
194 | 171 |
(...skipping 16 matching lines...) Expand all Loading... |
211 return _lseeki64(handle_->fd(), 0, SEEK_CUR); | 188 return _lseeki64(handle_->fd(), 0, SEEK_CUR); |
212 } | 189 } |
213 | 190 |
214 | 191 |
215 bool File::SetPosition(int64_t position) { | 192 bool File::SetPosition(int64_t position) { |
216 ASSERT(handle_->fd() >= 0); | 193 ASSERT(handle_->fd() >= 0); |
217 return _lseeki64(handle_->fd(), position, SEEK_SET) >= 0; | 194 return _lseeki64(handle_->fd(), position, SEEK_SET) >= 0; |
218 } | 195 } |
219 | 196 |
220 | 197 |
221 void File::SetTranslation(DartFileTranslation translation) { | |
222 ASSERT(handle_->fd() >= 0); | |
223 // Only allow setting the translation mode if we're attached to a terminal. | |
224 // TODO(zra): Is this restriction needed? Is it already handled correctly | |
225 // by _write()? | |
226 if (handle_->is_atty()) { | |
227 handle_->SetBinary(translation == kBinary); | |
228 } | |
229 } | |
230 | |
231 | |
232 bool File::Truncate(int64_t length) { | 198 bool File::Truncate(int64_t length) { |
233 ASSERT(handle_->fd() >= 0); | 199 ASSERT(handle_->fd() >= 0); |
234 return _chsize_s(handle_->fd(), length) == 0; | 200 return _chsize_s(handle_->fd(), length) == 0; |
235 } | 201 } |
236 | 202 |
237 | 203 |
238 bool File::Flush() { | 204 bool File::Flush() { |
239 ASSERT(handle_->fd()); | 205 ASSERT(handle_->fd()); |
240 return _commit(handle_->fd()) != -1; | 206 return _commit(handle_->fd()) != -1; |
241 } | 207 } |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 switch (fd) { | 301 switch (fd) { |
336 case 1: | 302 case 1: |
337 stdio_fd = _fileno(stdout); | 303 stdio_fd = _fileno(stdout); |
338 break; | 304 break; |
339 case 2: | 305 case 2: |
340 stdio_fd = _fileno(stderr); | 306 stdio_fd = _fileno(stderr); |
341 break; | 307 break; |
342 default: | 308 default: |
343 UNREACHABLE(); | 309 UNREACHABLE(); |
344 } | 310 } |
345 FileHandle* handle; | 311 _setmode(stdio_fd, _O_BINARY); |
346 if (_isatty(stdio_fd)) { | 312 return new File(new FileHandle(stdio_fd)); |
347 // We _dup these fds to avoid different Isoaltes racing on calls to | |
348 // _setmode() and _write() on the same file descriptor. That is, a call to | |
349 // _setmode() followed by a call to _write() on the same file descriptor is | |
350 // not atomic. When the corresponding Dart File object is closed, these | |
351 // dup'd fds will be closed. | |
352 int stdio_fd_dup = _dup(stdio_fd); | |
353 handle = new FileHandle(stdio_fd_dup); | |
354 handle->set_is_atty(true); | |
355 handle->set_real_fd(stdio_fd); | |
356 } else { | |
357 handle = new FileHandle(stdio_fd); | |
358 } | |
359 handle->SetBinary(true); | |
360 return new File(handle); | |
361 } | 313 } |
362 | 314 |
363 | 315 |
364 static bool StatHelper(wchar_t* path, struct __stat64* st) { | 316 static bool StatHelper(wchar_t* path, struct __stat64* st) { |
365 int stat_status = _wstat64(path, st); | 317 int stat_status = _wstat64(path, st); |
366 if (stat_status != 0) { | 318 if (stat_status != 0) { |
367 return false; | 319 return false; |
368 } | 320 } |
369 if ((st->st_mode & S_IFMT) != S_IFREG) { | 321 if ((st->st_mode & S_IFMT) != S_IFREG) { |
370 SetLastError(ERROR_NOT_SUPPORTED); | 322 SetLastError(ERROR_NOT_SUPPORTED); |
(...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
825 return kIdentical; | 777 return kIdentical; |
826 } else { | 778 } else { |
827 return kDifferent; | 779 return kDifferent; |
828 } | 780 } |
829 } | 781 } |
830 | 782 |
831 } // namespace bin | 783 } // namespace bin |
832 } // namespace dart | 784 } // namespace dart |
833 | 785 |
834 #endif // defined(HOST_OS_WINDOWS) | 786 #endif // defined(HOST_OS_WINDOWS) |
OLD | NEW |