diff options
author | Přemysl Eric Janouch <p@janouch.name> | 2023-12-26 07:42:51 +0100 |
---|---|---|
committer | Přemysl Eric Janouch <p@janouch.name> | 2023-12-26 07:42:51 +0100 |
commit | 37292f8897efae517e5a22142d9cafdd93a7891c (patch) | |
tree | c5e542a3d441b9a1e67e05c9059adb2dcf5d217d /main.go | |
parent | 6345d9e18e126b85c7e3f59e1b390084320bec8c (diff) | |
download | gallery-37292f8897efae517e5a22142d9cafdd93a7891c.tar.gz gallery-37292f8897efae517e5a22142d9cafdd93a7891c.tar.xz gallery-37292f8897efae517e5a22142d9cafdd93a7891c.zip |
Command arguments
Diffstat (limited to 'main.go')
-rw-r--r-- | main.go | 139 |
1 files changed, 92 insertions, 47 deletions
@@ -262,13 +262,16 @@ func parallelize(strings []string, callback parallelFunc) error { // cmdInit initializes a "gallery directory" that contains gallery.sqlite, // images, thumbs. func cmdInit(fs *flag.FlagSet, args []string) error { - if len(args) != 1 { + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() != 1 { return errWrongUsage } - - if err := openDB(args[0]); err != nil { + if err := openDB(fs.Arg(0)); err != nil { return err } + if _, err := db.Exec(initializeSQL); err != nil { return err } @@ -1076,14 +1079,17 @@ func handleAPISearch(w http.ResponseWriter, r *http.Request) { // cmdWeb runs a web UI against GD on ADDRESS. func cmdWeb(fs *flag.FlagSet, args []string) error { - if len(args) != 2 { + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() != 2 { return errWrongUsage } - if err := openDB(args[0]); err != nil { + if err := openDB(fs.Arg(0)); err != nil { return err } - address := args[1] + address := fs.Arg(1) // This separation is not strictly necessary, // but having an elementary level of security doesn't hurt either. @@ -1683,7 +1689,6 @@ func syncRun(ctx context.Context, tx *sql.Tx, roots []string) error { // cmdSync ensures the given (sub)roots are accurately reflected // in the database. func cmdSync(fs *flag.FlagSet, args []string) error { - // TODO: Convert the other commands as well. fullpaths := fs.Bool("fullpaths", false, "don't basename the arguments") if err := fs.Parse(args); err != nil { return err @@ -1691,7 +1696,6 @@ func cmdSync(fs *flag.FlagSet, args []string) error { if fs.NArg() < 2 { return errWrongUsage } - if err := openDB(fs.Arg(0)); err != nil { return err } @@ -1750,10 +1754,13 @@ func cmdSync(fs *flag.FlagSet, args []string) error { // cmdRemove is for manual removal of subtrees from the database. // Beware that inputs are database, not filesystem paths. func cmdRemove(fs *flag.FlagSet, args []string) error { - if len(args) < 2 { + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() < 2 { return errWrongUsage } - if err := openDB(args[0]); err != nil { + if err := openDB(fs.Arg(0)); err != nil { return err } @@ -1763,7 +1770,7 @@ func cmdRemove(fs *flag.FlagSet, args []string) error { } defer tx.Rollback() - for _, path := range args[1:] { + for _, path := range fs.Args()[1:] { var id sql.NullInt64 for _, name := range decodeWebPath(path) { if err := tx.QueryRow(`SELECT id FROM node @@ -1795,18 +1802,21 @@ func cmdRemove(fs *flag.FlagSet, args []string) error { // cmdTag mass imports tags from data passed on stdin as a TSV // of SHA1 TAG WEIGHT entries. func cmdTag(fs *flag.FlagSet, args []string) error { - if len(args) < 2 || len(args) > 3 { - return errors.New("usage: GD SPACE [DESCRIPTION]") + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() < 2 || fs.NArg() > 3 { + return errWrongUsage } - if err := openDB(args[0]); err != nil { + if err := openDB(fs.Arg(0)); err != nil { return err } - space := args[1] + space := fs.Arg(1) var description sql.NullString - if len(args) >= 3 { - description = sql.NullString{String: args[2], Valid: true} + if fs.NArg() >= 3 { + description = sql.NullString{String: fs.Arg(2), Valid: true} } // Note that starting as a write transaction prevents deadlocks. @@ -1871,7 +1881,6 @@ func cmdTag(fs *flag.FlagSet, args []string) error { if err := scanner.Err(); err != nil { return err } - return tx.Commit() } @@ -1946,10 +1955,13 @@ func checkFiles(root, suffix string, hashes []string) (bool, []string, error) { // cmdCheck carries out various database consistency checks. func cmdCheck(fs *flag.FlagSet, args []string) error { - if len(args) != 1 { - return errors.New("usage: GD") + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() != 1 { + return errWrongUsage } - if err := openDB(args[0]); err != nil { + if err := openDB(fs.Arg(0)); err != nil { return err } @@ -2063,14 +2075,17 @@ func makeThumbnailFor(sha1 string) (message string, err error) { // cmdThumbnail generates missing thumbnails, in parallel. func cmdThumbnail(fs *flag.FlagSet, args []string) error { - if len(args) < 1 { + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() < 1 { return errWrongUsage } - if err := openDB(args[0]); err != nil { + if err := openDB(fs.Arg(0)); err != nil { return err } - hexSHA1 := args[1:] + hexSHA1 := fs.Args()[1:] if len(hexSHA1) == 0 { // Get all unique images in the database with no thumbnail. var err error @@ -2216,16 +2231,19 @@ func makeDhashFor(sha1 string) (message string, err error) { return "", err } -// cmdDhash generates perceptual hash from thumbnails. +// cmdDhash computes perceptual hashes from thumbnails. func cmdDhash(fs *flag.FlagSet, args []string) error { - if len(args) < 1 { + if err := fs.Parse(args); err != nil { + return err + } + if fs.NArg() < 1 { return errWrongUsage } - if err := openDB(args[0]); err != nil { + if err := openDB(fs.Arg(0)); err != nil { return err } - hexSHA1 := args[1:] + hexSHA1 := fs.Args()[1:] if len(hexSHA1) == 0 { var err error hexSHA1, err = dbCollectStrings(`SELECT sha1 FROM image @@ -2242,40 +2260,67 @@ func cmdDhash(fs *flag.FlagSet, args []string) error { var errWrongUsage = errors.New("wrong usage") var commands = map[string]struct { - handler func(*flag.FlagSet, []string) error - usage string + handler func(*flag.FlagSet, []string) error + usage string + function string }{ - "init": {cmdInit, "GD"}, - "web": {cmdWeb, "GD ADDRESS"}, - "tag": {cmdTag, "GD SPACE [DESCRIPTION]"}, - "sync": {cmdSync, "GD ROOT..."}, - "remove": {cmdRemove, "GD PATH..."}, - "check": {cmdCheck, "GD"}, - "thumbnail": {cmdThumbnail, "GD [SHA1...]"}, - "dhash": {cmdDhash, "GD [SHA1...]"}, + "init": {cmdInit, "GD", "Initialize a database."}, + "web": {cmdWeb, "GD ADDRESS", "Launch a web interface."}, + "tag": {cmdTag, "GD SPACE [DESCRIPTION]", "Import tags."}, + "sync": {cmdSync, "GD ROOT...", "Synchronise with the filesystem."}, + "remove": {cmdRemove, "GD PATH...", "Remove database subtrees."}, + "check": {cmdCheck, "GD", "Run consistency checks."}, + "thumbnail": {cmdThumbnail, "GD [SHA1...]", "Generate thumbnails."}, + "dhash": {cmdDhash, "GD [SHA1...]", "Compute perceptual hashes."}, +} + +func usage() { + f := flag.CommandLine.Output() + fmt.Fprintf(f, "Usage: %s COMMAND [ARG...]\n", os.Args[0]) + flag.PrintDefaults() + + // The alphabetic ordering is unfortunate, but tolerable. + keys := []string{} + for key := range commands { + keys = append(keys, key) + } + sort.Strings(keys) + + fmt.Fprintf(f, "\nCommands:\n") + for _, key := range keys { + fmt.Fprintf(f, " %s [OPTION...] %s\n \t%s\n", + key, commands[key].usage, commands[key].function) + } } func main() { - // TODO: Implement a global -h command. - // The flag package doesn't reorder, unlike GNU, so it's fine. - if len(os.Args) <= 2 { - log.Fatalln("Missing arguments") + // This implements the -h switch for us by default. + // The rest of the handling here closely follows what flag does internally. + flag.Usage = usage + flag.Parse() + if flag.NArg() < 1 { + flag.Usage() + os.Exit(2) } - cmd, ok := commands[os.Args[1]] + cmd, ok := commands[flag.Arg(0)] if !ok { - log.Fatalln("Unknown command: " + os.Args[1]) + fmt.Fprintf(flag.CommandLine.Output(), + "unknown command: %s\n", flag.Arg(0)) + flag.Usage() + os.Exit(2) } - fs := flag.NewFlagSet(os.Args[1], flag.ExitOnError) + fs := flag.NewFlagSet(flag.Arg(0), flag.ExitOnError) fs.Usage = func() { fmt.Fprintf(fs.Output(), - "Usage: %s [OPTION...] %s...\n", fs.Name(), cmd.usage) + "Usage: %s [OPTION...] %s\n%s\n", + fs.Name(), cmd.usage, cmd.function) fs.PrintDefaults() } taskSemaphore = newSemaphore(runtime.NumCPU()) - err := cmd.handler(fs, os.Args[2:]) + err := cmd.handler(fs, flag.Args()[1:]) // Note that the database object has a closing finalizer, // we just additionally print any errors coming from there. |