commit d4f39b696e2dc75ca2e61c35f3b13f418b2ac2da Author: Vincent Batts Date: Tue Sep 22 03:26:55 2015 +0000 initial commit diff --git a/command.go b/command.go new file mode 100644 index 0000000..c0223c9 --- /dev/null +++ b/command.go @@ -0,0 +1,36 @@ +package main + +import ( + "strings" + "time" +) + +type Command struct { + rowid int64 + Args string + Times []int64 +} + +func (c Command) String() string { + return c.Args +} + +/* + * Transitional method to import from the ruby implementation + */ +func CommandFromLineImport(line string) (*Command, error) { + line_chunks := strings.SplitN(strings.TrimSpace(line), " ", 3) + t, err := time.Parse(ShellTime, strings.Join(line_chunks[0:2], " ")) + if err != nil { + return nil, err + } + + if len(line_chunks) < 3 { + return &Command{Time: t}, nil + } + + return &Command{ + Args: strings.TrimSpace(line_chunks[2], " "), + Time: t, + }, nil +} diff --git a/db.go b/db.go new file mode 100644 index 0000000..44cec2b --- /dev/null +++ b/db.go @@ -0,0 +1,181 @@ +package main + +import ( + "database/sql" + "fmt" + "os" + + _ "code.google.com/p/gosqlite/sqlite3" // registers sqlite +) + +type Database struct { + conn *sql.DB +} + +func (db *Database) Close() error { + return db.conn.Close() +} + +func (db Database) GetCommand(commandStr string) (*Command, error) { + rows, err := db.conn.Query("SELECT rowid, command FROM commands WHERE command = ?;", commandStr) + if err != nil { + return nil, err + } + var ( + rowid int64 + command string + ) + for rows.Next() { + err = rows.Scan(&rowid, &command) + if err != nil { + rows.Close() + return nil, err + } + } + rows.Close() + if rowid == 0 { + return nil, nil + } + cmd := Command{rowid: rowid, Args: command} + + if rows, err = db.conn.Query("SELECT time FROM history WHERE command_id = ?;", rowid); err != nil { + return nil, err + } + for rows.Next() { + var t int64 + if err = rows.Scan(&t); err != nil { + rows.Close() + return nil, err + } + cmd.Times = append(cmd.Times, t) + } + rows.Close() + + return &cmd, nil +} + +func (db *Database) InsertCommand(cmdTime int64, command string) error { + var ( + cmd *Command + err error + ) + + // will be + if cmd, err = db.GetCommand(command); err != nil { + return err + } + fmt.Printf("%#v\n", cmd) + + insertCommand, err := db.conn.Prepare("INSERT INTO commands (command) VALUES (?);") + if err != nil { + return err + } + insertTime, err := db.conn.Prepare("INSERT INTO history (command_id, time) VALUES (?,?);") + if err != nil { + return err + } + + tx, err := db.conn.Begin() + if err != nil { + return err + } + + var ( + commandId int64 + hasTime bool + ) + if cmd == nil { + res, err := tx.Stmt(insertCommand).Exec(command) + if err != nil { + tx.Rollback() + return err + } + commandId, err = res.LastInsertId() + if err != nil { + tx.Rollback() + return err + } + cmd = &Command{rowid: commandId, Args: command} + } else { + commandId = cmd.rowid + } + for i := range cmd.Times { + if cmd.Times[i] == cmdTime { + hasTime = true + } + } + if !hasTime { + if _, err = tx.Stmt(insertTime).Exec(commandId, cmdTime); err != nil { + tx.Rollback() + return err + } + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + +func NewDatabase(path string) (*Database, error) { + initDb := false + + stat, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + initDb = true + } else { + return nil, err + } + } + if stat != nil && stat.Size() == 0 { + initDb = true + } + + conn, err := sql.Open("sqlite3", path) + if err != nil { + return nil, err + } + db := &Database{conn: conn} + + if initDb { + if err := initializeDatabase(db); err != nil { + return nil, err + } + } + + return db, nil +} + +func initializeDatabase(db *Database) error { + if _, err := db.conn.Exec(createCommandsTable); err != nil { + return err + } + if _, err := db.conn.Exec(createHistoryTable); err != nil { + return err + } + if _, err := db.conn.Exec(createIndexes); err != nil { + return err + } + + return nil +} + +const ( + //id int NOT NULL PRIMARY KEY, + createCommandsTable = ` + CREATE TABLE IF NOT EXISTS commands ( + command text NOT NULL + ); ` + //id text NOT NULL PRIMARY KEY, + createHistoryTable = ` + CREATE TABLE IF NOT EXISTS history ( + "command_id" int NOT NULL, + "time" bigint NOT NULL, + CONSTRAINT "command_fk" FOREIGN KEY ("command_id") REFERENCES "commands" ("id") + ); ` + createIndexes = ` + CREATE UNIQUE INDEX IF NOT EXISTS "commands_ix" ON "commands" (command); + ` +) diff --git a/main.go b/main.go new file mode 100644 index 0000000..a33542b --- /dev/null +++ b/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "flag" + "fmt" + //"github.com/vbatts/go-gdbm" + "os" + "path" +) + +var ( + loadNew = true + flBashHistoryFile = flag.String("hist", path.Join(os.Getenv("HOME"), ".bash_history"), "path to the bash history file") + flDb = flag.String("db", path.Join(os.Getenv("HOME"), ".shell_history.db"), "path to database") + flList = flag.Bool("l", false, "list all commands stored") + flQuiet = flag.Bool("q", false, "less output") +) + +func main() { + flag.Parse() + + db, err := NewDatabase(*flDb) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer db.Close() + if *flList { + loadNew = false + } + + fmt.Println(db) + + if loadNew { + // TODO parse the bash history and load each + if err := db.InsertCommand(0, "ls -lh"); err != nil { + fmt.Fprintln(os.Stderr, err) + } + if err := db.InsertCommand(2, "ls -lh"); err != nil { + fmt.Fprintln(os.Stderr, err) + } + } +}