Skip to content

Commit 5fc99e3

Browse files
committed
Using UTF-16 API on Windows OS instead UNIX UTF-8 API
1 parent b132a38 commit 5fc99e3

File tree

1 file changed

+168
-21
lines changed

1 file changed

+168
-21
lines changed

Foundation/FileManager.swift

Lines changed: 168 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ open class FileManager : NSObject {
137137
let modeT = number.uint32Value
138138
#endif
139139
#if CAN_IMPORT_MINGWCRT
140-
if chmod(path, Int32(mode_t(modeT))) != 0 {
140+
var wcpath = [UInt16](path.utf16)
141+
wcpath.append(UInt16(0))
142+
143+
if _wchmod(wcpath, Int32(mode_t(modeT))) != 0 {
141144
fatalError("errno \(errno)")
142145
}
143146
#else
@@ -156,6 +159,10 @@ open class FileManager : NSObject {
156159
This method replaces createDirectoryAtPath:attributes:
157160
*/
158161
open func createDirectory(atPath path: String, withIntermediateDirectories createIntermediates: Bool, attributes: [FileAttributeKey : Any]? = [:]) throws {
162+
#if CAN_IMPORT_MINGWCRT
163+
var wcpath = [UInt16](path.utf16)
164+
wcpath.append(UInt16(0))
165+
#endif
159166
if createIntermediates {
160167
var isDir: ObjCBool = false
161168
if !fileExists(atPath: path, isDirectory: &isDir) {
@@ -164,7 +171,7 @@ open class FileManager : NSObject {
164171
try createDirectory(atPath: parent, withIntermediateDirectories: true, attributes: attributes)
165172
}
166173
#if CAN_IMPORT_MINGWCRT
167-
let mkdir_failed = _mkdir(path) != 0
174+
let mkdir_failed = _wmkdir(wcpath) != 0
168175
#else
169176
let mkdir_failed = mkdir(pathh, S_IRWXU | S_IRWXG | S_IRWXO) != 0
170177
#endif
@@ -180,7 +187,7 @@ open class FileManager : NSObject {
180187
}
181188
} else {
182189
#if CAN_IMPORT_MINGWCRT
183-
let mkdir_failed = _mkdir(path) != 0
190+
let mkdir_failed = _wmkdir(wcpath) != 0
184191
#else
185192
let mkdir_failed = mkdir(pathh, S_IRWXU | S_IRWXG | S_IRWXO) != 0
186193
#endif
@@ -207,14 +214,40 @@ open class FileManager : NSObject {
207214
*/
208215
open func contentsOfDirectory(atPath path: String) throws -> [String] {
209216
var contents : [String] = [String]()
210-
217+
#if CAN_IMPORT_MINGWCRT
218+
var wcpath = [UInt16](path.utf16)
219+
wcpath.append(UInt16(0))
220+
221+
let dir = _wopendir(wcpath)
222+
#else
211223
let dir = opendir(path)
212-
224+
#endif
225+
213226
if dir == nil {
214227
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadNoSuchFile.rawValue, userInfo: [NSFilePathErrorKey: path])
215228
}
216-
229+
230+
#if CAN_IMPORT_MINGWCRT
217231
defer {
232+
233+
_wclosedir(dir!)
234+
}
235+
236+
while let entry = _wreaddir(dir!) {
237+
let entryNameLen = withUnsafePointer(to: &entry.pointee.d_namlen) {
238+
UnsafeRawPointer($0).assumingMemoryBound(to: UInt16.self).pointee
239+
}
240+
let entryName = withUnsafePointer(to: &entry.pointee.d_name) {
241+
String(utf16CodeUnits:UnsafeRawPointer($0).assumingMemoryBound(to: UInt16.self), count:Int(entryNameLen))
242+
}
243+
244+
if entryName != "." && entryName != ".." {
245+
contents.append(entryName)
246+
}
247+
}
248+
#else
249+
defer {
250+
218251
closedir(dir!)
219252
}
220253

@@ -227,7 +260,7 @@ open class FileManager : NSObject {
227260
contents.append(entryName)
228261
}
229262
}
230-
263+
#endif
231264
return contents
232265
}
233266

@@ -245,31 +278,65 @@ open class FileManager : NSObject {
245278
- Returns: An array of NSString objects, each of which contains the path of an item in the directory specified by path. If path is a symbolic link, this method traverses the link. This method returns nil if it cannot retrieve the device of the linked-to file.
246279
*/
247280
open func subpathsOfDirectory(atPath path: String) throws -> [String] {
281+
var contents : [String] = [String]()
248282
#if CAN_IMPORT_MINGWCRT
249-
NSUnimplemented()
283+
var wcpath = [UInt16](path.utf16)
284+
wcpath.append(UInt16(0))
285+
286+
let dir = _wopendir(wcpath)
250287
#else
251-
var contents : [String] = [String]()
252288

253289
let dir = opendir(path)
290+
#endif
254291

255292
if dir == nil {
256293
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadNoSuchFile.rawValue, userInfo: [NSFilePathErrorKey: path])
257294
}
258295

296+
297+
#if CAN_IMPORT_MINGWCRT
259298
defer {
299+
300+
_wclosedir(dir!)
301+
}
302+
303+
var entry = _wreaddir(dir!)
304+
#else
305+
defer {
306+
260307
closedir(dir!)
261308
}
262-
309+
263310
var entry = readdir(dir!)
311+
#endif
264312

265313
while entry != nil {
314+
#if CAN_IMPORT_MINGWCRT
315+
let entryNameLen = withUnsafePointer(to: &entry!.pointee.d_namlen) {
316+
UnsafeRawPointer($0).assumingMemoryBound(to: UInt16.self).pointee
317+
}
318+
let entryName = withUnsafePointer(to: &entry!.pointee.d_name) {
319+
String(utf16CodeUnits:UnsafeRawPointer($0).assumingMemoryBound(to: UInt16.self), count:Int(entryNameLen))
320+
}
321+
#else
266322
let entryName = withUnsafePointer(to: &entry!.pointee.d_name) {
267323
String(cString: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self))
268324
}
325+
#endif
269326
// TODO: `entryName` should be limited in length to `entry.memory.d_namlen`.
270327
if entryName != "." && entryName != ".." {
271328
contents.append(entryName)
272-
329+
330+
#if CAN_IMPORT_MINGWCRT
331+
let subPath: String = path + "/" + entryName
332+
var isDir = false
333+
334+
if fileExists(atPath:subPath, isDirectory:&isDir) && isDir {
335+
print("\(subPath) isDir")
336+
let entries = try subpathsOfDirectory(atPath: subPath)
337+
contents.append(contentsOf: entries.map({file in "\(entryName)/\(file)"}))
338+
}
339+
#else
273340
let entryType = withUnsafePointer(to: &entry!.pointee.d_type) { (ptr) -> Int32 in
274341
return Int32(ptr.pointee)
275342
}
@@ -285,13 +352,16 @@ open class FileManager : NSObject {
285352
let entries = try subpathsOfDirectory(atPath: subPath)
286353
contents.append(contentsOf: entries.map({file in "\(entryName)/\(file)"}))
287354
}
355+
#endif
288356
}
289-
357+
#if CAN_IMPORT_MINGWCRT
358+
entry = _wreaddir(dir!)
359+
#else
290360
entry = readdir(dir!)
361+
#endif
291362
}
292363

293364
return contents
294-
#endif
295365
}
296366

297367
/* attributesOfItemAtPath:error: returns an NSDictionary of key/value pairs containing the attributes of the item (file, directory, symlink, etc.) at the path in question. If this method returns 'nil', an NSError will be returned by reference in the 'error' parameter. This method does not traverse a terminal symlink.
@@ -300,29 +370,46 @@ open class FileManager : NSObject {
300370
*/
301371
open func attributesOfItem(atPath path: String) throws -> [FileAttributeKey : Any] {
302372
#if CAN_IMPORT_MINGWCRT
303-
NSUnimplemented()
373+
var wcpath = [UInt16](path.utf16)
374+
wcpath.append(UInt16(0))
375+
376+
var s = _stat64()
377+
guard _wstat64(wcpath, &s) == 0 else {
378+
throw _NSErrorWithErrno(errno, reading: true, path: path)
379+
}
304380
#else
305381
var s = stat()
306382
guard lstat(path, &s) == 0 else {
307383
throw _NSErrorWithErrno(errno, reading: true, path: path)
308384
}
385+
#endif
309386
var result = [FileAttributeKey : Any]()
310387
result[.size] = NSNumber(value: UInt64(s.st_size))
311388

312389
#if os(OSX) || os(iOS)
313390
let ti = (TimeInterval(s.st_mtimespec.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtimespec.tv_nsec))
314391
#elseif os(Android)
315392
let ti = (TimeInterval(s.st_mtime) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtime_nsec))
393+
#elseif CAN_IMPORT_MINGWCRT
394+
let ti = (TimeInterval(s.st_mtime) - kCFAbsoluteTimeIntervalSince1970)
316395
#else
317-
let ti = (TimeInterval(s.st_mtim.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtim.tv_nsec))
396+
let ti = (TimeInterval(s.st_mtime.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtim.tv_nsec))
318397
#endif
319398
result[.modificationDate] = Date(timeIntervalSinceReferenceDate: ti)
320399

321400
result[.posixPermissions] = NSNumber(value: UInt64(s.st_mode & 0o7777))
322401
result[.referenceCount] = NSNumber(value: UInt64(s.st_nlink))
323402
result[.systemNumber] = NSNumber(value: UInt64(s.st_dev))
324403
result[.systemFileNumber] = NSNumber(value: UInt64(s.st_ino))
325-
404+
#if CAN_IMPORT_MINGWCRT
405+
var type : FileAttributeType
406+
switch Int32(s.st_mode) & S_IFMT {
407+
case S_IFCHR: type = .typeCharacterSpecial
408+
case S_IFDIR: type = .typeDirectory
409+
case S_IFREG: type = .typeRegular
410+
default: type = .typeUnknown
411+
}
412+
#else
326413
let pwd = getpwuid(s.st_uid)
327414
if pwd != nil && pwd!.pointee.pw_name != nil {
328415
let name = String(cString: pwd!.pointee.pw_name)
@@ -345,6 +432,7 @@ open class FileManager : NSObject {
345432
case S_IFSOCK: type = .typeSocket
346433
default: type = .typeUnknown
347434
}
435+
#endif
348436
result[.type] = type
349437

350438
if type == .typeBlockSpecial || type == .typeCharacterSpecial {
@@ -363,7 +451,7 @@ open class FileManager : NSObject {
363451
result[.groupOwnerAccountID] = NSNumber(value: UInt64(s.st_gid))
364452

365453
return result
366-
#endif
454+
367455
}
368456

369457
/* attributesOfFileSystemForPath:error: returns an NSDictionary of key/value pairs containing the attributes of the filesystem containing the provided path. If this method returns 'nil', an NSError will be returned by reference in the 'error' parameter. This method does not traverse a terminal symlink.
@@ -431,6 +519,16 @@ open class FileManager : NSObject {
431519
guard !self.fileExists(atPath: dstPath) else {
432520
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteFileExists.rawValue, userInfo: [NSFilePathErrorKey : NSString(dstPath)])
433521
}
522+
#if CAN_IMPORT_MINGWCRT
523+
var wcSrcPath = [UInt16](srcPath.utf16)
524+
wcSrcPath.append(UInt16(0))
525+
var wcDstPath = [UInt16](dstPath.utf16)
526+
wcDstPath.append(UInt16(0))
527+
528+
if _wrename(wcSrcPath, wcDstPath) != 0 {
529+
throw _NSErrorWithErrno(errno, reading: false, path: srcPath)
530+
}
531+
#else
434532
if rename(srcPath, dstPath) != 0 {
435533
if errno == EXDEV {
436534
// TODO: Copy and delete.
@@ -439,6 +537,7 @@ open class FileManager : NSObject {
439537
throw _NSErrorWithErrno(errno, reading: false, path: srcPath)
440538
}
441539
}
540+
#endif
442541
}
443542

444543
open func linkItem(atPath srcPath: String, toPath dstPath: String) throws {
@@ -462,7 +561,29 @@ open class FileManager : NSObject {
462561

463562
open func removeItem(atPath path: String) throws {
464563
#if CAN_IMPORT_MINGWCRT
465-
NSUnimplemented()
564+
var wcpath = [UInt16](path.utf16)
565+
wcpath.append(UInt16(0))
566+
567+
var s = _stat64()
568+
guard _wstat64(wcpath, &s) == 0 else {
569+
throw _NSErrorWithErrno(errno, reading: true, path: path)
570+
}
571+
572+
if (Int32(s.st_mode) & S_IFMT) == S_IFDIR {
573+
let subpaths = try subpathsOfDirectory(atPath: path)
574+
for subpath in subpaths {
575+
try removeItem(atPath: path + "/" + subpath)
576+
}
577+
578+
if _wrmdir(wcpath) != 0 {
579+
throw _NSErrorWithErrno(errno, reading: false, path: path)
580+
}
581+
582+
} else {
583+
if _wunlink(wcpath) != 0 {
584+
throw _NSErrorWithErrno(errno, reading: false, path: path)
585+
}
586+
}
466587
#else
467588
if rmdir(path) == 0 {
468589
return
@@ -554,19 +675,34 @@ open class FileManager : NSObject {
554675
open var currentDirectoryPath: String {
555676
#if CAN_IMPORT_MINGWCRT
556677
let length = Int32(_MAX_PATH) + 1
557-
var buf = [Int8](repeating: 0, count: Int(length))
678+
var buf = [UInt16](repeating: 0, count: Int(length))
679+
680+
_wgetcwd(&buf, length)
681+
682+
let result = String(utf16CodeUnits:buf, count:wcslen(buf))
558683
#else
559684
let length = Int(PATH_MAX) + 1
560685
var buf = [Int8](repeating: 0, count: length)
561-
#endif
686+
562687
getcwd(&buf, length)
688+
563689
let result = self.string(withFileSystemRepresentation: buf, length: Int(strlen(buf)))
690+
#endif
691+
692+
564693
return result
565694
}
566695

567696
@discardableResult
568697
open func changeCurrentDirectoryPath(_ path: String) -> Bool {
698+
#if CAN_IMPORT_MINGWCRT
699+
var wcpath = [UInt16](path.utf16)
700+
wcpath.append(UInt16(0))
701+
702+
return _wchdir(wcpath) == 0
703+
#else
569704
return chdir(path) == 0
705+
#endif
570706
}
571707

572708
/* The following methods are of limited utility. Attempting to predicate behavior based on the current state of the filesystem or a particular file on the filesystem is encouraging odd behavior in the face of filesystem race conditions. It's far better to attempt an operation (like loading a file or creating a directory) and handle the error gracefully than it is to try to figure out ahead of time whether the operation will succeed.
@@ -577,7 +713,18 @@ open class FileManager : NSObject {
577713

578714
open func fileExists(atPath path: String, isDirectory: UnsafeMutablePointer<ObjCBool>?) -> Bool {
579715
#if CAN_IMPORT_MINGWCRT
580-
NSUnimplemented()
716+
var wcpath = [UInt16](path.utf16)
717+
wcpath.append(UInt16(0))
718+
var s = _stat64()
719+
if _wstat64(wcpath, &s) >= 0 {
720+
if let isDirectory = isDirectory {
721+
isDirectory.pointee = (Int32(s.st_mode) & S_IFMT) == S_IFDIR
722+
}
723+
} else {
724+
return false
725+
}
726+
727+
return true
581728
#else
582729
var s = stat()
583730
if lstat(path, &s) >= 0 {

0 commit comments

Comments
 (0)