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

// The time package provides functionality for measuring and
// displaying time.
package time

import (
	"os";
)

// Seconds reports the number of seconds since the Unix epoch,
// January 1, 1970 00:00:00 UTC.
func Seconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec
}

// Nanoseconds reports the number of nanoseconds since the Unix epoch,
// January 1, 1970 00:00:00 UTC.
func Nanoseconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec*1e9 + nsec
}

// Days of the week.
const (
	Sunday = iota;
	Monday;
	Tuesday;
	Wednesday;
	Thursday;
	Friday;
	Saturday;
)

// Time is the struct representing a parsed time value.
type Time struct {
	Year int64;	// 2008 is 2008
	Month, Day int;	// Sep-17 is 9, 17
	Hour, Minute, Second int;	// 10:43:12 is 10, 43, 12
	Weekday int;		// Sunday, Monday, ...
	ZoneOffset int;	// seconds west of UTC
	Zone string;
}

var nonleapyear = []int{
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}
var leapyear = []int{
	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}

func months(year int64) []int {
	if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
		return leapyear
	}
	return nonleapyear
}

const (
	secondsPerDay = 24*60*60;

	daysPer400Years = 365*400+97;
	daysPer100Years = 365*100+24;
	daysPer4Years = 365*4+1;

	days1970To2001 = 31*365+8;
)

// SecondsToUTC converts sec, in number of seconds since the Unix epoch,
// into a parsed Time value in the UTC time zone.
func SecondsToUTC(sec int64) *Time {
	t := new(Time);

	// Split into time and day.
	day := sec/secondsPerDay;
	sec -= day*secondsPerDay;
	if sec < 0 {
		day--;
		sec += secondsPerDay
	}

	// Time
	t.Hour = int(sec/3600);
	t.Minute = int((sec/60)%60);
	t.Second = int(sec%60);

	// Day 0 = January 1, 1970 was a Thursday
	t.Weekday = int((day + Thursday) % 7);
	if t.Weekday < 0 {
		t.Weekday += 7
	}

	// Change day from 0 = 1970 to 0 = 2001,
	// to make leap year calculations easier
	// (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.)
	day -= days1970To2001;

	year := int64(2001);
	if day < 0 {
		// Go back enough 400 year cycles to make day positive.
		n := -day/daysPer400Years + 1;
		year -= 400*n;
		day += daysPer400Years*n;
	} else {
		// Cut off 400 year cycles.
		n := day/daysPer400Years;
		year += 400*n;
		day -= daysPer400Years*n;
	}

	// Cut off 100-year cycles
	n := day/daysPer100Years;
	year += 100*n;
	day -= daysPer100Years*n;

	// Cut off 4-year cycles
	n = day/daysPer4Years;
	year += 4*n;
	day -= daysPer4Years*n;

	// Cut off non-leap years.
	n = day/365;
	year += n;
	day -= 365*n;

	t.Year = year;

	// If someone ever needs yearday,
	// tyearday = day (+1?)

	months := months(year);
	var m int;
	yday := int(day);
	for m = 0; m < 12 && yday >= months[m]; m++ {
		yday -= months[m]
	}
	t.Month = m+1;
	t.Day = yday+1;
	t.Zone = "UTC";

	return t;
}

// UTC returns the current time as a parsed Time value in the UTC time zone.
func UTC() *Time {
	return SecondsToUTC(Seconds())
}

// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch,
// into a parsed Time value in the local time zone.
func SecondsToLocalTime(sec int64) *Time {
	z, offset := lookupTimezone(sec);
	t := SecondsToUTC(sec+int64(offset));
	t.Zone = z;
	t.ZoneOffset = offset;
	return t
}

// LocalTime returns the current time as a parsed Time value in the local time zone.
func LocalTime() *Time {
	return SecondsToLocalTime(Seconds())
}

// Seconds returns the number of seconds since January 1, 1970 represented by the
// parsed Time value.
func (t *Time) Seconds() int64 {
	// First, accumulate days since January 1, 2001.
	// Using 2001 instead of 1970 makes the leap-year
	// handling easier (see SecondsToUTC), because
	// it is at the beginning of the 4-, 100-, and 400-year cycles.
	day := int64(0);

	// Rewrite year to be >= 2001.
	year := t.Year;
	if year < 2001 {
		n := (2001 - year)/400 + 1;
		year += 400*n;
		day -= daysPer400Years*n;
	}

	// Add in days from 400-year cycles.
	n := (year - 2001) / 400;
	year -= 400*n;
	day += daysPer400Years*n;

	// Add in 100-year cycles.
	n = (year - 2001) / 100;
	year -= 100*n;
	day += daysPer100Years*n;

	// Add in 4-year cycles.
	n = (year - 2001) / 4;
	year -= 4*n;
	day += daysPer4Years*n;

	// Add in non-leap years.
	n = year - 2001;
	day += 365*n;

	// Add in days this year.
	months := months(t.Year);
	for m := 0; m < t.Month-1; m++ {
		day += int64(months[m])
	}
	day += int64(t.Day - 1);

	// Convert days to seconds since January 1, 2001.
	sec := day * secondsPerDay;

	// Add in time elapsed today.
	sec += int64(t.Hour) * 3600;
	sec += int64(t.Minute) * 60;
	sec += int64(t.Second);

	// Convert from seconds since 2001 to seconds since 1970.
	sec += days1970To2001 * secondsPerDay;

	// Account for local time zone.
	sec -= int64(t.ZoneOffset);
	return sec
}

var longDayNames = []string{
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday"
}

var shortDayNames = []string{
	"Sun",
	"Mon",
	"Tue",
	"Wed",
	"Thu",
	"Fri",
	"Sat"
}

var shortMonthNames = []string{
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec"
}

func copy(dst []byte, s string) {
	for i := 0; i < len(s); i++ {
		dst[i] = s[i]
	}
}

func decimal(dst []byte, n int) {
	if n < 0 {
		n = 0
	}
	for i := len(dst)-1; i >= 0; i-- {
		dst[i] = byte(n%10 + '0');
		n /= 10
	}
}

func addString(buf []byte, bp int, s string) int {
	n := len(s);
	copy(buf[bp:bp+n], s);
	return bp+n
}

// Just enough of strftime to implement the date formats below.
// Not exported.
func format(t *Time, fmt string) string {
	buf := make([]byte, 128);
	bp := 0;

	for i := 0; i < len(fmt); i++ {
		if fmt[i] == '%' {
			i++;
			switch fmt[i] {
			case 'A':	// %A full weekday name
				bp = addString(buf, bp, longDayNames[t.Weekday]);
			case 'a':	// %a abbreviated weekday name
				bp = addString(buf, bp, shortDayNames[t.Weekday]);
			case 'b':	// %b abbreviated month name
				bp = addString(buf, bp, shortMonthNames[t.Month-1]);
			case 'd':	// %d day of month (01-31)
				decimal(buf[bp:bp+2], t.Day);
				bp += 2;
			case 'e':	// %e day of month ( 1-31)
				if t.Day >= 10 {
					decimal(buf[bp:bp+2], t.Day)
				} else {
					buf[bp] = ' ';
					buf[bp+1] = byte(t.Day + '0')
				}
				bp += 2;
			case 'H':	// %H hour 00-23
				decimal(buf[bp:bp+2], t.Hour);
				bp += 2;
			case 'M':	// %M minute 00-59
				decimal(buf[bp:bp+2], t.Minute);
				bp += 2;
			case 'S':	// %S second 00-59
				decimal(buf[bp:bp+2], t.Second);
				bp += 2;
			case 'Y':	// %Y year 2008
				decimal(buf[bp:bp+4], int(t.Year));
				bp += 4;
			case 'y':	// %y year 08
				decimal(buf[bp:bp+2], int(t.Year%100));
				bp += 2;
			case 'Z':
				bp = addString(buf, bp, t.Zone);
			default:
				buf[bp] = '%';
				buf[bp+1] = fmt[i];
				bp += 2
			}
		} else {
			buf[bp] = fmt[i];
			bp++;
		}
	}
	return string(buf[0:bp])
}

// Asctime formats the parsed time value in the style of
// ANSI C asctime: Sun Nov  6 08:49:37 1994
func (t *Time) Asctime() string {
	return format(t, "%a %b %e %H:%M:%S %Y")
}

// RFC850 formats the parsed time value in the style of
// RFC 850: Sunday, 06-Nov-94 08:49:37 UTC
func (t *Time) RFC850() string {
	return format(t, "%A, %d-%b-%y %H:%M:%S %Z")
}

// RFC1123 formats the parsed time value in the style of
// RFC 1123: Sun, 06 Nov 1994 08:49:37 UTC
func (t *Time) RFC1123() string {
	return format(t, "%a, %d %b %Y %H:%M:%S %Z")
}

// String formats the parsed time value in the style of
// date(1) - Sun Nov  6 08:49:37 UTC 1994
func (t *Time) String() string {
	return format(t, "%a %b %e %H:%M:%S %Z %Y")
}
<commit_msg>do not crash printing the zero value for time.Time.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The time package provides functionality for measuring and
// displaying time.
package time

import (
	"os";
)

// Seconds reports the number of seconds since the Unix epoch,
// January 1, 1970 00:00:00 UTC.
func Seconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec
}

// Nanoseconds reports the number of nanoseconds since the Unix epoch,
// January 1, 1970 00:00:00 UTC.
func Nanoseconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec*1e9 + nsec
}

// Days of the week.
const (
	Sunday = iota;
	Monday;
	Tuesday;
	Wednesday;
	Thursday;
	Friday;
	Saturday;
)

// Time is the struct representing a parsed time value.
type Time struct {
	Year int64;	// 2008 is 2008
	Month, Day int;	// Sep-17 is 9, 17
	Hour, Minute, Second int;	// 10:43:12 is 10, 43, 12
	Weekday int;		// Sunday, Monday, ...
	ZoneOffset int;	// seconds west of UTC
	Zone string;
}

var nonleapyear = []int{
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}
var leapyear = []int{
	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}

func months(year int64) []int {
	if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
		return leapyear
	}
	return nonleapyear
}

const (
	secondsPerDay = 24*60*60;

	daysPer400Years = 365*400+97;
	daysPer100Years = 365*100+24;
	daysPer4Years = 365*4+1;

	days1970To2001 = 31*365+8;
)

// SecondsToUTC converts sec, in number of seconds since the Unix epoch,
// into a parsed Time value in the UTC time zone.
func SecondsToUTC(sec int64) *Time {
	t := new(Time);

	// Split into time and day.
	day := sec/secondsPerDay;
	sec -= day*secondsPerDay;
	if sec < 0 {
		day--;
		sec += secondsPerDay
	}

	// Time
	t.Hour = int(sec/3600);
	t.Minute = int((sec/60)%60);
	t.Second = int(sec%60);

	// Day 0 = January 1, 1970 was a Thursday
	t.Weekday = int((day + Thursday) % 7);
	if t.Weekday < 0 {
		t.Weekday += 7
	}

	// Change day from 0 = 1970 to 0 = 2001,
	// to make leap year calculations easier
	// (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.)
	day -= days1970To2001;

	year := int64(2001);
	if day < 0 {
		// Go back enough 400 year cycles to make day positive.
		n := -day/daysPer400Years + 1;
		year -= 400*n;
		day += daysPer400Years*n;
	} else {
		// Cut off 400 year cycles.
		n := day/daysPer400Years;
		year += 400*n;
		day -= daysPer400Years*n;
	}

	// Cut off 100-year cycles
	n := day/daysPer100Years;
	year += 100*n;
	day -= daysPer100Years*n;

	// Cut off 4-year cycles
	n = day/daysPer4Years;
	year += 4*n;
	day -= daysPer4Years*n;

	// Cut off non-leap years.
	n = day/365;
	year += n;
	day -= 365*n;

	t.Year = year;

	// If someone ever needs yearday,
	// tyearday = day (+1?)

	months := months(year);
	var m int;
	yday := int(day);
	for m = 0; m < 12 && yday >= months[m]; m++ {
		yday -= months[m]
	}
	t.Month = m+1;
	t.Day = yday+1;
	t.Zone = "UTC";

	return t;
}

// UTC returns the current time as a parsed Time value in the UTC time zone.
func UTC() *Time {
	return SecondsToUTC(Seconds())
}

// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch,
// into a parsed Time value in the local time zone.
func SecondsToLocalTime(sec int64) *Time {
	z, offset := lookupTimezone(sec);
	t := SecondsToUTC(sec+int64(offset));
	t.Zone = z;
	t.ZoneOffset = offset;
	return t
}

// LocalTime returns the current time as a parsed Time value in the local time zone.
func LocalTime() *Time {
	return SecondsToLocalTime(Seconds())
}

// Seconds returns the number of seconds since January 1, 1970 represented by the
// parsed Time value.
func (t *Time) Seconds() int64 {
	// First, accumulate days since January 1, 2001.
	// Using 2001 instead of 1970 makes the leap-year
	// handling easier (see SecondsToUTC), because
	// it is at the beginning of the 4-, 100-, and 400-year cycles.
	day := int64(0);

	// Rewrite year to be >= 2001.
	year := t.Year;
	if year < 2001 {
		n := (2001 - year)/400 + 1;
		year += 400*n;
		day -= daysPer400Years*n;
	}

	// Add in days from 400-year cycles.
	n := (year - 2001) / 400;
	year -= 400*n;
	day += daysPer400Years*n;

	// Add in 100-year cycles.
	n = (year - 2001) / 100;
	year -= 100*n;
	day += daysPer100Years*n;

	// Add in 4-year cycles.
	n = (year - 2001) / 4;
	year -= 4*n;
	day += daysPer4Years*n;

	// Add in non-leap years.
	n = year - 2001;
	day += 365*n;

	// Add in days this year.
	months := months(t.Year);
	for m := 0; m < t.Month-1; m++ {
		day += int64(months[m])
	}
	day += int64(t.Day - 1);

	// Convert days to seconds since January 1, 2001.
	sec := day * secondsPerDay;

	// Add in time elapsed today.
	sec += int64(t.Hour) * 3600;
	sec += int64(t.Minute) * 60;
	sec += int64(t.Second);

	// Convert from seconds since 2001 to seconds since 1970.
	sec += days1970To2001 * secondsPerDay;

	// Account for local time zone.
	sec -= int64(t.ZoneOffset);
	return sec
}

var longDayNames = []string{
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday"
}

var shortDayNames = []string{
	"Sun",
	"Mon",
	"Tue",
	"Wed",
	"Thu",
	"Fri",
	"Sat"
}

var shortMonthNames = []string{
	"---",
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec"
}

func copy(dst []byte, s string) {
	for i := 0; i < len(s); i++ {
		dst[i] = s[i]
	}
}

func decimal(dst []byte, n int) {
	if n < 0 {
		n = 0
	}
	for i := len(dst)-1; i >= 0; i-- {
		dst[i] = byte(n%10 + '0');
		n /= 10
	}
}

func addString(buf []byte, bp int, s string) int {
	n := len(s);
	copy(buf[bp:bp+n], s);
	return bp+n
}

// Just enough of strftime to implement the date formats below.
// Not exported.
func format(t *Time, fmt string) string {
	buf := make([]byte, 128);
	bp := 0;

	for i := 0; i < len(fmt); i++ {
		if fmt[i] == '%' {
			i++;
			switch fmt[i] {
			case 'A':	// %A full weekday name
				bp = addString(buf, bp, longDayNames[t.Weekday]);
			case 'a':	// %a abbreviated weekday name
				bp = addString(buf, bp, shortDayNames[t.Weekday]);
			case 'b':	// %b abbreviated month name
				bp = addString(buf, bp, shortMonthNames[t.Month]);
			case 'd':	// %d day of month (01-31)
				decimal(buf[bp:bp+2], t.Day);
				bp += 2;
			case 'e':	// %e day of month ( 1-31)
				if t.Day >= 10 {
					decimal(buf[bp:bp+2], t.Day)
				} else {
					buf[bp] = ' ';
					buf[bp+1] = byte(t.Day + '0')
				}
				bp += 2;
			case 'H':	// %H hour 00-23
				decimal(buf[bp:bp+2], t.Hour);
				bp += 2;
			case 'M':	// %M minute 00-59
				decimal(buf[bp:bp+2], t.Minute);
				bp += 2;
			case 'S':	// %S second 00-59
				decimal(buf[bp:bp+2], t.Second);
				bp += 2;
			case 'Y':	// %Y year 2008
				decimal(buf[bp:bp+4], int(t.Year));
				bp += 4;
			case 'y':	// %y year 08
				decimal(buf[bp:bp+2], int(t.Year%100));
				bp += 2;
			case 'Z':
				bp = addString(buf, bp, t.Zone);
			default:
				buf[bp] = '%';
				buf[bp+1] = fmt[i];
				bp += 2
			}
		} else {
			buf[bp] = fmt[i];
			bp++;
		}
	}
	return string(buf[0:bp])
}

// Asctime formats the parsed time value in the style of
// ANSI C asctime: Sun Nov  6 08:49:37 1994
func (t *Time) Asctime() string {
	return format(t, "%a %b %e %H:%M:%S %Y")
}

// RFC850 formats the parsed time value in the style of
// RFC 850: Sunday, 06-Nov-94 08:49:37 UTC
func (t *Time) RFC850() string {
	return format(t, "%A, %d-%b-%y %H:%M:%S %Z")
}

// RFC1123 formats the parsed time value in the style of
// RFC 1123: Sun, 06 Nov 1994 08:49:37 UTC
func (t *Time) RFC1123() string {
	return format(t, "%a, %d %b %Y %H:%M:%S %Z")
}

// String formats the parsed time value in the style of
// date(1) - Sun Nov  6 08:49:37 UTC 1994
func (t *Time) String() string {
	return format(t, "%a %b %e %H:%M:%S %Z %Y")
}
<|endoftext|>


<commit_before>// Notes: the program does not care the JSON doc
// is from who and where.
// This program will be expanded to:
// (1) include more JSON specs/formats.
// (2) one JSON doc has multiple JSON structures
// (3) Log output to separate file per process using Go’s logging framework
// (4) watchdog timer for spawned processes
// (5) recovery in case of process crashes
// (6) deal with repeated failures of startup
//     due to resoure constraints or other reasons
//     like network connection failures, etc.
// (7) a REST API support to stop/suspend/kill/cleanup
//     the spawned processes manually by the admin
// (8) more stuff to be added
//
// For now, the workflow is simply as follows:
// (a) read the JSON doc
// (b) parse the JSON
// (c) spawn processes
// (d) logging the output
// (e) starts watchdog timer if needed for each process
// (f) restart the process if it exits with error
// (g) ...

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"time"
)

type Watchdog struct {
	resets   chan int64
	timeouts chan bool
}

//create a new watchdog
func newWatchdog() *Watchdog {
	wd := &Watchdog{
		resets: make(chan int64, 100),

		timeouts: make(chan bool),
	}
	go wd.loop()
	return wd
}

//check if there are new resets
func (wd *Watchdog) pump(t0 int64) (t1 int64) {
	t1 = t0
	for {
		select {
		case t := <-wd.resets:
			if t > t0 {
				t1 = t
			}
		default:
			return
		}
	}
	panic("unreachable")
}

//if there is no reset, it is in idle state
//if there is reset, decrement the timer
//till it times out and set the timeout
//channel value to true
func (wd *Watchdog) loop() {
	var t0 int64
idle:
	t0 = <-wd.resets
	t0 = wd.pump(t0)
loop:
	t0 = t0 - time.Now().UnixNano()
	time.Sleep(time.Second * time.Duration(t0))
	now := time.Now().UnixNano()
	t0 = wd.pump(now)
	if t0 == now {
		wd.timeouts <- true
		goto idle
	}
	goto loop
}

//check errors
func check(e error) {
	if e != nil {
		fmt.Println("error:", e)
		panic(e)
	}
}

//this is to set the timeout in number of seconds
//use absolute time value to avoid miscalculations
func (wd *Watchdog) reset(timeoutNanoSecs int64) {
	wd.resets <- timeoutNanoSecs + time.Now().UnixNano()
}

func main() {

	type Process struct {
		Name    string   `json:"name"`
		Path    string   `json:"path"`
		Cmdargs string   `json:"cmdargs"`
		Env     []string `json:"env"`
	}

	type Processes struct {
		Processes []Process `json:"processes"`
	}

	//read the JSON doc
	jsonBlob, err := ioutil.ReadFile("JSONdoc.txt")
	check(err)
	fmt.Print(string(jsonBlob))

	//JSON parser
	var processes Processes
	var logFile *os.File

	err = json.Unmarshal(jsonBlob, &processes)
	check(err)
	fmt.Printf("%+v", processes)
	fmt.Println()

	//watchdog for each process
	var wdg []*Watchdog
	wdg = make([]*Watchdog, len(processes.Processes))

	//spawn processes
	for i := 0; i <= len(processes.Processes)-1; i++ {
		fileName := "logfile"
		//log file name is based on date/time
		cmd := exec.Command("date")
		cmdOut, err := cmd.Output()
		check(err)
		fileName += string(cmdOut) + "_" + string(48+i)
		logFile, err = os.Create(fileName)

		check(err)
		cmd = exec.Command(processes.Processes[i].Name, processes.Processes[i].Cmdargs)
		cmdOut, err = cmd.Output()
		//TODO: if there is error, needs to restart for a given number of times
		check(err)

		//test only for the watchdogs
		wdg[i] = newWatchdog()
        //set the timeout as 50 NanoSeconds in the test, it should change later
        //based on the application scenarios
		wdg[i].reset(50)

        // TO CHANGE: use go routine to check the timeout
        // and use another boolean to see if timeout occurs
        //before the process ends
        tmo:= <-wdg[i].timeouts
        if (tmo == true) {
		    fmt.Fprintf(logFile, "Timed out!")
        }
		//fmt.Println(string(cmdOut))
		log.Println(string(cmdOut))
		fmt.Fprintf(logFile, string(cmdOut))
		defer logFile.Close()
	}
}
<commit_msg>changed typo<commit_after>// Notes: the program does not care the JSON doc
// is from who and where.
// This program will be expanded to:
// (1) include more JSON specs/formats.
// (2) one JSON doc has multiple JSON structures
// (3) Log output to separate file per process using Go’s logging framework
// (4) watchdog timer for spawned processes
// (5) recovery in case of process crashes
// (6) deal with repeated failures of startup
//     due to resoure constraints or other reasons
//     like network connection failures, etc.
// (7) a REST API support to stop/suspend/kill/cleanup
//     the spawned processes manually by the admin
// (8) more stuff to be added
//
// For now, the workflow is simply as follows:
// (a) read the JSON doc
// (b) parse the JSON
// (c) spawn processes
// (d) logging the output
// (e) starts watchdog timer if needed for each process
// (f) restart the process if it exits with error
// (g) ...

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"time"
)

type Watchdog struct {
	resets   chan int64
	timeouts chan bool
}

//create a new watchdog
func newWatchdog() *Watchdog {
	wd := &Watchdog{
		resets: make(chan int64, 100),

		timeouts: make(chan bool),
	}
	go wd.loop()
	return wd
}

//check if there are new resets
func (wd *Watchdog) pump(t0 int64) (t1 int64) {
	t1 = t0
	for {
		select {
		case t := <-wd.resets:
			if t > t0 {
				t1 = t
			}
		default:
			return
		}
	}
	panic("unreachable")
}

//if there is no reset, it is in idle state
//if there is reset, decrement the timer
//till it times out and set the timeout
//channel value to true
func (wd *Watchdog) loop() {
	var t0 int64
idle:
	t0 = <-wd.resets
	t0 = wd.pump(t0)
loop:
	t0 = t0 - time.Now().UnixNano()
	time.Sleep(time.Second * time.Duration(t0))
	now := time.Now().UnixNano()
	t0 = wd.pump(now)
	if t0 == now {
		wd.timeouts <- true
		goto idle
	}
	goto loop
}

//check errors
func check(e error) {
	if e != nil {
		fmt.Println("error:", e)
		panic(e)
	}
}

//this is to set the timeout in number of NanoSeconds
//use absolute time value to avoid miscalculations
func (wd *Watchdog) reset(timeoutNanoSecs int64) {
	wd.resets <- timeoutNanoSecs + time.Now().UnixNano()
}

func main() {

	type Process struct {
		Name    string   `json:"name"`
		Path    string   `json:"path"`
		Cmdargs string   `json:"cmdargs"`
		Env     []string `json:"env"`
	}

	type Processes struct {
		Processes []Process `json:"processes"`
	}

	//read the JSON doc
	jsonBlob, err := ioutil.ReadFile("JSONdoc.txt")
	check(err)
	fmt.Print(string(jsonBlob))

	//JSON parser
	var processes Processes
	var logFile *os.File

	err = json.Unmarshal(jsonBlob, &processes)
	check(err)
	fmt.Printf("%+v", processes)
	fmt.Println()

	//watchdog for each process
	var wdg []*Watchdog
	wdg = make([]*Watchdog, len(processes.Processes))

	//spawn processes
	for i := 0; i <= len(processes.Processes)-1; i++ {
		fileName := "logfile"
		//log file name is based on date/time
		cmd := exec.Command("date")
		cmdOut, err := cmd.Output()
		check(err)
		fileName += string(cmdOut) + "_" + string(48+i)
		logFile, err = os.Create(fileName)

		check(err)
		cmd = exec.Command(processes.Processes[i].Name, processes.Processes[i].Cmdargs)
		cmdOut, err = cmd.Output()
		//TODO: if there is error, needs to restart for a given number of times
		check(err)

		//test only for the watchdogs
		wdg[i] = newWatchdog()
        //set the timeout as 50 NanoSeconds in the test, it should change later
        //based on the application scenarios
		wdg[i].reset(50)

        // TO CHANGE: use go routine to check the timeout
        // and use another boolean to see if timeout occurs
        //before the process ends
        tmo:= <-wdg[i].timeouts
        if (tmo == true) {
		    fmt.Fprintf(logFile, "Timed out!")
        }
		//fmt.Println(string(cmdOut))
		log.Println(string(cmdOut))
		fmt.Fprintf(logFile, string(cmdOut))
		defer logFile.Close()
	}
}
<|endoftext|>


<commit_before>package apidiff

import (
	"bufio"
	"fmt"
	"go/types"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"testing"

	"golang.org/x/tools/go/packages"
)

func TestChanges(t *testing.T) {
	dir, err := ioutil.TempDir("", "apidiff_test")
	if err != nil {
		t.Fatal(err)
	}
	dir = filepath.Join(dir, "go")
	wanti, wantc := splitIntoPackages(t, dir)
	defer os.RemoveAll(dir)
	sort.Strings(wanti)
	sort.Strings(wantc)

	oldpkg, err := load("apidiff/old", dir)
	if err != nil {
		t.Fatal(err)
	}
	newpkg, err := load("apidiff/new", dir)
	if err != nil {
		t.Fatal(err)
	}

	report := Changes(oldpkg.Types, newpkg.Types)

	if !reflect.DeepEqual(report.Incompatible, wanti) {
		t.Errorf("incompatibles: got %v\nwant %v\n", report.Incompatible, wanti)
	}
	if !reflect.DeepEqual(report.Compatible, wantc) {
		t.Errorf("compatibles: got %v\nwant %v\n", report.Compatible, wantc)
	}
}

func splitIntoPackages(t *testing.T, dir string) (incompatibles, compatibles []string) {
	// Read the input file line by line.
	// Write a line into the old or new package,
	// dependent on comments.
	// Also collect expected messages.
	f, err := os.Open("testdata/tests.go")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	if err := os.MkdirAll(filepath.Join(dir, "src", "apidiff"), 0700); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(filepath.Join(dir, "src", "apidiff", "go.mod"), []byte("module apidiff\n"), 0666); err != nil {
		t.Fatal(err)
	}

	oldd := filepath.Join(dir, "src/apidiff/old")
	newd := filepath.Join(dir, "src/apidiff/new")
	if err := os.MkdirAll(oldd, 0700); err != nil {
		t.Fatal(err)
	}
	if err := os.Mkdir(newd, 0700); err != nil && !os.IsExist(err) {
		t.Fatal(err)
	}

	oldf, err := os.Create(filepath.Join(oldd, "old.go"))
	if err != nil {
		t.Fatal(err)
	}
	newf, err := os.Create(filepath.Join(newd, "new.go"))
	if err != nil {
		t.Fatal(err)
	}

	wl := func(f *os.File, line string) {
		if _, err := fmt.Fprintln(f, line); err != nil {
			t.Fatal(err)
		}
	}
	writeBoth := func(line string) { wl(oldf, line); wl(newf, line) }
	writeln := writeBoth
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		tl := strings.TrimSpace(line)
		switch {
		case tl == "// old":
			writeln = func(line string) { wl(oldf, line) }
		case tl == "// new":
			writeln = func(line string) { wl(newf, line) }
		case tl == "// both":
			writeln = writeBoth
		case strings.HasPrefix(tl, "// i "):
			incompatibles = append(incompatibles, strings.TrimSpace(tl[4:]))
		case strings.HasPrefix(tl, "// c "):
			compatibles = append(compatibles, strings.TrimSpace(tl[4:]))
		default:
			writeln(line)
		}
	}
	if s.Err() != nil {
		t.Fatal(s.Err())
	}
	return
}

func load(importPath, goPath string) (*packages.Package, error) {
	cfg := &packages.Config{
		Mode: packages.LoadTypes,
	}
	if goPath != "" {
		cfg.Env = append(os.Environ(), "GOPATH="+goPath)
		cfg.Dir = filepath.Join(goPath, "src", filepath.FromSlash(importPath))
	}
	pkgs, err := packages.Load(cfg, importPath)
	if err != nil {
		return nil, err
	}
	if len(pkgs[0].Errors) > 0 {
		return nil, pkgs[0].Errors[0]
	}
	return pkgs[0], nil
}

func TestExportedFields(t *testing.T) {
	pkg, err := load("golang.org/x/exp/apidiff/testdata/exported_fields", "")
	if err != nil {
		t.Fatal(err)
	}
	typeof := func(name string) types.Type {
		return pkg.Types.Scope().Lookup(name).Type()
	}

	s := typeof("S")
	su := s.(*types.Named).Underlying().(*types.Struct)

	ef := exportedSelectableFields(su)
	wants := []struct {
		name string
		typ  types.Type
	}{
		{"A1", typeof("A1")},
		{"D", types.Typ[types.Bool]},
		{"E", types.Typ[types.Int]},
		{"F", typeof("F")},
		{"S", types.NewPointer(s)},
	}

	if got, want := len(ef), len(wants); got != want {
		t.Errorf("got %d fields, want %d\n%+v", got, want, ef)
	}
	for _, w := range wants {
		if got := ef[w.name]; got != nil && !types.Identical(got.Type(), w.typ) {
			t.Errorf("%s: got %v, want %v", w.name, got.Type(), w.typ)
		}
	}
}
<commit_msg>apidiff: fix old reference to golang.org/x/exp<commit_after>package apidiff

import (
	"bufio"
	"fmt"
	"go/types"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"testing"

	"golang.org/x/tools/go/packages"
)

func TestChanges(t *testing.T) {
	dir, err := ioutil.TempDir("", "apidiff_test")
	if err != nil {
		t.Fatal(err)
	}
	dir = filepath.Join(dir, "go")
	wanti, wantc := splitIntoPackages(t, dir)
	defer os.RemoveAll(dir)
	sort.Strings(wanti)
	sort.Strings(wantc)

	oldpkg, err := load("apidiff/old", dir)
	if err != nil {
		t.Fatal(err)
	}
	newpkg, err := load("apidiff/new", dir)
	if err != nil {
		t.Fatal(err)
	}

	report := Changes(oldpkg.Types, newpkg.Types)

	if !reflect.DeepEqual(report.Incompatible, wanti) {
		t.Errorf("incompatibles: got %v\nwant %v\n", report.Incompatible, wanti)
	}
	if !reflect.DeepEqual(report.Compatible, wantc) {
		t.Errorf("compatibles: got %v\nwant %v\n", report.Compatible, wantc)
	}
}

func splitIntoPackages(t *testing.T, dir string) (incompatibles, compatibles []string) {
	// Read the input file line by line.
	// Write a line into the old or new package,
	// dependent on comments.
	// Also collect expected messages.
	f, err := os.Open("testdata/tests.go")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	if err := os.MkdirAll(filepath.Join(dir, "src", "apidiff"), 0700); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(filepath.Join(dir, "src", "apidiff", "go.mod"), []byte("module apidiff\n"), 0666); err != nil {
		t.Fatal(err)
	}

	oldd := filepath.Join(dir, "src/apidiff/old")
	newd := filepath.Join(dir, "src/apidiff/new")
	if err := os.MkdirAll(oldd, 0700); err != nil {
		t.Fatal(err)
	}
	if err := os.Mkdir(newd, 0700); err != nil && !os.IsExist(err) {
		t.Fatal(err)
	}

	oldf, err := os.Create(filepath.Join(oldd, "old.go"))
	if err != nil {
		t.Fatal(err)
	}
	newf, err := os.Create(filepath.Join(newd, "new.go"))
	if err != nil {
		t.Fatal(err)
	}

	wl := func(f *os.File, line string) {
		if _, err := fmt.Fprintln(f, line); err != nil {
			t.Fatal(err)
		}
	}
	writeBoth := func(line string) { wl(oldf, line); wl(newf, line) }
	writeln := writeBoth
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		tl := strings.TrimSpace(line)
		switch {
		case tl == "// old":
			writeln = func(line string) { wl(oldf, line) }
		case tl == "// new":
			writeln = func(line string) { wl(newf, line) }
		case tl == "// both":
			writeln = writeBoth
		case strings.HasPrefix(tl, "// i "):
			incompatibles = append(incompatibles, strings.TrimSpace(tl[4:]))
		case strings.HasPrefix(tl, "// c "):
			compatibles = append(compatibles, strings.TrimSpace(tl[4:]))
		default:
			writeln(line)
		}
	}
	if s.Err() != nil {
		t.Fatal(s.Err())
	}
	return
}

func load(importPath, goPath string) (*packages.Package, error) {
	cfg := &packages.Config{
		Mode: packages.LoadTypes,
	}
	if goPath != "" {
		cfg.Env = append(os.Environ(), "GOPATH="+goPath)
		cfg.Dir = filepath.Join(goPath, "src", filepath.FromSlash(importPath))
	}
	pkgs, err := packages.Load(cfg, importPath)
	if err != nil {
		return nil, err
	}
	if len(pkgs[0].Errors) > 0 {
		return nil, pkgs[0].Errors[0]
	}
	return pkgs[0], nil
}

func TestExportedFields(t *testing.T) {
	pkg, err := load("golang.org/x/tools/internal/apidiff/testdata/exported_fields", "")
	if err != nil {
		t.Fatal(err)
	}
	typeof := func(name string) types.Type {
		return pkg.Types.Scope().Lookup(name).Type()
	}

	s := typeof("S")
	su := s.(*types.Named).Underlying().(*types.Struct)

	ef := exportedSelectableFields(su)
	wants := []struct {
		name string
		typ  types.Type
	}{
		{"A1", typeof("A1")},
		{"D", types.Typ[types.Bool]},
		{"E", types.Typ[types.Int]},
		{"F", typeof("F")},
		{"S", types.NewPointer(s)},
	}

	if got, want := len(ef), len(wants); got != want {
		t.Errorf("got %d fields, want %d\n%+v", got, want, ef)
	}
	for _, w := range wants {
		if got := ef[w.name]; got != nil && !types.Identical(got.Type(), w.typ) {
			t.Errorf("%s: got %v, want %v", w.name, got.Type(), w.typ)
		}
	}
}
<|endoftext|>


<commit_before>// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 utils

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

var resVersion = regexp.MustCompile(`(\d+.\d+.\d+).*`)

// EsVersion returns the Elasticsearch from environment variable, Java property file, or an error.
//
func EsVersion(fpath string) (string, error) {
	if envEsVersion := os.Getenv("ELASTICSEARCH_BUILD_VERSION"); envEsVersion != "" {
		splitVersion := resVersion.FindStringSubmatch(envEsVersion)
		return splitVersion[1], nil
	} else {
		return "", fmt.Errorf("ELASTICSEARCH_BUILD_VERSION is empty")
	}
}

// GitCommit returns the Git commit from environment variable or parsing information from fpath, or an error.
//
func GitCommit(fpath string) (string, error) {
	if esBuildHash := os.Getenv("ELASTICSEARCH_BUILD_HASH"); esBuildHash != "" {
		return esBuildHash[:7], nil
	}
	return "", fmt.Errorf("ELASTICSEARCH_BUILD_HASH is empty")
}

// GitTag returns the Git tag for fpath if available, or an error.
//
func GitTag(fpath string) (string, error) {
	basePath, err := basePathFromFilepath(fpath)
	if err != nil {
		return "", fmt.Errorf("GitCommit: %s", err)
	}

	commit, err := GitCommit(fpath)
	if err != nil {
		return "", fmt.Errorf("GitTag: %s", err)
	}

	args := strings.Split("git --git-dir="+basePath+".git tag --points-at "+commit, " ")
	cmd := exec.Command(args[0:1][0], args[1:]...)
	// fmt.Printf("> %s\n", strings.Join(cmd.Args, " "))

	out, err := cmd.Output()
	if err != nil {
		return "", nil
	}

	return strings.TrimSpace(string(out)), nil
}

func basePathFromFilepath(fpath string) (string, error) {
	var bpath strings.Builder

	fpath, err := filepath.Abs(fpath)
	if err != nil {
		return "", err
	}

	for _, p := range strings.Split(fpath, string(filepath.Separator)) {
		if p == "rest-api-spec" {
			break
		}
		bpath.WriteString(p)
		bpath.WriteRune(filepath.Separator)
	}

	return bpath.String(), nil
}
<commit_msg>Internal: Utils: Update regexp to allow for 7.x version (#282)<commit_after>// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 utils

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

var resVersion = regexp.MustCompile(`(\d+\.(x|\d+.\d+)).*`)

// EsVersion returns the Elasticsearch from environment variable, Java property file, or an error.
//
func EsVersion(fpath string) (string, error) {
	if envEsVersion := os.Getenv("ELASTICSEARCH_BUILD_VERSION"); envEsVersion != "" {
		splitVersion := resVersion.FindStringSubmatch(envEsVersion)
		return splitVersion[1], nil
	} else {
		return "", fmt.Errorf("ELASTICSEARCH_BUILD_VERSION is empty")
	}
}

// GitCommit returns the Git commit from environment variable or parsing information from fpath, or an error.
//
func GitCommit(fpath string) (string, error) {
	if esBuildHash := os.Getenv("ELASTICSEARCH_BUILD_HASH"); esBuildHash != "" {
		return esBuildHash[:7], nil
	}
	return "", fmt.Errorf("ELASTICSEARCH_BUILD_HASH is empty")
}

// GitTag returns the Git tag for fpath if available, or an error.
//
func GitTag(fpath string) (string, error) {
	basePath, err := basePathFromFilepath(fpath)
	if err != nil {
		return "", fmt.Errorf("GitCommit: %s", err)
	}

	commit, err := GitCommit(fpath)
	if err != nil {
		return "", fmt.Errorf("GitTag: %s", err)
	}

	args := strings.Split("git --git-dir="+basePath+".git tag --points-at "+commit, " ")
	cmd := exec.Command(args[0:1][0], args[1:]...)
	// fmt.Printf("> %s\n", strings.Join(cmd.Args, " "))

	out, err := cmd.Output()
	if err != nil {
		return "", nil
	}

	return strings.TrimSpace(string(out)), nil
}

func basePathFromFilepath(fpath string) (string, error) {
	var bpath strings.Builder

	fpath, err := filepath.Abs(fpath)
	if err != nil {
		return "", err
	}

	for _, p := range strings.Split(fpath, string(filepath.Separator)) {
		if p == "rest-api-spec" {
			break
		}
		bpath.WriteString(p)
		bpath.WriteRune(filepath.Separator)
	}

	return bpath.String(), nil
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 gengapic

import (
	"strings"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/googleapis/gapic-generator-go/internal/errors"
	"github.com/googleapis/gapic-generator-go/internal/pbinfo"
	"google.golang.org/genproto/googleapis/api/annotations"
	"google.golang.org/genproto/googleapis/rpc/code"
)

func (g *generator) clientOptions(serv *descriptor.ServiceDescriptorProto, servName string) error {
	p := g.printf

	// CallOptions struct
	{
		p("// %[1]sCallOptions contains the retry settings for each method of %[1]sClient.", servName)
		p("type %sCallOptions struct {", servName)
		for _, m := range serv.Method {
			p("%s []gax.CallOption", *m.Name)
		}
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{"gax", "github.com/googleapis/gax-go"}] = true
	}

	// defaultClientOptions
	{
		eHost, err := proto.GetExtension(serv.Options, annotations.E_DefaultHost)
		if err != nil {
			return errors.E(err, "cannot read default host")
		}

		p("func default%sClientOptions() []option.ClientOption {", servName)
		p("  return []option.ClientOption{")
		p(`    option.WithEndpoint("%s:443"),`, *eHost.(*string))
		p("    option.WithScopes(DefaultAuthScopes()...),")
		p("  }")
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/option"}] = true
	}

	// defaultCallOptions
	{
		type methodCode struct {
			method string
			codes  []code.Code
		}

		var defaultRetry []string
		var overrideRetry []methodCode

		for _, m := range serv.GetMethod() {
			if m.GetOptions() == nil {
				// Some methods are not annotated, this is not an error.
				continue
			}

			eHttp, err := proto.GetExtension(m.GetOptions(), annotations.E_Http)
			if err == proto.ErrMissingExtension {
				continue
			}
			if err != nil {
				return errors.E(err, "cannot read HTTP annotation")
			}
			// Generator spec mandates we should only retry on GET, unless there is an override.
			// TODO(pongad): implement the override.
			if _, ok := eHttp.(*annotations.HttpRule).Pattern.(*annotations.HttpRule_Get); ok {
				defaultRetry = append(defaultRetry, m.GetName())
			}
		}

		// TODO(pongad): read retry params from somewhere
		p("func default%[1]sCallOptions() *%[1]sCallOptions {", servName)

		if len(defaultRetry) > 0 || len(overrideRetry) > 0 {
			p("backoff := gax.Backoff{")
			p("  Initial: 100 * time.Millisecond,")
			p("  Max: time.Minute,")
			p("  Multiplier: 1.3,")
			p("}")

			g.imports[pbinfo.ImportSpec{Path: "time"}] = true
			g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/codes"}] = true
		}

		if len(defaultRetry) > 0 {
			p("retry := []gax.CallOption{")
			p("  gax.WithRetry(func() gax.Retryer {")
			p("    return gax.OnCodes([]codes.Code{")
			p("      codes.Internal,")
			p("      codes.Unavailable,")
			p("    }, backoff)")
			p("  }),")
			p("}")
			p("")

		}

		p("  return &%sCallOptions{", servName)
		for _, m := range defaultRetry {
			p("%s: retry,", m)
		}
		for _, retry := range overrideRetry {
			p("%s: []gax.CallOption{", retry.method)
			p("  gax.WithRetry(func() gax.Retryer {")
			p("    return gax.OnCodes([]codes.Code{")
			for _, c := range retry.codes {
				if c == code.Code_CANCELLED {
					// Go uses one 'l' spelling.
					p("codes.Canceled,")
				} else {
					p("codes.%s,", snakeToCamel(c.String()))
				}
			}
			p("    }, backoff)")
			p("  }),")
			p("},")
		}
		p("  }")
		p("}")
		p("")
	}

	return nil
}

func (g *generator) clientInit(serv *descriptor.ServiceDescriptorProto, servName string) error {
	p := g.printf

	var hasLRO bool
	for _, m := range serv.Method {
		if *m.OutputType == lroType {
			hasLRO = true
			break
		}
	}

	imp, err := g.descInfo.ImportSpec(serv)
	if err != nil {
		return err
	}

	// client struct
	{
		p("// %sClient is a client for interacting with %s API.", servName, g.apiName)
		p("//")
		p("// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls.")
		p("type %sClient struct {", servName)

		p("// The connection to the service.")
		p("conn *grpc.ClientConn")
		p("")

		p("// The gRPC API client.")
		p("%s %s.%sClient", grpcClientField(servName), imp.Name, serv.GetName())
		p("")

		if hasLRO {
			p("// LROClient is used internally to handle longrunning operations.")
			p("// It is exposed so that its CallOptions can be modified if required.")
			p("// Users should not Close this client.")
			p("LROClient *lroauto.OperationsClient")
			p("")

			g.imports[pbinfo.ImportSpec{Name: "lroauto", Path: "cloud.google.com/go/longrunning/autogen"}] = true
		}

		p("// The call options for this service.")
		p("CallOptions *%sCallOptions", servName)
		p("")

		p("// The x-goog-* metadata to be sent with each request.")
		p("xGoogMetadata metadata.MD")
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc"}] = true
		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true
	}

	// Client constructor
	{
		clientName := camelToSnake(serv.GetName())
		clientName = strings.Replace(clientName, "_", " ", -1)

		p("// New%sClient creates a new %s client.", servName, clientName)
		p("//")
		g.comment(g.comments[serv])
		p("func New%[1]sClient(ctx context.Context, opts ...option.ClientOption) (*%[1]sClient, error) {", servName)
		p("  conn, err := transport.DialGRPC(ctx, append(default%sClientOptions(), opts...)...)", servName)
		p("  if err != nil {")
		p("    return nil, err")
		p("  }")
		p("  c := &%sClient{", servName)
		p("    conn:        conn,")
		p("    CallOptions: default%sCallOptions(),", servName)
		p("")
		p("    %s: %s.New%sClient(conn),", grpcClientField(servName), imp.Name, serv.GetName())
		p("  }")
		p("  c.setGoogleClientInfo()")
		p("")

		if hasLRO {
			p("  c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn))")
			p("  if err != nil {")
			p("    // This error \"should not happen\", since we are just reusing old connection")
			p("    // and never actually need to dial.")
			p("    // If this does happen, we could leak conn. However, we cannot close conn:")
			p("    // If the user invoked the function with option.WithGRPCConn,")
			p("    // we would close a connection that's still in use.")
			p("    // TODO(pongad): investigate error conditions.")
			p("    return nil, err")
			p("  }")
		}

		p("  return c, nil")
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/transport"}] = true
		g.imports[pbinfo.ImportSpec{Path: "golang.org/x/net/context"}] = true
	}

	// Connection()
	{
		p("// Connection returns the client's connection to the API service.")
		p("func (c *%sClient) Connection() *grpc.ClientConn {", servName)
		p("  return c.conn")
		p("}")
		p("")
	}

	// Close()
	{
		p("// Close closes the connection to the API service. The user should invoke this when")
		p("// the client is no longer required.")
		p("func (c *%sClient) Close() error {", servName)
		p("  return c.conn.Close()")
		p("}")
		p("")
	}

	// setGoogleClientInfo
	{
		p("// setGoogleClientInfo sets the name and version of the application in")
		p("// the `x-goog-api-client` header passed on each request. Intended for")
		p("// use by Google-written clients.")
		p("func (c *%sClient) setGoogleClientInfo(keyval ...string) {", servName)
		p(`  kv := append([]string{"gl-go", versionGo()}, keyval...)`)
		p(`  kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version)`)
		p(`  c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))`)
		p("}")
		p("")
	}

	return nil
}
<commit_msg>change gapic gax-go to v2 (#68)<commit_after>// Copyright 2018 Google LLC
//
// 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
//
//     https://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 gengapic

import (
	"strings"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/googleapis/gapic-generator-go/internal/errors"
	"github.com/googleapis/gapic-generator-go/internal/pbinfo"
	"google.golang.org/genproto/googleapis/api/annotations"
	"google.golang.org/genproto/googleapis/rpc/code"
)

func (g *generator) clientOptions(serv *descriptor.ServiceDescriptorProto, servName string) error {
	p := g.printf

	// CallOptions struct
	{
		p("// %[1]sCallOptions contains the retry settings for each method of %[1]sClient.", servName)
		p("type %sCallOptions struct {", servName)
		for _, m := range serv.Method {
			p("%s []gax.CallOption", *m.Name)
		}
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{"gax", "github.com/googleapis/gax-go/v2"}] = true
	}

	// defaultClientOptions
	{
		eHost, err := proto.GetExtension(serv.Options, annotations.E_DefaultHost)
		if err != nil {
			return errors.E(err, "cannot read default host")
		}

		p("func default%sClientOptions() []option.ClientOption {", servName)
		p("  return []option.ClientOption{")
		p(`    option.WithEndpoint("%s:443"),`, *eHost.(*string))
		p("    option.WithScopes(DefaultAuthScopes()...),")
		p("  }")
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/option"}] = true
	}

	// defaultCallOptions
	{
		type methodCode struct {
			method string
			codes  []code.Code
		}

		var defaultRetry []string
		var overrideRetry []methodCode

		for _, m := range serv.GetMethod() {
			if m.GetOptions() == nil {
				// Some methods are not annotated, this is not an error.
				continue
			}

			eHttp, err := proto.GetExtension(m.GetOptions(), annotations.E_Http)
			if err == proto.ErrMissingExtension {
				continue
			}
			if err != nil {
				return errors.E(err, "cannot read HTTP annotation")
			}
			// Generator spec mandates we should only retry on GET, unless there is an override.
			// TODO(pongad): implement the override.
			if _, ok := eHttp.(*annotations.HttpRule).Pattern.(*annotations.HttpRule_Get); ok {
				defaultRetry = append(defaultRetry, m.GetName())
			}
		}

		// TODO(pongad): read retry params from somewhere
		p("func default%[1]sCallOptions() *%[1]sCallOptions {", servName)

		if len(defaultRetry) > 0 || len(overrideRetry) > 0 {
			p("backoff := gax.Backoff{")
			p("  Initial: 100 * time.Millisecond,")
			p("  Max: time.Minute,")
			p("  Multiplier: 1.3,")
			p("}")

			g.imports[pbinfo.ImportSpec{Path: "time"}] = true
			g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/codes"}] = true
		}

		if len(defaultRetry) > 0 {
			p("retry := []gax.CallOption{")
			p("  gax.WithRetry(func() gax.Retryer {")
			p("    return gax.OnCodes([]codes.Code{")
			p("      codes.Internal,")
			p("      codes.Unavailable,")
			p("    }, backoff)")
			p("  }),")
			p("}")
			p("")

		}

		p("  return &%sCallOptions{", servName)
		for _, m := range defaultRetry {
			p("%s: retry,", m)
		}
		for _, retry := range overrideRetry {
			p("%s: []gax.CallOption{", retry.method)
			p("  gax.WithRetry(func() gax.Retryer {")
			p("    return gax.OnCodes([]codes.Code{")
			for _, c := range retry.codes {
				if c == code.Code_CANCELLED {
					// Go uses one 'l' spelling.
					p("codes.Canceled,")
				} else {
					p("codes.%s,", snakeToCamel(c.String()))
				}
			}
			p("    }, backoff)")
			p("  }),")
			p("},")
		}
		p("  }")
		p("}")
		p("")
	}

	return nil
}

func (g *generator) clientInit(serv *descriptor.ServiceDescriptorProto, servName string) error {
	p := g.printf

	var hasLRO bool
	for _, m := range serv.Method {
		if *m.OutputType == lroType {
			hasLRO = true
			break
		}
	}

	imp, err := g.descInfo.ImportSpec(serv)
	if err != nil {
		return err
	}

	// client struct
	{
		p("// %sClient is a client for interacting with %s API.", servName, g.apiName)
		p("//")
		p("// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls.")
		p("type %sClient struct {", servName)

		p("// The connection to the service.")
		p("conn *grpc.ClientConn")
		p("")

		p("// The gRPC API client.")
		p("%s %s.%sClient", grpcClientField(servName), imp.Name, serv.GetName())
		p("")

		if hasLRO {
			p("// LROClient is used internally to handle longrunning operations.")
			p("// It is exposed so that its CallOptions can be modified if required.")
			p("// Users should not Close this client.")
			p("LROClient *lroauto.OperationsClient")
			p("")

			g.imports[pbinfo.ImportSpec{Name: "lroauto", Path: "cloud.google.com/go/longrunning/autogen"}] = true
		}

		p("// The call options for this service.")
		p("CallOptions *%sCallOptions", servName)
		p("")

		p("// The x-goog-* metadata to be sent with each request.")
		p("xGoogMetadata metadata.MD")
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc"}] = true
		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true
	}

	// Client constructor
	{
		clientName := camelToSnake(serv.GetName())
		clientName = strings.Replace(clientName, "_", " ", -1)

		p("// New%sClient creates a new %s client.", servName, clientName)
		p("//")
		g.comment(g.comments[serv])
		p("func New%[1]sClient(ctx context.Context, opts ...option.ClientOption) (*%[1]sClient, error) {", servName)
		p("  conn, err := transport.DialGRPC(ctx, append(default%sClientOptions(), opts...)...)", servName)
		p("  if err != nil {")
		p("    return nil, err")
		p("  }")
		p("  c := &%sClient{", servName)
		p("    conn:        conn,")
		p("    CallOptions: default%sCallOptions(),", servName)
		p("")
		p("    %s: %s.New%sClient(conn),", grpcClientField(servName), imp.Name, serv.GetName())
		p("  }")
		p("  c.setGoogleClientInfo()")
		p("")

		if hasLRO {
			p("  c.LROClient, err = lroauto.NewOperationsClient(ctx, option.WithGRPCConn(conn))")
			p("  if err != nil {")
			p("    // This error \"should not happen\", since we are just reusing old connection")
			p("    // and never actually need to dial.")
			p("    // If this does happen, we could leak conn. However, we cannot close conn:")
			p("    // If the user invoked the function with option.WithGRPCConn,")
			p("    // we would close a connection that's still in use.")
			p("    // TODO(pongad): investigate error conditions.")
			p("    return nil, err")
			p("  }")
		}

		p("  return c, nil")
		p("}")
		p("")

		g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/transport"}] = true
		g.imports[pbinfo.ImportSpec{Path: "golang.org/x/net/context"}] = true
	}

	// Connection()
	{
		p("// Connection returns the client's connection to the API service.")
		p("func (c *%sClient) Connection() *grpc.ClientConn {", servName)
		p("  return c.conn")
		p("}")
		p("")
	}

	// Close()
	{
		p("// Close closes the connection to the API service. The user should invoke this when")
		p("// the client is no longer required.")
		p("func (c *%sClient) Close() error {", servName)
		p("  return c.conn.Close()")
		p("}")
		p("")
	}

	// setGoogleClientInfo
	{
		p("// setGoogleClientInfo sets the name and version of the application in")
		p("// the `x-goog-api-client` header passed on each request. Intended for")
		p("// use by Google-written clients.")
		p("func (c *%sClient) setGoogleClientInfo(keyval ...string) {", servName)
		p(`  kv := append([]string{"gl-go", versionGo()}, keyval...)`)
		p(`  kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version)`)
		p(`  c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...))`)
		p("}")
		p("")
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 graphics

import (
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/web"
)

// orthoProjectionMatrix returns an orthogonal projection matrix for OpenGL.
//
// The matrix converts the coodinates (left, bottom) - (right, top) to the normalized device coodinates (-1, -1) - (1, 1).
func orthoProjectionMatrix(left, right, bottom, top int) []float32 {
	e11 := 2 / float32(right-left)
	e22 := 2 / float32(top-bottom)
	e14 := -1 * float32(right+left) / float32(right-left)
	e24 := -1 * float32(top+bottom) / float32(top-bottom)

	return []float32{
		e11, 0, 0, 0,
		0, e22, 0, 0,
		0, 0, 1, 0,
		e14, e24, 0, 1,
	}
}

// framebuffer is a wrapper of OpenGL's framebuffer.
type framebuffer struct {
	native    opengl.Framebuffer
	proMatrix []float32
	width     int
	height    int
}

// newFramebufferFromTexture creates a framebuffer from the given texture.
func newFramebufferFromTexture(texture *texture, width, height int) (*framebuffer, error) {
	native, err := opengl.GetContext().NewFramebuffer(opengl.Texture(texture.native))
	if err != nil {
		return nil, err
	}
	return &framebuffer{
		native: native,
		width:  width,
		height: height,
	}, nil
}

// newScreenFramebuffer creates a framebuffer for the screen.
func newScreenFramebuffer(width, height int) *framebuffer {
	return &framebuffer{
		native: opengl.GetContext().ScreenFramebuffer(),
		width:  width,
		height: height,
	}
}

// viewportSize returns the viewport size of the framebuffer.
func (f *framebuffer) viewportSize() (int, int) {
	// On some browsers, viewport size must be within the framebuffer size.
	// e.g. Edge (#71), Chrome on GPD Pocket (#420)
	if web.IsBrowser() {
		return f.width, f.height
	}

	// If possible, always use the same viewport size to reduce draw calls.
	m := MaxImageSize()
	return m, m
}

// setAsViewport sets the framebuffer as the current viewport.
func (f *framebuffer) setAsViewport() {
	w, h := f.viewportSize()
	opengl.GetContext().SetViewport(f.native, w, h)
}

// projectionMatrix returns a projection matrix of the framebuffer.
//
// A projection matrix converts the coodinates on the framebuffer
// (0, 0) - (viewport width, viewport height)
// to the normalized device coodinates (-1, -1) - (1, 1) with adjustment.
func (f *framebuffer) projectionMatrix() []float32 {
	if f.proMatrix != nil {
		return f.proMatrix
	}
	w, h := f.viewportSize()
	f.proMatrix = orthoProjectionMatrix(0, w, 0, h)
	return f.proMatrix
}
<commit_msg>graphics: Use the viewport size same as the framebuffer size<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 graphics

import (
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

// orthoProjectionMatrix returns an orthogonal projection matrix for OpenGL.
//
// The matrix converts the coodinates (left, bottom) - (right, top) to the normalized device coodinates (-1, -1) - (1, 1).
func orthoProjectionMatrix(left, right, bottom, top int) []float32 {
	e11 := 2 / float32(right-left)
	e22 := 2 / float32(top-bottom)
	e14 := -1 * float32(right+left) / float32(right-left)
	e24 := -1 * float32(top+bottom) / float32(top-bottom)

	return []float32{
		e11, 0, 0, 0,
		0, e22, 0, 0,
		0, 0, 1, 0,
		e14, e24, 0, 1,
	}
}

// framebuffer is a wrapper of OpenGL's framebuffer.
type framebuffer struct {
	native    opengl.Framebuffer
	proMatrix []float32
	width     int
	height    int
}

// newFramebufferFromTexture creates a framebuffer from the given texture.
func newFramebufferFromTexture(texture *texture, width, height int) (*framebuffer, error) {
	native, err := opengl.GetContext().NewFramebuffer(opengl.Texture(texture.native))
	if err != nil {
		return nil, err
	}
	return &framebuffer{
		native: native,
		width:  width,
		height: height,
	}, nil
}

// newScreenFramebuffer creates a framebuffer for the screen.
func newScreenFramebuffer(width, height int) *framebuffer {
	return &framebuffer{
		native: opengl.GetContext().ScreenFramebuffer(),
		width:  width,
		height: height,
	}
}

// viewportSize returns the viewport size of the framebuffer.
func (f *framebuffer) viewportSize() (int, int) {
	// On some environments, viewport size must be within the framebuffer size.
	// e.g. Edge (#71), Chrome on GPD Pocket (#420), macOS Mojave (#691).
	// Use the same size of the framebuffer here.
	return f.width, f.height
}

// setAsViewport sets the framebuffer as the current viewport.
func (f *framebuffer) setAsViewport() {
	w, h := f.viewportSize()
	opengl.GetContext().SetViewport(f.native, w, h)
}

// projectionMatrix returns a projection matrix of the framebuffer.
//
// A projection matrix converts the coodinates on the framebuffer
// (0, 0) - (viewport width, viewport height)
// to the normalized device coodinates (-1, -1) - (1, 1) with adjustment.
func (f *framebuffer) projectionMatrix() []float32 {
	if f.proMatrix != nil {
		return f.proMatrix
	}
	w, h := f.viewportSize()
	f.proMatrix = orthoProjectionMatrix(0, w, 0, h)
	return f.proMatrix
}
<|endoftext|>


<commit_before>package termstatus

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/text/width"
)

// Terminal is used to write messages and display status lines which can be
// updated. When the output is redirected to a file, the status lines are not
// printed.
type Terminal struct {
	wr              *bufio.Writer
	fd              uintptr
	errWriter       io.Writer
	buf             *bytes.Buffer
	msg             chan message
	status          chan status
	canUpdateStatus bool

	// will be closed when the goroutine which runs Run() terminates, so it'll
	// yield a default value immediately
	closed chan struct{}

	clearCurrentLine func(io.Writer, uintptr)
	moveCursorUp     func(io.Writer, uintptr, int)
}

type message struct {
	line string
	err  bool
}

type status struct {
	lines []string
}

type fder interface {
	Fd() uintptr
}

// New returns a new Terminal for wr. A goroutine is started to update the
// terminal. It is terminated when ctx is cancelled. When wr is redirected to
// a file (e.g. via shell output redirection) or is just an io.Writer (not the
// open *os.File for stdout), no status lines are printed. The status lines and
// normal output (via Print/Printf) are written to wr, error messages are
// written to errWriter. If disableStatus is set to true, no status messages
// are printed even if the terminal supports it.
func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
	t := &Terminal{
		wr:        bufio.NewWriter(wr),
		errWriter: errWriter,
		buf:       bytes.NewBuffer(nil),
		msg:       make(chan message),
		status:    make(chan status),
		closed:    make(chan struct{}),
	}

	if disableStatus {
		return t
	}

	if d, ok := wr.(fder); ok && canUpdateStatus(d.Fd()) {
		// only use the fancy status code when we're running on a real terminal.
		t.canUpdateStatus = true
		t.fd = d.Fd()
		t.clearCurrentLine = clearCurrentLine(wr, t.fd)
		t.moveCursorUp = moveCursorUp(wr, t.fd)
	}

	return t
}

// CanUpdateStatus return whether the status output is updated in place.
func (t *Terminal) CanUpdateStatus() bool {
	return t.canUpdateStatus
}

// Run updates the screen. It should be run in a separate goroutine. When
// ctx is cancelled, the status lines are cleanly removed.
func (t *Terminal) Run(ctx context.Context) {
	defer close(t.closed)
	if t.canUpdateStatus {
		t.run(ctx)
		return
	}

	t.runWithoutStatus(ctx)
}

// run listens on the channels and updates the terminal screen.
func (t *Terminal) run(ctx context.Context) {
	var status []string
	for {
		select {
		case <-ctx.Done():
			if !IsProcessBackground(t.fd) {
				t.undoStatus(len(status))
			}

			return

		case msg := <-t.msg:
			if IsProcessBackground(t.fd) {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}
			t.clearCurrentLine(t.wr, t.fd)

			var dst io.Writer
			if msg.err {
				dst = t.errWriter

				// assume t.wr and t.errWriter are different, so we need to
				// flush clearing the current line
				err := t.wr.Flush()
				if err != nil {
					fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
				}
			} else {
				dst = t.wr
			}

			if _, err := io.WriteString(dst, msg.line); err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
				continue
			}

			t.writeStatus(status)

			if err := t.wr.Flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case stat := <-t.status:
			if IsProcessBackground(t.fd) {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}

			status = status[:0]
			status = append(status, stat.lines...)
			t.writeStatus(status)
		}
	}
}

func (t *Terminal) writeStatus(status []string) {
	for _, line := range status {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteString(line)
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	if len(status) > 0 {
		t.moveCursorUp(t.wr, t.fd, len(status)-1)
	}

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

// runWithoutStatus listens on the channels and just prints out the messages,
// without status lines.
func (t *Terminal) runWithoutStatus(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case msg := <-t.msg:
			var flush func() error

			var dst io.Writer
			if msg.err {
				dst = t.errWriter
			} else {
				dst = t.wr
				flush = t.wr.Flush
			}

			if _, err := io.WriteString(dst, msg.line); err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
			}

			if flush == nil {
				continue
			}

			if err := flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case stat := <-t.status:
			for _, line := range stat.lines {
				// ensure that each line ends with newline
				withNewline := strings.TrimRight(line, "\n") + "\n"
				fmt.Fprint(t.wr, withNewline)
			}
			if err := t.wr.Flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}
		}
	}
}

func (t *Terminal) undoStatus(lines int) {
	for i := 0; i < lines; i++ {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteRune('\n')
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	t.moveCursorUp(t.wr, t.fd, lines)

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

func (t *Terminal) print(line string, isErr bool) {
	// make sure the line ends with a line break
	if line[len(line)-1] != '\n' {
		line += "\n"
	}

	select {
	case t.msg <- message{line: line, err: isErr}:
	case <-t.closed:
	}
}

// Print writes a line to the terminal.
func (t *Terminal) Print(line string) {
	t.print(line, false)
}

// Printf uses fmt.Sprintf to write a line to the terminal.
func (t *Terminal) Printf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Print(s)
}

// Error writes an error to the terminal.
func (t *Terminal) Error(line string) {
	t.print(line, true)
}

// Errorf uses fmt.Sprintf to write an error line to the terminal.
func (t *Terminal) Errorf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Error(s)
}

// Truncate s to fit in width (number of terminal cells) w.
// If w is negative, returns the empty string.
func truncate(s string, w int) string {
	if len(s) < w {
		// Since the display width of a character is at most 2
		// and all of ASCII (single byte per rune) has width 1,
		// no character takes more bytes to encode than its width.
		return s
	}

	for i, r := range s {
		// Determine width of the rune. This cannot be determined without
		// knowing the terminal font, so let's just be careful and treat
		// all ambigous characters as full-width, i.e., two cells.
		wr := 2
		switch width.LookupRune(r).Kind() {
		case width.Neutral, width.EastAsianNarrow:
			wr = 1
		}

		w -= wr
		if w < 0 {
			return s[:i]
		}
	}

	return s
}

// SetStatus updates the status lines.
func (t *Terminal) SetStatus(lines []string) {
	if len(lines) == 0 {
		return
	}

	width, _, err := terminal.GetSize(int(t.fd))
	if err != nil || width <= 0 {
		// use 80 columns by default
		width = 80
	}

	// make sure that all lines have a line break and are not too long
	for i, line := range lines {
		line = strings.TrimRight(line, "\n")
		line = truncate(line, width-2) + "\n"
		lines[i] = line
	}

	// make sure the last line does not have a line break
	last := len(lines) - 1
	lines[last] = strings.TrimRight(lines[last], "\n")

	select {
	case t.status <- status{lines: lines}:
	case <-t.closed:
	}
}
<commit_msg>internal/ui/termstatus: Use Fprintln to get a newline<commit_after>package termstatus

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/text/width"
)

// Terminal is used to write messages and display status lines which can be
// updated. When the output is redirected to a file, the status lines are not
// printed.
type Terminal struct {
	wr              *bufio.Writer
	fd              uintptr
	errWriter       io.Writer
	buf             *bytes.Buffer
	msg             chan message
	status          chan status
	canUpdateStatus bool

	// will be closed when the goroutine which runs Run() terminates, so it'll
	// yield a default value immediately
	closed chan struct{}

	clearCurrentLine func(io.Writer, uintptr)
	moveCursorUp     func(io.Writer, uintptr, int)
}

type message struct {
	line string
	err  bool
}

type status struct {
	lines []string
}

type fder interface {
	Fd() uintptr
}

// New returns a new Terminal for wr. A goroutine is started to update the
// terminal. It is terminated when ctx is cancelled. When wr is redirected to
// a file (e.g. via shell output redirection) or is just an io.Writer (not the
// open *os.File for stdout), no status lines are printed. The status lines and
// normal output (via Print/Printf) are written to wr, error messages are
// written to errWriter. If disableStatus is set to true, no status messages
// are printed even if the terminal supports it.
func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
	t := &Terminal{
		wr:        bufio.NewWriter(wr),
		errWriter: errWriter,
		buf:       bytes.NewBuffer(nil),
		msg:       make(chan message),
		status:    make(chan status),
		closed:    make(chan struct{}),
	}

	if disableStatus {
		return t
	}

	if d, ok := wr.(fder); ok && canUpdateStatus(d.Fd()) {
		// only use the fancy status code when we're running on a real terminal.
		t.canUpdateStatus = true
		t.fd = d.Fd()
		t.clearCurrentLine = clearCurrentLine(wr, t.fd)
		t.moveCursorUp = moveCursorUp(wr, t.fd)
	}

	return t
}

// CanUpdateStatus return whether the status output is updated in place.
func (t *Terminal) CanUpdateStatus() bool {
	return t.canUpdateStatus
}

// Run updates the screen. It should be run in a separate goroutine. When
// ctx is cancelled, the status lines are cleanly removed.
func (t *Terminal) Run(ctx context.Context) {
	defer close(t.closed)
	if t.canUpdateStatus {
		t.run(ctx)
		return
	}

	t.runWithoutStatus(ctx)
}

// run listens on the channels and updates the terminal screen.
func (t *Terminal) run(ctx context.Context) {
	var status []string
	for {
		select {
		case <-ctx.Done():
			if !IsProcessBackground(t.fd) {
				t.undoStatus(len(status))
			}

			return

		case msg := <-t.msg:
			if IsProcessBackground(t.fd) {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}
			t.clearCurrentLine(t.wr, t.fd)

			var dst io.Writer
			if msg.err {
				dst = t.errWriter

				// assume t.wr and t.errWriter are different, so we need to
				// flush clearing the current line
				err := t.wr.Flush()
				if err != nil {
					fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
				}
			} else {
				dst = t.wr
			}

			if _, err := io.WriteString(dst, msg.line); err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
				continue
			}

			t.writeStatus(status)

			if err := t.wr.Flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case stat := <-t.status:
			if IsProcessBackground(t.fd) {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}

			status = status[:0]
			status = append(status, stat.lines...)
			t.writeStatus(status)
		}
	}
}

func (t *Terminal) writeStatus(status []string) {
	for _, line := range status {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteString(line)
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	if len(status) > 0 {
		t.moveCursorUp(t.wr, t.fd, len(status)-1)
	}

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

// runWithoutStatus listens on the channels and just prints out the messages,
// without status lines.
func (t *Terminal) runWithoutStatus(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case msg := <-t.msg:
			var flush func() error

			var dst io.Writer
			if msg.err {
				dst = t.errWriter
			} else {
				dst = t.wr
				flush = t.wr.Flush
			}

			if _, err := io.WriteString(dst, msg.line); err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
			}

			if flush == nil {
				continue
			}

			if err := flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case stat := <-t.status:
			for _, line := range stat.lines {
				// Ensure that each message ends with exactly one newline.
				fmt.Fprintln(t.wr, strings.TrimRight(line, "\n"))
			}
			if err := t.wr.Flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}
		}
	}
}

func (t *Terminal) undoStatus(lines int) {
	for i := 0; i < lines; i++ {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteRune('\n')
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	t.moveCursorUp(t.wr, t.fd, lines)

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

func (t *Terminal) print(line string, isErr bool) {
	// make sure the line ends with a line break
	if line[len(line)-1] != '\n' {
		line += "\n"
	}

	select {
	case t.msg <- message{line: line, err: isErr}:
	case <-t.closed:
	}
}

// Print writes a line to the terminal.
func (t *Terminal) Print(line string) {
	t.print(line, false)
}

// Printf uses fmt.Sprintf to write a line to the terminal.
func (t *Terminal) Printf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Print(s)
}

// Error writes an error to the terminal.
func (t *Terminal) Error(line string) {
	t.print(line, true)
}

// Errorf uses fmt.Sprintf to write an error line to the terminal.
func (t *Terminal) Errorf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Error(s)
}

// Truncate s to fit in width (number of terminal cells) w.
// If w is negative, returns the empty string.
func truncate(s string, w int) string {
	if len(s) < w {
		// Since the display width of a character is at most 2
		// and all of ASCII (single byte per rune) has width 1,
		// no character takes more bytes to encode than its width.
		return s
	}

	for i, r := range s {
		// Determine width of the rune. This cannot be determined without
		// knowing the terminal font, so let's just be careful and treat
		// all ambigous characters as full-width, i.e., two cells.
		wr := 2
		switch width.LookupRune(r).Kind() {
		case width.Neutral, width.EastAsianNarrow:
			wr = 1
		}

		w -= wr
		if w < 0 {
			return s[:i]
		}
	}

	return s
}

// SetStatus updates the status lines.
func (t *Terminal) SetStatus(lines []string) {
	if len(lines) == 0 {
		return
	}

	width, _, err := terminal.GetSize(int(t.fd))
	if err != nil || width <= 0 {
		// use 80 columns by default
		width = 80
	}

	// make sure that all lines have a line break and are not too long
	for i, line := range lines {
		line = strings.TrimRight(line, "\n")
		line = truncate(line, width-2) + "\n"
		lines[i] = line
	}

	// make sure the last line does not have a line break
	last := len(lines) - 1
	lines[last] = strings.TrimRight(lines[last], "\n")

	select {
	case t.status <- status{lines: lines}:
	case <-t.closed:
	}
}
<|endoftext|>


<commit_before>package tree
/*
#include <libxml/xmlversion.h>
#include <libxml/HTMLtree.h>
#include <libxml/xmlstring.h>
#include <libxml/xpath.h>

char *
DumpXmlToString(xmlDoc *doc) {
  xmlChar *buff;
  int buffersize;
  xmlDocDumpFormatMemory(doc,
                         &buff,
                         &buffersize, 1);
  return (char *)buff;
}

char *
DumpHtmlToString(xmlDoc *doc) {
  xmlChar *buff;
  int buffersize;
  htmlDocDumpMemory(doc, &buff, &buffersize);
  return (char *)buff;
}

*/
import "C"
import "unsafe"

type Doc struct {
	DocPtr *C.xmlDoc
	*XmlNode
}

func NewDoc(ptr unsafe.Pointer) *Doc {
	doc := NewNode(ptr, nil).(*Doc)
	doc.DocPtr = (*C.xmlDoc)(ptr)
	return doc
}

func CreateHtmlDoc() *Doc {
	cDoc := C.htmlNewDoc(String2XmlChar(""), String2XmlChar(""))
	return NewNode(unsafe.Pointer(cDoc), nil).(*Doc)
}

func (doc *Doc) NewElement(name string) *Element {
	nameXmlCharPtr := String2XmlChar(name)
	defer XmlFreeChars(unsafe.Pointer(nameXmlCharPtr))
	return NewNode(unsafe.Pointer(C.xmlNewNode(nil, nameXmlCharPtr)), doc).(*Element)
}

func (doc *Doc) Free() {
	if doc.DocPtr != nil {
		C.xmlFreeDoc(doc.DocPtr)
		doc.DocPtr = nil
	}
}

func (doc *Doc) IsValid() bool {
	return (doc.DocPtr != nil)
}

func (doc *Doc) MetaEncoding() string {
	if ! doc.IsValid() {
		return ""
	}
	metaEncodingXmlCharPtr := C.htmlGetMetaEncoding(doc.DocPtr)
	return C.GoString((*C.char)(unsafe.Pointer(metaEncodingXmlCharPtr)))
}

func (doc *Doc) String() string {
	if ! doc.IsValid() {
		return ""
	}
	// TODO: Decide what type of return to do HTML or XML
	dumpCharPtr := C.DumpXmlToString(doc.DocPtr)
	defer XmlFreeChars(unsafe.Pointer(dumpCharPtr))
	return C.GoString(dumpCharPtr)
}

func (doc *Doc) DumpHTML() string {
	if ! doc.IsValid() {
		return ""
	}
	dumpCharPtr := C.DumpHtmlToString(doc.DocPtr)
	defer XmlFreeChars(unsafe.Pointer(dumpCharPtr))
	return C.GoString(dumpCharPtr)
}

func (doc *Doc) DumpXML() string {
	if ! doc.IsValid() {
		return ""
	}
	dumpCharPtr := C.DumpXmlToString(doc.DocPtr)
	defer XmlFreeChars(unsafe.Pointer(dumpCharPtr))
	return C.GoString(dumpCharPtr)
}

func (doc *Doc) RootElement() *Element {
	if ! doc.IsValid() {
		return nil
	}
	return NewNode(unsafe.Pointer(C.xmlDocGetRootElement(doc.DocPtr)), doc).(*Element)
}

func (doc *Doc) NewCData(content string) *CData {
	if ! doc.IsValid() {
		return nil
	}
	length := C.int(len([]byte(content)))
	cData := C.xmlNewCDataBlock(doc.DocPtr, String2XmlChar(content), length)
	return NewNode(unsafe.Pointer(cData), doc).(*CData)
}
<commit_msg>each document would keep a map from the id of an xmlNode pointer in C land to an XmlNode pointer in Go land. Using this map, we avoid creating multiple XmlNode objects atop the same C xmlNode object. This not only avoids freeing the same C object more than once, but also saves memory and GC overhead by creating much less Go objects.<commit_after>package tree
/*
#include <libxml/xmlversion.h>
#include <libxml/HTMLtree.h>
#include <libxml/xmlstring.h>
#include <libxml/xpath.h>

char *
DumpXmlToString(xmlDoc *doc) {
  xmlChar *buff;
  int buffersize;
  xmlDocDumpFormatMemory(doc,
                         &buff,
                         &buffersize, 1);
  return (char *)buff;
}

char *
DumpHtmlToString(xmlDoc *doc) {
  xmlChar *buff;
  int buffersize;
  htmlDocDumpMemory(doc, &buff, &buffersize);
  return (char *)buff;
}
*/
import "C"
import "unsafe"
import "fmt"

type Doc struct {
	DocPtr *C.xmlDoc
	*XmlNode
	nodeMap map[string]*XmlNode
}

func NewDoc(ptr unsafe.Pointer) *Doc {
	doc := NewNode(ptr, nil).(*Doc)
	doc.DocPtr = (*C.xmlDoc)(ptr)
	doc.InitDocNodeMap()
	return doc
}

func CreateHtmlDoc() *Doc {
	cDoc := C.htmlNewDoc(String2XmlChar(""), String2XmlChar(""))
	return NewNode(unsafe.Pointer(cDoc), nil).(*Doc)
}

func (doc *Doc) NewElement(name string) *Element {
	nameXmlCharPtr := String2XmlChar(name)
	defer XmlFreeChars(unsafe.Pointer(nameXmlCharPtr))
	return NewNode(unsafe.Pointer(C.xmlNewNode(nil, nameXmlCharPtr)), doc).(*Element)
}

func (doc *Doc) InitDocNodeMap() {
	if doc.nodeMap == nil {
		doc.nodeMap = make(map[string]*XmlNode)
	}
}

func (doc *Doc) LookupNode(nodePtr *C.xmlNode) (node *XmlNode) {
	id := fmt.Sprintf("%d", unsafe.Pointer(nodePtr))
	node = doc.nodeMap[id]
	if node == nil {
		node = &XmlNode{NodePtr: nodePtr, DocRef: doc}
		doc.nodeMap[id] = node
	}
	return
}

func (doc *Doc) Free() {
	if doc.DocPtr != nil {
		C.xmlFreeDoc(doc.DocPtr)
		doc.DocPtr = nil
	}
}

func (doc *Doc) IsValid() bool {
	return (doc.DocPtr != nil)
}

func (doc *Doc) MetaEncoding() string {
	if ! doc.IsValid() {
		return ""
	}
	metaEncodingXmlCharPtr := C.htmlGetMetaEncoding(doc.DocPtr)
	return C.GoString((*C.char)(unsafe.Pointer(metaEncodingXmlCharPtr)))
}

func (doc *Doc) String() string {
	if ! doc.IsValid() {
		return ""
	}
	// TODO: Decide what type of return to do HTML or XML
	dumpCharPtr := C.DumpXmlToString(doc.DocPtr)
	defer XmlFreeChars(unsafe.Pointer(dumpCharPtr))
	return C.GoString(dumpCharPtr)
}

func (doc *Doc) DumpHTML() string {
	if ! doc.IsValid() {
		return ""
	}
	dumpCharPtr := C.DumpHtmlToString(doc.DocPtr)
	defer XmlFreeChars(unsafe.Pointer(dumpCharPtr))
	return C.GoString(dumpCharPtr)
}

func (doc *Doc) DumpXML() string {
	if ! doc.IsValid() {
		return ""
	}
	dumpCharPtr := C.DumpXmlToString(doc.DocPtr)
	defer XmlFreeChars(unsafe.Pointer(dumpCharPtr))
	return C.GoString(dumpCharPtr)
}

func (doc *Doc) RootElement() *Element {
	if ! doc.IsValid() {
		return nil
	}
	return NewNode(unsafe.Pointer(C.xmlDocGetRootElement(doc.DocPtr)), doc).(*Element)
}

func (doc *Doc) NewCData(content string) *CData {
	if ! doc.IsValid() {
		return nil
	}
	length := C.int(len([]byte(content)))
	cData := C.xmlNewCDataBlock(doc.DocPtr, String2XmlChar(content), length)
	return NewNode(unsafe.Pointer(cData), doc).(*CData)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"net"
	"sync/atomic"
	"time"

	"github.com/getlantern/idletiming"
)

type limitedListener struct {
	net.Listener

	numConns    *uint64
	idleTimeout time.Duration

	stopped int32
	stop    chan bool
	restart chan bool
}

func newLimitedListener(l net.Listener, numConns *uint64, idleTimeout time.Duration) *limitedListener {
	listener := &limitedListener{
		Listener:    l,
		stop:        make(chan bool, 1),
		restart:     make(chan bool),
		numConns:    numConns,
		idleTimeout: idleTimeout,
	}

	return listener
}

func (sl *limitedListener) Accept() (net.Conn, error) {
	select {
	case <-sl.stop:
		<-sl.restart
	default:
	}

	conn, err := sl.Listener.Accept()
	if err != nil {
		return nil, err
	}

	atomic.AddUint64(sl.numConns, 1)
	log.Tracef("counter = %v after increment", *sl.numConns)
	lc := &LimitedConn{
		counter: sl.numConns,
	}
	lc.Conn = idletiming.Conn(conn, sl.idleTimeout, func() {
		lc.Close()
	})

	return lc, err
}

func (sl *limitedListener) IsStopped() bool {
	return atomic.LoadInt32(&sl.stopped) == 1
}

func (sl *limitedListener) Stop() {
	if !sl.IsStopped() {
		sl.stop <- true
		atomic.StoreInt32(&sl.stopped, 1)
	}
}

func (sl *limitedListener) Restart() {
	if sl.IsStopped() {
		sl.restart <- true
		atomic.StoreInt32(&sl.stopped, 0)
	}
}

type LimitedConn struct {
	net.Conn
	counter *uint64
	closed  uint32
}

func (c *LimitedConn) Close() (err error) {
	if atomic.SwapUint32(&c.closed, 1) == 1 {
		return errors.New("network connection already closed")
	}

	// Substract 1 by adding the two-complement of -1
	atomic.AddUint64(c.counter, ^uint64(0))
	log.Tracef("counter = %v after decrement", *c.counter)
	return c.Conn.Close()
}
<commit_msg>reword trace message<commit_after>package main

import (
	"errors"
	"net"
	"sync/atomic"
	"time"

	"github.com/getlantern/idletiming"
)

type limitedListener struct {
	net.Listener

	numConns    *uint64
	idleTimeout time.Duration

	stopped int32
	stop    chan bool
	restart chan bool
}

func newLimitedListener(l net.Listener, numConns *uint64, idleTimeout time.Duration) *limitedListener {
	listener := &limitedListener{
		Listener:    l,
		stop:        make(chan bool, 1),
		restart:     make(chan bool),
		numConns:    numConns,
		idleTimeout: idleTimeout,
	}

	return listener
}

func (sl *limitedListener) Accept() (net.Conn, error) {
	select {
	case <-sl.stop:
		<-sl.restart
	default:
	}

	conn, err := sl.Listener.Accept()
	if err != nil {
		return nil, err
	}

	atomic.AddUint64(sl.numConns, 1)
	log.Tracef("Accepted a new connection, now we have %v in total", *sl.numConns)
	lc := &LimitedConn{
		counter: sl.numConns,
	}
	lc.Conn = idletiming.Conn(conn, sl.idleTimeout, func() {
		lc.Close()
	})

	return lc, err
}

func (sl *limitedListener) IsStopped() bool {
	return atomic.LoadInt32(&sl.stopped) == 1
}

func (sl *limitedListener) Stop() {
	if !sl.IsStopped() {
		sl.stop <- true
		atomic.StoreInt32(&sl.stopped, 1)
	}
}

func (sl *limitedListener) Restart() {
	if sl.IsStopped() {
		sl.restart <- true
		atomic.StoreInt32(&sl.stopped, 0)
	}
}

type LimitedConn struct {
	net.Conn
	counter *uint64
	closed  uint32
}

func (c *LimitedConn) Close() (err error) {
	if atomic.SwapUint32(&c.closed, 1) == 1 {
		return errors.New("network connection already closed")
	}

	// Substract 1 by adding the two-complement of -1
	atomic.AddUint64(c.counter, ^uint64(0))
	log.Tracef("Closed a connection and left %v remaining", *c.counter)
	return c.Conn.Close()
}
<|endoftext|>


<commit_before>package pttbbs

import (
	"bytes"
	"code.google.com/p/vitess/go/memcache"
	"errors"
	"fmt"
	"strconv"
	"strings"
)

var (
	ErrInvalidArticleLine   = errors.New("Article line format error")
	ErrAttributeFetchFailed = errors.New("Cannot fetch some attributes")
	ErrUnknownValueType     = errors.New("Unknown value type")
)

type RemotePtt struct {
	BoarddAddr string

	connPool *MemcacheConnPool
}

func NewRemotePtt(boarddAddr string) *RemotePtt {
	return &RemotePtt{
		BoarddAddr: boarddAddr,
		connPool:   NewMemcacheConnPool(boarddAddr, 16),
	}
}

func key(bid int, attr string) string {
	return fmt.Sprintf("%d.%s", bid, attr)
}

func (p *RemotePtt) queryMemd(types string, keyvalue ...interface{}) (int, error) {
	var err error
	var memd *memcache.Connection
	if memd, err = p.connPool.GetConn(); err != nil {
		return 0, err
	}
	defer func() {
		p.connPool.ReleaseConn(memd, err)
	}()

	j := 0
	for i, t := range types {
		var result []byte
		key := keyvalue[j].(string)
		val := keyvalue[j+1]
		switch t {
		case 's':
			if result, _, err = memd.Get(key); err == nil {
				*val.(*string) = string(result)
			}
		case 'i':
			if result, _, err = memd.Get(key); err == nil {
				*val.(*int), err = strconv.Atoi(string(result))
			}
		case 'b':
			if result, _, err = memd.Get(key); err == nil {
				*val.(*[]byte) = result
			}
		default:
			return i, ErrUnknownValueType
		}
		if err != nil {
			return i, err
		}
		j += 2
	}

	return len(types), nil
}

func (p *RemotePtt) GetBoardChildren(bid int) (children []int, err error) {
	var result []byte
	if _, err = p.queryMemd("b", key(bid, "children"), &result); err != nil {
		return
	}

	children = make([]int, 0, 16)
	buf := bytes.NewReader(result)
	for {
		child := 0
		if n, err := fmt.Fscanf(buf, "%d,", &child); n != 1 || err != nil {
			break
		}
		children = append(children, child)
	}
	return
}

func (p *RemotePtt) GetBoard(bid int) (brd Board, err error) {
	var isboard, over18, hidden int
	if p.queryMemd("iiissssi",
		key(bid, "isboard"), &isboard,
		key(bid, "over18"), &over18,
		key(bid, "hidden"), &hidden,
		key(bid, "brdname"), &brd.BrdName,
		key(bid, "title"), &brd.Title,
		key(bid, "class"), &brd.Class,
		key(bid, "BM"), &brd.BM,
		key(bid, "parent"), &brd.Parent); err != nil {
		err = ErrAttributeFetchFailed
		return
	}
	brd.IsBoard = isboard == 1
	brd.Over18 = over18 == 1
	brd.Hidden = hidden == 1
	brd.Bid = bid
	return
}

func (p *RemotePtt) GetArticleCount(bid int) (count int, err error) {
	_, err = p.queryMemd("i", key(bid, "count"), &count)
	return
}

func (p *RemotePtt) GetArticleList(bid, offset int) (articles []Article, err error) {
	var result string
	if _, err = p.queryMemd("s", key(bid, "articles."+strconv.Itoa(offset)), &result); err != nil {
		return nil, err
	}

	articles = make([]Article, 0, 20)

	for _, line := range strings.Split(result, "\n") {
		if line == "" {
			break
		}

		parts := strings.SplitN(line, ",", 7)
		if len(parts) != 7 {
			return nil, ErrInvalidArticleLine
		}

		var off, rec, mode int
		if off, err = strconv.Atoi(parts[0]); err != nil {
			return nil, err
		}
		if rec, err = strconv.Atoi(parts[3]); err != nil {
			return nil, err
		}
		if mode, err = strconv.Atoi(parts[4]); err != nil {
			return nil, err
		}

		articles = append(articles, Article{
			Offset:    off,
			FileName:  parts[1],
			Date:      parts[2],
			Recommend: rec,
			FileMode:  mode,
			Owner:     parts[5],
			Title:     parts[6],
		})
	}
	return articles, nil
}

func (p *RemotePtt) GetArticleContent(bid int, filename string) (content []byte, err error) {
	_, err = p.queryMemd("b", key(bid, "article."+filename), &content)
	return
}

func (p *RemotePtt) BrdName2Bid(brdname string) (bid int, err error) {
	_, err = p.queryMemd("i", "tobid."+brdname, &bid)
	return
}
<commit_msg>pttbbs: Fix not getting returning error.<commit_after>package pttbbs

import (
	"bytes"
	"code.google.com/p/vitess/go/memcache"
	"errors"
	"fmt"
	"strconv"
	"strings"
)

var (
	ErrInvalidArticleLine   = errors.New("Article line format error")
	ErrAttributeFetchFailed = errors.New("Cannot fetch some attributes")
	ErrUnknownValueType     = errors.New("Unknown value type")
)

type RemotePtt struct {
	BoarddAddr string

	connPool *MemcacheConnPool
}

func NewRemotePtt(boarddAddr string) *RemotePtt {
	return &RemotePtt{
		BoarddAddr: boarddAddr,
		connPool:   NewMemcacheConnPool(boarddAddr, 16),
	}
}

func key(bid int, attr string) string {
	return fmt.Sprintf("%d.%s", bid, attr)
}

func (p *RemotePtt) queryMemd(types string, keyvalue ...interface{}) (int, error) {
	var err error
	var memd *memcache.Connection
	if memd, err = p.connPool.GetConn(); err != nil {
		return 0, err
	}
	defer func() {
		p.connPool.ReleaseConn(memd, err)
	}()

	j := 0
	for i, t := range types {
		var result []byte
		key := keyvalue[j].(string)
		val := keyvalue[j+1]
		switch t {
		case 's':
			if result, _, err = memd.Get(key); err == nil {
				*val.(*string) = string(result)
			}
		case 'i':
			if result, _, err = memd.Get(key); err == nil {
				*val.(*int), err = strconv.Atoi(string(result))
			}
		case 'b':
			if result, _, err = memd.Get(key); err == nil {
				*val.(*[]byte) = result
			}
		default:
			return i, ErrUnknownValueType
		}
		if err != nil {
			return i, err
		}
		j += 2
	}

	return len(types), nil
}

func (p *RemotePtt) GetBoardChildren(bid int) (children []int, err error) {
	var result []byte
	if _, err = p.queryMemd("b", key(bid, "children"), &result); err != nil {
		return
	}

	children = make([]int, 0, 16)
	buf := bytes.NewReader(result)
	for {
		child := 0
		if n, err := fmt.Fscanf(buf, "%d,", &child); n != 1 || err != nil {
			break
		}
		children = append(children, child)
	}
	return
}

func (p *RemotePtt) GetBoard(bid int) (brd Board, err error) {
	var isboard, over18, hidden int
	if _, err = p.queryMemd("iiissssi",
		key(bid, "isboard"), &isboard,
		key(bid, "over18"), &over18,
		key(bid, "hidden"), &hidden,
		key(bid, "brdname"), &brd.BrdName,
		key(bid, "title"), &brd.Title,
		key(bid, "class"), &brd.Class,
		key(bid, "BM"), &brd.BM,
		key(bid, "parent"), &brd.Parent); err != nil {
		err = ErrAttributeFetchFailed
		return
	}
	brd.IsBoard = isboard == 1
	brd.Over18 = over18 == 1
	brd.Hidden = hidden == 1
	brd.Bid = bid
	return
}

func (p *RemotePtt) GetArticleCount(bid int) (count int, err error) {
	_, err = p.queryMemd("i", key(bid, "count"), &count)
	return
}

func (p *RemotePtt) GetArticleList(bid, offset int) (articles []Article, err error) {
	var result string
	if _, err = p.queryMemd("s", key(bid, "articles."+strconv.Itoa(offset)), &result); err != nil {
		return nil, err
	}

	articles = make([]Article, 0, 20)

	for _, line := range strings.Split(result, "\n") {
		if line == "" {
			break
		}

		parts := strings.SplitN(line, ",", 7)
		if len(parts) != 7 {
			return nil, ErrInvalidArticleLine
		}

		var off, rec, mode int
		if off, err = strconv.Atoi(parts[0]); err != nil {
			return nil, err
		}
		if rec, err = strconv.Atoi(parts[3]); err != nil {
			return nil, err
		}
		if mode, err = strconv.Atoi(parts[4]); err != nil {
			return nil, err
		}

		articles = append(articles, Article{
			Offset:    off,
			FileName:  parts[1],
			Date:      parts[2],
			Recommend: rec,
			FileMode:  mode,
			Owner:     parts[5],
			Title:     parts[6],
		})
	}
	return articles, nil
}

func (p *RemotePtt) GetArticleContent(bid int, filename string) (content []byte, err error) {
	_, err = p.queryMemd("b", key(bid, "article."+filename), &content)
	return
}

func (p *RemotePtt) BrdName2Bid(brdname string) (bid int, err error) {
	_, err = p.queryMemd("i", "tobid."+brdname, &bid)
	return
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	if function == "execute" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<commit_msg>added execute<commit_after>/*
Copyright IBM Corp 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
    "gopkg.in/olebedev/go-duktape.v2"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	} else if function == "execute" {
      ctx := duktape.New()
      ctx.PevalString(args[1])
      args[1] := ctx.GetString(-1)
      ctx.Pop()
      ctx.DestroyHeap()
      return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	if function == "execute" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<|endoftext|>