1
0
spogulis no https://github.com/quitesimpleorg/hs9001.git synced 2025-07-02 06:13:49 +02:00

14 Revīzijas

Autors SHA1 Ziņojums Datums
c206b07b2d add/search: Process cmd exit code 2021-05-16 18:01:48 +02:00
3b01d7b898 search: Do not print consecutive equal commands by default 2021-05-16 17:13:29 +02:00
baa0d58a47 Fix a bug in DB migration 2021-04-05 14:37:37 +02:00
163429138c search(): Allow searching by timestamps 2021-04-05 14:22:05 +02:00
2af0c1d551 add(): Log current timestamp too 2021-04-05 14:21:54 +02:00
595595c4cb search: Allow searching for cwd 2021-04-05 13:15:20 +02:00
250af52750 add(): Pass HistoryEntry struct 2021-04-05 13:00:30 +02:00
88362e99a9 Introduce HistoryEntry struct ; refactor search and delete 2021-04-05 12:49:13 +02:00
776dcebb04 Log cwd 2021-04-05 11:54:09 +02:00
1809905992 Add DB migrations 2021-04-05 11:44:43 +02:00
305e4300cc Fix off-by-one in CLI 2021-04-05 10:43:54 +02:00
b02911c9b4 Add 'delete' subcommand to delete matching records from log 2021-04-05 10:29:50 +02:00
8477ba5bfe CLI: Properly parse cli flags for subcommands
Previously we parsed cli flags globally instead of per subcommand, which was weird.
2021-04-05 10:18:35 +02:00
54697be895 Search: Properly exit on error 2021-04-05 09:53:25 +02:00
4 mainīti faili ar 263 papildinājumiem un 56 dzēšanām

Parādīt failu

@ -31,15 +31,11 @@ apk add hs9001
### Setup / Config ### Setup / Config
```
hs9001 init
```
Add this to .bashrc Add this to .bashrc
``` ```
if [ -n "$PS1" ] ; then if [ -n "$PS1" ] ; then
PROMPT_COMMAND='hs9001 -ret $? add "$(history 1)"' PROMPT_COMMAND='hs9001 add -ret $? "$(history 1)"'
fi fi
``` ```
By default, every system user gets his own database. You can override this by setting the environment variable By default, every system user gets his own database. You can override this by setting the environment variable

5
go.mod
Parādīt failu

@ -2,4 +2,7 @@ module hs9001
go 1.16 go 1.16
require modernc.org/sqlite v1.10.0 // indirect require (
github.com/tj/go-naturaldate v1.3.0
modernc.org/sqlite v1.10.0
)

21
go.sum
Parādīt failu

@ -1,12 +1,26 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tj/assert v0.0.0-20190920132354-ee03d75cd160 h1:NSWpaDaurcAJY7PkL8Xt0PhZE7qpvbZl5ljd8r6U0bI=
github.com/tj/assert v0.0.0-20190920132354-ee03d75cd160/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
github.com/tj/go-naturaldate v1.3.0 h1:OgJIPkR/Jk4bFMBLbxZ8w+QUxwjqSvzd9x+yXocY4RI=
github.com/tj/go-naturaldate v1.3.0/go.mod h1:rpUbjivDKiS1BlfMGc2qUKNZ/yxgthOfmytQs8d8hKk=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -36,10 +50,15 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0= modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA= modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA=
modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY= modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.8.0 h1:Pp4uv9g0csgBMpGPABKtkieF6O5MGhfGo6ZiOdlYfR8= modernc.org/libc v1.8.0 h1:Pp4uv9g0csgBMpGPABKtkieF6O5MGhfGo6ZiOdlYfR8=
@ -55,8 +74,10 @@ modernc.org/sqlite v1.10.0 h1:0QNqx4EzfZzNEG13sFbS/L+egh0X5WXSckHrxHkySX8=
modernc.org/sqlite v1.10.0/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU= modernc.org/sqlite v1.10.0/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU=
modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc=
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/tcl v1.5.0 h1:euZSUNfE0Fd4W8VqXI1Ly1v7fqDJoBuAV88Ea+SnaSs=
modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc= modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc=
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=

287
main.go
Parādīt failu

@ -2,6 +2,7 @@ package main
import ( import (
"bufio" "bufio"
"container/list"
"database/sql" "database/sql"
"flag" "flag"
"fmt" "fmt"
@ -10,10 +11,22 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"time"
"github.com/tj/go-naturaldate"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
) )
type HistoryEntry struct {
id uint32
cmd string
cwd string
hostname string
user string
retval int
timestamp time.Time
}
func databaseLocation() string { func databaseLocation() string {
envOverride := os.Getenv("HS9001_DB_PATH") envOverride := os.Getenv("HS9001_DB_PATH")
if envOverride != "" { if envOverride != "" {
@ -28,6 +41,7 @@ func createConnection() *sql.DB {
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
return db return db
} }
@ -41,6 +55,75 @@ func initDatabase(conn *sql.DB) {
} }
} }
func migrateDatabase(conn *sql.DB, currentVersion int) {
migrations := []string{
"ALTER TABLE history ADD COLUMN workdir varchar(4096) DEFAULT ''",
"ALTER TABLE history ADD COLUMN retval integer DEFAULT -9001",
}
if !(len(migrations) > currentVersion) {
return
}
_, err := conn.Exec("BEGIN;")
if err != nil {
log.Panic(err)
}
for _, m := range migrations[currentVersion:] {
_, err := conn.Exec(m)
if err != nil {
log.Panic(err)
}
}
setDBVersion(conn, len(migrations))
_, err = conn.Exec("END;")
if err != nil {
log.Panic(err)
}
}
func fetchDBVersion(conn *sql.DB) int {
rows, err := conn.Query("PRAGMA user_version;")
if err != nil {
log.Panic(err)
}
defer rows.Close()
rows.Next()
var res int
rows.Scan(&res)
return res
}
func setDBVersion(conn *sql.DB, ver int) {
_, err := conn.Exec(fmt.Sprintf("PRAGMA user_version=%d", ver))
if err != nil {
log.Panic(err)
}
}
func NewHistoryEntry(cmd string, retval int) HistoryEntry {
wd, err := os.Getwd()
if err != nil {
log.Panic(err)
}
hostname, err := os.Hostname()
if err != nil {
log.Panic(err)
}
return HistoryEntry{
user: os.Getenv("USER"),
hostname: hostname,
cmd: cmd,
cwd: wd,
timestamp: time.Now(),
retval: retval,
}
}
func importFromStdin(conn *sql.DB) { func importFromStdin(conn *sql.DB) {
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)
@ -50,7 +133,10 @@ func importFromStdin(conn *sql.DB) {
} }
for scanner.Scan() { for scanner.Scan() {
add(conn, scanner.Text()) entry := NewHistoryEntry(scanner.Text(), -9001)
entry.cwd = ""
entry.timestamp = time.Unix(0, 0)
add(conn, entry)
} }
_, err = conn.Exec("END;") _, err = conn.Exec("END;")
@ -59,37 +145,62 @@ func importFromStdin(conn *sql.DB) {
} }
} }
func search(conn *sql.DB, q string) { func search(conn *sql.DB, q string, workdir string, beginTime time.Time, endTime time.Time, retval int) list.List {
queryStmt := "SELECT command FROM history WHERE command LIKE ? ORDER BY timestamp ASC" var sb strings.Builder
sb.WriteString("SELECT id, command, workdir, user, hostname, retval ")
sb.WriteString("FROM history ")
sb.WriteString("WHERE timestamp BETWEEN datetime(?, 'unixepoch') ")
sb.WriteString("AND datetime(?, 'unixepoch') ")
sb.WriteString("AND command LIKE ? ")
sb.WriteString("AND workdir LIKE ? ")
if retval != -9001 {
sb.WriteString("AND retval = ? ")
}
sb.WriteString("ORDER BY timestamp ASC ")
rows, err := conn.Query(queryStmt, "%"+q+"%") queryStmt := sb.String()
args := make([]interface{}, 0)
args = append(args, beginTime.Unix())
args = append(args, endTime.Unix())
args = append(args, q)
args = append(args, workdir)
if retval != -9001 {
args = append(args, retval)
}
rows, err := conn.Query(queryStmt, args...)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
var result list.List
defer rows.Close()
for rows.Next() { for rows.Next() {
var command string var entry HistoryEntry
err = rows.Scan(&command) err = rows.Scan(&entry.id, &entry.cmd, &entry.cwd, &entry.user, &entry.hostname, &entry.retval)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
fmt.Printf("%s\n", command) result.PushBack(&entry)
}
return result
}
func delete(conn *sql.DB, entryId uint32) {
queryStmt := "DELETE FROM history WHERE id = ?"
_, err := conn.Exec(queryStmt, entryId)
if err != nil {
log.Panic(err)
} }
} }
func add(conn *sql.DB, cmd string) { func add(conn *sql.DB, entry HistoryEntry) {
user := os.Getenv("USER") stmt, err := conn.Prepare("INSERT INTO history (user, command, hostname, workdir, timestamp, retval) VALUES (?, ?, ?, ?, datetime(?, 'unixepoch'),?)")
hostname, err := os.Hostname()
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
stmt, err := conn.Prepare("INSERT INTO history (user, command, hostname) VALUES (?, ?, ?)") _, err = stmt.Exec(entry.user, entry.cmd, entry.hostname, entry.cwd, entry.timestamp.Unix(), entry.retval)
if err != nil {
log.Panic(err)
}
_, err = stmt.Exec(user, cmd, hostname)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
@ -120,57 +231,133 @@ func exists(path string) (bool, error) {
} }
func printUsage() { func printUsage() {
fmt.Fprintf(os.Stderr, "Usage: ./hs9001 <add/search/init/import>\n") fmt.Fprintf(os.Stderr, "Usage: ./hs9001 <add/search/import>\n")
} }
func main() { func main() {
var ret int addCmd := flag.NewFlagSet("add", flag.ExitOnError)
flag.IntVar(&ret, "ret", 0, "Return value of the command to add") searchCmd := flag.NewFlagSet("search", flag.ExitOnError)
flag.Parse()
args := flag.Args()
argslen := len(args)
if argslen < 1 { if len(os.Args) < 2 {
printUsage() printUsage()
return return
} }
cmd := args[0] cmd := os.Args[1]
globalargs := os.Args[2:]
conn := createConnection() var conn *sql.DB
ok, _ := exists(databaseLocation())
if cmd == "add" { if !ok {
if ret == 23 { // 23 is our secret do not log status code
return
}
if argslen < 2 {
fmt.Fprint(os.Stderr, "Error: You need to provide the command to be added")
}
historycmd := args[1]
var rgx = regexp.MustCompile("\\s+\\d+\\s+(.*)")
rs := rgx.FindStringSubmatch(historycmd)
if len(rs) == 2 {
add(conn, rs[1])
}
} else if cmd == "search" {
if argslen < 2 {
fmt.Fprint(os.Stderr, "Please provide the search query\n")
}
q := strings.Join(args[1:], " ")
search(conn, q)
os.Exit(23)
} else if cmd == "init" {
err := os.MkdirAll(filepath.Dir(databaseLocation()), 0755) err := os.MkdirAll(filepath.Dir(databaseLocation()), 0755)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
conn = createConnection()
initDatabase(conn) initDatabase(conn)
} else if cmd == "import" {
importFromStdin(conn)
} else { } else {
fmt.Fprint(os.Stderr, "Error: Unknown command supplied\n\n") conn = createConnection()
}
migrateDatabase(conn, fetchDBVersion(conn))
switch cmd {
case "add":
var ret int
addCmd.IntVar(&ret, "ret", 0, "Return value of the command to add")
addCmd.Parse(globalargs)
args := addCmd.Args()
if ret == 23 { // 23 is our secret do not log status code
return
}
if len(args) < 1 {
fmt.Fprint(os.Stderr, "Error: You need to provide the command to be added")
}
historycmd := args[0]
var rgx = regexp.MustCompile("\\s+\\d+\\s+(.*)")
rs := rgx.FindStringSubmatch(historycmd)
if len(rs) == 2 {
add(conn, NewHistoryEntry(rs[1], ret))
}
case "search":
fallthrough
case "delete":
var workDir string
var beginTime string
var endTime string
var distinct bool = true
var retVal int
searchCmd.StringVar(&workDir, "workdir", "%", "Search only within this workdir")
searchCmd.StringVar(&beginTime, "begin", "50 years ago", "Start searching from this timeframe")
searchCmd.StringVar(&endTime, "end", "now", "End searching from this timeframe")
searchCmd.BoolVar(&distinct, "distinct", true, "Remove consecutive duplicate commands from output")
searchCmd.IntVar(&retVal, "ret", -9001, "Only query commands that returned with this exit code. -9001=all (default)")
searchCmd.Parse(globalargs)
args := searchCmd.Args()
beginTimestamp, err := naturaldate.Parse(beginTime, time.Now())
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to convert time string: %s\n", err.Error())
}
endTimeStamp, err := naturaldate.Parse(endTime, time.Now())
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to convert time string: %s\n", err.Error())
}
q := strings.Join(args, " ")
results := search(conn, "%"+q+"%", workDir, beginTimestamp, endTimeStamp, retVal)
previousCmd := ""
for e := results.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*HistoryEntry)
if !ok {
log.Panic("Failed to retrieve entries")
}
if !distinct || previousCmd != entry.cmd {
fmt.Printf("%s\n", entry.cmd)
}
previousCmd = entry.cmd
}
if cmd == "delete" {
_, err := conn.Exec("BEGIN;")
if err != nil {
log.Panic(err)
}
for e := results.Front(); e != nil; e = e.Next() {
entry, ok := e.Value.(*HistoryEntry)
if !ok {
log.Panic("Failed to retrieve entries")
}
delete(conn, entry.id)
}
_, err = conn.Exec("END;")
if err != nil {
log.Panic(err)
}
_, err = conn.Exec("VACUUM")
if err != nil {
log.Panic(err)
}
}
os.Exit(23)
case "import":
importFromStdin(conn)
default:
fmt.Fprintf(os.Stderr, "Error: Unknown subcommand '%s' supplied\n\n", cmd)
printUsage() printUsage()
return return
} }
} }