1111#include " llbuild/Basic/Stat.h"
1212#include " llvm/ADT/SmallVector.h"
1313#include " llvm/Support/ConvertUTF.h"
14+ #include " llvm/Support/Path.h"
1415
1516#if defined(_WIN32)
1617#include " LeanWindows.h"
1718#include < Shlwapi.h>
1819#include < direct.h>
1920#include < io.h>
21+ #include < time.h>
2022#else
2123#include < fnmatch.h>
2224#include < unistd.h>
@@ -28,7 +30,9 @@ using namespace llbuild::basic;
2830
2931bool sys::chdir (const char *fileName) {
3032#if defined(_WIN32)
31- return SetCurrentDirectoryA (fileName);
33+ llvm::SmallVector<UTF16, 20 > wFileName;
34+ llvm::convertUTF8ToUTF16String (fileName, wFileName);
35+ return SetCurrentDirectoryW ((LPCWSTR)wFileName.data ());
3236#else
3337 return ::chdir (fileName) == 0 ;
3438#endif
@@ -42,11 +46,68 @@ int sys::close(int fileHandle) {
4246#endif
4347}
4448
49+ #if defined(_WIN32)
50+ time_t filetimeToTime_t (FILETIME ft) {
51+ long long ltime = ft.dwLowDateTime | ((long long )ft.dwHighDateTime << 32 );
52+ return (time_t )((ltime - 116444736000000000 ) / 10000000 );
53+ }
54+ #endif
55+
4556int sys::lstat (const char *fileName, sys::StatStruct *buf) {
4657#if defined(_WIN32)
47- // We deliberately ignore lstat on Windows, and delegate
48- // to stat.
49- return ::_stat (fileName, buf);
58+ llvm::SmallVector<UTF16, 20 > wfilename;
59+ llvm::convertUTF8ToUTF16String (fileName, wfilename);
60+ HANDLE h = CreateFileW (
61+ /* lpFileName=*/ (LPCWSTR)wfilename.data (),
62+ /* dwDesiredAccess=*/ 0 ,
63+ /* dwShareMode=*/ FILE_SHARE_READ,
64+ /* lpSecurityAttributes=*/ NULL ,
65+ /* dwCreationDisposition=*/ OPEN_EXISTING,
66+ /* dwFlagsAndAttributes=*/ FILE_FLAG_OPEN_REPARSE_POINT |
67+ FILE_FLAG_BACKUP_SEMANTICS,
68+ /* hTemplateFile=*/ NULL );
69+ if (h == INVALID_HANDLE_VALUE) {
70+ int err = GetLastError ();
71+ if (err == ERROR_FILE_NOT_FOUND) {
72+ errno = ENOENT;
73+ }
74+ return -1 ;
75+ }
76+ BY_HANDLE_FILE_INFORMATION info;
77+ GetFileInformationByHandle (h, &info);
78+ // Group id is always 0 on Windows
79+ buf->st_gid = 0 ;
80+ buf->st_atime = filetimeToTime_t (info.ftLastAccessTime );
81+ buf->st_ctime = filetimeToTime_t (info.ftCreationTime );
82+ buf->st_dev = info.dwVolumeSerialNumber ;
83+ // inodes have meaning on FAT/HPFS/NTFS
84+ buf->st_ino = 0 ;
85+ buf->st_rdev = info.dwVolumeSerialNumber ;
86+ buf->st_mode =
87+ // On a symlink to a directory, Windows sets both the REPARSE_POINT and
88+ // DIRECTORY attributes. Since Windows doesn't provide S_IFLNK and we
89+ // want unix style "symlinks to directories are not directories
90+ // themselves, we say symlinks are regular files
91+ (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
92+ ? _S_IFREG
93+ : (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR
94+ : _S_IFREG;
95+ buf->st_mode |= (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
96+ ? _S_IREAD
97+ : _S_IREAD | _S_IWRITE;
98+ llvm::StringRef extension =
99+ llvm::sys::path::extension (llvm::StringRef (fileName));
100+ if (extension == " .exe" || extension == " .cmd" || extension == " .bat" ||
101+ extension == " .com" ) {
102+ buf->st_mode |= _S_IEXEC;
103+ }
104+ buf->st_mtime = filetimeToTime_t (info.ftLastWriteTime );
105+ buf->st_nlink = info.nNumberOfLinks ;
106+ buf->st_size = ((long long )info.nFileSizeHigh << 32 ) | info.nFileSizeLow ;
107+ // Uid is always 0 on Windows systems
108+ buf->st_uid = 0 ;
109+ CloseHandle (h);
110+ return 0 ;
50111#else
51112 return ::lstat (fileName, buf);
52113#endif
@@ -109,17 +170,25 @@ int sys::stat(const char *fileName, StatStruct *buf) {
109170#endif
110171}
111172
112- int sys::symlink (const char *source, const char *target) {
173+ // Create a symlink named linkPath which contains the string pointsTo
174+ int sys::symlink (const char *pointsTo, const char *linkPath) {
113175#if defined(_WIN32)
114- DWORD attributes = GetFileAttributesA (source);
115- if (attributes != INVALID_FILE_ATTRIBUTES &&
116- (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ) {
117- return ::CreateSymbolicLinkA (source, target, SYMBOLIC_LINK_FLAG_DIRECTORY);
118- }
119-
120- return ::CreateSymbolicLinkA (source, target, 0 );
176+ llvm::SmallVector<UTF16, 20 > wPointsTo;
177+ llvm::convertUTF8ToUTF16String (pointsTo, wPointsTo);
178+ llvm::SmallVector<UTF16, 20 > wLinkPath;
179+ llvm::convertUTF8ToUTF16String (linkPath, wLinkPath);
180+ DWORD attributes = GetFileAttributesW ((LPCWSTR)wPointsTo.data ());
181+ DWORD directoryFlag = (attributes != INVALID_FILE_ATTRIBUTES &&
182+ attributes & FILE_ATTRIBUTE_DIRECTORY)
183+ ? SYMBOLIC_LINK_FLAG_DIRECTORY
184+ : 0 ;
185+ // Note that CreateSymbolicLinkW takes its arguments in reverse order
186+ // compared to symlink/_symlink
187+ return !::CreateSymbolicLinkW (
188+ (LPCWSTR)wLinkPath.data (), (LPCWSTR)wPointsTo.data (),
189+ SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE | directoryFlag);
121190#else
122- return ::symlink (source, target );
191+ return ::symlink (pointsTo, linkPath );
123192#endif
124193}
125194
@@ -217,23 +286,29 @@ std::string sys::strerror(int error) {
217286
218287char *sys::strsep (char **stringp, const char *delim) {
219288#if defined(_WIN32)
220- char *begin, *end = *stringp;
289+ // If *stringp is NULL, the strsep() function returns NULL and does nothing
290+ // else.
221291 if (*stringp == NULL ) {
222292 return NULL ;
223293 }
224- int delimLen = strlen (delim);
225- bool found = false ;
226- while (*end) {
227- for (int i = 0 ; i < delimLen; i++) {
294+ char *begin = *stringp;
295+ char *end = *stringp;
296+ do {
297+ // Otherwise, this function finds the first token in the string *stringp,
298+ // that is delimited by one of the bytes in the string delim.
299+ for (int i = 0 ; delim[i] != ' \0 ' ; i++) {
228300 if (*end == delim[i]) {
229- found = true ;
301+ // This token is terminated by overwriting the delimiter with a null
302+ // byte ('\0'), and *stringp is updated to point past the token.
303+ *end = ' \0 ' ;
304+ *stringp = end + 1 ;
305+ return begin;
230306 }
231307 }
232- if (found) {
233- *stringp = end + 1 ;
234- }
235- }
236- *end = ' \0 ' ;
308+ } while (*(++end));
309+ // In case no delimiter was found, the token is taken to be the entire string
310+ // *stringp, and *stringp is made NULL.
311+ *stringp = NULL ;
237312 return begin;
238313#else
239314 return ::strsep (stringp, delim);
@@ -244,9 +319,20 @@ std::string sys::makeTmpDir() {
244319#if defined(_WIN32)
245320 char path[MAX_PATH];
246321 tmpnam_s (path, MAX_PATH);
322+ llvm::SmallVector<UTF16, 20 > wPath;
323+ llvm::convertUTF8ToUTF16String (path, wPath);
324+ CreateDirectoryW ((LPCWSTR)wPath.data (), NULL );
247325 return std::string (path);
248326#else
249327 char tmpDirPathBuf[] = " /tmp/fileXXXXXX" ;
250328 return std::string (mkdtemp (tmpDirPathBuf));
251329#endif
252330}
331+
332+ std::string sys::getPathSeparators () {
333+ #if defined(_WIN32)
334+ return " /\\ " ;
335+ #else
336+ return " /" ;
337+ #endif
338+ }
0 commit comments