Initial rewrite of grus
This commit is contained in:
parent
a3ede2f5ed
commit
fa68976a3e
40
README.org
40
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.
|
||||
|
|
2
go.sum
2
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=
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// +build !openbsd
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
grus()
|
||||
}
|
Loading…
Reference in New Issue