@@ -14,7 +14,7 @@ import Foundation
14
14
import Glibc
15
15
#endif
16
16
17
- func help( ) {
17
+ func help( ) -> Int32 {
18
18
print ( " plutil: [command_option] [other_options] file... \n " +
19
19
" The file '-' means stdin \n " +
20
20
" Command options are (-lint is the default): \n " +
@@ -33,49 +33,128 @@ func help() {
33
33
" -e extension specify alternate extension for converted files \n " +
34
34
" -r if writing JSON, output in human-readable form \n " +
35
35
" -- specifies that all further arguments are file names \n " )
36
+ return EXIT_SUCCESS
37
+ }
38
+
39
+ enum ExecutionMode {
40
+ case Help
41
+ case Lint
42
+ case Convert
43
+ case Print
44
+ }
45
+
46
+ enum ConversionFormat {
47
+ case XML1
48
+ case Binary1
49
+ case JSON
50
+ }
51
+
52
+ struct Options {
53
+ var mode : ExecutionMode = . Lint
54
+ var silent : Bool = false
55
+ var output : String ?
56
+ var fileExtension : String ?
57
+ var humanReadable : Bool ?
58
+ var conversionFormat : ConversionFormat ?
59
+ var inputs = [ String] ( )
60
+ }
61
+
62
+ enum OptionParseError : ErrorType {
63
+ case UnrecognizedArgument( String )
64
+ case MissingArgument( String )
65
+ case InvalidFormat( String )
36
66
}
37
67
38
- func lint ( args : [ String ] ) {
39
- var silent = false
40
- // Be nice and filter the rest of the arguments for other optional arguments
41
- let filteredArgs = args . filter { arg in
68
+ func parseArguments ( args: [ String ] ) throws -> Options {
69
+ var opts = Options ( )
70
+ var iterator = args . generate ( )
71
+ while let arg = iterator . next ( ) {
42
72
switch arg {
43
73
case " -- " :
44
- return false
45
-
74
+ while let path = iterator. next ( ) {
75
+ opts. inputs. append ( path)
76
+ }
77
+ break
46
78
case " -s " :
47
- silent = true
48
- return false
49
-
79
+ opts. silent = true
80
+ break
50
81
case " -o " :
51
- print ( " -o is not used with -lint " )
52
- help ( )
53
- exit ( EXIT_FAILURE)
54
-
82
+ if let path = iterator. next ( ) {
83
+ opts. output = path
84
+ } else {
85
+ throw OptionParseError . MissingArgument ( " -o requires a path argument " )
86
+ }
87
+ break
88
+ case " -convert " :
89
+ opts. mode = ExecutionMode . Convert
90
+ if let format = iterator. next ( ) {
91
+ switch format {
92
+ case " xml1 " :
93
+ opts. conversionFormat = ConversionFormat . XML1
94
+ break
95
+ case " binary1 " :
96
+ opts. conversionFormat = ConversionFormat . Binary1
97
+ break
98
+ case " json " :
99
+ opts. conversionFormat = ConversionFormat . JSON
100
+ break
101
+ default :
102
+ throw OptionParseError . InvalidFormat ( format)
103
+ }
104
+ } else {
105
+ throw OptionParseError . MissingArgument ( " -convert requires a format argument of xml1 binary1 json " )
106
+ }
107
+ break
55
108
case " -e " :
56
- print ( " -e is not used with -lint " )
57
- help ( )
58
- exit ( EXIT_FAILURE)
59
-
109
+ if let ext = iterator. next ( ) {
110
+ opts. fileExtension = ext
111
+ } else {
112
+ throw OptionParseError . MissingArgument ( " -e requires an extension argument " )
113
+ }
114
+ case " -help " :
115
+ opts. mode = ExecutionMode . Help
116
+ break
117
+ case " -lint " :
118
+ opts. mode = ExecutionMode . Lint
119
+ break
120
+ case " -p " :
121
+ opts. mode = ExecutionMode . Print
122
+ break
60
123
default :
61
124
if arg. hasPrefix ( " - " ) && arg. utf8. count > 1 {
62
- print ( " unrecognized option \( arg) " )
63
- help ( )
64
- exit ( EXIT_FAILURE)
125
+ throw OptionParseError . UnrecognizedArgument ( arg)
65
126
}
66
- return true
127
+ break
67
128
}
68
- return true
69
129
}
70
130
71
- if filteredArgs. count < 1 {
131
+ return opts
132
+ }
133
+
134
+
135
+ func lint( options: Options ) -> Int32 {
136
+ if options. output != nil {
137
+ print ( " -o is not used with -lint " )
138
+ help ( )
139
+ return EXIT_FAILURE
140
+ }
141
+
142
+ if options. fileExtension != nil {
143
+ print ( " -e is not used with -lint " )
144
+ help ( )
145
+ return EXIT_FAILURE
146
+ }
147
+
148
+ if options. inputs. count < 1 {
72
149
print ( " No files specified. " )
73
150
help ( )
74
- exit ( EXIT_FAILURE)
151
+ return EXIT_FAILURE
75
152
}
76
153
154
+ let silent = options. silent
155
+
77
156
var doError = false
78
- for file in filteredArgs {
157
+ for file in options . inputs {
79
158
let data : NSData ?
80
159
if file == " - " {
81
160
// stdin
@@ -103,7 +182,174 @@ func lint(args : [String]) {
103
182
}
104
183
105
184
if doError {
106
- exit ( EXIT_FAILURE)
185
+ return EXIT_FAILURE
186
+ } else {
187
+ return EXIT_SUCCESS
188
+ }
189
+ }
190
+
191
+ func convert( options: Options ) -> Int32 {
192
+ print ( " Unimplemented " )
193
+ return EXIT_FAILURE
194
+ }
195
+
196
+ enum DisplayType {
197
+ case Primary
198
+ case Key
199
+ case Value
200
+ }
201
+
202
+ extension Dictionary {
203
+ func display( indent: Int = 0 , type: DisplayType = . Primary) {
204
+ let indentation = String ( count: indent * 2 , repeatedValue: Character ( " " ) )
205
+ if type == . Primary || type == . Key {
206
+ print ( " \( indentation) [ \n " , terminator: " " )
207
+ } else {
208
+ print ( " [ \n " , terminator: " " )
209
+ }
210
+
211
+ forEach ( ) {
212
+ if let key = $0. 0 as? String {
213
+ key. display ( indent + 1 , type: . Key)
214
+ } else {
215
+ fatalError ( " plists should have strings as keys but got a \( $0. 0 . dynamicType) " )
216
+ }
217
+ print ( " => " , terminator: " " )
218
+ displayPlist ( $0. 1 , indent: indent + 1 , type: . Value)
219
+ }
220
+
221
+ print ( " \( indentation) ] \n " , terminator: " " )
222
+ }
223
+ }
224
+
225
+ extension Array {
226
+ func display( indent: Int = 0 , type: DisplayType = . Primary) {
227
+ let indentation = String ( count: indent * 2 , repeatedValue: Character ( " " ) )
228
+ if type == . Primary || type == . Key {
229
+ print ( " \( indentation) [ \n " , terminator: " " )
230
+ } else {
231
+ print ( " [ \n " , terminator: " " )
232
+ }
233
+
234
+ for idx in 0 ..< count {
235
+ print ( " \( indentation) \( idx) => " , terminator: " " )
236
+ displayPlist ( self [ idx] , indent: indent + 1 , type: . Value)
237
+ }
238
+
239
+ print ( " \( indentation) ] \n " , terminator: " " )
240
+ }
241
+ }
242
+
243
+ extension String {
244
+ func display( indent: Int = 0 , type: DisplayType = . Primary) {
245
+ let indentation = String ( count: indent * 2 , repeatedValue: Character ( " " ) )
246
+ if type == . Primary {
247
+ print ( " \( indentation) \" \( self ) \" \n " , terminator: " " )
248
+ }
249
+ else if type == . Key {
250
+ print ( " \( indentation) \" \( self ) \" " , terminator: " " )
251
+ } else {
252
+ print ( " \" \( self ) \" \n " , terminator: " " )
253
+ }
254
+ }
255
+ }
256
+
257
+ extension Bool {
258
+ func display( indent: Int = 0 , type: DisplayType = . Primary) {
259
+ let indentation = String ( count: indent * 2 , repeatedValue: Character ( " " ) )
260
+ if type == . Primary {
261
+ print ( " \( indentation) \" \( self ? " 1 " : " 0 " ) \" \n " , terminator: " " )
262
+ }
263
+ else if type == . Key {
264
+ print ( " \( indentation) \" \( self ? " 1 " : " 0 " ) \" " , terminator: " " )
265
+ } else {
266
+ print ( " \" \( self ? " 1 " : " 0 " ) \" \n " , terminator: " " )
267
+ }
268
+ }
269
+ }
270
+
271
+ extension NSNumber {
272
+ func display( indent: Int = 0 , type: DisplayType = . Primary) {
273
+ let indentation = String ( count: indent * 2 , repeatedValue: Character ( " " ) )
274
+ if type == . Primary {
275
+ print ( " \( indentation) \" \( self ) \" \n " , terminator: " " )
276
+ }
277
+ else if type == . Key {
278
+ print ( " \( indentation) \" \( self ) \" " , terminator: " " )
279
+ } else {
280
+ print ( " \" \( self ) \" \n " , terminator: " " )
281
+ }
282
+ }
283
+ }
284
+
285
+ extension NSData {
286
+ func display( indent: Int = 0 , type: DisplayType = . Primary) {
287
+ let indentation = String ( count: indent * 2 , repeatedValue: Character ( " " ) )
288
+ if type == . Primary {
289
+ print ( " \( indentation) \" \( self ) \" \n " , terminator: " " )
290
+ }
291
+ else if type == . Key {
292
+ print ( " \( indentation) \" \( self ) \" " , terminator: " " )
293
+ } else {
294
+ print ( " \" \( self ) \" \n " , terminator: " " )
295
+ }
296
+ }
297
+ }
298
+
299
+ func displayPlist( plist: Any , indent: Int = 0 , type: DisplayType = . Primary) {
300
+ if let val = plist as? Dictionary < String , Any > {
301
+ val. display ( indent, type: type)
302
+ } else if let val = plist as? Array < Any > {
303
+ val. display ( indent, type: type)
304
+ } else if let val = plist as? String {
305
+ val. display ( indent, type: type)
306
+ } else if let val = plist as? Bool {
307
+ val. display ( indent, type: type)
308
+ } else if let val = plist as? NSNumber {
309
+ val. display ( indent, type: type)
310
+ } else if let val = plist as? NSData {
311
+ val. display ( indent, type: type)
312
+ } else {
313
+ fatalError ( " unhandled type \( plist. dynamicType) " )
314
+ }
315
+ }
316
+
317
+ func display( options: Options ) -> Int32 {
318
+ if options. inputs. count < 1 {
319
+ print ( " No files specified. " )
320
+ help ( )
321
+ return EXIT_FAILURE
322
+ }
323
+
324
+ var doError = false
325
+ for file in options. inputs {
326
+ let data : NSData ?
327
+ if file == " - " {
328
+ // stdin
329
+ data = NSFileHandle . fileHandleWithStandardInput ( ) . readDataToEndOfFile ( )
330
+ } else {
331
+ data = NSData ( contentsOfFile: file)
332
+ }
333
+
334
+ if let d = data {
335
+ do {
336
+ let plist = try NSPropertyListSerialization . propertyListWithData ( d, options: [ ] , format: nil )
337
+ displayPlist ( plist)
338
+ } catch {
339
+ print ( " \( file) : \( error) " )
340
+ }
341
+
342
+ } else {
343
+ print ( " \( file) does not exists or is not readable or is not a regular file " )
344
+ doError = true
345
+ continue
346
+ }
347
+ }
348
+
349
+ if doError {
350
+ return EXIT_FAILURE
351
+ } else {
352
+ return EXIT_SUCCESS
107
353
}
108
354
}
109
355
@@ -117,25 +363,33 @@ func main() -> Int32 {
117
363
118
364
// Throw away process path
119
365
args. removeFirst ( )
120
-
121
- let command = args [ 0 ]
122
- switch command {
123
- case " -help " :
124
- help ( )
125
- return EXIT_SUCCESS
126
-
127
- case " -lint " :
128
- // Throw away command arg
129
- args. removeFirst ( )
130
- lint ( args)
131
-
132
- default :
133
- // Default is to lint
134
- lint ( args)
366
+ do {
367
+ let opts = try parseArguments ( args)
368
+ switch opts. mode {
369
+ case . Lint:
370
+ return lint ( opts)
371
+ case . Convert:
372
+ return convert ( opts)
373
+ case . Print:
374
+ return display ( opts)
375
+ case . Help:
376
+ return help ( )
377
+ }
378
+ } catch let err {
379
+ switch err as! OptionParseError {
380
+ case . UnrecognizedArgument( let arg) :
381
+ print ( " unrecognized option: \( arg) " )
382
+ help ( )
383
+ break
384
+ case . InvalidFormat( let format) :
385
+ print ( " unrecognized format \( format) \n format should be one of: xml1 binary1 json " )
386
+ break
387
+ case . MissingArgument( let errorStr) :
388
+ print ( errorStr)
389
+ break
390
+ }
391
+ return EXIT_FAILURE
135
392
}
136
-
137
- return EXIT_SUCCESS
138
-
139
393
}
140
394
141
395
exit ( main ( ) )
0 commit comments