xref: /openbsd-src/gnu/llvm/lldb/source/Host/common/File.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- File.cpp ----------------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Host/File.h"
10061da546Spatrick 
11be691f3bSpatrick #include <cerrno>
12be691f3bSpatrick #include <climits>
13be691f3bSpatrick #include <cstdarg>
14be691f3bSpatrick #include <cstdio>
15061da546Spatrick #include <fcntl.h>
16*f6aab3d8Srobert #include <optional>
17061da546Spatrick 
18061da546Spatrick #ifdef _WIN32
19061da546Spatrick #include "lldb/Host/windows/windows.h"
20061da546Spatrick #else
21061da546Spatrick #include <sys/ioctl.h>
22061da546Spatrick #include <sys/stat.h>
23061da546Spatrick #include <termios.h>
24061da546Spatrick #include <unistd.h>
25061da546Spatrick #endif
26061da546Spatrick 
27061da546Spatrick #include "lldb/Host/Config.h"
28061da546Spatrick #include "lldb/Host/FileSystem.h"
29061da546Spatrick #include "lldb/Host/Host.h"
30061da546Spatrick #include "lldb/Utility/DataBufferHeap.h"
31061da546Spatrick #include "lldb/Utility/FileSpec.h"
32061da546Spatrick #include "lldb/Utility/Log.h"
33*f6aab3d8Srobert #include "lldb/Utility/VASPrintf.h"
34*f6aab3d8Srobert #include "llvm/Support/ConvertUTF.h"
35*f6aab3d8Srobert #include "llvm/Support/Errno.h"
36*f6aab3d8Srobert #include "llvm/Support/FileSystem.h"
37*f6aab3d8Srobert #include "llvm/Support/Process.h"
38061da546Spatrick 
39061da546Spatrick using namespace lldb;
40061da546Spatrick using namespace lldb_private;
41061da546Spatrick using llvm::Expected;
42061da546Spatrick 
43061da546Spatrick Expected<const char *>
GetStreamOpenModeFromOptions(File::OpenOptions options)44061da546Spatrick File::GetStreamOpenModeFromOptions(File::OpenOptions options) {
45*f6aab3d8Srobert   File::OpenOptions rw =
46*f6aab3d8Srobert       options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
47*f6aab3d8Srobert                  File::eOpenOptionReadWrite);
48*f6aab3d8Srobert 
49061da546Spatrick   if (options & File::eOpenOptionAppend) {
50*f6aab3d8Srobert     if (rw == File::eOpenOptionReadWrite) {
51061da546Spatrick       if (options & File::eOpenOptionCanCreateNewOnly)
52061da546Spatrick         return "a+x";
53061da546Spatrick       else
54061da546Spatrick         return "a+";
55*f6aab3d8Srobert     } else if (rw == File::eOpenOptionWriteOnly) {
56061da546Spatrick       if (options & File::eOpenOptionCanCreateNewOnly)
57061da546Spatrick         return "ax";
58061da546Spatrick       else
59061da546Spatrick         return "a";
60061da546Spatrick     }
61*f6aab3d8Srobert   } else if (rw == File::eOpenOptionReadWrite) {
62061da546Spatrick     if (options & File::eOpenOptionCanCreate) {
63061da546Spatrick       if (options & File::eOpenOptionCanCreateNewOnly)
64061da546Spatrick         return "w+x";
65061da546Spatrick       else
66061da546Spatrick         return "w+";
67061da546Spatrick     } else
68061da546Spatrick       return "r+";
69*f6aab3d8Srobert   } else if (rw == File::eOpenOptionWriteOnly) {
70061da546Spatrick     return "w";
71*f6aab3d8Srobert   } else if (rw == File::eOpenOptionReadOnly) {
72*f6aab3d8Srobert     return "r";
73061da546Spatrick   }
74061da546Spatrick   return llvm::createStringError(
75061da546Spatrick       llvm::inconvertibleErrorCode(),
76061da546Spatrick       "invalid options, cannot convert to mode string");
77061da546Spatrick }
78061da546Spatrick 
GetOptionsFromMode(llvm::StringRef mode)79061da546Spatrick Expected<File::OpenOptions> File::GetOptionsFromMode(llvm::StringRef mode) {
80061da546Spatrick   OpenOptions opts =
81061da546Spatrick       llvm::StringSwitch<OpenOptions>(mode)
82*f6aab3d8Srobert           .Cases("r", "rb", eOpenOptionReadOnly)
83*f6aab3d8Srobert           .Cases("w", "wb", eOpenOptionWriteOnly)
84061da546Spatrick           .Cases("a", "ab",
85*f6aab3d8Srobert                  eOpenOptionWriteOnly | eOpenOptionAppend |
86*f6aab3d8Srobert                  eOpenOptionCanCreate)
87*f6aab3d8Srobert           .Cases("r+", "rb+", "r+b", eOpenOptionReadWrite)
88061da546Spatrick           .Cases("w+", "wb+", "w+b",
89*f6aab3d8Srobert                  eOpenOptionReadWrite | eOpenOptionCanCreate |
90061da546Spatrick                  eOpenOptionTruncate)
91061da546Spatrick           .Cases("a+", "ab+", "a+b",
92*f6aab3d8Srobert                  eOpenOptionReadWrite | eOpenOptionAppend |
93061da546Spatrick                      eOpenOptionCanCreate)
94*f6aab3d8Srobert           .Default(eOpenOptionInvalid);
95*f6aab3d8Srobert   if (opts != eOpenOptionInvalid)
96061da546Spatrick     return opts;
97061da546Spatrick   return llvm::createStringError(
98061da546Spatrick       llvm::inconvertibleErrorCode(),
99061da546Spatrick       "invalid mode, cannot convert to File::OpenOptions");
100061da546Spatrick }
101061da546Spatrick 
102061da546Spatrick int File::kInvalidDescriptor = -1;
103061da546Spatrick FILE *File::kInvalidStream = nullptr;
104061da546Spatrick 
Read(void * buf,size_t & num_bytes)105061da546Spatrick Status File::Read(void *buf, size_t &num_bytes) {
106061da546Spatrick   return std::error_code(ENOTSUP, std::system_category());
107061da546Spatrick }
Write(const void * buf,size_t & num_bytes)108061da546Spatrick Status File::Write(const void *buf, size_t &num_bytes) {
109061da546Spatrick   return std::error_code(ENOTSUP, std::system_category());
110061da546Spatrick }
111061da546Spatrick 
IsValid() const112061da546Spatrick bool File::IsValid() const { return false; }
113061da546Spatrick 
Close()114061da546Spatrick Status File::Close() { return Flush(); }
115061da546Spatrick 
GetWaitableHandle()116061da546Spatrick IOObject::WaitableHandle File::GetWaitableHandle() {
117061da546Spatrick   return IOObject::kInvalidHandleValue;
118061da546Spatrick }
119061da546Spatrick 
GetFileSpec(FileSpec & file_spec) const120061da546Spatrick Status File::GetFileSpec(FileSpec &file_spec) const {
121061da546Spatrick   file_spec.Clear();
122061da546Spatrick   return std::error_code(ENOTSUP, std::system_category());
123061da546Spatrick }
124061da546Spatrick 
GetDescriptor() const125061da546Spatrick int File::GetDescriptor() const { return kInvalidDescriptor; }
126061da546Spatrick 
GetStream()127061da546Spatrick FILE *File::GetStream() { return nullptr; }
128061da546Spatrick 
SeekFromStart(off_t offset,Status * error_ptr)129061da546Spatrick off_t File::SeekFromStart(off_t offset, Status *error_ptr) {
130061da546Spatrick   if (error_ptr)
131061da546Spatrick     *error_ptr = std::error_code(ENOTSUP, std::system_category());
132061da546Spatrick   return -1;
133061da546Spatrick }
134061da546Spatrick 
SeekFromCurrent(off_t offset,Status * error_ptr)135061da546Spatrick off_t File::SeekFromCurrent(off_t offset, Status *error_ptr) {
136061da546Spatrick   if (error_ptr)
137061da546Spatrick     *error_ptr = std::error_code(ENOTSUP, std::system_category());
138061da546Spatrick   return -1;
139061da546Spatrick }
140061da546Spatrick 
SeekFromEnd(off_t offset,Status * error_ptr)141061da546Spatrick off_t File::SeekFromEnd(off_t offset, Status *error_ptr) {
142061da546Spatrick   if (error_ptr)
143061da546Spatrick     *error_ptr = std::error_code(ENOTSUP, std::system_category());
144061da546Spatrick   return -1;
145061da546Spatrick }
146061da546Spatrick 
Read(void * dst,size_t & num_bytes,off_t & offset)147061da546Spatrick Status File::Read(void *dst, size_t &num_bytes, off_t &offset) {
148061da546Spatrick   return std::error_code(ENOTSUP, std::system_category());
149061da546Spatrick }
150061da546Spatrick 
Write(const void * src,size_t & num_bytes,off_t & offset)151061da546Spatrick Status File::Write(const void *src, size_t &num_bytes, off_t &offset) {
152061da546Spatrick   return std::error_code(ENOTSUP, std::system_category());
153061da546Spatrick }
154061da546Spatrick 
Flush()155061da546Spatrick Status File::Flush() { return Status(); }
156061da546Spatrick 
Sync()157061da546Spatrick Status File::Sync() { return Flush(); }
158061da546Spatrick 
CalculateInteractiveAndTerminal()159061da546Spatrick void File::CalculateInteractiveAndTerminal() {
160061da546Spatrick   const int fd = GetDescriptor();
161061da546Spatrick   if (!DescriptorIsValid(fd)) {
162061da546Spatrick     m_is_interactive = eLazyBoolNo;
163061da546Spatrick     m_is_real_terminal = eLazyBoolNo;
164061da546Spatrick     m_supports_colors = eLazyBoolNo;
165061da546Spatrick     return;
166061da546Spatrick   }
167061da546Spatrick   m_is_interactive = eLazyBoolNo;
168061da546Spatrick   m_is_real_terminal = eLazyBoolNo;
169061da546Spatrick #if defined(_WIN32)
170061da546Spatrick   if (_isatty(fd)) {
171061da546Spatrick     m_is_interactive = eLazyBoolYes;
172061da546Spatrick     m_is_real_terminal = eLazyBoolYes;
173061da546Spatrick #if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
174061da546Spatrick     m_supports_colors = eLazyBoolYes;
175061da546Spatrick #endif
176061da546Spatrick   }
177061da546Spatrick #else
178061da546Spatrick   if (isatty(fd)) {
179061da546Spatrick     m_is_interactive = eLazyBoolYes;
180061da546Spatrick     struct winsize window_size;
181061da546Spatrick     if (::ioctl(fd, TIOCGWINSZ, &window_size) == 0) {
182061da546Spatrick       if (window_size.ws_col > 0) {
183061da546Spatrick         m_is_real_terminal = eLazyBoolYes;
184061da546Spatrick         if (llvm::sys::Process::FileDescriptorHasColors(fd))
185061da546Spatrick           m_supports_colors = eLazyBoolYes;
186061da546Spatrick       }
187061da546Spatrick     }
188061da546Spatrick   }
189061da546Spatrick #endif
190061da546Spatrick }
191061da546Spatrick 
GetIsInteractive()192061da546Spatrick bool File::GetIsInteractive() {
193061da546Spatrick   if (m_is_interactive == eLazyBoolCalculate)
194061da546Spatrick     CalculateInteractiveAndTerminal();
195061da546Spatrick   return m_is_interactive == eLazyBoolYes;
196061da546Spatrick }
197061da546Spatrick 
GetIsRealTerminal()198061da546Spatrick bool File::GetIsRealTerminal() {
199061da546Spatrick   if (m_is_real_terminal == eLazyBoolCalculate)
200061da546Spatrick     CalculateInteractiveAndTerminal();
201061da546Spatrick   return m_is_real_terminal == eLazyBoolYes;
202061da546Spatrick }
203061da546Spatrick 
GetIsTerminalWithColors()204061da546Spatrick bool File::GetIsTerminalWithColors() {
205061da546Spatrick   if (m_supports_colors == eLazyBoolCalculate)
206061da546Spatrick     CalculateInteractiveAndTerminal();
207061da546Spatrick   return m_supports_colors == eLazyBoolYes;
208061da546Spatrick }
209061da546Spatrick 
Printf(const char * format,...)210061da546Spatrick size_t File::Printf(const char *format, ...) {
211061da546Spatrick   va_list args;
212061da546Spatrick   va_start(args, format);
213061da546Spatrick   size_t result = PrintfVarArg(format, args);
214061da546Spatrick   va_end(args);
215061da546Spatrick   return result;
216061da546Spatrick }
217061da546Spatrick 
PrintfVarArg(const char * format,va_list args)218061da546Spatrick size_t File::PrintfVarArg(const char *format, va_list args) {
219*f6aab3d8Srobert   llvm::SmallString<0> s;
220*f6aab3d8Srobert   if (VASprintf(s, format, args)) {
221*f6aab3d8Srobert     size_t written = s.size();;
222*f6aab3d8Srobert     Write(s.data(), written);
223*f6aab3d8Srobert     return written;
224061da546Spatrick   }
225*f6aab3d8Srobert   return 0;
226061da546Spatrick }
227061da546Spatrick 
GetOptions() const228061da546Spatrick Expected<File::OpenOptions> File::GetOptions() const {
229061da546Spatrick   return llvm::createStringError(
230061da546Spatrick       llvm::inconvertibleErrorCode(),
231061da546Spatrick       "GetOptions() not implemented for this File class");
232061da546Spatrick }
233061da546Spatrick 
GetPermissions(Status & error) const234061da546Spatrick uint32_t File::GetPermissions(Status &error) const {
235061da546Spatrick   int fd = GetDescriptor();
236061da546Spatrick   if (!DescriptorIsValid(fd)) {
237061da546Spatrick     error = std::error_code(ENOTSUP, std::system_category());
238061da546Spatrick     return 0;
239061da546Spatrick   }
240061da546Spatrick   struct stat file_stats;
241061da546Spatrick   if (::fstat(fd, &file_stats) == -1) {
242061da546Spatrick     error.SetErrorToErrno();
243061da546Spatrick     return 0;
244061da546Spatrick   }
245061da546Spatrick   error.Clear();
246061da546Spatrick   return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
247061da546Spatrick }
248061da546Spatrick 
GetOptions() const249061da546Spatrick Expected<File::OpenOptions> NativeFile::GetOptions() const { return m_options; }
250061da546Spatrick 
GetDescriptor() const251061da546Spatrick int NativeFile::GetDescriptor() const {
252061da546Spatrick   if (DescriptorIsValid())
253061da546Spatrick     return m_descriptor;
254061da546Spatrick 
255061da546Spatrick   // Don't open the file descriptor if we don't need to, just get it from the
256061da546Spatrick   // stream if we have one.
257061da546Spatrick   if (StreamIsValid()) {
258061da546Spatrick #if defined(_WIN32)
259061da546Spatrick     return _fileno(m_stream);
260061da546Spatrick #else
261061da546Spatrick     return fileno(m_stream);
262061da546Spatrick #endif
263061da546Spatrick   }
264061da546Spatrick 
265061da546Spatrick   // Invalid descriptor and invalid stream, return invalid descriptor.
266061da546Spatrick   return kInvalidDescriptor;
267061da546Spatrick }
268061da546Spatrick 
GetWaitableHandle()269061da546Spatrick IOObject::WaitableHandle NativeFile::GetWaitableHandle() {
270061da546Spatrick   return GetDescriptor();
271061da546Spatrick }
272061da546Spatrick 
GetStream()273061da546Spatrick FILE *NativeFile::GetStream() {
274061da546Spatrick   if (!StreamIsValid()) {
275061da546Spatrick     if (DescriptorIsValid()) {
276061da546Spatrick       auto mode = GetStreamOpenModeFromOptions(m_options);
277061da546Spatrick       if (!mode)
278061da546Spatrick         llvm::consumeError(mode.takeError());
279061da546Spatrick       else {
280061da546Spatrick         if (!m_own_descriptor) {
281061da546Spatrick // We must duplicate the file descriptor if we don't own it because when you
282061da546Spatrick // call fdopen, the stream will own the fd
283061da546Spatrick #ifdef _WIN32
284061da546Spatrick           m_descriptor = ::_dup(GetDescriptor());
285061da546Spatrick #else
286061da546Spatrick           m_descriptor = dup(GetDescriptor());
287061da546Spatrick #endif
288061da546Spatrick           m_own_descriptor = true;
289061da546Spatrick         }
290061da546Spatrick 
291061da546Spatrick         m_stream = llvm::sys::RetryAfterSignal(nullptr, ::fdopen, m_descriptor,
292061da546Spatrick                                                mode.get());
293061da546Spatrick 
294061da546Spatrick         // If we got a stream, then we own the stream and should no longer own
295061da546Spatrick         // the descriptor because fclose() will close it for us
296061da546Spatrick 
297061da546Spatrick         if (m_stream) {
298061da546Spatrick           m_own_stream = true;
299061da546Spatrick           m_own_descriptor = false;
300061da546Spatrick         }
301061da546Spatrick       }
302061da546Spatrick     }
303061da546Spatrick   }
304061da546Spatrick   return m_stream;
305061da546Spatrick }
306061da546Spatrick 
Close()307061da546Spatrick Status NativeFile::Close() {
308061da546Spatrick   Status error;
309061da546Spatrick   if (StreamIsValid()) {
310061da546Spatrick     if (m_own_stream) {
311061da546Spatrick       if (::fclose(m_stream) == EOF)
312061da546Spatrick         error.SetErrorToErrno();
313*f6aab3d8Srobert     } else {
314*f6aab3d8Srobert       File::OpenOptions rw =
315*f6aab3d8Srobert           m_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
316*f6aab3d8Srobert                        File::eOpenOptionReadWrite);
317*f6aab3d8Srobert 
318*f6aab3d8Srobert       if (rw == eOpenOptionWriteOnly || rw == eOpenOptionReadWrite) {
319061da546Spatrick         if (::fflush(m_stream) == EOF)
320061da546Spatrick           error.SetErrorToErrno();
321061da546Spatrick       }
322061da546Spatrick     }
323*f6aab3d8Srobert   }
324061da546Spatrick   if (DescriptorIsValid() && m_own_descriptor) {
325061da546Spatrick     if (::close(m_descriptor) != 0)
326061da546Spatrick       error.SetErrorToErrno();
327061da546Spatrick   }
328061da546Spatrick   m_descriptor = kInvalidDescriptor;
329061da546Spatrick   m_stream = kInvalidStream;
330061da546Spatrick   m_options = OpenOptions(0);
331061da546Spatrick   m_own_stream = false;
332061da546Spatrick   m_own_descriptor = false;
333061da546Spatrick   m_is_interactive = eLazyBoolCalculate;
334061da546Spatrick   m_is_real_terminal = eLazyBoolCalculate;
335061da546Spatrick   return error;
336061da546Spatrick }
337061da546Spatrick 
GetFileSpec(FileSpec & file_spec) const338061da546Spatrick Status NativeFile::GetFileSpec(FileSpec &file_spec) const {
339061da546Spatrick   Status error;
340061da546Spatrick #ifdef F_GETPATH
341061da546Spatrick   if (IsValid()) {
342061da546Spatrick     char path[PATH_MAX];
343061da546Spatrick     if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1)
344061da546Spatrick       error.SetErrorToErrno();
345061da546Spatrick     else
346061da546Spatrick       file_spec.SetFile(path, FileSpec::Style::native);
347061da546Spatrick   } else {
348061da546Spatrick     error.SetErrorString("invalid file handle");
349061da546Spatrick   }
350061da546Spatrick #elif defined(__linux__)
351061da546Spatrick   char proc[64];
352061da546Spatrick   char path[PATH_MAX];
353061da546Spatrick   if (::snprintf(proc, sizeof(proc), "/proc/self/fd/%d", GetDescriptor()) < 0)
354061da546Spatrick     error.SetErrorString("cannot resolve file descriptor");
355061da546Spatrick   else {
356061da546Spatrick     ssize_t len;
357061da546Spatrick     if ((len = ::readlink(proc, path, sizeof(path) - 1)) == -1)
358061da546Spatrick       error.SetErrorToErrno();
359061da546Spatrick     else {
360061da546Spatrick       path[len] = '\0';
361061da546Spatrick       file_spec.SetFile(path, FileSpec::Style::native);
362061da546Spatrick     }
363061da546Spatrick   }
364061da546Spatrick #else
365061da546Spatrick   error.SetErrorString(
366061da546Spatrick       "NativeFile::GetFileSpec is not supported on this platform");
367061da546Spatrick #endif
368061da546Spatrick 
369061da546Spatrick   if (error.Fail())
370061da546Spatrick     file_spec.Clear();
371061da546Spatrick   return error;
372061da546Spatrick }
373061da546Spatrick 
SeekFromStart(off_t offset,Status * error_ptr)374061da546Spatrick off_t NativeFile::SeekFromStart(off_t offset, Status *error_ptr) {
375061da546Spatrick   off_t result = 0;
376061da546Spatrick   if (DescriptorIsValid()) {
377061da546Spatrick     result = ::lseek(m_descriptor, offset, SEEK_SET);
378061da546Spatrick 
379061da546Spatrick     if (error_ptr) {
380061da546Spatrick       if (result == -1)
381061da546Spatrick         error_ptr->SetErrorToErrno();
382061da546Spatrick       else
383061da546Spatrick         error_ptr->Clear();
384061da546Spatrick     }
385061da546Spatrick   } else if (StreamIsValid()) {
386061da546Spatrick     result = ::fseek(m_stream, offset, SEEK_SET);
387061da546Spatrick 
388061da546Spatrick     if (error_ptr) {
389061da546Spatrick       if (result == -1)
390061da546Spatrick         error_ptr->SetErrorToErrno();
391061da546Spatrick       else
392061da546Spatrick         error_ptr->Clear();
393061da546Spatrick     }
394061da546Spatrick   } else if (error_ptr) {
395061da546Spatrick     error_ptr->SetErrorString("invalid file handle");
396061da546Spatrick   }
397061da546Spatrick   return result;
398061da546Spatrick }
399061da546Spatrick 
SeekFromCurrent(off_t offset,Status * error_ptr)400061da546Spatrick off_t NativeFile::SeekFromCurrent(off_t offset, Status *error_ptr) {
401061da546Spatrick   off_t result = -1;
402061da546Spatrick   if (DescriptorIsValid()) {
403061da546Spatrick     result = ::lseek(m_descriptor, offset, SEEK_CUR);
404061da546Spatrick 
405061da546Spatrick     if (error_ptr) {
406061da546Spatrick       if (result == -1)
407061da546Spatrick         error_ptr->SetErrorToErrno();
408061da546Spatrick       else
409061da546Spatrick         error_ptr->Clear();
410061da546Spatrick     }
411061da546Spatrick   } else if (StreamIsValid()) {
412061da546Spatrick     result = ::fseek(m_stream, offset, SEEK_CUR);
413061da546Spatrick 
414061da546Spatrick     if (error_ptr) {
415061da546Spatrick       if (result == -1)
416061da546Spatrick         error_ptr->SetErrorToErrno();
417061da546Spatrick       else
418061da546Spatrick         error_ptr->Clear();
419061da546Spatrick     }
420061da546Spatrick   } else if (error_ptr) {
421061da546Spatrick     error_ptr->SetErrorString("invalid file handle");
422061da546Spatrick   }
423061da546Spatrick   return result;
424061da546Spatrick }
425061da546Spatrick 
SeekFromEnd(off_t offset,Status * error_ptr)426061da546Spatrick off_t NativeFile::SeekFromEnd(off_t offset, Status *error_ptr) {
427061da546Spatrick   off_t result = -1;
428061da546Spatrick   if (DescriptorIsValid()) {
429061da546Spatrick     result = ::lseek(m_descriptor, offset, SEEK_END);
430061da546Spatrick 
431061da546Spatrick     if (error_ptr) {
432061da546Spatrick       if (result == -1)
433061da546Spatrick         error_ptr->SetErrorToErrno();
434061da546Spatrick       else
435061da546Spatrick         error_ptr->Clear();
436061da546Spatrick     }
437061da546Spatrick   } else if (StreamIsValid()) {
438061da546Spatrick     result = ::fseek(m_stream, offset, SEEK_END);
439061da546Spatrick 
440061da546Spatrick     if (error_ptr) {
441061da546Spatrick       if (result == -1)
442061da546Spatrick         error_ptr->SetErrorToErrno();
443061da546Spatrick       else
444061da546Spatrick         error_ptr->Clear();
445061da546Spatrick     }
446061da546Spatrick   } else if (error_ptr) {
447061da546Spatrick     error_ptr->SetErrorString("invalid file handle");
448061da546Spatrick   }
449061da546Spatrick   return result;
450061da546Spatrick }
451061da546Spatrick 
Flush()452061da546Spatrick Status NativeFile::Flush() {
453061da546Spatrick   Status error;
454061da546Spatrick   if (StreamIsValid()) {
455061da546Spatrick     if (llvm::sys::RetryAfterSignal(EOF, ::fflush, m_stream) == EOF)
456061da546Spatrick       error.SetErrorToErrno();
457061da546Spatrick   } else if (!DescriptorIsValid()) {
458061da546Spatrick     error.SetErrorString("invalid file handle");
459061da546Spatrick   }
460061da546Spatrick   return error;
461061da546Spatrick }
462061da546Spatrick 
Sync()463061da546Spatrick Status NativeFile::Sync() {
464061da546Spatrick   Status error;
465061da546Spatrick   if (DescriptorIsValid()) {
466061da546Spatrick #ifdef _WIN32
467061da546Spatrick     int err = FlushFileBuffers((HANDLE)_get_osfhandle(m_descriptor));
468061da546Spatrick     if (err == 0)
469061da546Spatrick       error.SetErrorToGenericError();
470061da546Spatrick #else
471061da546Spatrick     if (llvm::sys::RetryAfterSignal(-1, ::fsync, m_descriptor) == -1)
472061da546Spatrick       error.SetErrorToErrno();
473061da546Spatrick #endif
474061da546Spatrick   } else {
475061da546Spatrick     error.SetErrorString("invalid file handle");
476061da546Spatrick   }
477061da546Spatrick   return error;
478061da546Spatrick }
479061da546Spatrick 
480061da546Spatrick #if defined(__APPLE__)
481061da546Spatrick // Darwin kernels only can read/write <= INT_MAX bytes
482061da546Spatrick #define MAX_READ_SIZE INT_MAX
483061da546Spatrick #define MAX_WRITE_SIZE INT_MAX
484061da546Spatrick #endif
485061da546Spatrick 
Read(void * buf,size_t & num_bytes)486061da546Spatrick Status NativeFile::Read(void *buf, size_t &num_bytes) {
487061da546Spatrick   Status error;
488061da546Spatrick 
489061da546Spatrick #if defined(MAX_READ_SIZE)
490061da546Spatrick   if (num_bytes > MAX_READ_SIZE) {
491061da546Spatrick     uint8_t *p = (uint8_t *)buf;
492061da546Spatrick     size_t bytes_left = num_bytes;
493061da546Spatrick     // Init the num_bytes read to zero
494061da546Spatrick     num_bytes = 0;
495061da546Spatrick 
496061da546Spatrick     while (bytes_left > 0) {
497061da546Spatrick       size_t curr_num_bytes;
498061da546Spatrick       if (bytes_left > MAX_READ_SIZE)
499061da546Spatrick         curr_num_bytes = MAX_READ_SIZE;
500061da546Spatrick       else
501061da546Spatrick         curr_num_bytes = bytes_left;
502061da546Spatrick 
503061da546Spatrick       error = Read(p + num_bytes, curr_num_bytes);
504061da546Spatrick 
505061da546Spatrick       // Update how many bytes were read
506061da546Spatrick       num_bytes += curr_num_bytes;
507061da546Spatrick       if (bytes_left < curr_num_bytes)
508061da546Spatrick         bytes_left = 0;
509061da546Spatrick       else
510061da546Spatrick         bytes_left -= curr_num_bytes;
511061da546Spatrick 
512061da546Spatrick       if (error.Fail())
513061da546Spatrick         break;
514061da546Spatrick     }
515061da546Spatrick     return error;
516061da546Spatrick   }
517061da546Spatrick #endif
518061da546Spatrick 
519061da546Spatrick   ssize_t bytes_read = -1;
520061da546Spatrick   if (DescriptorIsValid()) {
521061da546Spatrick     bytes_read = llvm::sys::RetryAfterSignal(-1, ::read, m_descriptor, buf, num_bytes);
522061da546Spatrick     if (bytes_read == -1) {
523061da546Spatrick       error.SetErrorToErrno();
524061da546Spatrick       num_bytes = 0;
525061da546Spatrick     } else
526061da546Spatrick       num_bytes = bytes_read;
527061da546Spatrick   } else if (StreamIsValid()) {
528061da546Spatrick     bytes_read = ::fread(buf, 1, num_bytes, m_stream);
529061da546Spatrick 
530061da546Spatrick     if (bytes_read == 0) {
531061da546Spatrick       if (::feof(m_stream))
532061da546Spatrick         error.SetErrorString("feof");
533061da546Spatrick       else if (::ferror(m_stream))
534061da546Spatrick         error.SetErrorString("ferror");
535061da546Spatrick       num_bytes = 0;
536061da546Spatrick     } else
537061da546Spatrick       num_bytes = bytes_read;
538061da546Spatrick   } else {
539061da546Spatrick     num_bytes = 0;
540061da546Spatrick     error.SetErrorString("invalid file handle");
541061da546Spatrick   }
542061da546Spatrick   return error;
543061da546Spatrick }
544061da546Spatrick 
Write(const void * buf,size_t & num_bytes)545061da546Spatrick Status NativeFile::Write(const void *buf, size_t &num_bytes) {
546061da546Spatrick   Status error;
547061da546Spatrick 
548061da546Spatrick #if defined(MAX_WRITE_SIZE)
549061da546Spatrick   if (num_bytes > MAX_WRITE_SIZE) {
550061da546Spatrick     const uint8_t *p = (const uint8_t *)buf;
551061da546Spatrick     size_t bytes_left = num_bytes;
552061da546Spatrick     // Init the num_bytes written to zero
553061da546Spatrick     num_bytes = 0;
554061da546Spatrick 
555061da546Spatrick     while (bytes_left > 0) {
556061da546Spatrick       size_t curr_num_bytes;
557061da546Spatrick       if (bytes_left > MAX_WRITE_SIZE)
558061da546Spatrick         curr_num_bytes = MAX_WRITE_SIZE;
559061da546Spatrick       else
560061da546Spatrick         curr_num_bytes = bytes_left;
561061da546Spatrick 
562061da546Spatrick       error = Write(p + num_bytes, curr_num_bytes);
563061da546Spatrick 
564061da546Spatrick       // Update how many bytes were read
565061da546Spatrick       num_bytes += curr_num_bytes;
566061da546Spatrick       if (bytes_left < curr_num_bytes)
567061da546Spatrick         bytes_left = 0;
568061da546Spatrick       else
569061da546Spatrick         bytes_left -= curr_num_bytes;
570061da546Spatrick 
571061da546Spatrick       if (error.Fail())
572061da546Spatrick         break;
573061da546Spatrick     }
574061da546Spatrick     return error;
575061da546Spatrick   }
576061da546Spatrick #endif
577061da546Spatrick 
578061da546Spatrick   ssize_t bytes_written = -1;
579061da546Spatrick   if (DescriptorIsValid()) {
580061da546Spatrick     bytes_written =
581061da546Spatrick         llvm::sys::RetryAfterSignal(-1, ::write, m_descriptor, buf, num_bytes);
582061da546Spatrick     if (bytes_written == -1) {
583061da546Spatrick       error.SetErrorToErrno();
584061da546Spatrick       num_bytes = 0;
585061da546Spatrick     } else
586061da546Spatrick       num_bytes = bytes_written;
587061da546Spatrick   } else if (StreamIsValid()) {
588061da546Spatrick     bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
589061da546Spatrick 
590061da546Spatrick     if (bytes_written == 0) {
591061da546Spatrick       if (::feof(m_stream))
592061da546Spatrick         error.SetErrorString("feof");
593061da546Spatrick       else if (::ferror(m_stream))
594061da546Spatrick         error.SetErrorString("ferror");
595061da546Spatrick       num_bytes = 0;
596061da546Spatrick     } else
597061da546Spatrick       num_bytes = bytes_written;
598061da546Spatrick 
599061da546Spatrick   } else {
600061da546Spatrick     num_bytes = 0;
601061da546Spatrick     error.SetErrorString("invalid file handle");
602061da546Spatrick   }
603061da546Spatrick 
604061da546Spatrick   return error;
605061da546Spatrick }
606061da546Spatrick 
Read(void * buf,size_t & num_bytes,off_t & offset)607061da546Spatrick Status NativeFile::Read(void *buf, size_t &num_bytes, off_t &offset) {
608061da546Spatrick   Status error;
609061da546Spatrick 
610061da546Spatrick #if defined(MAX_READ_SIZE)
611061da546Spatrick   if (num_bytes > MAX_READ_SIZE) {
612061da546Spatrick     uint8_t *p = (uint8_t *)buf;
613061da546Spatrick     size_t bytes_left = num_bytes;
614061da546Spatrick     // Init the num_bytes read to zero
615061da546Spatrick     num_bytes = 0;
616061da546Spatrick 
617061da546Spatrick     while (bytes_left > 0) {
618061da546Spatrick       size_t curr_num_bytes;
619061da546Spatrick       if (bytes_left > MAX_READ_SIZE)
620061da546Spatrick         curr_num_bytes = MAX_READ_SIZE;
621061da546Spatrick       else
622061da546Spatrick         curr_num_bytes = bytes_left;
623061da546Spatrick 
624061da546Spatrick       error = Read(p + num_bytes, curr_num_bytes, offset);
625061da546Spatrick 
626061da546Spatrick       // Update how many bytes were read
627061da546Spatrick       num_bytes += curr_num_bytes;
628061da546Spatrick       if (bytes_left < curr_num_bytes)
629061da546Spatrick         bytes_left = 0;
630061da546Spatrick       else
631061da546Spatrick         bytes_left -= curr_num_bytes;
632061da546Spatrick 
633061da546Spatrick       if (error.Fail())
634061da546Spatrick         break;
635061da546Spatrick     }
636061da546Spatrick     return error;
637061da546Spatrick   }
638061da546Spatrick #endif
639061da546Spatrick 
640061da546Spatrick #ifndef _WIN32
641061da546Spatrick   int fd = GetDescriptor();
642061da546Spatrick   if (fd != kInvalidDescriptor) {
643061da546Spatrick     ssize_t bytes_read =
644061da546Spatrick         llvm::sys::RetryAfterSignal(-1, ::pread, fd, buf, num_bytes, offset);
645061da546Spatrick     if (bytes_read < 0) {
646061da546Spatrick       num_bytes = 0;
647061da546Spatrick       error.SetErrorToErrno();
648061da546Spatrick     } else {
649061da546Spatrick       offset += bytes_read;
650061da546Spatrick       num_bytes = bytes_read;
651061da546Spatrick     }
652061da546Spatrick   } else {
653061da546Spatrick     num_bytes = 0;
654061da546Spatrick     error.SetErrorString("invalid file handle");
655061da546Spatrick   }
656061da546Spatrick #else
657061da546Spatrick   std::lock_guard<std::mutex> guard(offset_access_mutex);
658061da546Spatrick   long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
659061da546Spatrick   SeekFromStart(offset);
660061da546Spatrick   error = Read(buf, num_bytes);
661061da546Spatrick   if (!error.Fail())
662061da546Spatrick     SeekFromStart(cur);
663061da546Spatrick #endif
664061da546Spatrick   return error;
665061da546Spatrick }
666061da546Spatrick 
Write(const void * buf,size_t & num_bytes,off_t & offset)667061da546Spatrick Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) {
668061da546Spatrick   Status error;
669061da546Spatrick 
670061da546Spatrick #if defined(MAX_WRITE_SIZE)
671061da546Spatrick   if (num_bytes > MAX_WRITE_SIZE) {
672061da546Spatrick     const uint8_t *p = (const uint8_t *)buf;
673061da546Spatrick     size_t bytes_left = num_bytes;
674061da546Spatrick     // Init the num_bytes written to zero
675061da546Spatrick     num_bytes = 0;
676061da546Spatrick 
677061da546Spatrick     while (bytes_left > 0) {
678061da546Spatrick       size_t curr_num_bytes;
679061da546Spatrick       if (bytes_left > MAX_WRITE_SIZE)
680061da546Spatrick         curr_num_bytes = MAX_WRITE_SIZE;
681061da546Spatrick       else
682061da546Spatrick         curr_num_bytes = bytes_left;
683061da546Spatrick 
684061da546Spatrick       error = Write(p + num_bytes, curr_num_bytes, offset);
685061da546Spatrick 
686061da546Spatrick       // Update how many bytes were read
687061da546Spatrick       num_bytes += curr_num_bytes;
688061da546Spatrick       if (bytes_left < curr_num_bytes)
689061da546Spatrick         bytes_left = 0;
690061da546Spatrick       else
691061da546Spatrick         bytes_left -= curr_num_bytes;
692061da546Spatrick 
693061da546Spatrick       if (error.Fail())
694061da546Spatrick         break;
695061da546Spatrick     }
696061da546Spatrick     return error;
697061da546Spatrick   }
698061da546Spatrick #endif
699061da546Spatrick 
700061da546Spatrick   int fd = GetDescriptor();
701061da546Spatrick   if (fd != kInvalidDescriptor) {
702061da546Spatrick #ifndef _WIN32
703061da546Spatrick     ssize_t bytes_written =
704061da546Spatrick         llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset);
705061da546Spatrick     if (bytes_written < 0) {
706061da546Spatrick       num_bytes = 0;
707061da546Spatrick       error.SetErrorToErrno();
708061da546Spatrick     } else {
709061da546Spatrick       offset += bytes_written;
710061da546Spatrick       num_bytes = bytes_written;
711061da546Spatrick     }
712061da546Spatrick #else
713061da546Spatrick     std::lock_guard<std::mutex> guard(offset_access_mutex);
714061da546Spatrick     long cur = ::lseek(m_descriptor, 0, SEEK_CUR);
715061da546Spatrick     SeekFromStart(offset);
716061da546Spatrick     error = Write(buf, num_bytes);
717061da546Spatrick     long after = ::lseek(m_descriptor, 0, SEEK_CUR);
718061da546Spatrick 
719061da546Spatrick     if (!error.Fail())
720061da546Spatrick       SeekFromStart(cur);
721061da546Spatrick 
722061da546Spatrick     offset = after;
723061da546Spatrick #endif
724061da546Spatrick   } else {
725061da546Spatrick     num_bytes = 0;
726061da546Spatrick     error.SetErrorString("invalid file handle");
727061da546Spatrick   }
728061da546Spatrick   return error;
729061da546Spatrick }
730061da546Spatrick 
PrintfVarArg(const char * format,va_list args)731061da546Spatrick size_t NativeFile::PrintfVarArg(const char *format, va_list args) {
732061da546Spatrick   if (StreamIsValid()) {
733061da546Spatrick     return ::vfprintf(m_stream, format, args);
734061da546Spatrick   } else {
735061da546Spatrick     return File::PrintfVarArg(format, args);
736061da546Spatrick   }
737061da546Spatrick }
738061da546Spatrick 
ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options)739061da546Spatrick mode_t File::ConvertOpenOptionsForPOSIXOpen(OpenOptions open_options) {
740061da546Spatrick   mode_t mode = 0;
741*f6aab3d8Srobert   File::OpenOptions rw =
742*f6aab3d8Srobert       open_options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
743*f6aab3d8Srobert                       File::eOpenOptionReadWrite);
744*f6aab3d8Srobert   if (rw == eOpenOptionReadWrite)
745061da546Spatrick     mode |= O_RDWR;
746*f6aab3d8Srobert   else if (rw == eOpenOptionWriteOnly)
747061da546Spatrick     mode |= O_WRONLY;
748*f6aab3d8Srobert   else if (rw == eOpenOptionReadOnly)
749*f6aab3d8Srobert     mode |= O_RDONLY;
750061da546Spatrick 
751061da546Spatrick   if (open_options & eOpenOptionAppend)
752061da546Spatrick     mode |= O_APPEND;
753061da546Spatrick 
754061da546Spatrick   if (open_options & eOpenOptionTruncate)
755061da546Spatrick     mode |= O_TRUNC;
756061da546Spatrick 
757061da546Spatrick   if (open_options & eOpenOptionNonBlocking)
758061da546Spatrick     mode |= O_NONBLOCK;
759061da546Spatrick 
760061da546Spatrick   if (open_options & eOpenOptionCanCreateNewOnly)
761061da546Spatrick     mode |= O_CREAT | O_EXCL;
762061da546Spatrick   else if (open_options & eOpenOptionCanCreate)
763061da546Spatrick     mode |= O_CREAT;
764061da546Spatrick 
765061da546Spatrick   return mode;
766061da546Spatrick }
767061da546Spatrick 
768*f6aab3d8Srobert llvm::Expected<SerialPort::Options>
OptionsFromURL(llvm::StringRef urlqs)769*f6aab3d8Srobert SerialPort::OptionsFromURL(llvm::StringRef urlqs) {
770*f6aab3d8Srobert   SerialPort::Options serial_options;
771*f6aab3d8Srobert   for (llvm::StringRef x : llvm::split(urlqs, '&')) {
772*f6aab3d8Srobert     if (x.consume_front("baud=")) {
773*f6aab3d8Srobert       unsigned int baud_rate;
774*f6aab3d8Srobert       if (!llvm::to_integer(x, baud_rate, 10))
775*f6aab3d8Srobert         return llvm::createStringError(llvm::inconvertibleErrorCode(),
776*f6aab3d8Srobert                                        "Invalid baud rate: %s",
777*f6aab3d8Srobert                                        x.str().c_str());
778*f6aab3d8Srobert       serial_options.BaudRate = baud_rate;
779*f6aab3d8Srobert     } else if (x.consume_front("parity=")) {
780*f6aab3d8Srobert       serial_options.Parity =
781*f6aab3d8Srobert           llvm::StringSwitch<std::optional<Terminal::Parity>>(x)
782*f6aab3d8Srobert               .Case("no", Terminal::Parity::No)
783*f6aab3d8Srobert               .Case("even", Terminal::Parity::Even)
784*f6aab3d8Srobert               .Case("odd", Terminal::Parity::Odd)
785*f6aab3d8Srobert               .Case("mark", Terminal::Parity::Mark)
786*f6aab3d8Srobert               .Case("space", Terminal::Parity::Space)
787*f6aab3d8Srobert               .Default(std::nullopt);
788*f6aab3d8Srobert       if (!serial_options.Parity)
789*f6aab3d8Srobert         return llvm::createStringError(
790*f6aab3d8Srobert             llvm::inconvertibleErrorCode(),
791*f6aab3d8Srobert             "Invalid parity (must be no, even, odd, mark or space): %s",
792*f6aab3d8Srobert             x.str().c_str());
793*f6aab3d8Srobert     } else if (x.consume_front("parity-check=")) {
794*f6aab3d8Srobert       serial_options.ParityCheck =
795*f6aab3d8Srobert           llvm::StringSwitch<std::optional<Terminal::ParityCheck>>(x)
796*f6aab3d8Srobert               .Case("no", Terminal::ParityCheck::No)
797*f6aab3d8Srobert               .Case("replace", Terminal::ParityCheck::ReplaceWithNUL)
798*f6aab3d8Srobert               .Case("ignore", Terminal::ParityCheck::Ignore)
799*f6aab3d8Srobert               // "mark" mode is not currently supported as it requires special
800*f6aab3d8Srobert               // input processing
801*f6aab3d8Srobert               // .Case("mark", Terminal::ParityCheck::Mark)
802*f6aab3d8Srobert               .Default(std::nullopt);
803*f6aab3d8Srobert       if (!serial_options.ParityCheck)
804*f6aab3d8Srobert         return llvm::createStringError(
805*f6aab3d8Srobert             llvm::inconvertibleErrorCode(),
806*f6aab3d8Srobert             "Invalid parity-check (must be no, replace, ignore or mark): %s",
807*f6aab3d8Srobert             x.str().c_str());
808*f6aab3d8Srobert     } else if (x.consume_front("stop-bits=")) {
809*f6aab3d8Srobert       unsigned int stop_bits;
810*f6aab3d8Srobert       if (!llvm::to_integer(x, stop_bits, 10) ||
811*f6aab3d8Srobert           (stop_bits != 1 && stop_bits != 2))
812*f6aab3d8Srobert         return llvm::createStringError(
813*f6aab3d8Srobert             llvm::inconvertibleErrorCode(),
814*f6aab3d8Srobert             "Invalid stop bit number (must be 1 or 2): %s", x.str().c_str());
815*f6aab3d8Srobert       serial_options.StopBits = stop_bits;
816*f6aab3d8Srobert     } else
817*f6aab3d8Srobert       return llvm::createStringError(llvm::inconvertibleErrorCode(),
818*f6aab3d8Srobert                                      "Unknown parameter: %s", x.str().c_str());
819*f6aab3d8Srobert   }
820*f6aab3d8Srobert   return serial_options;
821*f6aab3d8Srobert }
822*f6aab3d8Srobert 
823*f6aab3d8Srobert llvm::Expected<std::unique_ptr<SerialPort>>
Create(int fd,OpenOptions options,Options serial_options,bool transfer_ownership)824*f6aab3d8Srobert SerialPort::Create(int fd, OpenOptions options, Options serial_options,
825*f6aab3d8Srobert                    bool transfer_ownership) {
826*f6aab3d8Srobert   std::unique_ptr<SerialPort> out{
827*f6aab3d8Srobert       new SerialPort(fd, options, serial_options, transfer_ownership)};
828*f6aab3d8Srobert 
829*f6aab3d8Srobert   if (!out->GetIsInteractive())
830*f6aab3d8Srobert     return llvm::createStringError(llvm::inconvertibleErrorCode(),
831*f6aab3d8Srobert                                    "the specified file is not a teletype");
832*f6aab3d8Srobert 
833*f6aab3d8Srobert   Terminal term{fd};
834*f6aab3d8Srobert   if (llvm::Error error = term.SetRaw())
835*f6aab3d8Srobert     return std::move(error);
836*f6aab3d8Srobert   if (serial_options.BaudRate) {
837*f6aab3d8Srobert     if (llvm::Error error = term.SetBaudRate(*serial_options.BaudRate))
838*f6aab3d8Srobert       return std::move(error);
839*f6aab3d8Srobert   }
840*f6aab3d8Srobert   if (serial_options.Parity) {
841*f6aab3d8Srobert     if (llvm::Error error = term.SetParity(*serial_options.Parity))
842*f6aab3d8Srobert       return std::move(error);
843*f6aab3d8Srobert   }
844*f6aab3d8Srobert   if (serial_options.ParityCheck) {
845*f6aab3d8Srobert     if (llvm::Error error = term.SetParityCheck(*serial_options.ParityCheck))
846*f6aab3d8Srobert       return std::move(error);
847*f6aab3d8Srobert   }
848*f6aab3d8Srobert   if (serial_options.StopBits) {
849*f6aab3d8Srobert     if (llvm::Error error = term.SetStopBits(*serial_options.StopBits))
850*f6aab3d8Srobert       return std::move(error);
851*f6aab3d8Srobert   }
852*f6aab3d8Srobert 
853*f6aab3d8Srobert   return std::move(out);
854*f6aab3d8Srobert }
855*f6aab3d8Srobert 
SerialPort(int fd,OpenOptions options,SerialPort::Options serial_options,bool transfer_ownership)856*f6aab3d8Srobert SerialPort::SerialPort(int fd, OpenOptions options,
857*f6aab3d8Srobert                        SerialPort::Options serial_options,
858*f6aab3d8Srobert                        bool transfer_ownership)
859*f6aab3d8Srobert     : NativeFile(fd, options, transfer_ownership), m_state(fd) {}
860*f6aab3d8Srobert 
Close()861*f6aab3d8Srobert Status SerialPort::Close() {
862*f6aab3d8Srobert   m_state.Restore();
863*f6aab3d8Srobert   return NativeFile::Close();
864*f6aab3d8Srobert }
865*f6aab3d8Srobert 
866061da546Spatrick char File::ID = 0;
867061da546Spatrick char NativeFile::ID = 0;
868*f6aab3d8Srobert char SerialPort::ID = 0;
869