From fa68976a3e7adb8c2c3327c6afc44fef24297337 Mon Sep 17 00:00:00 2001 From: Andinus Date: Wed, 8 Apr 2020 04:31:21 +0530 Subject: Initial rewrite of grus --- README.org | 40 +++++++++++++++++---- go.sum | 2 ++ grus.go | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main_openbsd.go | 44 +++++++++++++++++++++++ main_other.go | 7 ++++ 5 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 grus.go create mode 100644 main_openbsd.go create mode 100644 main_other.go diff --git a/README.org b/README.org index 2aad5e9..859005d 100644 --- a/README.org +++ b/README.org @@ -10,12 +10,40 @@ Grus is a simple word unjumbler written in Go. | GitHub (Mirror) | [[https://github.com/andinus/grus][Grus - GitHub]] | *Tested on*: -- OpenBSD 6.6 (with /unveil/) +- OpenBSD 6.6 (with /pledge/ & /unveil/) -* Working -- Grus takes a word as input from the user -- Input is ordered in [[https://wikipedia.org/wiki/Lexicographical_order][lexical order]] -- Ordered input is searched in dictionary +* Documentation +Grus stops the search as soon as it unjumbles the word, so no anagrams are +returned & maybe all dictionaries were not searched. However, this behaviour can +be changed with two environment variables documented below. + +*Note*: If grus couldn't unjumble the word with first dictionary then it'll search +in next dictionary, search stops once the word gets unjumbled. + +| Environment variable | Explanation | Non-default values | +|----------------------+----------------------------+--------------------| +| =GRUS_SEARCH_ALL= | Search in all dictionaries | 1 / true | +| =GRUS_ANAGRAMS= | Print all anagrams | 1 / true | +** Examples +#+BEGIN_SRC sh +# unjumble word +grus word + +# print all anagrams +GRUS_ANAGRAMS=true grus word + +# search for word in all dictionaries +GRUS_SEARCH_ALL=true grus word + +# search for word in custom dictionaries too +grus word /path/to/dict1 /path/to/dict2 + +# search for word in all dictionaries +GRUS_SEARCH_ALL=1 grus word /path/to/dict1 /path/to/dict2 + +# search for word in all dictionaries & print all anagrams +GRUS_SEARCH_ALL=1 GRUS_ANAGRAMS=1 grus word +#+END_SRC * History Initial version of Grus was just a simple shell script that used the slowest method of unjumbling words, it checked every permutation of the word with all @@ -113,5 +141,5 @@ database. This was true till v0.1.0, v0.2.0 was rewritten & it dropped the use of database or any form of pre-parsing the dictionary. Instead it would look through each -line of dictionary & unjumble the word, while this is a lot slower than previous +line of dictionary & unjumble the word, while this may be slower than previous version but this is simpler. diff --git a/go.sum b/go.sum index dbd60ff..8d3b70d 100644 --- a/go.sum +++ b/go.sum @@ -3,3 +3,5 @@ golang.org/x/sys v0.0.0-20200406113430-c6e801f48ba2 h1:Z9pPywZscwuw0ijrLEbTzW9lp golang.org/x/sys v0.0.0-20200406113430-c6e801f48ba2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= tildegit.org/andinus/lynx v0.1.0 h1:7YjyF8h7MBGKRgQZT0j0I3uHRPf3mI2GMiDujXVlLS0= tildegit.org/andinus/lynx v0.1.0/go.mod h1:/PCNkKwfJ7pb6ziHa76a4gYp1R9S1Ro4ANjQwzSpBIk= +tildegit.org/andinus/lynx v0.2.0 h1:cBoAWqC/osZJE4VPdB0HhIEpMIC4A4eI9nEbHR/9Qvk= +tildegit.org/andinus/lynx v0.2.0/go.mod h1:/PCNkKwfJ7pb6ziHa76a4gYp1R9S1Ro4ANjQwzSpBIk= diff --git a/grus.go b/grus.go new file mode 100644 index 0000000..d4a63ad --- /dev/null +++ b/grus.go @@ -0,0 +1,106 @@ +package main + +import ( + "bufio" + "fmt" + "os" + + "tildegit.org/andinus/grus/lexical" +) + +func grus() { + if len(os.Args) == 1 { + fmt.Println("Usage: grus ") + os.Exit(1) + } + + version := "v0.2.0" + + if os.Args[1] == "version" { + fmt.Printf("Grus %s\n", version) + os.Exit(0) + } + + dicts := []string{ + "/usr/local/share/dict/words", + "/usr/share/dict/words", + "/usr/share/dict/special/4bsd", + "/usr/share/dict/special/math", + } + + // User has specified dictionaries, prepend them to dicts + // list. + if len(os.Args) >= 3 { + dicts = append(os.Args[2:], dicts...) + } + + // Check if user has asked to search in all dictionaries. + searchAll := false + searchAllEnv := os.Getenv("GRUS_SEARCH_ALL") + if searchAllEnv == "true" || + searchAllEnv == "1" { + searchAll = true + } + + // Check if user wants anagrams. + anagrams := false + anagramsEnv := os.Getenv("GRUS_ANAGRAMS") + if anagramsEnv == "true" || + anagramsEnv == "1" { + anagrams = true + } + + for _, dict := range dicts { + if _, err := os.Stat(dict); err != nil && + !os.IsNotExist(err) { + // Error is not nil & also it's not path + // doesn't exist error. We do it this way to + // avoid another level of indentation. + panic(err) + } else if err != nil && + os.IsNotExist(err) { + // If file doesn't exist then continue with + // next dictionary. + continue + } + + file, err := os.Open(dict) + panicOnErr(err) + defer file.Close() + + // We use this to record if the word was unjumbled. + unjumbled := false + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + // Filter words by comparing length first & + // run lexical.Sort on it only if the length + // is equal. + if len(scanner.Text()) == len(os.Args[1]) && + lexical.Sort(scanner.Text()) == lexical.Sort(os.Args[1]) { + fmt.Println(scanner.Text()) + // If the user doesn't want anagrams + // then exit the program. + if !anagrams { + os.Exit(0) + } + unjumbled = true + } + } + panicOnErr(scanner.Err()) + + // If word was unjumbled & user hasn't asked to search + // in all dictionaries then exit the program otherwise + // keep searching in other dictionaries. + if unjumbled && + !searchAll { + os.Exit(0) + } + } +} + +func panicOnErr(err error) { + if err != nil { + panic(err) + } +} diff --git a/main_openbsd.go b/main_openbsd.go new file mode 100644 index 0000000..7d466ee --- /dev/null +++ b/main_openbsd.go @@ -0,0 +1,44 @@ +// +build openbsd + +package main + +import ( + "os" + + "golang.org/x/sys/unix" + "tildegit.org/andinus/lynx" +) + +func main() { + err := unix.PledgePromises("unveil stdio rpath") + panicOnErr(err) + + unveil() + + // Drop unveil from promises. + err = unix.PledgePromises("stdio rpath") + panicOnErr(err) + + grus() +} + +func unveil() { + paths := make(map[string]string) + + paths["/usr/share/dict"] = "r" + paths["/usr/local/share/dict"] = "r" + + // Unveil user defined dictionaries. + if len(os.Args) >= 3 { + for _, dict := range os.Args[2:] { + paths[dict] = "r" + } + } + // This will not return error if the file doesn't exist. + err := lynx.UnveilPaths(paths) + panicOnErr(err) + + // Block further unveil calls. + err = lynx.UnveilBlock() + panicOnErr(err) +} diff --git a/main_other.go b/main_other.go new file mode 100644 index 0000000..88824ad --- /dev/null +++ b/main_other.go @@ -0,0 +1,7 @@ +// +build !openbsd + +package main + +func main() { + grus() +} -- cgit 1.4.1-2-gfad0