// runMakeBucketCmd is the handler for mc mb command func runMakeBucketCmd(ctx *cli.Context) { if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "mb", 1) // last argument is exit code } if !isMcConfigExists() { console.Fatalf("Please run \"mc config generate\". %s\n", errNotConfigured{}) } config := mustGetMcConfig() for _, arg := range ctx.Args() { targetURL, err := getExpandedURL(arg, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", arg, err) } } msg, err := doMakeBucketCmd(targetURL) if err != nil { console.Fatalln(msg) } console.Infoln(msg) } }
// runListCmd - is a handler for mc ls command func runListCmd(ctx *cli.Context) { args := ctx.Args() if globalAliasFlag { if !ctx.Args().Present() { args = []string{"."} } } else if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "ls", 1) // last argument is exit code } config := mustGetMcConfig() for _, arg := range args { targetURL, err := getExpandedURL(arg, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", arg, err) } } // if recursive strip off the "..." newTargetURL := stripRecursiveURL(targetURL) err = doListCmd(newTargetURL, isURLRecursive(targetURL)) if err != nil { console.Fatalf("Failed to list : %s. %s\n", targetURL, err) } } }
func runAccessCmd(ctx *cli.Context) { if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "access", 1) // last argument is exit code } config := mustGetMcConfig() acl := bucketACL(ctx.Args().First()) if !acl.isValidBucketACL() { console.Fatalf("Valid types are [private, public, readonly]. %s\n", errInvalidACL{acl: acl.String()}) } for _, arg := range ctx.Args().Tail() { targetURL, err := getExpandedURL(arg, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", arg, err) } } msg, err := doUpdateAccessCmd(targetURL, acl) if err != nil { console.Fatalln(msg) } console.Infoln(msg) } }
// checkCopySyntaxTypeB verifies if the source is a valid file and target is a valid dir. func checkCopySyntaxTypeB(srcURLs []string, tgtURL string) { if len(srcURLs) != 1 { console.Fatalf("Invalid number of source arguments to copy command. %s\n", NewIodine(iodine.New(errInvalidArgument{}, nil))) } srcURL := srcURLs[0] _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if srcContent.Type.IsDir() { console.Fatalf("Source ‘%s’ is a folder. Use ‘%s...’ argument to copy this folder and its contents recursively. %s\n", srcURL, srcURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } if !srcContent.Type.IsRegular() { console.Fatalf("Source ‘%s’ is not a file. %s\n", srcURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } _, tgtContent, err := url2Stat(tgtURL) // Target exist?. if err == nil { if !tgtContent.Type.IsDir() { console.Fatalf("Target ‘%s’ is not a folder. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } } }
func runCastCmd(ctx *cli.Context) { checkCastSyntax(ctx) session := newSessionV2() var err error session.Header.CommandType = "cast" session.Header.RootPath, err = os.Getwd() if err != nil { session.Close() session.Delete() console.Fatalf("Unable to get current working folder. %s\n", err) } // extract URLs. session.Header.CommandArgs, err = args2URLs(ctx.Args()) if err != nil { session.Close() session.Delete() console.Fatalf("One or more unknown URL types found in %s. %s\n", ctx.Args(), err) } doCastCmdSession(session) session.Close() session.Delete() }
func runCatCmd(ctx *cli.Context) { if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cat", 1) // last argument is exit code } if !isMcConfigExists() { console.Fatalf("Please run \"mc config generate\". %s\n", errNotConfigured{}) } config := mustGetMcConfig() // Convert arguments to URLs: expand alias, fix format... for _, arg := range ctx.Args() { sourceURL, err := getExpandedURL(arg, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", arg, err) } } errorMsg, err := doCatCmd(sourceURL) if err != nil { console.Fatalln(errorMsg) } } }
// Check for the environment early on and gracefully report. func checkConfig() { _, err := user.Current() if err != nil { console.Fatalf("Unable to determine current user. %s\n", err) } // Ensures config file is sane _, err = getMcConfig() if err != nil { console.Fatalf("Unable to read config file. %s\n", err) } }
// mustGetHostConfig retrieves host specific configuration such as access keys, exits upon error func mustGetHostConfig(URL string) *hostConfig { hostCfg, err := getHostConfig(URL) if err != nil { console.Fatalf("Unable to retrieve host configuration. %s\n", NewIodine(iodine.New(err, nil))) } return hostCfg }
// loadSession - reads session file if exists and re-initiates internal variables func loadSessionV2(sid string) (*sessionV2, error) { if !isSessionDirExists() { return nil, NewIodine(iodine.New(errInvalidArgument{}, nil)) } sessionFile := getSessionFile(sid) _, err := os.Stat(sessionFile) if err != nil { return nil, NewIodine(iodine.New(err, nil)) } s := &sessionV2{} s.Header = &sessionV2Header{} s.SessionID = sid s.Header.Version = "1.1.0" qs, err := quick.New(s.Header) if err != nil { return nil, NewIodine(iodine.New(err, nil)) } err = qs.Load(sessionFile) if err != nil { return nil, NewIodine(iodine.New(err, nil)) } s.mutex = new(sync.Mutex) s.Header = qs.Data().(*sessionV2Header) s.DataFP, err = os.Open(getSessionDataFile(s.SessionID)) if err != nil { console.Fatalf("Unable to open session data file \""+getSessionDataFile(s.SessionID)+"\". %s", NewIodine(iodine.New(errNotConfigured{}, nil))) } return s, nil }
// mustGetMcConfigDir - construct minio client config folder or fail func mustGetMcConfigDir() (configDir string) { configDir, err := getMcConfigDir() if err != nil { console.Fatalf("Unable to determine default configuration folder. %s\n", NewIodine(iodine.New(err, nil))) } return configDir }
// mustGetMcConfigPath - similar to getMcConfigPath, ignores errors func mustGetMcConfigPath() string { p, err := getMcConfigPath() if err != nil { console.Fatalf("Unable to determine default config path. %s\n", NewIodine(iodine.New(err, nil))) } return p }
// mustNewConfig instantiates a new config handler, exists upon error func mustNewConfig() quick.Config { config, err := newConfig() if err != nil { console.Fatalf("Unable to instantiate a new config handler. %s\n", NewIodine(iodine.New(err, nil))) } return config }
// mustGetMcConfig - reads configuration file and returns configs, exits on error func mustGetMcConfig() *configV1 { config, err := getMcConfig() if err != nil { console.Fatalf("Unable to retrieve mc configuration. %s\n", err) } return config }
// NOTE: All the parse rules should reduced to A: Copy(Source, Target). // // * VALID RULES // ======================= // A: copy(f, f) -> copy(f, f) // B: copy(f, d) -> copy(f, d/f) -> A // C: copy(d1..., d2) -> []copy(d1/f, d1/d2/f) -> []A // // * INVALID RULES // ========================= // A: copy(d, *) // B: copy(d..., f) // C: copy(*, d...) // func checkCopySyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), iodine.New(err, nil)) } srcURLs := URLs[:len(URLs)-1] tgtURL := URLs[len(URLs)-1] /****** Generic rules *******/ // Recursive URLs are not allowed in target. if isURLRecursive(tgtURL) { console.Fatalf("Target ‘%s’ cannot be recursive. %s\n", tgtURL, iodine.New(err, nil)) } switch guessCopyURLType(srcURLs, tgtURL) { case copyURLsTypeA: // Source is already a regular file. // no verification needed, pass through case copyURLsTypeB: // Source is already a regular file. // no verification needed, pass through case copyURLsTypeC: for _, srcURL := range srcURLs { srcURL = stripRecursiveURL(srcURL) _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, iodine.New(err, nil)) } if srcContent.Type.IsRegular() { // Ellipses is supported only for directories. console.Fatalf("Source ‘%s’ is not a directory. %s\n", stripRecursiveURL(srcURL), iodine.New(err, nil)) } } case copyURLsTypeD: // only verify if target is a valid directory and exists if !isTargetURLDir(tgtURL) { console.Fatalf("Target ‘%s’ should be a directory and exist, when we have a mixture of files and folders in source\n", tgtURL) } default: console.Fatalln("Invalid arguments. Unable to determine how to copy. Please report this issue at https://github.com/minio/mc/issues") } }
func checkCastSyntaxTypeB(srcURL string, tgtURLs []string) { if len(tgtURLs) == 0 && tgtURLs == nil { console.Fatalf("Invalid number of target arguments to cast command. %s\n", NewIodine(iodine.New(errInvalidArgument{}, nil))) } _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if srcContent.Type.IsDir() { console.Fatalf("Source ‘%s’ is a folder. Use ‘%s...’ argument to cast this folder and its contents recursively. %s\n", srcURL, srcURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } if !srcContent.Type.IsRegular() { console.Fatalf("Source ‘%s’ is not a file. %s\n", srcURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } // targetURL can be folder or file, internally TypeB calls TypeA if it finds a file }
// migrateSessionV1ToV2 migrates all session files from v1 to v2. // This function should be called from the main early on. func migrateSessionV1ToV2() { for _, sid := range getSessionIDsV1() { err := os.Remove(getSessionFileV1(sid)) if err != nil { console.Fatalf("Migration failed. Unable to remove old session file %s. %s\n", getSessionFileV1(sid), NewIodine(iodine.New(err, nil))) } } }
// checkCopySyntaxTypeD verifies if the source is a valid list of file or valid recursive dir and target is a valid dir. func checkCopySyntaxTypeD(srcURLs []string, tgtURL string) { for _, srcURL := range srcURLs { if isURLRecursive(srcURL) { srcURL = stripRecursiveURL(srcURL) _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if !srcContent.Type.IsDir() { // Ellipses is supported only for folders. console.Fatalf("Source ‘%s’ is not a folder. %s\n", stripRecursiveURL(srcURL), NewIodine(iodine.New(errInvalidArgument{}, nil))) } } else { // Regular URL. _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if srcContent.Type.IsDir() { console.Fatalf("Source ‘%s’ is a folder. Use ‘%s...’ argument to copy this folder and its contents recursively. %s\n", srcURL, srcURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } if !srcContent.Type.IsRegular() { console.Fatalf("Source ‘%s’ is not a file. %s\n", srcURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } } } _, tgtContent, err := url2Stat(tgtURL) // Target exist?. if err != nil { console.Fatalf("Unable to stat target ‘%s’. %s\n", tgtURL, NewIodine(iodine.New(err, nil))) } if !tgtContent.Type.IsDir() { console.Fatalf("Target ‘%s’ is not a folder. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } }
// Verify main command syntax. func checkMainSyntax(c *cli.Context) { configPath, err := getConfigPath() if err != nil { console.Fatalf("Unable to obtain user's home directory. \nError: %s\n", err) } if configPath == "" { console.Fatalln("Config folder cannot be empty, please specify --config-dir <foldername>.") } }
// checkCastSyntax(URLs []string) func checkCastSyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cast", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), NewIodine(iodine.New(err, nil))) } srcURL := URLs[0] tgtURLs := URLs[1:] /****** Generic rules *******/ // Source cannot be a folder (except when recursive) if !isURLRecursive(srcURL) { _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if !srcContent.Type.IsRegular() { if srcContent.Type.IsDir() { console.Fatalf("Source ‘%s’ is a folder. Please use ‘%s...’ to recursively copy this folder and its contents.\n", srcURL, srcURL) } console.Fatalf("Source ‘%s’ is not a regular file.\n", srcURL) } } // Recursive URLs are not allowed in target. for _, tgtURL := range tgtURLs { if isURLRecursive(tgtURL) { console.Fatalf("Target ‘%s’ cannot be recursive. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } } for _, tgtURL := range tgtURLs { url, err := client.Parse(tgtURL) if err != nil { console.Fatalf("Unable to parse target ‘%s’ argument. %s\n", tgtURL, NewIodine(iodine.New(err, nil))) } if url.Host != "" { if url.Path == string(url.Separator) { console.Fatalf("Bucket creation detected for %s, cloud storage URL's should use ‘mc mb’ to create buckets\n", tgtURL) } } } switch guessCastURLType(srcURL, tgtURLs) { case castURLsTypeA: // File -> File. checkCastSyntaxTypeA(srcURL, tgtURLs) case castURLsTypeB: // File -> Folder. checkCastSyntaxTypeB(srcURL, tgtURLs) case castURLsTypeC: // Folder -> Folder. checkCastSyntaxTypeC(srcURL, tgtURLs) default: console.Fatalln("Invalid arguments. Unable to determine how to cast. Please report this issue at https://github.com/minio/mc/issues") } }
// checkCastSyntax(URLs []string) func checkCastSyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cast", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), iodine.New(err, nil)) } srcURL := URLs[0] tgtURLs := URLs[1:] /****** Generic rules *******/ // Source cannot be a directory (except when recursive) if !isURLRecursive(srcURL) { _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, iodine.New(err, nil)) } if !srcContent.Type.IsRegular() { if srcContent.Type.IsDir() { console.Fatalf("Source ‘%s’ is a directory. Please use ‘%s...’ to recursively copy this directory and its contents.\n", srcURL, srcURL) } console.Fatalf("Source ‘%s’ is not a regular file.\n", srcURL) } } // Recursive URLs are not allowed in target. for _, tgtURL := range tgtURLs { if isURLRecursive(tgtURL) { console.Fatalf("Target ‘%s’ cannot be recursive. %s\n", tgtURL, iodine.New(err, nil)) } } switch guessCastURLType(srcURL, tgtURLs) { case castURLsTypeA: // Source is already a regular file. // case castURLsTypeB: // Source is already a regular file. // case castURLsTypeC: srcURL = stripRecursiveURL(srcURL) _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, iodine.New(err, nil)) } if srcContent.Type.IsRegular() { // Ellipses is supported only for directories. console.Fatalf("Source ‘%s’ is not a directory. %s\n", stripRecursiveURL(srcURL), iodine.New(err, nil)) } default: console.Fatalln("Invalid arguments. Unable to determine how to cast. Please report this issue at https://github.com/minio/mc/issues") } }
// provides a new session func newSessionV2() *sessionV2 { if !isMcConfigExists() { console.Fatalf("Please run \"mc config generate\". %s\n", errNotConfigured{}) } s := &sessionV2{} s.Header = &sessionV2Header{} s.Header.Version = "1.1.0" // map of command and files copied s.Header.CommandArgs = nil s.Header.When = time.Now().UTC() s.mutex = new(sync.Mutex) s.SessionID = newSID(8) var err error s.DataFP, err = os.Create(getSessionDataFile(s.SessionID)) if err != nil { console.Fatalf("Unable to create session data file \""+getSessionDataFile(s.SessionID)+"\". %s\n", err) } return s }
func getSessionIDs() (sids []string) { sessionList, err := filepath.Glob(getSessionDir() + "/*.json") if err != nil { console.Fatalf("Unable to list session folder ‘%s’, %s", getSessionDir(), NewIodine(iodine.New(err, nil))) } for _, path := range sessionList { sids = append(sids, strings.TrimSuffix(filepath.Base(path), ".json")) } return sids }
func registerApp() *cli.App { // Register all the commands registerCmd(lsCmd) // List contents of a bucket registerCmd(mbCmd) // make a bucket registerCmd(catCmd) // concantenate an object to standard output registerCmd(cpCmd) // copy objects and files from multiple sources to single destination registerCmd(castCmd) // cast objects and files from single source to multiple destinations registerCmd(sessionCmd) // session handling for resuming copy and cast operations registerCmd(diffCmd) // compare two objects registerCmd(accessCmd) // set permissions [public, private, readonly, authenticated] for buckets and folders. registerCmd(configCmd) // generate configuration "/home/harsha/.mc/config.json" file. registerCmd(updateCmd) // update Check for new software updates // register all the flags registerFlag(configFlag) // path to config folder registerFlag(quietFlag) // suppress console output registerFlag(forceFlag) // force copying data registerFlag(aliasFlag) // OS toolchain mimic registerFlag(themeFlag) // console theme flag registerFlag(jsonFlag) // json formatted output registerFlag(debugFlag) // enable debugging output app := cli.NewApp() app.Usage = "Minio Client for cloud storage and filesystems" app.Version = getVersion() app.Commands = commands app.Compiled = getVersion() app.Flags = flags app.Author = "Minio.io" app.CustomAppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: {{.Name}} {{if .Flags}}[global flags] {{end}}command{{if .Flags}} [command flags]{{end}} [arguments...] COMMANDS: {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} {{end}}{{if .Flags}} GLOBAL FLAGS: {{range .Flags}}{{.}} {{end}}{{end}} VERSION: {{if .Compiled}} {{.Compiled}}{{end}} {{range $key, $value := ExtraInfo}} {{$key}}: {{$value}} {{end}} ` app.CommandNotFound = func(ctx *cli.Context, command string) { console.Fatalf("Command not found: ‘%s’\n", command) } return app }
// NOTE: All the parse rules should reduced to A: Copy(Source, Target). // // * VALID RULES // ======================= // A: copy(f, f) -> copy(f, f) // B: copy(f, d) -> copy(f, d/f) -> A // C: copy(d1..., d2) -> []copy(d1/f, d2/d1/f) -> []A // D: copy([]{d1... | f}, d2) -> []{copy(d1/f, d2/d1/f) | copy(f, d2/f )} -> []A // // * INVALID RULES // ========================= // A: copy(d, *) // B: copy(d..., f) // C: copy(*, d...) // func checkCopySyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), NewIodine(iodine.New(err, nil))) } srcURLs := URLs[:len(URLs)-1] tgtURL := URLs[len(URLs)-1] /****** Generic rules *******/ // Recursive URLs are not allowed in target. if isURLRecursive(tgtURL) { console.Fatalf("Recursive option is not supported for target ‘%s’ argument. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } url, err := client.Parse(tgtURL) if err != nil { console.Fatalf("Unable to parse target ‘%s’ argument. %s\n", tgtURL, NewIodine(iodine.New(err, nil))) } if url.Host != "" { if url.Path == string(url.Separator) { console.Fatalf("Bucket creation detected for %s, cloud storage URL's should use ‘mc mb’ to create buckets\n", tgtURL) } } switch guessCopyURLType(srcURLs, tgtURL) { case copyURLsTypeA: // File -> File. checkCopySyntaxTypeA(srcURLs, tgtURL) case copyURLsTypeB: // File -> Folder. checkCopySyntaxTypeB(srcURLs, tgtURL) case copyURLsTypeC: // Folder... -> Folder. checkCopySyntaxTypeC(srcURLs, tgtURL) case copyURLsTypeD: // File | Folder... -> Folder. checkCopySyntaxTypeD(srcURLs, tgtURL) default: console.Fatalln("Invalid arguments. Unable to determine how to copy. Please report this issue at https://github.com/minio/mc/issues") } }
// runDiffCmd - is a handler for mc diff command func runDiffCmd(ctx *cli.Context) { if len(ctx.Args()) != 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "diff", 1) // last argument is exit code } config := mustGetMcConfig() firstURL := ctx.Args().First() secondURL := ctx.Args()[1] var err error firstURL, err = getExpandedURL(firstURL, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", firstURL, err) } } secondURL, err = getExpandedURL(secondURL, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", secondURL, err) } } if isURLRecursive(secondURL) { console.Fatalf("Second URL cannot be recursive. %s\n", errInvalidArgument{}) } newFirstURL := stripRecursiveURL(firstURL) for diff := range doDiffCmd(newFirstURL, secondURL, isURLRecursive(firstURL)) { if diff.err != nil { console.Fatalln(diff.message) } console.Infoln(diff.message) } }
func clearSession(sid string) { if sid == "all" { for _, sid := range getSessionIDs() { session, err := loadSessionV2(sid) if err != nil { console.Fatalf("Unable to load session ‘%s’, %s", sid, NewIodine(iodine.New(err, nil))) } session.Close() } return } if !isSession(sid) { console.Fatalf("Session ‘%s’ not found.\n", sid) } session, err := loadSessionV2(sid) if err != nil { console.Fatalf("Unable to load session ‘%s’, %s", sid, NewIodine(iodine.New(err, nil))) } session.Close() }
func getSessionIDsV1() (sids []string) { sessionList, err := filepath.Glob(getSessionDir() + "/*") if err != nil { console.Fatalf("Unable to list session directory ‘%s’, %s", getSessionDir(), NewIodine(iodine.New(err, nil))) } for _, path := range sessionList { sidReg := regexp.MustCompile("^[a-zA-Z]{8}$") sid := filepath.Base(path) if sidReg.Match([]byte(sid)) { sessionV1, err := loadSessionV1(sid) if err != nil { console.Fatalf("Unable to load session ‘%s’, %s", sid, NewIodine(iodine.New(err, nil))) } if sessionV1.Version != "1.0.0" { continue } sids = append(sids, sid) } } return sids }
func firstTimeRun() { if !isMcConfigExists() { if err := createMcConfigDir(); err != nil { console.Fatalf("Unable to create ‘mc’ config folder. %s\n", err) } config, err := newConfig() if err != nil { console.Fatalln(NewIodine(iodine.New(err, nil))) } err = writeConfig(config) if err != nil { console.Fatalln(NewIodine(iodine.New(err, nil))) } console.Infoln("Configuration written to [" + mustGetMcConfigPath() + "]. Please update your access credentials.") } if !isSessionDirExists() { if err := createSessionDir(); err != nil { console.Fatalf("Unable to create ‘mc’ session folder. %s\n", err) } } checkGolangVersion() }
func checkCastSyntaxTypeC(srcURL string, tgtURLs []string) { if len(tgtURLs) == 0 && tgtURLs == nil { console.Fatalf("Invalid number of target arguments to cast command. %s\n", NewIodine(iodine.New(errInvalidArgument{}, nil))) } srcURL = stripRecursiveURL(srcURL) _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if srcContent.Type.IsRegular() { // Ellipses is supported only for folders. console.Fatalf("Source ‘%s’ is not a folder. %s\n", stripRecursiveURL(srcURL), NewIodine(iodine.New(err, nil))) } for _, tgtURL := range tgtURLs { _, content, err := url2Stat(tgtURL) if err == nil { if !content.Type.IsDir() { console.Fatalf("One of the target ‘%s’ is not a folder. cannot have mixtures of directories and files while copying directories recursively. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } } } }
// checkCopySyntaxTypeC verifies if the source is a valid recursive dir and target is a valid dir. func checkCopySyntaxTypeC(srcURLs []string, tgtURL string) { if len(srcURLs) != 1 { console.Fatalf("Invalid number of source arguments to copy command. %s\n", NewIodine(iodine.New(errInvalidArgument{}, nil))) } srcURL := srcURLs[0] srcURL = stripRecursiveURL(srcURL) _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if srcContent.Type.IsRegular() { // Ellipses is supported only for folders. console.Fatalf("Source ‘%s’ is not a folder. %s\n", stripRecursiveURL(srcURL), NewIodine(iodine.New(errInvalidArgument{}, nil))) } _, tgtContent, err := url2Stat(tgtURL) // Target exist?. if err == nil { if !tgtContent.Type.IsDir() { console.Fatalf("Target ‘%s’ is not a folder. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } } }