From 1d9c5ef8e2caad5c55e3c2dd4d6832c6b4435e37 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Fri, 16 Oct 2015 09:12:59 -0400 Subject: [PATCH] publish ch1/... --- ch1/dup1/main.go | 31 +++++++++++++++++ ch1/dup2/main.go | 48 ++++++++++++++++++++++++++ ch1/dup3/main.go | 37 ++++++++++++++++++++ ch1/echo1/main.go | 24 +++++++++++++ ch1/echo2/main.go | 24 +++++++++++++ ch1/echo3/main.go | 20 +++++++++++ ch1/fetch/main.go | 34 +++++++++++++++++++ ch1/fetchall/main.go | 49 +++++++++++++++++++++++++++ ch1/helloworld/main.go | 9 +++++ ch1/lissajous/main.go | 77 ++++++++++++++++++++++++++++++++++++++++++ ch1/server1/main.go | 26 ++++++++++++++ ch1/server2/main.go | 41 ++++++++++++++++++++++ ch1/server3/main.go | 52 ++++++++++++++++++++++++++++ 13 files changed, 472 insertions(+) create mode 100644 ch1/dup1/main.go create mode 100644 ch1/dup2/main.go create mode 100644 ch1/dup3/main.go create mode 100644 ch1/echo1/main.go create mode 100644 ch1/echo2/main.go create mode 100644 ch1/echo3/main.go create mode 100644 ch1/fetch/main.go create mode 100644 ch1/fetchall/main.go create mode 100644 ch1/lissajous/main.go create mode 100644 ch1/server1/main.go create mode 100644 ch1/server2/main.go create mode 100644 ch1/server3/main.go diff --git a/ch1/dup1/main.go b/ch1/dup1/main.go new file mode 100644 index 0000000..f476741 --- /dev/null +++ b/ch1/dup1/main.go @@ -0,0 +1,31 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 8. +//!+ + +// Dup1 prints the text of each line that appears more than +// once in the standard input, preceded by its count. +package main + +import ( + "bufio" + "fmt" + "os" +) + +func main() { + counts := make(map[string]int) + input := bufio.NewScanner(os.Stdin) + for input.Scan() { + counts[input.Text()]++ + } + // NOTE: ignoring potential errors from input.Err() + for line, n := range counts { + if n > 1 { + fmt.Printf("%d\t%s\n", n, line) + } + } +} + +//!- diff --git a/ch1/dup2/main.go b/ch1/dup2/main.go new file mode 100644 index 0000000..e894f84 --- /dev/null +++ b/ch1/dup2/main.go @@ -0,0 +1,48 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 10. +//!+ + +// Dup2 prints the count and text of lines that appear more than once +// in the input. It reads from stdin or from a list of named files. +package main + +import ( + "bufio" + "fmt" + "os" +) + +func main() { + counts := make(map[string]int) + files := os.Args[1:] + if len(files) == 0 { + countLines(os.Stdin, counts) + } else { + for _, arg := range files { + f, err := os.Open(arg) + if err != nil { + fmt.Fprintf(os.Stderr, "dup2: %v\n", err) + continue + } + countLines(f, counts) + f.Close() + } + } + for line, n := range counts { + if n > 1 { + fmt.Printf("%d\t%s\n", n, line) + } + } +} + +func countLines(f *os.File, counts map[string]int) { + input := bufio.NewScanner(f) + for input.Scan() { + counts[input.Text()]++ + } + // NOTE: ignoring potential errors from input.Err() +} + +//!- diff --git a/ch1/dup3/main.go b/ch1/dup3/main.go new file mode 100644 index 0000000..d5a0ad9 --- /dev/null +++ b/ch1/dup3/main.go @@ -0,0 +1,37 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 12. + +// Dup3 prints the count and text of lines that appear more than once +// in the named input files. +//!+ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "strings" +) + +func main() { + counts := make(map[string]int) + for _, filename := range os.Args[1:] { + data, err := ioutil.ReadFile(filename) + if err != nil { + fmt.Fprintf(os.Stderr, "dup3: %v\n", err) + continue + } + for _, line := range strings.Split(string(data), "\n") { + counts[line]++ + } + } + for line, n := range counts { + if n > 1 { + fmt.Printf("%d\t%s\n", n, line) + } + } +} + +//!- diff --git a/ch1/echo1/main.go b/ch1/echo1/main.go new file mode 100644 index 0000000..e8f8969 --- /dev/null +++ b/ch1/echo1/main.go @@ -0,0 +1,24 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 4. +//!+ + +// Echo1 prints its command-line arguments. +package main + +import ( + "fmt" + "os" +) + +func main() { + var s, sep string + for i := 1; i < len(os.Args); i++ { + s += sep + os.Args[i] + sep = " " + } + fmt.Println(s) +} + +//!- diff --git a/ch1/echo2/main.go b/ch1/echo2/main.go new file mode 100644 index 0000000..82d312c --- /dev/null +++ b/ch1/echo2/main.go @@ -0,0 +1,24 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 6. +//!+ + +// Echo2 prints its command-line arguments. +package main + +import ( + "fmt" + "os" +) + +func main() { + s, sep := "", "" + for _, arg := range os.Args[1:] { + s += sep + arg + sep = " " + } + fmt.Println(s) +} + +//!- diff --git a/ch1/echo3/main.go b/ch1/echo3/main.go new file mode 100644 index 0000000..031a650 --- /dev/null +++ b/ch1/echo3/main.go @@ -0,0 +1,20 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 8. + +// Echo3 prints its command-line arguments. +package main + +import ( + "fmt" + "os" + "strings" +) + +//!+ +func main() { + fmt.Println(strings.Join(os.Args[1:], " ")) +} + +//!- diff --git a/ch1/fetch/main.go b/ch1/fetch/main.go new file mode 100644 index 0000000..5bde965 --- /dev/null +++ b/ch1/fetch/main.go @@ -0,0 +1,34 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 16. +//!+ + +// Fetch prints the content found at a URL. +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" +) + +func main() { + for _, url := range os.Args[1:] { + resp, err := http.Get(url) + if err != nil { + fmt.Fprintf(os.Stderr, "fetch: %v\n", err) + os.Exit(1) + } + b, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err) + os.Exit(1) + } + fmt.Printf("%s", b) + } +} + +//!- diff --git a/ch1/fetchall/main.go b/ch1/fetchall/main.go new file mode 100644 index 0000000..ad32109 --- /dev/null +++ b/ch1/fetchall/main.go @@ -0,0 +1,49 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 17. +//!+ + +// Fetchall fetches URLs in parallel and reports their times and sizes. +package main + +import ( + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "time" +) + +func main() { + start := time.Now() + ch := make(chan string) + for _, url := range os.Args[1:] { + go fetch(url, ch) // start a goroutine + } + for range os.Args[1:] { + fmt.Println(<-ch) // receive from channel ch + } + fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds()) +} + +func fetch(url string, ch chan<- string) { + start := time.Now() + resp, err := http.Get(url) + if err != nil { + ch <- fmt.Sprint(err) // send to channel ch + return + } + + nbytes, err := io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() // don't leak resources + if err != nil { + ch <- fmt.Sprintf("while reading %s: %v", url, err) + return + } + secs := time.Since(start).Seconds() + ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url) +} + +//!- diff --git a/ch1/helloworld/main.go b/ch1/helloworld/main.go index 078ddff..cd5e0ab 100644 --- a/ch1/helloworld/main.go +++ b/ch1/helloworld/main.go @@ -1,3 +1,10 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 1. + +// Helloworld is our first Go program. +//!+ package main import "fmt" @@ -5,3 +12,5 @@ import "fmt" func main() { fmt.Println("Hello, 世界") } + +//!- diff --git a/ch1/lissajous/main.go b/ch1/lissajous/main.go new file mode 100644 index 0000000..8614d7b --- /dev/null +++ b/ch1/lissajous/main.go @@ -0,0 +1,77 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// Run with "web" command-line argument for web server. +// See page 13. +//!+main + +// Lissajous generates GIF animations of random Lissajous figures. +package main + +import ( + "image" + "image/color" + "image/gif" + "io" + //!-main + "log" + //!+main + "math" + "math/rand" + //!-main + "net/http" + //!+main + "os" +) + +var palette = []color.Color{color.White, color.Black} + +const ( + whiteIndex = 0 // first color in palette + blackIndex = 1 // next color in palette +) + +func main() { + //!-main + if len(os.Args) > 1 && os.Args[1] == "web" { + //!+http + handler := func(w http.ResponseWriter, r *http.Request) { + lissajous(w) + } + http.HandleFunc("/", handler) + //!-http + log.Fatal(http.ListenAndServe("localhost:8000", nil)) + return + } + //!+main + lissajous(os.Stdout) +} + +func lissajous(out io.Writer) { + const ( + cycles = 5 // number of complete x oscillator revolutions + res = 0.001 // angular resolution + size = 100 // image canvas covers [-size..+size] + nframes = 64 // number of animation frames + delay = 8 // delay between frames in 10ms units + ) + freq := rand.Float64() * 3.0 // relative frequency of y oscillator + anim := gif.GIF{LoopCount: nframes} + phase := 0.0 // phase difference + for i := 0; i < nframes; i++ { + rect := image.Rect(0, 0, 2*size+1, 2*size+1) + img := image.NewPaletted(rect, palette) + for t := 0.0; t < cycles*2*math.Pi; t += res { + x := math.Sin(t) + y := math.Sin(t*freq + phase) + img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), + blackIndex) + } + phase += 0.1 + anim.Delay = append(anim.Delay, delay) + anim.Image = append(anim.Image, img) + } + gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors +} + +//!-main diff --git a/ch1/server1/main.go b/ch1/server1/main.go new file mode 100644 index 0000000..0bc7ce0 --- /dev/null +++ b/ch1/server1/main.go @@ -0,0 +1,26 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 19. +//!+ + +// Server1 is a minimal "echo" server. +package main + +import ( + "fmt" + "log" + "net/http" +) + +func main() { + http.HandleFunc("/", handler) // each request calls handler + log.Fatal(http.ListenAndServe("localhost:8000", nil)) +} + +// handler echoes the Path component of the request URL r. +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) +} + +//!- diff --git a/ch1/server2/main.go b/ch1/server2/main.go new file mode 100644 index 0000000..dd8ec57 --- /dev/null +++ b/ch1/server2/main.go @@ -0,0 +1,41 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 20. +//!+ + +// Server2 is a minimal "echo" and counter server. +package main + +import ( + "fmt" + "log" + "net/http" + "sync" +) + +var mu sync.Mutex +var count int + +func main() { + http.HandleFunc("/", handler) + http.HandleFunc("/count", counter) + log.Fatal(http.ListenAndServe("localhost:8000", nil)) +} + +// handler echoes the Path component of the requested URL. +func handler(w http.ResponseWriter, r *http.Request) { + mu.Lock() + count++ + mu.Unlock() + fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) +} + +// counter echoes the number of calls so far. +func counter(w http.ResponseWriter, r *http.Request) { + mu.Lock() + fmt.Fprintf(w, "Count %d\n", count) + mu.Unlock() +} + +//!- diff --git a/ch1/server3/main.go b/ch1/server3/main.go new file mode 100644 index 0000000..aa8316c --- /dev/null +++ b/ch1/server3/main.go @@ -0,0 +1,52 @@ +// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ + +// See page 21. +//!+ + +// Server3 is a minimal "echo" and counter server. +package main + +import ( + "fmt" + "log" + "net/http" + "sync" +) + +var mu sync.Mutex +var count int + +func main() { + http.HandleFunc("/", handler) + http.HandleFunc("/count", counter) + log.Fatal(http.ListenAndServe("localhost:8000", nil)) +} + +//!+handler +// handler echoes the HTTP request. +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "%s %s %s\n", r.Method, r.URL, r.Proto) + for k, v := range r.Header { + fmt.Fprintf(w, "Header[%q] = %q\n", k, v) + } + fmt.Fprintf(w, "Host = %q\n", r.Host) + fmt.Fprintf(w, "RemoteAddr = %q\n", r.RemoteAddr) + if err := r.ParseForm(); err != nil { + log.Print(err) + } + for k, v := range r.Form { + fmt.Fprintf(w, "Form[%q] = %q\n", k, v) + } +} + +//!-handler + +// counter echoes the number of calls so far. +func counter(w http.ResponseWriter, r *http.Request) { + mu.Lock() + fmt.Fprintf(w, "Count %d", count) + mu.Unlock() +} + +//!-