// Opens the database at the specified path func OpenAt(path string) (*Database, error) { log.Infof(2, "opening database at '%v'.", path) _, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { log.Warnf("creating database at '%v'.", path) } else { log.Warnf("could not stat database: %v", err) } } connection, err := sql.Open("sqlite3", path) if err != nil { return nil, fmt.Errorf("could not open database: %v", err) } transaction, err := connection.Begin() if err != nil { return nil, fmt.Errorf("could not begin transaciton: %v", err) } database := &Database{connection, transaction} if err := database.CreateSchema(); err != nil { return nil, errors.New("could not create database schema: " + err.Error()) } return database, nil }
func tagFrom(fromPath string, paths []string, recursive bool) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() fingerprintAlgorithmSetting, err := store.Setting("fingerprintAlgorithm") if err != nil { return fmt.Errorf("could not retrieve fingerprint algorithm: %v", err) } file, err := store.FileByPath(fromPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", fromPath, err) } if file == nil { return fmt.Errorf("%v: path is not tagged") } fileTags, err := store.FileTagsByFileId(file.Id) if err != nil { return fmt.Errorf("%v: could not retrieve filetags: %v", fromPath, err) } tagValuePairs := make([]TagValuePair, len(fileTags)) for index, fileTag := range fileTags { tagValuePairs[index] = TagValuePair{fileTag.TagId, fileTag.ValueId} } wereErrors := false for _, path := range paths { if err := tagPath(store, path, tagValuePairs, recursive, fingerprintAlgorithmSetting.Value); err != nil { switch { case os.IsPermission(err): log.Warnf("%v: permisison denied", path) wereErrors = true case os.IsNotExist(err): log.Warnf("%v: no such file", path) wereErrors = true default: return fmt.Errorf("%v: could not stat file: %v", path, err) } } } if wereErrors { return blankError } return nil }
func statusCheckFile(file *entities.File, report *StatusReport) error { relPath := path.Rel(file.Path()) log.Infof(2, "%v: checking file status.", file.Path()) stat, err := os.Stat(file.Path()) if err != nil { switch { case os.IsNotExist(err): log.Infof(2, "%v: file is missing.", file.Path()) report.AddRow(Row{relPath, MISSING}) return nil case os.IsPermission(err): log.Warnf("%v: permission denied.", file.Path()) case strings.Contains(err.Error(), "not a directory"): report.AddRow(Row{relPath, MISSING}) return nil default: return fmt.Errorf("%v: could not stat: %v", file.Path(), err) } } else { if stat.Size() != file.Size || stat.ModTime().UTC() != file.ModTime { log.Infof(2, "%v: file is modified.", file.Path()) report.AddRow(Row{relPath, MODIFIED}) } else { log.Infof(2, "%v: file is unchanged.", file.Path()) report.AddRow(Row{relPath, TAGGED}) } } return nil }
func createTags(names []string) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() tags, err := store.TagsByNames(names) if err != nil { return fmt.Errorf("could not retrieve tags %v: %v", names, err) } wereErrors := false for _, tag := range tags { log.Warnf("tag '%v' already exists", tag.Name) wereErrors = true } if wereErrors { return blankError } for _, name := range names { _, err := store.AddTag(name) if err != nil { return fmt.Errorf("could not add tag '%v': %v", name, err) } } return nil }
func untagPathsAll(paths []string, recursive bool) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() wereErrors := false for _, path := range paths { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", path, err) } file, err := store.FileByPath(absPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err) } if file == nil { log.Warnf("%v: file is not tagged.", path) wereErrors = true continue } log.Infof(2, "%v: removing all tags.", file.Path()) if err := store.DeleteFileTagsByFileId(file.Id); err != nil { return fmt.Errorf("%v: could not remove file's tags: %v", file.Path(), err) } if recursive { childFiles, err := store.FilesByDirectory(file.Path()) if err != nil { return fmt.Errorf("%v: could not retrieve files for directory: %v", file.Path()) } for _, childFile := range childFiles { if err := store.DeleteFileTagsByFileId(childFile.Id); err != nil { return fmt.Errorf("%v: could not remove file's tags: %v", childFile.Path(), err) } } } } if wereErrors { return blankError } return nil }
func findNewFiles(searchPath string, report *StatusReport, dirOnly bool) error { log.Infof(2, "%v: finding new files.", searchPath) relPath := path.Rel(searchPath) if !report.ContainsRow(relPath) { report.AddRow(Row{relPath, UNTAGGED}) } absPath, err := filepath.Abs(searchPath) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", searchPath, err) } stat, err := os.Stat(absPath) if err != nil { switch { case os.IsNotExist(err): return nil case os.IsPermission(err): log.Warnf("%v: permission denied.", searchPath) return nil default: return fmt.Errorf("%v: could not stat: %v", searchPath, err) } } if !dirOnly && stat.IsDir() { dir, err := os.Open(absPath) if err != nil { return fmt.Errorf("%v: could not open file: %v", searchPath, err) } dirNames, err := dir.Readdirnames(0) if err != nil { return fmt.Errorf("%v: could not read directory listing: %v", searchPath, err) } for _, dirName := range dirNames { dirPath := filepath.Join(searchPath, dirName) err = findNewFiles(dirPath, report, dirOnly) if err != nil { return err } } } return nil }
func listFilesForQuery(queryText string, dirOnly, fileOnly, topOnly, leafOnly, recursive, print0, showCount, onePerLine bool) error { if queryText == "" { return fmt.Errorf("query must be specified. Use --all to show all files.") } store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() log.Info(2, "parsing query") expression, err := query.Parse(queryText) if err != nil { return err } log.Info(2, "checking tag names") wereErrors := false tagNames := query.TagNames(expression) tags, err := store.TagsByNames(tagNames) for _, tagName := range tagNames { if !tags.ContainsName(tagName) { log.Warnf("no such tag '%v'.", tagName) wereErrors = true continue } } if wereErrors { return blankError } log.Info(2, "querying database") files, err := store.QueryFiles(expression) if err != nil { return fmt.Errorf("could not query files: %v", err) } if err = listFiles(files, dirOnly, fileOnly, topOnly, leafOnly, recursive, print0, showCount, onePerLine); err != nil { return err } return nil }
func listValuesForTags(store *storage.Storage, tagNames []string, showCount, onePerLine bool) error { wereErrors := false for _, tagName := range tagNames { tag, err := store.TagByName(tagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { log.Warnf("no such tag, '%v'.", tagName) wereErrors = true continue } log.Infof(2, "retrieving values for tag '%v'.", tagName) values, err := store.ValuesByTag(tag.Id) if err != nil { return fmt.Errorf("could not retrieve values for tag '%v': %v", tagName, err) } if showCount { fmt.Printf("%v: %v\n", tagName, len(values)) } else { if onePerLine { fmt.Println(tagName) for _, value := range values { fmt.Println(value.Name) } fmt.Println() } else { valueNames := make([]string, len(values)) for index, value := range values { valueNames[index] = value.Name } fmt.Printf("%v: %v\n", tagName, strings.Join(valueNames, " ")) } } } if wereErrors { return blankError } return nil }
func getOrCreateTag(store *storage.Storage, tagName string) (*entities.Tag, error) { tag, err := store.TagByName(tagName) if err != nil { return nil, fmt.Errorf("could not look up tag '%v': %v", tagName, err) } if tag == nil { tag, err = store.AddTag(tagName) if err != nil { return nil, fmt.Errorf("could not create tag '%v': %v", tagName, err) } log.Warnf("New tag '%v'.", tagName) } return tag, nil }
func copyExec(options Options, args []string) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() sourceTagName := args[0] destTagNames := args[1:] sourceTag, err := store.Db.TagByName(sourceTagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", sourceTagName, err) } if sourceTag == nil { return fmt.Errorf("no such tag '%v'.", sourceTagName) } wereErrors := false for _, destTagName := range destTagNames { destTag, err := store.Db.TagByName(destTagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", destTagName, err) } if destTag != nil { log.Warnf("a tag with name '%v' already exists.", destTagName) wereErrors = true continue } log.Infof(2, "copying tag '%v' to '%v'.", sourceTagName, destTagName) if _, err = store.CopyTag(sourceTag.Id, destTagName); err != nil { return fmt.Errorf("could not copy tag '%v' to '%v': %v", sourceTagName, destTagName, err) } } if wereErrors { return blankError } return nil }
func enumerate(path string, files []FileSystemFile) ([]FileSystemFile, error) { stat, err := os.Stat(path) if err != nil { switch { case os.IsNotExist(err): return files, nil case os.IsPermission(err): log.Warnf("%v: permission denied", path) return files, nil default: return nil, fmt.Errorf("%v: could not stat: %v", path, err) } } files = append(files, FileSystemFile{path, stat.IsDir()}) if stat.IsDir() { dir, err := os.Open(path) if err != nil { return nil, fmt.Errorf("%v: could not open directory: %v", path, err) } names, err := dir.Readdirnames(0) dir.Close() if err != nil { return nil, fmt.Errorf("%v: could not read directory entries: %v", path, err) } for _, name := range names { childPath := filepath.Join(path, name) files, err = enumerate(childPath, files) if err != nil { return nil, err } } } return files, nil }
func deleteExec(options Options, args []string) error { if len(args) == 0 { return fmt.Errorf("no tags to delete specified.") } store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() wereErrors := false for _, tagName := range args { tag, err := store.TagByName(tagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { log.Warnf("no such tag '%v'.", tagName) wereErrors = true continue } err = store.DeleteTag(tag.Id) if err != nil { return fmt.Errorf("could not delete tag '%v': %v", tagName, err) } } if wereErrors { return blankError } return nil }
func enumerateFileSystemPath(files fileInfoMap, path string) error { stat, err := os.Stat(path) if err != nil { switch { case os.IsPermission(err): log.Warnf("%v: permission denied", path) return nil case os.IsNotExist(err): return nil default: return fmt.Errorf("%v: could not stat file: %v", path, err) } } files[path] = stat if stat.IsDir() { file, err := os.Open(path) if err != nil { return fmt.Errorf("%v: could not open file: %v", path, err) } childFilenames, err := file.Readdirnames(0) file.Close() if err != nil { return fmt.Errorf("%v: could not read directory: %v", file.Name(), err) } for _, childFilename := range childFilenames { childPath := filepath.Join(path, childFilename) enumerateFileSystemPath(files, childPath) } } return nil }
func untagPaths(paths, tagArgs []string, recursive bool) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() tagValuePairs := make([]TagValuePair, 0, 10) wereErrors := false for _, tagArg := range tagArgs { var tagName, valueName string index := strings.Index(tagArg, "=") switch index { case -1, 0: tagName = tagArg default: tagName = tagArg[0:index] valueName = tagArg[index+1 : len(tagArg)] } tag, err := store.TagByName(tagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", tagName, err) } if tag == nil { log.Warnf("no such tag '%v'", tagName) wereErrors = true } value, err := store.ValueByName(valueName) if err != nil { return fmt.Errorf("could not retrieve value '%v': %v", valueName, err) } if value == nil { log.Warnf("no such value '%v'", valueName) wereErrors = true } if tag != nil && value != nil { tagValuePairs = append(tagValuePairs, TagValuePair{tag.Id, value.Id}) } } for _, path := range paths { absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not get absolute path: %v", path, err) } file, err := store.FileByPath(absPath) if err != nil { return fmt.Errorf("%v: could not retrieve file: %v", path, err) } if file == nil { log.Warnf("%v: file is not tagged", path) wereErrors = true continue } log.Infof(2, "%v: unapplying tags.", file.Path()) for _, tagValuePair := range tagValuePairs { if err := store.DeleteFileTag(file.Id, tagValuePair.TagId, tagValuePair.ValueId); err != nil { return fmt.Errorf("%v: could not remove tag #%v, value #%v: %v", file.Path(), tagValuePair.TagId, tagValuePair.ValueId, err) } } if recursive { childFiles, err := store.FilesByDirectory(file.Path()) if err != nil { return fmt.Errorf("%v: could not retrieve files for directory: %v", file.Path()) } for _, childFile := range childFiles { log.Infof(2, "%v: unapplying tags.", childFile.Path()) for _, tagValuePair := range tagValuePairs { if err := store.DeleteFileTag(childFile.Id, tagValuePair.TagId, tagValuePair.ValueId); err != nil { return fmt.Errorf("%v: could not remove tag #%v, value #%v: %v", childFile.Path(), tagValuePair.TagId, tagValuePair.ValueId, err) } } } } } if wereErrors { return blankError } return nil }
func tagPaths(tagArgs, paths []string, recursive bool) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() fingerprintAlgorithmSetting, err := store.Setting("fingerprintAlgorithm") if err != nil { return fmt.Errorf("could not retrieve fingerprint algorithm: %v", err) } tagValuePairs := make([]TagValuePair, 0, 10) for _, tagArg := range tagArgs { var tagName, valueName string index := strings.Index(tagArg, "=") switch index { case -1, 0: tagName = tagArg default: tagName = tagArg[0:index] valueName = tagArg[index+1 : len(tagArg)] } tag, err := getOrCreateTag(store, tagName) if err != nil { return err } value, err := getOrCreateValue(store, valueName) if err != nil { return err } tagValuePairs = append(tagValuePairs, TagValuePair{tag.Id, value.Id}) } wereErrors := false for _, path := range paths { if err := tagPath(store, path, tagValuePairs, recursive, fingerprintAlgorithmSetting.Value); err != nil { switch { case os.IsPermission(err): log.Warnf("%v: permisison denied", path) wereErrors = true case os.IsNotExist(err): log.Warnf("%v: no such file", path) wereErrors = true default: return fmt.Errorf("%v: could not stat file: %v", path, err) } } } if wereErrors { return blankError } return nil }
func mergeExec(options Options, args []string) error { if len(args) < 2 { return fmt.Errorf("too few arguments.") } store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() defer store.Commit() destTagName := args[len(args)-1] destTag, err := store.TagByName(destTagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", destTagName, err) } if destTag == nil { return fmt.Errorf("no such tag '%v'.", destTagName) } wereErrors := false for _, sourceTagName := range args[0 : len(args)-1] { if sourceTagName == destTagName { log.Warnf("cannot merge tag '%v' into itself.", sourceTagName) wereErrors = true } sourceTag, err := store.TagByName(sourceTagName) if err != nil { return fmt.Errorf("could not retrieve tag '%v': %v", sourceTagName, err) } if sourceTag == nil { log.Warnf("no such tag '%v'.", sourceTagName) wereErrors = true continue } log.Infof(2, "finding files tagged '%v'.", sourceTagName) fileTags, err := store.FileTagsByTagId(sourceTag.Id) if err != nil { return fmt.Errorf("could not retrieve files for tag '%v': %v", sourceTagName, err) } log.Infof(2, "applying tag '%v' to these files.", destTagName) for _, fileTag := range fileTags { _, err = store.AddFileTag(fileTag.FileId, destTag.Id, fileTag.ValueId) if err != nil { return fmt.Errorf("could not apply tag '%v' to file #%v: %v", destTagName, fileTag.FileId, err) } } log.Infof(2, "deleting tag '%v'.", sourceTagName) err = store.DeleteTag(sourceTag.Id) if err != nil { return fmt.Errorf("could not delete tag '%v': %v", sourceTagName, err) } } if wereErrors { return blankError } return nil }
func listTagsForPaths(store *storage.Storage, paths []string, showCount, onePerLine bool) error { wereErrors := false for _, path := range paths { log.Infof(2, "%v: retrieving tags.", path) file, err := store.FileByPath(path) if err != nil { log.Warn(err.Error()) continue } var tagNames []string if file != nil { fileTags, err := store.FileTagsByFileId(file.Id) if err != nil { return err } tagNames, err = lookupTagNames(store, fileTags) if err != nil { return err } } else { _, err := os.Stat(path) if err != nil { switch { case os.IsPermission(err): log.Warnf("%v: permission denied", path) wereErrors = true continue case os.IsNotExist(err): log.Warnf("%v: no such file", path) wereErrors = true continue default: return fmt.Errorf("%v: could not stat file: %v", path, err) } } } if showCount { fmt.Println(path + ": " + strconv.Itoa(len(tagNames))) } else { if onePerLine { fmt.Println(path) for _, tagName := range tagNames { fmt.Println(tagName) } fmt.Println() } else { fmt.Println(path + ": " + strings.Join(tagNames, " ")) } } } if wereErrors { return blankError } return nil }
func findDuplicatesOf(paths []string, recursive bool) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() fingerprintAlgorithmSetting, err := store.Setting("fingerprintAlgorithm") if err != nil { return fmt.Errorf("could not retrieve fingerprint algorithm: %v", err) } wereErrors := false for _, path := range paths { _, err := os.Stat(path) if err != nil { switch { case os.IsNotExist(err): log.Warnf("%v: no such file", path) wereErrors = true continue case os.IsPermission(err): log.Warnf("%v: permission denied", path) wereErrors = true continue default: return err } } } if wereErrors { return blankError } if recursive { p, err := _path.Enumerate(paths) if err != nil { return fmt.Errorf("could not enumerate paths: %v", err) } paths = make([]string, len(p)) for index, path := range p { paths[index] = path.Path } } first := true for _, path := range paths { log.Infof(2, "%v: identifying duplicate files.", path) fp, err := fingerprint.Create(path, fingerprintAlgorithmSetting.Value) if err != nil { return fmt.Errorf("%v: could not create fingerprint: %v", path, err) } if fp == fingerprint.Fingerprint("") { continue } files, err := store.FilesByFingerprint(fp) if err != nil { return fmt.Errorf("%v: could not retrieve files matching fingerprint '%v': %v", path, fp, err) } absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not determine absolute path: %v", path, err) } // filter out the file we're searching on dupes := files.Where(func(file *entities.File) bool { return file.Path() != absPath }) if len(paths) > 1 && len(dupes) > 0 { if first { first = false } else { fmt.Println() } fmt.Printf("%v:\n", path) for _, dupe := range dupes { relPath := _path.Rel(dupe.Path()) fmt.Printf(" %v\n", relPath) } } else { for _, dupe := range dupes { relPath := _path.Rel(dupe.Path()) fmt.Println(relPath) } } } return nil }