package main import ( "bufio" "flag" "fmt" "io" "os" "regexp" "strings" "text/tabwriter" ) type queue struct { q []string maxl int } func (q *queue) insert(s string) { if len(q.q) <= q.maxl { q.q = append(q.q, s) return } q.q = append(q.q[1:len(q.q)], s) } type work struct { c chan string left int done chan struct{} } // By writing to w.done we make sure that the goroutine has finished // sending to the out channel before returning from this method. func (w *work) finish() { close(w.c) w.done <- struct{}{} return } func (w *work) print_kwic(sl []string, out chan string) { for word := range w.c { sl = append(sl, word) } out <- strings.Join(sl, "\t") <-w.done } func feedworkers(wlist []*work, w string) []*work { var ret []*work if len(wlist) == 0 { return ret } ret = make([]*work, len(wlist)) copy(ret, wlist) for _, wc := range wlist { if wc.left == 0 { wc.finish() ret = wlist[1:] } else { wc.c <- w wc.left -= 1 } } return ret } func printqueue(q *queue, words chan string, kw string, left int, out chan string) { var hungryhippos []*work for w := range words { index := strings.Index(w, kw) if index < 0 { q.insert(w) hungryhippos = feedworkers(hungryhippos, w) continue } qstr := fmt.Sprintf("%q", w) q.insert(qstr) wc := &work{c: make(chan string, 10), left: 5, done: make(chan struct{})} hungryhippos = feedworkers(hungryhippos, qstr) sl := make([]string, MAX) copy(sl, q.q[6:]) hungryhippos = append(hungryhippos, wc) go wc.print_kwic(sl, out) } for _, wc := range hungryhippos { wc.finish() } close(out) } var tw *tabwriter.Writer const MAX int = 11 func main() { flag.Parse() kw := flag.Arg(0) if len(kw) == 0 { fmt.Printf("Need a string to search for as an argument.\n") os.Exit(1) } tw = tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) splitreg := regexp.MustCompile("[ \t\n]") q := queue{maxl: MAX, q: make([]string, MAX)} sc := make(chan string, 1000) out := make(chan string, 1000) reader := bufio.NewReader(os.Stdin) line, err := reader.ReadString(byte('\n')) go func(c chan string) { var w string for err == nil { splline := splitreg.Split(line, -1) for _, w = range splline { c <- w } line, err = reader.ReadString(byte('\n')) } if err != io.EOF { fmt.Printf("Got an error: %q.\n", err) } c <- w close(c) }(sc) go printqueue(&q, sc, kw, 5, out) for l := range out { fmt.Fprintf(tw, "%s\n", l) } tw.Flush() }