safe-rm 1.0.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/rm.sh CHANGED
@@ -1,34 +1,33 @@
1
1
  #!/bin/bash
2
2
 
3
- # You could modify these environment variables to change the default constants
4
- # Default to ~/.Trash on Mac, ~/.local/share/Trash/files on Linux.
5
- DEFAULT_TRASH="$HOME/.Trash"
6
- if [[ "$(uname -s)" == "Linux" ]]; then
7
- DEFAULT_TRASH="$HOME/.local/share/Trash/files"
8
- fi
3
+ # Basic configuration
4
+ # ------------------------------------------------------------------------------
9
5
 
6
+ DEFAULT_SAFE_RM_CONFIG="$HOME/.safe-rm/config"
10
7
 
11
- SAFE_RM_TRASH=${SAFE_RM_TRASH:="$DEFAULT_TRASH"}
8
+ # You could modify the location of the configuration file by using
9
+ # ```sh
10
+ # $ export SAFE_RM_CONFIG=/path/to/safe-rm.conf
11
+ # ```
12
+ SAFE_RM_CONFIG=${SAFE_RM_CONFIG:="$DEFAULT_SAFE_RM_CONFIG"}
13
+
14
+ if [[ -f "$SAFE_RM_CONFIG" ]]; then
15
+ source "$SAFE_RM_CONFIG"
16
+ fi
12
17
 
18
+ SAFE_RM_CONFIG_ROOT=${SAFE_RM_CONFIG_ROOT:="$HOME/.safe-rm"}
13
19
 
14
20
  # Print debug info or not
15
21
  SAFE_RM_DEBUG=${SAFE_RM_DEBUG:=}
16
- # -------------------------------------------------------------------------------
17
22
 
18
- # Simple basename: /bin/rm -> rm
19
- COMMAND=${0##*/}
20
-
21
- # pwd
22
- __DIRNAME=$(pwd)
23
+ # Whether to delete files in the trash permanently, defaults to NO
24
+ if [[ ${SAFE_RM_PERM_DEL_FILES_IN_TRASH:0:1} =~ [yY] ]]; then
25
+ SAFE_RM_PERM_DEL_FILES_IN_TRASH=1
26
+ else
27
+ SAFE_RM_PERM_DEL_FILES_IN_TRASH=
28
+ fi
23
29
 
24
- GUID=0
25
- TIME=
26
- date_time(){
27
- TIME=$(date +%Y-%m-%d_%H:%M:%S)-$GUID
28
- (( GUID += 1 ))
29
- }
30
30
 
31
- # tools
32
31
  debug(){
33
32
  if [[ -n "$SAFE_RM_DEBUG" ]]; then
34
33
  echo "[D] $@" >&2
@@ -36,7 +35,124 @@ debug(){
36
35
  }
37
36
 
38
37
 
39
- # parse argv -------------------------------------------------------------------------------
38
+ error(){
39
+ echo $@ >&2
40
+ }
41
+
42
+
43
+ # global exit code, default to 0
44
+ EXIT_CODE=0
45
+
46
+ # Usage:
47
+ # ```
48
+ # do_exit $LINENO
49
+ # do_exit $LINENO 1
50
+ # ```
51
+ do_exit(){
52
+ local line=$1
53
+ local code=$EXIT_CODE
54
+
55
+ if [[ $# -eq 2 ]]; then
56
+ code=$2
57
+ fi
58
+
59
+ # Exit immediately
60
+ debug "$line: exit code $code"
61
+ exit $code
62
+ }
63
+
64
+
65
+ if [[ "$(uname -s)" == "Darwin"* && -z $SAFE_RM_DEBUG_LINUX ]]; then
66
+ OS_TYPE="MacOS"
67
+ DEFAULT_TRASH="$HOME/.Trash"
68
+ else
69
+ OS_TYPE="Linux"
70
+ DEFAULT_TRASH="$HOME/.local/share/Trash"
71
+ SAFE_RM_USE_APPLESCRIPT=
72
+ fi
73
+
74
+
75
+ # The target trash directory to dispose files and directories,
76
+ # defaults to the system trash directory
77
+ SAFE_RM_TRASH=${SAFE_RM_TRASH:="$DEFAULT_TRASH"}
78
+
79
+
80
+ if [[ "$OS_TYPE" == "MacOS" ]]; then
81
+ if command -v osascript &> /dev/null; then
82
+ # `SAFE_RM_USE_APPLESCRIPT=no` in your SAFE_RM_CONFIG file
83
+ # to disable AppleScript
84
+ if [[ "$SAFE_RM_USE_APPLESCRIPT" == "no" ]]; then
85
+ debug "$LINENO: applescript disabled by conf"
86
+ SAFE_RM_USE_APPLESCRIPT=
87
+
88
+ elif [[ "$SAFE_RM_TRASH" == "$DEFAULT_TRASH" ]]; then
89
+ debug "$LINENO: applescript enabled"
90
+ SAFE_RM_USE_APPLESCRIPT=1
91
+ else
92
+ debug "$LINENO: applescript disabled due to custom trash"
93
+ SAFE_RM_USE_APPLESCRIPT=
94
+ fi
95
+ else
96
+ SAFE_RM_USE_APPLESCRIPT=
97
+ fi
98
+ else
99
+ if mkdir -p "$SAFE_RM_TRASH/files" &> /dev/null; then
100
+ debug "$LINENO: linux trash enabled"
101
+ else
102
+ error "$COMMAND: failed to create trash directory $SAFE_RM_TRASH/files"
103
+ do_exit $LINENO 1
104
+ fi
105
+
106
+ if mkdir -p "$SAFE_RM_TRASH/info" &> /dev/null; then
107
+ :
108
+ else
109
+ error "$COMMAND: failed to create trash info directory $SAFE_RM_TRASH/info"
110
+ do_exit $LINENO 1
111
+ fi
112
+ fi
113
+
114
+
115
+ SAFE_RM_PROTECTED_RULES="${SAFE_RM_CONFIG_ROOT}/.gitignore"
116
+
117
+ debug $SAFE_RM_PROTECTED_RULES
118
+
119
+ # But if it is not a file
120
+ if [[ -f "$SAFE_RM_PROTECTED_RULES" ]]; then
121
+ if command -v git &> /dev/null; then
122
+ debug "$LINENO: protected rules enabled: $SAFE_RM_PROTECTED_RULES"
123
+
124
+ if git -C "$SAFE_RM_CONFIG_ROOT" rev-parse --is-inside-work-tree &> /dev/null; then
125
+ :
126
+ else
127
+ error "[WARNING] safe-rm requires a git repository to use protected rules"
128
+ error "Initializing a git repository in \"$SAFE_RM_CONFIG_ROOT\" ..."
129
+
130
+ git -C "$SAFE_RM_CONFIG_ROOT" init -q
131
+
132
+ error "Success"
133
+ fi
134
+ else
135
+ error "[WARNING] safe-rm requires git installed to use protected rules"
136
+ error " please install git"
137
+ error " or remove the file \"$SAFE_RM_PROTECTED_RULES\""
138
+ SAFE_RM_PROTECTED_RULES=
139
+ fi
140
+ else
141
+ SAFE_RM_PROTECTED_RULES=
142
+ fi
143
+
144
+
145
+ # ------------------------------------------------------------------------------
146
+
147
+ # Simple basename: /bin/rm -> rm
148
+ COMMAND=${0##*/}
149
+
150
+ # pwd
151
+ __DIRNAME=$(pwd)
152
+
153
+
154
+ # parse argv
155
+ # ------------------------------------------------------------------------------
40
156
 
41
157
  invalid_option(){
42
158
  # if there's an invalid option, `rm` only takes the second char of the option string
@@ -48,19 +164,20 @@ invalid_option(){
48
164
  }
49
165
 
50
166
  usage(){
51
- echo "usage: rm [-f | -i | -I] [-dPRrvW] file ..."
52
- echo " unlink file"
167
+ echo "usage: rm [-f | -i] [-dIRrv] file ..."
168
+ echo " unlink [--] file"
53
169
 
54
170
  # if has an invalid option, exit with 64
55
171
  exit 64
56
172
  }
57
173
 
58
174
 
59
- if [[ "$#" = 0 ]]; then
175
+ if [[ "$#" == 0 ]]; then
60
176
  echo "safe-rm"
61
177
  usage
62
178
  fi
63
179
 
180
+
64
181
  ARG_END=
65
182
  FILE_NAME=
66
183
  ARG=
@@ -92,10 +209,15 @@ push_file(){
92
209
 
93
210
  # pre-parse argument vector
94
211
  while [[ -n $1 ]]; do
95
- # case:
212
+ # Case 1:
96
213
  # rm -v abc -r --force
97
214
  # -> -r will be ignored
98
215
  # -> args: ['-v'], files: ['abc', '-r', 'force']
216
+
217
+ # Case 2:
218
+ # rm -- -r
219
+ # -> -r will be treated as a file
220
+ # -> args: [], files: ['-r']
99
221
  if [[ -n $ARG_END ]]; then
100
222
  push_file "$1"
101
223
 
@@ -112,24 +234,24 @@ while [[ -n $1 ]]; do
112
234
  # Regex in bash is not perl regex,
113
235
  # in which `'*'` means "anything" (including nothing)
114
236
  -[a-zA-Z]*)
115
- split_push_arg $1; debug "short option $1"
237
+ split_push_arg $1; debug "$LINENO: short option $1"
116
238
  ;;
117
239
 
118
240
  # rm --force a
119
241
  --[a-zA-Z]*)
120
- push_arg $1; debug "option $1"
242
+ push_arg $1; debug "$LINENO: option $1"
121
243
  ;;
122
244
 
123
245
  # rm -- -a
124
246
  --)
125
- ARG_END=1; debug "divider"
247
+ ARG_END=1; debug "$LINENO: divider"
126
248
  ;;
127
249
 
128
250
  # case:
129
251
  # rm -
130
252
  # -> args: [], files: ['-']
131
253
  *)
132
- push_file "$1"; debug "file $1"
254
+ push_file "$1"; debug "$LINENO: file $1"
133
255
  ARG_END=1
134
256
  ;;
135
257
  esac
@@ -144,9 +266,7 @@ OPT_INTERACTIVE=
144
266
  OPT_INTERACTIVE_ONCE=
145
267
  OPT_RECURSIVE=
146
268
  OPT_VERBOSE=
147
-
148
- # global exit code, default to 0
149
- EXIT_CODE=0
269
+ OPT_EMPTY_DIR=
150
270
 
151
271
  # parse options
152
272
  for arg in ${ARG[@]}; do
@@ -159,29 +279,33 @@ for arg in ${ARG[@]}; do
159
279
  # ;;
160
280
 
161
281
  -f|--force)
162
- OPT_FORCE=1; debug "force : $arg"
282
+ OPT_FORCE=1; debug "$LINENO: force : $arg"
163
283
  ;;
164
284
 
165
285
  # interactive=always
166
286
  -i|--interactive|--interactive=always)
167
- OPT_INTERACTIVE=1; debug "interactive : $arg"
287
+ OPT_INTERACTIVE=1; debug "$LINENO: interactive : $arg"
168
288
  OPT_INTERACTIVE_ONCE=
169
289
  ;;
170
290
 
171
291
  # interactive=once. interactive=once and interactive=always are exclusive
172
292
  -I|--interactive=once)
173
- OPT_INTERACTIVE_ONCE=1; debug "interactive_once : $arg"
293
+ OPT_INTERACTIVE_ONCE=1; debug "$LINENO: interactive_once : $arg"
174
294
  OPT_INTERACTIVE=;
175
295
  ;;
176
296
 
177
297
  # both r and R is allowed
178
298
  -[rR]|--[rR]ecursive)
179
- OPT_RECURSIVE=1; debug "recursive : $arg"
299
+ OPT_RECURSIVE=1; debug "$LINENO: recursive : $arg"
180
300
  ;;
181
301
 
182
302
  # only lowercase v is allowed
183
303
  -v|--verbose)
184
- OPT_VERBOSE=1; debug "verbose : $arg"
304
+ OPT_VERBOSE=1; debug "$LINENO: verbose : $arg"
305
+ ;;
306
+
307
+ -d|--directory)
308
+ OPT_EMPTY_DIR=1; debug "$LINENO: empty dir : $arg"
185
309
  ;;
186
310
 
187
311
  *)
@@ -189,7 +313,8 @@ for arg in ${ARG[@]}; do
189
313
  ;;
190
314
  esac
191
315
  done
192
- # /parse argv -------------------------------------------------------------------------------
316
+ # /parse argv
317
+ # ------------------------------------------------------------------------------
193
318
 
194
319
 
195
320
  # make sure recycled bin exists
@@ -198,7 +323,7 @@ if [[ ! -e $SAFE_RM_TRASH ]]; then
198
323
  echo -n "(yes/no): "
199
324
 
200
325
  read answer
201
- if [[ $answer = "yes" || ! -n $anwser ]]; then
326
+ if [[ $answer == "yes" || ! -n $anwser ]]; then
202
327
  mkdir -p "$SAFE_RM_TRASH"
203
328
  else
204
329
  echo "Canceled!"
@@ -206,59 +331,79 @@ if [[ ! -e $SAFE_RM_TRASH ]]; then
206
331
  fi
207
332
  fi
208
333
 
334
+
335
+ check_return_status(){
336
+ local status=$?
337
+ if [[ $status -ne "0" ]]; then
338
+ debug "$LINENO: last command returned status $status"
339
+ EXIT_CODE=$status
340
+ fi
341
+ }
342
+
209
343
  # try to remove a file or directory
210
344
  remove(){
211
345
  local file=$1
212
346
 
213
347
  # if is dir
214
- if [[ -d $file ]]; then
348
+ if [[ -d "$file" && ! -L "$file" ]]; then
215
349
 
216
- # if a directory, and without '-r' option
217
- if [[ ! -n $OPT_RECURSIVE ]]; then
218
- debug "$LINENO: $file: is a directory"
219
- echo "$COMMAND: $file: is a directory"
220
- return 1
221
- fi
222
-
223
- if [[ $file = './' ]]; then
224
- echo "$COMMAND: $file: Invalid argument"
225
- return 1
226
- fi
227
-
228
- if [[ $OPT_INTERACTIVE = 1 ]]; then
229
- echo -n "examine files in directory $file? "
230
- read answer
350
+ # if a directory, and without '-r' option
351
+ if [[ ! -n $OPT_RECURSIVE ]]; then
352
+ # if with '-d' option, and is an empty dir
353
+ if [[ -n $OPT_EMPTY_DIR && ! $(ls -A "$file") ]]; then
354
+ debug "$LINENO: trash an empty directory $file"
355
+ trash "$file"
356
+ return
357
+ fi
231
358
 
232
- # actually, as long as the answer start with 'y', the file will be removed
233
- # default to no remove
234
- if [[ ${answer:0:1} =~ [yY] ]]; then
359
+ debug "$LINENO: $file: is a directory"
360
+ error "$COMMAND: $file: is a directory"
361
+ return 1
362
+ fi
235
363
 
236
- # if choose to examine the dir, recursively check files first
237
- recursive_remove "$file"
364
+ if [[ "$file" == './' ]]; then
365
+ echo "$COMMAND: $file: Invalid argument"
366
+ return 1
367
+ fi
238
368
 
239
- # interact with the dir at last
240
- echo -n "remove $file? "
369
+ if [[ "$OPT_INTERACTIVE" == 1 ]]; then
370
+ echo -n "examine files in directory $file? "
241
371
  read answer
372
+
373
+ # actually, as long as the answer start with 'y', the file will be removed
374
+ # default to no remove
242
375
  if [[ ${answer:0:1} =~ [yY] ]]; then
243
- [[ $(ls -A "$file") ]] && {
244
- echo "$COMMAND: $file: Directory not empty"
245
376
 
246
- return 1
377
+ # if choose to examine the dir, recursively check files first
378
+ recursive_remove "$file"
247
379
 
248
- } || {
249
- trash "$file"
250
- debug "$LINENO: trash returned status $?"
251
- }
380
+ # interact with the dir at last
381
+ echo -n "remove $file? "
382
+ read answer
383
+ if [[ ${answer:0:1} =~ [yY] ]]; then
384
+ [[ $(ls -A "$file") ]] && {
385
+ echo "$COMMAND: $file: Directory not empty"
386
+
387
+ return 1
388
+
389
+ } || {
390
+ trash "$file"
391
+ debug "$LINENO: trash returned status $?"
392
+ }
393
+ fi
252
394
  fi
395
+ else
396
+ # The file
397
+ # - is a symbolic link
398
+ # - is a file
399
+ # - does not exist
400
+ trash "$file"
401
+ debug "$LINENO: trash returned status $?"
253
402
  fi
254
- else
255
- trash "$file"
256
- debug "$LINENO: trash returned status $?"
257
- fi
258
403
 
259
404
  # if is a file
260
405
  else
261
- if [[ "$OPT_INTERACTIVE" = 1 ]]; then
406
+ if [[ "$OPT_INTERACTIVE" == 1 ]]; then
262
407
  echo -n "remove $file? "
263
408
  read answer
264
409
  if [[ ${answer:0:1} =~ [yY] ]]; then
@@ -288,55 +433,249 @@ recursive_remove(){
288
433
  }
289
434
 
290
435
 
291
- # trash a file or dir directly
292
436
  trash(){
293
- debug "trash $1"
437
+ local target=$1
438
+
439
+ if [[ -n $SAFE_RM_PERM_DEL_FILES_IN_TRASH ]]; then
440
+ if [[ "$target" == "$SAFE_RM_TRASH"* ]]; then
441
+ # If the target is already in the trash, delete it permanently
442
+ /bin/rm -rf "$target"
443
+ else
444
+ do_trash "$target"
445
+ fi
446
+ else
447
+ do_trash "$target"
448
+ fi
449
+
450
+ check_return_status
451
+ }
452
+
453
+
454
+ do_trash(){
455
+ local target=$1
456
+
457
+ debug "$LINENO: trash $target"
458
+
459
+ if is_protected "$target"; then
460
+ error "\"$target\" is protected by your configuration"
461
+ return 1
462
+ fi
463
+
464
+ if [[ -n $SAFE_RM_USE_APPLESCRIPT ]]; then
465
+ applescript_trash "$target"
466
+ elif [[ "$OS_TYPE" == "MacOS" ]]; then
467
+ mac_trash "$target"
468
+ else
469
+ linux_trash "$target"
470
+ fi
471
+ }
472
+
473
+
474
+ get_absolute_path(){
475
+ echo $(cd "$(dirname "$1")" && pwd)/$(basename "$1")
476
+ }
294
477
 
295
- # origin file path
296
- local file=$1
297
478
 
298
- # the first parameter to be passed to `mv`
299
- local move=$file
300
- local base=$(basename "$file")
301
- local travel=
479
+ # Returns
480
+ # - 0: the target is protected
481
+ # - 1: the target is not protected
482
+ is_protected(){
483
+ if [[ ! -n $SAFE_RM_PROTECTED_RULES ]]; then
484
+ # If no protected rules are set, the target is not protected
485
+ return 1
486
+ fi
487
+
488
+ local target=$1
489
+ local abs_path=$(get_absolute_path "$target")
490
+
491
+ # /path/to/foo -> path/to/foo
492
+ local rel_path=${abs_path#/}
493
+
494
+ debug "$LINENO: check whether $rel_path is protected"
495
+
496
+ local ignored=$(git -C "$SAFE_RM_CONFIG_ROOT" check-ignore -v --no-index "$rel_path")
497
+
498
+ debug "$LINENO: git check-ignore result: $ignored"
499
+
500
+ if [[ -n "$ignored" ]]; then
501
+ return 0
502
+ else
503
+ return 1
504
+ fi
505
+ }
506
+
507
+
508
+ applescript_trash(){
509
+ local target=$1
510
+
511
+ [[ "$OPT_VERBOSE" == 1 ]] && list_files "$target"
512
+
513
+ debug "$LINENO: osascript delete $target"
514
+
515
+ osascript -e "tell application \"Finder\" to delete (POSIX file \"$target\" as alias)" &> /dev/null
516
+
517
+ # TODO: handle osascript errors
518
+ return 0
519
+ }
520
+
521
+
522
+ _short_time_ret=
523
+ short_time(){
524
+ _short_time_ret=$(date +%H.%M.%S)
525
+ }
526
+
527
+ _mac_trash_path_ret=
528
+ check_mac_trash_path(){
529
+ local path=$1
530
+ local ext=$2
531
+ local full_path="$path$ext"
532
+
533
+ # if already in the trash
534
+ if [[ -e "$full_path" ]]; then
535
+ debug "$LINENO: $full_path already exists"
536
+
537
+ # renew $_short_time_ret
538
+ short_time
539
+ check_mac_trash_path "$path $_short_time_ret" "$ext"
540
+ else
541
+ _mac_trash_path_ret=$full_path
542
+ fi
543
+ }
544
+
545
+
546
+ _traveled=
547
+ _to_move=
548
+
549
+ check_target_to_move(){
550
+ _traveled=
551
+ _to_move=$1
302
552
 
303
553
  # basename ./ -> .
304
554
  # basename ../ -> ..
305
555
  # basename ../abc -> abc
306
556
  # basename ../.abc -> .abc
307
- if [[ -d "$file" && ${base:0:1} = '.' ]]; then
308
- # then file must be a relative dir
309
- cd $file
310
-
311
- # pwd can't be piped?
312
- move=$(pwd)
313
- move=$(basename "$move")
314
- cd ..
315
- travel=1
557
+ if [[ -d "$_to_move" ]]; then
558
+ # We don't know whether a relative path is the pwd or not
559
+ if [[ "${_to_move:0:1}" == '.' || "$_to_move" == "$__DIRNAME" ]]; then
560
+ cd "$_to_move"
561
+
562
+ # pwd can't be piped?
563
+ local current=$(pwd)
564
+ _to_move=$(basename "$current")
565
+
566
+ # We can not `mv` a dir that is the pwd,
567
+ # or it will throw an "Operation not permitted" error,
568
+ # so we have to `cd` to the parent dir first
569
+ cd ..
570
+ _traveled=1
571
+ fi
316
572
  fi
573
+ }
574
+
575
+ # trash a file or dir directly
576
+ mac_trash(){
577
+ check_target_to_move "$1"
578
+ local move=$_to_move
579
+ local base=$(basename "$move")
580
+
581
+ # foo.jpg => "foo" + ".jpg"
582
+ # foo => "foo" + ""
583
+
584
+ local name="${base%.*}"
585
+ local ext="${base##*.}"
586
+
587
+ if [[ "$name" == "$ext" ]]; then
588
+ ext=
589
+ else
590
+ ext=".$ext"
591
+ fi
592
+
593
+ # foo.jpg => "foo 12.34.56.jpg"
594
+
595
+ check_mac_trash_path "$SAFE_RM_TRASH/$name" "$ext"
596
+ local trash_path=$_mac_trash_path_ret
317
597
 
318
- local trash_name=$SAFE_RM_TRASH/$base
598
+ [[ "$OPT_VERBOSE" == 1 ]] && list_files "$1"
599
+
600
+ debug "$LINENO: mv $move to $trash_path"
601
+ mv "$move" "$trash_path"
602
+
603
+ [[ "$_traveled" == 1 ]] && cd $__DIRNAME &> /dev/null
604
+
605
+ # default status
606
+ return 0
607
+ }
608
+
609
+
610
+ # Check if the base name already exists in the trash
611
+ # If it does, foo -> foo.1
612
+ # If foo.1 exists, foo.1 -> foo.2
613
+ check_linux_trash_base(){
614
+ local base=$1
615
+ local trash="$SAFE_RM_TRASH/files"
616
+ local path="$trash/$base"
319
617
 
320
618
  # if already in the trash
321
- if [[ -e "$trash_name" ]]; then
322
- # renew $TIME
323
- date_time
324
- trash_name="$trash_name-$TIME"
619
+ if [[ -e "$path" ]]; then
620
+ debug "$LINENO: $path already exists"
621
+
622
+ local max_n=0
623
+ local num=
624
+
625
+ while IFS= read -r file; do
626
+ if [[ $file =~ ${filename}\.([0-9]+)$ ]]; then
627
+ # Remove leading zeros and make sure the number is in base 10
628
+ num=$((10#${BASH_REMATCH[1]}))
629
+ if ((num > max_n)); then
630
+ max_n=$num
631
+ fi
632
+ fi
633
+ done < <(find "$trash" -maxdepth 1)
634
+
635
+ (( max_n += 1 ))
636
+
637
+ echo "$base.$max_n"
638
+ else
639
+ echo "$base"
325
640
  fi
641
+ }
642
+
643
+
644
+ # trash a file or dir directly for linux
645
+ # - move the target into
646
+ linux_trash(){
647
+ check_target_to_move "$1"
648
+ local move=$_to_move
649
+ local base=$(basename "$move")
650
+
651
+ base=$(check_linux_trash_base "$base")
652
+
653
+ local trash_path="$SAFE_RM_TRASH/files/$base"
326
654
 
327
- [[ "$OPT_VERBOSE" = 1 ]] && list_files "$file"
655
+ [[ "$OPT_VERBOSE" == 1 ]] && list_files "$1"
328
656
 
329
- debug "mv $move to $trash_name"
330
- mv "$move" "$trash_name"
657
+ # Move the target into the trash
658
+ debug "$LINENO: mv $move to $trash_path"
659
+ mv "$move" "$trash_path"
331
660
 
332
- [[ "$travel" = 1 ]] && cd $__DIRNAME &> /dev/null
661
+ # Save linux trash info
662
+ local info_path="$SAFE_RM_TRASH/info/$base.trashinfo"
663
+ local trash_time=$(date +%Y-%m-%dT%H:%M:%S)
664
+ cat > "$info_path" <<EOF
665
+ [Trash Info]
666
+ Path=$move
667
+ DeletionDate=$trash_time
668
+ EOF
669
+
670
+ [[ "$_traveled" == 1 ]] && cd $__DIRNAME &> /dev/null
333
671
 
334
- #default status
335
672
  return 0
336
673
  }
337
674
 
675
+
338
676
  # list all files and maintain outward sequence
339
- # we can't just use `find $file`, 'coz `find` act a inward searching, unlike rm -v
677
+ # we can't just use `find $file`,
678
+ # 'coz `find` act a inward searching, unlike rm -v
340
679
  list_files(){
341
680
  if [[ -d "$1" ]]; then
342
681
  local list=$(ls -A "$1")
@@ -352,43 +691,39 @@ list_files(){
352
691
 
353
692
 
354
693
  # debug: get $FILE_NAME array length
355
- debug "${#FILE_NAME[@]} files or directory to process: ${FILE_NAME[@]}"
694
+ debug "$LINENO: ${#FILE_NAME[@]} files or directory to process: ${FILE_NAME[@]}"
356
695
 
357
- # test remove interactive_once: ask for 3 or more files or with recorsive option
358
- if [[ (${#FILE_NAME[@]} > 2 || $OPT_RECURSIVE = 1) && $OPT_INTERACTIVE_ONCE = 1 ]]; then
696
+ # test remove interactive_once: ask for 3 or more files or with recursive option
697
+ if [[ (${#FILE_NAME[@]} > 2 || $OPT_RECURSIVE == 1) && $OPT_INTERACTIVE_ONCE == 1 ]]; then
359
698
  echo -n "$COMMAND: remove all arguments? "
360
699
  read answer
361
700
 
362
701
  # actually, as long as the answer start with 'y', the file will be removed
363
702
  # default to no remove
364
703
  if [[ ! ${answer:0:1} =~ [yY] ]]; then
365
- debug "EXIT_CODE $EXIT_CODE"
366
- exit $EXIT_CODE
704
+ do_exit $LINENO
367
705
  fi
368
706
  fi
369
707
 
370
708
  for file in "${FILE_NAME[@]}"; do
371
- debug "result file $file"
709
+ debug "$LINENO: result file $file"
372
710
 
373
- if [[ $file = "/" ]]; then
374
- echo "it is dangerous to operate recursively on /"
375
- echo "are you insane?"
711
+ if [[ $file == "/" ]]; then
712
+ error "it is dangerous to operate recursively on /"
713
+ error "are you insane?"
376
714
  EXIT_CODE=1
377
-
378
- # Exit immediately
379
- debug "EXIT_CODE $EXIT_CODE"
380
- exit $EXIT_CODE
715
+ continue
381
716
  fi
382
717
 
383
- if [[ $file = "." || $file = ".." ]]; then
384
- echo "$COMMAND: \".\" and \"..\" may not be removed"
718
+ if [[ $file == "." || $file == ".." ]]; then
719
+ error "$COMMAND: \".\" and \"..\" may not be removed"
385
720
  EXIT_CODE=1
386
721
  continue
387
722
  fi
388
723
 
389
- #the same check also apply on /. /..
390
- if [[ $(basename "$file") = "." || $(basename "$file") = ".." ]]; then
391
- echo "$COMMAND: \".\" and \"..\" may not be removed"
724
+ # the same check also apply on /. /..
725
+ if [[ $(basename "$file") == "." || $(basename "$file") == ".." ]]; then
726
+ error "$COMMAND: \".\" and \"..\" may not be removed"
392
727
  EXIT_CODE=1
393
728
  continue
394
729
  fi
@@ -397,23 +732,22 @@ for file in "${FILE_NAME[@]}"; do
397
732
  ls_result=$(ls -d "$file" 2> /dev/null)
398
733
 
399
734
  # debug
400
- debug "ls_result: $ls_result"
735
+ debug "$LINENO: ls_result: $ls_result"
401
736
 
402
737
  if [[ -n "$ls_result" ]]; then
403
738
  for file in "$ls_result"; do
404
739
  remove "$file"
405
740
  status=$?
406
- debug "remove returned status: $status"
741
+ debug "$LINENO: remove returned status: $status"
407
742
 
408
743
  if [[ ! $status == 0 ]]; then
409
744
  EXIT_CODE=1
410
745
  fi
411
746
  done
412
- else
413
- echo "$COMMAND: $file: No such file or directory"
747
+ elif [[ -z "$OPT_FORCE" ]]; then
748
+ error "$COMMAND: $file: No such file or directory" >&2
414
749
  EXIT_CODE=1
415
750
  fi
416
751
  done
417
752
 
418
- debug "EXIT_CODE $EXIT_CODE"
419
- exit $EXIT_CODE
753
+ do_exit $LINENO