Skip to content

Commit 56b41fc

Browse files
committed
Add -I flag
1 parent 4f6ab06 commit 56b41fc

File tree

4 files changed

+83
-78
lines changed

4 files changed

+83
-78
lines changed

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ A wrapper for the "rm" command with soft-deletes, config-based deletion, debug i
77
## "GNU Like" command line arguments
88

99
- `-i` Interactivly prompt before each deletion request
10+
- `-I` Prompt if deleting more three files
1011
- `--help` Display help information (without deletion)
1112
- `--version` Display version information (without deletion)
1213

@@ -28,10 +29,9 @@ A wrapper for the "rm" command with soft-deletes, config-based deletion, debug i
2829
- `-v`, `--verbose` Emit additional verbose information
2930
- `--version` Show version information
3031
- `--help` Show help information
31-
- `-I` Prompt if deleting more than three files
3232
- `--interactive[=WHEN]` Interactive with a custom threshold
3333
- `--one-file-system` Do not allow cross-file-system deletes
34-
- `-f`, `--force` (partially implemented)
34+
- `-f`, `--force` Bypass protections
3535

3636
## Features
3737

@@ -111,10 +111,10 @@ hard:
111111
soft:
112112
- "*.bak"
113113
# do not allow deleting these files/directories
114-
# without using the `-f` or `--force` flags
115-
# this does not make the file un-deletable
116-
# through other tools, but it does protect
117-
# against accidental deletion through 2rm
114+
# without using the `--bypass-protected` flag this
115+
# does not make the file protected at the system level
116+
# through other tools, but it does protect against
117+
# accidental deletion through 2rm
118118
protected:
119119
- ".ssh/"
120120
```

src/cli/args.go

+1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ const NOTIFICATION_CLA = "--notify"
1111

1212
// gnu rm CLI arguments
1313
const INTERACTIVE_CLA = "-i"
14+
const INTERACTIVE_GROUP_CLA = "-I"
1415
const HELP_CLA = "--help"
1516
const VERSION_CLA = "--version"

src/cli/docs.go

+17-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,28 @@ Mark FILE(s) for deletion.
1010
1111
"GNU Like" OPTION(s):
1212
-i Prompt before every deletion request
13-
--force Bypass 2rm protections
13+
14+
-I Prompt once before deleting more than three files
15+
16+
-f, --force Bypass protections
1417
1518
2rm OPTION(s) Flags:
19+
1620
--overwrite Overwrite the disk location location with zeros
21+
1722
--hard Do not soft-delete FILE(s)
23+
1824
--soft Soft delete a file and a store backup (default /tmp/2rm)
19-
--silent Do not print out additional information priduced by 2rm. This is useful for scripting situations
20-
--dry-run Perform a dry run and show all the files that would be deleted
21-
--bypass-protected Using this flag will allow you to delete a file protected by the 2rm config
25+
26+
--silent Do not print out additional information priduced by 2rm.
27+
This is useful for scripting situations
28+
29+
--dry-run Perform a dry run and show all the files that would be
30+
deleted if run without the dry-run flag
31+
32+
--bypass-protected Using this flag will allow you to delete a file
33+
protected by the 2rm config
34+
2235
--notify Send a system notification once deletion is complete
2336
2437
By default, 2rm will soft-delete a file and store a backup (default /tmp/2rm)

src/patches/rm.go

+59-68
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,17 @@ import (
1515
)
1616

1717
const TRASH_DIR_PERMISSIONS = 0755
18+
const INTERACTIVE_THRESHOLD = 3
1819

1920
func RmPatch(arguments []string, config models.Config) {
20-
forceHardDelete := util.InArray(arguments, cli.HARD_DELETE_CLA)
21-
forceSoftDelete := util.InArray(arguments, cli.SOFT_DELETE_CLA)
2221
silent := util.InArray(arguments, cli.SILENT_CLA)
2322
dryRun := util.InArray(arguments, cli.DRY_RUN_CLA)
24-
bypassProtected := util.InArray(arguments, cli.BYPASS_PROTECTED_CLA)
25-
overwrite := util.InArray(arguments, cli.OVERWRITE_CLA)
2623
shouldNotify := util.InArray(arguments, cli.NOTIFICATION_CLA)
2724

2825
requestingHelp := util.InArray(arguments, cli.HELP_CLA)
2926
requestingVersion := util.InArray(arguments, cli.VERSION_CLA)
3027

31-
actionedArgs := removeUnNeededArguments(
32-
removeDangerousArguments(arguments),
33-
)
28+
actionedArgs := removeDangerousArguments(arguments)
3429

3530
if requestingHelp {
3631
cli.PrintHelp()
@@ -54,36 +49,7 @@ func RmPatch(arguments []string, config models.Config) {
5449
return
5550
}
5651

57-
for _, path := range filePaths {
58-
absolutePath := relativeToAbsolute(path)
59-
isTmp := isTmpPath(absolutePath)
60-
61-
isProtected := config.IsProtected(absolutePath)
62-
if isProtected && bypassProtected {
63-
fmt.Println("Cannot delete protected file:", absolutePath)
64-
fmt.Println("Use the --bypass-protected flag to force deletion")
65-
continue
66-
}
67-
68-
isConfigHardDelete := config.ShouldHardDelete(absolutePath)
69-
isConfigSoftDelete := config.ShouldSoftDelete(absolutePath)
70-
71-
// overwriting a file is not exclusive to hard/soft deletes
72-
// meaning that you can overwrite the contents of a file with zeros and
73-
// also soft delete it
74-
// I have made this decision because I think soft-deleting an
75-
// overwritten file has auditing/logging use cases
76-
// e.g. Who deleted this file? When was it deleted?
77-
// if we hard deleted the file, we would lose this information
78-
isConfigOverwrite := config.ShouldOverwrite(absolutePath)
79-
if overwrite || isConfigOverwrite {
80-
overwriteFile(absolutePath)
81-
}
82-
83-
shouldHardDelete := isTmp || forceHardDelete || isConfigHardDelete && !isConfigSoftDelete && !forceSoftDelete
84-
85-
deletePath(absolutePath, shouldHardDelete, config, arguments)
86-
}
52+
deletePaths(filePaths, config, arguments)
8753

8854
if shouldNotify {
8955
fileNames := strings.Join(filePaths, ", ")
@@ -94,28 +60,6 @@ func RmPatch(arguments []string, config models.Config) {
9460
}
9561
}
9662

97-
func removeUnNeededArguments(arguments []string) []string {
98-
returnedArguments := []string{}
99-
unNeededArguments := []string{
100-
"-r",
101-
cli.HARD_DELETE_CLA,
102-
cli.SOFT_DELETE_CLA,
103-
cli.SILENT_CLA,
104-
cli.DRY_RUN_CLA,
105-
cli.BYPASS_PROTECTED_CLA,
106-
cli.OVERWRITE_CLA,
107-
cli.NOTIFICATION_CLA,
108-
}
109-
110-
for _, arg := range arguments {
111-
if !util.InArray(unNeededArguments, arg) {
112-
returnedArguments = append(returnedArguments, arg)
113-
}
114-
}
115-
116-
return returnedArguments
117-
}
118-
11963
func removeDangerousArguments(arguments []string) []string {
12064
// I have excluded the root slash as a forbidden argument just incase
12165
// you make a typo like rm ./myDirectory /
@@ -165,20 +109,61 @@ func backupFileName(path string) string {
165109
return result + ".bak"
166110
}
167111

168-
func deletePath(path string, hard bool, config models.Config, arguments []string) {
169-
isInteractive := util.InArray(arguments, cli.INTERACTIVE_CLA)
112+
func deletePaths(paths []string, config models.Config, arguments []string) {
113+
forceHardDelete := util.InArray(arguments, cli.HARD_DELETE_CLA)
114+
forceSoftDelete := util.InArray(arguments, cli.SOFT_DELETE_CLA)
115+
bypassProtected := util.InArray(arguments, cli.BYPASS_PROTECTED_CLA)
116+
overwrite := util.InArray(arguments, cli.OVERWRITE_CLA)
170117

171-
if isInteractive {
172-
fmt.Println("Are you sure you want to delete", path, "? (y/n)")
173-
var response string
174-
fmt.Scanln(&response)
118+
hasInteraciveCla := util.InArray(arguments, cli.INTERACTIVE_CLA)
119+
hasGroupInteractiveCla := util.InArray(arguments, cli.INTERACTIVE_GROUP_CLA)
120+
isInteractiveGroup := hasGroupInteractiveCla && len(paths) >= INTERACTIVE_THRESHOLD
121+
isInteractive := hasInteraciveCla || isInteractiveGroup
122+
123+
for _, path := range paths {
124+
if isInteractive {
125+
fmt.Println("Are you sure you want to delete", path, "? (y/n)")
126+
var response string
127+
fmt.Scanln(&response)
128+
129+
if response != "y" && response != "yes" {
130+
fmt.Println("Skipping file", path)
131+
continue
132+
}
133+
}
175134

176-
if response != "y" && response != "yes" {
177-
fmt.Println("Exiting without removing file(s).")
178-
return
135+
absolutePath := relativeToAbsolute(path)
136+
isTmp := isTmpPath(absolutePath)
137+
138+
isProtected := config.IsProtected(absolutePath)
139+
if isProtected && bypassProtected {
140+
fmt.Println("Cannot delete protected file:", absolutePath)
141+
fmt.Println("Use the --bypass-protected flag to force deletion")
142+
continue
143+
}
144+
145+
isConfigHardDelete := config.ShouldHardDelete(absolutePath)
146+
isConfigSoftDelete := config.ShouldSoftDelete(absolutePath)
147+
148+
// overwriting a file is not exclusive to hard/soft deletes
149+
// meaning that you can overwrite the contents of a file with zeros and
150+
// also soft delete it
151+
// I have made this decision because I think soft-deleting an
152+
// overwritten file has auditing/logging use cases
153+
// e.g. Who deleted this file? When was it deleted?
154+
// if we hard deleted the file, we would lose this information
155+
isConfigOverwrite := config.ShouldOverwrite(absolutePath)
156+
if overwrite || isConfigOverwrite {
157+
overwriteFile(absolutePath)
179158
}
159+
160+
shouldHardDelete := isTmp || forceHardDelete || isConfigHardDelete && !isConfigSoftDelete && !forceSoftDelete
161+
162+
deletePath(absolutePath, shouldHardDelete, config, arguments)
180163
}
164+
}
181165

166+
func deletePath(path string, hard bool, config models.Config, arguments []string) {
182167
if hard {
183168
hardDelete(path)
184169
} else {
@@ -225,6 +210,12 @@ func softDelete(filePath string, tempDir string) {
225210
}
226211

227212
err = os.Remove(absoluteSrcPath)
213+
if err != nil {
214+
fmt.Println("Error deleting file:", err)
215+
216+
// pause the program so the user can see the error message
217+
fmt.Scanln()
218+
}
228219
}
229220

230221
func hardDelete(filePath string) {

0 commit comments

Comments
 (0)