>> rewrite arg parisng to remove "commands"
> add strategy and directory flags
This commit is contained in:
parent
50d7189654
commit
c82804bf26
2 changed files with 75 additions and 66 deletions
|
|
@ -7,8 +7,8 @@ Say you want to backup some of your files on to a memory stick. Most likely
|
||||||
that memory stick uses `FAT32` or `exFAT` as its file system, as this is the
|
that memory stick uses `FAT32` or `exFAT` as its file system, as this is the
|
||||||
most compatible with the most operating systems. But if any of your files
|
most compatible with the most operating systems. But if any of your files
|
||||||
have special characters like `:` or `?` in their names, this won't work (see
|
have special characters like `:` or `?` in their names, this won't work (see
|
||||||
<https://learn.microsoft.com/en-gb/windows/win32/fileio/exfat-specification>
|
<https://learn.microsoft.com/en-gb/windows/win32/fileio/exfat-specification>,
|
||||||
for more details).
|
section 7.7.3 for more details).
|
||||||
|
|
||||||
Alternatively, say you have a file that needs to be on an older computer
|
Alternatively, say you have a file that needs to be on an older computer
|
||||||
that will not render some characters (like multi-character emoji)
|
that will not render some characters (like multi-character emoji)
|
||||||
|
|
|
||||||
117
main.go
117
main.go
|
|
@ -37,19 +37,14 @@ import (
|
||||||
*/
|
*/
|
||||||
type runeset = [][2]rune
|
type runeset = [][2]rune
|
||||||
|
|
||||||
/**
|
|
||||||
* A runemap represents a list of rune swaps that should be preformed before
|
|
||||||
* dealing with any invalid runes. The first number in each entry is the
|
|
||||||
* Code Point of the rune to be replaced, the second the Code Point of its
|
|
||||||
* replacement.
|
|
||||||
*/
|
|
||||||
type runemap = [][2]rune
|
|
||||||
|
|
||||||
// Unrelated to the standard library's "context".
|
// Unrelated to the standard library's "context".
|
||||||
type context = struct {
|
type context = struct {
|
||||||
command string
|
FileSpecs []string
|
||||||
hasDryRun bool
|
Directories []string
|
||||||
fileSpecs []string
|
DryRun bool
|
||||||
|
Strategy string
|
||||||
|
DoHelp bool
|
||||||
|
DoVersion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -74,11 +69,6 @@ var FAT_RUNESET = runeset{
|
||||||
{ 0x7d, MAX_CODE_POINT},
|
{ 0x7d, MAX_CODE_POINT},
|
||||||
}
|
}
|
||||||
|
|
||||||
var DEFAULT_RUNEMAP = runemap{
|
|
||||||
{ 0x20, 0x5f }, // space -> underscore (a.k.a low line)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// MAIN FUNCTIONS
|
/// MAIN FUNCTIONS
|
||||||
func isFatValid(r rune) bool {
|
func isFatValid(r rune) bool {
|
||||||
for _, runeRange := range FAT_RUNESET {
|
for _, runeRange := range FAT_RUNESET {
|
||||||
|
|
@ -89,11 +79,8 @@ func isFatValid(r rune) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func restrictRuneset(s, strategy string, userSubs runemap) string {
|
func restrictRuneset(s, strategy string) string {
|
||||||
result := s
|
result := s
|
||||||
for _, sub := range userSubs {
|
|
||||||
result = strings.ReplaceAll(result, string(sub[0]), string(sub[1]))
|
|
||||||
}
|
|
||||||
toValidSubs := make(map[rune]string)
|
toValidSubs := make(map[rune]string)
|
||||||
if strategy == "remove" {
|
if strategy == "remove" {
|
||||||
for _, r := range result {
|
for _, r := range result {
|
||||||
|
|
@ -126,20 +113,35 @@ func kaput(err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Collect all invalid args into one error instead of failing fast.
|
||||||
func parseCLIArgs(args []string) (context, error) {
|
func parseCLIArgs(args []string) (context, error) {
|
||||||
var result context
|
// Set defaults
|
||||||
|
result := context{
|
||||||
|
FileSpecs: []string{},
|
||||||
|
Directories: []string{},
|
||||||
|
DryRun: false,
|
||||||
|
Strategy: "fat",
|
||||||
|
DoHelp: false,
|
||||||
|
DoVersion: false,
|
||||||
|
}
|
||||||
index := 1
|
index := 1
|
||||||
isFlag := func(arg string) bool {
|
isFlag := func(arg string) bool {
|
||||||
return arg[0] == '-'
|
return arg[0] == '-'
|
||||||
}
|
}
|
||||||
if !isFlag(args[index]) {
|
|
||||||
result.command = args[index]
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
for index < len(args) {
|
for index < len(args) {
|
||||||
switch arg := args[index]; arg {
|
switch arg := args[index]; arg {
|
||||||
|
case "-d", "--directory":
|
||||||
|
result.Directories = append(result.Directories, arg)
|
||||||
|
index++
|
||||||
case "-n", "--dry-run":
|
case "-n", "--dry-run":
|
||||||
result.hasDryRun = true
|
result.DryRun = true
|
||||||
|
case "-s", "--strategy":
|
||||||
|
result.Strategy = args[index]
|
||||||
|
index++
|
||||||
|
case "-h", "--help":
|
||||||
|
result.DoHelp = true
|
||||||
|
case "-v", "--version":
|
||||||
|
result.DoVersion = true
|
||||||
default:
|
default:
|
||||||
if isFlag(arg) {
|
if isFlag(arg) {
|
||||||
return result, errors.New(fmt.Sprintf(
|
return result, errors.New(fmt.Sprintf(
|
||||||
|
|
@ -147,7 +149,7 @@ func parseCLIArgs(args []string) (context, error) {
|
||||||
arg,
|
arg,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
result.fileSpecs = append(result.fileSpecs, arg)
|
result.FileSpecs = append(result.FileSpecs, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
|
|
@ -155,48 +157,55 @@ func parseCLIArgs(args []string) (context, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanCommand(ctx context) {
|
// TODO: Finish writing options short help. Mention man page where relevant.
|
||||||
for _, file := range ctx.fileSpecs {
|
func printHelp() {
|
||||||
|
fmt.Println(`Owl - a hunter of bad characters in filenames
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
owl [options] FILES
|
||||||
|
|
||||||
|
Rename FILES such that all characters that invalid in FAT file systems
|
||||||
|
(?,\,*,etc.) are removed.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-s,--strategy
|
||||||
|
-h,--help
|
||||||
|
-v,--version
|
||||||
|
-d,--directory DIRECTORY
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, err := parseCLIArgs(os.Args)
|
||||||
|
kaput(err)
|
||||||
|
if ctx.DoHelp {
|
||||||
|
printHelp()
|
||||||
|
} else if ctx.DoVersion {
|
||||||
|
fmt.Printf(
|
||||||
|
"Owl - a hunter of bad characters in file names\nversion %s\n",
|
||||||
|
OwlVersion,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
for _, file := range ctx.FileSpecs {
|
||||||
if _, err := os.Stat(file); err != nil {
|
if _, err := os.Stat(file); err != nil {
|
||||||
warn("File <<%s>> does not exist!", file)
|
warn("File <<%s>> does not exist!", file)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
oldName := fpath.Base(file)
|
oldName := fpath.Base(file)
|
||||||
dirName := fpath.Dir(file)
|
dirName := fpath.Dir(file)
|
||||||
if ctx.hasDryRun {
|
if ctx.DryRun {
|
||||||
fmt.Printf(
|
fmt.Printf(
|
||||||
"%s -> <<%s>>\n",
|
"%s -> <<%s>>\n",
|
||||||
file,
|
file,
|
||||||
restrictRuneset(oldName, "represent", DEFAULT_RUNEMAP),
|
restrictRuneset(oldName, "represent"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
os.Rename(file,fpath.Join(
|
os.Rename(file,fpath.Join(
|
||||||
dirName,
|
dirName,
|
||||||
restrictRuneset(oldName, "represent", DEFAULT_RUNEMAP),
|
restrictRuneset(oldName, "represent"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func helpCommand() {
|
|
||||||
fmt.Println(`TBD`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func versionCommand() {
|
|
||||||
fmt.Printf("Owl file renaming tool, version %s\n", OwlVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ctx, err := parseCLIArgs(os.Args)
|
|
||||||
kaput(err)
|
|
||||||
switch ctx.command {
|
|
||||||
case "clean":
|
|
||||||
cleanCommand(ctx)
|
|
||||||
case "help":
|
|
||||||
helpCommand()
|
|
||||||
case "version":
|
|
||||||
versionCommand()
|
|
||||||
default:
|
|
||||||
helpCommand()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue