diff options
-rw-r--r-- | README.markdown | 24 | ||||
-rw-r--r-- | conflang.go | 160 | ||||
-rw-r--r-- | links.go | 26 | ||||
-rw-r--r-- | main.go | 19 | ||||
-rw-r--r-- | yr_no.go | 24 |
5 files changed, 208 insertions, 45 deletions
diff --git a/README.markdown b/README.markdown index 346d386..ccf2d1c 100644 --- a/README.markdown +++ b/README.markdown @@ -9,22 +9,26 @@ A simple start page with a background image from [/r/EarthPorn](http://www.reddi ## Configuration -startpage uses two files in your home directory for configuration: +The startpage configuration is located in the file ~/.startpagerc. It is a list of commands. A command has a name and can optionally have parameters separated by spaces or tabs. A backspace `\\` will interpret the next charcter literally (can be used to escape whitespace, linebreaks and backspaces). Commands are separated by newlines. -### ~/.startpage-urls +These commands are implemented: -This describes the hyperlinks that are displayed on the startpage. A list of key-value-pairs. Each line is such a pair. Key and value are separated with `->`. The key is the title of the link, the value the URL. +### `set-weather-place` -Example: +Takes one argument, the place used for weather info. startpage uses [yr.no](http://www.yr.no) to get weather data. Use the search box on that page to search for your place. You will then be redirected to an URL like this: `http://www.yr.no/place/<myplace>`. Put the `<myplace>` part after the `set-weather-place` command like this: + + set-weather-place <myplace> - github -> http://www.github.com - reddit -> http://www.reddit.com - go -> http://www.golang.org - example -> http://www.example.org +### `add-link` -### ~/.startpage-weather +Add a link that is displayed on the startpage. First argument is the title, second one the URL. + +Example: -The place for the weather is stored here. startpage uses [yr.no](http://www.yr.no) to get weather data. Use the search box on that page to search for your place. You will then be redirected to an URL like this: `http://www.yr.no/place/<myplace>`. Put the `<myplace>` part into the `.startpage-weather` file. + add-link github http://www.github.com + add-link reddit http://www.reddit.com + add-link go http://www.golang.org + add-link another\ example http://www.example.org ## Running diff --git a/conflang.go b/conflang.go new file mode 100644 index 0000000..0b4a856 --- /dev/null +++ b/conflang.go @@ -0,0 +1,160 @@ +package main + +import ( + "bufio" + "fmt" + "io" +) + +type toktype int + +const ( + tText toktype = iota + tNextCmd +) + +type token struct { + Type toktype + Data string + Line int +} + +func scan(r io.Reader, tokens chan<- token, errch chan<- error) { + emit := func(t toktype, d []byte, line int) { + if t == tText && len(d) == 0 { + return + } + tokens <- token{t, string(d), line} + } + + err := func() error { + br := bufio.NewReader(r) + + escaped := false + data := []byte{} + line := 1 + + for { + b, err := br.ReadByte() + + switch err { + case nil: + case io.EOF: + return nil + default: + return ErrorAtLine{line, err} + } + + if b == '\n' { + line++ + } + + if escaped { + data = append(data, b) + escaped = false + continue + } + + switch b { + case '\\': + escaped = true + case ' ', '\t': + emit(tText, data, line) + data = data[:0] + case '\n': + emit(tText, data, line) + data = data[:0] + emit(tNextCmd, nil, line) + default: + data = append(data, b) + } + } + + emit(tText, data, line) + emit(tNextCmd, nil, line) + return nil + }() + + close(tokens) + errch <- err +} + +type command struct { + Name string + Params []string + Line int +} + +func parse(tokens <-chan token, cmds chan<- command) { + defer close(cmds) + + startcmd := true + cmd := command{"", make([]string, 0), 0} + + for tok := range tokens { + switch tok.Type { + case tText: + if startcmd { + cmd.Name = tok.Data + cmd.Line = tok.Line + startcmd = false + } else { + cmd.Params = append(cmd.Params, tok.Data) + } + case tNextCmd: + if !startcmd { + cmds <- cmd + cmd.Name = "" + cmd.Params = make([]string, 0) + startcmd = true + } + } + } +} + +type cmdfunc func(params []string) error + +var commands = map[string]cmdfunc{ + "nop": func(_ []string) error { return nil }, +} + +func RegisterCommand(name string, f cmdfunc) { + commands[name] = f +} + +type ErrorAtLine struct { + Line int + Err error +} + +func (err ErrorAtLine) Error() string { + return fmt.Sprintf("%s (at line %d)", err.Err, err.Line) +} + +type CommandNotFound string + +func (c CommandNotFound) Error() string { + return fmt.Sprintf("Command \"%s\" not found", c) +} + +func RunCommands(r io.Reader) error { + errch := make(chan error) + tokens := make(chan token) + cmds := make(chan command) + + go scan(r, tokens, errch) + go parse(tokens, cmds) + + for cmd := range cmds { + f, ok := commands[cmd.Name] + if !ok { + return ErrorAtLine{cmd.Line, CommandNotFound(cmd.Name)} + } + + if err := f(cmd.Params); err != nil { + return ErrorAtLine{cmd.Line, err} + } + } + + return <-errch +} @@ -1,11 +1,8 @@ package main import ( - "bufio" + "errors" "html/template" - "log" - "os" - "strings" ) type Link struct { @@ -13,22 +10,13 @@ type Link struct { URL template.URL } -func GetLinks() (links []Link) { - fh, err := os.Open(os.ExpandEnv("$HOME/.startpage-urls")) - if err != nil { - log.Printf("Couldn't read links: %s", err) - return - } - defer fh.Close() +var links = []Link{} - scanner := bufio.NewScanner(fh) - for scanner.Scan() { - parts := strings.SplitN(scanner.Text(), "->", 2) - links = append(links, Link{ - strings.TrimSpace(parts[0]), - template.URL(strings.TrimSpace(parts[1])), - }) +func addLinkCmd(params []string) error { + if len(params) != 2 { + return errors.New("add-link needs 2 parameters: title url") } - return + links = append(links, Link{params[0], template.URL(params[1])}) + return nil } @@ -90,11 +90,30 @@ func loadTemplate() { panic(errors.New("could not find template in $GOPATH/src/github.com/kch42/startpage")) } +func initCmds() { + RegisterCommand("add-link", addLinkCmd) + RegisterCommand("set-weather-place", setPlaceCmd) +} + +func runConf() { + f, err := os.Open(os.ExpandEnv("$HOME/.startpagerc")) + if err != nil { + log.Fatalf("Could not open startpagerc: %s", err) + } + defer f.Close() + + if err := RunCommands(f); err != nil { + log.Fatal(err) + } +} + func main() { laddr := flag.String("laddr", ":25145", "Listen on this port") flag.Parse() loadTemplate() + initCmds() + runConf() pornch := make(chan bool) weatherch := make(chan bool) @@ -1,13 +1,10 @@ package main import ( - "bytes" "encoding/xml" + "errors" "fmt" - "io" "net/http" - "os" - "strings" "time" ) @@ -61,21 +58,16 @@ type weatherdata struct { Forecast []*Weather `xml:"forecast>tabular>time"` } -func getPlace() string { - fh, err := os.Open(os.ExpandEnv("$HOME/.startpage-weather")) - if err != nil { - panic(err) - } - defer fh.Close() +var place = "" - buf := new(bytes.Buffer) - if _, err := io.Copy(buf, fh); err != nil { - panic(err) +func setPlaceCmd(params []string) error { + if len(params) != 1 { + return errors.New("set-weather-place needs one parameter") } - return strings.TrimSpace(string(buf.Bytes())) -} -var place = getPlace() + place = params[0] + return nil +} func CurrentWeather() (Weather, Sun, error) { url := "http://www.yr.no/place/" + place + "/forecast_hour_by_hour.xml" |