1
0
Fork 0

Initial rewrite of grus

This commit is contained in:
Andinus 2020-04-08 04:31:21 +05:30
parent a3ede2f5ed
commit fa68976a3e
Signed by: andinus
GPG Key ID: B67D55D482A799FD
5 changed files with 193 additions and 6 deletions

View File

@ -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.

2
go.sum
View File

@ -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=

106
grus.go Normal file
View File

@ -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 <word> <dictionaries>")
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)
}
}

44
main_openbsd.go Normal file
View File

@ -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)
}

7
main_other.go Normal file
View File

@ -0,0 +1,7 @@
// +build !openbsd
package main
func main() {
grus()
}