|
|
|
@ -35,8 +35,8 @@ func _main() error {
|
|
|
|
if !ok {
|
|
|
|
if !ok {
|
|
|
|
defaultFilepath = "-"
|
|
|
|
defaultFilepath = "-"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
filepath := flag.String("f", defaultFilepath, "($PTTODO_FILE) path to yaml file")
|
|
|
|
filepath := flag.String("f", defaultFilepath, "($PTTODO_FILE) path to yaml file or dir (starting with root then alphabetical for dir)")
|
|
|
|
filepathToMergeIn := flag.String("g", "", "path to yaml file to merge into -f")
|
|
|
|
filepathToMergeIn := flag.String("g", "", "path to yaml file to merge into -f (modifies f)")
|
|
|
|
root := flag.String("root", DUMP_TODO, "path to pretty print ("+fmt.Sprint([]string{DUMP_ALL, DUMP_TODO, DUMP_SCHEDULED, DUMP_DONE})+")")
|
|
|
|
root := flag.String("root", DUMP_TODO, "path to pretty print ("+fmt.Sprint([]string{DUMP_ALL, DUMP_TODO, DUMP_SCHEDULED, DUMP_DONE})+")")
|
|
|
|
tags := flag.String("tags", "", "csv of all tags to find, -tag to invert")
|
|
|
|
tags := flag.String("tags", "", "csv of all tags to find, -tag to invert")
|
|
|
|
search := flag.String("search", "", "fts case insensitive")
|
|
|
|
search := flag.String("search", "", "fts case insensitive")
|
|
|
|
@ -48,8 +48,17 @@ func _main() error {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
filepaths := []string{*filepath}
|
|
|
|
|
|
|
|
if stat, err := os.Stat(*filepath); err != nil && !os.IsNotExist(err) {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
} else if err == nil && stat.IsDir() {
|
|
|
|
|
|
|
|
filepaths, err = listDir(*filepath)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if *e {
|
|
|
|
if *e {
|
|
|
|
if err := edit(*dry, *filepath); err != nil {
|
|
|
|
if err := edit(*dry, filepaths); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -57,7 +66,7 @@ func _main() error {
|
|
|
|
if *tags != "" {
|
|
|
|
if *tags != "" {
|
|
|
|
tagslist = strings.Split(*tags, ",")
|
|
|
|
tagslist = strings.Split(*tags, ",")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dump(*dry, os.Stdout, *filepath, tagslist, *search, *root)
|
|
|
|
return dump(*dry, os.Stdout, filepaths, tagslist, *search, *root)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func verifyRoot(root pttodo.Root) error {
|
|
|
|
func verifyRoot(root pttodo.Root) error {
|
|
|
|
@ -79,16 +88,19 @@ func verifyRoot(root pttodo.Root) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func verifyFile(path string) error {
|
|
|
|
func verifyFile(path string) error {
|
|
|
|
if err := dump(true, io.Discard, path, nil, "", DUMP_ALL); err != nil {
|
|
|
|
if err := dump(true, io.Discard, []string{path}, nil, "", DUMP_ALL); err != nil {
|
|
|
|
return fmt.Errorf("failed verifying file %s: %w", path, err)
|
|
|
|
return fmt.Errorf("failed verifying file %s: %w", path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func edit(dry bool, filepath string) error {
|
|
|
|
func edit(dry bool, filepaths []string) error {
|
|
|
|
var tempFile string
|
|
|
|
tempDir, err := ioutil.TempDir(os.TempDir(), "edit-pttodo")
|
|
|
|
cp := func() error {
|
|
|
|
if err != nil {
|
|
|
|
f, err := ioutil.TempFile(os.TempDir(), path.Base(filepath))
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cpOne := func(filepath string) error {
|
|
|
|
|
|
|
|
f, err := os.Create(path.Join(tempDir, path.Base(filepath)))
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -103,14 +115,18 @@ func edit(dry bool, filepath string) error {
|
|
|
|
g.Close()
|
|
|
|
g.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
f.Close()
|
|
|
|
tempFile = f.Name() + ".yaml"
|
|
|
|
return nil
|
|
|
|
return os.Rename(f.Name(), tempFile)
|
|
|
|
}
|
|
|
|
|
|
|
|
cp := func() error {
|
|
|
|
|
|
|
|
for _, filepath := range filepaths {
|
|
|
|
|
|
|
|
if err := cpOne(filepath); err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vi := func() error {
|
|
|
|
vi := func() error {
|
|
|
|
bin := "vim"
|
|
|
|
bin := "vim"
|
|
|
|
if editor := os.Getenv("EDITOR"); editor != "" {
|
|
|
|
|
|
|
|
bin = editor
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
editorbin, err := exec.LookPath(bin)
|
|
|
|
editorbin, err := exec.LookPath(bin)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
editorbin, err = exec.LookPath("vi")
|
|
|
|
editorbin, err = exec.LookPath("vi")
|
|
|
|
@ -118,12 +134,17 @@ func edit(dry bool, filepath string) error {
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
args := []string{editorbin, tempFile}
|
|
|
|
args := []string{editorbin, "-p"}
|
|
|
|
|
|
|
|
tempfiles, err := listDir(tempDir)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
args = append(args, tempfiles...)
|
|
|
|
cpid, err := syscall.ForkExec(
|
|
|
|
cpid, err := syscall.ForkExec(
|
|
|
|
editorbin,
|
|
|
|
editorbin,
|
|
|
|
args,
|
|
|
|
args,
|
|
|
|
&syscall.ProcAttr{
|
|
|
|
&syscall.ProcAttr{
|
|
|
|
Dir: "",
|
|
|
|
Dir: tempDir,
|
|
|
|
Env: os.Environ(),
|
|
|
|
Env: os.Environ(),
|
|
|
|
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
|
|
|
|
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
|
|
|
|
Sys: nil,
|
|
|
|
Sys: nil,
|
|
|
|
@ -145,7 +166,7 @@ func edit(dry bool, filepath string) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
verify := func() error {
|
|
|
|
verifyOne := func(tempFile string) error {
|
|
|
|
for {
|
|
|
|
for {
|
|
|
|
err := verifyFile(tempFile)
|
|
|
|
err := verifyFile(tempFile)
|
|
|
|
if err == nil {
|
|
|
|
if err == nil {
|
|
|
|
@ -162,13 +183,31 @@ func edit(dry bool, filepath string) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return verifyFile(tempFile)
|
|
|
|
return verifyFile(tempFile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
save := func() error {
|
|
|
|
verify := func() error {
|
|
|
|
|
|
|
|
for _, filepath := range filepaths {
|
|
|
|
|
|
|
|
tempFile := path.Join(tempDir, path.Base(filepath))
|
|
|
|
|
|
|
|
if err := verifyOne(tempFile); err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
saveOne := func(filepath string) error {
|
|
|
|
|
|
|
|
tempFile := path.Join(tempDir, path.Base(filepath))
|
|
|
|
if dry {
|
|
|
|
if dry {
|
|
|
|
log.Printf("would've saved %s as %s", tempFile, filepath)
|
|
|
|
log.Printf("would've saved %s as %s", tempFile, filepath)
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return os.Rename(tempFile, filepath)
|
|
|
|
return os.Rename(tempFile, filepath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
save := func() error {
|
|
|
|
|
|
|
|
for _, filepath := range filepaths {
|
|
|
|
|
|
|
|
if err := saveOne(filepath); err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, foo := range []func() error{cp, vi, verify, save} {
|
|
|
|
for _, foo := range []func() error{cp, vi, verify, save} {
|
|
|
|
if err := foo(); err != nil {
|
|
|
|
if err := foo(); err != nil {
|
|
|
|
@ -176,7 +215,7 @@ func edit(dry bool, filepath string) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !dry {
|
|
|
|
if !dry {
|
|
|
|
os.Remove(tempFile)
|
|
|
|
os.RemoveAll(tempDir)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
@ -241,21 +280,38 @@ func marshalRootToTempFile(root pttodo.Root) (string, error) {
|
|
|
|
return filepath, err
|
|
|
|
return filepath, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func dump(dry bool, writer io.Writer, filepath string, tags []string, search, rootDisplay string) error {
|
|
|
|
func dump(dry bool, writer io.Writer, filepaths []string, tags []string, search, rootDisplay string) error {
|
|
|
|
reader, err := filePathReader(filepath)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b, err := ioutil.ReadAll(reader)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var root pttodo.Root
|
|
|
|
var root pttodo.Root
|
|
|
|
if err := yaml.Unmarshal(b, &root); err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
for _, filepath := range filepaths {
|
|
|
|
|
|
|
|
reader, err := filePathReader(filepath)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b, err := ioutil.ReadAll(reader)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var root2 pttodo.Root
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal(b, &root2); err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
root2.MoveScheduledToTodo()
|
|
|
|
|
|
|
|
root.MergeIn(root2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !dry {
|
|
|
|
|
|
|
|
b3, err := yaml.Marshal(root2)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.WriteFile(filepath, b3, os.ModePerm); err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
root.MoveScheduledToTodo()
|
|
|
|
root.MoveScheduledToTodo()
|
|
|
|
|
|
|
|
|
|
|
|
var v interface{} = root
|
|
|
|
var v interface{} = root
|
|
|
|
@ -296,21 +352,13 @@ func dump(dry bool, writer io.Writer, filepath string, tags []string, search, ro
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v = todos
|
|
|
|
v = todos
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
b2, err := yaml.Marshal(v)
|
|
|
|
b2, err := yaml.Marshal(v)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintf(writer, "%s\n", b2)
|
|
|
|
fmt.Fprintf(writer, "%s\n", b2)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
if dry {
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b3, err := yaml.Marshal(root)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.WriteFile(filepath, b3, os.ModePerm)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func filePathReader(path string) (io.Reader, error) {
|
|
|
|
func filePathReader(path string) (io.Reader, error) {
|
|
|
|
@ -326,3 +374,18 @@ func filePathReader(path string) (io.Reader, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return reader, nil
|
|
|
|
return reader, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func listDir(dname string) ([]string, error) {
|
|
|
|
|
|
|
|
entries, err := os.ReadDir(dname)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
paths := make([]string, 0, len(entries))
|
|
|
|
|
|
|
|
for i := range entries {
|
|
|
|
|
|
|
|
if entries[i].IsDir() {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
paths = append(paths, path.Join(dname, entries[i].Name()))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return paths, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|