6
6
// spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's
7
7
8
8
use clap:: { Arg , ArgAction , Command } ;
9
+ use std:: collections:: HashMap ;
9
10
use std:: ffi:: OsString ;
10
11
use std:: fs;
11
12
use std:: os:: unix:: fs:: { MetadataExt , PermissionsExt } ;
12
13
use std:: path:: Path ;
14
+ use thiserror:: Error ;
13
15
use uucore:: display:: Quotable ;
14
- use uucore:: error:: { ExitCode , UResult , USimpleError , UUsageError , set_exit_code} ;
16
+ use uucore:: error:: { ExitCode , UError , UResult , USimpleError , UUsageError , set_exit_code} ;
15
17
use uucore:: fs:: display_permissions_unix;
16
18
use uucore:: libc:: mode_t;
17
19
#[ cfg( not( windows) ) ]
18
20
use uucore:: mode;
19
21
use uucore:: perms:: { TraverseSymlinks , configure_symlink_and_recursion} ;
20
22
use uucore:: { format_usage, show, show_error} ;
21
23
22
- use uucore:: locale:: get_message;
24
+ use uucore:: locale:: { get_message, get_message_with_args} ;
25
+
26
+ #[ derive( Debug , Error ) ]
27
+ enum ChmodError {
28
+ #[ error( "{}" , get_message_with_args( "chmod-error-cannot-stat" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
29
+ CannotStat ( String ) ,
30
+ #[ error( "{}" , get_message_with_args( "chmod-error-dangling-symlink" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
31
+ DanglingSymlink ( String ) ,
32
+ #[ error( "{}" , get_message_with_args( "chmod-error-no-such-file" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
33
+ NoSuchFile ( String ) ,
34
+ #[ error( "{}" , get_message_with_args( "chmod-error-preserve-root" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
35
+ PreserveRoot ( String ) ,
36
+ #[ error( "{}" , get_message_with_args( "chmod-error-permission-denied" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
37
+ PermissionDenied ( String ) ,
38
+ #[ error( "{}" , get_message_with_args( "chmod-error-new-permissions" , HashMap :: from( [ ( "file" . to_string( ) , _0. clone( ) ) , ( "actual" . to_string( ) , _1. clone( ) ) , ( "expected" . to_string( ) , _2. clone( ) ) ] ) ) ) ]
39
+ NewPermissions ( String , String , String ) ,
40
+ }
41
+
42
+ impl UError for ChmodError { }
23
43
24
44
mod options {
25
45
pub const HELP : & str = "help" ;
@@ -103,11 +123,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
103
123
let fmode = match matches. get_one :: < String > ( options:: REFERENCE ) {
104
124
Some ( fref) => match fs:: metadata ( fref) {
105
125
Ok ( meta) => Some ( meta. mode ( ) & 0o7777 ) ,
106
- Err ( err) => {
107
- return Err ( USimpleError :: new (
108
- 1 ,
109
- format ! ( "cannot stat attributes of {}: {err}" , fref. quote( ) ) ,
110
- ) ) ;
126
+ Err ( _) => {
127
+ return Err ( ChmodError :: CannotStat ( fref. to_string ( ) ) . into ( ) ) ;
111
128
}
112
129
} ,
113
130
None => None ,
@@ -135,7 +152,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
135
152
} ;
136
153
137
154
if files. is_empty ( ) {
138
- return Err ( UUsageError :: new ( 1 , "missing operand" . to_string ( ) ) ) ;
155
+ return Err ( UUsageError :: new (
156
+ 1 ,
157
+ get_message ( "chmod-error-missing-operand" ) ,
158
+ ) ) ;
139
159
}
140
160
141
161
let ( recursive, dereference, traverse_symlinks) =
@@ -168,55 +188,55 @@ pub fn uu_app() -> Command {
168
188
. arg (
169
189
Arg :: new ( options:: HELP )
170
190
. long ( options:: HELP )
171
- . help ( "Print help information." )
191
+ . help ( get_message ( "chmod- help-print-help" ) )
172
192
. action ( ArgAction :: Help ) ,
173
193
)
174
194
. arg (
175
195
Arg :: new ( options:: CHANGES )
176
196
. long ( options:: CHANGES )
177
197
. short ( 'c' )
178
- . help ( "like verbose but report only when a change is made" )
198
+ . help ( get_message ( "chmod-help-changes" ) )
179
199
. action ( ArgAction :: SetTrue ) ,
180
200
)
181
201
. arg (
182
202
Arg :: new ( options:: QUIET )
183
203
. long ( options:: QUIET )
184
204
. visible_alias ( "silent" )
185
205
. short ( 'f' )
186
- . help ( "suppress most error messages" )
206
+ . help ( get_message ( "chmod-help-quiet" ) )
187
207
. action ( ArgAction :: SetTrue ) ,
188
208
)
189
209
. arg (
190
210
Arg :: new ( options:: VERBOSE )
191
211
. long ( options:: VERBOSE )
192
212
. short ( 'v' )
193
- . help ( "output a diagnostic for every file processed" )
213
+ . help ( get_message ( "chmod-help-verbose" ) )
194
214
. action ( ArgAction :: SetTrue ) ,
195
215
)
196
216
. arg (
197
217
Arg :: new ( options:: NO_PRESERVE_ROOT )
198
218
. long ( options:: NO_PRESERVE_ROOT )
199
- . help ( "do not treat '/' specially (the default)" )
219
+ . help ( get_message ( "chmod-help-no-preserve-root" ) )
200
220
. action ( ArgAction :: SetTrue ) ,
201
221
)
202
222
. arg (
203
223
Arg :: new ( options:: PRESERVE_ROOT )
204
224
. long ( options:: PRESERVE_ROOT )
205
- . help ( "fail to operate recursively on '/'" )
225
+ . help ( get_message ( "chmod-help-preserve-root" ) )
206
226
. action ( ArgAction :: SetTrue ) ,
207
227
)
208
228
. arg (
209
229
Arg :: new ( options:: RECURSIVE )
210
230
. long ( options:: RECURSIVE )
211
231
. short ( 'R' )
212
- . help ( "change files and directories recursively" )
232
+ . help ( get_message ( "chmod-help-recursive" ) )
213
233
. action ( ArgAction :: SetTrue ) ,
214
234
)
215
235
. arg (
216
236
Arg :: new ( options:: REFERENCE )
217
237
. long ( "reference" )
218
238
. value_hint ( clap:: ValueHint :: FilePath )
219
- . help ( "use RFILE's mode instead of MODE values" ) ,
239
+ . help ( get_message ( "chmod-help-reference" ) ) ,
220
240
)
221
241
. arg (
222
242
Arg :: new ( options:: MODE ) . required_unless_present ( options:: REFERENCE ) ,
@@ -265,27 +285,21 @@ impl Chmoder {
265
285
}
266
286
267
287
if !self . quiet {
268
- show ! ( USimpleError :: new(
269
- 1 ,
270
- format!( "cannot operate on dangling symlink {}" , filename. quote( ) ) ,
271
- ) ) ;
288
+ show ! ( ChmodError :: DanglingSymlink ( filename. to_string( ) ) ) ;
272
289
set_exit_code ( 1 ) ;
273
290
}
274
291
275
292
if self . verbose {
276
293
println ! (
277
- "failed to change mode of {} from 0000 (---------) to 1500 (r-x-----T)" ,
278
- filename. quote( )
294
+ "{}" ,
295
+ get_message_with_args(
296
+ "chmod-verbose-failed-dangling" ,
297
+ HashMap :: from( [ ( "file" . to_string( ) , filename. quote( ) . to_string( ) ) ] )
298
+ )
279
299
) ;
280
300
}
281
301
} else if !self . quiet {
282
- show ! ( USimpleError :: new(
283
- 1 ,
284
- format!(
285
- "cannot access {}: No such file or directory" ,
286
- filename. quote( )
287
- )
288
- ) ) ;
302
+ show ! ( ChmodError :: NoSuchFile ( filename. to_string( ) ) ) ;
289
303
}
290
304
// GNU exits with exit code 1 even if -q or --quiet are passed
291
305
// So we set the exit code, because it hasn't been set yet if `self.quiet` is true.
@@ -298,13 +312,7 @@ impl Chmoder {
298
312
continue ;
299
313
}
300
314
if self . recursive && self . preserve_root && filename == "/" {
301
- return Err ( USimpleError :: new (
302
- 1 ,
303
- format ! (
304
- "it is dangerous to operate recursively on {}\n chmod: use --no-preserve-root to override this failsafe" ,
305
- filename. quote( )
306
- ) ,
307
- ) ) ;
315
+ return Err ( ChmodError :: PreserveRoot ( filename. to_string ( ) ) . into ( ) ) ;
308
316
}
309
317
if self . recursive {
310
318
r = self . walk_dir ( file) ;
@@ -368,12 +376,9 @@ impl Chmoder {
368
376
} else if err. kind ( ) == std:: io:: ErrorKind :: PermissionDenied {
369
377
// These two filenames would normally be conditionally
370
378
// quoted, but GNU's tests expect them to always be quoted
371
- Err ( USimpleError :: new (
372
- 1 ,
373
- format ! ( "{}: Permission denied" , file. quote( ) ) ,
374
- ) )
379
+ Err ( ChmodError :: PermissionDenied ( file. to_string_lossy ( ) . to_string ( ) ) . into ( ) )
375
380
} else {
376
- Err ( USimpleError :: new ( 1 , format ! ( "{}: {err}" , file. quote ( ) ) ) )
381
+ Err ( ChmodError :: CannotStat ( file. to_string_lossy ( ) . to_string ( ) ) . into ( ) )
377
382
} ;
378
383
}
379
384
} ;
@@ -420,15 +425,12 @@ impl Chmoder {
420
425
self . change_file ( fperm, new_mode, file) ?;
421
426
// if a permission would have been removed if umask was 0, but it wasn't because umask was not 0, print an error and fail
422
427
if ( new_mode & !naively_expected_new_mode) != 0 {
423
- return Err ( USimpleError :: new (
424
- 1 ,
425
- format ! (
426
- "{}: new permissions are {}, not {}" ,
427
- file. maybe_quote( ) ,
428
- display_permissions_unix( new_mode as mode_t, false ) ,
429
- display_permissions_unix( naively_expected_new_mode as mode_t, false )
430
- ) ,
431
- ) ) ;
428
+ return Err ( ChmodError :: NewPermissions (
429
+ file. to_string_lossy ( ) . to_string ( ) ,
430
+ display_permissions_unix ( new_mode as mode_t , false ) ,
431
+ display_permissions_unix ( naively_expected_new_mode as mode_t , false ) ,
432
+ )
433
+ . into ( ) ) ;
432
434
}
433
435
}
434
436
}
0 commit comments