package main import ( "bufio" "fmt" "os" "tildegit.org/andinus/grus/lexical" "tildegit.org/andinus/lynx" ) func main() { initGrus() if len(os.Args) == 1 { fmt.Println("Usage: grus ") os.Exit(1) } version := "v0.3.1" // Print version if first argument is version. if os.Args[1] == "version" { fmt.Printf("Grus %s\n", version) os.Exit(0) } // Define default environment variables. envVar := make(map[string]bool) envVar["GRUS_SEARCH_ALL"] = false envVar["GRUS_ANAGRAMS"] = true envVar["GRUS_STRICT_UNJUMBLE"] = false envVar["GRUS_PRINT_PATH"] = false // Check environment variables. for k, _ := range envVar { env := os.Getenv(k) if env == "false" || env == "0" { envVar[k] = false } else if env == "true" || env == "1" { envVar[k] = true } } // Print environment variables if first argument is env. if os.Args[1] == "env" { for k, v := range envVar { fmt.Printf("%s: %t\n", k, v) } os.Exit(0) } // Define default dictionaries. dicts := []string{ "/usr/local/share/dict/words", "/usr/local/share/dict/web2", "/usr/share/dict/words", "/usr/share/dict/web2", "/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...) } // We use this to record if the word was unjumbled. unjumbled := false for k, 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 } // Print path to dictionary if printPath is true. if envVar["GRUS_PRINT_PATH"] { if k != 0 { fmt.Println() } fmt.Println(dict) } file, err := os.Open(dict) panicOnErr(err) 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 !envVar["GRUS_ANAGRAMS"] { os.Exit(0) } unjumbled = true } } panicOnErr(scanner.Err()) file.Close() // If the user has asked to strictly unjumble then we // cannot exit till it's unjumbled. if envVar["GRUS_STRICT_UNJUMBLE"] && !unjumbled { // If user has asked to strictly unjumble & we // haven't done that yet & this is the last // dictionary then we've failed to unjumble it // & the program must exit with a non-zero // exit code. if k == len(dicts)-1 { os.Exit(1) } // Cannot exit, must search next dictionary. continue } // If user hasn't asked to search all dictionaries // then exit the program. if !envVar["GRUS_SEARCH_ALL"] { os.Exit(0) } } } func initGrus() { // We need less permissions on these conditions. if len(os.Args) == 1 || os.Args[1] == "version" || os.Args[1] == "env" { err := lynx.PledgePromises("stdio") panicOnErr(err) } else { err := lynx.PledgePromises("unveil stdio rpath") panicOnErr(err) unveil() // Drop unveil from promises. err = lynx.PledgePromises("stdio rpath") panicOnErr(err) } } 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) } func panicOnErr(err error) { if err != nil { panic(err) } }