<commit_before>package main

import (
	"github.com/nsf/termbox-go"
	"log"
)

type direction int

const (
	up direction = iota
	down
	left
	right
)

type game struct {
	viewport *viewport
	screens  Stack
	world    *world
	logger   *log.Logger
}

func NewGame(logger *log.Logger) *game {
	screens := Stack{}
	screens.Push(welcomeScreen{})
	width, height := termbox.Size()
	world := NewWorld(250, 100)
	player := newPlayer(world)
	viewport := centeredViewport(player.location, width, height, world)
	world.player = player
	var entity autonomous = newFungus(world)
	world.entities.add(entity)
	game := &game{viewport, screens, world, logger}
	return game
}

func (g *game) PopLastScreen() Drawable {
	screen := g.screens.Pop()
	if screen != nil {
		return screen.(Drawable)
	} else {
		return nil
	}
}

func (g *game) Update(screen Drawable, event termbox.Event) {
	nextScreens := screen.Input(g, event)
	if len(nextScreens) > 0 {
		for _, s := range nextScreens {
			g.screens.Push(s)
		}
	}
}

func (g *game) Render(screen Drawable) {
	termbox.Clear(termbox.ColorWhite, termbox.ColorBlack)
	screen.(Drawable).Draw(g)
	termbox.Flush()
}

func (g *game) Run() {
	screen := g.PopLastScreen()
	for screen != nil {
		g.Render(screen)
		event := termbox.PollEvent()
		g.Update(screen, event)
		screen = g.PopLastScreen()
	}
}
<commit_msg>Reorganize NewGame code<commit_after>package main

import (
	"github.com/nsf/termbox-go"
	"log"
)

type direction int

const (
	up direction = iota
	down
	left
	right
)

type game struct {
	viewport *viewport
	screens  Stack
	world    *world
	logger   *log.Logger
}

func NewGame(logger *log.Logger) *game {
	world := NewWorld(250, 100)

	player := newPlayer(world)
	world.player = player

	var entity autonomous = newFungus(world)
	world.entities.add(entity)

	width, height := termbox.Size()
	viewport := centeredViewport(player.location, width, height, world)

	screens := Stack{}
	screens.Push(welcomeScreen{})
	game := &game{
		viewport: viewport,
		screens: screens,
		world: world,
		logger: logger,
	}
	return game
}

func (g *game) PopLastScreen() Drawable {
	screen := g.screens.Pop()
	if screen != nil {
		return screen.(Drawable)
	} else {
		return nil
	}
}

func (g *game) Update(screen Drawable) {
	event := termbox.PollEvent()
	nextScreens := screen.Input(g, event)
	if len(nextScreens) > 0 {
		for _, s := range nextScreens {
			g.screens.Push(s)
		}
	}
}

func (g *game) Render(screen Drawable) {
	termbox.Clear(termbox.ColorWhite, termbox.ColorBlack)
	screen.(Drawable).Draw(g)
	termbox.Flush()
}

func (g *game) Run() {
	screen := g.PopLastScreen()
	for screen != nil {
		g.Render(screen)
		g.Update(screen)
		screen = g.PopLastScreen()
	}
}
<|endoftext|>


<commit_before>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import (
	"encoding/json"
	"fmt"
	"github.com/gocql/gocql"
	"log"
	"net/http"
)

type Grid struct {
	GridId    gocql.UUID `json:"grid_id"`   // timeuuid
	TurnId    int        `json:"turn_id"`   // turn number
	OffsetMs  float64    `json:"offset_ms"` // time offset from beginning of game (ms)
	TurnMs    float64    `json:"turn_ms"`   // time elapsed between turns
	Player    string     `json:"player"`    // arbitrary string, player name
	Score     float32    `json:"score"`     // score at the end of the turn
	TileVal   int        `json:"tile_val"`  // the new tile value put on the board
	TileIdx   int        `json:"tile_idx"`  // index on the grid where the new tile was placed
	Direction string     `json:"dir"`       // up down left right init
	Grid      []int      `json:"grid"`      // every value in the grid
}

func (g *Grid) Save(cass *gocql.Session) error {
	query := `INSERT INTO grids (grid_id, turn_id, offset_ms, turn_ms, player, score, tile_val, tile_idx, direction, grid) VALUES (?,?,?,?,?,?,?,?,?,?)`
	return cass.Query(query, g.GridId, g.TurnId, g.OffsetMs, g.TurnMs, g.Player, g.Score, g.TileVal, g.TileIdx, g.Direction, g.Grid).Exec()
}

func GridHandler(w http.ResponseWriter, r *http.Request) {
	g := Grid{}
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&g)
	if err != nil {
		log.Printf("PUT invalid json data: %s", err)
		http.Error(w, fmt.Sprintf("PUT invalid json data: %s", err), 500)
	}

	switch r.Method {
	case "PUT":
		err := g.Save(cass)
		if err != nil {
			log.Printf("Write to Cassandra failed: %s", err)
			http.Error(w, "Write to Cassandra failed!", 500)
		}
	default:
		http.Error(w, fmt.Sprintf("method '%s' not implemented", r.Method), 500)
		return
	}

	js, err := json.Marshal(g)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(js)
}
<commit_msg>Add a quick pass at a GetGrids() function.<commit_after>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import (
	"encoding/json"
	"fmt"
	"github.com/gocql/gocql"
	"log"
	"net/http"
)

type Grid struct {
	GridId    gocql.UUID `json:"grid_id"`   // timeuuid
	TurnId    int        `json:"turn_id"`   // turn number
	OffsetMs  float64    `json:"offset_ms"` // time offset from beginning of game (ms)
	TurnMs    float64    `json:"turn_ms"`   // time elapsed between turns
	Player    string     `json:"player"`    // arbitrary string, player name
	Score     float32    `json:"score"`     // score at the end of the turn
	TileVal   int        `json:"tile_val"`  // the new tile value put on the board
	TileIdx   int        `json:"tile_idx"`  // index on the grid where the new tile was placed
	Direction string     `json:"dir"`       // up down left right init
	Grid      []int      `json:"grid"`      // every value in the grid
}

type Grids []Grid

func (g *Grid) Save(cass *gocql.Session) error {
	query := `INSERT INTO grids (grid_id, turn_id, offset_ms, turn_ms, player, score, tile_val, tile_idx, direction, grid) VALUES (?,?,?,?,?,?,?,?,?,?)`
	return cass.Query(query, g.GridId, g.TurnId, g.OffsetMs, g.TurnMs, g.Player, g.Score, g.TileVal, g.TileIdx, g.Direction, g.Grid).Exec()
}

func GetGrids(cass *gocql.Session, id gocql.UUID, min_turn_id, max_turn_id int) (list Grids, err error) {
	list = make(Grids, 0)

	query := `SELECT grid_id, turn_id, offset_ms, turn_ms, player, score, tile_val, tile_idx, direction, grid FROM grids WHERE grid_id=?`
	iq := cass.Query(query, id).Iter()

	for {
		g := Grid{}
		ok := iq.Scan(&g.GridId, &g.TurnId, &g.OffsetMs, &g.TurnMs, &g.Player, &g.Score, &g.TileVal, &g.TileIdx, &g.Direction, &g.Grid)
		if ok {
			list = append(list, g)
		} else {
			break
		}
	}
	if err = iq.Close(); err != nil {
		log.Printf("Error during Cassandra query: %s", err)
	}

	return list, err
}

func GridHandler(w http.ResponseWriter, r *http.Request) {
	g := Grid{}
	dec := json.NewDecoder(r.Body)
	err := dec.Decode(&g)
	if err != nil {
		log.Printf("PUT invalid json data: %s", err)
		http.Error(w, fmt.Sprintf("PUT invalid json data: %s", err), 500)
	}

	switch r.Method {
	case "PUT":
		err := g.Save(cass)
		if err != nil {
			log.Printf("Write to Cassandra failed: %s", err)
			http.Error(w, "Write to Cassandra failed!", 500)
		}
	default:
		http.Error(w, fmt.Sprintf("method '%s' not implemented", r.Method), 500)
		return
	}
}
<|endoftext|>


<commit_before>package meow-data-structures

import (
	"fmt"
	"bytes"
	"sync"
)

type meowHashSet struct {
	meowLock *sync.Mutex
	stuffs map[interface{}]interface{}
}<commit_msg>add new hash set mapping<commit_after>package meow-data-structures

import (
	"fmt"
	"bytes"
	"sync"
)

type meowHashSet struct {
	meowLock *sync.Mutex
	stuffs map[interface{}]interface{}
}

func meowNewHashSet() *meowHashSet {
	meowInstance := &meowHashSet{}
	meowInstance.meowLock = &sync.Mutex{}
	meowInstance.stuffs = make(map[interface{}]interface{})
	return meowInstance
}<|endoftext|>


<commit_before>package main

import (
	"./goleg"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

type Config struct {
	// Base settings (required)
	Listen  string `json:"listen"`
	DataDir string `json: "datadir"`
	// HTTPS settings
	UseHTTPS         bool   `json:"usehttps"`
	CertFile         string `json:"certfile"`
	PkeyFile         string `json:"pkeyfile"`
	SplayTreeEnabled bool   `json:"splaytreeenabled"`
	LZ4Enabled       bool   `json:"lz4enabled"`
	AOLEnabled       bool   `json:"aolenabled"`
}

var config Config
var databases map[string]goleg.Database

const (
	Usage = `
Usage: %s -config <config path> [options]
	Config path:
		Path to a configuration file. This is required.
Options:
	-v
		Version
	-h
		This help.
	-bind
		Override Listen directive in configuration.
	-dir
		Override db storage location in configuration.
	-enable-lz4
		Enables LZ4 compression.
	-enable-splay-tree
		Enables the splay tree.
	-enable-aol
		Enables the append-only log.
`
)

func main() {
	// Parse command line flags (if there are)
	directory := flag.String("dir", "", "Directory where to store dumps and data")
	configfile := flag.String("config", "", "Config file to read settings from")
	bindaddr := flag.String("bind", "", "Address and port to bind, host:port")
	splaytreeenabled := flag.Bool("enable-splay-tree", false, "Enables the splay tree")
	lz4enabled := flag.Bool("enable-lz4", false, "Enables LZ4 compression")
	aolenabled := flag.Bool("enable-aol", false, "Enables the append-only log")

	flag.Parse()

	if *configfile == "" {
		fmt.Println("Config file is required")
		fmt.Printf(Usage, os.Args[0])
		os.Exit(1)
	}

	// Parse config file
	rawconf, err := ioutil.ReadFile(*configfile)
	if err != nil {
		fmt.Println("Could not read configuration. Please see the documentation.")
		fmt.Printf(Usage, os.Args[0])
		os.Exit(1)
	}
	err = json.Unmarshal(rawconf, &config)
	if err != nil {
		panic(err.Error())
	}

	databases = make(map[string]goleg.Database)

	// Override config option with command line ones
	if *directory != "" {
		config.DataDir = *directory
	}

	if *bindaddr != "" {
		config.Listen = *bindaddr
	}

	if *splaytreeenabled {
		config.SplayTreeEnabled = *splaytreeenabled
	}

	if *lz4enabled {
		config.LZ4Enabled = *lz4enabled
	}

	if *aolenabled {
		config.AOLEnabled = *aolenabled
	}

	http.HandleFunc("/", handler)
	defer unload()
	var httperr error
	log.Println("Starting server...")
	if config.LZ4Enabled {
		log.Println("LZ4 compression is enabled")
	}
	if config.AOLEnabled {
		log.Println("Append-only log is enabled")
	}
	if config.SplayTreeEnabled {
		log.Println("Splay-tree log is enabled")
	}
	if config.UseHTTPS {
		log.Println("Listening on https://" + config.Listen)
		httperr = http.ListenAndServeTLS(config.Listen, config.CertFile, config.PkeyFile, nil)
	} else {
		log.Println("Listening on http://" + config.Listen)
		httperr = http.ListenAndServe(config.Listen, nil)
	}
	if httperr != nil {
		log.Fatal(httperr)
	}
}

func unload() {
	for v := range databases {
		databases[v].Close()
	}
}
<commit_msg>s/log//<commit_after>package main

import (
	"./goleg"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

type Config struct {
	// Base settings (required)
	Listen  string `json:"listen"`
	DataDir string `json: "datadir"`
	// HTTPS settings
	UseHTTPS         bool   `json:"usehttps"`
	CertFile         string `json:"certfile"`
	PkeyFile         string `json:"pkeyfile"`
	SplayTreeEnabled bool   `json:"splaytreeenabled"`
	LZ4Enabled       bool   `json:"lz4enabled"`
	AOLEnabled       bool   `json:"aolenabled"`
}

var config Config
var databases map[string]goleg.Database

const (
	Usage = `
Usage: %s -config <config path> [options]
	Config path:
		Path to a configuration file. This is required.
Options:
	-v
		Version
	-h
		This help.
	-bind
		Override Listen directive in configuration.
	-dir
		Override db storage location in configuration.
	-enable-lz4
		Enables LZ4 compression.
	-enable-splay-tree
		Enables the splay tree.
	-enable-aol
		Enables the append-only log.
`
)

func main() {
	// Parse command line flags (if there are)
	directory := flag.String("dir", "", "Directory where to store dumps and data")
	configfile := flag.String("config", "", "Config file to read settings from")
	bindaddr := flag.String("bind", "", "Address and port to bind, host:port")
	splaytreeenabled := flag.Bool("enable-splay-tree", false, "Enables the splay tree")
	lz4enabled := flag.Bool("enable-lz4", false, "Enables LZ4 compression")
	aolenabled := flag.Bool("enable-aol", false, "Enables the append-only log")

	flag.Parse()

	if *configfile == "" {
		fmt.Println("Config file is required")
		fmt.Printf(Usage, os.Args[0])
		os.Exit(1)
	}

	// Parse config file
	rawconf, err := ioutil.ReadFile(*configfile)
	if err != nil {
		fmt.Println("Could not read configuration. Please see the documentation.")
		fmt.Printf(Usage, os.Args[0])
		os.Exit(1)
	}
	err = json.Unmarshal(rawconf, &config)
	if err != nil {
		panic(err.Error())
	}

	databases = make(map[string]goleg.Database)

	// Override config option with command line ones
	if *directory != "" {
		config.DataDir = *directory
	}

	if *bindaddr != "" {
		config.Listen = *bindaddr
	}

	if *splaytreeenabled {
		config.SplayTreeEnabled = *splaytreeenabled
	}

	if *lz4enabled {
		config.LZ4Enabled = *lz4enabled
	}

	if *aolenabled {
		config.AOLEnabled = *aolenabled
	}

	http.HandleFunc("/", handler)
	defer unload()
	var httperr error
	log.Println("Starting server...")
	if config.LZ4Enabled {
		log.Println("LZ4 compression is enabled")
	}
	if config.AOLEnabled {
		log.Println("Append-only log is enabled")
	}
	if config.SplayTreeEnabled {
		log.Println("Splay-tree is enabled")
	}
	if config.UseHTTPS {
		log.Println("Listening on https://" + config.Listen)
		httperr = http.ListenAndServeTLS(config.Listen, config.CertFile, config.PkeyFile, nil)
	} else {
		log.Println("Listening on http://" + config.Listen)
		httperr = http.ListenAndServe(config.Listen, nil)
	}
	if httperr != nil {
		log.Fatal(httperr)
	}
}

func unload() {
	for v := range databases {
		databases[v].Close()
	}
}
<|endoftext|>


<commit_before>package bsdatomtoics

import (
  "fmt"
  "net/http"
  "io"
  "io/ioutil"
  "os"
  "encoding/xml"
  "strings"
  "time"
)

const queryStart = "https://www.beaverton.k12.or.us/"
const queryEnd = "_vti_bin/BSD.Extranet/Syndication.svc/"
//District: "https://www.beaverton.k12.or.us/_vti_bin/BSD.Extranet/Syndication.svc/GetDistrictCalendarFeed?format=atom"
//JW "https://www.beaverton.k12.or.us/schools/jacob-wismer/_vti_bin/BSD.Extranet/Syndication.svc/GetSchoolEventsFeed?format=atom"
const outputfmt = "%s\n"

// Atom Feed Data Structure

type Feed struct {
  XMLName xml.Name "http://www.w3.org/2005/Atom feed"
  Title   string   `xml:"title"`
  Id      string   `xml:"id"`
  Link    []Link   `xml:"link"`
  Updated Time     `xml:"updated"`
  Entry   []Entry  `xml:"entry"`
}

type Entry struct {
  Title   string `xml:"title"`
  Id      string `xml:"id"`
  Link    []Link `xml:"link"`
  Updated Time   `xml:"updated"`
  Content string `xml:"content"`
}

type Link struct {
  Rel    string   "attr"
  Href   string   "attr"
}

type Text struct {
  Type   string   "attr"
  Body   string   "chardata"
}

type Time string

func FetchBytes() ([]byte, error) {
  return FetchBytesWith(http.DefaultClient, "")
}

func FetchBytesWith(client *http.Client, school string) ([]byte, error) {
   finalUri := queryStart;
   if school != "" { //jacob-wismer
      finalUri = finalUri + "schools/" + school + "/"
   }
   finalUri = finalUri + queryEnd
   if school == "" {
      finalUri = finalUri + "GetDistrictCalendarFeed?format=atom"
   } else {
      finalUri = finalUri + "GetSchoolEventsFeed?format=atom"
   }

   r, err := client.Get(finalUri)
   if (err != nil || r.StatusCode != 200) {
      return nil, err
   }
   rc, err := ioutil.ReadAll(r.Body);
   defer r.Body.Close();
   if (err != nil) { return nil, err }
   return rc, nil;
}

func AtomToICS(bytes []byte, writer io.Writer, debug bool) {
  var bsd Feed

  if (bytes == nil || len(bytes) == 0) {
    fmt.Fprintf(writer, "no bytes received in input to AtomToICS")
    return
  }
  err := xml.Unmarshal(bytes, &bsd)

  if (debug){
    fmt.Fprintf(os.Stderr, "Title: %s\n", bsd.Title)
    fmt.Fprintf(os.Stderr, "Id: %s\n", bsd.Id)
    fmt.Fprintf(os.Stderr, "Last updated: %v\n", len(bsd.Updated))
    fmt.Fprintf(os.Stderr, "Entry count after unmarshal: %v\n", len(bsd.Entry))
  }
  if err == nil {
    fmt.Fprintf(writer, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//BSDATOMTOICS v1.0//EN\r\n")
    for i := 0; i < len(bsd.Entry); i++ {
      fmt.Fprintf(writer, "BEGIN:VEVENT\r\n")
      fmt.Fprintf(writer, "SUMMARY:%s\r\n", bsd.Entry[i].Title)
      startTime, endTime, location := parseStartEndLocation(bsd.Entry[i].Content, debug)
      start, end := formatTimeForICS(startTime, endTime)
      fmt.Fprintf(writer, "DTSTART%s\r\n", start)
      fmt.Fprintf(writer, "DTEND%s\r\n", end)
      fmt.Fprintf(writer, "LOCATION:%s\r\n", location)
      fmt.Fprintf(writer, "END:VEVENT\r\n")
    }
    fmt.Fprintf(writer, "END:VCALENDAR\r\n")
  } else {
    fmt.Fprintf(os.Stderr, "Unable to parse the Atom feed (%v)\n", err)
  }
}

func parseStartEndLocation(content string, debug bool) (time.Time, time.Time, string) {
  //Event Time: 3/23/2015 12:00:00 PM - 3/27/2015 1:00:00 PM  Location:  Spring Break - Schools closed
  //fmt.Fprintf(os.Stderr, "Raw input: '%s'\n", content)
  strippedContent := strings.Replace(content, "\n", "", -1)
  reallyStrippedContent := strings.Replace(strippedContent, "\r", "", -1)
  eventTimeRemoved := strings.TrimLeft(strings.Replace(reallyStrippedContent, "Event Time: ", "", 1), " ")
  if (debug) {
    fmt.Fprintf(os.Stderr, "After removal: '%s'\n", eventTimeRemoved)
  }
  timeFromLocation := strings.SplitAfterN(eventTimeRemoved, "Location: ", 2)
  time := timeFromLocation[0]
  location := timeFromLocation[1]
  startFromEnd := strings.SplitAfterN(time, " - ", 2)
  return toTime(strings.Replace(startFromEnd[0], " - ", "", 1)),
         toTime(strings.TrimRight(strings.Replace(startFromEnd[1], "Location: ", "", 1), " ")),
         strings.Trim(location, " ")
}

func toTime(timeStr string) time.Time {
  loc, _ := time.LoadLocation("America/Los_Angeles")
  //fmt.Fprintf(os.Stderr, "Input: |%s|\n", timeStr)
  //Mon Jan 2 15:04:05 -0700 MST 2006
  shortForm := "1/2/2006 3:04:05 PM"
  timeVal, _ := time.ParseInLocation(shortForm, timeStr, loc)
  return timeVal//.UTC().Format("20060102T030400Z")
}

func formatTimeForICS(start time.Time, end time.Time) (string, string) {
  // start/end should be in local time. We need to detect midnight->11:59pm
  // entries and switch them to all day
  startHour, startMin, startSec := start.Clock()
  endHour, endMin, endSec := end.Clock()
  if !(startHour == 0 && startMin == 0 && startSec == 0 &&
     endHour == 23 && endMin == 59 && endSec == 0) {
       return ":" + start.UTC().Format("20060102T030400Z"),
              ":" + end.UTC().Format("20060102T030400Z")
  }
  // we're on an all day event - grab the start date and build the
  // correct string values
  startDay := ";VALUE=DATE:" + start.Format("20060102")
  endDay   := ";VALUE=DATE:" + start.Add(time.Duration(24) * time.Hour).Format("20060102")

  return startDay, endDay
}<commit_msg>track end of all day events better<commit_after>package bsdatomtoics

import (
  "fmt"
  "net/http"
  "io"
  "io/ioutil"
  "os"
  "encoding/xml"
  "strings"
  "time"
)

const queryStart = "https://www.beaverton.k12.or.us/"
const queryEnd = "_vti_bin/BSD.Extranet/Syndication.svc/"
//District: "https://www.beaverton.k12.or.us/_vti_bin/BSD.Extranet/Syndication.svc/GetDistrictCalendarFeed?format=atom"
//JW "https://www.beaverton.k12.or.us/schools/jacob-wismer/_vti_bin/BSD.Extranet/Syndication.svc/GetSchoolEventsFeed?format=atom"
const outputfmt = "%s\n"

// Atom Feed Data Structure

type Feed struct {
  XMLName xml.Name "http://www.w3.org/2005/Atom feed"
  Title   string   `xml:"title"`
  Id      string   `xml:"id"`
  Link    []Link   `xml:"link"`
  Updated Time     `xml:"updated"`
  Entry   []Entry  `xml:"entry"`
}

type Entry struct {
  Title   string `xml:"title"`
  Id      string `xml:"id"`
  Link    []Link `xml:"link"`
  Updated Time   `xml:"updated"`
  Content string `xml:"content"`
}

type Link struct {
  Rel    string   "attr"
  Href   string   "attr"
}

type Text struct {
  Type   string   "attr"
  Body   string   "chardata"
}

type Time string

func FetchBytes() ([]byte, error) {
  return FetchBytesWith(http.DefaultClient, "")
}

func FetchBytesWith(client *http.Client, school string) ([]byte, error) {
   finalUri := queryStart;
   if school != "" { //jacob-wismer
      finalUri = finalUri + "schools/" + school + "/"
   }
   finalUri = finalUri + queryEnd
   if school == "" {
      finalUri = finalUri + "GetDistrictCalendarFeed?format=atom"
   } else {
      finalUri = finalUri + "GetSchoolEventsFeed?format=atom"
   }

   r, err := client.Get(finalUri)
   if (err != nil || r.StatusCode != 200) {
      return nil, err
   }
   rc, err := ioutil.ReadAll(r.Body);
   defer r.Body.Close();
   if (err != nil) { return nil, err }
   return rc, nil;
}

func AtomToICS(bytes []byte, writer io.Writer, debug bool) {
  var bsd Feed

  if (bytes == nil || len(bytes) == 0) {
    fmt.Fprintf(writer, "no bytes received in input to AtomToICS")
    return
  }
  err := xml.Unmarshal(bytes, &bsd)

  if (debug){
    fmt.Fprintf(os.Stderr, "Title: %s\n", bsd.Title)
    fmt.Fprintf(os.Stderr, "Id: %s\n", bsd.Id)
    fmt.Fprintf(os.Stderr, "Last updated: %v\n", len(bsd.Updated))
    fmt.Fprintf(os.Stderr, "Entry count after unmarshal: %v\n", len(bsd.Entry))
  }
  if err == nil {
    fmt.Fprintf(writer, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//BSDATOMTOICS v1.0//EN\r\n")
    for i := 0; i < len(bsd.Entry); i++ {
      fmt.Fprintf(writer, "BEGIN:VEVENT\r\n")
      fmt.Fprintf(writer, "SUMMARY:%s\r\n", bsd.Entry[i].Title)
      startTime, endTime, location := parseStartEndLocation(bsd.Entry[i].Content, debug)
      start, end := formatTimeForICS(startTime, endTime)
      fmt.Fprintf(writer, "DTSTART%s\r\n", start)
      fmt.Fprintf(writer, "DTEND%s\r\n", end)
      fmt.Fprintf(writer, "LOCATION:%s\r\n", location)
      fmt.Fprintf(writer, "END:VEVENT\r\n")
    }
    fmt.Fprintf(writer, "END:VCALENDAR\r\n")
  } else {
    fmt.Fprintf(os.Stderr, "Unable to parse the Atom feed (%v)\n", err)
  }
}

func parseStartEndLocation(content string, debug bool) (time.Time, time.Time, string) {
  //Event Time: 3/23/2015 12:00:00 PM - 3/27/2015 1:00:00 PM  Location:  Spring Break - Schools closed
  //fmt.Fprintf(os.Stderr, "Raw input: '%s'\n", content)
  strippedContent := strings.Replace(content, "\n", "", -1)
  reallyStrippedContent := strings.Replace(strippedContent, "\r", "", -1)
  eventTimeRemoved := strings.TrimLeft(strings.Replace(reallyStrippedContent, "Event Time: ", "", 1), " ")
  if (debug) {
    fmt.Fprintf(os.Stderr, "After removal: '%s'\n", eventTimeRemoved)
  }
  timeFromLocation := strings.SplitAfterN(eventTimeRemoved, "Location: ", 2)
  time := timeFromLocation[0]
  location := timeFromLocation[1]
  startFromEnd := strings.SplitAfterN(time, " - ", 2)
  return toTime(strings.Replace(startFromEnd[0], " - ", "", 1)),
         toTime(strings.TrimRight(strings.Replace(startFromEnd[1], "Location: ", "", 1), " ")),
         strings.Trim(location, " ")
}

func toTime(timeStr string) time.Time {
  loc, _ := time.LoadLocation("America/Los_Angeles")
  //fmt.Fprintf(os.Stderr, "Input: |%s|\n", timeStr)
  //Mon Jan 2 15:04:05 -0700 MST 2006
  shortForm := "1/2/2006 3:04:05 PM"
  timeVal, _ := time.ParseInLocation(shortForm, timeStr, loc)
  return timeVal//.UTC().Format("20060102T030400Z")
}

func formatTimeForICS(start time.Time, end time.Time) (string, string) {
  // start/end should be in local time. We need to detect midnight->11:59pm
  // entries and switch them to all day
  startHour, startMin, startSec := start.Clock()
  endHour, endMin, endSec := end.Clock()
  if !(startHour == 0 && startMin == 0 && startSec == 0 &&
     endHour == 23 && endMin == 59 && endSec == 0) {
       return ":" + start.UTC().Format("20060102T030400Z"),
              ":" + end.UTC().Format("20060102T030400Z")
  }
  // we're on an all day event - grab the start date and build the
  // correct string values
  startDay := ";VALUE=DATE:" + start.Format("20060102")
  endDay   := ";VALUE=DATE:" + end.Add(time.Duration(1) * time.Minute).Format("20060102")

  return startDay, endDay
}<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fs

import (
	"fmt"
	"sort"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

// State required for reading from directories.
type dirHandle struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	in *inode.DirInode

	/////////////////////////
	// Mutable state
	/////////////////////////

	Mu syncutil.InvariantMutex

	// All entries in the directory. Populated the first time we need one.
	//
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	//
	// GUARDED_BY(Mu)
	entries []fuseutil.Dirent

	// Has entries yet been populated?
	//
	// INVARIANT: If !entriesValid, then len(entries) == 0
	//
	// GUARDED_BY(Mu)
	entriesValid bool
}

// Create a directory handle that obtains listings from the supplied inode.
func newDirHandle(in *inode.DirInode) (dh *dirHandle) {
	// Set up the basic struct.
	dh = &dirHandle{
		in: in,
	}

	// Set up invariant checking.
	dh.Mu = syncutil.NewInvariantMutex(dh.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Dirents, sorted by name.
type SortedDirents []fuseutil.Dirent

func (p SortedDirents) Len() int           { return len(p) }
func (p SortedDirents) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p SortedDirents) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func (dh *dirHandle) checkInvariants() {
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	for i := 0; i < len(dh.entries)-1; i++ {
		if !(dh.entries[i+1].Offset == dh.entries[i].Offset+1) {
			panic(
				fmt.Sprintf(
					"Unexpected offset sequence: %v, %v",
					dh.entries[i].Offset,
					dh.entries[i+1].Offset))
		}
	}

	// INVARIANT: If !entriesValid, then len(entries) == 0
	if !dh.entriesValid && len(dh.entries) != 0 {
		panic("Unexpected non-empty entries slice")
	}
}

// Read some entries from the directory inode. Return newTok == "" (possibly
// with a non-empty list of entries) when the end of the directory has been
// hit.
func readSomeEntries(
	ctx context.Context,
	in *inode.DirInode,
	tok string,
	firstEntryOffset fuseops.DirOffset) (
	entries []fuseutil.Dirent, newTok string, err error) {
	entries, newTok, err = in.ReadEntries(ctx, tok)
	if err != nil {
		err = fmt.Errorf("ReadEntries: %v", err)
		return
	}

	// Return a bogus inode ID for each entry, but not the root inode ID.
	//
	// NOTE(jacobsa): As far as I can tell this is harmless. Minting and
	// returning a real inode ID is difficult because fuse does not count
	// readdir as an operation that increases the inode ID's lookup count and
	// we therefore don't get a forget for it later, but we would like to not
	// have to remember every inode ID that we've ever minted for readdir.
	//
	// If it turns out this is not harmless, we'll need to switch to something
	// like inode IDs based on (object name, generation) hashes. But then what
	// about the birthday problem? And more importantly, what about our
	// semantic of not minting a new inode ID when the generation changes due
	// to a local action?
	for i, _ := range entries {
		entries[i].Inode = fuseops.RootInodeID + 1
	}

	// Fix up the offset of any entries returned.
	for i := 0; i < len(entries); i++ {
		entries[i].Offset = firstEntryOffset + 1 + fuseops.DirOffset(i)
	}

	return
}

// Call readSomeEntries in a loop until the directory is exhausted. Return
// contents sorted by name.
func readAllEntries(
	ctx context.Context,
	in *inode.DirInode) (entries SortedDirents, err error) {
	// Ensure that our result is actually sorted.
	defer func() {
		sort.Sort(entries)
	}()

	// Read in a loop.
	var tok string
	for {
		var batch []fuseutil.Dirent

		// Accumulate some more entries.
		firstOffset := fuseops.DirOffset(len(entries))
		batch, tok, err = readSomeEntries(ctx, in, tok, firstOffset)
		if err != nil {
			return
		}

		entries = append(entries, batch...)

		// Are we done?
		if tok == "" {
			break
		}
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Handle a request to read from the directory, without responding.
//
// Special case: we assume that a zero offset indicates that rewinddir has been
// called (since fuse gives us no way to intercept and know for sure), and
// start the listing process over again.
//
// LOCKS_REQUIRED(dh.Mu)
func (dh *dirHandle) ReadDir(
	op *fuseops.ReadDirOp) (err error) {
	// If the request is for offset zero, we assume that either this is the first
	// call or rewinddir has been called. Reset state.
	if op.Offset == 0 {
		dh.entries = nil
		dh.entriesValid = false
	}

	// Do we need to read entries from GCS?
	if !dh.entriesValid {
		var entries []fuseutil.Dirent
		entries, err = readAllEntries(op.Context(), dh.in)
		if err != nil {
			err = fmt.Errorf("readAllEntries: %v", err)
			return
		}

		dh.entries = entries
		dh.entriesValid = true
	}

	// Is the offset past the end of what we have buffered? If so, this must be
	// an invalid seekdir according to posix.
	index := int(op.Offset)
	if index > len(dh.entries) {
		err = fuse.EINVAL
		return
	}

	// We copy out entries until we run out of entries or space.
	for i := index; i < len(dh.entries); i++ {
		op.Data = fuseutil.AppendDirent(op.Data, dh.entries[i])
		if len(op.Data) > op.Size {
			op.Data = op.Data[:op.Size]
			break
		}
	}

	return
}
<commit_msg>Fix up conflicting names in readdir.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fs

import (
	"fmt"
	"sort"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

// State required for reading from directories.
type dirHandle struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	in *inode.DirInode

	/////////////////////////
	// Mutable state
	/////////////////////////

	Mu syncutil.InvariantMutex

	// All entries in the directory. Populated the first time we need one.
	//
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	//
	// GUARDED_BY(Mu)
	entries []fuseutil.Dirent

	// Has entries yet been populated?
	//
	// INVARIANT: If !entriesValid, then len(entries) == 0
	//
	// GUARDED_BY(Mu)
	entriesValid bool
}

// Create a directory handle that obtains listings from the supplied inode.
func newDirHandle(in *inode.DirInode) (dh *dirHandle) {
	// Set up the basic struct.
	dh = &dirHandle{
		in: in,
	}

	// Set up invariant checking.
	dh.Mu = syncutil.NewInvariantMutex(dh.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Dirents, sorted by name.
type SortedDirents []fuseutil.Dirent

func (p SortedDirents) Len() int           { return len(p) }
func (p SortedDirents) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p SortedDirents) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func (dh *dirHandle) checkInvariants() {
	// INVARIANT: For each i, entries[i+1].Offset == entries[i].Offset + 1
	for i := 0; i < len(dh.entries)-1; i++ {
		if !(dh.entries[i+1].Offset == dh.entries[i].Offset+1) {
			panic(
				fmt.Sprintf(
					"Unexpected offset sequence: %v, %v",
					dh.entries[i].Offset,
					dh.entries[i+1].Offset))
		}
	}

	// INVARIANT: If !entriesValid, then len(entries) == 0
	if !dh.entriesValid && len(dh.entries) != 0 {
		panic("Unexpected non-empty entries slice")
	}
}

// Read some entries from the directory inode. Return newTok == "" (possibly
// with a non-empty list of entries) when the end of the directory has been
// hit.
func readSomeEntries(
	ctx context.Context,
	in *inode.DirInode,
	tok string,
	firstEntryOffset fuseops.DirOffset) (
	entries []fuseutil.Dirent, newTok string, err error) {
	entries, newTok, err = in.ReadEntries(ctx, tok)
	if err != nil {
		err = fmt.Errorf("ReadEntries: %v", err)
		return
	}

	// Return a bogus inode ID for each entry, but not the root inode ID.
	//
	// NOTE(jacobsa): As far as I can tell this is harmless. Minting and
	// returning a real inode ID is difficult because fuse does not count
	// readdir as an operation that increases the inode ID's lookup count and
	// we therefore don't get a forget for it later, but we would like to not
	// have to remember every inode ID that we've ever minted for readdir.
	//
	// If it turns out this is not harmless, we'll need to switch to something
	// like inode IDs based on (object name, generation) hashes. But then what
	// about the birthday problem? And more importantly, what about our
	// semantic of not minting a new inode ID when the generation changes due
	// to a local action?
	for i, _ := range entries {
		entries[i].Inode = fuseops.RootInodeID + 1
	}

	// Fix up the offset of any entries returned.
	for i := 0; i < len(entries); i++ {
		entries[i].Offset = firstEntryOffset + 1 + fuseops.DirOffset(i)
	}

	return
}

// Call readSomeEntries in a loop until the directory is exhausted. Return
// contents sorted by name.
func readAllEntries(
	ctx context.Context,
	in *inode.DirInode) (entries SortedDirents, err error) {
	// Ensure that our result is actually sorted.
	defer func() {
		sort.Sort(entries)
	}()

	// Read in a loop.
	var tok string
	for {
		var batch []fuseutil.Dirent

		// Accumulate some more entries.
		firstOffset := fuseops.DirOffset(len(entries))
		batch, tok, err = readSomeEntries(ctx, in, tok, firstOffset)
		if err != nil {
			return
		}

		entries = append(entries, batch...)

		// Are we done?
		if tok == "" {
			break
		}
	}

	return
}

// Resolve name conflicts between file objects and directory objects (e.g. the
// objects "foo/bar" and "foo/bar/") by appending U+000A, which is illegal in
// GCS object names, to conflicting file names.
func fixConflictingNames(in SortedDirents) (out []fuseutil.Dirent, err error) {
	// Sanity check.
	if !sort.IsSorted(in) {
		err = fmt.Errorf("Expected sorted input")
		return
	}

	// Fix adjacent duplicates.
	for i, e := range in {
		if i > 0 {
			prev := in[i-1]
			if e.Type == fuseutil.DT_File && e.Name == prev.Name {
				e.Name += "\n"
			}
		}

		out = append(out, e)
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Handle a request to read from the directory, without responding.
//
// Special case: we assume that a zero offset indicates that rewinddir has been
// called (since fuse gives us no way to intercept and know for sure), and
// start the listing process over again.
//
// LOCKS_REQUIRED(dh.Mu)
func (dh *dirHandle) ReadDir(
	op *fuseops.ReadDirOp) (err error) {
	// If the request is for offset zero, we assume that either this is the first
	// call or rewinddir has been called. Reset state.
	if op.Offset == 0 {
		dh.entries = nil
		dh.entriesValid = false
	}

	// Do we need to read entries from GCS?
	if !dh.entriesValid {
		// Read entries.
		var entries []fuseutil.Dirent
		entries, err = readAllEntries(op.Context(), dh.in)
		if err != nil {
			err = fmt.Errorf("readAllEntries: %v", err)
			return
		}

		// Fix name conflicts.
		entries, err = fixConflictingNames(entries)
		if err != nil {
			err = fmt.Errorf("fixConflictingNames: %v", err)
			return
		}

		// Update state.
		dh.entries = entries
		dh.entriesValid = true
	}

	// Is the offset past the end of what we have buffered? If so, this must be
	// an invalid seekdir according to posix.
	index := int(op.Offset)
	if index > len(dh.entries) {
		err = fuse.EINVAL
		return
	}

	// We copy out entries until we run out of entries or space.
	for i := index; i < len(dh.entries); i++ {
		op.Data = fuseutil.AppendDirent(op.Data, dh.entries[i])
		if len(op.Data) > op.Size {
			op.Data = op.Data[:op.Size]
			break
		}
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/codegangsta/cli"
	"github.com/kr/pty"
	"github.com/mattn/go-shellwords"
	"github.com/pivotal-golang/archiver/compressor"
	"github.com/pkg/term"

	"github.com/cloudfoundry-incubator/garden"
	gclient "github.com/cloudfoundry-incubator/garden/client"
	gconn "github.com/cloudfoundry-incubator/garden/client/connection"
)

func handleComplete(c *cli.Context) {
	// This will complete if no args are passed
	if c.Args().Present() {
		return
	}

	containers, err := client(c).Containers(nil)
	failIf(err)

	for _, container := range containers {
		fmt.Println(container.Handle())
	}
}

func fail(err error) {
	fmt.Fprintln(os.Stderr, "failed:", err)
	os.Exit(1)
}

func failIf(err error) {
	if err != nil {
		fail(err)
	}
}

func client(c *cli.Context) garden.Client {
	target := c.GlobalString("target")
	return gclient.New(gconn.New("tcp", target))
}

func handle(c *cli.Context) string {
	if len(c.Args()) == 0 {
		fail(errors.New("must provide container handle"))
	}
	return c.Args().First()
}

func main() {
	app := cli.NewApp()
	app.Name = "gaol"
	app.Usage = "a cli for garden"
	app.Version = "0.0.1"
	app.Author = "Chris Brown"
	app.Email = "cbrown@pivotal.io"
	app.EnableBashCompletion = true

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "target, t",
			Value:  "localhost:7777",
			Usage:  "server to which commands are sent",
			EnvVar: "GAOL_TARGET",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:  "ping",
			Usage: "check if the server is running",
			Action: func(c *cli.Context) {
				err := client(c).Ping()
				failIf(err)
			},
		},
		{
			Name:  "create",
			Usage: "create a container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "handle, n",
					Usage: "name to give container",
				},
				cli.StringFlag{
					Name:  "rootfs, r",
					Usage: "rootfs image with which to create the container",
				},
				cli.StringSliceFlag{
					Name:  "env, e",
					Usage: "set environment variables",
					Value: &cli.StringSlice{},
				},
				cli.DurationFlag{
					Name:  "grace, g",
					Usage: "grace time (resetting ttl) of container",
					Value: 5 * time.Minute,
				},
				cli.BoolFlag{
					Name:  "privileged, p",
					Usage: "privileged user in container is privileged in host",
				},
			},
			Action: func(c *cli.Context) {
				handle := c.String("handle")
				grace := c.Duration("grace")
				rootfs := c.String("rootfs")
				env := c.StringSlice("env")
				privileged := c.Bool("privileged")

				container, err := client(c).Create(garden.ContainerSpec{
					Handle:     handle,
					GraceTime:  grace,
					RootFSPath: rootfs,
					Privileged: privileged,
					Env:        env,
				})
				failIf(err)

				fmt.Println(container.Handle())
			},
		},
		{
			Name:         "destroy",
			Usage:        "destroy a container",
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				client := client(c)
				handles := c.Args()

				for _, handle := range handles {
					err := client.Destroy(handle)
					failIf(err)
				}
			},
		},
		{
			Name:  "list",
			Usage: "get a list of running containers",
			Flags: []cli.Flag{
				cli.StringSliceFlag{
					Name:  "properties, p",
					Usage: "filter by properties (name=val)",
					Value: &cli.StringSlice{},
				},
			},
			Action: func(c *cli.Context) {
				properties := garden.Properties{}
				for _, prop := range c.StringSlice("properties") {
					segs := strings.SplitN(prop, "=", 2)
					if len(segs) < 2 {
						fail(errors.New("malformed property pair (must be name=value)"))
					}

					properties[segs[0]] = segs[1]
				}

				containers, err := client(c).Containers(nil)
				failIf(err)

				for _, container := range containers {
					fmt.Println(container.Handle())

					props, _ := container.Properties()
					for k, v := range props {
						fmt.Printf("  %s=%s\n", k, v)
					}
				}
			},
		},
		{
			Name:  "run",
			Usage: "run a command in a container",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "attach, a",
					Usage: "attach to the process after it is started",
				},
				cli.StringFlag{
					Name:  "dir, d",
					Usage: "current working directory of process",
				},
				cli.StringFlag{
					Name:  "user, u",
					Usage: "user to run the process as",
				},
				cli.StringFlag{
					Name:  "command, c",
					Usage: "the command to run",
				},
				cli.StringSliceFlag{
					Name:  "env, e",
					Usage: "set environment variables",
					Value: &cli.StringSlice{},
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				attach := c.Bool("attach")
				dir := c.String("dir")
				user := c.String("user")
				command := c.String("command")
				env := c.StringSlice("env")

				handle := handle(c)
				container, err := client(c).Lookup(handle)
				failIf(err)

				var processIo garden.ProcessIO
				if attach {
					processIo = garden.ProcessIO{
						Stdin:  os.Stdin,
						Stdout: os.Stdout,
						Stderr: os.Stderr,
					}
				} else {
					processIo = garden.ProcessIO{}
				}

				args, err := shellwords.Parse(command)
				failIf(err)

				process, err := container.Run(garden.ProcessSpec{
					Path: args[0],
					Args: args[1:],
					Dir:  dir,
					User: user,
					Env:  env,
				}, processIo)
				failIf(err)

				if attach {
					status, err := process.Wait()
					failIf(err)
					os.Exit(status)
				} else {
					fmt.Println(process.ID())
				}
			},
		},
		{
			Name:  "attach",
			Usage: "attach to command running in the container",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "pid, p",
					Usage: "process id to connect to",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				pid := uint32(c.Int("pid"))
				if pid == 0 {
					err := errors.New("must specify pid to attach to")
					failIf(err)
				}

				handle := handle(c)
				container, err := client(c).Lookup(handle)
				failIf(err)

				process, err := container.Attach(pid, garden.ProcessIO{
					Stdin:  os.Stdin,
					Stdout: os.Stdout,
					Stderr: os.Stderr,
				})
				failIf(err)

				_, err = process.Wait()
				failIf(err)
			},
		},
		{
			Name:  "shell",
			Usage: "open a shell inside the running container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "user, u",
					Usage: "user to open shell as",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				container, err := client(c).Lookup(handle(c))
				failIf(err)

				term, err := term.Open(os.Stdin.Name())
				failIf(err)

				err = term.SetRaw()
				failIf(err)

				rows, cols, err := pty.Getsize(os.Stdin)
				failIf(err)

				process, err := container.Run(garden.ProcessSpec{
					User: c.String("user"),
					Path: "/bin/sh",
					Args: []string{"-l"},
					Env:  []string{"TERM=" + os.Getenv("TERM")},
					TTY: &garden.TTYSpec{
						WindowSize: &garden.WindowSize{
							Rows:    rows,
							Columns: cols,
						},
					},
				}, garden.ProcessIO{
					Stdin:  term,
					Stdout: term,
					Stderr: term,
				})
				if err != nil {
					term.Restore()
					failIf(err)
				}

				resized := make(chan os.Signal, 10)
				signal.Notify(resized, syscall.SIGWINCH)

				go func() {
					for {
						<-resized

						rows, cols, err := pty.Getsize(os.Stdin)
						if err == nil {
							process.SetTTY(garden.TTYSpec{
								WindowSize: &garden.WindowSize{
									Rows:    rows,
									Columns: cols,
								},
							})
						}
					}
				}()

				process.Wait()
				term.Restore()
			},
		},
		{
			Name:  "stream-in",
			Usage: "stream data into the container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "to-file, t",
					Usage: "destination path in the container",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				handle := handle(c)

				dst := c.String("to-file")
				if dst == "" {
					fail(errors.New("missing --to-file argument"))
				}

				container, err := client(c).Lookup(handle)
				failIf(err)

				// perform dance to get correct file names
				tmpDir, err := ioutil.TempDir("", "gaol")
				failIf(err)
				defer os.RemoveAll(tmpDir)

				tmp, err := os.Create(filepath.Join(tmpDir, filepath.Base(dst)))
				failIf(err)

				_, err = io.Copy(tmp, os.Stdin)
				failIf(err)

				err = tmp.Close()
				failIf(err)

				reader, writer := io.Pipe()
				go func(w io.WriteCloser) {
					err := compressor.WriteTar(tmp.Name(), w)
					failIf(err)
					w.Close()
				}(writer)

				streamInSpec := garden.StreamInSpec{
					Path:      filepath.Dir(dst),
					TarStream: reader,
				}
				err = container.StreamIn(streamInSpec)
				failIf(err)
			},
		},
		{
			Name:  "stream-out",
			Usage: "stream data out of the container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "from-file, f",
					Usage: "source path in the container",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				handle := handle(c)

				src := c.String("from-file")
				if src == "" {
					fail(errors.New("missing --from-file argument"))
				}

				container, err := client(c).Lookup(handle)
				failIf(err)

				streamOutSpec := garden.StreamOutSpec{Path: src}
				output, err := container.StreamOut(streamOutSpec)
				failIf(err)

				tr := tar.NewReader(output)
				_, err = tr.Next()
				failIf(err)

				_, err = io.Copy(os.Stdout, tr)
				failIf(err)
			},
		},
		{
			Name:  "net-in",
			Usage: "map a port on the host to a port in the container",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "port, p",
					Usage: "container port",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				target := c.GlobalString("target")
				requestedContainerPort := uint32(c.Int("port"))

				if target == "" {
					fail(errors.New("target must be set"))
				}

				handle := handle(c)
				container, err := client(c).Lookup(handle)
				failIf(err)

				hostPort, _, err := container.NetIn(0, requestedContainerPort)
				failIf(err)

				host, _, err := net.SplitHostPort(target)
				failIf(err)

				fmt.Println(net.JoinHostPort(host, fmt.Sprintf("%d", hostPort)))
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>container creation with bind-mounts<commit_after>package main

import (
	"archive/tar"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/codegangsta/cli"
	"github.com/kr/pty"
	"github.com/mattn/go-shellwords"
	"github.com/pivotal-golang/archiver/compressor"
	"github.com/pkg/term"

	"github.com/cloudfoundry-incubator/garden"
	gclient "github.com/cloudfoundry-incubator/garden/client"
	gconn "github.com/cloudfoundry-incubator/garden/client/connection"
)

func handleComplete(c *cli.Context) {
	// This will complete if no args are passed
	if c.Args().Present() {
		return
	}

	containers, err := client(c).Containers(nil)
	failIf(err)

	for _, container := range containers {
		fmt.Println(container.Handle())
	}
}

func fail(err error) {
	fmt.Fprintln(os.Stderr, "failed:", err)
	os.Exit(1)
}

func failIf(err error) {
	if err != nil {
		fail(err)
	}
}

func client(c *cli.Context) garden.Client {
	target := c.GlobalString("target")
	return gclient.New(gconn.New("tcp", target))
}

func handle(c *cli.Context) string {
	if len(c.Args()) == 0 {
		fail(errors.New("must provide container handle"))
	}
	return c.Args().First()
}

func main() {
	app := cli.NewApp()
	app.Name = "gaol"
	app.Usage = "a cli for garden"
	app.Version = "0.0.1"
	app.Author = "Chris Brown"
	app.Email = "cbrown@pivotal.io"
	app.EnableBashCompletion = true

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "target, t",
			Value:  "localhost:7777",
			Usage:  "server to which commands are sent",
			EnvVar: "GAOL_TARGET",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:  "ping",
			Usage: "check if the server is running",
			Action: func(c *cli.Context) {
				err := client(c).Ping()
				failIf(err)
			},
		},
		{
			Name:  "create",
			Usage: "create a container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "handle, n",
					Usage: "name to give container",
				},
				cli.StringFlag{
					Name:  "rootfs, r",
					Usage: "rootfs image with which to create the container",
				},
				cli.StringSliceFlag{
					Name:  "env, e",
					Usage: "set environment variables",
					Value: &cli.StringSlice{},
				},
				cli.DurationFlag{
					Name:  "grace, g",
					Usage: "grace time (resetting ttl) of container",
					Value: 5 * time.Minute,
				},
				cli.BoolFlag{
					Name:  "privileged, p",
					Usage: "privileged user in container is privileged in host",
				},
				cli.StringSliceFlag{
					Name:  "bind-mount, m",
					Usage: "bind-mount host-path:container-path",
					Value: &cli.StringSlice{},
				},
			},
			Action: func(c *cli.Context) {
				handle := c.String("handle")
				grace := c.Duration("grace")
				rootfs := c.String("rootfs")
				env := c.StringSlice("env")
				privileged := c.Bool("privileged")
				mounts := c.StringSlice("bind-mount")

				var bindMounts []garden.BindMount
				for _, pair := range mounts {
					segs := strings.SplitN(pair, ":", 2)
					if len(segs) != 2 {
						fmt.Fprintf(os.Stderr, "invalid bind-mount segment (must be host-path:container-path): %s", pair)
						os.Exit(1)
					}

					bindMounts = append(bindMounts, garden.BindMount{
						SrcPath: segs[0],
						DstPath: segs[1],
						Mode:    garden.BindMountModeRW,
						Origin:  garden.BindMountOriginHost,
					})
				}

				container, err := client(c).Create(garden.ContainerSpec{
					Handle:     handle,
					GraceTime:  grace,
					RootFSPath: rootfs,
					Privileged: privileged,
					Env:        env,
					BindMounts: bindMounts,
				})
				failIf(err)

				fmt.Println(container.Handle())
			},
		},
		{
			Name:         "destroy",
			Usage:        "destroy a container",
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				client := client(c)
				handles := c.Args()

				for _, handle := range handles {
					err := client.Destroy(handle)
					failIf(err)
				}
			},
		},
		{
			Name:  "list",
			Usage: "get a list of running containers",
			Flags: []cli.Flag{
				cli.StringSliceFlag{
					Name:  "properties, p",
					Usage: "filter by properties (name=val)",
					Value: &cli.StringSlice{},
				},
			},
			Action: func(c *cli.Context) {
				properties := garden.Properties{}
				for _, prop := range c.StringSlice("properties") {
					segs := strings.SplitN(prop, "=", 2)
					if len(segs) < 2 {
						fail(errors.New("malformed property pair (must be name=value)"))
					}

					properties[segs[0]] = segs[1]
				}

				containers, err := client(c).Containers(nil)
				failIf(err)

				for _, container := range containers {
					fmt.Println(container.Handle())

					props, _ := container.Properties()
					for k, v := range props {
						fmt.Printf("  %s=%s\n", k, v)
					}
				}
			},
		},
		{
			Name:  "run",
			Usage: "run a command in a container",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "attach, a",
					Usage: "attach to the process after it is started",
				},
				cli.StringFlag{
					Name:  "dir, d",
					Usage: "current working directory of process",
				},
				cli.StringFlag{
					Name:  "user, u",
					Usage: "user to run the process as",
				},
				cli.StringFlag{
					Name:  "command, c",
					Usage: "the command to run",
				},
				cli.StringSliceFlag{
					Name:  "env, e",
					Usage: "set environment variables",
					Value: &cli.StringSlice{},
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				attach := c.Bool("attach")
				dir := c.String("dir")
				user := c.String("user")
				command := c.String("command")
				env := c.StringSlice("env")

				handle := handle(c)
				container, err := client(c).Lookup(handle)
				failIf(err)

				var processIo garden.ProcessIO
				if attach {
					processIo = garden.ProcessIO{
						Stdin:  os.Stdin,
						Stdout: os.Stdout,
						Stderr: os.Stderr,
					}
				} else {
					processIo = garden.ProcessIO{}
				}

				args, err := shellwords.Parse(command)
				failIf(err)

				process, err := container.Run(garden.ProcessSpec{
					Path: args[0],
					Args: args[1:],
					Dir:  dir,
					User: user,
					Env:  env,
				}, processIo)
				failIf(err)

				if attach {
					status, err := process.Wait()
					failIf(err)
					os.Exit(status)
				} else {
					fmt.Println(process.ID())
				}
			},
		},
		{
			Name:  "attach",
			Usage: "attach to command running in the container",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "pid, p",
					Usage: "process id to connect to",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				pid := uint32(c.Int("pid"))
				if pid == 0 {
					err := errors.New("must specify pid to attach to")
					failIf(err)
				}

				handle := handle(c)
				container, err := client(c).Lookup(handle)
				failIf(err)

				process, err := container.Attach(pid, garden.ProcessIO{
					Stdin:  os.Stdin,
					Stdout: os.Stdout,
					Stderr: os.Stderr,
				})
				failIf(err)

				_, err = process.Wait()
				failIf(err)
			},
		},
		{
			Name:  "shell",
			Usage: "open a shell inside the running container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "user, u",
					Usage: "user to open shell as",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				container, err := client(c).Lookup(handle(c))
				failIf(err)

				term, err := term.Open(os.Stdin.Name())
				failIf(err)

				err = term.SetRaw()
				failIf(err)

				rows, cols, err := pty.Getsize(os.Stdin)
				failIf(err)

				process, err := container.Run(garden.ProcessSpec{
					User: c.String("user"),
					Path: "/bin/sh",
					Args: []string{"-l"},
					Env:  []string{"TERM=" + os.Getenv("TERM")},
					TTY: &garden.TTYSpec{
						WindowSize: &garden.WindowSize{
							Rows:    rows,
							Columns: cols,
						},
					},
				}, garden.ProcessIO{
					Stdin:  term,
					Stdout: term,
					Stderr: term,
				})
				if err != nil {
					term.Restore()
					failIf(err)
				}

				resized := make(chan os.Signal, 10)
				signal.Notify(resized, syscall.SIGWINCH)

				go func() {
					for {
						<-resized

						rows, cols, err := pty.Getsize(os.Stdin)
						if err == nil {
							process.SetTTY(garden.TTYSpec{
								WindowSize: &garden.WindowSize{
									Rows:    rows,
									Columns: cols,
								},
							})
						}
					}
				}()

				process.Wait()
				term.Restore()
			},
		},
		{
			Name:  "stream-in",
			Usage: "stream data into the container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "to-file, t",
					Usage: "destination path in the container",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				handle := handle(c)

				dst := c.String("to-file")
				if dst == "" {
					fail(errors.New("missing --to-file argument"))
				}

				container, err := client(c).Lookup(handle)
				failIf(err)

				// perform dance to get correct file names
				tmpDir, err := ioutil.TempDir("", "gaol")
				failIf(err)
				defer os.RemoveAll(tmpDir)

				tmp, err := os.Create(filepath.Join(tmpDir, filepath.Base(dst)))
				failIf(err)

				_, err = io.Copy(tmp, os.Stdin)
				failIf(err)

				err = tmp.Close()
				failIf(err)

				reader, writer := io.Pipe()
				go func(w io.WriteCloser) {
					err := compressor.WriteTar(tmp.Name(), w)
					failIf(err)
					w.Close()
				}(writer)

				streamInSpec := garden.StreamInSpec{
					Path:      filepath.Dir(dst),
					TarStream: reader,
				}
				err = container.StreamIn(streamInSpec)
				failIf(err)
			},
		},
		{
			Name:  "stream-out",
			Usage: "stream data out of the container",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "from-file, f",
					Usage: "source path in the container",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				handle := handle(c)

				src := c.String("from-file")
				if src == "" {
					fail(errors.New("missing --from-file argument"))
				}

				container, err := client(c).Lookup(handle)
				failIf(err)

				streamOutSpec := garden.StreamOutSpec{Path: src}
				output, err := container.StreamOut(streamOutSpec)
				failIf(err)

				tr := tar.NewReader(output)
				_, err = tr.Next()
				failIf(err)

				_, err = io.Copy(os.Stdout, tr)
				failIf(err)
			},
		},
		{
			Name:  "net-in",
			Usage: "map a port on the host to a port in the container",
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "port, p",
					Usage: "container port",
				},
			},
			BashComplete: handleComplete,
			Action: func(c *cli.Context) {
				target := c.GlobalString("target")
				requestedContainerPort := uint32(c.Int("port"))

				if target == "" {
					fail(errors.New("target must be set"))
				}

				handle := handle(c)
				container, err := client(c).Lookup(handle)
				failIf(err)

				hostPort, _, err := container.NetIn(0, requestedContainerPort)
				failIf(err)

				host, _, err := net.SplitHostPort(target)
				failIf(err)

				fmt.Println(net.JoinHostPort(host, fmt.Sprintf("%d", hostPort)))
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package doublestar

import (
	"io/fs"
	"os"
	"path"
	"strings"
)

// Glob returns the names of all files matching pattern or nil if there is no
// matching file. The syntax of pattern is the same as in Match(). The pattern
// may describe hierarchical names such as usr/*/bin/ed.
//
// Glob ignores file system errors such as I/O errors reading directories.
// The only possible returned error is ErrBadPattern, reporting that the
// pattern is malformed.
//
// Note: this is meant as a drop-in replacement for io/fs.Glob(). Like
// io/fs.Glob(), this function assumes that your pattern uses `/` as the path
// separator even if that's not correct for your OS (like Windows). If you
// aren't sure if that's the case, you can use filepath.ToSlash() on your
// pattern before calling Glob().
//
// Like `io/fs.Glob()`, patterns containing `/./`, `/../`, or starting with `/`
// will return no results and no errors. You can use SplitPattern to divide a
// pattern into a base path (to initialize an `FS` object) and pattern.
//
func Glob(fsys fs.FS, pattern string) ([]string, error) {
	if !ValidatePattern(pattern) {
		return nil, ErrBadPattern
	}
	if hasMidDoubleStar(pattern) {
		// If the pattern has a `**` anywhere but the very end, GlobWalk is more
		// performant because it can get away with less allocations. If the pattern
		// ends in a `**`, both methods are pretty much the same, but Glob has a
		// _very_ slight advantage because of lower function call overhead.
		var matches []string
		err := doGlobWalk(fsys, pattern, true, func(p string, d fs.DirEntry) error {
			matches = append(matches, p)
			return nil
		})
		return matches, err
	}
	return doGlob(fsys, pattern, nil, true)
}

var wildcardReplacer = strings.NewReplacer("\\*", "*", "\\?", "?", "\\[", "[", "\\]", "]", "\\{", "{", "\\}", "}")

func unescapeWildcards(pattern string) string {
	return wildcardReplacer.Replace(pattern)
}

// Does the actual globbin'
func doGlob(fsys fs.FS, pattern string, m []string, firstSegment bool) (matches []string, err error) {
	matches = m
	patternStart := indexMeta(pattern)
	if patternStart == -1 {
		// pattern doesn't contain any meta characters - does a file matching the
		// pattern exist?
		// The pattern may contain escaped wildcard characters for an exact path match.
		path := unescapeWildcards(pattern)
		if exists(fsys, path) {
			matches = append(matches, path)
			return
		} else {
			return
		}
	}

	dir := "."
	splitIdx := lastIndexSlashOrAlt(pattern)
	if splitIdx != -1 {
		if pattern[splitIdx] == '}' {
			openingIdx := indexMatchedOpeningAlt(pattern[:splitIdx])
			if openingIdx == -1 {
				// if there's no matching opening index, technically Match() will treat
				// an unmatched `}` as nothing special, so... we will, too!
				splitIdx = lastIndexSlash(pattern[:splitIdx])
			} else {
				// otherwise, we have to handle the alts:
				return globAlts(fsys, pattern, openingIdx, splitIdx, matches, firstSegment)
			}
		}

		dir = pattern[:splitIdx]
		pattern = pattern[splitIdx+1:]
	}

	// if `splitIdx` is less than `patternStart`, we know `dir` has no meta
	// characters. They would be equal if they are both -1, which means `dir`
	// will be ".", and we know that doesn't have meta characters either.
	if splitIdx <= patternStart {
		return globDir(fsys, dir, pattern, matches, firstSegment)
	}

	var dirs []string
	dirs, err = doGlob(fsys, dir, matches, false)
	if err != nil {
		return
	}
	for _, d := range dirs {
		matches, err = globDir(fsys, d, pattern, matches, firstSegment)
		if err != nil {
			return
		}
	}

	return
}

// handle alts in the glob pattern - `openingIdx` and `closingIdx` are the
// indexes of `{` and `}`, respectively
func globAlts(fsys fs.FS, pattern string, openingIdx, closingIdx int, m []string, firstSegment bool) (matches []string, err error) {
	matches = m

	var dirs []string
	startIdx := 0
	afterIdx := closingIdx + 1
	splitIdx := lastIndexSlashOrAlt(pattern[:openingIdx])
	if splitIdx == -1 || pattern[splitIdx] == '}' {
		// no common prefix
		dirs = []string{""}
	} else {
		// our alts have a common prefix that we can process first
		dirs, err = doGlob(fsys, pattern[:splitIdx], matches, false)
		if err != nil {
			return
		}

		startIdx = splitIdx + 1
	}

	for _, d := range dirs {
		patIdx := openingIdx + 1
		altResultsStartIdx := len(matches)
		thisResultStartIdx := altResultsStartIdx
		for patIdx < closingIdx {
			nextIdx := indexNextAlt(pattern[patIdx:closingIdx], true)
			if nextIdx == -1 {
				nextIdx = closingIdx
			} else {
				nextIdx += patIdx
			}

			alt := buildAlt(d, pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx)
			matches, err = doGlob(fsys, alt, matches, firstSegment)
			if err != nil {
				return
			}

			matchesLen := len(matches)
			if altResultsStartIdx != thisResultStartIdx && thisResultStartIdx != matchesLen {
				// Alts can result in matches that aren't sorted, or, worse, duplicates
				// (consider the trivial pattern `path/to/{a,*}`). Since doGlob returns
				// sorted results, we can do a sort of in-place merge and remove
				// duplicates. But, we only need to do this if this isn't the first alt
				// (ie, `altResultsStartIdx != thisResultsStartIdx`) and if the latest
				// alt actually added some matches (`thisResultStartIdx !=
				// len(matches)`)
				matches = sortAndRemoveDups(matches, altResultsStartIdx, thisResultStartIdx, matchesLen)

				// length of matches may have changed
				thisResultStartIdx = len(matches)
			} else {
				thisResultStartIdx = matchesLen
			}

			patIdx = nextIdx + 1
		}
	}

	return
}

// find files/subdirectories in the given `dir` that match `pattern`
func globDir(fsys fs.FS, dir, pattern string, matches []string, canMatchFiles bool) (m []string, e error) {
	m = matches

	if pattern == "" {
		// pattern can be an empty string if the original pattern ended in a slash,
		// in which case, we should just return dir, but only if it actually exists
		// and it's a directory (or a symlink to a directory)
		if isPathDir(fsys, dir) {
			m = append(m, dir)
		}
		return
	}

	if pattern == "**" {
		m = globDoubleStar(fsys, dir, m, canMatchFiles)
		return
	}

	dirs, err := fs.ReadDir(fsys, dir)
	if err != nil {
		// ignore IO errors
		return
	}

	var matched bool
	for _, info := range dirs {
		name := info.Name()
		if canMatchFiles || isDir(fsys, dir, name, info) {
			matched, e = matchWithSeparator(pattern, name, '/', false)
			if e != nil {
				return
			}
			if matched {
				m = append(m, path.Join(dir, name))
			}
		}
	}

	return
}

func globDoubleStar(fsys fs.FS, dir string, matches []string, canMatchFiles bool) []string {
	dirs, err := fs.ReadDir(fsys, dir)
	if err != nil {
		// ignore IO errors
		return matches
	}

	// `**` can match *this* dir, so add it
	matches = append(matches, dir)
	for _, info := range dirs {
		name := info.Name()
		if isDir(fsys, dir, name, info) {
			matches = globDoubleStar(fsys, path.Join(dir, name), matches, canMatchFiles)
		} else if canMatchFiles {
			matches = append(matches, path.Join(dir, name))
		}
	}

	return matches
}

// Returns true if the pattern has a doublestar in the middle of the pattern.
// In this case, GlobWalk is faster because it can get away with less
// allocations. However, Glob has a _very_ slight edge if the pattern ends in
// `**`.
func hasMidDoubleStar(p string) bool {
	// subtract 3: 2 because we want to return false if the pattern ends in `**`
	// (Glob is _very_ slightly faster in that case), and the extra 1 because our
	// loop checks p[i] and p[i+1].
	l := len(p) - 3
	for i := 0; i < l; i++ {
		if p[i] == '\\' {
			// escape next byte
			i++
		} else if p[i] == '*' && p[i+1] == '*' {
			return true
		}
	}
	return false
}

// Returns the index of the first unescaped meta character, or negative 1.
func indexMeta(s string) int {
	var c byte
	l := len(s)
	for i := 0; i < l; i++ {
		c = s[i]
		if c == '*' || c == '?' || c == '[' || c == '{' {
			return i
		} else if c == '\\' {
			// skip next byte
			i++
		}
	}
	return -1
}

// Returns the index of the last unescaped slash or closing alt (`}`) in the
// string, or negative 1.
func lastIndexSlashOrAlt(s string) int {
	for i := len(s) - 1; i >= 0; i-- {
		if (s[i] == '/' || s[i] == '}') && (i == 0 || s[i-1] != '\\') {
			return i
		}
	}
	return -1
}

// Returns the index of the last unescaped slash in the string, or negative 1.
func lastIndexSlash(s string) int {
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == '/' && (i == 0 || s[i-1] != '\\') {
			return i
		}
	}
	return -1
}

// Assuming the byte after the end of `s` is a closing `}`, this function will
// find the index of the matching `{`. That is, it'll skip over any nested `{}`
// and account for escaping.
func indexMatchedOpeningAlt(s string) int {
	alts := 1
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == '}' && (i == 0 || s[i-1] != '\\') {
			alts++
		} else if s[i] == '{' && (i == 0 || s[i-1] != '\\') {
			if alts--; alts == 0 {
				return i
			}
		}
	}
	return -1
}

// Returns true if the path exists
func exists(fsys fs.FS, name string) bool {
	if _, err := fs.Stat(fsys, name); os.IsNotExist(err) {
		return false
	}
	return true
}

// Returns true if the path is a directory, or a symlink to a directory
func isPathDir(fsys fs.FS, name string) bool {
	info, err := fs.Stat(fsys, name)
	if err != nil {
		return false
	}
	return info.IsDir()
}

// Returns whether or not the given DirEntry is a directory. If the DirEntry
// represents a symbolic link, the link is followed by running fs.Stat() on
// `path.Join(dir, name)`
func isDir(fsys fs.FS, dir string, name string, info fs.DirEntry) bool {
	if (info.Type() & fs.ModeSymlink) > 0 {
		finfo, err := fs.Stat(fsys, path.Join(dir, name))
		if err != nil {
			return false
		}
		return finfo.IsDir()
	}
	return info.IsDir()
}

// Builds a string from an alt
func buildAlt(prefix, pattern string, startIdx, openingIdx, currentIdx, nextIdx, afterIdx int) string {
	// pattern:
	//   ignored/start{alts,go,here}remaining - len = 36
	//           |    |     | |     ^--- afterIdx   = 27
	//           |    |     | \--------- nextIdx    = 21
	//           |    |     \----------- currentIdx = 19
	//           |    \----------------- openingIdx = 13
	//           \---------------------- startIdx   = 8
	//
	// result:
	//   prefix/startgoremaining - len = 7 + 5 + 2 + 9 = 23
	var buf []byte
	patLen := len(pattern)
	size := (openingIdx - startIdx) + (nextIdx - currentIdx) + (patLen - afterIdx)
	if prefix != "" && prefix != "." {
		buf = make([]byte, 0, size+len(prefix)+1)
		buf = append(buf, prefix...)
		buf = append(buf, '/')
	} else {
		buf = make([]byte, 0, size)
	}
	buf = append(buf, pattern[startIdx:openingIdx]...)
	buf = append(buf, pattern[currentIdx:nextIdx]...)
	if afterIdx < patLen {
		buf = append(buf, pattern[afterIdx:]...)
	}
	return string(buf)
}

// Running alts can produce results that are not sorted, and, worse, can cause
// duplicates (consider the trivial pattern `path/to/{a,*}`). Since we know
// each run of doGlob is sorted, we can basically do the "merge" step of a
// merge sort in-place.
func sortAndRemoveDups(matches []string, idx1, idx2, l int) []string {
	var tmp string
	for ; idx1 < idx2; idx1++ {
		if matches[idx1] < matches[idx2] {
			// order is correct
			continue
		} else if matches[idx1] > matches[idx2] {
			// need to swap and then re-sort matches above idx2
			tmp = matches[idx1]
			matches[idx1] = matches[idx2]

			shft := idx2 + 1
			for ; shft < l && matches[shft] < tmp; shft++ {
				matches[shft-1] = matches[shft]
			}
			matches[shft-1] = tmp
		} else {
			// duplicate - shift matches above idx2 down one and decrement l
			for shft := idx2 + 1; shft < l; shft++ {
				matches[shft-1] = matches[shft]
			}
			if l--; idx2 == l {
				// nothing left to do... matches[idx2:] must have been full of dups
				break
			}
		}
	}
	return matches[:l]
}
<commit_msg>Update glob.go<commit_after>package doublestar

import (
	"io/fs"
	"os"
	"path"
	"strings"
)

// Glob returns the names of all files matching pattern or nil if there is no
// matching file. The syntax of pattern is the same as in Match(). The pattern
// may describe hierarchical names such as usr/*/bin/ed.
//
// Glob ignores file system errors such as I/O errors reading directories.
// The only possible returned error is ErrBadPattern, reporting that the
// pattern is malformed.
//
// Note: this is meant as a drop-in replacement for io/fs.Glob(). Like
// io/fs.Glob(), this function assumes that your pattern uses `/` as the path
// separator even if that's not correct for your OS (like Windows). If you
// aren't sure if that's the case, you can use filepath.ToSlash() on your
// pattern before calling Glob().
//
// Like `io/fs.Glob()`, patterns containing `/./`, `/../`, or starting with `/`
// will return no results and no errors. You can use SplitPattern to divide a
// pattern into a base path (to initialize an `FS` object) and pattern.
//
func Glob(fsys fs.FS, pattern string) ([]string, error) {
	if !ValidatePattern(pattern) {
		return nil, ErrBadPattern
	}
	if hasMidDoubleStar(pattern) {
		// If the pattern has a `**` anywhere but the very end, GlobWalk is more
		// performant because it can get away with less allocations. If the pattern
		// ends in a `**`, both methods are pretty much the same, but Glob has a
		// _very_ slight advantage because of lower function call overhead.
		var matches []string
		err := doGlobWalk(fsys, pattern, true, func(p string, d fs.DirEntry) error {
			matches = append(matches, p)
			return nil
		})
		return matches, err
	}
	return doGlob(fsys, pattern, nil, true)
}

var wildcardReplacer = strings.NewReplacer("\\*", "*", "\\?", "?", "\\[", "[", "\\]", "]", "\\{", "{", "\\}", "}")

func unescapeWildcards(pattern string) string {
	return wildcardReplacer.Replace(pattern)
}

// Does the actual globbin'
func doGlob(fsys fs.FS, pattern string, m []string, firstSegment bool) (matches []string, err error) {
	matches = m
	patternStart := indexMeta(pattern)
	if patternStart == -1 {
		// pattern doesn't contain any meta characters - does a file matching the
		// pattern exist?
		// The pattern may contain escaped wildcard characters for an exact path match.
		path := unescapeWildcards(pattern)
		if exists(fsys, path) {
			matches = append(matches, path)
			return
		} else {
			return
		}
	}

	dir := "."
	splitIdx := lastIndexSlashOrAlt(pattern)
	if splitIdx != -1 {
		if pattern[splitIdx] == '}' {
			openingIdx := indexMatchedOpeningAlt(pattern[:splitIdx])
			if openingIdx == -1 {
				// if there's no matching opening index, technically Match() will treat
				// an unmatched `}` as nothing special, so... we will, too!
				splitIdx = lastIndexSlash(pattern[:splitIdx])
			} else {
				// otherwise, we have to handle the alts:
				return globAlts(fsys, pattern, openingIdx, splitIdx, matches, firstSegment)
			}
		}

		dir = pattern[:splitIdx]
		pattern = pattern[splitIdx+1:]
	}

	// if `splitIdx` is less than `patternStart`, we know `dir` has no meta
	// characters. They would be equal if they are both -1, which means `dir`
	// will be ".", and we know that doesn't have meta characters either.
	if splitIdx <= patternStart {
		return globDir(fsys, dir, pattern, matches, firstSegment)
	}

	var dirs []string
	dirs, err = doGlob(fsys, dir, matches, false)
	if err != nil {
		return
	}
	for _, d := range dirs {
		matches, err = globDir(fsys, d, pattern, matches, firstSegment)
		if err != nil {
			return
		}
	}

	return
}

// handle alts in the glob pattern - `openingIdx` and `closingIdx` are the
// indexes of `{` and `}`, respectively
func globAlts(fsys fs.FS, pattern string, openingIdx, closingIdx int, m []string, firstSegment bool) (matches []string, err error) {
	matches = m

	var dirs []string
	startIdx := 0
	afterIdx := closingIdx + 1
	splitIdx := lastIndexSlashOrAlt(pattern[:openingIdx])
	if splitIdx == -1 || pattern[splitIdx] == '}' {
		// no common prefix
		dirs = []string{""}
	} else {
		// our alts have a common prefix that we can process first
		dirs, err = doGlob(fsys, pattern[:splitIdx], matches, false)
		if err != nil {
			return
		}

		startIdx = splitIdx + 1
	}

	for _, d := range dirs {
		patIdx := openingIdx + 1
		altResultsStartIdx := len(matches)
		thisResultStartIdx := altResultsStartIdx
		for patIdx < closingIdx {
			nextIdx := indexNextAlt(pattern[patIdx:closingIdx], true)
			if nextIdx == -1 {
				nextIdx = closingIdx
			} else {
				nextIdx += patIdx
			}

			alt := buildAlt(d, pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx)
			matches, err = doGlob(fsys, alt, matches, firstSegment)
			if err != nil {
				return
			}

			matchesLen := len(matches)
			if altResultsStartIdx != thisResultStartIdx && thisResultStartIdx != matchesLen {
				// Alts can result in matches that aren't sorted, or, worse, duplicates
				// (consider the trivial pattern `path/to/{a,*}`). Since doGlob returns
				// sorted results, we can do a sort of in-place merge and remove
				// duplicates. But, we only need to do this if this isn't the first alt
				// (ie, `altResultsStartIdx != thisResultsStartIdx`) and if the latest
				// alt actually added some matches (`thisResultStartIdx !=
				// len(matches)`)
				matches = sortAndRemoveDups(matches, altResultsStartIdx, thisResultStartIdx, matchesLen)

				// length of matches may have changed
				thisResultStartIdx = len(matches)
			} else {
				thisResultStartIdx = matchesLen
			}

			patIdx = nextIdx + 1
		}
	}

	return
}

// find files/subdirectories in the given `dir` that match `pattern`
func globDir(fsys fs.FS, dir, pattern string, matches []string, canMatchFiles bool) (m []string, e error) {
	m = matches

	if pattern == "" {
		// pattern can be an empty string if the original pattern ended in a slash,
		// in which case, we should just return dir, but only if it actually exists
		// and it's a directory (or a symlink to a directory)
		if isPathDir(fsys, dir) {
			m = append(m, dir)
		}
		return
	}

	if pattern == "**" {
		m = globDoubleStar(fsys, dir, m, canMatchFiles)
		return
	}

	dirs, err := fs.ReadDir(fsys, dir)
	if err != nil {
		// ignore IO errors
		return
	}

	var matched bool
	for _, info := range dirs {
		name := info.Name()
		if canMatchFiles || isDir(fsys, dir, name, info) {
			matched, e = matchWithSeparator(pattern, name, '/', false)
			if e != nil {
				return
			}
			if matched {
				m = append(m, path.Join(dir, name))
			}
		}
	}

	return
}

func globDoubleStar(fsys fs.FS, dir string, matches []string, canMatchFiles bool) []string {
	dirs, err := fs.ReadDir(fsys, dir)
	if err != nil {
		// ignore IO errors
		return matches
	}

	// `**` can match *this* dir, so add it
	matches = append(matches, dir)
	for _, info := range dirs {
		name := info.Name()
		if isDir(fsys, dir, name, info) {
			matches = globDoubleStar(fsys, path.Join(dir, name), matches, canMatchFiles)
		} else if canMatchFiles {
			matches = append(matches, path.Join(dir, name))
		}
	}

	return matches
}

// Returns true if the pattern has a doublestar in the middle of the pattern.
// In this case, GlobWalk is faster because it can get away with less
// allocations. However, Glob has a _very_ slight edge if the pattern ends in
// `**`.
func hasMidDoubleStar(p string) bool {
	// subtract 3: 2 because we want to return false if the pattern ends in `**`
	// (Glob is _very_ slightly faster in that case), and the extra 1 because our
	// loop checks p[i] and p[i+1].
	l := len(p) - 3
	for i := 0; i < l; i++ {
		if p[i] == '\\' {
			// escape next byte
			i++
		} else if p[i] == '*' && p[i+1] == '*' {
			return true
		}
	}
	return false
}

// Returns the index of the first unescaped meta character, or negative 1.
func indexMeta(s string) int {
	var c byte
	l := len(s)
	for i := 0; i < l; i++ {
		c = s[i]
		if c == '*' || c == '?' || c == '[' || c == '{' {
			return i
		} else if c == '\\' {
			// skip next byte
			i++
		}
	}
	return -1
}

// Returns the index of the last unescaped slash or closing alt (`}`) in the
// string, or negative 1.
func lastIndexSlashOrAlt(s string) int {
	for i := len(s) - 1; i >= 0; i-- {
		if (s[i] == '/' || s[i] == '}') && (i == 0 || s[i-1] != '\\') {
			return i
		}
	}
	return -1
}

// Returns the index of the last unescaped slash in the string, or negative 1.
func lastIndexSlash(s string) int {
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == '/' && (i == 0 || s[i-1] != '\\') {
			return i
		}
	}
	return -1
}

// Assuming the byte after the end of `s` is a closing `}`, this function will
// find the index of the matching `{`. That is, it'll skip over any nested `{}`
// and account for escaping.
func indexMatchedOpeningAlt(s string) int {
	alts := 1
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == '}' && (i == 0 || s[i-1] != '\\') {
			alts++
		} else if s[i] == '{' && (i == 0 || s[i-1] != '\\') {
			if alts--; alts == 0 {
				return i
			}
		}
	}
	return -1
}

// Returns true if the path exists
func exists(fsys fs.FS, name string) bool {
	if _, err := fs.Stat(fsys, name); err != nil {
		return false
	}
	return true
}

// Returns true if the path is a directory, or a symlink to a directory
func isPathDir(fsys fs.FS, name string) bool {
	info, err := fs.Stat(fsys, name)
	if err != nil {
		return false
	}
	return info.IsDir()
}

// Returns whether or not the given DirEntry is a directory. If the DirEntry
// represents a symbolic link, the link is followed by running fs.Stat() on
// `path.Join(dir, name)`
func isDir(fsys fs.FS, dir string, name string, info fs.DirEntry) bool {
	if (info.Type() & fs.ModeSymlink) > 0 {
		finfo, err := fs.Stat(fsys, path.Join(dir, name))
		if err != nil {
			return false
		}
		return finfo.IsDir()
	}
	return info.IsDir()
}

// Builds a string from an alt
func buildAlt(prefix, pattern string, startIdx, openingIdx, currentIdx, nextIdx, afterIdx int) string {
	// pattern:
	//   ignored/start{alts,go,here}remaining - len = 36
	//           |    |     | |     ^--- afterIdx   = 27
	//           |    |     | \--------- nextIdx    = 21
	//           |    |     \----------- currentIdx = 19
	//           |    \----------------- openingIdx = 13
	//           \---------------------- startIdx   = 8
	//
	// result:
	//   prefix/startgoremaining - len = 7 + 5 + 2 + 9 = 23
	var buf []byte
	patLen := len(pattern)
	size := (openingIdx - startIdx) + (nextIdx - currentIdx) + (patLen - afterIdx)
	if prefix != "" && prefix != "." {
		buf = make([]byte, 0, size+len(prefix)+1)
		buf = append(buf, prefix...)
		buf = append(buf, '/')
	} else {
		buf = make([]byte, 0, size)
	}
	buf = append(buf, pattern[startIdx:openingIdx]...)
	buf = append(buf, pattern[currentIdx:nextIdx]...)
	if afterIdx < patLen {
		buf = append(buf, pattern[afterIdx:]...)
	}
	return string(buf)
}

// Running alts can produce results that are not sorted, and, worse, can cause
// duplicates (consider the trivial pattern `path/to/{a,*}`). Since we know
// each run of doGlob is sorted, we can basically do the "merge" step of a
// merge sort in-place.
func sortAndRemoveDups(matches []string, idx1, idx2, l int) []string {
	var tmp string
	for ; idx1 < idx2; idx1++ {
		if matches[idx1] < matches[idx2] {
			// order is correct
			continue
		} else if matches[idx1] > matches[idx2] {
			// need to swap and then re-sort matches above idx2
			tmp = matches[idx1]
			matches[idx1] = matches[idx2]

			shft := idx2 + 1
			for ; shft < l && matches[shft] < tmp; shft++ {
				matches[shft-1] = matches[shft]
			}
			matches[shft-1] = tmp
		} else {
			// duplicate - shift matches above idx2 down one and decrement l
			for shft := idx2 + 1; shft < l; shft++ {
				matches[shft-1] = matches[shft]
			}
			if l--; idx2 == l {
				// nothing left to do... matches[idx2:] must have been full of dups
				break
			}
		}
	}
	return matches[:l]
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/nsf/termbox-go"
)

const VERSION = "0.0.1"

type Row struct {
	Text string
}

func NewRow(text string) Row {
	r := Row{}
	r.Text = strings.Replace(text, "\t", "    ", -1)
	return r
}

func (r *Row) DisplayText() string {
	return r.Text
}

func (r *Row) Length() int {
	return len(r.DisplayText())
}

func (r *Row) InsertChar(position int, c rune) {
	line := ""
	if position <= len(r.Text) {
		line += r.Text[0:position]
	} else {
		line += r.Text
	}
	line += string(c)
	if position < len(r.Text) {
		line += r.Text[position+1:]
	}
	r.Text = line
}

type Editor struct {
	Reader     *bufio.Reader
	ScreenRows int
	ScreenCols int
	EditRows   int // actual number of rows used for text editing
	EditCols   int
	CursorRow  int
	CursorCol  int
	Message    string // status message
	Rows       []Row
	RowOffset  int
	ColOffset  int
}

func NewEditor() *Editor {
	e := &Editor{}
	e.Reader = bufio.NewReader(os.Stdin)
	e.Rows = make([]Row, 0)
	return e
}

func (e *Editor) ReadFile(path string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	s := string(b)
	lines := strings.Split(s, "\n")
	e.Rows = make([]Row, 0)
	for _, line := range lines {
		e.Rows = append(e.Rows, NewRow(line))
	}
	return nil
}

func (e *Editor) ProcessKeyPress() error {
	event := termbox.PollEvent()

	e.Message = fmt.Sprintf(" event=%+v", event)

	switch event.Type {
	case termbox.EventKey:
		break // handle these below
	case termbox.EventResize:
		termbox.Flush()
		return nil
	default:
		return nil
	}

	key := event.Key
	if key != 0 {
		switch key {
		case termbox.KeyCtrlQ:
			e.Exit()
			return errors.New("quit")
		case termbox.KeyPgup:
			e.CursorRow = e.RowOffset
			for i := 0; i < e.EditRows; i++ {
				e.MoveCursor(termbox.KeyArrowUp)
			}
		case termbox.KeyPgdn:
			e.CursorRow = e.RowOffset + e.EditRows - 1
			for i := 0; i < e.EditRows; i++ {
				e.MoveCursor(termbox.KeyArrowDown)
			}
		case termbox.KeyCtrlA, termbox.KeyHome:
			e.CursorCol = 0
		case termbox.KeyCtrlE, termbox.KeyEnd:
			e.CursorCol = 0
			if e.CursorRow < len(e.Rows) {
				e.CursorCol = e.Rows[e.CursorRow].Length() - 1
			}
		case termbox.KeyArrowUp, termbox.KeyArrowDown, termbox.KeyArrowLeft, termbox.KeyArrowRight:
			e.MoveCursor(key)
		}
	}
	ch := event.Ch
	if ch != 0 {
		e.InsertChar(ch)
	}
	return nil
}

func (e *Editor) Scroll() {
	if e.CursorRow < e.RowOffset {
		e.RowOffset = e.CursorRow
	}
	if e.CursorRow-e.RowOffset >= e.EditRows {
		e.RowOffset = e.CursorRow - e.EditRows + 1
	}
	if e.CursorCol < e.ColOffset {
		e.ColOffset = e.CursorCol
	}
	if e.CursorCol-e.ColOffset >= e.EditCols {
		e.ColOffset = e.CursorCol - e.EditCols + 1
	}
}

func (e *Editor) RefreshScreen() {
	w, h := termbox.Size()
	e.ScreenRows = h
	e.ScreenCols = w
	e.EditRows = e.ScreenRows - 2
	e.EditCols = e.ScreenCols

	e.Scroll()
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[?25l")...) // hide cursor
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	buffer = e.DrawRows(buffer)

	buffer = append(buffer, []byte(fmt.Sprintf("\x1b[%d;%dH", e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset))...)

	termbox.SetCursor(e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset)

	buffer = append(buffer, []byte("\x1b[?25h")...) // show cursor
	os.Stdout.Write(buffer)
}

func (e *Editor) Exit() {
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[2J")...)   // clear screen
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	os.Stdout.Write(buffer)
}

func (e *Editor) DrawRows(buffer []byte) []byte {
	for y := 0; y < e.ScreenRows; y++ {
		if y == e.ScreenRows-2 {
			buffer = append(buffer, []byte("\x1b[7m")...)
			text := fmt.Sprintf(" %d/%d ", e.CursorRow, len(e.Rows))
			for len(text) < e.ScreenCols-1 {
				text = " " + text
			}
			buffer = append(buffer, []byte(text)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\x1b[m")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else if y == e.ScreenRows-1 {
			text := e.Message
			if len(text) > e.ScreenCols {
				text = text[0:e.ScreenCols]
			}
			buffer = append(buffer, []byte(text)...)
			buffer = append(buffer, []byte("\x1b[K")...)
		} else if (y + e.RowOffset) < len(e.Rows) {
			line := e.Rows[y+e.RowOffset].DisplayText()

			if len(line) > e.ColOffset {
				line = line[e.ColOffset:]
			} else {
				line = ""
			}
			if len(line) > e.ScreenCols {
				line = line[0:e.ScreenCols]
			}
			buffer = append(buffer, []byte(line)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else {
			if y == e.ScreenRows/3 {
				welcome := fmt.Sprintf("goed editor -- version %s", VERSION)
				padding := (e.ScreenCols - len(welcome)) / 2
				buffer = append(buffer, []byte("~")...)
				for i := 1; i <= padding; i++ {
					buffer = append(buffer, []byte(" ")...)
				}
				buffer = append(buffer, []byte(welcome)...)
			} else {
				buffer = append(buffer, []byte("~")...)
			}
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		}
	}
	return buffer
}

func (e *Editor) MoveCursor(key termbox.Key) {

	switch key {
	case termbox.KeyArrowLeft:
		if e.CursorCol > 0 {
			e.CursorCol--
		} else if e.CursorRow > 0 {
			e.CursorRow--
			e.CursorCol = e.Rows[e.CursorRow].Length() - 1
		}
	case termbox.KeyArrowRight:
		if e.CursorRow < len(e.Rows) {
			rowLength := e.Rows[e.CursorRow].Length()
			if e.CursorCol < rowLength-1 {
				e.CursorCol++
			} else if e.CursorRow < len(e.Rows)-1 {
				e.CursorRow++
				e.CursorCol = 0
			}
		}
	case termbox.KeyArrowUp:
		if e.CursorRow > 0 {
			e.CursorRow--
		}
	case termbox.KeyArrowDown:
		if e.CursorRow < len(e.Rows)-1 {
			e.CursorRow++
		}
	}

	if e.CursorRow < len(e.Rows) {
		rowLength := e.Rows[e.CursorRow].Length()
		if e.CursorCol > rowLength-1 {
			e.CursorCol = rowLength - 1
			if e.CursorCol < 0 {
				e.CursorCol = 0
			}
		}
	}

}

func (e *Editor) InsertChar(c rune) {
	e.Rows[e.CursorRow].InsertChar(e.CursorCol, c)
	e.CursorCol += 1
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	e := NewEditor()
	e.ReadFile("goed.go")
	for {
		e.RefreshScreen()
		err = e.ProcessKeyPress()
		if err != nil {
			break
		}
	}
}
<commit_msg>Simplifications<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/nsf/termbox-go"
)

const VERSION = "0.0.1"

// A row of text in the editor
type Row struct {
	Text string
}

func NewRow(text string) Row {
	r := Row{}
	r.Text = strings.Replace(text, "\t", "    ", -1)
	return r
}

func (r *Row) DisplayText() string {
	return r.Text
}

func (r *Row) Length() int {
	return len(r.DisplayText())
}

func (r *Row) InsertChar(position int, c rune) {
	line := ""
	if position <= len(r.Text) {
		line += r.Text[0:position]
	} else {
		line += r.Text
	}
	line += string(c)
	if position < len(r.Text) {
		line += r.Text[position+1:]
	}
	r.Text = line
}

// The Editor
type Editor struct {
	ScreenRows int
	ScreenCols int
	EditRows   int // actual number of rows used for text editing
	EditCols   int
	CursorRow  int
	CursorCol  int
	Message    string // status message
	Rows       []Row
	RowOffset  int
	ColOffset  int
	FileName   string
	Running    bool
}

func NewEditor() *Editor {
	e := &Editor{}
	e.Rows = make([]Row, 0)
	e.Running = true
	return e
}

func (e *Editor) ReadFile(path string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	s := string(b)
	lines := strings.Split(s, "\n")
	e.Rows = make([]Row, 0)
	for _, line := range lines {
		e.Rows = append(e.Rows, NewRow(line))
	}
	e.FileName = path
	return nil
}

func (e *Editor) ProcessNextEvent() error {
	event := termbox.PollEvent()

	e.Message = fmt.Sprintf("event=%+v", event)

	switch event.Type {
	case termbox.EventResize:
		termbox.Flush()
		return nil
	case termbox.EventKey:
		break // handle these below
	default:
		return nil
	}

	key := event.Key
	if key != 0 {
		switch key {
		case termbox.KeyCtrlQ:
			e.Running = false
		case termbox.KeyPgup:
			e.CursorRow = e.RowOffset
			for i := 0; i < e.EditRows; i++ {
				e.MoveCursor(termbox.KeyArrowUp)
			}
		case termbox.KeyPgdn:
			e.CursorRow = e.RowOffset + e.EditRows - 1
			for i := 0; i < e.EditRows; i++ {
				e.MoveCursor(termbox.KeyArrowDown)
			}
		case termbox.KeyCtrlA, termbox.KeyHome:
			e.CursorCol = 0
		case termbox.KeyCtrlE, termbox.KeyEnd:
			e.CursorCol = 0
			if e.CursorRow < len(e.Rows) {
				e.CursorCol = e.Rows[e.CursorRow].Length() - 1
			}
		case termbox.KeyArrowUp, termbox.KeyArrowDown, termbox.KeyArrowLeft, termbox.KeyArrowRight:
			e.MoveCursor(key)
		}
	}
	ch := event.Ch
	if ch != 0 {
		e.InsertChar(ch)
	}
	return nil
}

func (e *Editor) Scroll() {
	if e.CursorRow < e.RowOffset {
		e.RowOffset = e.CursorRow
	}
	if e.CursorRow-e.RowOffset >= e.EditRows {
		e.RowOffset = e.CursorRow - e.EditRows + 1
	}
	if e.CursorCol < e.ColOffset {
		e.ColOffset = e.CursorCol
	}
	if e.CursorCol-e.ColOffset >= e.EditCols {
		e.ColOffset = e.CursorCol - e.EditCols + 1
	}
}

func (e *Editor) DrawScreen() {
	w, h := termbox.Size()
	e.ScreenRows = h
	e.ScreenCols = w
	e.EditRows = e.ScreenRows - 2
	e.EditCols = e.ScreenCols

	e.Scroll()
	buffer := make([]byte, 0)
	buffer = append(buffer, []byte("\x1b[?25l")...) // hide cursor
	buffer = append(buffer, []byte("\x1b[1;1H")...) // move cursor to row 1, col 1
	buffer = e.DrawRows(buffer)

	buffer = append(buffer, []byte(fmt.Sprintf("\x1b[%d;%dH", e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset))...)

	termbox.SetCursor(e.CursorRow+1-e.RowOffset, e.CursorCol+1-e.ColOffset)

	buffer = append(buffer, []byte("\x1b[?25h")...) // show cursor
	os.Stdout.Write(buffer)
}

func (e *Editor) DrawRows(buffer []byte) []byte {
	for y := 0; y < e.ScreenRows; y++ {
		if y == e.ScreenRows-2 {
			// draw status bar
			buffer = append(buffer, []byte("\x1b[7m")...)
			finalText := fmt.Sprintf(" %d/%d ", e.CursorRow, len(e.Rows))
			text := " " + e.FileName + " "
			for len(text) < e.ScreenCols-len(finalText)-1 {
				text = text + " "
			}
			text += finalText
			buffer = append(buffer, []byte(text)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\x1b[m")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else if y == e.ScreenRows-1 {
			// draw message bar
			text := e.Message
			if len(text) > e.ScreenCols {
				text = text[0:e.ScreenCols]
			}
			buffer = append(buffer, []byte(text)...)
			buffer = append(buffer, []byte("\x1b[K")...)
		} else if (y + e.RowOffset) < len(e.Rows) {
			// draw editor text
			line := e.Rows[y+e.RowOffset].DisplayText()
			if e.ColOffset < len(line) {
				line = line[e.ColOffset:]
			} else {
				line = ""
			}
			if len(line) > e.ScreenCols {
				line = line[0:e.ScreenCols]
			}
			buffer = append(buffer, []byte(line)...)
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		} else {
			if y == e.ScreenRows/3 {
				welcome := fmt.Sprintf("goed editor -- version %s", VERSION)
				padding := (e.ScreenCols - len(welcome)) / 2
				buffer = append(buffer, []byte("~")...)
				for i := 1; i <= padding; i++ {
					buffer = append(buffer, []byte(" ")...)
				}
				buffer = append(buffer, []byte(welcome)...)
			} else {
				buffer = append(buffer, []byte("~")...)
			}
			buffer = append(buffer, []byte("\x1b[K")...)
			buffer = append(buffer, []byte("\r\n")...)
		}
	}
	return buffer
}

func (e *Editor) MoveCursor(key termbox.Key) {
	switch key {
	case termbox.KeyArrowLeft:
		if e.CursorCol > 0 {
			e.CursorCol--
		} else if e.CursorRow > 0 {
			// wrap around
			e.CursorRow--
			e.CursorCol = e.Rows[e.CursorRow].Length() - 1
		}
	case termbox.KeyArrowRight:
		if e.CursorRow < len(e.Rows) {
			rowLength := e.Rows[e.CursorRow].Length()
			if e.CursorCol < rowLength-1 {
				e.CursorCol++
			} else if e.CursorRow < len(e.Rows)-1 {
				// wrap around
				e.CursorRow++
				e.CursorCol = 0
			}
		}
	case termbox.KeyArrowUp:
		if e.CursorRow > 0 {
			e.CursorRow--
		}
	case termbox.KeyArrowDown:
		if e.CursorRow < len(e.Rows)-1 {
			e.CursorRow++
		}
	}
	// don't go past the end of the current line
	if e.CursorRow < len(e.Rows) {
		rowLength := e.Rows[e.CursorRow].Length()
		if e.CursorCol > rowLength-1 {
			e.CursorCol = rowLength - 1
			if e.CursorCol < 0 {
				e.CursorCol = 0
			}
		}
	}
}

func (e *Editor) InsertChar(c rune) {
	e.Rows[e.CursorRow].InsertChar(e.CursorCol, c)
	e.CursorCol += 1
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	e := NewEditor()
	e.ReadFile("goed.go")
	for e.Running {
		e.DrawScreen()
		e.ProcessNextEvent()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"regexp"
)

var out io.Writer

func init() {
	out = os.Stdout
}

// Does the grepping
func grep(r io.Reader, re *regexp.Regexp, opt *grepOpt) (int, error) {
	buf := bufio.NewReader(r)
	n := 1
	count := 0
	for {
		b, _, err := buf.ReadLine()
		if err != nil {
			if err == io.EOF {
				return count, nil
			}
			return 0, err
		}
		line := string(b)
		if re.MatchString(line) {
			count++
			if opt.optFilename {
				fmt.Fprintf(out, "%s:", opt.filename)
			}
			if opt.optNumber {
				fmt.Fprintf(out, "%d:", n)
			}
			fmt.Fprintln(out, line)
		}
		n++
	}
	return count, nil
}

<commit_msg>introduce MultiMatcher interface and add implementation of string<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	// "regexp"
)

var out io.Writer

func init() {
	out = os.Stdout
}

// Does the grepping
func grep(r io.Reader, m MultiMatcher, opt *grepOpt) (int, error) {
	buf := bufio.NewReader(r)
	n := 1
	count := 0
	for {
		b, _, err := buf.ReadLine()
		if err != nil {
			if err == io.EOF {
				return count, nil
			}
			return 0, err
		}
		line := string(b)
		if m.MatchString(line) {
			count++
			if opt.optFilename {
				fmt.Fprintf(out, "%s:", opt.filename)
			}
			if opt.optNumber {
				fmt.Fprintf(out, "%d:", n)
			}
			fmt.Fprintln(out, line)
		}
		n++
	}
	return count, nil
}

<|endoftext|>