|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"os/signal"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"text/tabwriter"
|
|
|
|
)
|
|
|
|
|
|
|
|
var globalDB *sql.DB
|
|
|
|
var globalOpts Options
|
|
|
|
|
|
|
|
// const orderby = "-frequency, -mark, CASE WHEN updated_at IS NULL THEN created_at ELSE updated_at END DESC"
|
|
|
|
const orderby = "CASE WHEN updated_at IS NULL THEN created_at ELSE updated_at END DESC"
|
|
|
|
const defaultEditor = "vi"
|
|
|
|
|
|
|
|
func cmdShow(db *sql.DB, opts Options) bool {
|
|
|
|
if len(opts.IDs) == 0 && len(opts.Aliases) == 0 {
|
|
|
|
opts.IDs = append(opts.IDs, int64(getLastAttrID(db)))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range opts.IDs {
|
|
|
|
attr := findAttributeByID(db, id)
|
|
|
|
//fmt.Printf(attr.GetValue())
|
|
|
|
printToLess(attr.GetValue())
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range opts.Aliases {
|
|
|
|
attr := findAttributeByAlias(db, alias, false)
|
|
|
|
//fmt.Printf(attr.GetValue())
|
|
|
|
printToLess(attr.GetValue())
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdCat(db *sql.DB, opts Options) bool {
|
|
|
|
if len(opts.IDs) == 0 && len(opts.Aliases) == 0 {
|
|
|
|
opts.IDs = append(opts.IDs, int64(getLastAttrID(db)))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range opts.IDs {
|
|
|
|
attr := findAttributeByID(db, id)
|
|
|
|
fmt.Printf(attr.GetValue())
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range opts.Aliases {
|
|
|
|
attr := findAttributeByAlias(db, alias, false)
|
|
|
|
fmt.Printf(attr.GetValue())
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdMount(db *sql.DB, opts Options) bool {
|
|
|
|
log.Println("Not implemented yet")
|
|
|
|
/*
|
|
|
|
globalDB = db
|
|
|
|
globalOpts = opts
|
|
|
|
globalOpts.Offset = 0
|
|
|
|
globalOpts.Limit = 40
|
|
|
|
Mount(opts.MountPoint)
|
|
|
|
*/
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdAddFiles(db *sql.DB, files []string) bool {
|
|
|
|
tx, err := db.Begin()
|
|
|
|
|
|
|
|
// stmt, err := tx.Prepare("INSERT INTO attributes (name, pwd, value_text, value_blob) VALUES (?, ?, ?, ?)")
|
|
|
|
stmt, err := tx.Prepare("INSERT INTO attributes (name, value_text, value_blob) VALUES (?, ?, ?)")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer stmt.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range files {
|
|
|
|
content, err := ioutil.ReadFile(file)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fileAbsPath, err := filepath.Abs(file)
|
|
|
|
// fileRelPath, err := filepath.Rel(pwd, fileAbsPath)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
//_, err = stmt.Exec("file", pwd, fileRelPath, content)
|
|
|
|
_, err = stmt.Exec("file", fileAbsPath, content)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.Commit()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdLs(db *sql.DB, w *tabwriter.Writer, opts Options) bool {
|
|
|
|
attrs := listWithFilters(db, opts)
|
|
|
|
for _, attr := range attrs {
|
|
|
|
if opts.ListFilepaths {
|
|
|
|
fmt.Println(attr.Filepath())
|
|
|
|
} else {
|
|
|
|
attr.Print(w, opts.Recursive, opts.Indent, opts.Filters, opts.AfterLinesCount)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdNew(db *sql.DB, opts Options) bool {
|
|
|
|
|
|
|
|
var value_text string
|
|
|
|
|
|
|
|
if opts.FromStdin {
|
|
|
|
lines := make([]string, 0, 0)
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
|
|
|
|
c := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(c, os.Interrupt)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for _ = range c {
|
|
|
|
// CTRL-c
|
|
|
|
// Do nothing, this will continue executing the rest of the code
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
for {
|
|
|
|
line, _, err := reader.ReadLine()
|
|
|
|
if err != nil {
|
|
|
|
// EOF
|
|
|
|
break
|
|
|
|
}
|
|
|
|
lines = append(lines, string(line))
|
|
|
|
if opts.Verbose {
|
|
|
|
log.Printf("%s\n", prettyAttr("eton", string(line)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value_text = strings.Join(lines, "\n")
|
|
|
|
} else if len(opts.Note) > 0 {
|
|
|
|
value_text = opts.Note
|
|
|
|
} else {
|
|
|
|
f, err := ioutil.TempFile("", "eton-edit")
|
|
|
|
check(err)
|
|
|
|
|
|
|
|
openEditor(f.Name())
|
|
|
|
value_text = readFile(f.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
lastInsertId := saveString(db, value_text)
|
|
|
|
if lastInsertId > 0 && opts.Verbose {
|
|
|
|
fmt.Printf("New note ID:%d\n", lastInsertId)
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdAdd(db *sql.DB, id int, attrs []string) bool {
|
|
|
|
// TODO
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdAddAttr(db *sql.DB, id int, attrs []string) bool {
|
|
|
|
var stmt *sql.Stmt
|
|
|
|
|
|
|
|
tx, err := db.Begin()
|
|
|
|
|
|
|
|
if id == -1 {
|
|
|
|
stmt, err = tx.Prepare("INSERT INTO attributes (name, value_text) VALUES (?, ?)")
|
|
|
|
} else {
|
|
|
|
stmt, err = tx.Prepare("INSERT INTO attributes (name, value_text, parent_id) VALUES (?, ?, ?)")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer stmt.Close()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, attr := range attrs {
|
|
|
|
name := ""
|
|
|
|
value := ""
|
|
|
|
|
|
|
|
nameValuePair := strings.SplitN(attr, ":", 2)
|
|
|
|
|
|
|
|
switch len(nameValuePair) {
|
|
|
|
case 1:
|
|
|
|
value = nameValuePair[0]
|
|
|
|
case 2:
|
|
|
|
name = nameValuePair[0]
|
|
|
|
value = nameValuePair[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
if id == -1 {
|
|
|
|
_, err = stmt.Exec(name, value)
|
|
|
|
} else {
|
|
|
|
_, err = stmt.Exec(name, value, id)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.Commit()
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdUnalias(db *sql.DB, opts Options) bool {
|
|
|
|
attr := findAttributeByAlias(db, opts.Alias, true)
|
|
|
|
if attr.GetID() == -1 {
|
|
|
|
log.Fatalf("alias \"%s\" not found", opts.Alias)
|
|
|
|
} else {
|
|
|
|
attr.SetAlias(db, "")
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdAlias(db *sql.DB, opts Options) bool {
|
|
|
|
if !(opts.ID > 0 && len(opts.Alias1) > 0 || len(opts.Alias2) > 0) && !(len(opts.Alias1) > 0 && len(opts.Alias2) > 0) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
var attr Attr
|
|
|
|
|
|
|
|
if opts.ID > 0 {
|
|
|
|
attr = findAttributeByID(db, opts.ID)
|
|
|
|
if len(opts.Alias1) > 0 {
|
|
|
|
attr.SetAlias(db, opts.Alias1)
|
|
|
|
} else if len(opts.Alias2) > 0 {
|
|
|
|
attr.SetAlias(db, opts.Alias2)
|
|
|
|
}
|
|
|
|
} else if len(opts.Alias1) > 0 && len(opts.Alias2) > 0 {
|
|
|
|
attr1 := findAttributeByAlias(db, opts.Alias1, true)
|
|
|
|
attr2 := findAttributeByAlias(db, opts.Alias2, true)
|
|
|
|
|
|
|
|
if attr1.GetID() > 0 && attr2.GetID() <= 0 {
|
|
|
|
attr1.SetAlias(db, opts.Alias2)
|
|
|
|
} else if attr1.GetID() <= 0 && attr2.GetID() > 0 {
|
|
|
|
attr2.SetAlias(db, opts.Alias1)
|
|
|
|
} else {
|
|
|
|
log.Println("not changing anything", attr1.GetID(), attr2.GetID())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdEdit(db *sql.DB, opts Options) bool {
|
|
|
|
var totalUpdated int64
|
|
|
|
|
|
|
|
if len(opts.IDs) == 0 && len(opts.Aliases) == 0 {
|
|
|
|
opts.IDs = append(opts.IDs, int64(getLastAttrID(db)))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range opts.IDs {
|
|
|
|
attr := findAttributeByID(db, id)
|
|
|
|
totalUpdated += attr.Edit(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range opts.Aliases {
|
|
|
|
attr := findAttributeByAlias(db, alias, false)
|
|
|
|
totalUpdated += attr.Edit(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.Verbose {
|
|
|
|
fmt.Println(totalUpdated, "records updated")
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdRm(db *sql.DB, opts Options) bool {
|
|
|
|
|
|
|
|
var totalUpdated int64
|
|
|
|
|
|
|
|
for _, id := range opts.IDs {
|
|
|
|
attr := findAttributeByID(db, id)
|
|
|
|
totalUpdated += attr.Rm(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range opts.Aliases {
|
|
|
|
attr := findAttributeByAlias(db, alias, true)
|
|
|
|
totalUpdated += attr.Rm(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
if totalUpdated > 0 {
|
|
|
|
fmt.Println(totalUpdated, "deleted")
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdUnrm(db *sql.DB, opts Options) bool {
|
|
|
|
var totalUpdated int64
|
|
|
|
|
|
|
|
for _, id := range opts.IDs {
|
|
|
|
attr := findAttributeByID(db, id)
|
|
|
|
totalUpdated += attr.Unrm(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range opts.Aliases {
|
|
|
|
attr := findAttributeByAlias(db, alias, true)
|
|
|
|
totalUpdated += attr.Unrm(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
if totalUpdated > 0 {
|
|
|
|
fmt.Println(totalUpdated, "recovered")
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdInit(db *sql.DB) bool {
|
|
|
|
InitializeDatabase(db)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdMark(db *sql.DB, opts Options) bool {
|
|
|
|
var totalUpdated int64
|
|
|
|
for _, id := range opts.IDs {
|
|
|
|
attr := findAttributeByID(db, id)
|
|
|
|
totalUpdated += attr.SetMark(db, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range opts.Aliases {
|
|
|
|
attr := findAttributeByAlias(db, alias, false)
|
|
|
|
totalUpdated += attr.SetMark(db, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(totalUpdated, "marked")
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func cmdUnmark(db *sql.DB, opts Options) bool {
|
|
|
|
var totalUpdated int64
|
|
|
|
for _, id := range opts.IDs {
|
|
|
|
attr := findAttributeByID(db, id)
|
|
|
|
totalUpdated += attr.SetMark(db, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, alias := range opts.Aliases {
|
|
|
|
attr := findAttributeByAlias(db, alias, false)
|
|
|
|
totalUpdated += attr.SetMark(db, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(totalUpdated, "marked")
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
func openEditor(filepath string) {
|
|
|
|
var cmd *exec.Cmd
|
|
|
|
|
|
|
|
editor := os.Getenv("EDITOR")
|
|
|
|
|
|
|
|
if len(editor) > 0 {
|
|
|
|
cmd = exec.Command(editor, filepath)
|
|
|
|
} else {
|
|
|
|
if _, err := os.Stat("/usr/bin/sensible-editor"); err == nil {
|
|
|
|
cmd = exec.Command("/usr/bin/sensible-editor", filepath)
|
|
|
|
} else {
|
|
|
|
cmd = exec.Command("/usr/bin/env", defaultEditor, filepath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Stdin = os.Stdin
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
func readFile(filepath string) string {
|
|
|
|
data, err := ioutil.ReadFile(filepath)
|
|
|
|
check(err)
|
|
|
|
return string(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func check(e error) {
|
|
|
|
if e != nil {
|
|
|
|
// log.Fatal(e)
|
|
|
|
panic(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printToLess(text string) {
|
|
|
|
// declare your pager
|
|
|
|
cmd := exec.Command("/usr/bin/env", "less")
|
|
|
|
// create a pipe (blocking)
|
|
|
|
r, stdin := io.Pipe()
|
|
|
|
// Set your i/o's
|
|
|
|
cmd.Stdin = r
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
|
|
// Create a blocking chan, Run the pager and unblock once it is finished
|
|
|
|
c := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(c)
|
|
|
|
cmd.Run()
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Pass anything to your pipe
|
|
|
|
fmt.Fprintf(stdin, text)
|
|
|
|
|
|
|
|
// Close stdin (result in pager to exit)
|
|
|
|
stdin.Close()
|
|
|
|
|
|
|
|
// Wait for the pager to be finished
|
|
|
|
<-c
|
|
|
|
}
|