<commit_before>package homecloud

import (
	"encoding/json"
	"fmt"
	"regexp"
	"time"

	"git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/rpc/json2"
	"github.com/ninjasphere/redigo/redis"
)

var log = logger.GetLogger("HomeCloud")

var conn *ninja.Connection

var thingModel *ThingModel
var deviceModel *DeviceModel
var channelModel *ChannelModel
var roomModel *RoomModel
var driverModel *DriverModel

var locationRegexp = regexp.MustCompile("\\$device\\/([A-F0-9]*)\\/[^\\/]*\\/location")

type incomingLocationUpdate struct {
	Zone *string `json:"zone,omitempty"`
}

type outgoingLocationUpdate struct {
	ID         *string `json:"id"`
	HasChanged bool    `json:"hasChanged"`
}

var RedisPool = &redis.Pool{
	MaxIdle:     3,
	IdleTimeout: 240 * time.Second,
	Dial: func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", ":6379")
		if err != nil {
			return nil, err
		}
		/*if _, err := c.Do("AUTH", password); err != nil {
			c.Close()
			return nil, err
		}*/
		return c, err
	},
	TestOnBorrow: func(c redis.Conn, t time.Time) error {
		_, err := c.Do("PING")
		return err
	},
}

func Start(c *ninja.Connection) {

	//FIXME
	conn = c

	thingModel = NewThingModel(RedisPool, conn)
	conn.MustExportService(thingModel, "$home/services/ThingModel", &model.ServiceAnnouncement{
		Schema: "/service/thing-model",
	})

	deviceModel = NewDeviceModel(RedisPool, conn)
	conn.MustExportService(deviceModel, "$home/services/DeviceModel", &model.ServiceAnnouncement{
		Schema: "/service/device-model",
	})

	channelModel = NewChannelModel(RedisPool, conn)
	conn.MustExportService(deviceModel, "$home/services/ChannelModel", &model.ServiceAnnouncement{
		Schema: "/service/channel-model",
	})

	roomModel = NewRoomModel(RedisPool, conn)
	conn.MustExportService(roomModel, "$home/services/RoomModel", &model.ServiceAnnouncement{
		Schema: "/service/room-model",
	})

	if config.Bool(false, "clearcloud") {
		log.Infof("Clearing all cloud data in 5 seconds")

		time.Sleep(time.Second * 5)

		thingModel.ClearCloud()
		channelModel.ClearCloud()
		deviceModel.ClearCloud()
		roomModel.ClearCloud()

		log.Infof("All cloud data cleared? Probably.")

		return
	}

	roomModel.MustSync()
	deviceModel.MustSync()
	channelModel.MustSync()
	thingModel.MustSync()

	driverModel = NewDriverModel(RedisPool, conn)

	startManagingDrivers()
	startManagingDevices()
	startMonitoringLocations()

	//spew.Dump(thingModel.FetchAll())
	//spew.Dump(thingModel.FetchByType("light"))
}

func startDriver(node string, driverID string, config *string) error {

	var rawConfig json.RawMessage
	if config != nil {
		rawConfig = []byte(*config)
	} else {
		rawConfig = []byte("{}")
	}

	client := conn.GetServiceClient(fmt.Sprintf("$node/%s/driver/%s", node, driverID))
	err := client.Call("start", &rawConfig, nil, 10*time.Second)

	if err != nil {
		jsonError, ok := err.(*json2.Error)
		if ok {
			if jsonError.Code == json2.E_INVALID_REQ {

				err := driverModel.DeleteConfig(driverID)
				if err != nil {
					log.Warningf("Driver %s could not parse its config. Also, we couldn't clear it! errors:%s and %s", driverID, jsonError.Message, err)
				} else {
					log.Warningf("Driver %s could not parse its config, so we cleared it from redis. error:%s", driverID, jsonError.Message)
				}

				return startDriver(node, driverID, nil)
			}
		}
	}

	return err
}

func startManagingDrivers() {

	conn.Subscribe("$node/:node/driver/:driver/event/announce", func(announcement *json.RawMessage, values map[string]string) bool {

		node, driver := values["node"], values["driver"]

		log.Infof("Got driver announcement node:%s driver:%s announcement:%s", node, driver, announcement)

		if announcement == nil {
			log.Warningf("Nil driver announcement from node:%s driver:%s", node, driver)
			return true
		}

		module := &model.Module{}
		err := json.Unmarshal(*announcement, module)

		if announcement == nil {
			log.Warningf("Could not parse announcement from node:%s driver:%s error:%s", node, driver, err)
			return true
		}

		err = driverModel.Create(module)
		if err != nil {
			log.Warningf("Failed to save driver announcement for %s error:%s", driver, err)
		}

		config, err := driverModel.GetConfig(values["driver"])

		if err != nil {
			log.Warningf("Failed to retrieve config for driver %s error:%s", driver, err)
		} else {
			err = startDriver(node, driver, config)
			if err != nil {
				log.Warningf("Failed to start driver: %s error:%s", driver, err)
			}
		}

		return true
	})

	conn.Subscribe("$node/:node/driver/:driver/event/config", func(config *json.RawMessage, values map[string]string) bool {
		log.Infof("Got driver config node:%s driver:%s config:%s", values["node"], values["driver"], *config)

		if config != nil {
			err := driverModel.SetConfig(values["driver"], string(*config))

			if err != nil {
				log.Warningf("Failed to save config for driver: %s error: %s", values["driver"], err)
			}
		} else {
			log.Infof("Nil config recevied from node:%s driver:%s", values["node"], values["driver"])
		}

		return true
	})

}

func startManagingDevices() {

	conn.Subscribe("$device/:id/event/announce", func(announcement *json.RawMessage, values map[string]string) bool {

		id := values["id"]

		log.Infof("Got device announcement device:%s announcement:%s", id, announcement)

		if announcement == nil {
			log.Warningf("Nil driver announcement from device:%s", id)
			return true
		}

		device := &model.Device{}
		err := json.Unmarshal(*announcement, device)

		if announcement == nil {
			log.Warningf("Could not parse announcement from device:%s error:%s", id, err)
			return true
		}

		err = deviceModel.Create(device)
		if err != nil {
			log.Warningf("Failed to save device announcement for device:%s error:%s", id, err)
		}

		return true
	})

	conn.Subscribe("$device/:device/channel/:channel/event/announce", func(announcement *json.RawMessage, values map[string]string) bool {

		deviceID, channelID := values["device"], values["channel"]

		log.Infof("Got channel announcement device:%s channel:%s announcement:%s", deviceID, channelID, announcement)

		if announcement == nil {
			log.Warningf("Nil channel announcement from device:%s channel:%s", deviceID, channelID)
			return true
		}

		channel := &model.Channel{}
		err := json.Unmarshal(*announcement, channel)

		if announcement == nil {
			log.Warningf("Could not parse channel announcement from device:%s channel:%s error:%s", deviceID, channelID, err)
			return true
		}

		err = channelModel.Create(deviceID, channel)
		if err != nil {
			log.Warningf("Failed to save channel announcement for device:%s channel:%s error:%s", deviceID, channelID, err)
		}

		return true
	})

}

func startMonitoringLocations() {

	filter, err := mqtt.NewTopicFilter("$device/+/+/location", 0)
	if err != nil {
		log.FatalError(err, "Failed to subscribe to device locations")
	}

	receipt, err := conn.GetMqttClient().StartSubscription(func(_ *mqtt.MqttClient, message mqtt.Message) {

		deviceID := locationRegexp.FindAllStringSubmatch(message.Topic(), -1)[0][1]

		update := &incomingLocationUpdate{}
		err := json.Unmarshal(message.Payload(), update)
		if err != nil {
			log.Errorf("Failed to parse location update %s to %s : %s", message.Payload(), message.Topic(), err)
			return
		}

		thing, err := thingModel.FetchByDeviceId(deviceID)
		if err != nil {
			log.FatalError(err, fmt.Sprintf("Failed to fetch thing by device id %s", deviceID))
		}

		if update.Zone == nil {
			log.Debugf("< Incoming location update: device %s not in a zone", deviceID)
		} else {
			log.Debugf("< Incoming location update: device %s is in zone %s", deviceID, *update.Zone)
		}

		hasChangedZone := true

		if thing == nil {
			log.Debugf("Device %s is not attached to a thing. Ignoring.", deviceID)
		} else {

			if (thing.Location != nil && update.Zone != nil && *thing.Location == *update.Zone) || (thing.Location == nil && update.Zone == nil) {
				// It's already there
				log.Debugf("Thing %s (%s) (Device %s) was already in that zone.", thing.ID, thing.Name, deviceID)
				hasChangedZone = true
			} else {

				log.Debugf("Thing %s (%s) (Device %s) moved from %s to %s", thing.ID, thing.Name, deviceID, thing.Location, update.Zone)

				err = roomModel.MoveThing(thing.Location, update.Zone, thing.ID)

				if err != nil {
					log.FatalError(err, fmt.Sprintf("Failed to update location of thing %s", thing.ID))
				}

				err = thingModel.SetLocation(thing.ID, update.Zone)
				if err != nil {
					log.FatalError(err, fmt.Sprintf("Failed to update location property of thing %s", thing.ID))
				}

				if update.Zone != nil {
					room, err := roomModel.Fetch(*update.Zone)
					if err != nil {
						log.FatalError(err, fmt.Sprintf("Failed to fetch room %s", *update.Zone))
					}

					if room == nil {
						// XXX: TODO: Remove me once the cloud room model is sync'd and locatino service uses it
						log.Infof("Unknown room %s. Advising remote location service to forget it.", *update.Zone)

						pubReceipt := conn.GetMqttClient().Publish(mqtt.QoS(0), "$location/delete", message.Payload())
						<-pubReceipt
					}
				}
			}

			topic := fmt.Sprintf("$device/%s/channel/%s/%s/event/state", deviceID, "location", "location")

			payload, _ := json.Marshal(&outgoingLocationUpdate{
				ID:         update.Zone,
				HasChanged: hasChangedZone,
			})

			pubReceipt := conn.GetMqttClient().Publish(mqtt.QoS(0), topic, payload)
			<-pubReceipt

		}

	}, filter)

	if err != nil {
		log.Fatalf("Failed to subscribe to device locations: %s", err)
	}

	<-receipt
}
<commit_msg>Stop then Start all the drivers on launch (after waiting 10 sec)<commit_after>package homecloud

import (
	"encoding/json"
	"fmt"
	"regexp"
	"time"

	"git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/rpc/json2"
	"github.com/ninjasphere/redigo/redis"
)

var log = logger.GetLogger("HomeCloud")

var conn *ninja.Connection

var thingModel *ThingModel
var deviceModel *DeviceModel
var channelModel *ChannelModel
var roomModel *RoomModel
var driverModel *DriverModel

var locationRegexp = regexp.MustCompile("\\$device\\/([A-F0-9]*)\\/[^\\/]*\\/location")

type incomingLocationUpdate struct {
	Zone *string `json:"zone,omitempty"`
}

type outgoingLocationUpdate struct {
	ID         *string `json:"id"`
	HasChanged bool    `json:"hasChanged"`
}

var RedisPool = &redis.Pool{
	MaxIdle:     3,
	IdleTimeout: 240 * time.Second,
	Dial: func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", ":6379")
		if err != nil {
			return nil, err
		}
		/*if _, err := c.Do("AUTH", password); err != nil {
			c.Close()
			return nil, err
		}*/
		return c, err
	},
	TestOnBorrow: func(c redis.Conn, t time.Time) error {
		_, err := c.Do("PING")
		return err
	},
}

func Start(c *ninja.Connection) {

	//FIXME
	conn = c

	thingModel = NewThingModel(RedisPool, conn)
	conn.MustExportService(thingModel, "$home/services/ThingModel", &model.ServiceAnnouncement{
		Schema: "/service/thing-model",
	})

	deviceModel = NewDeviceModel(RedisPool, conn)
	conn.MustExportService(deviceModel, "$home/services/DeviceModel", &model.ServiceAnnouncement{
		Schema: "/service/device-model",
	})

	channelModel = NewChannelModel(RedisPool, conn)
	conn.MustExportService(deviceModel, "$home/services/ChannelModel", &model.ServiceAnnouncement{
		Schema: "/service/channel-model",
	})

	roomModel = NewRoomModel(RedisPool, conn)
	conn.MustExportService(roomModel, "$home/services/RoomModel", &model.ServiceAnnouncement{
		Schema: "/service/room-model",
	})

	if config.Bool(false, "clearcloud") {
		log.Infof("Clearing all cloud data in 5 seconds")

		time.Sleep(time.Second * 5)

		thingModel.ClearCloud()
		channelModel.ClearCloud()
		deviceModel.ClearCloud()
		roomModel.ClearCloud()

		log.Infof("All cloud data cleared? Probably.")

		return
	}

	roomModel.MustSync()
	deviceModel.MustSync()
	channelModel.MustSync()
	thingModel.MustSync()

	driverModel = NewDriverModel(RedisPool, conn)

	startManagingDrivers()
	startManagingDevices()
	startMonitoringLocations()

	go func() {
		// Give it a chance to sync first...
		time.Sleep(time.Second * 10)
		startDrivers()
	}()

}

func startDrivers() {

	do := func(name string, task string) error {
		return conn.SendNotification("$node/"+config.Serial()+"/module/"+task, name)
	}

	for _, name := range []string{"driver-go-zigbee", "driver-go-sonos", "driver-go-lifx", "driver-go-ble", "driver-go-hue", "driver-go-wemo"} {
		log.Infof("-- (Re)starting '%s'", name)

		err := do(name, "stop")
		if err != nil {
			log.Fatalf("Failed to send %s stop message! %s", name, err)
		}

		time.Sleep(time.Second * 2)

		err = do(name, "start")
		if err != nil {
			log.Fatalf("Failed to send %s start message! %s", name, err)
		}
	}

}

func startDriver(node string, driverID string, config *string) error {

	var rawConfig json.RawMessage
	if config != nil {
		rawConfig = []byte(*config)
	} else {
		rawConfig = []byte("{}")
	}

	client := conn.GetServiceClient(fmt.Sprintf("$node/%s/driver/%s", node, driverID))
	err := client.Call("start", &rawConfig, nil, 10*time.Second)

	if err != nil {
		jsonError, ok := err.(*json2.Error)
		if ok {
			if jsonError.Code == json2.E_INVALID_REQ {

				err := driverModel.DeleteConfig(driverID)
				if err != nil {
					log.Warningf("Driver %s could not parse its config. Also, we couldn't clear it! errors:%s and %s", driverID, jsonError.Message, err)
				} else {
					log.Warningf("Driver %s could not parse its config, so we cleared it from redis. error:%s", driverID, jsonError.Message)
				}

				return startDriver(node, driverID, nil)
			}
		}
	}

	return err
}

func startManagingDrivers() {

	conn.Subscribe("$node/:node/driver/:driver/event/announce", func(announcement *json.RawMessage, values map[string]string) bool {

		node, driver := values["node"], values["driver"]

		log.Infof("Got driver announcement node:%s driver:%s announcement:%s", node, driver, announcement)

		if announcement == nil {
			log.Warningf("Nil driver announcement from node:%s driver:%s", node, driver)
			return true
		}

		module := &model.Module{}
		err := json.Unmarshal(*announcement, module)

		if announcement == nil {
			log.Warningf("Could not parse announcement from node:%s driver:%s error:%s", node, driver, err)
			return true
		}

		err = driverModel.Create(module)
		if err != nil {
			log.Warningf("Failed to save driver announcement for %s error:%s", driver, err)
		}

		config, err := driverModel.GetConfig(values["driver"])

		if err != nil {
			log.Warningf("Failed to retrieve config for driver %s error:%s", driver, err)
		} else {
			err = startDriver(node, driver, config)
			if err != nil {
				log.Warningf("Failed to start driver: %s error:%s", driver, err)
			}
		}

		return true
	})

	conn.Subscribe("$node/:node/driver/:driver/event/config", func(config *json.RawMessage, values map[string]string) bool {
		log.Infof("Got driver config node:%s driver:%s config:%s", values["node"], values["driver"], *config)

		if config != nil {
			err := driverModel.SetConfig(values["driver"], string(*config))

			if err != nil {
				log.Warningf("Failed to save config for driver: %s error: %s", values["driver"], err)
			}
		} else {
			log.Infof("Nil config recevied from node:%s driver:%s", values["node"], values["driver"])
		}

		return true
	})

}

func startManagingDevices() {

	conn.Subscribe("$device/:id/event/announce", func(announcement *json.RawMessage, values map[string]string) bool {

		id := values["id"]

		log.Infof("Got device announcement device:%s announcement:%s", id, announcement)

		if announcement == nil {
			log.Warningf("Nil driver announcement from device:%s", id)
			return true
		}

		device := &model.Device{}
		err := json.Unmarshal(*announcement, device)

		if announcement == nil {
			log.Warningf("Could not parse announcement from device:%s error:%s", id, err)
			return true
		}

		err = deviceModel.Create(device)
		if err != nil {
			log.Warningf("Failed to save device announcement for device:%s error:%s", id, err)
		}

		return true
	})

	conn.Subscribe("$device/:device/channel/:channel/event/announce", func(announcement *json.RawMessage, values map[string]string) bool {

		deviceID, channelID := values["device"], values["channel"]

		log.Infof("Got channel announcement device:%s channel:%s announcement:%s", deviceID, channelID, announcement)

		if announcement == nil {
			log.Warningf("Nil channel announcement from device:%s channel:%s", deviceID, channelID)
			return true
		}

		channel := &model.Channel{}
		err := json.Unmarshal(*announcement, channel)

		if announcement == nil {
			log.Warningf("Could not parse channel announcement from device:%s channel:%s error:%s", deviceID, channelID, err)
			return true
		}

		err = channelModel.Create(deviceID, channel)
		if err != nil {
			log.Warningf("Failed to save channel announcement for device:%s channel:%s error:%s", deviceID, channelID, err)
		}

		return true
	})

}

func startMonitoringLocations() {

	filter, err := mqtt.NewTopicFilter("$device/+/+/location", 0)
	if err != nil {
		log.FatalError(err, "Failed to subscribe to device locations")
	}

	receipt, err := conn.GetMqttClient().StartSubscription(func(_ *mqtt.MqttClient, message mqtt.Message) {

		deviceID := locationRegexp.FindAllStringSubmatch(message.Topic(), -1)[0][1]

		update := &incomingLocationUpdate{}
		err := json.Unmarshal(message.Payload(), update)
		if err != nil {
			log.Errorf("Failed to parse location update %s to %s : %s", message.Payload(), message.Topic(), err)
			return
		}

		thing, err := thingModel.FetchByDeviceId(deviceID)
		if err != nil {
			log.FatalError(err, fmt.Sprintf("Failed to fetch thing by device id %s", deviceID))
		}

		if update.Zone == nil {
			log.Debugf("< Incoming location update: device %s not in a zone", deviceID)
		} else {
			log.Debugf("< Incoming location update: device %s is in zone %s", deviceID, *update.Zone)
		}

		hasChangedZone := true

		if thing == nil {
			log.Debugf("Device %s is not attached to a thing. Ignoring.", deviceID)
		} else {

			if (thing.Location != nil && update.Zone != nil && *thing.Location == *update.Zone) || (thing.Location == nil && update.Zone == nil) {
				// It's already there
				log.Debugf("Thing %s (%s) (Device %s) was already in that zone.", thing.ID, thing.Name, deviceID)
				hasChangedZone = true
			} else {

				log.Debugf("Thing %s (%s) (Device %s) moved from %s to %s", thing.ID, thing.Name, deviceID, thing.Location, update.Zone)

				err = roomModel.MoveThing(thing.Location, update.Zone, thing.ID)

				if err != nil {
					log.FatalError(err, fmt.Sprintf("Failed to update location of thing %s", thing.ID))
				}

				err = thingModel.SetLocation(thing.ID, update.Zone)
				if err != nil {
					log.FatalError(err, fmt.Sprintf("Failed to update location property of thing %s", thing.ID))
				}

				if update.Zone != nil {
					room, err := roomModel.Fetch(*update.Zone)
					if err != nil {
						log.FatalError(err, fmt.Sprintf("Failed to fetch room %s", *update.Zone))
					}

					if room == nil {
						// XXX: TODO: Remove me once the cloud room model is sync'd and locatino service uses it
						log.Infof("Unknown room %s. Advising remote location service to forget it.", *update.Zone)

						pubReceipt := conn.GetMqttClient().Publish(mqtt.QoS(0), "$location/delete", message.Payload())
						<-pubReceipt
					}
				}
			}

			topic := fmt.Sprintf("$device/%s/channel/%s/%s/event/state", deviceID, "location", "location")

			payload, _ := json.Marshal(&outgoingLocationUpdate{
				ID:         update.Zone,
				HasChanged: hasChangedZone,
			})

			pubReceipt := conn.GetMqttClient().Publish(mqtt.QoS(0), topic, payload)
			<-pubReceipt

		}

	}, filter)

	if err != nil {
		log.Fatalf("Failed to subscribe to device locations: %s", err)
	}

	<-receipt
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/mundipagg/boleto-api/config"
)

//ListCert Lista os Certificados necessários e chama o método que faz a cópia
func ListCert() error {

	list := []string{
		config.Get().CertBoletoPathCrt,
		config.Get().CertBoletoPathKey,
		config.Get().CertBoletoPathCa,
		config.Get().CertICP_PathPkey,
		config.Get().CertICP_PathChainCertificates,
	}

	var err error

	for _, v := range list {

		err = copyCert(v)

		if err != nil {
			return err
		}

	}

	return err

}

func copyCert(c string) error {
	execPath, _ := os.Getwd()

	f := strings.Split(c, "/")

	fName := f[len(f)-1]

	destwd := fmt.Sprintf("%s/boleto_cert/%s", execPath, fName)

	srcFile, err := os.Open(execPath + "/boleto_orig/" + fName)
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}
	defer srcFile.Close()

	destFile, err := os.Create(destwd)
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}
	defer destFile.Close()

	_, err = io.Copy(destFile, srcFile)
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}

	err = destFile.Sync()
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}

	err = os.Chmod(destwd, 0777)
	if err != nil {
		fmt.Println("Error: ", err.Error())
		return err
	}

	fmt.Println("Cert Copy Sucessful: ", destwd)

	return err
}
<commit_msg>:fire: Remove destwd desnecessario<commit_after>package util

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/mundipagg/boleto-api/config"
)

//ListCert Lista os Certificados necessários e chama o método que faz a cópia
func ListCert() error {

	list := []string{
		config.Get().CertBoletoPathCrt,
		config.Get().CertBoletoPathKey,
		config.Get().CertBoletoPathCa,
		config.Get().CertICP_PathPkey,
		config.Get().CertICP_PathChainCertificates,
	}

	var err error

	for _, v := range list {

		err = copyCert(v)

		if err != nil {
			return err
		}

	}

	return err

}

func copyCert(c string) error {
	execPath, _ := os.Getwd()

	f := strings.Split(c, "/")

	fName := f[len(f)-1]

	srcFile, err := os.Open(execPath + "/boleto_orig/" + fName)
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}
	defer srcFile.Close()

	destFile, err := os.Create(c)
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}
	defer destFile.Close()

	_, err = io.Copy(destFile, srcFile)
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}

	err = destFile.Sync()
	if err != nil {
		fmt.Println("Error:", err.Error())
		return err
	}

	err = os.Chmod(c, 0777)
	if err != nil {
		fmt.Println("Error: ", err.Error())
		return err
	}

	fmt.Println("Cert Copy Sucessful: ", c)

	return err
}
<|endoftext|>


<commit_before>package util

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"os"
<<<<<<< HEAD
	"path"
=======
>>>>>>> 65ff4533ecaacfd494b95dec6cef179afb6c9bdb
	"strconv"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
<<<<<<< HEAD
	"github.com/mitchellh/go-homedir"
=======
>>>>>>> 65ff4533ecaacfd494b95dec6cef179afb6c9bdb
)

func ToBytes(in interface{}) []byte {
	b, err := json.Marshal(in)
	if err != nil {
		panic("toBytes is unable to marshal input")
	}
	return b
}

func DumpJSON(descr string, in interface{}) {
	fmt.Printf("%s ------------------------- json dump start ---------------------------------------\n", descr)
	out, err := json.MarshalIndent(in, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}

	os.Stdout.Write(out)
	fmt.Printf("\n%s ------------------------- json dump end ---------------------------------------\n\n", descr)
}

func Dump(descr string, in interface{}) {
	fmt.Printf("%s ------------------------- dump start ---------------------------------------\n", descr)
	spew.Dump(in)
	fmt.Printf("%s -------------------------  dump end  ---------------------------------------\n\n", descr)
}

func SafeUnquote(in string) (string, error) {
	if strings.HasPrefix(in, "\"") && strings.HasSuffix(in, "\"") {
		q, err := strconv.Unquote(in)
		if err != nil {
			return "", err
		}

		return q, nil
	}

	return in, nil
}

//WaitForCondition is a testify Condition for timeout based testing
func WaitForCondition(d time.Duration, testFn func() bool) bool {
	if d < time.Second {
		panic("WaitForCondition: test duration to small")
	}

	test := time.Tick(500 * time.Millisecond)
	timeout := time.Tick(d)

	check := make(chan struct{}, 1)
	done := make(chan struct{}, 1)
	defer close(check)
	defer close(done)

	go func() {
		for {
			select {
			case <-done:
				return
			case <-test:
				if testFn() {
					check <- struct{}{}
					return
				}
			}
		}
	}()

	for {
		select {
		case <-check:
			return true
		case <-timeout:
			done <- struct{}{}
			return false
		}
	}
}

func RandomizeBytes(in []byte) []byte {
	bs := make([]byte, 8)
	binary.LittleEndian.PutUint64(bs, uint64(time.Now().Unix()))
	return append(in, bs...)
}

func round(num float64) int {
	return int(num + math.Copysign(0.5, num))
}

func ToFixedRounded(num float64, precision int) float64 {
	output := math.Pow(10, float64(precision))
	return float64(round(num*output)) / output
}

func ToFixed(num float64, precision int) float64 {
	output := math.Pow(10, float64(precision))
	return float64(num*output) / output
}

func JoinHome(p string) (string, error) {
	home, err := homedir.Dir()
	if err != nil {
		return "", err
	}

	return path.Join(home, p), nil
}
<commit_msg>autopush@1508782803<commit_after>package util

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"os"

	"path"
	"strconv"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/mitchellh/go-homedir"
)

func ToBytes(in interface{}) []byte {
	b, err := json.Marshal(in)
	if err != nil {
		panic("toBytes is unable to marshal input")
	}
	return b
}

func DumpJSON(descr string, in interface{}) {
	fmt.Printf("%s ------------------------- json dump start ---------------------------------------\n", descr)
	out, err := json.MarshalIndent(in, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}

	os.Stdout.Write(out)
	fmt.Printf("\n%s ------------------------- json dump end ---------------------------------------\n\n", descr)
}

func Dump(descr string, in interface{}) {
	fmt.Printf("%s ------------------------- dump start ---------------------------------------\n", descr)
	spew.Dump(in)
	fmt.Printf("%s -------------------------  dump end  ---------------------------------------\n\n", descr)
}

func SafeUnquote(in string) (string, error) {
	if strings.HasPrefix(in, "\"") && strings.HasSuffix(in, "\"") {
		q, err := strconv.Unquote(in)
		if err != nil {
			return "", err
		}

		return q, nil
	}

	return in, nil
}

//WaitForCondition is a testify Condition for timeout based testing
func WaitForCondition(d time.Duration, testFn func() bool) bool {
	if d < time.Second {
		panic("WaitForCondition: test duration to small")
	}

	test := time.Tick(500 * time.Millisecond)
	timeout := time.Tick(d)

	check := make(chan struct{}, 1)
	done := make(chan struct{}, 1)
	defer close(check)
	defer close(done)

	go func() {
		for {
			select {
			case <-done:
				return
			case <-test:
				if testFn() {
					check <- struct{}{}
					return
				}
			}
		}
	}()

	for {
		select {
		case <-check:
			return true
		case <-timeout:
			done <- struct{}{}
			return false
		}
	}
}

func RandomizeBytes(in []byte) []byte {
	bs := make([]byte, 8)
	binary.LittleEndian.PutUint64(bs, uint64(time.Now().Unix()))
	return append(in, bs...)
}

func round(num float64) int {
	return int(num + math.Copysign(0.5, num))
}

func ToFixedRounded(num float64, precision int) float64 {
	output := math.Pow(10, float64(precision))
	return float64(round(num*output)) / output
}

func ToFixed(num float64, precision int) float64 {
	output := math.Pow(10, float64(precision))
	return float64(num*output) / output
}

func JoinHome(p string) (string, error) {
	home, err := homedir.Dir()
	if err != nil {
		return "", err
	}

	return path.Join(home, p), nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The LUCI 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 main

import (
	"fmt"
	"io"
	"io/ioutil"
	"reflect"
	"strconv"
	"strings"
	"testing"
	"testing/iotest"

	"github.com/kr/pretty"
	"go.chromium.org/luci/common/isolated"
)

func fakeOsOpen(name string) (io.ReadCloser, error) {
	var r io.Reader
	switch {
	case name == "/err/open":
		return nil, fmt.Errorf("failed to open %q", name)
	case name == "/err/read":
		r = iotest.TimeoutReader(strings.NewReader("sample-file"))
	case strings.HasPrefix(name, "/size/"):
		size, _ := strconv.Atoi(strings.TrimPrefix(name, "/size/"))
		r = strings.NewReader(strings.Repeat("x", size))
	default:
		r = strings.NewReader(fmt.Sprintf("I am the file with name %q", name))
	}
	return ioutil.NopCloser(r), nil
}

func TestItemBundle(t *testing.T) {
	oldOpen := osOpen
	osOpen = fakeOsOpen
	defer func() { osOpen = oldOpen }()

	// All tests use an archive threshold of 5000 bytes.
	const threshold = 5000

	testCases := []struct {
		desc        string
		sizes       []int64
		wantBundles []int                // The number of items in each bundle.
		wantDigests []isolated.HexDigest // The hash of each bundle's tar.
		wantSize    []int                // The size of each bundle's tar.
	}{
		{
			desc:        "one bundle",
			sizes:       []int64{100, 200, 300},
			wantBundles: []int{3},
			wantSize:    []int{1024 + 1024 + 1024 + 1024},
			wantDigests: []isolated.HexDigest{
				"003afa69f9b267a2cf267cc5ff715968cb05354b",
			},
		},
		{
			desc:        "multi bundle",
			sizes:       []int64{100, 200, 300, 400, 500, 2000},
			wantBundles: []int{3, 2, 1},
			wantSize: []int{
				1024 + 1024 + 1024 + 1024,
				1024 + 1024 + 1024,
				1024 + 2560,
			},
			wantDigests: []isolated.HexDigest{
				"003afa69f9b267a2cf267cc5ff715968cb05354b",
				"7f8cedcc12dc7ddcbe0849a46628b2748f5e84aa",
				"e4a390dbfa081e564fe61368bac77bb9ef30a4e7",
			},
		},
		{
			desc:        "file below boundary",
			sizes:       []int64{511},
			wantBundles: []int{1},
			wantSize:    []int{2048},
			wantDigests: []isolated.HexDigest{
				"6103d5cc5fc494f9490107d61a952c6ce1e48be6",
			},
		},
		{
			desc:        "file on boundary",
			sizes:       []int64{512},
			wantBundles: []int{1},
			wantSize:    []int{2048},
			wantDigests: []isolated.HexDigest{
				"8c63e33b6264ade5f62ab62b322de8d8ac5eeb8b",
			},
		},
		{
			desc:        "all items over threshold",
			sizes:       []int64{5000, 6000, 4500},
			wantBundles: []int{1, 1, 1},
			wantSize:    []int{6656, 7680, 6144},
			wantDigests: []isolated.HexDigest{
				"2cd4cb7a965d9ca34cbb692e3885f7074f28a4a4",
				"925b04df322c98d013b470727df5644644d93e70",
				"1f576aa4d44aea793c751bf03de2d513a2dd65eb",
			},
		},
	}

	for _, tc := range testCases {
		// Construct the input items and expected output.
		var items []*Item
		for _, size := range tc.sizes {
			items = append(items, &Item{
				RelPath: fmt.Sprintf("./%d", size),
				Path:    fmt.Sprintf("/size/%d", size),
				Size:    size,
			})
		}
		var wantBundles []*ItemBundle
		temp := items
		for _, bSize := range tc.wantBundles {
			bundle := &ItemBundle{
				Items: temp[:bSize],
			}
			wantBundles = append(wantBundles, bundle)
			temp = temp[bSize:]
			for _, item := range bundle.Items {
				bundle.ItemSize += item.Size
			}
		}

		bundles := ShardItems(items, threshold)
		if !reflect.DeepEqual(bundles, wantBundles) {
			t.Errorf("%s:\ngot  %s\nwant %s", tc.desc, pretty.Sprint(bundles), pretty.Sprint(wantBundles))
		}

		for i, bundle := range bundles {
			digest, size, err := bundle.Digest()
			if err != nil {
				t.Errorf("%s: bundle[%d].Digest gave err %v, want nil", tc.desc, i, err)
				continue
			}
			if digest != tc.wantDigests[i] {
				t.Errorf("%s: bundle[%d].Digest() hash = %q, want %q", tc.desc, i, digest, tc.wantDigests[i])
			}
			if size != int64(tc.wantSize[i]) {
				t.Errorf("%s: bundle[%d].Digest() size = %d, want %d", tc.desc, i, size, tc.wantSize[i])
			}

			rc, err := bundle.Contents()
			if err != nil {
				t.Errorf("%s: bundle[%d].Contents gave err %v, want nil", tc.desc, i, err)
				continue
			}
			tar, err := ioutil.ReadAll(rc)
			rc.Close()
			if err != nil {
				t.Errorf("%s: reading bundle[%d].Contents gave err %v, want nil", tc.desc, i, err)
			}
			if got := len(tar); got != tc.wantSize[i] {
				t.Errorf("%s: bundle[%d].Contents had len %d, want %d", tc.desc, i, got, tc.wantSize[i])
			}
		}
	}
}

func TestItemBundle_Errors(t *testing.T) {
	oldOpen := osOpen
	osOpen = fakeOsOpen
	defer func() { osOpen = oldOpen }()

	testItems := []*Item{
		{
			// File that fails to open.
			RelPath: "./open",
			Path:    "/err/open",
			Size:    123,
		},
		{
			// File that fails to read.
			RelPath: "./read",
			Path:    "/err/read",
			Size:    123,
		},
	}

	bundles := ShardItems(testItems, 0)
	if len(bundles) != 2 {
		t.Errorf("len(bundles) = %d, want 2", len(bundles))
	}
	for _, bundle := range bundles {
		if _, _, err := bundle.Digest(); err == nil {
			t.Errorf("Path %q, bundle.Digest gave nil error; want some error", bundle.Items[0].Path)
		}
		rc, err := bundle.Contents()
		if err != nil {
			t.Errorf("Path %q, bundle.Contents gave error %v; want nil error", bundle.Items[0].Path, err)
			continue
		}
		_, err = ioutil.ReadAll(rc)
		rc.Close()
		if err == nil {
			t.Errorf("Path %q, reading contents gave nil error, want some error", bundle.Items[0].Path)
		}
	}
}
<commit_msg>[isolate] refactor tests of tar_archiver.<commit_after>// Copyright 2016 The LUCI 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 main

import (
	"fmt"
	"io"
	"io/ioutil"
	"strconv"
	"strings"
	"testing"
	"testing/iotest"

	"go.chromium.org/luci/common/isolated"

	. "github.com/smartystreets/goconvey/convey"
)

func TestItemBundle(t *testing.T) {
	// TODO(tandrii): instead of monkey patching global vars, refactor to use Go
	// Interfaces and allow t.parallel().
	oldOpen := osOpen
	osOpen = fakeOsOpen
	defer func() { osOpen = oldOpen }()

	Convey("Item Bundling works", t, func() {
		Convey("one bundle", func() {
			r, err := runShardItems([]int64{100, 200, 300}, 5000)
			So(err, ShouldBeNil)
			So(r, ShouldResemble, &shards{
				bundles: []int{3},
				sizes:   []int64{1024 + 1024 + 1024 + 1024},
				digests: []isolated.HexDigest{
					"003afa69f9b267a2cf267cc5ff715968cb05354b",
				},
			})
		})

		Convey("multi bundle", func() {
			r, err := runShardItems([]int64{100, 200, 300, 400, 500, 2000}, 5000)
			So(err, ShouldBeNil)
			So(r, ShouldResemble, &shards{
				bundles: []int{3, 2, 1},
				sizes: []int64{
					1024 + 1024 + 1024 + 1024,
					1024 + 1024 + 1024,
					1024 + 2560,
				},
				digests: []isolated.HexDigest{
					"003afa69f9b267a2cf267cc5ff715968cb05354b",
					"7f8cedcc12dc7ddcbe0849a46628b2748f5e84aa",
					"e4a390dbfa081e564fe61368bac77bb9ef30a4e7",
				},
			})
			// TODO(tandrii): check determinism irrespective of order of sizes (aka
			// items in a list).
		})

		Convey("file below boundary", func() {
			r, err := runShardItems([]int64{511}, 5000)
			So(err, ShouldBeNil)
			So(r, ShouldResemble, &shards{
				bundles: []int{1},
				sizes:   []int64{2048},
				digests: []isolated.HexDigest{
					"6103d5cc5fc494f9490107d61a952c6ce1e48be6",
				},
			})
		})

		Convey("file on boundary", func() {
			r, err := runShardItems([]int64{512}, 5000)
			So(err, ShouldBeNil)
			So(r, ShouldResemble, &shards{
				bundles: []int{1},
				sizes:   []int64{2048},
				digests: []isolated.HexDigest{
					"8c63e33b6264ade5f62ab62b322de8d8ac5eeb8b",
				},
			})
		})

		Convey("all items over threshold", func() {
			r, err := runShardItems([]int64{5000, 6000, 4500}, 5000)
			So(err, ShouldBeNil)
			So(r, ShouldResemble, &shards{
				bundles: []int{1, 1, 1},
				sizes:   []int64{6656, 7680, 6144},
				digests: []isolated.HexDigest{
					"2cd4cb7a965d9ca34cbb692e3885f7074f28a4a4",
					"925b04df322c98d013b470727df5644644d93e70",
					"1f576aa4d44aea793c751bf03de2d513a2dd65eb",
				},
			})
		})
	})

	Convey("Item Bundling with Errors", t, func() {
		testItems := []*Item{
			{
				// File that fails to open.
				RelPath: "./open",
				Path:    "/err/open",
				Size:    123,
			},
			{
				// File that fails to read.
				RelPath: "./read",
				Path:    "/err/read",
				Size:    123,
			},
		}

		bundles := ShardItems(testItems, 0)
		So(len(bundles), ShouldEqual, 2)
		for _, bundle := range bundles {
			if _, _, err := bundle.Digest(); err == nil {
				t.Errorf("Path %q, bundle.Digest gave nil error; want some error", bundle.Items[0].Path)
			}
			rc, err := bundle.Contents()
			if err != nil {
				t.Errorf("Path %q, bundle.Contents gave error %v; want nil error", bundle.Items[0].Path, err)
				continue
			}
			_, err = ioutil.ReadAll(rc)
			rc.Close()
			if err == nil {
				t.Errorf("Path %q, reading contents gave nil error, want some error", bundle.Items[0].Path)
			}
		}
	})
}

func fakeOsOpen(name string) (io.ReadCloser, error) {
	var r io.Reader
	switch {
	case name == "/err/open":
		return nil, fmt.Errorf("failed to open %q", name)
	case name == "/err/read":
		r = iotest.TimeoutReader(strings.NewReader("sample-file"))
	case strings.HasPrefix(name, "/size/"):
		size, _ := strconv.Atoi(strings.TrimPrefix(name, "/size/"))
		r = strings.NewReader(strings.Repeat("x", size))
	default:
		r = strings.NewReader(fmt.Sprintf("I am the file with name %q", name))
	}
	return ioutil.NopCloser(r), nil
}

type shards struct {
	bundles []int
	sizes   []int64
	digests []isolated.HexDigest
}

func runShardItems(sizes []int64, threshold int64) (*shards, error) {
	// Construct the input items, generating path based on size.
	var items []*Item
	for _, size := range sizes {
		items = append(items, &Item{
			RelPath: fmt.Sprintf("./%d", size),
			Path:    fmt.Sprintf("/size/%d", size),
			Size:    size,
		})
	}
	r := &shards{}
	bundles := ShardItems(items, threshold)
	for i, b := range bundles {
		digest, size, err := b.Digest()
		if err != nil {
			return nil, fmt.Errorf("bundle[%d] Digest failed: %s", i, err)
		}
		rc, err := b.Contents()
		if err != nil {
			return nil, fmt.Errorf("bundle[%d] Contents failed: %s", i, err)
		}
		defer rc.Close()
		tar, err := ioutil.ReadAll(rc)
		if err != nil {
			return nil, fmt.Errorf("bundle[%d] reading Contents failed: %s", i, err)
		}
		if got := len(tar); int64(got) != size {
			return nil, fmt.Errorf("bundle[%d] Contents size %d differs from Digest size %d", i, got, size)
		}
		r.bundles = append(r.bundles, len(b.Items))
		r.sizes = append(r.sizes, size)
		r.digests = append(r.digests, digest)
	}
	return r, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Markus Dittrich. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// gobble is a simple program for retrieving files via
// http, https, and ftp á la wget

package main

import (
	"flag"
	"fmt"
	"io"
	//	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// command line settings
var (
	urlTarget   = flag.String("u", "", "url to download")
	outFileName = flag.String("o", "", "name of output file")
	toStdOut    = flag.Bool("s", false, "output to stdout")
)

// general settings
var (
	numBytes = 40960 // chunk site for reading and writing
	version  = 0.1   // gobble version
)

// progress bar
var progressBar = "-----------------------------------"

func main() {

	flag.Parse()
	if *urlTarget == "" {
		usage()
	}

	// start http client
	client := &http.Client{}
	resp, err := client.Get(*urlTarget)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	// open output file; nil if stdout was requested
	file := os.Stdout
	if !*toStdOut {
		file, err = openOutfile()
		if err != nil {
			log.Fatal("failed to open output file: ", err)
		}
		defer file.Close()
		printInfo(*urlTarget, resp)
	}

	totalBytes := resp.ContentLength
	bytesRead, err := copyContent(resp.Body, file, totalBytes)
	if err != nil {
		log.Fatal(err)
	}

	if !*toStdOut {
		fmt.Println(statusString(bytesRead, totalBytes))
	}
}

// copyContent reads the body content from the http connection and then
// copies it either to the provided file or stdou
func copyContent(body io.ReadCloser, file *os.File, totalBytes int64) (int, error) {

	buffer := make([]byte, numBytes)
	bytesRead := 0
	n := 0
	for {
		// read numBytes
		var err error
		n, err = io.ReadFull(body, buffer)
		if err != nil {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				break // this is the regular end-of-file - we are done
			} else {
				return 0, err
			}
		}

		// write numBytes
		nOut, err := bufWrite(buffer, file)
		if err != nil {
			log.Fatal(err)
		} else if nOut != n {
			return 0, fmt.Errorf("% bytes read but %d byte written", n, nOut)
		}

		bytesRead += n
		if !*toStdOut {
			fmt.Print(statusString(bytesRead, totalBytes))
		}
	}

	// write whatever is left
	_, err := bufWrite(buffer[:n], file)
	if err != nil {
		return 0, err
	}

	bytesRead += n
	return bytesRead, nil
}

// bufWrite writes content either to stdout or the requested output file
func bufWrite(content []byte, file *os.File) (int, error) {
	n, err := file.Write(content)
	if err != nil {
		return n, err
	}
	return n, nil
}

// openOutfile opens the output file if one was requested
// Otherwise, we assume the output file is index.html
func openOutfile() (*os.File, error) {

	fileName := *outFileName
	if fileName == "" {

		// can we extract a
		urlInfo, err := url.Parse(*urlTarget)
		if err != nil {
			return nil, err
		}
		if fileName = filepath.Base(urlInfo.Path); fileName == "." || fileName == "/" {
			fileName = "index.html"
		}
	}

	// if fileName already exists we bail
	if _, err := os.Stat(fileName); err == nil {
		return nil, fmt.Errorf("%s already exists\n", fileName)
	}

	file, err := os.Create(fileName)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// statusString returns the status string corresponding to the given
// number of bytes read.
// NOTE: Sites which don't provide the content length return a value of
// -1 for totalbytes. In this case we print a simpler content string
func statusString(bytesRead int, totalBytes int64) string {
	var formatString string
	if totalBytes == -1 {
		progressString := "<=>"
		formatString = fmt.Sprintf("progress: %10d Bytes    %-30s  \r", bytesRead,
			progressString)
	} else {
		percentage := float64(bytesRead) / float64(totalBytes) * 100
		progressString := strings.Join(
			[]string{progressBar[1 : 2+int(percentage/4)], ">"}, "")
		formatString = fmt.Sprintf("progress: %10d Bytes    %-30s  %2.1f%%\r", bytesRead,
			progressString, percentage)
	}
	return formatString
}

// printInfo prints a brief informative header about the connection
func printInfo(urlTarget string, resp *http.Response) {
	fmt.Println("********* This is gobble version ", version, " ***************")

	urlInfo, err := url.Parse(urlTarget)
	if err != nil {
		return
	}
	cname, _ := net.LookupCNAME(urlInfo.Host)
	ips, _ := net.LookupIP(cname)
	fmt.Println("Connecting to", cname, "  ", ips)
	fmt.Printf("Status %s   Protocol %s  TransferEncoding %v\n", resp.Status,
		resp.Proto, resp.TransferEncoding)
	fmt.Printf("Content Length: %d bytes\n", resp.ContentLength)
	fmt.Println()
}

// usage prints the package usage and then exits
func usage() {
	fmt.Println(os.Args[0], "[options]", "\n\noptions:")
	flag.PrintDefaults()
	os.Exit(1)
}
<commit_msg>Added ability to deal with URLs of the type www.xxx.yy.<commit_after>// Copyright 2014 Markus Dittrich. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// gobble is a simple program for retrieving files via
// http, https, and ftp á la wget

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// command line settings
var (
	urlTarget   = flag.String("u", "", "url to download")
	outFileName = flag.String("o", "", "name of output file")
	toStdout    = flag.Bool("s", false, "output to stdout")
)

// general settings
var (
	numBytes = 40960 // chunk site for reading and writing
	version  = 0.1   // gobble version
)

// progress bar
var progressBar = "-----------------------------------"

func main() {

	flag.Parse()
	if *urlTarget == "" {
		usage()
	}
	url := normalizeURLTarget(*urlTarget)

	// start http client
	client := &http.Client{}
	resp, err := client.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	// open output file; nil if stdout was requested
	file := os.Stdout
	if !*toStdout {
		file, err = openOutfile(*outFileName, url)
		if err != nil {
			log.Fatal("failed to open output file: ", err)
		}
		defer file.Close()
		printInfo(url, resp)
	}

	totalBytes := resp.ContentLength
	bytesRead, err := copyContent(resp.Body, file, totalBytes, *toStdout)
	if err != nil {
		log.Fatal(err)
	}

	if !*toStdout {
		fmt.Println(statusString(bytesRead, totalBytes))
	}
}

// copyContent reads the body content from the http connection and then
// copies it either to the provided file or stdou
func copyContent(body io.ReadCloser, file *os.File, totalBytes int64,
	wantStdout bool) (int, error) {

	buffer := make([]byte, numBytes)
	bytesRead := 0
	n := 0
	for {
		// read numBytes
		var err error
		n, err = io.ReadFull(body, buffer)
		if err != nil {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				break // this is the regular end-of-file - we are done
			} else {
				return 0, err
			}
		}

		// write numBytes
		nOut, err := bufWrite(buffer, file)
		if err != nil {
			log.Fatal(err)
		} else if nOut != n {
			return 0, fmt.Errorf("% bytes read but %d byte written", n, nOut)
		}

		bytesRead += n
		if !wantStdout {
			fmt.Print(statusString(bytesRead, totalBytes))
		}
	}

	// write whatever is left
	_, err := bufWrite(buffer[:n], file)
	if err != nil {
		return 0, err
	}

	bytesRead += n
	return bytesRead, nil
}

// bufWrite writes content either to stdout or the requested output file
func bufWrite(content []byte, file *os.File) (int, error) {
	n, err := file.Write(content)
	if err != nil {
		return n, err
	}
	return n, nil
}

// openOutfile opens the output file if one was requested
// Otherwise, we assume the output file is index.html
func openOutfile(outFileName, urlTarget string) (*os.File, error) {

	fileName := outFileName
	if fileName == "" {

		// can we extract a
		urlInfo, err := url.Parse(urlTarget)
		if err != nil {
			return nil, err
		}
		if fileName = filepath.Base(urlInfo.Path); fileName == "." || fileName == "/" {
			fileName = "index.html"
		}
	}

	// if fileName already exists we bail
	if _, err := os.Stat(fileName); err == nil {
		return nil, fmt.Errorf("%s already exists\n", fileName)
	}

	file, err := os.Create(fileName)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// normalizeURLTarget currently only checks if an URL starts with
// http:// and if not appends it
func normalizeURLTarget(urlTarget string) string {
	outString := urlTarget
	if !strings.HasPrefix(urlTarget, "http://") {
		outString = "http://" + urlTarget
	}
	return outString
}

// statusString returns the status string corresponding to the given
// number of bytes read.
// NOTE: Sites which don't provide the content length return a value of
// -1 for totalbytes. In this case we print a simpler content string
func statusString(bytesRead int, totalBytes int64) string {
	var formatString string
	if totalBytes == -1 {
		progressString := "<=>"
		formatString = fmt.Sprintf("progress: %10d Bytes    %-30s  \r", bytesRead,
			progressString)
	} else {
		percentage := float64(bytesRead) / float64(totalBytes) * 100
		progressString := strings.Join(
			[]string{progressBar[1 : 2+int(percentage/4)], ">"}, "")
		formatString = fmt.Sprintf("progress: %10d Bytes    %-30s  %2.1f%%\r", bytesRead,
			progressString, percentage)
	}
	return formatString
}

// printInfo prints a brief informative header about the connection
func printInfo(urlTarget string, resp *http.Response) {
	fmt.Println("********* This is gobble version ", version, " ***************")

	urlInfo, err := url.Parse(urlTarget)
	if err != nil {
		return
	}
	cname, _ := net.LookupCNAME(urlInfo.Host)
	ips, _ := net.LookupIP(cname)
	fmt.Println("Connecting to", cname, "  ", ips)
	fmt.Printf("Status %s   Protocol %s  TransferEncoding %v\n", resp.Status,
		resp.Proto, resp.TransferEncoding)
	fmt.Printf("Content Length: %d bytes\n", resp.ContentLength)
	fmt.Println()
}

// usage prints the package usage and then exits
func usage() {
	fmt.Println(os.Args[0], "[options]", "\n\noptions:")
	flag.PrintDefaults()
	os.Exit(1)
}
<|endoftext|>


<commit_before>package gomapr

import (
	"errors"
	"log"
	"math/rand"
	"sync"
)

var (
	EndOfEmit = errors.New("Nothing left to emit")
)

type Event interface{}
type Partial interface{}
type ReduceKey interface{}

type MapReduce interface {
	Emit() (Event, error)
	Map(Event) (ReduceKey, Partial)
	Reduce(ReduceKey, []Partial) (ReduceKey, Partial)
}

// Corresponds to a set of values that a reducer can join.
type partialGroup struct {
	values []Partial
	l      *sync.Mutex
}

func newPartialGroup() *partialGroup {
	return &partialGroup{
		values: make([]Partial, 0),
		l:      &sync.Mutex{},
	}
}

// Adds a value to the group.
func (p *partialGroup) add(v interface{}) {
	p.values = append(p.values, v)
}

// Replaces the contents of the partial group.
func (p *partialGroup) replace(v interface{}) {
	p.values = []Partial{v}
}

// Contains all partial groups.
type reduceWorkspace struct {
	groups map[ReduceKey]*partialGroup
	l      *sync.Mutex
}

func newReduceWorkspace() *reduceWorkspace {
	return &reduceWorkspace{
		make(map[ReduceKey]*partialGroup),
		&sync.Mutex{},
	}
}

// Returns a partial group by its key.
func (r *reduceWorkspace) getPartialGroup(key ReduceKey) *partialGroup {
	r.l.Lock()
	defer r.l.Unlock()

	partialGroup, ok := r.groups[key]
	if !ok {
		partialGroup = newPartialGroup()
		r.groups[key] = partialGroup
	}

	return partialGroup
}

// Adds a key-value pair to its appropriate partial group.
func (r *reduceWorkspace) add(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.l.Lock()
	partialGroup.add(value)
	partialGroup.l.Unlock()
}

// Replaces an existing partial group with the input arguments.
func (r *reduceWorkspace) replace(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.l.Lock()
	partialGroup.replace(value)
	partialGroup.l.Unlock()
}

// Contains configuration for a MapReduce task.
type Runner struct {
	reduceWorkspace *reduceWorkspace
	mr              MapReduce
	mapWg           *sync.WaitGroup
	reduceWg        *sync.WaitGroup
	mappers         int
	reduceFactor    float64
	unreduced       map[ReduceKey]struct{}
	unreducedL      *sync.Mutex
}

func NewRunner(mr MapReduce, mappers int, reduceFactor float64) *Runner {
	if reduceFactor < 0 || reduceFactor > 1 {
		panic("Invalid reduce factor")
	}

	return &Runner{
		reduceWorkspace: newReduceWorkspace(),
		mr:              mr,
		mapWg:           &sync.WaitGroup{},
		reduceWg:        &sync.WaitGroup{},
		mappers:         mappers,
		reduceFactor:    reduceFactor,
		unreduced:       make(map[ReduceKey]struct{}),
		unreducedL:      &sync.Mutex{},
	}
}

// Returns the map containing all groups. Only safe to call after
// the task has completed.
func (r *Runner) Groups() map[ReduceKey]Partial {
	r.reduceWorkspace.l.Lock()
	defer r.reduceWorkspace.l.Unlock()

	groups := make(map[ReduceKey]Partial)

	for k, v := range r.reduceWorkspace.groups {
		groups[k] = v.values[0]
	}

	return groups
}

// Maps the input it receives on its emitted channel, spawning
// a reduce task when appropriate.
func (r *Runner) mapWorker(emitted chan Event) {
	for val := range emitted {
		key, mapped := r.mr.Map(val)
		r.reduceWorkspace.add(key, mapped)

		// Launch reduce step probabilistically.
		if rand.Float64() < r.reduceFactor {
			r.unreducedL.Lock()
			delete(r.unreduced, key)
			r.unreducedL.Unlock()

			r.reduceWg.Add(1)
			go r.reduce(key)
		} else {
			r.unreducedL.Lock()
			r.unreduced[key] = struct{}{}
			r.unreducedL.Unlock()
		}
	}

	r.mapWg.Done()
}

// Reduces the partial group with the matching input key.
func (r *Runner) reduce(key ReduceKey) {
	partialGroup := r.reduceWorkspace.getPartialGroup(key)

	partialGroup.l.Lock()
	defer partialGroup.l.Unlock()

	if len(partialGroup.values) > 1 {
		newKey, partial := r.mr.Reduce(key, partialGroup.values)

		if key == newKey {
			partialGroup.replace(partial)
		} else {
			r.reduceWorkspace.replace(key, partial)
		}
	}

	r.reduceWg.Done()
}

// Starts the MapReduce task.
func (r *Runner) Run() {
	events := make(chan Event, r.mappers)

	// Create background mapping workers.
	for i := 0; i < r.mappers; i++ {
		r.mapWg.Add(1)
		go r.mapWorker(events)
	}

	// Emit all events.
	go func() {
		for {
			event, err := r.mr.Emit()
			if err != EndOfEmit && err != nil {
				log.Printf("Error emitting: %v", err)
				break
			}

			events <- event

			if err == EndOfEmit {
				break
			}

		}
		close(events)
	}()

	r.mapWg.Wait()

	// Reduce unreduced keys.
	for key, _ := range r.unreduced {
		r.reduceWg.Add(1)
		go r.reduce(key)
	}

	r.reduceWg.Wait()
}

// Runs MapReduce synchronously.
func (r *Runner) RunSynchronous() {
	for {
		event, err := r.mr.Emit()
		if err != EndOfEmit && err != nil {
			log.Printf("Error emitting: %v", err)
			break
		}

		key, mapped := r.mr.Map(event)
		r.reduceWorkspace.add(key, mapped)

		if err == EndOfEmit {
			break
		}

	}

	for key, _ := range r.reduceWorkspace.groups {
		r.reduceWg.Add(1)
		r.reduce(key)
	}
}
<commit_msg>Reset unreduced<commit_after>package gomapr

import (
	"errors"
	"log"
	"math/rand"
	"sync"
)

var (
	EndOfEmit = errors.New("Nothing left to emit")
)

type Event interface{}
type Partial interface{}
type ReduceKey interface{}

type MapReduce interface {
	Emit() (Event, error)
	Map(Event) (ReduceKey, Partial)
	Reduce(ReduceKey, []Partial) (ReduceKey, Partial)
}

// Corresponds to a set of values that a reducer can join.
type partialGroup struct {
	values []Partial
	l      *sync.Mutex
}

func newPartialGroup() *partialGroup {
	return &partialGroup{
		values: make([]Partial, 0),
		l:      &sync.Mutex{},
	}
}

// Adds a value to the group.
func (p *partialGroup) add(v interface{}) {
	p.values = append(p.values, v)
}

// Replaces the contents of the partial group.
func (p *partialGroup) replace(v interface{}) {
	p.values = []Partial{v}
}

// Contains all partial groups.
type reduceWorkspace struct {
	groups map[ReduceKey]*partialGroup
	l      *sync.Mutex
}

func newReduceWorkspace() *reduceWorkspace {
	return &reduceWorkspace{
		make(map[ReduceKey]*partialGroup),
		&sync.Mutex{},
	}
}

// Returns a partial group by its key.
func (r *reduceWorkspace) getPartialGroup(key ReduceKey) *partialGroup {
	r.l.Lock()
	defer r.l.Unlock()

	partialGroup, ok := r.groups[key]
	if !ok {
		partialGroup = newPartialGroup()
		r.groups[key] = partialGroup
	}

	return partialGroup
}

// Adds a key-value pair to its appropriate partial group.
func (r *reduceWorkspace) add(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.l.Lock()
	partialGroup.add(value)
	partialGroup.l.Unlock()
}

// Replaces an existing partial group with the input arguments.
func (r *reduceWorkspace) replace(key ReduceKey, value Partial) {
	partialGroup := r.getPartialGroup(key)

	partialGroup.l.Lock()
	partialGroup.replace(value)
	partialGroup.l.Unlock()
}

// Contains configuration for a MapReduce task.
type Runner struct {
	reduceWorkspace *reduceWorkspace
	mr              MapReduce
	mapWg           *sync.WaitGroup
	reduceWg        *sync.WaitGroup
	mappers         int
	reduceFactor    float64
	unreduced       map[ReduceKey]struct{}
	unreducedL      *sync.Mutex
}

func NewRunner(mr MapReduce, mappers int, reduceFactor float64) *Runner {
	if reduceFactor < 0 || reduceFactor > 1 {
		panic("Invalid reduce factor")
	}

	return &Runner{
		reduceWorkspace: newReduceWorkspace(),
		mr:              mr,
		mapWg:           &sync.WaitGroup{},
		reduceWg:        &sync.WaitGroup{},
		mappers:         mappers,
		reduceFactor:    reduceFactor,
		unreduced:       make(map[ReduceKey]struct{}),
		unreducedL:      &sync.Mutex{},
	}
}

// Returns the map containing all groups. Only safe to call after
// the task has completed.
func (r *Runner) Groups() map[ReduceKey]Partial {
	r.reduceWorkspace.l.Lock()
	defer r.reduceWorkspace.l.Unlock()

	groups := make(map[ReduceKey]Partial)

	for k, v := range r.reduceWorkspace.groups {
		groups[k] = v.values[0]
	}

	return groups
}

// Maps the input it receives on its emitted channel, spawning
// a reduce task when appropriate.
func (r *Runner) mapWorker(emitted chan Event) {
	for val := range emitted {
		key, mapped := r.mr.Map(val)
		r.reduceWorkspace.add(key, mapped)

		// Launch reduce step probabilistically.
		if rand.Float64() < r.reduceFactor {
			r.unreducedL.Lock()
			delete(r.unreduced, key)
			r.unreducedL.Unlock()

			r.reduceWg.Add(1)
			go r.reduce(key)
		} else {
			r.unreducedL.Lock()
			r.unreduced[key] = struct{}{}
			r.unreducedL.Unlock()
		}
	}

	r.mapWg.Done()
}

// Reduces the partial group with the matching input key.
func (r *Runner) reduce(key ReduceKey) {
	partialGroup := r.reduceWorkspace.getPartialGroup(key)

	partialGroup.l.Lock()
	defer partialGroup.l.Unlock()

	if len(partialGroup.values) > 1 {
		newKey, partial := r.mr.Reduce(key, partialGroup.values)

		if key == newKey {
			partialGroup.replace(partial)
		} else {
			r.reduceWorkspace.replace(key, partial)
		}
	}

	r.reduceWg.Done()
}

// Starts the MapReduce task.
func (r *Runner) Run() {
	events := make(chan Event, r.mappers)

	// Create background mapping workers.
	for i := 0; i < r.mappers; i++ {
		r.mapWg.Add(1)
		go r.mapWorker(events)
	}

	// Emit all events.
	go func() {
		for {
			event, err := r.mr.Emit()
			if err != EndOfEmit && err != nil {
				log.Printf("Error emitting: %v", err)
				break
			}

			events <- event

			if err == EndOfEmit {
				break
			}

		}
		close(events)
	}()

	r.mapWg.Wait()

	// Reduce unreduced keys.
	for key, _ := range r.unreduced {
		r.reduceWg.Add(1)
		go r.reduce(key)
	}
	r.unreduced = make(map[ReduceKey]struct{})
	r.reduceWg.Wait()
}

// Runs MapReduce synchronously.
func (r *Runner) RunSynchronous() {
	for {
		event, err := r.mr.Emit()
		if err != EndOfEmit && err != nil {
			log.Printf("Error emitting: %v", err)
			break
		}

		key, mapped := r.mr.Map(event)
		r.reduceWorkspace.add(key, mapped)

		if err == EndOfEmit {
			break
		}

	}

	for key, _ := range r.reduceWorkspace.groups {
		r.reduceWg.Add(1)
		r.reduce(key)
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 Andreas Louca. All rights reserved.
// Use of this source code is goverend by a BSD-style
// license that can be found in the LICENSE file.

package gosnmp

import (
	"fmt"
	l "github.com/alouca/gologger"
	"net"
	"strings"
	"time"
)

type GoSNMP struct {
	Target    string
	Community string
	Version   SnmpVersion
	Timeout   time.Duration
	conn      net.Conn
	Log       *l.Logger
}

var DEFAULT_PORT = 161

// Creates a new SNMP Client. Target is the IP address, Community the SNMP Community String and Version the SNMP version.
// Currently only v2c is supported. Timeout parameter is measured in seconds.
func NewGoSNMP(target, community string, version SnmpVersion, timeout int64) (*GoSNMP, error) {
	if !strings.Contains(target, ":") {
		target = fmt.Sprintf("%s:%d", target, DEFAULT_PORT)
	}

	// Open a UDP connection to the target
	conn, err := net.DialTimeout("udp", target, time.Duration(timeout)*time.Second)

	if err != nil {
		return nil, fmt.Errorf("Error establishing connection to host: %s\n", err.Error())
	}
	s := &GoSNMP{target, community, version, time.Duration(timeout) * time.Second, conn, l.CreateLogger(false, false)}

	return s, nil
}

// Enables verbose logging
func (x *GoSNMP) SetVerbose(v bool) {
	x.Log.VerboseFlag = v
}

// Enables debugging
func (x *GoSNMP) SetDebug(d bool) {
	x.Log.DebugFlag = d
}

// Sets the timeout for network read/write functions. Defaults to 5 seconds.
func (x *GoSNMP) SetTimeout(seconds int64) {
	if seconds <= 0 {
		seconds = 5
	}
	x.Timeout = time.Duration(seconds) * time.Second
}

// StreamWalk will start walking a specified OID, and push through a channel the results
// as it receives them, without waiting for the whole process to finish to return the
// results. Once it has completed the walk, the channel is closed.
func (x *GoSNMP) StreamWalk(oid string, c chan SnmpPDU) error {
	if oid == "" {
		close(c)
		return fmt.Errorf("No OID given\n")
	}

	requestOid := oid

	for {
		res, err := x.GetNext(oid)
		if err != nil {
			close(c)
			return err
		}
		if res != nil {
			if len(res.Variables) > 0 {
				if strings.Index(res.Variables[0].Name, requestOid) > -1 {
					c <- res.Variables[0]
					// Set to the next
					oid = res.Variables[0].Name
					x.Log.Debug("Moving to %s\n", oid)
				} else {
					x.Log.Debug("Root OID mismatch, stopping walk\n")
					break
				}
			} else {
				break
			}
		} else {
			break
		}

	}
	close(c)
	return
}

func (x *GoSNMP) BulkWalk(max_repetitions uint8, oid string) (results []SnmpPDU, err error) {
	if oid == "" {
		return nil, fmt.Errorf("No OID given\n")
	}
	return x._bulkWalk(max_repetitions, oid, oid)
}
func (x *GoSNMP) _bulkWalk(max_repetitions uint8, searching_oid string, root_oid string) (results []SnmpPDU, err error) {
	response, err := x.GetBulk(0, max_repetitions, searching_oid)
	if err != nil {
		return
	}
	for i, v := range response.Variables {
		if v.Value == "endOfMib" {
			return
		}
		// is this variable still in the requested oid range
		if strings.HasPrefix(v.Name, root_oid) {
			results = append(results, v)
			// is the last oid received still in the requested range
			if i == len(response.Variables)-1 {
				var sub_results []SnmpPDU
				sub_results, err = x._bulkWalk(max_repetitions, v.Name, root_oid)
				if err != nil {
					return
				}
				results = append(results, sub_results...)
			}
		}
	}
	return
}

// Walk will SNMP walk the target, blocking until the process is complete
func (x *GoSNMP) Walk(oid string) (results []SnmpPDU, err error) {
	if oid == "" {
		return nil, fmt.Errorf("No OID given\n")
	}
	results = make([]SnmpPDU, 0)
	requestOid := oid

	for {
		res, err := x.GetNext(oid)
		if err != nil {
			return results, err
		}
		if res != nil {
			if len(res.Variables) > 0 {
				if strings.Index(res.Variables[0].Name, requestOid) > -1 {
					results = append(results, res.Variables[0])
					// Set to the next
					oid = res.Variables[0].Name
					x.Log.Debug("Moving to %s\n", oid)
				} else {
					x.Log.Debug("Root OID mismatch, stopping walk\n")
					break
				}
			} else {
				break
			}
		} else {
			break
		}

	}
	return
}

// Marshals & send an SNMP request. Unmarshals the response and returns back the parsed
// SNMP packet
func (x *GoSNMP) sendPacket(packet *SnmpPacket) (*SnmpPacket, error) {
	// Set timeouts on the connection
	deadline := time.Now()
	x.conn.SetDeadline(deadline.Add(x.Timeout))

	// Marshal it
	fBuf, err := packet.marshal()

	if err != nil {
		return nil, err
	}

	// Send the packet!
	_, err = x.conn.Write(fBuf)
	if err != nil {
		return nil, fmt.Errorf("Error writing to socket: %s\n", err.Error())
	}
	// Try to read the response
	resp := make([]byte, 8192, 8192)
	n, err := x.conn.Read(resp)

	if err != nil {
		return nil, fmt.Errorf("Error reading from UDP: %s\n", err.Error())
	}

	// Unmarshal the read bytes
	pdu, err := Unmarshal(resp[:n])

	if err != nil {
		return nil, fmt.Errorf("Unable to decode packet: %s\n", err.Error())
	} else {
		if len(pdu.Variables) < 1 {
			return nil, fmt.Errorf("No responses received.")
		} else {
			return pdu, nil
		}
	}

	return nil, nil
}

// Sends an SNMP Get Next Request to the target. Returns the next variable response from the OID given or an error
func (x *GoSNMP) GetNext(oid string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.Error = 0
	packet.ErrorIndex = 0
	packet.RequestType = GetNextRequest
	packet.Version = 1 // version 2
	packet.Variables = []SnmpPDU{SnmpPDU{Name: oid, Type: Null}}

	return x.sendPacket(packet)
}

// Debug function. Unmarshals raw bytes and returns the result without the network part
func (x *GoSNMP) Debug(data []byte) (*SnmpPacket, error) {
	packet, err := Unmarshal(data)

	if err != nil {
		return nil, fmt.Errorf("Unable to decode packet: %s\n", err.Error())
	}
	return packet, nil
}

// Sends an SNMP BULK-GET request to the target. Returns a Variable with the response or an error
func (x *GoSNMP) GetBulk(non_repeaters, max_repetitions uint8, oids ...string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.NonRepeaters = non_repeaters
	packet.MaxRepetitions = max_repetitions
	packet.RequestType = GetBulkRequest
	packet.Version = 1 // version 2
	packet.Variables = make([]SnmpPDU, len(oids))

	for i, oid := range oids {
		packet.Variables[i] = SnmpPDU{Name: oid, Type: Null}
	}

	return x.sendPacket(packet)
}

// Sends an SNMP GET request to the target. Returns a Variable with the response or an error
func (x *GoSNMP) Get(oid string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.Error = 0
	packet.ErrorIndex = 0
	packet.RequestType = GetRequest
	packet.Version = 1 // version 2
	packet.Variables = []SnmpPDU{SnmpPDU{Name: oid, Type: Null}}

	return x.sendPacket(packet)
}

// Sends an SNMP GET request to the target. Returns a Variable with the response or an error
func (x *GoSNMP) GetMulti(oids []string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.Error = 0
	packet.ErrorIndex = 0
	packet.RequestType = GetRequest
	packet.Version = 1 // version 2
	packet.Variables = make([]SnmpPDU, len(oids))

	for i, oid := range oids {
		packet.Variables[i] = SnmpPDU{Name: oid, Type: Null}
	}

	return x.sendPacket(packet)
}
<commit_msg>Fix stupid bug I made<commit_after>// Copyright 2012 Andreas Louca. All rights reserved.
// Use of this source code is goverend by a BSD-style
// license that can be found in the LICENSE file.

package gosnmp

import (
	"fmt"
	l "github.com/alouca/gologger"
	"net"
	"strings"
	"time"
)

type GoSNMP struct {
	Target    string
	Community string
	Version   SnmpVersion
	Timeout   time.Duration
	conn      net.Conn
	Log       *l.Logger
}

var DEFAULT_PORT = 161

// Creates a new SNMP Client. Target is the IP address, Community the SNMP Community String and Version the SNMP version.
// Currently only v2c is supported. Timeout parameter is measured in seconds.
func NewGoSNMP(target, community string, version SnmpVersion, timeout int64) (*GoSNMP, error) {
	if !strings.Contains(target, ":") {
		target = fmt.Sprintf("%s:%d", target, DEFAULT_PORT)
	}

	// Open a UDP connection to the target
	conn, err := net.DialTimeout("udp", target, time.Duration(timeout)*time.Second)

	if err != nil {
		return nil, fmt.Errorf("Error establishing connection to host: %s\n", err.Error())
	}
	s := &GoSNMP{target, community, version, time.Duration(timeout) * time.Second, conn, l.CreateLogger(false, false)}

	return s, nil
}

// Enables verbose logging
func (x *GoSNMP) SetVerbose(v bool) {
	x.Log.VerboseFlag = v
}

// Enables debugging
func (x *GoSNMP) SetDebug(d bool) {
	x.Log.DebugFlag = d
}

// Sets the timeout for network read/write functions. Defaults to 5 seconds.
func (x *GoSNMP) SetTimeout(seconds int64) {
	if seconds <= 0 {
		seconds = 5
	}
	x.Timeout = time.Duration(seconds) * time.Second
}

// StreamWalk will start walking a specified OID, and push through a channel the results
// as it receives them, without waiting for the whole process to finish to return the
// results. Once it has completed the walk, the channel is closed.
func (x *GoSNMP) StreamWalk(oid string, c chan SnmpPDU) error {
	if oid == "" {
		close(c)
		return fmt.Errorf("No OID given\n")
	}

	requestOid := oid

	for {
		res, err := x.GetNext(oid)
		if err != nil {
			close(c)
			return err
		}
		if res != nil {
			if len(res.Variables) > 0 {
				if strings.Index(res.Variables[0].Name, requestOid) > -1 {
					c <- res.Variables[0]
					// Set to the next
					oid = res.Variables[0].Name
					x.Log.Debug("Moving to %s\n", oid)
				} else {
					x.Log.Debug("Root OID mismatch, stopping walk\n")
					break
				}
			} else {
				break
			}
		} else {
			break
		}

	}
	close(c)
	return nil
}

func (x *GoSNMP) BulkWalk(max_repetitions uint8, oid string) (results []SnmpPDU, err error) {
	if oid == "" {
		return nil, fmt.Errorf("No OID given\n")
	}
	return x._bulkWalk(max_repetitions, oid, oid)
}
func (x *GoSNMP) _bulkWalk(max_repetitions uint8, searching_oid string, root_oid string) (results []SnmpPDU, err error) {
	response, err := x.GetBulk(0, max_repetitions, searching_oid)
	if err != nil {
		return
	}
	for i, v := range response.Variables {
		if v.Value == "endOfMib" {
			return
		}
		// is this variable still in the requested oid range
		if strings.HasPrefix(v.Name, root_oid) {
			results = append(results, v)
			// is the last oid received still in the requested range
			if i == len(response.Variables)-1 {
				var sub_results []SnmpPDU
				sub_results, err = x._bulkWalk(max_repetitions, v.Name, root_oid)
				if err != nil {
					return
				}
				results = append(results, sub_results...)
			}
		}
	}
	return
}

// Walk will SNMP walk the target, blocking until the process is complete
func (x *GoSNMP) Walk(oid string) (results []SnmpPDU, err error) {
	if oid == "" {
		return nil, fmt.Errorf("No OID given\n")
	}
	results = make([]SnmpPDU, 0)
	requestOid := oid

	for {
		res, err := x.GetNext(oid)
		if err != nil {
			return results, err
		}
		if res != nil {
			if len(res.Variables) > 0 {
				if strings.Index(res.Variables[0].Name, requestOid) > -1 {
					results = append(results, res.Variables[0])
					// Set to the next
					oid = res.Variables[0].Name
					x.Log.Debug("Moving to %s\n", oid)
				} else {
					x.Log.Debug("Root OID mismatch, stopping walk\n")
					break
				}
			} else {
				break
			}
		} else {
			break
		}

	}
	return
}

// Marshals & send an SNMP request. Unmarshals the response and returns back the parsed
// SNMP packet
func (x *GoSNMP) sendPacket(packet *SnmpPacket) (*SnmpPacket, error) {
	// Set timeouts on the connection
	deadline := time.Now()
	x.conn.SetDeadline(deadline.Add(x.Timeout))

	// Marshal it
	fBuf, err := packet.marshal()

	if err != nil {
		return nil, err
	}

	// Send the packet!
	_, err = x.conn.Write(fBuf)
	if err != nil {
		return nil, fmt.Errorf("Error writing to socket: %s\n", err.Error())
	}
	// Try to read the response
	resp := make([]byte, 8192, 8192)
	n, err := x.conn.Read(resp)

	if err != nil {
		return nil, fmt.Errorf("Error reading from UDP: %s\n", err.Error())
	}

	// Unmarshal the read bytes
	pdu, err := Unmarshal(resp[:n])

	if err != nil {
		return nil, fmt.Errorf("Unable to decode packet: %s\n", err.Error())
	} else {
		if len(pdu.Variables) < 1 {
			return nil, fmt.Errorf("No responses received.")
		} else {
			return pdu, nil
		}
	}

	return nil, nil
}

// Sends an SNMP Get Next Request to the target. Returns the next variable response from the OID given or an error
func (x *GoSNMP) GetNext(oid string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.Error = 0
	packet.ErrorIndex = 0
	packet.RequestType = GetNextRequest
	packet.Version = 1 // version 2
	packet.Variables = []SnmpPDU{SnmpPDU{Name: oid, Type: Null}}

	return x.sendPacket(packet)
}

// Debug function. Unmarshals raw bytes and returns the result without the network part
func (x *GoSNMP) Debug(data []byte) (*SnmpPacket, error) {
	packet, err := Unmarshal(data)

	if err != nil {
		return nil, fmt.Errorf("Unable to decode packet: %s\n", err.Error())
	}
	return packet, nil
}

// Sends an SNMP BULK-GET request to the target. Returns a Variable with the response or an error
func (x *GoSNMP) GetBulk(non_repeaters, max_repetitions uint8, oids ...string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.NonRepeaters = non_repeaters
	packet.MaxRepetitions = max_repetitions
	packet.RequestType = GetBulkRequest
	packet.Version = 1 // version 2
	packet.Variables = make([]SnmpPDU, len(oids))

	for i, oid := range oids {
		packet.Variables[i] = SnmpPDU{Name: oid, Type: Null}
	}

	return x.sendPacket(packet)
}

// Sends an SNMP GET request to the target. Returns a Variable with the response or an error
func (x *GoSNMP) Get(oid string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.Error = 0
	packet.ErrorIndex = 0
	packet.RequestType = GetRequest
	packet.Version = 1 // version 2
	packet.Variables = []SnmpPDU{SnmpPDU{Name: oid, Type: Null}}

	return x.sendPacket(packet)
}

// Sends an SNMP GET request to the target. Returns a Variable with the response or an error
func (x *GoSNMP) GetMulti(oids []string) (*SnmpPacket, error) {
	var err error
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
		}
	}()

	// Create the packet
	packet := new(SnmpPacket)

	packet.Community = x.Community
	packet.Error = 0
	packet.ErrorIndex = 0
	packet.RequestType = GetRequest
	packet.Version = 1 // version 2
	packet.Variables = make([]SnmpPDU, len(oids))

	for i, oid := range oids {
		packet.Variables[i] = SnmpPDU{Name: oid, Type: Null}
	}

	return x.sendPacket(packet)
}
<|endoftext|>


<commit_before>package gothub

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
)

// The HTTP host that we will hit to use the GitHub API.
const (
	GitHubUrl string = "https://api.github.com"
)

var (
	ErrRateLimitReached = errors.New("Rate limit reached")
	ErrNoJSON           = errors.New("GitHub did not return a JSON response")
)

// The GitHub struct represents an active session to the GitHub API.
type GitHub struct {
	httpClient         *http.Client
	Authorization      string
	RateLimit          int
	RateLimitRemaining int
}

func hashAuth(u, p string) string {
	var a = fmt.Sprintf("%s:%s", u, p)
	return base64.StdEncoding.EncodeToString([]byte(a))
}

// Log in to GitHub using basic, username/password authentication.
func BasicLogin(username, password string) (*GitHub, error) {
	// Format and Base64-encode the provided username and password, in preparation for basic
	// HTTP auth.
	authorization := fmt.Sprintf("Basic %s", hashAuth(username, password))
	request, err := http.NewRequest("GET", GitHubUrl, nil)
	if err != nil {
		return nil, err
	}

	// Set the Authorization header.
	request.Header.Set("Authorization", authorization)

	// Create a new HTTP client (which we will eventually provide to the GitHub struct), for
	// issuing the above HTTP request, and future requests.
	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return nil, err
	}

	if response.StatusCode == http.StatusOK {
		// Yaaaaaaay!
		ratelimit, err := strconv.Atoi(response.Header.Get("X-RateLimit-Limit"))
		if err != nil {
			return nil, err
		}

		remaining, err := strconv.Atoi(response.Header.Get("X-RateLimit-Remaining"))
		if err != nil {
			return nil, err
		}

		return &GitHub{httpClient: client, Authorization: authorization,
			RateLimit: ratelimit, RateLimitRemaining: remaining}, nil
	}

	// Should we get here, the basic authentication request failed.
	e := "Authorization failed with HTTP code: %d"
	return nil, errors.New(fmt.Sprintf(e, response.StatusCode))
}

// Updates the call limit rates in the GitHub struct.
func (g *GitHub) updateRates(r *http.Response) (err error) {
	limit, err := strconv.Atoi(r.Header.Get("X-RateLimit-Limit"))
	if err != nil {
		return
	}
	g.RateLimit = limit

	remaining, err := strconv.Atoi(r.Header.Get("X-RateLimit-Remaining"))
	if err != nil {
		return
	}
	g.RateLimitRemaining = remaining
	return
}

// Calls the GitHub API and returns the raw, HTTP response body.
func call(g *GitHub, method, uri string) (response *http.Response, err error) {
	if g.RateLimitRemaining == 0 {
		err = ErrRateLimitReached
		return
	}

	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	request, err := http.NewRequest(method, url, nil)
	if err != nil {
		return
	}

	request.Header.Set("Authorization", g.Authorization)

	// Fire off the request.
	response, err = g.httpClient.Do(request)
	if err != nil {
		return
	}

	// Update the call rates
	g.updateRates(response)

	// Check to make sure the API came back with an appropriate HTTP status
	// code, depending on the request method
	switch method {
	case "GET":
		if response.StatusCode != http.StatusOK {
			e := "GitHub API responded with HTTP %d"
			err = errors.New(fmt.Sprintf(e, response.StatusCode))
		}

	case "POST":
		switch response.StatusCode {
		case http.StatusCreated:
			return
		}

	case "DELETE":
		switch response.StatusCode {
		case http.StatusNoContent:
			return
		}
	}

	return
}

// Calls the GitHub API, but will unmarshal a JSON response to the struct
// provided to `rs`.
func (g *GitHub) callGithubApi(method, uri string, rs interface{}) error {
	response, err := call(g, method, uri)
	if err != nil {
		return err
	}

	// Check to make sure we actually got JSON back.
	switch response.Header.Get("Content-Type") {
	case "application/json":
		fallthrough
	case "application/json; charset=utf-8":
		var js []byte
		js, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		} else {
			err = json.Unmarshal(js, rs)
		}
	default:
		err = ErrNoJSON
	}

	return err
}

// Stuffs the approriate Authorization header into place on the request, then
// calls the GitHub API and udpates the API limit rates.
func (g *GitHub) call(req *http.Request) (response *http.Response, err error) {
	if g.RateLimitRemaining == 0 {
		err = ErrRateLimitReached
		return
	}

	req.Header.Set("Authorization", g.Authorization)

	response, err = g.httpClient.Do(req)
	g.updateRates(response)
	return
}

// Makes an HTTP GET request to the specified GitHub endpoint.
func (g *GitHub) get(uri string, extraHeaders map[string]string) (resp *http.Response, err error) {
	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}

	if extraHeaders != nil {
		for k, v := range extraHeaders {
			request.Header.Set(k, v)
		}
	}

	resp, err = g.call(request)
	return
}

// Makes an HTTP POST request to the specified GitHub endpoint.
func (g *GitHub) post(uri string, extraHeaders map[string]string, content *bytes.Buffer) (resp *http.Response, err error) {
	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	request, err := http.NewRequest("POST", url, content)
	if err != nil {
		return
	}

	// Add (any of) the extra headers to the request
	if extraHeaders != nil {
		for h, v := range extraHeaders {
			request.Header.Set(h, v)
		}
	}

	// Set the Content-Type header
	request.Header.Set("Content-Type", "application/json; charset=utf-8")
	resp, err = g.call(request)
	return
}

// Makes an HTTP DELETE request to the specified GitHub endpoint.
func (g *GitHub) delete(uri string, extraHeaders map[string]string, content *bytes.Buffer) (resp *http.Response, err error) {
	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	request, err := http.NewRequest("DELETE", url, content)
	if err != nil {
		return
	}

	if extraHeaders != nil {
		for h, v := range extraHeaders {
			request.Header.Set(h, v)
		}
	}

	request.Header.Set("Content-Type", "application/json; charset=utf-8")
	resp, err = g.call(request)
	return
}
<commit_msg>Fixed the request-construction logic for HTTP PUT and DELETE requests, and renamed the methods to be "http<Method>".<commit_after>package gothub

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
)

// The HTTP host that we will hit to use the GitHub API.
const (
	GitHubUrl string = "https://api.github.com"
)

var (
	ErrRateLimitReached = errors.New("Rate limit reached")
	ErrNoJSON           = errors.New("GitHub did not return a JSON response")
)

// The GitHub struct represents an active session to the GitHub API.
type GitHub struct {
	httpClient         *http.Client
	Authorization      string
	RateLimit          int
	RateLimitRemaining int
}

func hashAuth(u, p string) string {
	var a = fmt.Sprintf("%s:%s", u, p)
	return base64.StdEncoding.EncodeToString([]byte(a))
}

// Log in to GitHub using basic, username/password authentication.
func BasicLogin(username, password string) (*GitHub, error) {
	// Format and Base64-encode the provided username and password, in preparation for basic
	// HTTP auth.
	authorization := fmt.Sprintf("Basic %s", hashAuth(username, password))
	request, err := http.NewRequest("GET", GitHubUrl, nil)
	if err != nil {
		return nil, err
	}

	// Set the Authorization header.
	request.Header.Set("Authorization", authorization)

	// Create a new HTTP client (which we will eventually provide to the GitHub struct), for
	// issuing the above HTTP request, and future requests.
	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return nil, err
	}

	if response.StatusCode == http.StatusOK {
		// Yaaaaaaay!
		ratelimit, err := strconv.Atoi(response.Header.Get("X-RateLimit-Limit"))
		if err != nil {
			return nil, err
		}

		remaining, err := strconv.Atoi(response.Header.Get("X-RateLimit-Remaining"))
		if err != nil {
			return nil, err
		}

		return &GitHub{httpClient: client, Authorization: authorization,
			RateLimit: ratelimit, RateLimitRemaining: remaining}, nil
	}

	// Should we get here, the basic authentication request failed.
	e := "Authorization failed with HTTP code: %d"
	return nil, errors.New(fmt.Sprintf(e, response.StatusCode))
}

// Updates the call limit rates in the GitHub struct.
func (g *GitHub) updateRates(r *http.Response) (err error) {
	limit, err := strconv.Atoi(r.Header.Get("X-RateLimit-Limit"))
	if err != nil {
		return
	}
	g.RateLimit = limit

	remaining, err := strconv.Atoi(r.Header.Get("X-RateLimit-Remaining"))
	if err != nil {
		return
	}
	g.RateLimitRemaining = remaining
	return
}

// Calls the GitHub API and returns the raw, HTTP response body.
func call(g *GitHub, method, uri string) (response *http.Response, err error) {
	if g.RateLimitRemaining == 0 {
		err = ErrRateLimitReached
		return
	}

	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	request, err := http.NewRequest(method, url, nil)
	if err != nil {
		return
	}

	request.Header.Set("Authorization", g.Authorization)

	// Fire off the request.
	response, err = g.httpClient.Do(request)
	if err != nil {
		return
	}

	// Update the call rates
	g.updateRates(response)

	// Check to make sure the API came back with an appropriate HTTP status
	// code, depending on the request method
	switch method {
	case "GET":
		if response.StatusCode != http.StatusOK {
			e := "GitHub API responded with HTTP %d"
			err = errors.New(fmt.Sprintf(e, response.StatusCode))
		}

	case "POST":
		switch response.StatusCode {
		case http.StatusCreated:
			return
		}

	case "DELETE":
		switch response.StatusCode {
		case http.StatusNoContent:
			return
		}
	}

	return
}

// Calls the GitHub API, but will unmarshal a JSON response to the struct
// provided to `rs`.
func (g *GitHub) callGithubApi(method, uri string, rs interface{}) error {
	response, err := call(g, method, uri)
	if err != nil {
		return err
	}

	// Check to make sure we actually got JSON back.
	switch response.Header.Get("Content-Type") {
	case "application/json":
		fallthrough
	case "application/json; charset=utf-8":
		var js []byte
		js, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		} else {
			err = json.Unmarshal(js, rs)
		}
	default:
		err = ErrNoJSON
	}

	return err
}

// Stuffs the approriate Authorization header into place on the request, then
// calls the GitHub API and udpates the API limit rates.
func (g *GitHub) call(req *http.Request) (response *http.Response, err error) {
	if g.RateLimitRemaining == 0 {
		err = ErrRateLimitReached
		return
	}

	req.Header.Set("Authorization", g.Authorization)

	response, err = g.httpClient.Do(req)
	g.updateRates(response)
	return
}

// Makes an HTTP GET request to the specified GitHub endpoint.
func (g *GitHub) httpGet(uri string, extraHeaders map[string]string) (resp *http.Response, err error) {
	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}

	if extraHeaders != nil {
		for k, v := range extraHeaders {
			request.Header.Set(k, v)
		}
	}

	resp, err = g.call(request)
	return
}

// Makes an HTTP POST request to the specified GitHub endpoint.
func (g *GitHub) httpPost(uri string, extraHeaders map[string]string, content *bytes.Buffer) (resp *http.Response, err error) {
	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	request, err := http.NewRequest("POST", url, content)
	if err != nil {
		return
	}

	// Add (any of) the extra headers to the request
	if extraHeaders != nil {
		for h, v := range extraHeaders {
			request.Header.Set(h, v)
		}
	}

	// Set the Content-Type header
	request.Header.Set("Content-Type", "application/json; charset=utf-8")
	resp, err = g.call(request)
	return
}

// Makes an HTTP DELETE request to the specified GitHub endpoint.
func (g *GitHub) httpDelete(uri string, extraHeaders map[string]string, content *bytes.Buffer) (resp *http.Response, err error) {
	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	var request *http.Request
	if content != nil {
		request, err = http.NewRequest("DELETE", url, content)
	} else {
		request, err = http.NewRequest("DELETE", url, nil)
	}
	if err != nil {
		return
	}

	if extraHeaders != nil {
		for h, v := range extraHeaders {
			request.Header.Set(h, v)
		}
	}

	request.Header.Set("Content-Type", "application/json; charset=utf-8")
	resp, err = g.call(request)
	return
}

// Makes an HTTP PUT request.
func (g *GitHub) httpPut(uri string, extraHeaders map[string]string, content *bytes.Buffer) (resp *http.Response, err error) {
	url := fmt.Sprintf("%s%s", GitHubUrl, uri)
	var request *http.Request
	if content != nil {
		request, err = http.NewRequest("PUT", url, content)
	} else {
		request, err = http.NewRequest("PUT", url, nil)
	}
	if err != nil {
		return
	}

	if extraHeaders != nil {
		for h, v := range extraHeaders {
			request.Header.Set(h, v)
		}
	}

	request.Header.Set("Content-Type", "application/json; charset=utf-8")
	resp, err = g.call(request)
	return
}
<|endoftext|>


<commit_before>// GoUblu github.com/jwoehr/goublu
// goublu launches and serves as a better-than-Java console for
// https://github.com/jwoehr/ublu Ublu, a Java-coded domain-specific language
// for remote programming of IBM midrange and mainframe systems.
// Neither this project nor Ublu are associated with IBM.
package main

import (
	"bufio"
	"fmt"
	"github.com/jroimartin/gocui"
	"io"
	"log"
	"os"
	"os/exec"
)

var DefaultEditor gocui.Editor

// Do layout in cogui loop.
// Most of the work actually in go functions in main()
func layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("ubluout", -1, -1, maxX, maxY-4); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Autoscroll = true
	}
	if v, err := g.SetView("ubluin", -1, maxY-4, maxX, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Editable = true
		v.Editor = DefaultEditor
		v.Wrap = true
		if _, err := g.SetCurrentView("ubluin"); err != nil {
			return err
		}
	}
	return nil
}

// Exit via the gui instead of via Ublu
func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}

func ubluout(g *gocui.Gui, text string) {
	v, err := g.View("ubluout")
	if err != nil {
		// handle error
	}
	fmt.Fprint(v, text)
}

func main() {

	// Prepare command
	myCmds := []string{"-jar", "/opt/ublu/ublu.jar", "-g", "--"}
	ubluArgs := append(myCmds, os.Args[1:]...)
	cmd := exec.Command("java", ubluArgs...)

	// Pipes
	stdin, _ := cmd.StdinPipe()
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()

	defer stdout.Close()
	defer stderr.Close()

	// Readers
	outreader := bufio.NewReader(stdout)
	errreader := bufio.NewReader(stderr)

	// cogui
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		log.Panicln(err)
	}

	// Deliver Ublu's stdout
	go func() {
		for {
			text, _ := outreader.ReadString('\n')
			ubluout(g, text)
		}
	}()

	// Deliver Ublu's stderr
	go func() {
		for {
			text, _ := errreader.ReadString('\n')
			ubluout(g, text)
		}
	}()

	// DefaultEditor is the default editor.
	DefaultEditor = gocui.EditorFunc(func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		switch {
		case ch != 0 && mod == 0:
			v.EditWrite(ch)
		case key == gocui.KeySpace:
			v.EditWrite(' ')
		case key == gocui.KeyBackspace || key == gocui.KeyBackspace2:
			v.EditDelete(true)
		case key == gocui.KeyDelete:
			v.EditDelete(false)
		case key == gocui.KeyInsert:
			v.Overwrite = !v.Overwrite
		case key == gocui.KeyEnter:
			// v.EditNewLine()
			var l string
			var err error
			// cx, cy := v.Cursor()
			_, cy := v.Cursor()
			if l, err = v.Line(cy); err != nil {
				l = ""
			}
			w, _ := g.View("ubluout")
			fmt.Fprint(w, l)
			io.WriteString(stdin, l+"\n")
			v.Clear()
		case key == gocui.KeyArrowDown:
			v.MoveCursor(0, 1, false)
		case key == gocui.KeyArrowUp:
			v.MoveCursor(0, -1, false)
		case key == gocui.KeyArrowLeft:
			v.MoveCursor(-1, 0, false)
		case key == gocui.KeyArrowRight:
			v.MoveCursor(1, 0, false)
		}
	})

	defer g.Close()

	g.Cursor = true
	g.SetManagerFunc(layout)

	go func() {
		if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
			log.Panicln(err)
		}
	}()

	cmd.Run()
}
<commit_msg>Now wraps output<commit_after>// GoUblu github.com/jwoehr/goublu
// goublu launches and serves as a better-than-Java console for
// https://github.com/jwoehr/ublu Ublu, a Java-coded domain-specific language
// for remote programming of IBM midrange and mainframe systems.
// Neither this project nor Ublu are associated with IBM.
package main

import (
	"bufio"
	"fmt"
	"github.com/jroimartin/gocui"
	"io"
	"log"
	"os"
	"os/exec"
	// "unicode/utf8"
)

var DefaultEditor gocui.Editor

const inputLineOffset = 2

func layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("ubluout", -1, -1, maxX, maxY-inputLineOffset); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Autoscroll = true
	}
	if v, err := g.SetView("ubluin", -1, maxY-inputLineOffset, maxX, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Editable = true
		v.Editor = DefaultEditor
		v.Wrap = true
		if _, err := g.SetCurrentView("ubluin"); err != nil {
			return err
		}
	}
	return nil
}

/*
// Exit via the gui instead of via Ublu
func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}
*/

func ubluout(g *gocui.Gui, text string) {
	v, err := g.View("ubluout")
	if err != nil {
		// handle error
	}
	count := len(text)
	width, _ := g.Size()
	// width = width - 1
	for i := 0; i < count; i = i + width {
		fmt.Fprint(v, text[i:min(count-1, i+width)])
		if i < count-1 {
			fmt.Fprint(v, "\n")
		}
	}
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

func main() {

	// Prepare command
	myCmds := []string{"-jar", "/opt/ublu/ublu.jar", "-g", "--"}
	ubluArgs := append(myCmds, os.Args[1:]...)
	cmd := exec.Command("java", ubluArgs...)

	// Pipes
	stdin, _ := cmd.StdinPipe()
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()

	defer stdout.Close()
	defer stderr.Close()

	// Readers
	outreader := bufio.NewReader(stdout)
	errreader := bufio.NewReader(stderr)

	// cogui
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		log.Panicln(err)
	}

	// Deliver Ublu's stdout
	go func() {
		for {
			text, _ := outreader.ReadString('\n')
			ubluout(g, text)
		}
	}()

	// Deliver Ublu's stderr
	go func() {
		for {
			text, _ := errreader.ReadString('\n')
			ubluout(g, text)
		}
	}()

	// DefaultEditor is the default editor.
	DefaultEditor = gocui.EditorFunc(func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		switch {
		case ch != 0 && mod == 0:
			v.EditWrite(ch)
		case key == gocui.KeySpace:
			v.EditWrite(' ')
		case key == gocui.KeyBackspace || key == gocui.KeyBackspace2:
			v.EditDelete(true)
		case key == gocui.KeyDelete:
			v.EditDelete(false)
		case key == gocui.KeyInsert:
			v.Overwrite = !v.Overwrite
		case key == gocui.KeyEnter:
			// v.EditNewLine()
			var l string
			var err error
			// cx, cy := v.Cursor()
			cx, cy := v.Cursor()
			_, gy := g.Size()
			if l, err = v.Line(cy); err != nil {
				l = ""
			}
			w, _ := g.View("ubluout")
			fmt.Fprint(w, l+"\n")
			io.WriteString(stdin, l+"\n")
			v.Clear()
			v.MoveCursor(0-cx, (gy-inputLineOffset)-cy, false)
		case key == gocui.KeyArrowDown:
			v.MoveCursor(0, 1, false)
		case key == gocui.KeyArrowUp:
			v.MoveCursor(0, -1, false)
		case key == gocui.KeyArrowLeft:
			v.MoveCursor(-1, 0, false)
		case key == gocui.KeyArrowRight:
			v.MoveCursor(1, 0, false)
		}
	})

	defer g.Close()

	g.Cursor = true
	g.SetManagerFunc(layout)

	go func() {
		if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
			log.Panicln(err)
		}
	}()

	cmd.Run()
}
<|endoftext|>