-
Notifications
You must be signed in to change notification settings - Fork 8
/
File.swift
464 lines (370 loc) · 18.9 KB
/
File.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
//
// File.swift
// SwiftRuby
//
// Created by John Holdsworth on 26/09/2015.
// Copyright © 2015 John Holdsworth. All rights reserved.
//
// $Id: //depot/SwiftRuby/File.swift#13 $
//
// Repo: https://github.com/RubyNative/SwiftRuby
//
// See: http://ruby-doc.org/core-2.2.3/File.html
//
import Foundation
public let ALT_SEPARATOR = "/"
public let PATH_SEPARATOR = ":"
public let SEPARATOR = "/"
public let Separator = "/"
@discardableResult
public func unixOK(_ what: string_like, _ returnValue: Int32, file: StaticString?, line: UInt = 0) -> Bool {
if returnValue != 0 {
if file != nil {
SRError("\(what.to_s) failed, returning \(returnValue)", file: file!, line: line)
}
return false
}
return true
}
open class File : IO {
let filepath: String
var umask: Int {
get {
let omask = Darwin.umask(0)
_ = Darwin.umask(omask)
return Int(omask)
}
set {
_ = Darwin.umask(mode_t(newValue))
}
}
public init?(filepath: string_like, mode: string_like = "r", file: StaticString, line: UInt) {
self.filepath = filepath.to_s
super.init(what: "fopen '\(filepath.to_s)', mode '\(mode.to_s)'", unixFILE: fopen(filepath.to_s, mode.to_s), file: file, line: line)
if ifValid() == nil {
return nil
}
}
// MARK: Class Methods
open class func new(_ file_name: string_like, _ mode: string_like = "r", _ perm: Int? = nil, file: StaticString = #file, line: UInt = #line) -> File? {
let newFile = File(filepath: file_name, mode: mode, file: file, line: line)
if perm != nil {
_ = newFile?.chmod(perm!, file: file, line: line)
}
return newFile
}
open class func open(_ file_name: string_like, _ mode: string_like = "r", _ perm: Int? = nil, file: StaticString = #file, line: UInt = #line) -> File? {
return new(file_name, mode, perm, file: file, line: line)
}
open class func absolute_path(_ file_name: string_like, _ dir_string: string_like? = nil, file: StaticString = #file, line: UInt = #line) -> String? {
var baseURL: URL?
if dir_string != nil {
baseURL = URL(fileURLWithPath: dir_string!.to_s)
}
let fileURL = URL(fileURLWithPath: file_name.to_s)
#if swift(>=2.3)
return URL(string: fileURL.absoluteString, relativeTo: baseURL)?.absoluteURL.path ////
#else
return NSURL(string: fileURL.absoluteString, relativeToURL: baseURL)?.absoluteURL.path ////
#endif
}
open class func basename(_ file_name: string_like, _ suffix: string_like? = nil, file: StaticString = #file, line: UInt = #line) -> String? {
var file_name = file_name.to_s
if suffix != nil {
if suffix!.to_s == ".*" {
file_name = extremoved(file_name)!
}
else {
SRNotImplemented("File.basename with suffix ofer than '.*'", file: file, line: line)
}
}
return URL(fileURLWithPath: file_name).lastPathComponent ////
}
open class func birthtime(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Time? {
return Stat(file_name, file: file, line: line)?.ctime
}
open class func blockdev(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.blockdev
}
open class func chardev(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.chardev
}
@discardableResult
open class func chmod(_ mode_int: Int, _ file_names: array_like, file: StaticString = #file, line: UInt = #line) -> Bool {
var ok = true
for file_name in file_names.to_a {
ok = ok && unixOK("File.chmod '\(file_name)'", Darwin.chmod(file_name, mode_t(mode_int)), file: file, line: line)
}
return ok
}
open class func chown(_ owner_s: string_like?, _ group_s: string_like?, _ file_names: array_like, file: StaticString = #file, line: UInt = #line) -> Bool {
var owner_int = owner_s != nil ? Int(owner_s!.to_s) : nil
if owner_int == nil && owner_s != nil {
owner_int = user_uid(owner_s!, file: file, line: line)
if owner_int == nil {
return false
}
}
var group_int = group_s != nil ? Int(group_s!.to_s) : nil
if group_int == nil && group_s != nil {
group_int = group_gid(group_s!, file: file, line: line)
if group_int == nil {
return false
}
}
var ok = true
for file_name in file_names.to_a {
ok = ok && unixOK("File.chown '\(file_name)'", Darwin.chown(file_name, uid_t(owner_int ?? -1), gid_t(group_int ?? -1)), file: file, line: line)
}
return ok
}
open class func user_uid(_ user_s: string_like, file: StaticString = #file, line: UInt = #line) -> Int? {
var buff = [Int8](repeating: 0, count: Int(PATH_MAX))
var ret: UnsafeMutablePointer<passwd>?
var info = passwd()
if !unixOK("File.getpwnam \(user_s.to_s)", getpwnam_r(user_s.to_s, &info, &buff, buff.count, &ret), file: file, line: line) {
return nil
}
return Int(info.pw_uid)
}
open class func group_gid(_ group_s: string_like, file: StaticString = #file, line: UInt = #line) -> Int? {
var buff = [Int8](repeating: 0, count: Int(PATH_MAX))
var ret: UnsafeMutablePointer<group>?
var info = group()
if !unixOK("File.getgrnam \(group_s.to_s)", getgrnam_r(group_s.to_s, &info, &buff, buff.count, &ret), file: file, line: line) {
return nil
}
return Int(info.gr_gid)
}
open class func ctime(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Time? {
return birthtime(file_name)
}
open class func delete(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return unixOK("File.delete '\(file_name.to_s)'", Darwin.unlink(file_name.to_s), file: file, line: line)
}
open class func directory(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.directory
}
open class func dirname(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> String? {
return NSURL(fileURLWithPath: file_name.to_s).deletingLastPathComponent?.path
}
open class func executable(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.executable
}
open class func executable_real(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.executable_real
}
open class func exist(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return Stat(file_name, file: nil) != nil
}
open class func exists(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return exist(file_name)
}
open class func expand_path(_ file_name: string_like, _ dir_string: string_like? = nil, file: StaticString = #file, line: UInt = #line) -> String? {
return NSURL(fileURLWithPath: file_name.to_s).standardized?.path
}
open class func extname(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> String? {
return URL(fileURLWithPath: file_name.to_s).pathExtension
}
open class func file(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return Stat(file_name, file: file, line: line)?.file == true
}
// public class func fnmatch(pattern: string_like, _ path: string_like, _ flags: string_like? = nil, file: StaticString = #file, line: UInt = #line) -> Bool {
// RKNotImplemented("File.fnmatch")
// return false
// }
open class func ftype(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> String? {
return Stat(file_name, file: file, line: line)?.ftype
}
open class func grpowned(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.grpowned
}
open class func identical(_ file_1: string_like, _ file_2: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
if let stat1 = Stat(file_1, file: file, line: line), let stat2 = Stat(file_2, file: file, line: line) {
return stat1 == stat2
}
return false
}
open class func join(_ strings: [String], file: StaticString = #file, line: UInt = #line) -> String {
return strings.joined(separator: SEPARATOR)
}
open class func lchmod(_ mode_int: Int, _ file_names: array_like, file: StaticString = #file, line: UInt = #line) -> Bool {
var ok = true
for file_name in file_names.to_a {
ok = ok && unixOK("File.lchmod '\(file_name)'", Darwin.lchmod(file_name, mode_t(mode_int)), file: file, line: line)
}
return ok
}
open class func lchown(_ owner_s: string_like?, _ group_s: string_like?, _ file_names: array_like, file: StaticString = #file, line: UInt = #line) -> Bool {
var owner_int = owner_s != nil ? Int(owner_s!.to_s) : nil
if owner_int == nil && owner_s != nil {
owner_int = user_uid(owner_s!, file: file, line: line)
if owner_int == nil {
return false
}
}
var group_int = group_s != nil ? Int(group_s!.to_s) : nil
if group_int == nil && group_s != nil {
group_int = group_gid(group_s!, file: file, line: line)
if group_int == nil {
return false
}
}
var ok = true
for file_name in file_names.to_a {
ok = ok && unixOK("File.lchown '\(file_name.to_s)'", Darwin.lchown(file_name.to_s, uid_t(owner_int ?? -1), gid_t(group_int ?? -1)), file: file, line: line)
}
return ok
}
open class func link(_ old_name: string_like, _ new_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return unixOK("File.link '\(old_name.to_s)' '\(new_name.to_s)'", Darwin.link(old_name.to_s, new_name.to_s), file: file, line: line)
}
open class func lstat(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Stat? {
return Stat(file_name, statLink: true, file: file, line: line)
}
open class func mtime(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Time? {
return Stat(file_name, file: file, line: line)?.mtime
}
open class func owned(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.owned
}
open class func path(_ path: string_like, file: StaticString = #file, line: UInt = #line) -> String {
return path.to_s ////
}
open class func pipe(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.pipe
}
open class func readable(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.readable
}
open class func readable_real(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.readable_real
}
open class func readlink(_ link_name: string_like, file: StaticString = #file, line: UInt = #line) -> String? {
var path = [Int8](repeating: 0, count: Int(PATH_MAX+1))
let length = Darwin.readlink(link_name.to_s, &path, path.count) /// readlinkat for relatives?
if unixOK("File.readlink '\(link_name.to_s)'", length == -1 ? 1 : 0, file: file, line: line) {
path[length] = 0
return String(validatingUTF8: path)
}
return nil
}
open class func realdirpath(_ file_name: string_like, _ dir_string: string_like? = nil, file: StaticString = #file, line: UInt = #line) -> String? {
if dir_string != nil {
SRNotImplemented("File.realdirpath with dir_string argument", file: file, line: line)
}
return NSURL(fileURLWithPath: file_name.to_s).resolvingSymlinksInPath?.path ////
}
open class func realpath(_ file_name: string_like, _ dir_string: string_like? = nil, file: StaticString = #file, line: UInt = #line) -> String? {
if dir_string != nil {
SRNotImplemented("File.realpath with dir_string argument", file: file, line: line)
}
return NSURL(fileURLWithPath: file_name.to_s).resolvingSymlinksInPath?.path ////
}
open class func extremoved(_ file_name: string_like, _ suffix: string_like? = nil, file: StaticString = #file, line: UInt = #line) -> String? {
return NSURL(fileURLWithPath: file_name.to_s).deletingPathExtension?.path
}
open class func rename(_ old_name: string_like, _ new_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return unixOK("File.rename '\(old_name.to_s)' '\(new_name.to_s)'", Darwin.rename(old_name.to_s, new_name.to_s), file: file, line: line)
}
open class func setgid(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.setgid
}
open class func setuid(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.setuid
}
open class func size(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Int? {
return Stat(file_name, file: file, line: line)?.size
}
open class func socket(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.socket
}
open class func split(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> [String?] {
return [dirname(file_name), basename(file_name)]
}
open class func stat(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Stat? {
return Stat(file_name, file: file, line: line)
}
open class func sticky(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.sticky
}
open class func symlink(_ old_name: string_like, _ new_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return unixOK("File.symlink '\(old_name.to_s)' '\(new_name.to_s)'", Darwin.symlink(old_name.to_s, new_name.to_s), file: file, line: line)
}
open class func truncate(_ file_name: string_like, _ integer: Int, file: StaticString = #file, line: UInt = #line) -> Bool {
return unixOK("File.truncate '\(file_name.to_s)' \(integer)", Darwin.truncate(file_name.to_s, off_t(integer)), file: file, line: line)
}
open class func unlink(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return delete(file_name, file: file, line: line)
}
open class func unlink_f(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool {
return File.exists(file_name) || File.lstat(file_name) != nil ? unlink(file_name, file: file, line: line) : true
}
open class func utime(_ file_name: string_like, _ actime: int_like, _ modtime: int_like, file: StaticString = #file, line: UInt = #line) -> Bool {
var times = utimbuf()
times.actime = time_t(actime.to_i)
times.modtime = time_t(modtime.to_i)
return unixOK("File.utime '\(file_name.to_s)'", Darwin.utime(file_name.to_s, ×), file: file, line: line)
}
open class func writable(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.writable
}
open class func writable_real(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.writable_real
}
open class func write(_ file_name: string_like, string: data_like, file: StaticString = #file, line: UInt = #line) -> fixnum? {
return File(filepath: file_name, mode: "w", file: file, line: line)?.write(string)
}
open class func zero(_ file_name: string_like, file: StaticString = #file, line: UInt = #line) -> Bool? {
return Stat(file_name, file: file, line: line)?.zero
}
// MARK: Instance Methods
open var atime: Time? {
return stat?.atime
}
open var birthtime: Time? {
return stat?.birthtime
}
open func chmod(_ mode_int: Int, file: StaticString = #file, line: UInt = #line) -> Bool {
return unixOK("File.chmod \(mode_int) '\(filepath)'", Darwin.chmod(filepath, mode_t(mode_int)), file: file, line: line)
}
open func chown(_ owner_s: string_like?, _ group_s: string_like?, file: StaticString = #file, line: UInt = #line) -> Bool {
var owner_int = owner_s != nil ? Int(owner_s!.to_s) : nil
if owner_int == nil && owner_s != nil {
owner_int = File.user_uid(owner_s!, file: file, line: line)
if owner_int == nil {
return false
}
}
var group_int = group_s != nil ? Int(group_s!.to_s) : nil
if group_int == nil && group_s != nil {
group_int = File.group_gid(group_s!, file: file, line: line)
if group_int == nil {
return false
}
}
return unixOK("File.chown '\(filepath)' \(String(describing: owner_int)) \(String(describing: group_int))", Darwin.chown(filepath,
uid_t(owner_int ?? -1), gid_t(group_int ?? -1)), file: file, line: line)
}
open var ctime: Time? {
return birthtime
}
// open func flock(_ locking_constant: Int, file: StaticString = #file, line: UInt = #line) -> Bool {
// return unixOK("File.flock '\(filepath)' \(locking_constant)", Darwin.flock(Int32(fileno), Int32(locking_constant)), file: file, line: line)
// }
open var lstat: Stat? {
return Stat(filepath, statLink: true, file: #file, line: #line)
}
open var mtime: Time? {
return stat?.mtime
}
open var path: String {
return filepath
}
open var to_path: String {
return filepath
}
open func truncate(_ integer: Int, file: StaticString = #file, line: UInt = #line) -> Bool {
return unixOK("File.truncate '\(filepath)' \(integer)", Darwin.truncate(filepath, off_t(integer)), file: file, line: line)
}
}