<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

import "syscall"

// An operating-system independent representation of Unix data structures.
// OS-specific routines in this directory convert the OS-local versions to these.

// Getpagesize returns the underlying system's memory page size.
func Getpagesize() int { return syscall.Getpagesize() }

// A FileInfo describes a file and is returned by Stat, Fstat, and Lstat
type FileInfo struct {
	Dev             uint64 // device number of file system holding file.
	Ino             uint64 // inode number.
	Nlink           uint64 // number of hard links.
	Mode            uint32 // permission and mode bits.
	Uid             int    // user id of owner.
	Gid             int    // group id of owner.
	Rdev            uint64 // device type for special file.
	Size            int64  // length in bytes.
	Blksize         int64  // size of blocks, in bytes.
	Blocks          int64  // number of blocks allocated for file.
	Atime_ns        int64  // access time; nanoseconds since epoch.
	Mtime_ns        int64  // modified time; nanoseconds since epoch.
	Ctime_ns        int64  // status change time; nanoseconds since epoch.
	Name            string // name of file as presented to Open.
	FollowedSymlink bool   // followed a symlink to get this information
}

// IsFifo reports whether the FileInfo describes a FIFO file.
func (f *FileInfo) IsFifo() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFIFO }

// IsChar reports whether the FileInfo describes a character special file.
func (f *FileInfo) IsChar() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFCHR }

// IsDirectory reports whether the FileInfo describes a directory.
func (f *FileInfo) IsDirectory() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFDIR }

// IsBlock reports whether the FileInfo describes a block special file.
func (f *FileInfo) IsBlock() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFBLK }

// IsRegular reports whether the FileInfo describes a regular file.
func (f *FileInfo) IsRegular() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFREG }

// IsSymlink reports whether the FileInfo describes a symbolic link.
func (f *FileInfo) IsSymlink() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFLNK }

// IsSocket reports whether the FileInfo describes a socket.
func (f *FileInfo) IsSocket() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFSOCK }

// Permission returns the file permission bits.
func (f *FileInfo) Permission() uint32 { return f.Mode & 0777 }
<commit_msg>os: fix documentation for FileInfo.Name. It's the base name, not the full name. Fixes issue 2047.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

import "syscall"

// An operating-system independent representation of Unix data structures.
// OS-specific routines in this directory convert the OS-local versions to these.

// Getpagesize returns the underlying system's memory page size.
func Getpagesize() int { return syscall.Getpagesize() }

// A FileInfo describes a file and is returned by Stat, Fstat, and Lstat
type FileInfo struct {
	Dev             uint64 // device number of file system holding file.
	Ino             uint64 // inode number.
	Nlink           uint64 // number of hard links.
	Mode            uint32 // permission and mode bits.
	Uid             int    // user id of owner.
	Gid             int    // group id of owner.
	Rdev            uint64 // device type for special file.
	Size            int64  // length in bytes.
	Blksize         int64  // size of blocks, in bytes.
	Blocks          int64  // number of blocks allocated for file.
	Atime_ns        int64  // access time; nanoseconds since epoch.
	Mtime_ns        int64  // modified time; nanoseconds since epoch.
	Ctime_ns        int64  // status change time; nanoseconds since epoch.
	Name            string // base name of the file name provided in Open, Stat, etc.
	FollowedSymlink bool   // followed a symlink to get this information
}

// IsFifo reports whether the FileInfo describes a FIFO file.
func (f *FileInfo) IsFifo() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFIFO }

// IsChar reports whether the FileInfo describes a character special file.
func (f *FileInfo) IsChar() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFCHR }

// IsDirectory reports whether the FileInfo describes a directory.
func (f *FileInfo) IsDirectory() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFDIR }

// IsBlock reports whether the FileInfo describes a block special file.
func (f *FileInfo) IsBlock() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFBLK }

// IsRegular reports whether the FileInfo describes a regular file.
func (f *FileInfo) IsRegular() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFREG }

// IsSymlink reports whether the FileInfo describes a symbolic link.
func (f *FileInfo) IsSymlink() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFLNK }

// IsSocket reports whether the FileInfo describes a socket.
func (f *FileInfo) IsSocket() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFSOCK }

// Permission returns the file permission bits.
func (f *FileInfo) Permission() uint32 { return f.Mode & 0777 }
<|endoftext|>


<commit_before>package weather

import (
	"io"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

const (
	WOEID_BOSTON = "2367105"

	YWeatherEndpoint = "https://weather.yahooapis.com/forecastrss?w="
)

func fetch() error {
	resp, err := http.Get(YWeatherEndpoint + WOEID_BOSTON)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	file, err := os.Create(os.Getenv("HOME") + "/.cache/weather")
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = io.Copy(file, resp.Body)
	return err
}

func show() string {
	cmd := exec.Command("show-weather.sh")
	out, _ := cmd.Output()
	return strings.TrimSpace(string(out))
}

func Monitor() <-chan string {
	ch := make(chan string, 1)
	fetch()
	go func() {
		for {
			ch <- show()
			time.Sleep(20 * time.Minute)
			fetch()
		}
	}()
	return ch
}
<commit_msg>Some re-working of the retry behavior<commit_after>package weather

import (
	"errors"
	"io"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"fmt"
)

var (
	httpNotOK = errors.New("HTTP response was not 200 OK")
)

const (
	WOEID_BOSTON = "2367105"

	YWeatherEndpoint = "https://weather.yahooapis.com/forecastrss?w="
)

func fetch() error {
	resp, err := http.Get(YWeatherEndpoint + WOEID_BOSTON)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return httpNotOK
	}
	defer resp.Body.Close()
	file, err := os.Create(os.Getenv("HOME") + "/.cache/weather")
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = io.Copy(file, resp.Body)
	return err
}

func show() string {
	cmd := exec.Command("show-weather.sh")
	out, _ := cmd.Output()
	return strings.TrimSpace(string(out))
}

func Monitor() <-chan string {
	ch := make(chan string, 1)
	fetch()
	go monitor(ch)
	return ch
}

func monitor(ch chan<- string) {
	for {
		backoff := time.Second
		err := fetch()
		for err != nil {
			fmt.Println(err)
			backoff *= 2
			if backoff > time.Minute / 2 {
				backoff = time.Minute / 2
			}
			fmt.Println("backoff = ", backoff)
			time.Sleep(backoff)
			err = fetch()
		}
		ch <- show()
		time.Sleep(20 * time.Minute)
	}

}
<|endoftext|>


<commit_before>package ninchat

const (
	maxKeepaliveInterval = second * 56
	minReceiveTimeout    = second * 64
)

// webSocketTransport runs a reconnect loop for a given host.  It doesn't mind
// disconnections, but a failure to connect and establish a session stops the
// loop.
func webSocketTransport(s *Session, host string) (connWorked, gotOnline bool) {
	var ws *webSocket

	defer func() {
		if ws != nil {
			ws.close()
		}
	}()

	for {
		gotOnline = false
		hostHealthy := false

		s.log("connecting to", host)

		ws = newWebSocket("wss://"+host+socketPath, jitterDuration(connectTimeout, 0.1))

		select {
		case _, connected := <-ws.notify:
			if connected {
				s.log("connected")
				s.connState("connected")

				connWorked = true
				gotOnline, hostHealthy = webSocketHandshake(s, ws)
			} else {
				s.log("connection failed:", ws.err)
			}

		case <-s.closeNotify:
		}

		goingAway := ws.goingAway

		ws.close()
		ws = nil

		if goingAway {
			s.log("disconnected (server is going away)")
		} else {
			s.log("disconnected")
		}

		if !gotOnline || !hostHealthy || !s.running || goingAway {
			return
		}
	}
}

// webSocketHandshake creates or resumes a session, and runs I/O loops after
// that.
func webSocketHandshake(s *Session, ws *webSocket) (gotOnline, hostHealthy bool) {
	var params map[string]interface{}

	if s.sessionId == nil {
		s.log("session creation")
		params = s.makeCreateSessionAction()
	} else {
		s.log("session resumption")
		params = s.makeResumeSessionAction(true)
	}

	if err := ws.sendJSON(params); err != nil {
		s.log("send:", err)
	}

	if s.sessionId == nil {
		var params map[string]interface{}

		connected := true
		timer := newTimer(jitterDuration(sessionCreateTimeout, 0.2))

		for {
			var err error

			if params, err = ws.receiveJSON(); err != nil {
				s.log("session creation:", err)
				return
			}
			if params != nil {
				timer.Stop()
				break
			}

			if !connected {
				s.log("disconnected during session creation:", ws.err)
				timer.Stop()
				return
			}

			select {
			case _, connected = <-ws.notify:

			case <-timer.C:
				s.log("session creation timeout")
				return
			}
		}

		if !s.handleSessionEvent(params) {
			return
		}

		gotOnline = true
		hostHealthy = true
		s.connActive()
	}

	fail := make(chan struct{}, 1)
	done := make(chan struct{})

	go webSocketSend(s, ws, fail, done)

	gotEvents, hostHealthy := webSocketReceive(s, ws, fail)
	if gotEvents {
		gotOnline = true
	}

	<-done

	return
}

// webSocketSend sends buffered actions and acknowledges events received by
// webSocketReceive.  It makes sure that something is sent to the server every
// now and then.
func webSocketSend(s *Session, ws *webSocket, fail chan struct{}, done chan<- struct{}) {
	defer func() {
		done <- struct{}{}
	}()

	keeper := newTimer(jitterDuration(maxKeepaliveInterval, -0.3))
	defer keeper.Stop()

	s.numSent = 0

	for {
		for s.numSent < len(s.sendBuffer) {
			action := s.sendBuffer[s.numSent]

			if action.Payload != nil {
				action.Params["frames"] = len(action.Payload)
			}

			if s.receivedEventId != s.ackedEventId {
				action.Params["event_id"] = s.receivedEventId

				s.sendEventAck = false
				s.ackedEventId = s.receivedEventId
			}

			err := ws.sendJSON(action.Params)

			delete(action.Params, "frames")
			delete(action.Params, "event_id")

			if err != nil {
				s.log("send:", err)
				fail <- struct{}{}
				return
			}

			if action.Payload != nil {
				if err := ws.sendPayload(action); err != nil {
					s.log("send:", err)
					fail <- struct{}{}
					return
				}
			}

			if action.id == 0 {
				s.sendBuffer = append(s.sendBuffer[:s.numSent], s.sendBuffer[s.numSent+1:]...)
			} else {
				s.numSent++
			}

			keeper.Reset(jitterDuration(maxKeepaliveInterval, -0.3))
		}

		if s.sendEventAck && s.receivedEventId != s.ackedEventId {
			action := s.makeResumeSessionAction(false)

			if err := ws.sendJSON(action); err != nil {
				s.log("send:", err)
				fail <- struct{}{}
				return
			}
		}

		select {
		case _, sending := <-s.sendNotify:
			if !sending {
				closeSession := map[string]interface{}{
					"action": "close_session",
				}

				if err := ws.sendJSON(closeSession); err != nil {
					s.log("send:", err)
				}

				return
			}

		case <-keeper.C:
			// empty keepalive frame between actions
			if err := ws.send(emptyData()); err != nil {
				s.log("send:", err)
				fail <- struct{}{}
				return
			}

			keeper.Reset(jitterDuration(maxKeepaliveInterval, -0.3))

		case <-fail:
			return
		}
	}
}

// webSocketReceive receives events.  It stops if the server doesn't send
// anything for some time.
func webSocketReceive(s *Session, ws *webSocket, fail chan struct{}) (gotEvents, hostHealthy bool) {
	var event *Event
	var frames int

	watchdog := newTimer(jitterDuration(minReceiveTimeout, 0.3))
	defer watchdog.Stop()

	acker := newTimer(-1)
	defer acker.Stop()

	for {
		var ackNeeded bool

		for {
			if event == nil {
				data := ws.receive()
				if data == nil {
					break
				}

				watchdog.Reset(jitterDuration(minReceiveTimeout, 0.7))
				s.connActive()

				text := stringData(data)
				if dataLength(text) == 0 {
					// keepalive frame
					continue
				}

				var params map[string]interface{}

				if err := jsonUnmarshalObject(text, &params); err != nil {
					s.log("receive:", err)
					hostHealthy = false
					fail <- struct{}{}
					return
				}

				event = &Event{
					Params: params,
				}

				if n, _ := event.Int("frames"); n > 0 {
					frames = n
				}
			} else {
				data := ws.receive()
				if data == nil {
					break
				}

				event.Payload = append(event.Payload, data)
				frames--
			}

			if frames == 0 {
				_, sessionLost, needsAck, ok := s.handleEvent(event)
				if !ok {
					if sessionLost {
						gotEvents = true
					} else {
						hostHealthy = false
					}

					fail <- struct{}{}
					return
				}

				if needsAck {
					ackNeeded = true
				}

				event = nil
				frames = 0

				gotEvents = true
				hostHealthy = true
			}

			select {
			case <-s.closeNotify:
				return

			case <-fail:
				return

			default:
				// don't wait
			}
		}

		if ackNeeded && !acker.Active() {
			acker.Reset(jitterDuration(maxEventAckDelay, -0.3))
		}

		select {
		case _, connected := <-ws.notify:
			if !connected {
				s.log("receive:", ws.err)
				fail <- struct{}{}
				return
			}

		case <-watchdog.C:
			s.log("receive timeout")
			fail <- struct{}{}
			return

		case <-acker.C:
			if !s.sendEventAck && s.ackedEventId != s.receivedEventId {
				s.sendAck()
			}

		case <-s.closeNotify:
			return

		case <-fail:
			return
		}
	}
}
<commit_msg>websocket: consume all buffered events when disconnected<commit_after>package ninchat

const (
	maxKeepaliveInterval = second * 56
	minReceiveTimeout    = second * 64
)

// webSocketTransport runs a reconnect loop for a given host.  It doesn't mind
// disconnections, but a failure to connect and establish a session stops the
// loop.
func webSocketTransport(s *Session, host string) (connWorked, gotOnline bool) {
	var ws *webSocket

	defer func() {
		if ws != nil {
			ws.close()
		}
	}()

	for {
		gotOnline = false
		hostHealthy := false

		s.log("connecting to", host)

		ws = newWebSocket("wss://"+host+socketPath, jitterDuration(connectTimeout, 0.1))

		select {
		case _, connected := <-ws.notify:
			if connected {
				s.log("connected")
				s.connState("connected")

				connWorked = true
				gotOnline, hostHealthy = webSocketHandshake(s, ws)
			} else if ws.err != nil {
				s.log("connection failed:", ws.err)
			} else {
				s.log("connection failed")
			}

		case <-s.closeNotify:
		}

		goingAway := ws.goingAway

		ws.close()
		ws = nil

		if goingAway {
			s.log("disconnected (server is going away)")
		} else {
			s.log("disconnected")
		}

		if !gotOnline || !hostHealthy || !s.running || goingAway {
			return
		}
	}
}

// webSocketHandshake creates or resumes a session, and runs I/O loops after
// that.
func webSocketHandshake(s *Session, ws *webSocket) (gotOnline, hostHealthy bool) {
	var params map[string]interface{}

	if s.sessionId == nil {
		s.log("session creation")
		params = s.makeCreateSessionAction()
	} else {
		s.log("session resumption")
		params = s.makeResumeSessionAction(true)
	}

	if err := ws.sendJSON(params); err != nil {
		s.log("send:", err)
	}

	if s.sessionId == nil {
		var params map[string]interface{}

		connected := true
		timer := newTimer(jitterDuration(sessionCreateTimeout, 0.2))

		for {
			var err error

			if params, err = ws.receiveJSON(); err != nil {
				s.log("session creation:", err)
				return
			}
			if params != nil {
				timer.Stop()
				break
			}

			if !connected {
				s.log("disconnected during session creation:", ws.err)
				timer.Stop()
				return
			}

			select {
			case _, connected = <-ws.notify:

			case <-timer.C:
				s.log("session creation timeout")
				return
			}
		}

		if !s.handleSessionEvent(params) {
			return
		}

		gotOnline = true
		hostHealthy = true
		s.connActive()
	}

	fail := make(chan struct{}, 1)
	done := make(chan struct{})

	go webSocketSend(s, ws, fail, done)

	gotEvents, hostHealthy := webSocketReceive(s, ws, fail)
	if gotEvents {
		gotOnline = true
	}

	<-done

	return
}

// webSocketSend sends buffered actions and acknowledges events received by
// webSocketReceive.  It makes sure that something is sent to the server every
// now and then.
func webSocketSend(s *Session, ws *webSocket, fail chan struct{}, done chan<- struct{}) {
	defer func() {
		done <- struct{}{}
	}()

	keeper := newTimer(jitterDuration(maxKeepaliveInterval, -0.3))
	defer keeper.Stop()

	s.numSent = 0

	for {
		for s.numSent < len(s.sendBuffer) {
			action := s.sendBuffer[s.numSent]

			if action.Payload != nil {
				action.Params["frames"] = len(action.Payload)
			}

			if s.receivedEventId != s.ackedEventId {
				action.Params["event_id"] = s.receivedEventId

				s.sendEventAck = false
				s.ackedEventId = s.receivedEventId
			}

			err := ws.sendJSON(action.Params)

			delete(action.Params, "frames")
			delete(action.Params, "event_id")

			if err != nil {
				s.log("send:", err)
				fail <- struct{}{}
				return
			}

			if action.Payload != nil {
				if err := ws.sendPayload(action); err != nil {
					s.log("send:", err)
					fail <- struct{}{}
					return
				}
			}

			if action.id == 0 {
				s.sendBuffer = append(s.sendBuffer[:s.numSent], s.sendBuffer[s.numSent+1:]...)
			} else {
				s.numSent++
			}

			keeper.Reset(jitterDuration(maxKeepaliveInterval, -0.3))
		}

		if s.sendEventAck && s.receivedEventId != s.ackedEventId {
			action := s.makeResumeSessionAction(false)

			if err := ws.sendJSON(action); err != nil {
				s.log("send:", err)
				fail <- struct{}{}
				return
			}
		}

		select {
		case _, sending := <-s.sendNotify:
			if !sending {
				closeSession := map[string]interface{}{
					"action": "close_session",
				}

				if err := ws.sendJSON(closeSession); err != nil {
					s.log("send:", err)
				}

				return
			}

		case <-keeper.C:
			// empty keepalive frame between actions
			if err := ws.send(emptyData()); err != nil {
				s.log("send:", err)
				fail <- struct{}{}
				return
			}

			keeper.Reset(jitterDuration(maxKeepaliveInterval, -0.3))

		case <-fail:
			return
		}
	}
}

// webSocketReceive receives events.  It stops if the server doesn't send
// anything for some time.
func webSocketReceive(s *Session, ws *webSocket, fail chan struct{}) (gotEvents, hostHealthy bool) {
	wsNotify := ws.notify
	connected := true

	var event *Event
	var frames int

	watchdog := newTimer(jitterDuration(minReceiveTimeout, 0.3))
	defer watchdog.Stop()

	acker := newTimer(-1)
	defer acker.Stop()

	for {
		var ackNeeded bool

		for {
			if event == nil {
				data := ws.receive()
				if data == nil {
					break
				}

				watchdog.Reset(jitterDuration(minReceiveTimeout, 0.7))
				s.connActive()

				text := stringData(data)
				if dataLength(text) == 0 {
					// keepalive frame
					continue
				}

				var params map[string]interface{}

				if err := jsonUnmarshalObject(text, &params); err != nil {
					s.log("receive:", err)
					hostHealthy = false
					fail <- struct{}{}
					return
				}

				event = &Event{
					Params: params,
				}

				if n, _ := event.Int("frames"); n > 0 {
					frames = n
				}
			} else {
				data := ws.receive()
				if data == nil {
					if !connected {
						// disconnected before complete payload was sent
						event = nil
						frames = 0
					}
					break
				}

				event.Payload = append(event.Payload, data)
				frames--
			}

			if frames == 0 {
				_, sessionLost, needsAck, ok := s.handleEvent(event)
				if !ok {
					if sessionLost {
						gotEvents = true
					} else {
						hostHealthy = false
					}

					fail <- struct{}{}
					return
				}

				if needsAck {
					ackNeeded = true
				}

				event = nil
				frames = 0

				gotEvents = true
				hostHealthy = true
			}

			select {
			case <-s.closeNotify:
				return

			case <-fail:
				return

			default:
				// don't wait
			}
		}

		if !connected && event == nil {
			if ws.err != nil {
				s.log("receive:", ws.err)
			} else {
				s.log("receive disconnect")
			}
			fail <- struct{}{}
			return
		}

		if ackNeeded && !acker.Active() {
			acker.Reset(jitterDuration(maxEventAckDelay, -0.3))
		}

		select {
		case _, connected = <-wsNotify:
			if !connected {
				wsNotify = nil
			}

		case <-watchdog.C:
			s.log("receive timeout")
			fail <- struct{}{}
			return

		case <-acker.C:
			if !s.sendEventAck && s.ackedEventId != s.receivedEventId {
				s.sendAck()
			}

		case <-s.closeNotify:
			return

		case <-fail:
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package editor

import (
	"fmt"
	"github.com/maruel/wi/wi-plugin"
	"log"
	"sort"
)

// commands is the map of registered commands.
type commands struct {
	commands map[string]wi.Command
}

func (c *commands) Register(cmd wi.Command) bool {
	name := cmd.Name()
	_, ok := c.commands[name]
	c.commands[name] = cmd
	return !ok
}

func (c *commands) Get(cmd string) wi.Command {
	return c.commands[cmd]
}

func makeCommands() wi.Commands {
	return &commands{make(map[string]wi.Command)}
}

// privilegedCommandImplHandler is the CommandHandler to use when coupled with
// privilegedCommandImpl.
type privilegedCommandImplHandler func(c *privilegedCommandImpl, e *editor, w *window, args ...string)

// privilegedCommandImpl is the boilerplate Command implementation for builtin
// commands that can access the editor directly.
//
// Native (builtin) commands can mutate the editor.
//
// This command handler has access to the internals of the editor. Because of
// this, it can only be native commands inside the editor process.
type privilegedCommandImpl struct {
	NameValue      string
	ExpectedArgs   int // If >= 0, the command will be aborted if the number of arguments is not exactly this value. Set to -1 to disable verification. On abort, an alert with the long description of the command is done.
	HandlerValue   privilegedCommandImplHandler
	CategoryValue  wi.CommandCategory
	ShortDescValue wi.LangMap
	LongDescValue  wi.LangMap
}

func (c *privilegedCommandImpl) Name() string {
	return c.NameValue
}

func (c *privilegedCommandImpl) Handle(cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	if c.ExpectedArgs != -1 && len(args) != c.ExpectedArgs {
		cd.ExecuteCommand(w, "alert", c.LongDesc(cd, w))
	}
	// Convert types to internal types.
	e := cd.(*editor)
	wInternal := w.(*window)
	c.HandlerValue(c, e, wInternal, args...)
}

func (c *privilegedCommandImpl) Category(cd wi.CommandDispatcherFull, w wi.Window) wi.CommandCategory {
	return c.CategoryValue
}

func (c *privilegedCommandImpl) ShortDesc(cd wi.CommandDispatcherFull, w wi.Window) string {
	return wi.GetStr(cd.CurrentLanguage(), c.ShortDescValue)
}

func (c *privilegedCommandImpl) LongDesc(cd wi.CommandDispatcherFull, w wi.Window) string {
	return wi.GetStr(cd.CurrentLanguage(), c.LongDescValue)
}

// Default commands

func cmdAlert(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	cd.ExecuteCommand(w, "window_new", "0", "bottom", "infobar_alert", args[0])
}

func commandLogRecurse(w *window, cd wi.CommandDispatcherFull) {
	// TODO(maruel): Create a proper enumerator.
	cmds := w.view.Commands().(*commands)
	names := make([]string, 0, len(cmds.commands))
	for k, _ := range cmds.commands {
		names = append(names, k)
	}
	sort.Strings(names)
	for _, n := range names {
		c := cmds.commands[n]
		log.Printf("  %s  %s: %s", w.Id(), c.Name(), c.ShortDesc(cd, w))
	}
	for _, child := range w.childrenWindows {
		commandLogRecurse(child, cd)
	}
}

func cmdCommandLog(c *privilegedCommandImpl, e *editor, w *window, args ...string) {
	// Start at the root and recurse.
	commandLogRecurse(e.rootWindow, e)
}

func cmdEditorBootstrapUI(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	// TODO(maruel): Use onAttach instead of hard coding names.
	cd.ExecuteCommand(w, "window_new", "0", "bottom", "status_root")
	cd.ExecuteCommand(w, "window_new", "0:1", "left", "status_name")
	cd.ExecuteCommand(w, "window_new", "0:1", "right", "status_position")
}

func cmdDocumentNew(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	cmd := make([]string, 3+len(args))
	cmd[0] = w.Id()
	cmd[1] = "fill"
	cmd[2] = "new_document"
	copy(cmd[3:], args)
	cd.ExecuteCommand(w, "window_new", cmd...)
}

func cmdDocumentOpen(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	// The Window and View are created synchronously. The View is populated
	// asynchronously.
	log.Printf("Faking opening a file: %s", args)
}

func isDirtyRecurse(cd wi.CommandDispatcherFull, w wi.Window) bool {
	for _, child := range w.ChildrenWindows() {
		if isDirtyRecurse(cd, child) {
			return true
		}
		v := child.View()
		if v.IsDirty() {
			cd.ExecuteCommand(w, "alert", fmt.Sprintf(wi.GetStr(cd.CurrentLanguage(), viewDirty), v.Title()))
			return true
		}
	}
	return false
}

func cmdEditorQuit(c *privilegedCommandImpl, e *editor, w *window, args ...string) {
	// TODO(maruel): For all the View, question if fine to quit via
	// view.IsDirty(). If not fine, "prompt" y/n to force quit. If n, stop there.
	// - Send a signal to each plugin.
	// - Send a signal back to the main loop.
	if !isDirtyRecurse(e, e.rootWindow) {
		e.quitFlag = true
		// editor_redraw wakes up the command event loop so it detects it's time to
		// quit.
		wi.PostCommand(e, "editor_redraw")
	}
}

func cmdEditorRedraw(c *privilegedCommandImpl, e *editor, w *window, args ...string) {
	go func() {
		e.viewReady <- true
	}()
}

func cmdAlias(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	if args[0] == "window" {
	} else if args[0] == "global" {
		w = wi.RootWindow(w)
	} else {
		cmd := wi.GetCommand(cd, w, "alias")
		cd.ExecuteCommand(w, "alert", cmd.LongDesc(cd, w))
		return
	}
	alias := &wi.CommandAlias{args[1], args[2]}
	w.View().Commands().Register(alias)
}

func cmdKeyBind(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	location := args[0]
	modeName := args[1]
	keyName := args[2]
	cmdName := args[3]

	if location == "global" {
		w = wi.RootWindow(w)
	} else if location != "window" {
		cmd := wi.GetCommand(cd, w, "keybind")
		cd.ExecuteCommand(w, "alert", cmd.LongDesc(cd, w))
		return
	}

	var mode wi.KeyboardMode
	if modeName == "command" {
		mode = wi.CommandMode
	} else if modeName == "edit" {
		mode = wi.CommandMode
	} else if modeName == "all" {
		mode = wi.AllMode
	} else {
		cmd := wi.GetCommand(cd, w, "keybind")
		cd.ExecuteCommand(w, "alert", cmd.LongDesc(cd, w))
		return
	}
	w.View().KeyBindings().Set(mode, keyName, cmdName)
}

func cmdShowCommandWindow(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	// Create the Window with the command view and attach it to the currently
	// focused Window.
	cd.ExecuteCommand(w, "window_new", w.Id(), "floating", "command")
}

// RegisterDefaultCommands registers the top-level native commands. This
// includes the window management commands, opening a new file buffer (it's a
// text editor after all) and help, quitting, etc. It doesn't includes handling
// a file buffer itself, it's up to the relevant view to add the corresponding
// commands. For example, "open" is implemented but "write" is not!
func RegisterDefaultCommands(dispatcher wi.Commands) {
	// Native commands.
	var defaultCommands = []wi.Command{
		&wi.CommandImpl{
			"alert",
			1,
			cmdAlert,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Shows a modal message",
			},
			wi.LangMap{
				wi.LangEn: "Prints a message in a modal dialog box.",
			},
		},
		&privilegedCommandImpl{
			"command_log",
			0,
			cmdCommandLog,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Logs the command",
			},
			wi.LangMap{
				wi.LangEn: "Logs the commands (temporary).",
			},
		},
		&wi.CommandImpl{
			"document_new",
			-1,
			cmdDocumentNew,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Create a new buffer",
			},
			wi.LangMap{
				wi.LangEn: "Create a new buffer.",
			},
		},
		&wi.CommandImpl{
			"document_open",
			-1,
			cmdDocumentOpen,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Opens a file in a new buffer",
			},
			wi.LangMap{
				wi.LangEn: "Opens a file in a new buffer.",
			},
		},
		&wi.CommandImpl{
			"editor_bootstrap_ui",
			0,
			cmdEditorBootstrapUI,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Bootstraps the editor's UI",
			},
			wi.LangMap{
				wi.LangEn: "Bootstraps the editor's UI. This command is automatically run on startup and cannot be executed afterward. It adds the standard status bar. This command exists so it can be overriden by a plugin, so it can create its own status bar.",
			},
		},
		&privilegedCommandImpl{
			"editor_quit",
			0,
			cmdEditorQuit,
			wi.EditorCategory,
			wi.LangMap{
				wi.LangEn: "Quits",
			},
			wi.LangMap{
				wi.LangEn: "Quits the editor. Optionally bypasses writing the files to disk.",
			},
		},
		&privilegedCommandImpl{
			"editor_redraw",
			0,
			cmdEditorRedraw,
			wi.EditorCategory,
			wi.LangMap{
				wi.LangEn: "Forcibly redraws the terminal",
			},
			wi.LangMap{
				wi.LangEn: "Forcibly redraws the terminal.",
			},
		},

		&wi.CommandImpl{
			"alias",
			3,
			cmdAlias,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Binds an alias to another command",
			},
			wi.LangMap{
				// TODO(maruel): For complex aliasing, use macro?
				wi.LangEn: "Usage: alias [window|global] <alias> <name>\nBinds an alias to another command. The alias can either be local to the window or global",
			},
		},
		&wi.CommandImpl{
			"keybind",
			4,
			cmdKeyBind,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Binds a keyboard mapping to a command",
			},
			wi.LangMap{
				wi.LangEn: "Usage: keybind [window|global] [command|edit|all] <key> <command>\nBinds a keyboard mapping to a command. The binding can be to the active view for view-specific key binding or to the root view for global key bindings.",
			},
		},
		&wi.CommandImpl{
			"show_command_window",
			0,
			cmdShowCommandWindow,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Shows the interactive command window",
			},
			wi.LangMap{
				wi.LangEn: "This commands exists so it can be bound to a key to pop up the interactive command window.",
			},
		},

		&wi.CommandAlias{"new", "document_new"},
		&wi.CommandAlias{"open", "document_open"},
		&wi.CommandAlias{"q", "editor_quit"},
		&wi.CommandAlias{"quit", "editor_quit"},

		// DIRECTION = up/down/left/right
		// window_DIRECTION
		// cursor_move_DIRECTION
		// add_text/insert/delete
		// undo/redo
		// verb/movement/multiplier
		// Modes, select (both column and normal), command.
		// ...
	}
	for _, cmd := range defaultCommands {
		dispatcher.Register(cmd)
	}
}
<commit_msg>Rename 'alias' to 'command_alias' for consistency<commit_after>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package editor

import (
	"fmt"
	"github.com/maruel/wi/wi-plugin"
	"log"
	"sort"
)

// commands is the map of registered commands.
type commands struct {
	commands map[string]wi.Command
}

func (c *commands) Register(cmd wi.Command) bool {
	name := cmd.Name()
	_, ok := c.commands[name]
	c.commands[name] = cmd
	return !ok
}

func (c *commands) Get(cmd string) wi.Command {
	return c.commands[cmd]
}

func makeCommands() wi.Commands {
	return &commands{make(map[string]wi.Command)}
}

// privilegedCommandImplHandler is the CommandHandler to use when coupled with
// privilegedCommandImpl.
type privilegedCommandImplHandler func(c *privilegedCommandImpl, e *editor, w *window, args ...string)

// privilegedCommandImpl is the boilerplate Command implementation for builtin
// commands that can access the editor directly.
//
// Native (builtin) commands can mutate the editor.
//
// This command handler has access to the internals of the editor. Because of
// this, it can only be native commands inside the editor process.
type privilegedCommandImpl struct {
	NameValue      string
	ExpectedArgs   int // If >= 0, the command will be aborted if the number of arguments is not exactly this value. Set to -1 to disable verification. On abort, an alert with the long description of the command is done.
	HandlerValue   privilegedCommandImplHandler
	CategoryValue  wi.CommandCategory
	ShortDescValue wi.LangMap
	LongDescValue  wi.LangMap
}

func (c *privilegedCommandImpl) Name() string {
	return c.NameValue
}

func (c *privilegedCommandImpl) Handle(cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	if c.ExpectedArgs != -1 && len(args) != c.ExpectedArgs {
		cd.ExecuteCommand(w, "alert", c.LongDesc(cd, w))
	}
	// Convert types to internal types.
	e := cd.(*editor)
	wInternal := w.(*window)
	c.HandlerValue(c, e, wInternal, args...)
}

func (c *privilegedCommandImpl) Category(cd wi.CommandDispatcherFull, w wi.Window) wi.CommandCategory {
	return c.CategoryValue
}

func (c *privilegedCommandImpl) ShortDesc(cd wi.CommandDispatcherFull, w wi.Window) string {
	return wi.GetStr(cd.CurrentLanguage(), c.ShortDescValue)
}

func (c *privilegedCommandImpl) LongDesc(cd wi.CommandDispatcherFull, w wi.Window) string {
	return wi.GetStr(cd.CurrentLanguage(), c.LongDescValue)
}

// Default commands

func cmdAlert(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	cd.ExecuteCommand(w, "window_new", "0", "bottom", "infobar_alert", args[0])
}

func commandLogRecurse(w *window, cd wi.CommandDispatcherFull) {
	// TODO(maruel): Create a proper enumerator.
	cmds := w.view.Commands().(*commands)
	names := make([]string, 0, len(cmds.commands))
	for k, _ := range cmds.commands {
		names = append(names, k)
	}
	sort.Strings(names)
	for _, n := range names {
		c := cmds.commands[n]
		log.Printf("  %s  %s: %s", w.Id(), c.Name(), c.ShortDesc(cd, w))
	}
	for _, child := range w.childrenWindows {
		commandLogRecurse(child, cd)
	}
}

func cmdCommandLog(c *privilegedCommandImpl, e *editor, w *window, args ...string) {
	// Start at the root and recurse.
	commandLogRecurse(e.rootWindow, e)
}

func cmdEditorBootstrapUI(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	// TODO(maruel): Use onAttach instead of hard coding names.
	cd.ExecuteCommand(w, "window_new", "0", "bottom", "status_root")
	cd.ExecuteCommand(w, "window_new", "0:1", "left", "status_name")
	cd.ExecuteCommand(w, "window_new", "0:1", "right", "status_position")
}

func cmdDocumentNew(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	cmd := make([]string, 3+len(args))
	cmd[0] = w.Id()
	cmd[1] = "fill"
	cmd[2] = "new_document"
	copy(cmd[3:], args)
	cd.ExecuteCommand(w, "window_new", cmd...)
}

func cmdDocumentOpen(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	// The Window and View are created synchronously. The View is populated
	// asynchronously.
	log.Printf("Faking opening a file: %s", args)
}

func isDirtyRecurse(cd wi.CommandDispatcherFull, w wi.Window) bool {
	for _, child := range w.ChildrenWindows() {
		if isDirtyRecurse(cd, child) {
			return true
		}
		v := child.View()
		if v.IsDirty() {
			cd.ExecuteCommand(w, "alert", fmt.Sprintf(wi.GetStr(cd.CurrentLanguage(), viewDirty), v.Title()))
			return true
		}
	}
	return false
}

func cmdEditorQuit(c *privilegedCommandImpl, e *editor, w *window, args ...string) {
	// TODO(maruel): For all the View, question if fine to quit via
	// view.IsDirty(). If not fine, "prompt" y/n to force quit. If n, stop there.
	// - Send a signal to each plugin.
	// - Send a signal back to the main loop.
	if !isDirtyRecurse(e, e.rootWindow) {
		e.quitFlag = true
		// editor_redraw wakes up the command event loop so it detects it's time to
		// quit.
		wi.PostCommand(e, "editor_redraw")
	}
}

func cmdEditorRedraw(c *privilegedCommandImpl, e *editor, w *window, args ...string) {
	go func() {
		e.viewReady <- true
	}()
}

func cmdCommandAlias(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	if args[0] == "window" {
	} else if args[0] == "global" {
		w = wi.RootWindow(w)
	} else {
		cmd := wi.GetCommand(cd, w, "command_alias")
		cd.ExecuteCommand(w, "alert", cmd.LongDesc(cd, w))
		return
	}
	alias := &wi.CommandAlias{args[1], args[2]}
	w.View().Commands().Register(alias)
}

func cmdCommandKeyBind(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	location := args[0]
	modeName := args[1]
	keyName := args[2]
	cmdName := args[3]

	if location == "global" {
		w = wi.RootWindow(w)
	} else if location != "window" {
		cmd := wi.GetCommand(cd, w, "keybind")
		cd.ExecuteCommand(w, "alert", cmd.LongDesc(cd, w))
		return
	}

	var mode wi.KeyboardMode
	if modeName == "command" {
		mode = wi.CommandMode
	} else if modeName == "edit" {
		mode = wi.CommandMode
	} else if modeName == "all" {
		mode = wi.AllMode
	} else {
		cmd := wi.GetCommand(cd, w, "keybind")
		cd.ExecuteCommand(w, "alert", cmd.LongDesc(cd, w))
		return
	}
	w.View().KeyBindings().Set(mode, keyName, cmdName)
}

func cmdShowCommandWindow(c *wi.CommandImpl, cd wi.CommandDispatcherFull, w wi.Window, args ...string) {
	// Create the Window with the command view and attach it to the currently
	// focused Window.
	cd.ExecuteCommand(w, "window_new", w.Id(), "floating", "command")
}

// RegisterDefaultCommands registers the top-level native commands. This
// includes the window management commands, opening a new file buffer (it's a
// text editor after all) and help, quitting, etc. It doesn't includes handling
// a file buffer itself, it's up to the relevant view to add the corresponding
// commands. For example, "open" is implemented but "write" is not!
func RegisterDefaultCommands(dispatcher wi.Commands) {
	// Native commands.
	defaultCommands := []wi.Command{
		&wi.CommandImpl{
			"alert",
			1,
			cmdAlert,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Shows a modal message",
			},
			wi.LangMap{
				wi.LangEn: "Prints a message in a modal dialog box.",
			},
		},
		&wi.CommandImpl{
			"document_new",
			-1,
			cmdDocumentNew,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Create a new buffer",
			},
			wi.LangMap{
				wi.LangEn: "Create a new buffer.",
			},
		},
		&wi.CommandImpl{
			"document_open",
			-1,
			cmdDocumentOpen,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Opens a file in a new buffer",
			},
			wi.LangMap{
				wi.LangEn: "Opens a file in a new buffer.",
			},
		},
		&wi.CommandImpl{
			"editor_bootstrap_ui",
			0,
			cmdEditorBootstrapUI,
			wi.WindowCategory,
			wi.LangMap{
				wi.LangEn: "Bootstraps the editor's UI",
			},
			wi.LangMap{
				wi.LangEn: "Bootstraps the editor's UI. This command is automatically run on startup and cannot be executed afterward. It adds the standard status bar. This command exists so it can be overriden by a plugin, so it can create its own status bar.",
			},
		},
		&privilegedCommandImpl{
			"editor_quit",
			0,
			cmdEditorQuit,
			wi.EditorCategory,
			wi.LangMap{
				wi.LangEn: "Quits",
			},
			wi.LangMap{
				wi.LangEn: "Quits the editor. Optionally bypasses writing the files to disk.",
			},
		},
		&privilegedCommandImpl{
			"editor_redraw",
			0,
			cmdEditorRedraw,
			wi.EditorCategory,
			wi.LangMap{
				wi.LangEn: "Forcibly redraws the terminal",
			},
			wi.LangMap{
				wi.LangEn: "Forcibly redraws the terminal.",
			},
		},

		&wi.CommandImpl{
			"command_alias",
			3,
			cmdCommandAlias,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Binds an alias to another command",
			},
			wi.LangMap{
				// TODO(maruel): For complex aliasing, use macro?
				wi.LangEn: "Usage: command_alias [window|global] <alias> <name>\nBinds an alias to another command. The alias can either be local to the window or global",
			},
		},
		&wi.CommandImpl{
			"command_keybind",
			4,
			cmdCommandKeyBind,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Binds a keyboard mapping to a command",
			},
			wi.LangMap{
				wi.LangEn: "Usage: command_keybind [window|global] [command|edit|all] <key> <command>\nBinds a keyboard mapping to a command. The binding can be to the active view for view-specific key binding or to the root view for global key bindings.",
			},
		},
		&privilegedCommandImpl{
			"command_log",
			0,
			cmdCommandLog,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Logs the command",
			},
			wi.LangMap{
				wi.LangEn: "Logs the commands (temporary).",
			},
		},
		&wi.CommandImpl{
			"show_command_window",
			0,
			cmdShowCommandWindow,
			wi.CommandsCategory,
			wi.LangMap{
				wi.LangEn: "Shows the interactive command window",
			},
			wi.LangMap{
				wi.LangEn: "This commands exists so it can be bound to a key to pop up the interactive command window.",
			},
		},

		&wi.CommandAlias{"alias", "command_alias"},
		&wi.CommandAlias{"keybind", "command_keybind"},
		&wi.CommandAlias{"new", "document_new"},
		&wi.CommandAlias{"open", "document_open"},
		&wi.CommandAlias{"q", "editor_quit"},
		&wi.CommandAlias{"quit", "editor_quit"},

		// DIRECTION = up/down/left/right
		// window_DIRECTION
		// cursor_move_DIRECTION
		// add_text/insert/delete
		// undo/redo
		// verb/movement/multiplier
		// Modes, select (both column and normal), command.
		// ...
	}
	for _, cmd := range defaultCommands {
		dispatcher.Register(cmd)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017, OpenCensus Authors
//
// 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 tag

import (
	"encoding/binary"
	"fmt"
)

// KeyType defines the types of keys allowed. Currently only keyTypeString is
// supported.
type keyType byte

const (
	keyTypeString keyType = iota
	keyTypeInt64
	keyTypeTrue
	keyTypeFalse

	tagsVersionID = byte(0)
)

type encoderGRPC struct {
	buf               []byte
	writeIdx, readIdx int
}

// writeKeyString writes the fieldID '0' followed by the key string and value
// string.
func (eg *encoderGRPC) writeTagString(k, v string) {
	eg.writeByte(byte(keyTypeString))
	eg.writeStringWithVarintLen(k)
	eg.writeStringWithVarintLen(v)
}

func (eg *encoderGRPC) writeTagUint64(k string, i uint64) {
	eg.writeByte(byte(keyTypeInt64))
	eg.writeStringWithVarintLen(k)
	eg.writeUint64(i)
}

func (eg *encoderGRPC) writeTagTrue(k string) {
	eg.writeByte(byte(keyTypeTrue))
	eg.writeStringWithVarintLen(k)
}

func (eg *encoderGRPC) writeTagFalse(k string) {
	eg.writeByte(byte(keyTypeFalse))
	eg.writeStringWithVarintLen(k)
}

func (eg *encoderGRPC) writeBytesWithVarintLen(bytes []byte) {
	length := len(bytes)

	eg.growIfRequired(binary.MaxVarintLen64 + length)
	eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
	copy(eg.buf[eg.writeIdx:], bytes)
	eg.writeIdx += length
}

func (eg *encoderGRPC) writeStringWithVarintLen(s string) {
	length := len(s)

	eg.growIfRequired(binary.MaxVarintLen64 + length)
	eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
	copy(eg.buf[eg.writeIdx:], s)
	eg.writeIdx += length
}

func (eg *encoderGRPC) writeByte(v byte) {
	eg.growIfRequired(1)
	eg.buf[eg.writeIdx] = v
	eg.writeIdx++
}

func (eg *encoderGRPC) writeUint32(i uint32) {
	eg.growIfRequired(4)
	binary.LittleEndian.PutUint32(eg.buf[eg.writeIdx:], i)
	eg.writeIdx += 4
}

func (eg *encoderGRPC) writeUint64(i uint64) {
	eg.growIfRequired(8)
	binary.LittleEndian.PutUint64(eg.buf[eg.writeIdx:], i)
	eg.writeIdx += 8
}

func (eg *encoderGRPC) readByte() byte {
	b := eg.buf[eg.readIdx]
	eg.readIdx++
	return b
}

func (eg *encoderGRPC) readUint32() uint32 {
	i := binary.LittleEndian.Uint32(eg.buf[eg.readIdx:])
	eg.readIdx += 4
	return i
}

func (eg *encoderGRPC) readUint64() uint64 {
	i := binary.LittleEndian.Uint64(eg.buf[eg.readIdx:])
	eg.readIdx += 8
	return i
}

func (eg *encoderGRPC) readBytesWithVarintLen() ([]byte, error) {
	if eg.readEnded() {
		return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
	}
	length, valueStart := binary.Uvarint(eg.buf[eg.readIdx:])
	if valueStart <= 0 {
		return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
	}

	valueStart += eg.readIdx
	valueEnd := valueStart + int(length)
	if valueEnd > len(eg.buf) {
		return nil, fmt.Errorf("malformed encoding: length:%v, upper:%v, maxLength:%v", length, valueEnd, len(eg.buf))
	}

	eg.readIdx = valueEnd
	return eg.buf[valueStart:valueEnd], nil
}

func (eg *encoderGRPC) readStringWithVarintLen() (string, error) {
	bytes, err := eg.readBytesWithVarintLen()
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func (eg *encoderGRPC) growIfRequired(expected int) {
	if len(eg.buf)-eg.writeIdx < expected {
		tmp := make([]byte, 2*(len(eg.buf)+1)+expected)
		copy(tmp, eg.buf)
		eg.buf = tmp
	}
}

func (eg *encoderGRPC) readEnded() bool {
	return eg.readIdx >= len(eg.buf)
}

func (eg *encoderGRPC) bytes() []byte {
	return eg.buf[:eg.writeIdx]
}

// Encode encodes the tag map into a []byte. It is useful to propagate
// the tag maps on wire in binary format.
func Encode(m *Map) []byte {
	eg := &encoderGRPC{
		buf: make([]byte, len(m.m)),
	}

	eg.writeByte(byte(tagsVersionID))
	for k, v := range m.m {
		eg.writeByte(byte(keyTypeString))
		eg.writeStringWithVarintLen(k.name)
		eg.writeBytesWithVarintLen([]byte(v))
	}

	return eg.bytes()
}

// Decode  decodes the given []byte into a tag map.
func Decode(bytes []byte) (*Map, error) {
	ts := newMap(0)

	eg := &encoderGRPC{
		buf: bytes,
	}
	if len(eg.buf) == 0 {
		return ts, nil
	}

	version := eg.readByte()
	if version > tagsVersionID {
		return nil, fmt.Errorf("cannot decode: unsupported version: %q; supports only up to: %q", version, tagsVersionID)
	}

	for !eg.readEnded() {
		typ := keyType(eg.readByte())

		switch typ {
		case keyTypeString:
			break
		default:
			return nil, fmt.Errorf("cannot decode: invalid key type: %q", typ)
		}

		k, err := eg.readBytesWithVarintLen()
		if err != nil {
			return nil, err
		}

		v, err := eg.readBytesWithVarintLen()
		if err != nil {
			return nil, err
		}

		key, err := NewKey(string(k))
		if err != nil {
			return nil, err // no partial failures
		}
		val := string(v)
		if !checkValue(val) {
			return nil, errInvalidValue // no partial failures
		}
		ts.upsert(key, val)
	}
	return ts, nil
}
<commit_msg>Simplify key type check in tag decode (#304)<commit_after>// Copyright 2017, OpenCensus Authors
//
// 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 tag

import (
	"encoding/binary"
	"fmt"
)

// KeyType defines the types of keys allowed. Currently only keyTypeString is
// supported.
type keyType byte

const (
	keyTypeString keyType = iota
	keyTypeInt64
	keyTypeTrue
	keyTypeFalse

	tagsVersionID = byte(0)
)

type encoderGRPC struct {
	buf               []byte
	writeIdx, readIdx int
}

// writeKeyString writes the fieldID '0' followed by the key string and value
// string.
func (eg *encoderGRPC) writeTagString(k, v string) {
	eg.writeByte(byte(keyTypeString))
	eg.writeStringWithVarintLen(k)
	eg.writeStringWithVarintLen(v)
}

func (eg *encoderGRPC) writeTagUint64(k string, i uint64) {
	eg.writeByte(byte(keyTypeInt64))
	eg.writeStringWithVarintLen(k)
	eg.writeUint64(i)
}

func (eg *encoderGRPC) writeTagTrue(k string) {
	eg.writeByte(byte(keyTypeTrue))
	eg.writeStringWithVarintLen(k)
}

func (eg *encoderGRPC) writeTagFalse(k string) {
	eg.writeByte(byte(keyTypeFalse))
	eg.writeStringWithVarintLen(k)
}

func (eg *encoderGRPC) writeBytesWithVarintLen(bytes []byte) {
	length := len(bytes)

	eg.growIfRequired(binary.MaxVarintLen64 + length)
	eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
	copy(eg.buf[eg.writeIdx:], bytes)
	eg.writeIdx += length
}

func (eg *encoderGRPC) writeStringWithVarintLen(s string) {
	length := len(s)

	eg.growIfRequired(binary.MaxVarintLen64 + length)
	eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
	copy(eg.buf[eg.writeIdx:], s)
	eg.writeIdx += length
}

func (eg *encoderGRPC) writeByte(v byte) {
	eg.growIfRequired(1)
	eg.buf[eg.writeIdx] = v
	eg.writeIdx++
}

func (eg *encoderGRPC) writeUint32(i uint32) {
	eg.growIfRequired(4)
	binary.LittleEndian.PutUint32(eg.buf[eg.writeIdx:], i)
	eg.writeIdx += 4
}

func (eg *encoderGRPC) writeUint64(i uint64) {
	eg.growIfRequired(8)
	binary.LittleEndian.PutUint64(eg.buf[eg.writeIdx:], i)
	eg.writeIdx += 8
}

func (eg *encoderGRPC) readByte() byte {
	b := eg.buf[eg.readIdx]
	eg.readIdx++
	return b
}

func (eg *encoderGRPC) readUint32() uint32 {
	i := binary.LittleEndian.Uint32(eg.buf[eg.readIdx:])
	eg.readIdx += 4
	return i
}

func (eg *encoderGRPC) readUint64() uint64 {
	i := binary.LittleEndian.Uint64(eg.buf[eg.readIdx:])
	eg.readIdx += 8
	return i
}

func (eg *encoderGRPC) readBytesWithVarintLen() ([]byte, error) {
	if eg.readEnded() {
		return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
	}
	length, valueStart := binary.Uvarint(eg.buf[eg.readIdx:])
	if valueStart <= 0 {
		return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
	}

	valueStart += eg.readIdx
	valueEnd := valueStart + int(length)
	if valueEnd > len(eg.buf) {
		return nil, fmt.Errorf("malformed encoding: length:%v, upper:%v, maxLength:%v", length, valueEnd, len(eg.buf))
	}

	eg.readIdx = valueEnd
	return eg.buf[valueStart:valueEnd], nil
}

func (eg *encoderGRPC) readStringWithVarintLen() (string, error) {
	bytes, err := eg.readBytesWithVarintLen()
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func (eg *encoderGRPC) growIfRequired(expected int) {
	if len(eg.buf)-eg.writeIdx < expected {
		tmp := make([]byte, 2*(len(eg.buf)+1)+expected)
		copy(tmp, eg.buf)
		eg.buf = tmp
	}
}

func (eg *encoderGRPC) readEnded() bool {
	return eg.readIdx >= len(eg.buf)
}

func (eg *encoderGRPC) bytes() []byte {
	return eg.buf[:eg.writeIdx]
}

// Encode encodes the tag map into a []byte. It is useful to propagate
// the tag maps on wire in binary format.
func Encode(m *Map) []byte {
	eg := &encoderGRPC{
		buf: make([]byte, len(m.m)),
	}

	eg.writeByte(byte(tagsVersionID))
	for k, v := range m.m {
		eg.writeByte(byte(keyTypeString))
		eg.writeStringWithVarintLen(k.name)
		eg.writeBytesWithVarintLen([]byte(v))
	}

	return eg.bytes()
}

// Decode  decodes the given []byte into a tag map.
func Decode(bytes []byte) (*Map, error) {
	ts := newMap(0)

	eg := &encoderGRPC{
		buf: bytes,
	}
	if len(eg.buf) == 0 {
		return ts, nil
	}

	version := eg.readByte()
	if version > tagsVersionID {
		return nil, fmt.Errorf("cannot decode: unsupported version: %q; supports only up to: %q", version, tagsVersionID)
	}

	for !eg.readEnded() {
		typ := keyType(eg.readByte())

		if typ != keyTypeString {
			return nil, fmt.Errorf("cannot decode: invalid key type: %q", typ)
		}

		k, err := eg.readBytesWithVarintLen()
		if err != nil {
			return nil, err
		}

		v, err := eg.readBytesWithVarintLen()
		if err != nil {
			return nil, err
		}

		key, err := NewKey(string(k))
		if err != nil {
			return nil, err // no partial failures
		}
		val := string(v)
		if !checkValue(val) {
			return nil, errInvalidValue // no partial failures
		}
		ts.upsert(key, val)
	}
	return ts, nil
}
<|endoftext|>


<commit_before>package terminal

import "github.com/nsf/termbox-go"
import "strings"

type Menu struct {
	cols, rows  int
	col0, row0  int
	screen      *Screen
	keyboard    *Keyboard
	selection   int
	rowShift    int
	borderColor termbox.Attribute
	choices     []string
}

func NewMenu(screen *Screen) *Menu {
	menu := Menu{}
	menu.SetDims()
	menu.screen = screen
	menu.keyboard = NewKeyboard()
	menu.borderColor = termbox.ColorBlue
	return &menu
}

func (menu *Menu) SetDims() {
	cols, rows := termbox.Size()
	menu.rows = rows - 8
	menu.col0 = 4
	menu.row0 = 4
	if len(menu.choices) < menu.rows {
		menu.rows = len(menu.choices)
	}
	menu.cols = 4
	for _, choice := range menu.choices {
		if len(choice) + 2 > menu.cols {
			menu.cols = len(choice) + 2
		}
	}
	if menu.cols > cols - 8 {
		menu.cols = cols - 8
	}
}

func (menu *Menu) Clear() {
	borderColor := menu.borderColor
	menu.screen.WriteStringColor(menu.row0-1, menu.col0-2, strings.Repeat(" ", menu.cols+4), borderColor, borderColor)
	menu.screen.WriteStringColor(menu.row0+menu.rows, menu.col0-2, strings.Repeat(" ", menu.cols+4), borderColor, borderColor)
	for row := 0; row < menu.rows; row++ {
		menu.screen.WriteStringColor(menu.row0+row, menu.col0-2, "  ", borderColor, borderColor)
		menu.screen.WriteStringColor(menu.row0+row, menu.col0+menu.cols, "  ", borderColor, borderColor)
		menu.screen.WriteString(menu.row0+row, menu.col0, strings.Repeat(" ", menu.cols))
	}
}

func (menu *Menu) ShowSearchStr(searchStr string) {
	borderColor := menu.borderColor
	menu.screen.WriteStringColor(menu.row0-1, menu.col0, searchStr, termbox.ColorBlack, borderColor)
}

func (menu *Menu) Show(choices []string) {
	menu.Clear()
	if menu.selection >= menu.rows-1+menu.rowShift {
		menu.rowShift = menu.selection - menu.rows + 1
	}
	if menu.selection < menu.rowShift {
		menu.rowShift = menu.selection
	}
	for row := 0; row < menu.rows; row++ {
		idx := menu.rowShift + row
		if idx >= len(choices) {
			break
		}
		line := choices[idx]
		if len(line) >= menu.cols {
			line = line[:menu.cols]
		}
		menu.screen.WriteString(menu.row0+row, menu.col0, line)
	}
	for col := 0; col < menu.cols; col++ {
		menu.screen.Highlight(menu.row0+menu.selection-menu.rowShift, menu.col0+col)
	}
}

func (menu *Menu) Choose(choices []string) int {
	menu.choices = choices
	menu.SetDims()
	menu.selection = 0
	searchStr := ""
loop:
	for {
		menu.Show(choices)
		menu.ShowSearchStr(searchStr)
		menu.screen.Flush()
		cmd, r := menu.keyboard.GetKey()
		switch cmd {
		case "enter":
			break loop
		case "ctrlC":
			return -1
		case "arrowDown":
			if menu.selection < len(choices)-1 {
				menu.selection++
			}
		case "arrowUp":
			if menu.selection > 0 {
				menu.selection--
			}
		case "pageDown":
			menu.selection += 10
			if menu.selection >= len(choices) {
				menu.selection = len(choices) - 1
			}
		case "pageUp":
			menu.selection -= 10
			if menu.selection < 0 {
				menu.selection = 0
			}
		case "unknown":
		case "char":
			searchStr += string(r)
			menu.selection = menu.Search(choices, searchStr)
		case "backspace":
			if len(searchStr) > 0 {
				searchStr = searchStr[:len(searchStr)-1]
				menu.selection = menu.Search(choices, searchStr)
			}
		case "ctrlU":
			searchStr = ""
		case "ctrlN":
			menu.selection = menu.SearchNext(choices, searchStr)
		default:
		}
	}
	return menu.selection
}

func (menu *Menu) Search(choices []string, searchStr string) int {
	for index := 0; index < len(choices); index++ {
		if strings.Contains(strings.ToLower(choices[index]), strings.ToLower(searchStr)) {
			return index
		}
	}
	return menu.selection
}

func (menu *Menu) SearchNext(choices []string, searchStr string) int {
	index := menu.selection
	for {
		index++
		if index >= len(choices) {
			index = 0
		}
		if index == menu.selection {
			break
		}
		if strings.Contains(strings.ToLower(choices[index]), strings.ToLower(searchStr)) {
			break
		}
	}
	return index
}
<commit_msg>go fmt<commit_after>package terminal

import "github.com/nsf/termbox-go"
import "strings"

type Menu struct {
	cols, rows  int
	col0, row0  int
	screen      *Screen
	keyboard    *Keyboard
	selection   int
	rowShift    int
	borderColor termbox.Attribute
	choices     []string
}

func NewMenu(screen *Screen) *Menu {
	menu := Menu{}
	menu.SetDims()
	menu.screen = screen
	menu.keyboard = NewKeyboard()
	menu.borderColor = termbox.ColorBlue
	return &menu
}

func (menu *Menu) SetDims() {
	cols, rows := termbox.Size()
	menu.rows = rows - 8
	menu.col0 = 4
	menu.row0 = 4
	if len(menu.choices) < menu.rows {
		menu.rows = len(menu.choices)
	}
	menu.cols = 4
	for _, choice := range menu.choices {
		if len(choice)+2 > menu.cols {
			menu.cols = len(choice) + 2
		}
	}
	if menu.cols > cols-8 {
		menu.cols = cols - 8
	}
}

func (menu *Menu) Clear() {
	borderColor := menu.borderColor
	menu.screen.WriteStringColor(menu.row0-1, menu.col0-2, strings.Repeat(" ", menu.cols+4), borderColor, borderColor)
	menu.screen.WriteStringColor(menu.row0+menu.rows, menu.col0-2, strings.Repeat(" ", menu.cols+4), borderColor, borderColor)
	for row := 0; row < menu.rows; row++ {
		menu.screen.WriteStringColor(menu.row0+row, menu.col0-2, "  ", borderColor, borderColor)
		menu.screen.WriteStringColor(menu.row0+row, menu.col0+menu.cols, "  ", borderColor, borderColor)
		menu.screen.WriteString(menu.row0+row, menu.col0, strings.Repeat(" ", menu.cols))
	}
}

func (menu *Menu) ShowSearchStr(searchStr string) {
	borderColor := menu.borderColor
	menu.screen.WriteStringColor(menu.row0-1, menu.col0, searchStr, termbox.ColorBlack, borderColor)
}

func (menu *Menu) Show(choices []string) {
	menu.Clear()
	if menu.selection >= menu.rows-1+menu.rowShift {
		menu.rowShift = menu.selection - menu.rows + 1
	}
	if menu.selection < menu.rowShift {
		menu.rowShift = menu.selection
	}
	for row := 0; row < menu.rows; row++ {
		idx := menu.rowShift + row
		if idx >= len(choices) {
			break
		}
		line := choices[idx]
		if len(line) >= menu.cols {
			line = line[:menu.cols]
		}
		menu.screen.WriteString(menu.row0+row, menu.col0, line)
	}
	for col := 0; col < menu.cols; col++ {
		menu.screen.Highlight(menu.row0+menu.selection-menu.rowShift, menu.col0+col)
	}
}

func (menu *Menu) Choose(choices []string) int {
	menu.choices = choices
	menu.SetDims()
	menu.selection = 0
	searchStr := ""
loop:
	for {
		menu.Show(choices)
		menu.ShowSearchStr(searchStr)
		menu.screen.Flush()
		cmd, r := menu.keyboard.GetKey()
		switch cmd {
		case "enter":
			break loop
		case "ctrlC":
			return -1
		case "arrowDown":
			if menu.selection < len(choices)-1 {
				menu.selection++
			}
		case "arrowUp":
			if menu.selection > 0 {
				menu.selection--
			}
		case "pageDown":
			menu.selection += 10
			if menu.selection >= len(choices) {
				menu.selection = len(choices) - 1
			}
		case "pageUp":
			menu.selection -= 10
			if menu.selection < 0 {
				menu.selection = 0
			}
		case "unknown":
		case "char":
			searchStr += string(r)
			menu.selection = menu.Search(choices, searchStr)
		case "backspace":
			if len(searchStr) > 0 {
				searchStr = searchStr[:len(searchStr)-1]
				menu.selection = menu.Search(choices, searchStr)
			}
		case "ctrlU":
			searchStr = ""
		case "ctrlN":
			menu.selection = menu.SearchNext(choices, searchStr)
		default:
		}
	}
	return menu.selection
}

func (menu *Menu) Search(choices []string, searchStr string) int {
	for index := 0; index < len(choices); index++ {
		if strings.Contains(strings.ToLower(choices[index]), strings.ToLower(searchStr)) {
			return index
		}
	}
	return menu.selection
}

func (menu *Menu) SearchNext(choices []string, searchStr string) int {
	index := menu.selection
	for {
		index++
		if index >= len(choices) {
			index = 0
		}
		if index == menu.selection {
			break
		}
		if strings.Contains(strings.ToLower(choices[index]), strings.ToLower(searchStr)) {
			break
		}
	}
	return index
}
<|endoftext|>


<commit_before>package termite

import (
	"os"
	"log"
	"sync"
	"rpc"
	"github.com/hanwen/go-fuse/fuse"
	"strings"
	"path/filepath"
)

type RpcFs struct {
	fuse.DefaultFileSystem
	cache *ContentCache

	client *rpc.Client

	// Roots that we should try to fetch locally.
	localRoots []string

	// Should be acquired before attrMutex if applicable.
	dirMutex    sync.Mutex
	directories map[string]*DirResponse

	attrMutex    sync.RWMutex
	attrResponse map[string]*FileAttr

	fetchMutex sync.Mutex
	fetchCond  sync.Cond
	fetchMap   map[string]bool
}

type UpdateRequest struct {
	Files []FileAttr
}

type UpdateResponse struct {

}

func NewRpcFs(server *rpc.Client, cache *ContentCache) *RpcFs {
	me := &RpcFs{}
	me.client = server
	me.directories = make(map[string]*DirResponse)
	me.cache = cache
	me.attrResponse = make(map[string]*FileAttr)

	me.fetchMap = make(map[string]bool)
	me.fetchCond.L = &me.fetchMutex
	return me
}

func (me *RpcFs) Update(req *UpdateRequest, resp *UpdateResponse) os.Error {
	me.updateFiles(req.Files)
	return nil
}

func (me *RpcFs) updateFiles(files []FileAttr) {
	me.dirMutex.Lock()
	defer me.dirMutex.Unlock()

	me.attrMutex.Lock()
	defer me.attrMutex.Unlock()

	flushDirs := []string{}
	for _, r := range files {
		p := strings.TrimLeft(r.Path, string(filepath.Separator))

		d, _ := filepath.Split(p)
		a, existed := me.attrResponse[p]

		if r.Deletion() || (existed && a.Status != fuse.ENOENT) {
			flushDirs = append(flushDirs, d)
		}

		newVal := r
		me.attrResponse[p] = &newVal
	}

	for _, d := range flushDirs {
		me.directories[d] = nil, false
	}
}

func (me *RpcFs) GetDir(name string) *DirResponse {
	me.dirMutex.Lock()
	defer me.dirMutex.Unlock()

	r, ok := me.directories[name]
	if ok {
		return r
	}

	// TODO - asynchronous.
	// TODO - eliminate cut & paste
	req := &DirRequest{Name: "/" + name}
	rep := &DirResponse{}
	err := me.client.Call("FsServer.ReadDir", req, rep)
	if err != nil {
		log.Println("GetDir error:", err)
		return nil
	}

	me.directories[name] = rep
	return rep
}

func (me *RpcFs) OpenDir(name string) (chan fuse.DirEntry, fuse.Status) {
	r := me.GetDir(name)

	if r == nil {
		return nil, fuse.ENOENT
	}
	c := make(chan fuse.DirEntry, len(r.NameModeMap))
	for k, mode := range r.NameModeMap {
		c <- fuse.DirEntry{
			Name: k,
			Mode: mode,
		}
	}
	close(c)
	return c, fuse.OK
}

func (me *RpcFs) Open(name string, flags uint32) (fuse.File, fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}
	a := me.getFileAttr(name)
	if a == nil {
		return nil, fuse.ENOENT
	}
	if !a.Status.Ok() {
		return nil, a.Status
	}

	p := me.cache.Path(a.Hash)
	if _, err := os.Lstat(p); fuse.OsErrorToErrno(err) == fuse.ENOENT {
		log.Printf("Fetching contents for file %s", name)
		err = me.FetchHash(a.FileInfo.Size, a.Hash)
		// should return something else?
		if err != nil {
			return nil, fuse.ENOENT
		}
	}

	f, err := os.Open(p)
	if err != nil {
		return nil, fuse.OsErrorToErrno(err)
	}

	return &fuse.WithFlags{
		&fuse.LoopbackFile{File: f},
		fuse.FOPEN_KEEP_CACHE,
	}, fuse.OK
}

func (me *RpcFs) FetchHash(size int64, hash []byte) os.Error {
	key := string(hash)
	me.fetchMutex.Lock()
	defer me.fetchMutex.Unlock()
	for me.fetchMap[key] && !me.cache.HasHash(hash) {
		me.fetchCond.Wait()
	}

	if me.cache.HasHash(hash) {
		return nil
	}
	me.fetchMap[key] = true
	defer func() {
		me.fetchMap[key] = false, false
		me.fetchCond.Signal()
	}()
	me.fetchMutex.Unlock()
	err := me.fetchOnce(size, hash)
	me.fetchMutex.Lock()
	return err
}

func (me *RpcFs) fetchOnce(size int64, hash []byte) os.Error {
	// TODO - should save in smaller chunks.
	return FetchBetweenContentServers(me.client, "FsServer.FileContent", size, hash,
		me.cache)
}

func (me *RpcFs) Readlink(name string) (string, fuse.Status) {
	a := me.getFileAttr(name)
	if a == nil {
		return "", fuse.ENOENT
	}

	if !a.Status.Ok() {
		return "", a.Status
	}
	if !a.FileInfo.IsSymlink() {
		return "", fuse.EINVAL
	}

	return a.Link, fuse.OK
}

func (me *RpcFs) getFileAttr(name string) *FileAttr {
	me.attrMutex.RLock()
	result, ok := me.attrResponse[name]
	me.attrMutex.RUnlock()

	if ok {
		return result
	}

	me.attrMutex.Lock()
	defer me.attrMutex.Unlock()
	result, ok = me.attrResponse[name]
	if ok {
		return result
	}

	abs := "/" + name

	req := &AttrRequest{Name: abs}
	rep := &AttrResponse{}
	err := me.client.Call("FsServer.GetAttr", req, rep)
	if err != nil {
		log.Println("GetAttr error:", err)
		return nil
	}
	var wanted *FileAttr
	for _, attr := range rep.Attrs {
		if attr.Content != nil {
			me.cache.Save(attr.Content)
		}

		me.considerSaveLocal(attr)
		fa := attr
		me.attrResponse[strings.TrimLeft(attr.Path, "/")] = &fa
		if attr.Path == abs {
			wanted = &fa
		}
	}
	return wanted
}

func (me *RpcFs) considerSaveLocal(attr FileAttr) {
	absPath := attr.Path
	if !attr.Status.Ok() || !attr.FileInfo.IsRegular() {
		return
	}
	found := false
	for _, root := range me.localRoots {
		if HasDirPrefix(absPath, root) {
			found = true
		}
	}
	if !found {
		return
	}

	fi, _ := os.Lstat(absPath)
	if fi == nil {
		return
	}
	if EncodeFileInfo(*fi) != EncodeFileInfo(*attr.FileInfo) {
		return
	}

	// Avoid fetching local data; this assumes that most paths
	// will be the same between master and worker.
	me.cache.SaveImmutablePath(absPath)
}

func (me *RpcFs) GetAttr(name string) (*os.FileInfo, fuse.Status) {
	if name == "" {
		return &os.FileInfo{
			Mode: fuse.S_IFDIR | 0755,
		}, fuse.OK
	}

	r := me.getFileAttr(name)
	if r == nil {
		return nil, fuse.ENOENT
	}

	return r.FileInfo, r.Status
}
<commit_msg>Create rpcFsFile, which has a GetAttr() method.<commit_after>package termite

import (
	"os"
	"log"
	"sync"
	"rpc"
	"github.com/hanwen/go-fuse/fuse"
	"strings"
	"path/filepath"
)

type RpcFs struct {
	fuse.DefaultFileSystem
	cache *ContentCache

	client *rpc.Client

	// Roots that we should try to fetch locally.
	localRoots []string

	// Should be acquired before attrMutex if applicable.
	dirMutex    sync.Mutex
	directories map[string]*DirResponse

	attrMutex    sync.RWMutex
	attrResponse map[string]*FileAttr

	fetchMutex sync.Mutex
	fetchCond  sync.Cond
	fetchMap   map[string]bool
}

type UpdateRequest struct {
	Files []FileAttr
}

type UpdateResponse struct {

}

func NewRpcFs(server *rpc.Client, cache *ContentCache) *RpcFs {
	me := &RpcFs{}
	me.client = server
	me.directories = make(map[string]*DirResponse)
	me.cache = cache
	me.attrResponse = make(map[string]*FileAttr)

	me.fetchMap = make(map[string]bool)
	me.fetchCond.L = &me.fetchMutex
	return me
}

func (me *RpcFs) Update(req *UpdateRequest, resp *UpdateResponse) os.Error {
	me.updateFiles(req.Files)
	return nil
}

func (me *RpcFs) updateFiles(files []FileAttr) {
	me.dirMutex.Lock()
	defer me.dirMutex.Unlock()

	me.attrMutex.Lock()
	defer me.attrMutex.Unlock()

	flushDirs := []string{}
	for _, r := range files {
		p := strings.TrimLeft(r.Path, string(filepath.Separator))

		d, _ := filepath.Split(p)
		a, existed := me.attrResponse[p]

		if r.Deletion() || (existed && a.Status != fuse.ENOENT) {
			flushDirs = append(flushDirs, d)
		}

		newVal := r
		me.attrResponse[p] = &newVal
	}

	for _, d := range flushDirs {
		me.directories[d] = nil, false
	}
}

func (me *RpcFs) GetDir(name string) *DirResponse {
	me.dirMutex.Lock()
	defer me.dirMutex.Unlock()

	r, ok := me.directories[name]
	if ok {
		return r
	}

	// TODO - asynchronous.
	// TODO - eliminate cut & paste
	req := &DirRequest{Name: "/" + name}
	rep := &DirResponse{}
	err := me.client.Call("FsServer.ReadDir", req, rep)
	if err != nil {
		log.Println("GetDir error:", err)
		return nil
	}

	me.directories[name] = rep
	return rep
}

func (me *RpcFs) OpenDir(name string) (chan fuse.DirEntry, fuse.Status) {
	r := me.GetDir(name)

	if r == nil {
		return nil, fuse.ENOENT
	}
	c := make(chan fuse.DirEntry, len(r.NameModeMap))
	for k, mode := range r.NameModeMap {
		c <- fuse.DirEntry{
			Name: k,
			Mode: mode,
		}
	}
	close(c)
	return c, fuse.OK
}

type rpcFsFile struct {
	fuse.LoopbackFile
	os.FileInfo
}

func (me *rpcFsFile) GetAttr()  (*os.FileInfo, fuse.Status) {
	return &me.FileInfo, fuse.OK
}

func (me *RpcFs) Open(name string, flags uint32) (fuse.File, fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}
	a := me.getFileAttr(name)
	if a == nil {
		return nil, fuse.ENOENT
	}
	if !a.Status.Ok() {
		return nil, a.Status
	}

	p := me.cache.Path(a.Hash)
	if _, err := os.Lstat(p); fuse.OsErrorToErrno(err) == fuse.ENOENT {
		log.Printf("Fetching contents for file %s", name)
		err = me.FetchHash(a.FileInfo.Size, a.Hash)
		// should return something else?
		if err != nil {
			return nil, fuse.ENOENT
		}
	}

	f, err := os.Open(p)
	if err != nil {
		return nil, fuse.OsErrorToErrno(err)
	}

	return &fuse.WithFlags{
		&rpcFsFile{
			fuse.LoopbackFile{File: f},
			*a.FileInfo,
		},
		fuse.FOPEN_KEEP_CACHE,
	}, fuse.OK
}

func (me *RpcFs) FetchHash(size int64, hash []byte) os.Error {
	key := string(hash)
	me.fetchMutex.Lock()
	defer me.fetchMutex.Unlock()
	for me.fetchMap[key] && !me.cache.HasHash(hash) {
		me.fetchCond.Wait()
	}

	if me.cache.HasHash(hash) {
		return nil
	}
	me.fetchMap[key] = true
	defer func() {
		me.fetchMap[key] = false, false
		me.fetchCond.Signal()
	}()
	me.fetchMutex.Unlock()
	err := me.fetchOnce(size, hash)
	me.fetchMutex.Lock()
	return err
}

func (me *RpcFs) fetchOnce(size int64, hash []byte) os.Error {
	// TODO - should save in smaller chunks.
	return FetchBetweenContentServers(me.client, "FsServer.FileContent", size, hash,
		me.cache)
}

func (me *RpcFs) Readlink(name string) (string, fuse.Status) {
	a := me.getFileAttr(name)
	if a == nil {
		return "", fuse.ENOENT
	}

	if !a.Status.Ok() {
		return "", a.Status
	}
	if !a.FileInfo.IsSymlink() {
		return "", fuse.EINVAL
	}

	return a.Link, fuse.OK
}

func (me *RpcFs) getFileAttr(name string) *FileAttr {
	me.attrMutex.RLock()
	result, ok := me.attrResponse[name]
	me.attrMutex.RUnlock()

	if ok {
		return result
	}

	me.attrMutex.Lock()
	defer me.attrMutex.Unlock()
	result, ok = me.attrResponse[name]
	if ok {
		return result
	}

	abs := "/" + name

	req := &AttrRequest{Name: abs}
	rep := &AttrResponse{}
	err := me.client.Call("FsServer.GetAttr", req, rep)
	if err != nil {
		log.Println("GetAttr error:", err)
		return nil
	}
	var wanted *FileAttr
	for _, attr := range rep.Attrs {
		if attr.Content != nil {
			me.cache.Save(attr.Content)
		}

		me.considerSaveLocal(attr)
		fa := attr
		me.attrResponse[strings.TrimLeft(attr.Path, "/")] = &fa
		if attr.Path == abs {
			wanted = &fa
		}
	}
	return wanted
}

func (me *RpcFs) considerSaveLocal(attr FileAttr) {
	absPath := attr.Path
	if !attr.Status.Ok() || !attr.FileInfo.IsRegular() {
		return
	}
	found := false
	for _, root := range me.localRoots {
		if HasDirPrefix(absPath, root) {
			found = true
		}
	}
	if !found {
		return
	}

	fi, _ := os.Lstat(absPath)
	if fi == nil {
		return
	}
	if EncodeFileInfo(*fi) != EncodeFileInfo(*attr.FileInfo) {
		return
	}

	// Avoid fetching local data; this assumes that most paths
	// will be the same between master and worker.
	me.cache.SaveImmutablePath(absPath)
}

func (me *RpcFs) GetAttr(name string) (*os.FileInfo, fuse.Status) {
	if name == "" {
		return &os.FileInfo{
			Mode: fuse.S_IFDIR | 0755,
		}, fuse.OK
	}

	r := me.getFileAttr(name)
	if r == nil {
		return nil, fuse.ENOENT
	}

	return r.FileInfo, r.Status
}
<|endoftext|>


<commit_before>package goat

import (
	"time"
)

type IoWorker interface {
	Read(request Request)
	Write(request Request)
}

type MapWorker struct {
}

type SqlWorker struct {
}

// map read and write

//takes a pointer to a shard map and a request and replies with a response struct
func (m MapWorker) Read(request Request, shard map[string]interface{}) {
	var response Response
	_, ok := shard[request.Id]
	if ok {
		response.Data = shard[request.Id]
		response.Id = request.Id
		response.Db = "map"
		request.ResponseChan <- response
	} else {
		responseChan := make(chan Response)
		var dbReadRequest Request
		dbReadRequest.ResponseChan = responseChan
		dbReadRequest.Id = request.Id
		Static.RequestChan <- dbReadRequest
		select {
		case response := <-responseChan:
			var mapWriteRequest Request
			mapWriteRequest.Id = request.Id
			mapWriteRequest.Data = response.Data
			Static.RequestChan <- mapWriteRequest
		case <-time.After(100 * time.Millisecond):
			close(responseChan)
		}
	}
	<-request.ResponseChan
}

// takes a pointer to a map shard and writes the data from a request and
// sends back a conformation that the write has taken place.
func (m MapWorker) Write(request Request, shard map[string]interface{}) {
	//var comp WriteResponse
	var response Response
	shard[request.Id] = request.Data
	/*
		comp.Complete = true
		response.Data = comp
	*/
	response.Db = "map"
	response.Id = request.Id
	request.ResponseChan <- response
}

// sql read and write
func (s SqlWorker) Read(request Request) {
}
func (s SqlWorker) Write(Request Request) {
}
<commit_msg>Remove ioWorker, fix merge<commit_after><|endoftext|>


<commit_before>//  Copyright 2017 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 main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)

const defaultEtag = "NONE"

var (
	metadataURL       = "http://169.254.169.254/computeMetadata/v1/"
	metadataRecursive = "/?recursive=true&alt=json"
	metadataHang      = "&wait_for_change=true&timeout_sec=60"
	defaultTimeout    = 70 * time.Second
	etag              = defaultEtag
)

type metadata struct {
	Instance instance
	Project  project
}

func (m *metadata) UnmarshalJSON(b []byte) error {
	// We can't unmarshal into metadata directly as it would create an infinite loop.
	type temp metadata
	var t temp
	err := json.Unmarshal(b, &t)
	if err == nil {
		*m = metadata(t)
		return nil
	}

	// If this is a syntax error return a useful error.
	sErr, ok := err.(*json.SyntaxError)
	if !ok {
		return err
	}

	// Byte number where the error line starts.
	start := bytes.LastIndex(b[:sErr.Offset], []byte("\n")) + 1
	// Assume end byte of error line is EOF unless this isn't the last line.
	end := len(b)
	if i := bytes.Index(b[start:], []byte("\n")); i >= 0 {
		end = start + i
	}

	// Position of error in line (where to place the '^').
	pos := int(sErr.Offset) - start
	if pos != 0 {
		pos = pos - 1
	}

	return fmt.Errorf("JSON syntax error: %s \n%s\n%s^", err, b[start:end], strings.Repeat(" ", pos))
}

type virtualClock struct {
	DriftToken int `json:"drift-token"`
}

type instance struct {
	ID                json.Number
	MachineType       string
	Attributes        attributes
	NetworkInterfaces []networkInterfaces
	VirtualClock      virtualClock
}

type networkInterfaces struct {
	ForwardedIps      []string
	ForwardedIpv6s    []string
	TargetInstanceIps []string
	IPAliases         []string
	Mac               string
	DHCPv6Refresh     string
}

type project struct {
	Attributes       attributes
	ProjectID        string
	NumericProjectID json.Number
}

type attributes struct {
	BlockProjectKeys      bool
	EnableOSLogin         *bool
	TwoFactor             *bool
	SecurityKey           *bool
	SSHKeys               []string
	WindowsKeys           windowsKeys
	Diagnostics           string
	DisableAddressManager *bool
	DisableAccountManager *bool
	EnableDiagnostics     *bool
	EnableWSFC            *bool
	WSFCAddresses         string
	WSFCAgentPort         string
}

type windowsKey struct {
	Email               string
	ExpireOn            string
	Exponent            string
	Modulus             string
	UserName            string
	HashFunction        string
	AddToAdministrators *bool
	PasswordLength      int
}

type windowsKeys []windowsKey

func (k *windowsKeys) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	for _, jskey := range strings.Split(s, "\n") {
		var wk windowsKey
		if err := json.Unmarshal([]byte(jskey), &wk); err != nil {
			if !containsString(jskey, badKeys) {
				logger.Errorf("failed to unmarshal windows key from metadata: %s", err)
				badKeys = append(badKeys, jskey)
			}
			continue
		}
		if wk.Exponent != "" && wk.Modulus != "" && wk.UserName != "" && !wk.expired() {
			*k = append(*k, wk)
		}
	}
	return nil
}

func (a *attributes) UnmarshalJSON(b []byte) error {
	var mkbool = func(value bool) *bool {
		res := new(bool)
		*res = value
		return res
	}
	// Unmarshal to literal JSON types before doing anything else.
	type inner struct {
		BlockProjectKeys      string      `json:"block-project-ssh-keys"`
		Diagnostics           string      `json:"diagnostics"`
		DisableAccountManager string      `json:"disable-account-manager"`
		DisableAddressManager string      `json:"disable-address-manager"`
		EnableDiagnostics     string      `json:"enable-diagnostics"`
		EnableOSLogin         string      `json:"enable-oslogin"`
		EnableWSFC            string      `json:"enable-wsfc"`
		OldSSHKeys            string      `json:"sshKeys"`
		SSHKeys               string      `json:"ssh-keys"`
		TwoFactor             string      `json:"enable-oslogin-2fa"`
		SecurityKey           string      `json:"enable-oslogin-sk"`
		WindowsKeys           windowsKeys `json:"windows-keys"`
		WSFCAddresses         string      `json:"wsfc-addrs"`
		WSFCAgentPort         string      `json:"wsfc-agent-port"`
	}
	var temp inner
	if err := json.Unmarshal(b, &temp); err != nil {
		return err
	}
	a.Diagnostics = temp.Diagnostics
	a.WSFCAddresses = temp.WSFCAddresses
	a.WSFCAgentPort = temp.WSFCAgentPort
	a.WindowsKeys = temp.WindowsKeys

	value, err := strconv.ParseBool(temp.BlockProjectKeys)
	if err == nil {
		a.BlockProjectKeys = value
	}
	value, err = strconv.ParseBool(temp.EnableDiagnostics)
	if err == nil {
		a.EnableDiagnostics = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.DisableAccountManager)
	if err == nil {
		a.DisableAccountManager = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.DisableAddressManager)
	if err == nil {
		a.DisableAddressManager = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.EnableOSLogin)
	if err == nil {
		a.EnableOSLogin = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.EnableWSFC)
	if err == nil {
		a.EnableWSFC = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.TwoFactor)
	if err == nil {
		a.TwoFactor = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.SecurityKey)
	if err == nil {
		a.SecurityKey = mkbool(value)
	}
	// So SSHKeys will be nil instead of []string{}
	if temp.SSHKeys != "" {
		a.SSHKeys = strings.Split(temp.SSHKeys, "\n")
	}
	if temp.OldSSHKeys != "" {
		a.BlockProjectKeys = true
		a.SSHKeys = append(a.SSHKeys, strings.Split(temp.OldSSHKeys, "\n")...)
	}
	return nil
}

func updateEtag(resp *http.Response) bool {
	oldEtag := etag
	etag = resp.Header.Get("etag")
	if etag == "" {
		etag = defaultEtag
	}
	return etag != oldEtag
}

func watchMetadata(ctx context.Context) (*metadata, error) {
	return getMetadata(ctx, true)
}

func getMetadata(ctx context.Context, hang bool) (*metadata, error) {
	logger.Debugf("getMetadata, %t", hang)
	client := &http.Client{
		Timeout: defaultTimeout,
	}

	finalURL := metadataURL + metadataRecursive
	if hang {
		finalURL += metadataHang
	}
	finalURL += ("&last_etag=" + etag)

	req, err := http.NewRequest("GET", finalURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Metadata-Flavor", "Google")
	req = req.WithContext(ctx)

	resp, err := client.Do(req)
	// Don't return error on a canceled context.
	if err != nil && ctx.Err() != nil {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	// We return the response even if the etag has not been updated.
	if hang {
		updateEtag(resp)
	}

	md, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, err
	}
	var ret metadata
	return &ret, json.Unmarshal(md, &ret)
}
<commit_msg>Remove extra slash from metadata URL. (#151)<commit_after>//  Copyright 2017 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 main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)

const defaultEtag = "NONE"

var (
	metadataURL       = "http://169.254.169.254/computeMetadata/v1"
	metadataRecursive = "/?recursive=true&alt=json"
	metadataHang      = "&wait_for_change=true&timeout_sec=60"
	defaultTimeout    = 70 * time.Second
	etag              = defaultEtag
)

type metadata struct {
	Instance instance
	Project  project
}

func (m *metadata) UnmarshalJSON(b []byte) error {
	// We can't unmarshal into metadata directly as it would create an infinite loop.
	type temp metadata
	var t temp
	err := json.Unmarshal(b, &t)
	if err == nil {
		*m = metadata(t)
		return nil
	}

	// If this is a syntax error return a useful error.
	sErr, ok := err.(*json.SyntaxError)
	if !ok {
		return err
	}

	// Byte number where the error line starts.
	start := bytes.LastIndex(b[:sErr.Offset], []byte("\n")) + 1
	// Assume end byte of error line is EOF unless this isn't the last line.
	end := len(b)
	if i := bytes.Index(b[start:], []byte("\n")); i >= 0 {
		end = start + i
	}

	// Position of error in line (where to place the '^').
	pos := int(sErr.Offset) - start
	if pos != 0 {
		pos = pos - 1
	}

	return fmt.Errorf("JSON syntax error: %s \n%s\n%s^", err, b[start:end], strings.Repeat(" ", pos))
}

type virtualClock struct {
	DriftToken int `json:"drift-token"`
}

type instance struct {
	ID                json.Number
	MachineType       string
	Attributes        attributes
	NetworkInterfaces []networkInterfaces
	VirtualClock      virtualClock
}

type networkInterfaces struct {
	ForwardedIps      []string
	ForwardedIpv6s    []string
	TargetInstanceIps []string
	IPAliases         []string
	Mac               string
	DHCPv6Refresh     string
}

type project struct {
	Attributes       attributes
	ProjectID        string
	NumericProjectID json.Number
}

type attributes struct {
	BlockProjectKeys      bool
	EnableOSLogin         *bool
	TwoFactor             *bool
	SecurityKey           *bool
	SSHKeys               []string
	WindowsKeys           windowsKeys
	Diagnostics           string
	DisableAddressManager *bool
	DisableAccountManager *bool
	EnableDiagnostics     *bool
	EnableWSFC            *bool
	WSFCAddresses         string
	WSFCAgentPort         string
}

type windowsKey struct {
	Email               string
	ExpireOn            string
	Exponent            string
	Modulus             string
	UserName            string
	HashFunction        string
	AddToAdministrators *bool
	PasswordLength      int
}

type windowsKeys []windowsKey

func (k *windowsKeys) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	for _, jskey := range strings.Split(s, "\n") {
		var wk windowsKey
		if err := json.Unmarshal([]byte(jskey), &wk); err != nil {
			if !containsString(jskey, badKeys) {
				logger.Errorf("failed to unmarshal windows key from metadata: %s", err)
				badKeys = append(badKeys, jskey)
			}
			continue
		}
		if wk.Exponent != "" && wk.Modulus != "" && wk.UserName != "" && !wk.expired() {
			*k = append(*k, wk)
		}
	}
	return nil
}

func (a *attributes) UnmarshalJSON(b []byte) error {
	var mkbool = func(value bool) *bool {
		res := new(bool)
		*res = value
		return res
	}
	// Unmarshal to literal JSON types before doing anything else.
	type inner struct {
		BlockProjectKeys      string      `json:"block-project-ssh-keys"`
		Diagnostics           string      `json:"diagnostics"`
		DisableAccountManager string      `json:"disable-account-manager"`
		DisableAddressManager string      `json:"disable-address-manager"`
		EnableDiagnostics     string      `json:"enable-diagnostics"`
		EnableOSLogin         string      `json:"enable-oslogin"`
		EnableWSFC            string      `json:"enable-wsfc"`
		OldSSHKeys            string      `json:"sshKeys"`
		SSHKeys               string      `json:"ssh-keys"`
		TwoFactor             string      `json:"enable-oslogin-2fa"`
		SecurityKey           string      `json:"enable-oslogin-sk"`
		WindowsKeys           windowsKeys `json:"windows-keys"`
		WSFCAddresses         string      `json:"wsfc-addrs"`
		WSFCAgentPort         string      `json:"wsfc-agent-port"`
	}
	var temp inner
	if err := json.Unmarshal(b, &temp); err != nil {
		return err
	}
	a.Diagnostics = temp.Diagnostics
	a.WSFCAddresses = temp.WSFCAddresses
	a.WSFCAgentPort = temp.WSFCAgentPort
	a.WindowsKeys = temp.WindowsKeys

	value, err := strconv.ParseBool(temp.BlockProjectKeys)
	if err == nil {
		a.BlockProjectKeys = value
	}
	value, err = strconv.ParseBool(temp.EnableDiagnostics)
	if err == nil {
		a.EnableDiagnostics = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.DisableAccountManager)
	if err == nil {
		a.DisableAccountManager = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.DisableAddressManager)
	if err == nil {
		a.DisableAddressManager = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.EnableOSLogin)
	if err == nil {
		a.EnableOSLogin = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.EnableWSFC)
	if err == nil {
		a.EnableWSFC = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.TwoFactor)
	if err == nil {
		a.TwoFactor = mkbool(value)
	}
	value, err = strconv.ParseBool(temp.SecurityKey)
	if err == nil {
		a.SecurityKey = mkbool(value)
	}
	// So SSHKeys will be nil instead of []string{}
	if temp.SSHKeys != "" {
		a.SSHKeys = strings.Split(temp.SSHKeys, "\n")
	}
	if temp.OldSSHKeys != "" {
		a.BlockProjectKeys = true
		a.SSHKeys = append(a.SSHKeys, strings.Split(temp.OldSSHKeys, "\n")...)
	}
	return nil
}

func updateEtag(resp *http.Response) bool {
	oldEtag := etag
	etag = resp.Header.Get("etag")
	if etag == "" {
		etag = defaultEtag
	}
	return etag != oldEtag
}

func watchMetadata(ctx context.Context) (*metadata, error) {
	return getMetadata(ctx, true)
}

func getMetadata(ctx context.Context, hang bool) (*metadata, error) {
	logger.Debugf("getMetadata, %t", hang)
	client := &http.Client{
		Timeout: defaultTimeout,
	}

	finalURL := metadataURL + metadataRecursive
	if hang {
		finalURL += metadataHang
	}
	finalURL += ("&last_etag=" + etag)

	req, err := http.NewRequest("GET", finalURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Metadata-Flavor", "Google")
	req = req.WithContext(ctx)

	resp, err := client.Do(req)
	// Don't return error on a canceled context.
	if err != nil && ctx.Err() != nil {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	// We return the response even if the etag has not been updated.
	if hang {
		updateEtag(resp)
	}

	md, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, err
	}
	var ret metadata
	return &ret, json.Unmarshal(md, &ret)
}
<|endoftext|>