rclone/fs/config.go

261 lines
5.7 KiB
Go
Raw Normal View History

// Read and write the config file
package fs
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"os/user"
"path"
"sort"
"strconv"
"strings"
"time"
"github.com/Unknwon/goconfig"
)
const (
configFileName = ".rclone.conf"
)
// Global
var (
// Config file
ConfigFile *goconfig.ConfigFile
// Config file path
ConfigPath string
// Global config
Config = &ConfigInfo{}
// Home directory
HomeDir string
// Flags
verbose = flag.Bool("verbose", false, "Print lots more stuff")
quiet = flag.Bool("quiet", false, "Print as little stuff as possible")
modifyWindow = flag.Duration("modify-window", time.Nanosecond, "Max time diff to be considered the same")
checkers = flag.Int("checkers", 8, "Number of checkers to run in parallel.")
transfers = flag.Int("transfers", 4, "Number of file transfers to run in parallel.")
)
// Filesystem config options
type ConfigInfo struct {
Verbose bool
Quiet bool
ModifyWindow time.Duration
Checkers int
Transfers int
}
// Loads the config file
func LoadConfig() {
// Read some flags if set
//
// FIXME read these from the config file too
Config.Verbose = *verbose
Config.Quiet = *quiet
Config.ModifyWindow = *modifyWindow
Config.Checkers = *checkers
Config.Transfers = *transfers
// Find users home directory
usr, err := user.Current()
if err != nil {
log.Printf("Couldn't find home directory: %v", err)
return
}
HomeDir = usr.HomeDir
ConfigPath = path.Join(HomeDir, configFileName)
// Load configuration file.
ConfigFile, err = goconfig.LoadConfigFile(ConfigPath)
if err != nil {
log.Printf("Failed to load config file %v - using defaults", ConfigPath)
}
}
// Save configuration file.
func SaveConfig() {
err := goconfig.SaveConfigFile(ConfigFile, ConfigPath)
if err != nil {
log.Fatalf("Failed to save config file: %v", err)
}
}
// Show an overview of the config file
func ShowConfig() {
remotes := ConfigFile.GetSectionList()
sort.Strings(remotes)
fmt.Printf("%-20s %s\n", "Name", "Type")
fmt.Printf("%-20s %s\n", "====", "====")
for _, remote := range remotes {
fmt.Printf("%-20s %s\n", remote, ConfigFile.MustValue(remote, "type"))
}
}
// ChooseRemote chooses a remote name
func ChooseRemote() string {
remotes := ConfigFile.GetSectionList()
sort.Strings(remotes)
return Choose("remote", remotes, nil, false)
}
// Read some input
func ReadLine() string {
buf := bufio.NewReader(os.Stdin)
line, err := buf.ReadString('\n')
if err != nil {
log.Fatalf("Failed to read line: %v", err)
}
return strings.TrimSpace(line)
}
// Command - choose one
func Command(commands []string) int {
opts := []string{}
for _, text := range commands {
fmt.Printf("%c) %s\n", text[0], text[1:])
opts = append(opts, text[:1])
}
optString := strings.Join(opts, "")
optHelp := strings.Join(opts, "/")
for {
fmt.Printf("%s> ", optHelp)
result := strings.ToLower(ReadLine())
if len(result) != 1 {
continue
}
i := strings.IndexByte(optString, result[0])
if i >= 0 {
return i
}
}
}
// Choose one of the defaults or type a new string if newOk is set
func Choose(what string, defaults, help []string, newOk bool) string {
fmt.Printf("Choose a number from below")
if newOk {
fmt.Printf(", or type in your own value")
}
fmt.Println()
for i, text := range defaults {
if help != nil {
parts := strings.Split(help[i], "\n")
for _, part := range parts {
fmt.Printf(" * %s\n", part)
}
}
fmt.Printf("%2d) %s\n", i+1, text)
}
for {
fmt.Printf("%s> ", what)
result := ReadLine()
i, err := strconv.Atoi(result)
if err != nil {
if newOk {
return result
}
continue
}
if i >= 1 && i <= len(defaults) {
return defaults[i-1]
}
}
}
// Show the contents of the remote
func ShowRemote(name string) {
fmt.Printf("--------------------\n")
fmt.Printf("[%s]\n", name)
for _, key := range ConfigFile.GetKeyList(name) {
fmt.Printf("%s = %s\n", key, ConfigFile.MustValue(name, key))
}
fmt.Printf("--------------------\n")
}
// Print the contents of the remote and ask if it is OK
func OkRemote(name string) bool {
ShowRemote(name)
switch i := Command([]string{"yYes this is OK", "eEdit this remote", "dDelete this remote"}); i {
case 0:
return true
case 1:
return false
case 2:
ConfigFile.DeleteSection(name)
return true
default:
log.Printf("Bad choice %d", i)
}
return false
}
// Make a new remote
func NewRemote(name string) {
fmt.Printf("What type of source is it?\n")
types := []string{}
for _, item := range fsRegistry {
types = append(types, item.Name)
}
newType := Choose("type", types, nil, false)
ConfigFile.SetValue(name, "type", newType)
fs, err := Find(newType)
if err != nil {
log.Fatalf("Failed to find fs: %v", err)
}
for _, option := range fs.Options {
ConfigFile.SetValue(name, option.Name, option.Choose())
}
if OkRemote(name) {
SaveConfig()
return
}
EditRemote(name)
}
// Edit a remote
func EditRemote(name string) {
ShowRemote(name)
fmt.Printf("Edit remote\n")
for {
for _, key := range ConfigFile.GetKeyList(name) {
value := ConfigFile.MustValue(name, key)
fmt.Printf("Press enter to accept current value, or type in a new one\n")
fmt.Printf("%s = %s>", key, value)
newValue := ReadLine()
if newValue != "" {
ConfigFile.SetValue(name, key, newValue)
}
}
if OkRemote(name) {
break
}
}
SaveConfig()
}
// Edit the config file interactively
func EditConfig() {
for {
fmt.Printf("Current remotes:\n\n")
ShowConfig()
fmt.Printf("\n")
switch i := Command([]string{"eEdit existing remote", "nNew remote", "dDelete remote", "qQuit config"}); i {
case 0:
name := ChooseRemote()
EditRemote(name)
case 1:
fmt.Printf("name> ")
name := ReadLine()
NewRemote(name)
case 2:
name := ChooseRemote()
ConfigFile.DeleteSection(name)
case 3:
return
}
}
}